OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / golang.org / x / crypto / openpgp / write.go
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 }