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.
10 error_ "crypto/openpgp/error"
11 "crypto/openpgp/packet"
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)
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)
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)
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)
47 func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err error) {
48 out, err := armor.Encode(w, SignatureType, nil)
52 err = detachSign(out, signer, message, sigType)
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")
63 if signer.PrivateKey.Encrypted {
64 return error_.InvalidArgumentError("signing key is encrypted")
67 sig := new(packet.Signature)
69 sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
70 sig.Hash = crypto.SHA256
71 sig.CreationTime = time.Now()
72 sig.IssuerKeyId = &signer.PrivateKey.KeyId
74 h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
78 io.Copy(wrappedHash, message)
80 err = sig.Sign(h, signer.PrivateKey)
85 return sig.Serialize(w)
88 // FileHints contains metadata about encrypted files. This metadata is, itself,
90 type FileHints struct {
91 // IsBinary can be set to hint that the contents are binary data.
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.
98 // ModTime contains the modification time of the file, or the zero time if not applicable.
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
105 func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err error) {
110 key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128)
114 w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, packet.CipherAES128, key)
118 var epochSeconds uint32
119 if !hints.ModTime.IsZero() {
120 epochSeconds = uint32(hints.ModTime.Unix())
122 return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
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) {
129 for _, v := range a {
130 for _, v2 := range b {
142 func hashToHashId(h crypto.Hash) uint8 {
143 v, ok := s2k.HashToHashId(h)
145 panic("tried to convert unknown hash")
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
157 signer = signed.signingKey().PrivateKey
158 if signer == nil || signer.Encrypted {
159 return nil, error_.InvalidArgumentError("signing key must be decrypted")
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),
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),
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:]
182 encryptKeys := make([]Key, len(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")
189 sig := to[i].primaryIdentity().SelfSignature
191 preferredSymmetric := sig.PreferredSymmetric
192 if len(preferredSymmetric) == 0 {
193 preferredSymmetric = defaultCiphers
195 preferredHashes := sig.PreferredHash
196 if len(preferredHashes) == 0 {
197 preferredHashes = defaultHashes
199 candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
200 candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
203 if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
204 return nil, error_.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
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 {
214 for _, key := range encryptKeys {
215 if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil {
220 encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey)
226 ops := &packet.OnePassSignature{
227 SigType: packet.SigTypeBinary,
229 PubKeyAlgo: signer.PubKeyAlgo,
233 if err := ops.Serialize(encryptedData); err != nil {
244 // If we need to write a signature packet after the literal
245 // data then we need to stop literalData from closing
247 w = noOpCloser{encryptedData}
250 var epochSeconds uint32
251 if !hints.ModTime.IsZero() {
252 epochSeconds = uint32(hints.ModTime.Unix())
254 literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
260 return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil
262 return literalData, nil
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
273 signer *packet.PrivateKey
276 func (s signatureWriter) Write(data []byte) (int, error) {
278 return s.literalData.Write(data)
281 func (s signatureWriter) Close() error {
282 sig := &packet.Signature{
283 SigType: packet.SigTypeBinary,
284 PubKeyAlgo: s.signer.PubKeyAlgo,
286 CreationTime: time.Now(),
287 IssuerKeyId: &s.signer.KeyId,
290 if err := sig.Sign(s.h, s.signer); err != nil {
293 if err := s.literalData.Close(); err != nil {
296 if err := sig.Serialize(s.encryptedData); err != nil {
299 return s.encryptedData.Close()
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 {
309 func (c noOpCloser) Write(data []byte) (n int, err error) {
310 return c.w.Write(data)
313 func (c noOpCloser) Close() error {