OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / crypto / openpgp / armor / armor.go
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 // import "golang.org/x/crypto/openpgp/armor"
8
9 import (
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.
30 type 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
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) > 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.
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.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 }