main
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}