main
Raw Download raw file
  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	"crypto/sha256"
 11	"io"
 12	"strconv"
 13
 14	"github.com/ProtonMail/go-crypto/openpgp/errors"
 15	"github.com/ProtonMail/go-crypto/openpgp/s2k"
 16	"golang.org/x/crypto/hkdf"
 17)
 18
 19// This is the largest session key that we'll support. Since at most 256-bit cipher
 20// is supported in OpenPGP, this is large enough to contain also the auth tag.
 21const maxSessionKeySizeInBytes = 64
 22
 23// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
 24// 4880, section 5.3.
 25type SymmetricKeyEncrypted struct {
 26	Version      int
 27	CipherFunc   CipherFunction
 28	Mode         AEADMode
 29	s2k          func(out, in []byte)
 30	iv           []byte
 31	encryptedKey []byte // Contains also the authentication tag for AEAD
 32}
 33
 34// parse parses an SymmetricKeyEncrypted packet as specified in
 35// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#name-symmetric-key-encrypted-ses
 36func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
 37	var buf [1]byte
 38
 39	// Version
 40	if _, err := readFull(r, buf[:]); err != nil {
 41		return err
 42	}
 43	ske.Version = int(buf[0])
 44	if ske.Version != 4 && ske.Version != 5 && ske.Version != 6 {
 45		return errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
 46	}
 47
 48	if V5Disabled && ske.Version == 5 {
 49		return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
 50	}
 51
 52	if ske.Version > 5 {
 53		// Scalar octet count
 54		if _, err := readFull(r, buf[:]); err != nil {
 55			return err
 56		}
 57	}
 58
 59	// Cipher function
 60	if _, err := readFull(r, buf[:]); err != nil {
 61		return err
 62	}
 63	ske.CipherFunc = CipherFunction(buf[0])
 64	if !ske.CipherFunc.IsSupported() {
 65		return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[0])))
 66	}
 67
 68	if ske.Version >= 5 {
 69		// AEAD mode
 70		if _, err := readFull(r, buf[:]); err != nil {
 71			return errors.StructuralError("cannot read AEAD octet from packet")
 72		}
 73		ske.Mode = AEADMode(buf[0])
 74	}
 75
 76	if ske.Version > 5 {
 77		// Scalar octet count
 78		if _, err := readFull(r, buf[:]); err != nil {
 79			return err
 80		}
 81	}
 82
 83	var err error
 84	if ske.s2k, err = s2k.Parse(r); err != nil {
 85		if _, ok := err.(errors.ErrDummyPrivateKey); ok {
 86			return errors.UnsupportedError("missing key GNU extension in session key")
 87		}
 88		return err
 89	}
 90
 91	if ske.Version >= 5 {
 92		// AEAD IV
 93		iv := make([]byte, ske.Mode.IvLength())
 94		_, err := readFull(r, iv)
 95		if err != nil {
 96			return errors.StructuralError("cannot read AEAD IV")
 97		}
 98
 99		ske.iv = iv
100	}
101
102	encryptedKey := make([]byte, maxSessionKeySizeInBytes)
103	// The session key may follow. We just have to try and read to find
104	// out. If it exists then we limit it to maxSessionKeySizeInBytes.
105	n, err := readFull(r, encryptedKey)
106	if err != nil && err != io.ErrUnexpectedEOF {
107		return err
108	}
109
110	if n != 0 {
111		if n == maxSessionKeySizeInBytes {
112			return errors.UnsupportedError("oversized encrypted session key")
113		}
114		ske.encryptedKey = encryptedKey[:n]
115	}
116	return nil
117}
118
119// Decrypt attempts to decrypt an encrypted session key and returns the key and
120// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data
121// packet.
122func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
123	key := make([]byte, ske.CipherFunc.KeySize())
124	ske.s2k(key, passphrase)
125	if len(ske.encryptedKey) == 0 {
126		return key, ske.CipherFunc, nil
127	}
128	switch ske.Version {
129	case 4:
130		plaintextKey, cipherFunc, err := ske.decryptV4(key)
131		return plaintextKey, cipherFunc, err
132	case 5, 6:
133		plaintextKey, err := ske.aeadDecrypt(ske.Version, key)
134		return plaintextKey, CipherFunction(0), err
135	}
136	err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
137	return nil, CipherFunction(0), err
138}
139
140func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction, error) {
141	// the IV is all zeros
142	iv := make([]byte, ske.CipherFunc.blockSize())
143	c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv)
144	plaintextKey := make([]byte, len(ske.encryptedKey))
145	c.XORKeyStream(plaintextKey, ske.encryptedKey)
146	cipherFunc := CipherFunction(plaintextKey[0])
147	if cipherFunc.blockSize() == 0 {
148		return nil, ske.CipherFunc, errors.UnsupportedError(
149			"unknown cipher: " + strconv.Itoa(int(cipherFunc)))
150	}
151	plaintextKey = plaintextKey[1:]
152	if len(plaintextKey) != cipherFunc.KeySize() {
153		return nil, cipherFunc, errors.StructuralError(
154			"length of decrypted key not equal to cipher keysize")
155	}
156	return plaintextKey, cipherFunc, nil
157}
158
159func (ske *SymmetricKeyEncrypted) aeadDecrypt(version int, key []byte) ([]byte, error) {
160	adata := []byte{0xc3, byte(version), byte(ske.CipherFunc), byte(ske.Mode)}
161	aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata, version)
162
163	plaintextKey, err := aead.Open(nil, ske.iv, ske.encryptedKey, adata)
164	if err != nil {
165		return nil, err
166	}
167	return plaintextKey, nil
168}
169
170// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w.
171// The packet contains a random session key, encrypted by a key derived from
172// the given passphrase. The session key is returned and must be passed to
173// SerializeSymmetricallyEncrypted.
174// If config is nil, sensible defaults will be used.
175func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
176	cipherFunc := config.Cipher()
177
178	sessionKey := make([]byte, cipherFunc.KeySize())
179	_, err = io.ReadFull(config.Random(), sessionKey)
180	if err != nil {
181		return
182	}
183
184	err = SerializeSymmetricKeyEncryptedReuseKey(w, sessionKey, passphrase, config)
185	if err != nil {
186		return
187	}
188
189	key = sessionKey
190	return
191}
192
193// SerializeSymmetricKeyEncryptedReuseKey serializes a symmetric key packet to w.
194// The packet contains the given session key, encrypted by a key derived from
195// the given passphrase. The returned session key must be passed to
196// SerializeSymmetricallyEncrypted.
197// If config is nil, sensible defaults will be used.
198// Deprecated: Use SerializeSymmetricKeyEncryptedAEADReuseKey instead.
199func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) {
200	return SerializeSymmetricKeyEncryptedAEADReuseKey(w, sessionKey, passphrase, config.AEAD() != nil, config)
201}
202
203// SerializeSymmetricKeyEncryptedAEADReuseKey serializes a symmetric key packet to w.
204// The packet contains the given session key, encrypted by a key derived from
205// the given passphrase. The returned session key must be passed to
206// SerializeSymmetricallyEncrypted.
207// If aeadSupported is set, SKESK v6 is used, otherwise v4.
208// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
209// If config is nil, sensible defaults will be used.
210func SerializeSymmetricKeyEncryptedAEADReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, aeadSupported bool, config *Config) (err error) {
211	var version int
212	if aeadSupported {
213		version = 6
214	} else {
215		version = 4
216	}
217	cipherFunc := config.Cipher()
218	// cipherFunc must be AES
219	if !cipherFunc.IsSupported() || cipherFunc < CipherAES128 || cipherFunc > CipherAES256 {
220		return errors.UnsupportedError("unsupported cipher: " + strconv.Itoa(int(cipherFunc)))
221	}
222
223	keySize := cipherFunc.KeySize()
224	s2kBuf := new(bytes.Buffer)
225	keyEncryptingKey := make([]byte, keySize)
226	// s2k.Serialize salts and stretches the passphrase, and writes the
227	// resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf.
228	err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, config.S2K())
229	if err != nil {
230		return
231	}
232	s2kBytes := s2kBuf.Bytes()
233
234	var packetLength int
235	switch version {
236	case 4:
237		packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
238	case 5, 6:
239		ivLen := config.AEAD().Mode().IvLength()
240		tagLen := config.AEAD().Mode().TagLength()
241		packetLength = 3 + len(s2kBytes) + ivLen + keySize + tagLen
242	}
243	if version > 5 {
244		packetLength += 2 // additional octet count fields
245	}
246
247	err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
248	if err != nil {
249		return
250	}
251
252	// Symmetric Key Encrypted Version
253	buf := []byte{byte(version)}
254
255	if version > 5 {
256		// Scalar octet count
257		buf = append(buf, byte(3+len(s2kBytes)+config.AEAD().Mode().IvLength()))
258	}
259
260	// Cipher function
261	buf = append(buf, byte(cipherFunc))
262
263	if version >= 5 {
264		// AEAD mode
265		buf = append(buf, byte(config.AEAD().Mode()))
266	}
267	if version > 5 {
268		// Scalar octet count
269		buf = append(buf, byte(len(s2kBytes)))
270	}
271	_, err = w.Write(buf)
272	if err != nil {
273		return
274	}
275	_, err = w.Write(s2kBytes)
276	if err != nil {
277		return
278	}
279
280	switch version {
281	case 4:
282		iv := make([]byte, cipherFunc.blockSize())
283		c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv)
284		encryptedCipherAndKey := make([]byte, keySize+1)
285		c.XORKeyStream(encryptedCipherAndKey, buf[1:])
286		c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey)
287		_, err = w.Write(encryptedCipherAndKey)
288		if err != nil {
289			return
290		}
291	case 5, 6:
292		mode := config.AEAD().Mode()
293		adata := []byte{0xc3, byte(version), byte(cipherFunc), byte(mode)}
294		aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata, version)
295
296		// Sample iv using random reader
297		iv := make([]byte, config.AEAD().Mode().IvLength())
298		_, err = io.ReadFull(config.Random(), iv)
299		if err != nil {
300			return
301		}
302		// Seal and write (encryptedData includes auth. tag)
303
304		encryptedData := aead.Seal(nil, iv, sessionKey, adata)
305		_, err = w.Write(iv)
306		if err != nil {
307			return
308		}
309		_, err = w.Write(encryptedData)
310		if err != nil {
311			return
312		}
313	}
314
315	return
316}
317
318func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte, version int) (aead cipher.AEAD) {
319	var blockCipher cipher.Block
320	if version > 5 {
321		hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData)
322
323		encryptionKey := make([]byte, c.KeySize())
324		_, _ = readFull(hkdfReader, encryptionKey)
325
326		blockCipher = c.new(encryptionKey)
327	} else {
328		blockCipher = c.new(inputKey)
329	}
330	return mode.new(blockCipher)
331}