diff options
Diffstat (limited to 'vendor/golang.org/x/crypto/openpgp/write.go')
-rw-r--r-- | vendor/golang.org/x/crypto/openpgp/write.go | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/vendor/golang.org/x/crypto/openpgp/write.go b/vendor/golang.org/x/crypto/openpgp/write.go new file mode 100644 index 0000000..65a304c --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/write.go | |||
@@ -0,0 +1,378 @@ | |||
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 openpgp | ||
6 | |||
7 | import ( | ||
8 | "crypto" | ||
9 | "hash" | ||
10 | "io" | ||
11 | "strconv" | ||
12 | "time" | ||
13 | |||
14 | "golang.org/x/crypto/openpgp/armor" | ||
15 | "golang.org/x/crypto/openpgp/errors" | ||
16 | "golang.org/x/crypto/openpgp/packet" | ||
17 | "golang.org/x/crypto/openpgp/s2k" | ||
18 | ) | ||
19 | |||
20 | // DetachSign signs message with the private key from signer (which must | ||
21 | // already have been decrypted) and writes the signature to w. | ||
22 | // If config is nil, sensible defaults will be used. | ||
23 | func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { | ||
24 | return detachSign(w, signer, message, packet.SigTypeBinary, config) | ||
25 | } | ||
26 | |||
27 | // ArmoredDetachSign signs message with the private key from signer (which | ||
28 | // must already have been decrypted) and writes an armored signature to w. | ||
29 | // If config is nil, sensible defaults will be used. | ||
30 | func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { | ||
31 | return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) | ||
32 | } | ||
33 | |||
34 | // DetachSignText signs message (after canonicalising the line endings) with | ||
35 | // the private key from signer (which must already have been decrypted) and | ||
36 | // writes the signature to w. | ||
37 | // If config is nil, sensible defaults will be used. | ||
38 | func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { | ||
39 | return detachSign(w, signer, message, packet.SigTypeText, config) | ||
40 | } | ||
41 | |||
42 | // ArmoredDetachSignText signs message (after canonicalising the line endings) | ||
43 | // with the private key from signer (which must already have been decrypted) | ||
44 | // and writes an armored signature to w. | ||
45 | // If config is nil, sensible defaults will be used. | ||
46 | func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { | ||
47 | return armoredDetachSign(w, signer, message, packet.SigTypeText, config) | ||
48 | } | ||
49 | |||
50 | func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { | ||
51 | out, err := armor.Encode(w, SignatureType, nil) | ||
52 | if err != nil { | ||
53 | return | ||
54 | } | ||
55 | err = detachSign(out, signer, message, sigType, config) | ||
56 | if err != nil { | ||
57 | return | ||
58 | } | ||
59 | return out.Close() | ||
60 | } | ||
61 | |||
62 | func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { | ||
63 | if signer.PrivateKey == nil { | ||
64 | return errors.InvalidArgumentError("signing key doesn't have a private key") | ||
65 | } | ||
66 | if signer.PrivateKey.Encrypted { | ||
67 | return errors.InvalidArgumentError("signing key is encrypted") | ||
68 | } | ||
69 | |||
70 | sig := new(packet.Signature) | ||
71 | sig.SigType = sigType | ||
72 | sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo | ||
73 | sig.Hash = config.Hash() | ||
74 | sig.CreationTime = config.Now() | ||
75 | sig.IssuerKeyId = &signer.PrivateKey.KeyId | ||
76 | |||
77 | h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) | ||
78 | if err != nil { | ||
79 | return | ||
80 | } | ||
81 | io.Copy(wrappedHash, message) | ||
82 | |||
83 | err = sig.Sign(h, signer.PrivateKey, config) | ||
84 | if err != nil { | ||
85 | return | ||
86 | } | ||
87 | |||
88 | return sig.Serialize(w) | ||
89 | } | ||
90 | |||
91 | // FileHints contains metadata about encrypted files. This metadata is, itself, | ||
92 | // encrypted. | ||
93 | type FileHints struct { | ||
94 | // IsBinary can be set to hint that the contents are binary data. | ||
95 | IsBinary bool | ||
96 | // FileName hints at the name of the file that should be written. It's | ||
97 | // truncated to 255 bytes if longer. It may be empty to suggest that the | ||
98 | // file should not be written to disk. It may be equal to "_CONSOLE" to | ||
99 | // suggest the data should not be written to disk. | ||
100 | FileName string | ||
101 | // ModTime contains the modification time of the file, or the zero time if not applicable. | ||
102 | ModTime time.Time | ||
103 | } | ||
104 | |||
105 | // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. | ||
106 | // The resulting WriteCloser must be closed after the contents of the file have | ||
107 | // been written. | ||
108 | // If config is nil, sensible defaults will be used. | ||
109 | func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { | ||
110 | if hints == nil { | ||
111 | hints = &FileHints{} | ||
112 | } | ||
113 | |||
114 | key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) | ||
115 | if err != nil { | ||
116 | return | ||
117 | } | ||
118 | w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) | ||
119 | if err != nil { | ||
120 | return | ||
121 | } | ||
122 | |||
123 | literaldata := w | ||
124 | if algo := config.Compression(); algo != packet.CompressionNone { | ||
125 | var compConfig *packet.CompressionConfig | ||
126 | if config != nil { | ||
127 | compConfig = config.CompressionConfig | ||
128 | } | ||
129 | literaldata, err = packet.SerializeCompressed(w, algo, compConfig) | ||
130 | if err != nil { | ||
131 | return | ||
132 | } | ||
133 | } | ||
134 | |||
135 | var epochSeconds uint32 | ||
136 | if !hints.ModTime.IsZero() { | ||
137 | epochSeconds = uint32(hints.ModTime.Unix()) | ||
138 | } | ||
139 | return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds) | ||
140 | } | ||
141 | |||
142 | // intersectPreferences mutates and returns a prefix of a that contains only | ||
143 | // the values in the intersection of a and b. The order of a is preserved. | ||
144 | func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { | ||
145 | var j int | ||
146 | for _, v := range a { | ||
147 | for _, v2 := range b { | ||
148 | if v == v2 { | ||
149 | a[j] = v | ||
150 | j++ | ||
151 | break | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return a[:j] | ||
157 | } | ||
158 | |||
159 | func hashToHashId(h crypto.Hash) uint8 { | ||
160 | v, ok := s2k.HashToHashId(h) | ||
161 | if !ok { | ||
162 | panic("tried to convert unknown hash") | ||
163 | } | ||
164 | return v | ||
165 | } | ||
166 | |||
167 | // Encrypt encrypts a message to a number of recipients and, optionally, signs | ||
168 | // it. hints contains optional information, that is also encrypted, that aids | ||
169 | // the recipients in processing the message. The resulting WriteCloser must | ||
170 | // be closed after the contents of the file have been written. | ||
171 | // If config is nil, sensible defaults will be used. | ||
172 | func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { | ||
173 | var signer *packet.PrivateKey | ||
174 | if signed != nil { | ||
175 | signKey, ok := signed.signingKey(config.Now()) | ||
176 | if !ok { | ||
177 | return nil, errors.InvalidArgumentError("no valid signing keys") | ||
178 | } | ||
179 | signer = signKey.PrivateKey | ||
180 | if signer == nil { | ||
181 | return nil, errors.InvalidArgumentError("no private key in signing key") | ||
182 | } | ||
183 | if signer.Encrypted { | ||
184 | return nil, errors.InvalidArgumentError("signing key must be decrypted") | ||
185 | } | ||
186 | } | ||
187 | |||
188 | // These are the possible ciphers that we'll use for the message. | ||
189 | candidateCiphers := []uint8{ | ||
190 | uint8(packet.CipherAES128), | ||
191 | uint8(packet.CipherAES256), | ||
192 | uint8(packet.CipherCAST5), | ||
193 | } | ||
194 | // These are the possible hash functions that we'll use for the signature. | ||
195 | candidateHashes := []uint8{ | ||
196 | hashToHashId(crypto.SHA256), | ||
197 | hashToHashId(crypto.SHA512), | ||
198 | hashToHashId(crypto.SHA1), | ||
199 | hashToHashId(crypto.RIPEMD160), | ||
200 | } | ||
201 | // In the event that a recipient doesn't specify any supported ciphers | ||
202 | // or hash functions, these are the ones that we assume that every | ||
203 | // implementation supports. | ||
204 | defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] | ||
205 | defaultHashes := candidateHashes[len(candidateHashes)-1:] | ||
206 | |||
207 | encryptKeys := make([]Key, len(to)) | ||
208 | for i := range to { | ||
209 | var ok bool | ||
210 | encryptKeys[i], ok = to[i].encryptionKey(config.Now()) | ||
211 | if !ok { | ||
212 | return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") | ||
213 | } | ||
214 | |||
215 | sig := to[i].primaryIdentity().SelfSignature | ||
216 | |||
217 | preferredSymmetric := sig.PreferredSymmetric | ||
218 | if len(preferredSymmetric) == 0 { | ||
219 | preferredSymmetric = defaultCiphers | ||
220 | } | ||
221 | preferredHashes := sig.PreferredHash | ||
222 | if len(preferredHashes) == 0 { | ||
223 | preferredHashes = defaultHashes | ||
224 | } | ||
225 | candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) | ||
226 | candidateHashes = intersectPreferences(candidateHashes, preferredHashes) | ||
227 | } | ||
228 | |||
229 | if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { | ||
230 | return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") | ||
231 | } | ||
232 | |||
233 | cipher := packet.CipherFunction(candidateCiphers[0]) | ||
234 | // If the cipher specified by config is a candidate, we'll use that. | ||
235 | configuredCipher := config.Cipher() | ||
236 | for _, c := range candidateCiphers { | ||
237 | cipherFunc := packet.CipherFunction(c) | ||
238 | if cipherFunc == configuredCipher { | ||
239 | cipher = cipherFunc | ||
240 | break | ||
241 | } | ||
242 | } | ||
243 | |||
244 | var hash crypto.Hash | ||
245 | for _, hashId := range candidateHashes { | ||
246 | if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { | ||
247 | hash = h | ||
248 | break | ||
249 | } | ||
250 | } | ||
251 | |||
252 | // If the hash specified by config is a candidate, we'll use that. | ||
253 | if configuredHash := config.Hash(); configuredHash.Available() { | ||
254 | for _, hashId := range candidateHashes { | ||
255 | if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { | ||
256 | hash = h | ||
257 | break | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | |||
262 | if hash == 0 { | ||
263 | hashId := candidateHashes[0] | ||
264 | name, ok := s2k.HashIdToString(hashId) | ||
265 | if !ok { | ||
266 | name = "#" + strconv.Itoa(int(hashId)) | ||
267 | } | ||
268 | return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") | ||
269 | } | ||
270 | |||
271 | symKey := make([]byte, cipher.KeySize()) | ||
272 | if _, err := io.ReadFull(config.Random(), symKey); err != nil { | ||
273 | return nil, err | ||
274 | } | ||
275 | |||
276 | for _, key := range encryptKeys { | ||
277 | if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { | ||
278 | return nil, err | ||
279 | } | ||
280 | } | ||
281 | |||
282 | encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) | ||
283 | if err != nil { | ||
284 | return | ||
285 | } | ||
286 | |||
287 | if signer != nil { | ||
288 | ops := &packet.OnePassSignature{ | ||
289 | SigType: packet.SigTypeBinary, | ||
290 | Hash: hash, | ||
291 | PubKeyAlgo: signer.PubKeyAlgo, | ||
292 | KeyId: signer.KeyId, | ||
293 | IsLast: true, | ||
294 | } | ||
295 | if err := ops.Serialize(encryptedData); err != nil { | ||
296 | return nil, err | ||
297 | } | ||
298 | } | ||
299 | |||
300 | if hints == nil { | ||
301 | hints = &FileHints{} | ||
302 | } | ||
303 | |||
304 | w := encryptedData | ||
305 | if signer != nil { | ||
306 | // If we need to write a signature packet after the literal | ||
307 | // data then we need to stop literalData from closing | ||
308 | // encryptedData. | ||
309 | w = noOpCloser{encryptedData} | ||
310 | |||
311 | } | ||
312 | var epochSeconds uint32 | ||
313 | if !hints.ModTime.IsZero() { | ||
314 | epochSeconds = uint32(hints.ModTime.Unix()) | ||
315 | } | ||
316 | literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) | ||
317 | if err != nil { | ||
318 | return nil, err | ||
319 | } | ||
320 | |||
321 | if signer != nil { | ||
322 | return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil | ||
323 | } | ||
324 | return literalData, nil | ||
325 | } | ||
326 | |||
327 | // signatureWriter hashes the contents of a message while passing it along to | ||
328 | // literalData. When closed, it closes literalData, writes a signature packet | ||
329 | // to encryptedData and then also closes encryptedData. | ||
330 | type signatureWriter struct { | ||
331 | encryptedData io.WriteCloser | ||
332 | literalData io.WriteCloser | ||
333 | hashType crypto.Hash | ||
334 | h hash.Hash | ||
335 | signer *packet.PrivateKey | ||
336 | config *packet.Config | ||
337 | } | ||
338 | |||
339 | func (s signatureWriter) Write(data []byte) (int, error) { | ||
340 | s.h.Write(data) | ||
341 | return s.literalData.Write(data) | ||
342 | } | ||
343 | |||
344 | func (s signatureWriter) Close() error { | ||
345 | sig := &packet.Signature{ | ||
346 | SigType: packet.SigTypeBinary, | ||
347 | PubKeyAlgo: s.signer.PubKeyAlgo, | ||
348 | Hash: s.hashType, | ||
349 | CreationTime: s.config.Now(), | ||
350 | IssuerKeyId: &s.signer.KeyId, | ||
351 | } | ||
352 | |||
353 | if err := sig.Sign(s.h, s.signer, s.config); err != nil { | ||
354 | return err | ||
355 | } | ||
356 | if err := s.literalData.Close(); err != nil { | ||
357 | return err | ||
358 | } | ||
359 | if err := sig.Serialize(s.encryptedData); err != nil { | ||
360 | return err | ||
361 | } | ||
362 | return s.encryptedData.Close() | ||
363 | } | ||
364 | |||
365 | // noOpCloser is like an ioutil.NopCloser, but for an io.Writer. | ||
366 | // TODO: we have two of these in OpenPGP packages alone. This probably needs | ||
367 | // to be promoted somewhere more common. | ||
368 | type noOpCloser struct { | ||
369 | w io.Writer | ||
370 | } | ||
371 | |||
372 | func (c noOpCloser) Write(data []byte) (n int, err error) { | ||
373 | return c.w.Write(data) | ||
374 | } | ||
375 | |||
376 | func (c noOpCloser) Close() error { | ||
377 | return nil | ||
378 | } | ||