LEFT | RIGHT |
(no file at all) | |
| 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. |
| 4 |
| 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 |
| 8 |
| 9 import ( |
| 10 "bufio" |
| 11 "bytes" |
| 12 "code.google.com/p/go.crypto/openpgp/errors" |
| 13 "encoding/base64" |
| 14 "io" |
| 15 ) |
| 16 |
| 17 // A Block represents an OpenPGP armored structure. |
| 18 // |
| 19 // The encoded form is: |
| 20 // -----BEGIN Type----- |
| 21 // Headers |
| 22 // |
| 23 // base64-encoded Bytes |
| 24 // '=' base64 encoded checksum |
| 25 // -----END Type----- |
| 26 // where Headers is a possibly empty sequence of Key: Value lines. |
| 27 // |
| 28 // Since the armored data can be very large, this package presents a streaming |
| 29 // interface. |
| 30 type Block struct { |
| 31 Type string // The type, taken from the preamble (i.e. "PG
P SIGNATURE"). |
| 32 Header map[string]string // Optional headers. |
| 33 Body io.Reader // A Reader from which the contents can be rea
d |
| 34 lReader lineReader |
| 35 oReader openpgpReader |
| 36 } |
| 37 |
| 38 var ArmorCorrupt error = errors.StructuralError("armor invalid") |
| 39 |
| 40 const crc24Init = 0xb704ce |
| 41 const crc24Poly = 0x1864cfb |
| 42 const crc24Mask = 0xffffff |
| 43 |
| 44 // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 |
| 45 func crc24(crc uint32, d []byte) uint32 { |
| 46 for _, b := range d { |
| 47 crc ^= uint32(b) << 16 |
| 48 for i := 0; i < 8; i++ { |
| 49 crc <<= 1 |
| 50 if crc&0x1000000 != 0 { |
| 51 crc ^= crc24Poly |
| 52 } |
| 53 } |
| 54 } |
| 55 return crc |
| 56 } |
| 57 |
| 58 var armorStart = []byte("-----BEGIN ") |
| 59 var armorEnd = []byte("-----END ") |
| 60 var armorEndOfLine = []byte("-----") |
| 61 |
| 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 { |
| 65 in *bufio.Reader |
| 66 buf []byte |
| 67 eof bool |
| 68 crc uint32 |
| 69 } |
| 70 |
| 71 func (l *lineReader) Read(p []byte) (n int, err error) { |
| 72 if l.eof { |
| 73 return 0, io.EOF |
| 74 } |
| 75 |
| 76 if len(l.buf) > 0 { |
| 77 n = copy(p, l.buf) |
| 78 l.buf = l.buf[n:] |
| 79 return |
| 80 } |
| 81 |
| 82 line, isPrefix, err := l.in.ReadLine() |
| 83 if err != nil { |
| 84 return |
| 85 } |
| 86 if isPrefix { |
| 87 return 0, ArmorCorrupt |
| 88 } |
| 89 |
| 90 if len(line) == 5 && line[0] == '=' { |
| 91 // This is the checksum line |
| 92 var expectedBytes [3]byte |
| 93 var m int |
| 94 m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) |
| 95 if m != 3 || err != nil { |
| 96 return |
| 97 } |
| 98 l.crc = uint32(expectedBytes[0])<<16 | |
| 99 uint32(expectedBytes[1])<<8 | |
| 100 uint32(expectedBytes[2]) |
| 101 |
| 102 line, _, err = l.in.ReadLine() |
| 103 if err != nil && err != io.EOF { |
| 104 return |
| 105 } |
| 106 if !bytes.HasPrefix(line, armorEnd) { |
| 107 return 0, ArmorCorrupt |
| 108 } |
| 109 |
| 110 l.eof = true |
| 111 return 0, io.EOF |
| 112 } |
| 113 |
| 114 if len(line) > 64 { |
| 115 return 0, ArmorCorrupt |
| 116 } |
| 117 |
| 118 n = copy(p, line) |
| 119 bytesToSave := len(line) - n |
| 120 if bytesToSave > 0 { |
| 121 if cap(l.buf) < bytesToSave { |
| 122 l.buf = make([]byte, 0, bytesToSave) |
| 123 } |
| 124 l.buf = l.buf[0:bytesToSave] |
| 125 copy(l.buf, line[n:]) |
| 126 } |
| 127 |
| 128 return |
| 129 } |
| 130 |
| 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 { |
| 135 lReader *lineReader |
| 136 b64Reader io.Reader |
| 137 currentCRC uint32 |
| 138 } |
| 139 |
| 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]) |
| 143 |
| 144 if err == io.EOF { |
| 145 if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { |
| 146 return 0, ArmorCorrupt |
| 147 } |
| 148 } |
| 149 |
| 150 return |
| 151 } |
| 152 |
| 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) |
| 159 var line []byte |
| 160 ignoreNext := false |
| 161 |
| 162 TryNextBlock: |
| 163 p = nil |
| 164 |
| 165 // Skip leading garbage |
| 166 for { |
| 167 ignoreThis := ignoreNext |
| 168 line, ignoreNext, err = r.ReadLine() |
| 169 if err != nil { |
| 170 return |
| 171 } |
| 172 if ignoreNext || ignoreThis { |
| 173 continue |
| 174 } |
| 175 line = bytes.TrimSpace(line) |
| 176 if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasP
refix(line, armorStart) { |
| 177 break |
| 178 } |
| 179 } |
| 180 |
| 181 p = new(Block) |
| 182 p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) |
| 183 p.Header = make(map[string]string) |
| 184 nextIsContinuation := false |
| 185 var lastKey string |
| 186 |
| 187 // Read headers |
| 188 for { |
| 189 isContinuation := nextIsContinuation |
| 190 line, nextIsContinuation, err = r.ReadLine() |
| 191 if err != nil { |
| 192 p = nil |
| 193 return |
| 194 } |
| 195 if isContinuation { |
| 196 p.Header[lastKey] += string(line) |
| 197 continue |
| 198 } |
| 199 line = bytes.TrimSpace(line) |
| 200 if len(line) == 0 { |
| 201 break |
| 202 } |
| 203 |
| 204 i := bytes.Index(line, []byte(": ")) |
| 205 if i == -1 { |
| 206 goto TryNextBlock |
| 207 } |
| 208 lastKey = string(line[:i]) |
| 209 p.Header[lastKey] = string(line[i+2:]) |
| 210 } |
| 211 |
| 212 p.lReader.in = r |
| 213 p.oReader.currentCRC = crc24Init |
| 214 p.oReader.lReader = &p.lReader |
| 215 p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) |
| 216 p.Body = &p.oReader |
| 217 |
| 218 return |
| 219 } |
LEFT | RIGHT |