]>
Commit | Line | Data |
---|---|---|
c680a8e1 RS |
1 | // Copyright 2011 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 packet | |
6 | ||
7 | import ( | |
8 | "crypto/cipher" | |
9 | "crypto/sha1" | |
10 | "crypto/subtle" | |
11 | "golang.org/x/crypto/openpgp/errors" | |
12 | "hash" | |
13 | "io" | |
14 | "strconv" | |
15 | ) | |
16 | ||
17 | // SymmetricallyEncrypted represents a symmetrically encrypted byte string. The | |
18 | // encrypted contents will consist of more OpenPGP packets. See RFC 4880, | |
19 | // sections 5.7 and 5.13. | |
20 | type SymmetricallyEncrypted struct { | |
21 | MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. | |
22 | contents io.Reader | |
23 | prefix []byte | |
24 | } | |
25 | ||
26 | const symmetricallyEncryptedVersion = 1 | |
27 | ||
28 | func (se *SymmetricallyEncrypted) parse(r io.Reader) error { | |
29 | if se.MDC { | |
30 | // See RFC 4880, section 5.13. | |
31 | var buf [1]byte | |
32 | _, err := readFull(r, buf[:]) | |
33 | if err != nil { | |
34 | return err | |
35 | } | |
36 | if buf[0] != symmetricallyEncryptedVersion { | |
37 | return errors.UnsupportedError("unknown SymmetricallyEncrypted version") | |
38 | } | |
39 | } | |
40 | se.contents = r | |
41 | return nil | |
42 | } | |
43 | ||
44 | // Decrypt returns a ReadCloser, from which the decrypted contents of the | |
45 | // packet can be read. An incorrect key can, with high probability, be detected | |
46 | // immediately and this will result in a KeyIncorrect error being returned. | |
47 | func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { | |
48 | keySize := c.KeySize() | |
49 | if keySize == 0 { | |
50 | return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) | |
51 | } | |
52 | if len(key) != keySize { | |
53 | return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") | |
54 | } | |
55 | ||
56 | if se.prefix == nil { | |
57 | se.prefix = make([]byte, c.blockSize()+2) | |
58 | _, err := readFull(se.contents, se.prefix) | |
59 | if err != nil { | |
60 | return nil, err | |
61 | } | |
62 | } else if len(se.prefix) != c.blockSize()+2 { | |
63 | return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths") | |
64 | } | |
65 | ||
66 | ocfbResync := OCFBResync | |
67 | if se.MDC { | |
68 | // MDC packets use a different form of OCFB mode. | |
69 | ocfbResync = OCFBNoResync | |
70 | } | |
71 | ||
72 | s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) | |
73 | if s == nil { | |
74 | return nil, errors.ErrKeyIncorrect | |
75 | } | |
76 | ||
77 | plaintext := cipher.StreamReader{S: s, R: se.contents} | |
78 | ||
79 | if se.MDC { | |
80 | // MDC packets have an embedded hash that we need to check. | |
81 | h := sha1.New() | |
82 | h.Write(se.prefix) | |
83 | return &seMDCReader{in: plaintext, h: h}, nil | |
84 | } | |
85 | ||
86 | // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. | |
87 | return seReader{plaintext}, nil | |
88 | } | |
89 | ||
90 | // seReader wraps an io.Reader with a no-op Close method. | |
91 | type seReader struct { | |
92 | in io.Reader | |
93 | } | |
94 | ||
95 | func (ser seReader) Read(buf []byte) (int, error) { | |
96 | return ser.in.Read(buf) | |
97 | } | |
98 | ||
99 | func (ser seReader) Close() error { | |
100 | return nil | |
101 | } | |
102 | ||
103 | const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size | |
104 | ||
105 | // An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold | |
106 | // of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an | |
107 | // MDC packet containing a hash of the previous contents which is checked | |
108 | // against the running hash. See RFC 4880, section 5.13. | |
109 | type seMDCReader struct { | |
110 | in io.Reader | |
111 | h hash.Hash | |
112 | trailer [mdcTrailerSize]byte | |
113 | scratch [mdcTrailerSize]byte | |
114 | trailerUsed int | |
115 | error bool | |
116 | eof bool | |
117 | } | |
118 | ||
119 | func (ser *seMDCReader) Read(buf []byte) (n int, err error) { | |
120 | if ser.error { | |
121 | err = io.ErrUnexpectedEOF | |
122 | return | |
123 | } | |
124 | if ser.eof { | |
125 | err = io.EOF | |
126 | return | |
127 | } | |
128 | ||
129 | // If we haven't yet filled the trailer buffer then we must do that | |
130 | // first. | |
131 | for ser.trailerUsed < mdcTrailerSize { | |
132 | n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) | |
133 | ser.trailerUsed += n | |
134 | if err == io.EOF { | |
135 | if ser.trailerUsed != mdcTrailerSize { | |
136 | n = 0 | |
137 | err = io.ErrUnexpectedEOF | |
138 | ser.error = true | |
139 | return | |
140 | } | |
141 | ser.eof = true | |
142 | n = 0 | |
143 | return | |
144 | } | |
145 | ||
146 | if err != nil { | |
147 | n = 0 | |
148 | return | |
149 | } | |
150 | } | |
151 | ||
152 | // If it's a short read then we read into a temporary buffer and shift | |
153 | // the data into the caller's buffer. | |
154 | if len(buf) <= mdcTrailerSize { | |
155 | n, err = readFull(ser.in, ser.scratch[:len(buf)]) | |
156 | copy(buf, ser.trailer[:n]) | |
157 | ser.h.Write(buf[:n]) | |
158 | copy(ser.trailer[:], ser.trailer[n:]) | |
159 | copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) | |
160 | if n < len(buf) { | |
161 | ser.eof = true | |
162 | err = io.EOF | |
163 | } | |
164 | return | |
165 | } | |
166 | ||
167 | n, err = ser.in.Read(buf[mdcTrailerSize:]) | |
168 | copy(buf, ser.trailer[:]) | |
169 | ser.h.Write(buf[:n]) | |
170 | copy(ser.trailer[:], buf[n:]) | |
171 | ||
172 | if err == io.EOF { | |
173 | ser.eof = true | |
174 | } | |
175 | return | |
176 | } | |
177 | ||
178 | // This is a new-format packet tag byte for a type 19 (MDC) packet. | |
179 | const mdcPacketTagByte = byte(0x80) | 0x40 | 19 | |
180 | ||
181 | func (ser *seMDCReader) Close() error { | |
182 | if ser.error { | |
183 | return errors.SignatureError("error during reading") | |
184 | } | |
185 | ||
186 | for !ser.eof { | |
187 | // We haven't seen EOF so we need to read to the end | |
188 | var buf [1024]byte | |
189 | _, err := ser.Read(buf[:]) | |
190 | if err == io.EOF { | |
191 | break | |
192 | } | |
193 | if err != nil { | |
194 | return errors.SignatureError("error during reading") | |
195 | } | |
196 | } | |
197 | ||
198 | if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { | |
199 | return errors.SignatureError("MDC packet not found") | |
200 | } | |
201 | ser.h.Write(ser.trailer[:2]) | |
202 | ||
203 | final := ser.h.Sum(nil) | |
204 | if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { | |
205 | return errors.SignatureError("hash mismatch") | |
206 | } | |
207 | return nil | |
208 | } | |
209 | ||
210 | // An seMDCWriter writes through to an io.WriteCloser while maintains a running | |
211 | // hash of the data written. On close, it emits an MDC packet containing the | |
212 | // running hash. | |
213 | type seMDCWriter struct { | |
214 | w io.WriteCloser | |
215 | h hash.Hash | |
216 | } | |
217 | ||
218 | func (w *seMDCWriter) Write(buf []byte) (n int, err error) { | |
219 | w.h.Write(buf) | |
220 | return w.w.Write(buf) | |
221 | } | |
222 | ||
223 | func (w *seMDCWriter) Close() (err error) { | |
224 | var buf [mdcTrailerSize]byte | |
225 | ||
226 | buf[0] = mdcPacketTagByte | |
227 | buf[1] = sha1.Size | |
228 | w.h.Write(buf[:2]) | |
229 | digest := w.h.Sum(nil) | |
230 | copy(buf[2:], digest) | |
231 | ||
232 | _, err = w.w.Write(buf[:]) | |
233 | if err != nil { | |
234 | return | |
235 | } | |
236 | return w.w.Close() | |
237 | } | |
238 | ||
239 | // noOpCloser is like an ioutil.NopCloser, but for an io.Writer. | |
240 | type noOpCloser struct { | |
241 | w io.Writer | |
242 | } | |
243 | ||
244 | func (c noOpCloser) Write(data []byte) (n int, err error) { | |
245 | return c.w.Write(data) | |
246 | } | |
247 | ||
248 | func (c noOpCloser) Close() error { | |
249 | return nil | |
250 | } | |
251 | ||
252 | // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet | |
253 | // to w and returns a WriteCloser to which the to-be-encrypted packets can be | |
254 | // written. | |
255 | // If config is nil, sensible defaults will be used. | |
256 | func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { | |
257 | if c.KeySize() != len(key) { | |
258 | return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") | |
259 | } | |
260 | writeCloser := noOpCloser{w} | |
261 | ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) | |
262 | if err != nil { | |
263 | return | |
264 | } | |
265 | ||
266 | _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) | |
267 | if err != nil { | |
268 | return | |
269 | } | |
270 | ||
271 | block := c.new(key) | |
272 | blockSize := block.BlockSize() | |
273 | iv := make([]byte, blockSize) | |
274 | _, err = config.Random().Read(iv) | |
275 | if err != nil { | |
276 | return | |
277 | } | |
278 | s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync) | |
279 | _, err = ciphertext.Write(prefix) | |
280 | if err != nil { | |
281 | return | |
282 | } | |
283 | plaintext := cipher.StreamWriter{S: s, W: ciphertext} | |
284 | ||
285 | h := sha1.New() | |
286 | h.Write(iv) | |
287 | h.Write(iv[blockSize-2:]) | |
288 | contents = &seMDCWriter{w: plaintext, h: h} | |
289 | return | |
290 | } |