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.
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"
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)
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)
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)
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)
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)
55 err = detachSign(out, signer, message, sigType, config)
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")
66 if signer.PrivateKey.Encrypted {
67 return errors.InvalidArgumentError("signing key is encrypted")
70 sig := new(packet.Signature)
72 sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
73 sig.Hash = config.Hash()
74 sig.CreationTime = config.Now()
75 sig.IssuerKeyId = &signer.PrivateKey.KeyId
77 h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
81 io.Copy(wrappedHash, message)
83 err = sig.Sign(h, signer.PrivateKey, config)
88 return sig.Serialize(w)
91 // FileHints contains metadata about encrypted files. This metadata is, itself,
93 type FileHints struct {
94 // IsBinary can be set to hint that the contents are binary data.
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.
101 // ModTime contains the modification time of the file, or the zero time if not applicable.
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
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) {
114 key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
118 w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
124 if algo := config.Compression(); algo != packet.CompressionNone {
125 var compConfig *packet.CompressionConfig
127 compConfig = config.CompressionConfig
129 literaldata, err = packet.SerializeCompressed(w, algo, compConfig)
135 var epochSeconds uint32
136 if !hints.ModTime.IsZero() {
137 epochSeconds = uint32(hints.ModTime.Unix())
139 return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds)
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) {
146 for _, v := range a {
147 for _, v2 := range b {
159 func hashToHashId(h crypto.Hash) uint8 {
160 v, ok := s2k.HashToHashId(h)
162 panic("tried to convert unknown hash")
167 // writeAndSign writes the data as a payload package and, optionally, signs
168 // it. hints contains optional information, that is also encrypted,
169 // that aids the recipients in processing the message. The resulting
170 // WriteCloser must be closed after the contents of the file have been
171 // written. If config is nil, sensible defaults will be used.
172 func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
173 var signer *packet.PrivateKey
175 signKey, ok := signed.signingKey(config.Now())
177 return nil, errors.InvalidArgumentError("no valid signing keys")
179 signer = signKey.PrivateKey
181 return nil, errors.InvalidArgumentError("no private key in signing key")
183 if signer.Encrypted {
184 return nil, errors.InvalidArgumentError("signing key must be decrypted")
189 for _, hashId := range candidateHashes {
190 if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() {
196 // If the hash specified by config is a candidate, we'll use that.
197 if configuredHash := config.Hash(); configuredHash.Available() {
198 for _, hashId := range candidateHashes {
199 if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash {
207 hashId := candidateHashes[0]
208 name, ok := s2k.HashIdToString(hashId)
210 name = "#" + strconv.Itoa(int(hashId))
212 return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
216 ops := &packet.OnePassSignature{
217 SigType: packet.SigTypeBinary,
219 PubKeyAlgo: signer.PubKeyAlgo,
223 if err := ops.Serialize(payload); err != nil {
234 // If we need to write a signature packet after the literal
235 // data then we need to stop literalData from closing
240 var epochSeconds uint32
241 if !hints.ModTime.IsZero() {
242 epochSeconds = uint32(hints.ModTime.Unix())
244 literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
250 return signatureWriter{payload, literalData, hash, hash.New(), signer, config}, nil
252 return literalData, nil
255 // Encrypt encrypts a message to a number of recipients and, optionally, signs
256 // it. hints contains optional information, that is also encrypted, that aids
257 // the recipients in processing the message. The resulting WriteCloser must
258 // be closed after the contents of the file have been written.
259 // If config is nil, sensible defaults will be used.
260 func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
262 return nil, errors.InvalidArgumentError("no encryption recipient provided")
265 // These are the possible ciphers that we'll use for the message.
266 candidateCiphers := []uint8{
267 uint8(packet.CipherAES128),
268 uint8(packet.CipherAES256),
269 uint8(packet.CipherCAST5),
271 // These are the possible hash functions that we'll use for the signature.
272 candidateHashes := []uint8{
273 hashToHashId(crypto.SHA256),
274 hashToHashId(crypto.SHA384),
275 hashToHashId(crypto.SHA512),
276 hashToHashId(crypto.SHA1),
277 hashToHashId(crypto.RIPEMD160),
279 // In the event that a recipient doesn't specify any supported ciphers
280 // or hash functions, these are the ones that we assume that every
281 // implementation supports.
282 defaultCiphers := candidateCiphers[len(candidateCiphers)-1:]
283 defaultHashes := candidateHashes[len(candidateHashes)-1:]
285 encryptKeys := make([]Key, len(to))
288 encryptKeys[i], ok = to[i].encryptionKey(config.Now())
290 return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
293 sig := to[i].primaryIdentity().SelfSignature
295 preferredSymmetric := sig.PreferredSymmetric
296 if len(preferredSymmetric) == 0 {
297 preferredSymmetric = defaultCiphers
299 preferredHashes := sig.PreferredHash
300 if len(preferredHashes) == 0 {
301 preferredHashes = defaultHashes
303 candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
304 candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
307 if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
308 return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
311 cipher := packet.CipherFunction(candidateCiphers[0])
312 // If the cipher specified by config is a candidate, we'll use that.
313 configuredCipher := config.Cipher()
314 for _, c := range candidateCiphers {
315 cipherFunc := packet.CipherFunction(c)
316 if cipherFunc == configuredCipher {
322 symKey := make([]byte, cipher.KeySize())
323 if _, err := io.ReadFull(config.Random(), symKey); err != nil {
327 for _, key := range encryptKeys {
328 if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil {
333 payload, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
338 return writeAndSign(payload, candidateHashes, signed, hints, config)
341 // Sign signs a message. The resulting WriteCloser must be closed after the
342 // contents of the file have been written. hints contains optional information
343 // that aids the recipients in processing the message.
344 // If config is nil, sensible defaults will be used.
345 func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) {
347 return nil, errors.InvalidArgumentError("no signer provided")
350 // These are the possible hash functions that we'll use for the signature.
351 candidateHashes := []uint8{
352 hashToHashId(crypto.SHA256),
353 hashToHashId(crypto.SHA384),
354 hashToHashId(crypto.SHA512),
355 hashToHashId(crypto.SHA1),
356 hashToHashId(crypto.RIPEMD160),
358 defaultHashes := candidateHashes[len(candidateHashes)-1:]
359 preferredHashes := signed.primaryIdentity().SelfSignature.PreferredHash
360 if len(preferredHashes) == 0 {
361 preferredHashes = defaultHashes
363 candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
364 return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, config)
367 // signatureWriter hashes the contents of a message while passing it along to
368 // literalData. When closed, it closes literalData, writes a signature packet
369 // to encryptedData and then also closes encryptedData.
370 type signatureWriter struct {
371 encryptedData io.WriteCloser
372 literalData io.WriteCloser
375 signer *packet.PrivateKey
376 config *packet.Config
379 func (s signatureWriter) Write(data []byte) (int, error) {
381 return s.literalData.Write(data)
384 func (s signatureWriter) Close() error {
385 sig := &packet.Signature{
386 SigType: packet.SigTypeBinary,
387 PubKeyAlgo: s.signer.PubKeyAlgo,
389 CreationTime: s.config.Now(),
390 IssuerKeyId: &s.signer.KeyId,
393 if err := sig.Sign(s.h, s.signer, s.config); err != nil {
396 if err := s.literalData.Close(); err != nil {
399 if err := sig.Serialize(s.encryptedData); err != nil {
402 return s.encryptedData.Close()
405 // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
406 // TODO: we have two of these in OpenPGP packages alone. This probably needs
407 // to be promoted somewhere more common.
408 type noOpCloser struct {
412 func (c noOpCloser) Write(data []byte) (n int, err error) {
413 return c.w.Write(data)
416 func (c noOpCloser) Close() error {