1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
6 // very similar to PEM except that it has an additional CRC checksum.
7 package armor // import "golang.org/x/crypto/openpgp/armor"
13 "golang.org/x/crypto/openpgp/errors"
17 // A Block represents an OpenPGP armored structure.
19 // The encoded form is:
20 // -----BEGIN Type-----
23 // base64-encoded Bytes
24 // '=' base64 encoded checksum
26 // where Headers is a possibly empty sequence of Key: Value lines.
28 // Since the armored data can be very large, this package presents a streaming
31 Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE").
32 Header map[string]string // Optional headers.
33 Body io.Reader // A Reader from which the contents can be read
38 var ArmorCorrupt error = errors.StructuralError("armor invalid")
40 const crc24Init = 0xb704ce
41 const crc24Poly = 0x1864cfb
42 const crc24Mask = 0xffffff
44 // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
45 func crc24(crc uint32, d []byte) uint32 {
47 crc ^= uint32(b) << 16
48 for i := 0; i < 8; i++ {
50 if crc&0x1000000 != 0 {
58 var armorStart = []byte("-----BEGIN ")
59 var armorEnd = []byte("-----END ")
60 var armorEndOfLine = []byte("-----")
62 // lineReader wraps a line based reader. It watches for the end of an armor
63 // block and records the expected CRC value.
64 type lineReader struct {
71 func (l *lineReader) Read(p []byte) (n int, err error) {
82 line, isPrefix, err := l.in.ReadLine()
87 return 0, ArmorCorrupt
90 if len(line) == 5 && line[0] == '=' {
91 // This is the checksum line
92 var expectedBytes [3]byte
94 m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
95 if m != 3 || err != nil {
98 l.crc = uint32(expectedBytes[0])<<16 |
99 uint32(expectedBytes[1])<<8 |
100 uint32(expectedBytes[2])
102 line, _, err = l.in.ReadLine()
103 if err != nil && err != io.EOF {
106 if !bytes.HasPrefix(line, armorEnd) {
107 return 0, ArmorCorrupt
115 return 0, ArmorCorrupt
119 bytesToSave := len(line) - n
121 if cap(l.buf) < bytesToSave {
122 l.buf = make([]byte, 0, bytesToSave)
124 l.buf = l.buf[0:bytesToSave]
125 copy(l.buf, line[n:])
131 // openpgpReader passes Read calls to the underlying base64 decoder, but keeps
132 // a running CRC of the resulting data and checks the CRC against the value
133 // found by the lineReader at EOF.
134 type openpgpReader struct {
140 func (r *openpgpReader) Read(p []byte) (n int, err error) {
141 n, err = r.b64Reader.Read(p)
142 r.currentCRC = crc24(r.currentCRC, p[:n])
145 if r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
146 return 0, ArmorCorrupt
153 // Decode reads a PGP armored block from the given Reader. It will ignore
154 // leading garbage. If it doesn't find a block, it will return nil, io.EOF. The
155 // given Reader is not usable after calling this function: an arbitrary amount
156 // of data may have been read past the end of the block.
157 func Decode(in io.Reader) (p *Block, err error) {
158 r := bufio.NewReaderSize(in, 100)
165 // Skip leading garbage
167 ignoreThis := ignoreNext
168 line, ignoreNext, err = r.ReadLine()
172 if ignoreNext || ignoreThis {
175 line = bytes.TrimSpace(line)
176 if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
182 p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
183 p.Header = make(map[string]string)
184 nextIsContinuation := false
189 isContinuation := nextIsContinuation
190 line, nextIsContinuation, err = r.ReadLine()
196 p.Header[lastKey] += string(line)
199 line = bytes.TrimSpace(line)
204 i := bytes.Index(line, []byte(": "))
208 lastKey = string(line[:i])
209 p.Header[lastKey] = string(line[i+2:])
213 p.oReader.currentCRC = crc24Init
214 p.oReader.lReader = &p.lReader
215 p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)