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.
12 "crypto/openpgp/error"
18 // A Block represents an OpenPGP armored structure.
20 // The encoded form is:
21 // -----BEGIN Type-----
24 // base64-encoded Bytes
25 // '=' base64 encoded checksum
27 // where Headers is a possibly empty sequence of Key: Value lines.
29 // Since the armored data can be very large, this package presents a streaming
32 Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE").
33 Header map[string]string // Optional headers.
34 Body io.Reader // A Reader from which the contents can be read
39 var ArmorCorrupt os.Error = error.StructuralError("armor invalid")
41 const crc24Init = 0xb704ce
42 const crc24Poly = 0x1864cfb
43 const crc24Mask = 0xffffff
45 // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
46 func crc24(crc uint32, d []byte) uint32 {
48 crc ^= uint32(b) << 16
49 for i := 0; i < 8; i++ {
51 if crc&0x1000000 != 0 {
59 var armorStart = []byte("-----BEGIN ")
60 var armorEnd = []byte("-----END ")
61 var armorEndOfLine = []byte("-----")
63 // lineReader wraps a line based reader. It watches for the end of an armor
64 // block and records the expected CRC value.
65 type lineReader struct {
72 func (l *lineReader) Read(p []byte) (n int, err os.Error) {
83 line, isPrefix, err := l.in.ReadLine()
88 return 0, ArmorCorrupt
91 if len(line) == 5 && line[0] == '=' {
92 // This is the checksum line
93 var expectedBytes [3]byte
95 m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
96 if m != 3 || err != nil {
99 l.crc = uint32(expectedBytes[0])<<16 |
100 uint32(expectedBytes[1])<<8 |
101 uint32(expectedBytes[2])
103 line, _, err = l.in.ReadLine()
104 if err != nil && err != os.EOF {
107 if !bytes.HasPrefix(line, armorEnd) {
108 return 0, ArmorCorrupt
116 return 0, ArmorCorrupt
120 bytesToSave := len(line) - n
122 if cap(l.buf) < bytesToSave {
123 l.buf = make([]byte, 0, bytesToSave)
125 l.buf = l.buf[0:bytesToSave]
126 copy(l.buf, line[n:])
132 // openpgpReader passes Read calls to the underlying base64 decoder, but keeps
133 // a running CRC of the resulting data and checks the CRC against the value
134 // found by the lineReader at EOF.
135 type openpgpReader struct {
141 func (r *openpgpReader) Read(p []byte) (n int, err os.Error) {
142 n, err = r.b64Reader.Read(p)
143 r.currentCRC = crc24(r.currentCRC, p[:n])
146 if r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
147 return 0, ArmorCorrupt
154 // Decode reads a PGP armored block from the given Reader. It will ignore
155 // leading garbage. If it doesn't find a block, it will return nil, os.EOF. The
156 // given Reader is not usable after calling this function: an arbitary amount
157 // of data may have been read past the end of the block.
158 func Decode(in io.Reader) (p *Block, err os.Error) {
159 r, _ := bufio.NewReaderSize(in, 100)
166 // Skip leading garbage
168 ignoreThis := ignoreNext
169 line, ignoreNext, err = r.ReadLine()
173 if ignoreNext || ignoreThis {
176 line = bytes.TrimSpace(line)
177 if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
183 p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
184 p.Header = make(map[string]string)
185 nextIsContinuation := false
190 isContinuation := nextIsContinuation
191 line, nextIsContinuation, err = r.ReadLine()
197 p.Header[lastKey] += string(line)
200 line = bytes.TrimSpace(line)
205 i := bytes.Index(line, []byte(": "))
209 lastKey = string(line[:i])
210 p.Header[lastKey] = string(line[i+2:])
214 p.oReader.currentCRC = crc24Init
215 p.oReader.lReader = &p.lReader
216 p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)