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
5package packet
6
7import (
8	"bytes"
9	"crypto/cipher"
10	"io"
11	"strconv"
12
13	"golang.org/x/crypto/openpgp/errors"
14	"golang.org/x/crypto/openpgp/s2k"
15)
16
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.
19const maxSessionKeySizeInBytes = 64
20
21// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
22// 4880, section 5.3.
23type SymmetricKeyEncrypted struct {
24	CipherFunc   CipherFunction
25	s2k          func(out, in []byte)
26	encryptedKey []byte
27}
28
29const symmetricKeyEncryptedVersion = 4
30
31func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
32	// RFC 4880, section 5.3.
33	var buf [2]byte
34	if _, err := readFull(r, buf[:]); err != nil {
35		return err
36	}
37	if buf[0] != symmetricKeyEncryptedVersion {
38		return errors.UnsupportedError("SymmetricKeyEncrypted version")
39	}
40	ske.CipherFunc = CipherFunction(buf[1])
41
42	if ske.CipherFunc.KeySize() == 0 {
43		return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1])))
44	}
45
46	var err error
47	ske.s2k, err = s2k.Parse(r)
48	if err != nil {
49		return err
50	}
51
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 {
57		return err
58	}
59
60	if n != 0 {
61		if n == maxSessionKeySizeInBytes {
62			return errors.UnsupportedError("oversized encrypted session key")
63		}
64		ske.encryptedKey = encryptedKey[:n]
65	}
66
67	return nil
68}
69
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
72// packet.
73func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
74	key := make([]byte, ske.CipherFunc.KeySize())
75	ske.s2k(key, passphrase)
76
77	if len(ske.encryptedKey) == 0 {
78		return key, ske.CipherFunc, nil
79	}
80
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)))
89	}
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) + ")")
94	}
95	return plaintextKey, cipherFunc, nil
96}
97
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.
103func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
104	cipherFunc := config.Cipher()
105	keySize := cipherFunc.KeySize()
106	if keySize == 0 {
107		return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
108	}
109
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()})
115	if err != nil {
116		return
117	}
118	s2kBytes := s2kBuf.Bytes()
119
120	packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
121	err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
122	if err != nil {
123		return
124	}
125
126	var buf [2]byte
127	buf[0] = symmetricKeyEncryptedVersion
128	buf[1] = byte(cipherFunc)
129	_, err = w.Write(buf[:])
130	if err != nil {
131		return
132	}
133	_, err = w.Write(s2kBytes)
134	if err != nil {
135		return
136	}
137
138	sessionKey := make([]byte, keySize)
139	_, err = io.ReadFull(config.Random(), sessionKey)
140	if err != nil {
141		return
142	}
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)
149	if err != nil {
150		return
151	}
152
153	key = sessionKey
154	return
155}
156