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.
13 "golang.org/x/crypto/openpgp/errors"
14 "golang.org/x/crypto/openpgp/s2k"
17 // This is the largest session key that we'll support. Since no 512-bit cipher
18 // has even been seriously used, this is comfortably large.
19 const maxSessionKeySizeInBytes = 64
21 // SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
23 type SymmetricKeyEncrypted struct {
24 CipherFunc CipherFunction
25 s2k func(out, in []byte)
29 const symmetricKeyEncryptedVersion = 4
31 func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
32 // RFC 4880, section 5.3.
34 if _, err := readFull(r, buf[:]); err != nil {
37 if buf[0] != symmetricKeyEncryptedVersion {
38 return errors.UnsupportedError("SymmetricKeyEncrypted version")
40 ske.CipherFunc = CipherFunction(buf[1])
42 if ske.CipherFunc.KeySize() == 0 {
43 return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1])))
47 ske.s2k, err = s2k.Parse(r)
52 encryptedKey := make([]byte, maxSessionKeySizeInBytes)
53 // The session key may follow. We just have to try and read to find
54 // out. If it exists then we limit it to maxSessionKeySizeInBytes.
55 n, err := readFull(r, encryptedKey)
56 if err != nil && err != io.ErrUnexpectedEOF {
61 if n == maxSessionKeySizeInBytes {
62 return errors.UnsupportedError("oversized encrypted session key")
64 ske.encryptedKey = encryptedKey[:n]
70 // Decrypt attempts to decrypt an encrypted session key and returns the key and
71 // the cipher to use when decrypting a subsequent Symmetrically Encrypted Data
73 func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
74 key := make([]byte, ske.CipherFunc.KeySize())
75 ske.s2k(key, passphrase)
77 if len(ske.encryptedKey) == 0 {
78 return key, ske.CipherFunc, nil
81 // the IV is all zeros
82 iv := make([]byte, ske.CipherFunc.blockSize())
83 c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv)
84 plaintextKey := make([]byte, len(ske.encryptedKey))
85 c.XORKeyStream(plaintextKey, ske.encryptedKey)
86 cipherFunc := CipherFunction(plaintextKey[0])
87 if cipherFunc.blockSize() == 0 {
88 return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
90 plaintextKey = plaintextKey[1:]
91 if l, cipherKeySize := len(plaintextKey), cipherFunc.KeySize(); l != cipherFunc.KeySize() {
92 return nil, cipherFunc, errors.StructuralError("length of decrypted key (" + strconv.Itoa(l) + ") " +
93 "not equal to cipher keysize (" + strconv.Itoa(cipherKeySize) + ")")
95 return plaintextKey, cipherFunc, nil
98 // SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The
99 // packet contains a random session key, encrypted by a key derived from the
100 // given passphrase. The session key is returned and must be passed to
101 // SerializeSymmetricallyEncrypted.
102 // If config is nil, sensible defaults will be used.
103 func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
104 cipherFunc := config.Cipher()
105 keySize := cipherFunc.KeySize()
107 return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
110 s2kBuf := new(bytes.Buffer)
111 keyEncryptingKey := make([]byte, keySize)
112 // s2k.Serialize salts and stretches the passphrase, and writes the
113 // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf.
114 err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()})
118 s2kBytes := s2kBuf.Bytes()
120 packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
121 err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
127 buf[0] = symmetricKeyEncryptedVersion
128 buf[1] = byte(cipherFunc)
129 _, err = w.Write(buf[:])
133 _, err = w.Write(s2kBytes)
138 sessionKey := make([]byte, keySize)
139 _, err = io.ReadFull(config.Random(), sessionKey)
143 iv := make([]byte, cipherFunc.blockSize())
144 c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv)
145 encryptedCipherAndKey := make([]byte, keySize+1)
146 c.XORKeyStream(encryptedCipherAndKey, buf[1:])
147 c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey)
148 _, err = w.Write(encryptedCipherAndKey)