OSDN Git Service

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