OSDN Git Service

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