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. 7package armor // import "golang.org/x/crypto/openpgp/armor" 8 9import ( 10 "bufio" 11 "bytes" 12 "encoding/base64" 13 "golang.org/x/crypto/openpgp/errors" 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. 30type Block struct { 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 34 lReader lineReader 35 oReader openpgpReader 36} 37 38var ArmorCorrupt error = errors.StructuralError("armor invalid") 39 40const crc24Init = 0xb704ce 41const crc24Poly = 0x1864cfb 42const crc24Mask = 0xffffff 43 44// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 45func 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 58var armorStart = []byte("-----BEGIN ") 59var armorEnd = []byte("-----END ") 60var 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. 64type lineReader struct { 65 in *bufio.Reader 66 buf []byte 67 eof bool 68 crc uint32 69} 70 71func (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) > 96 { 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. 134type openpgpReader struct { 135 lReader *lineReader 136 b64Reader io.Reader 137 currentCRC uint32 138} 139 140func (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. 157func Decode(in io.Reader) (p *Block, err error) { 158 r := bufio.NewReaderSize(in, 100) 159 var line []byte 160 ignoreNext := false 161 162TryNextBlock: 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.HasPrefix(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} 220