main
1// Copyright (C) 2019 ProtonTech AG
2
3package packet
4
5import (
6 "io"
7
8 "github.com/ProtonMail/go-crypto/openpgp/errors"
9 "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
10)
11
12// AEADEncrypted represents an AEAD Encrypted Packet.
13// See https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-aead-encrypted-data-packet-t
14type AEADEncrypted struct {
15 cipher CipherFunction
16 mode AEADMode
17 chunkSizeByte byte
18 Contents io.Reader // Encrypted chunks and tags
19 initialNonce []byte // Referred to as IV in RFC4880-bis
20}
21
22// Only currently defined version
23const aeadEncryptedVersion = 1
24
25func (ae *AEADEncrypted) parse(buf io.Reader) error {
26 headerData := make([]byte, 4)
27 if n, err := io.ReadFull(buf, headerData); n < 4 {
28 return errors.AEADError("could not read aead header:" + err.Error())
29 }
30 // Read initial nonce
31 mode := AEADMode(headerData[2])
32 nonceLen := mode.IvLength()
33
34 // This packet supports only EAX and OCB
35 // https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-aead-encrypted-data-packet-t
36 if nonceLen == 0 || mode > AEADModeOCB {
37 return errors.AEADError("unknown mode")
38 }
39
40 initialNonce := make([]byte, nonceLen)
41 if n, err := io.ReadFull(buf, initialNonce); n < nonceLen {
42 return errors.AEADError("could not read aead nonce:" + err.Error())
43 }
44 ae.Contents = buf
45 ae.initialNonce = initialNonce
46 c := headerData[1]
47 if _, ok := algorithm.CipherById[c]; !ok {
48 return errors.UnsupportedError("unknown cipher: " + string(c))
49 }
50 ae.cipher = CipherFunction(c)
51 ae.mode = mode
52 ae.chunkSizeByte = headerData[3]
53 return nil
54}
55
56// Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or
57// an error.
58func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) {
59 return ae.decrypt(key)
60}
61
62// decrypt prepares an aeadCrypter and returns a ReadCloser from which
63// decrypted bytes can be read (see aeadDecrypter.Read()).
64func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) {
65 blockCipher := ae.cipher.new(key)
66 aead := ae.mode.new(blockCipher)
67 // Carry the first tagLen bytes
68 chunkSize := decodeAEADChunkSize(ae.chunkSizeByte)
69 tagLen := ae.mode.TagLength()
70 chunkBytes := make([]byte, chunkSize+tagLen*2)
71 peekedBytes := chunkBytes[chunkSize+tagLen:]
72 n, err := io.ReadFull(ae.Contents, peekedBytes)
73 if n < tagLen || (err != nil && err != io.EOF) {
74 return nil, errors.AEADError("Not enough data to decrypt:" + err.Error())
75 }
76
77 return &aeadDecrypter{
78 aeadCrypter: aeadCrypter{
79 aead: aead,
80 chunkSize: chunkSize,
81 nonce: ae.initialNonce,
82 associatedData: ae.associatedData(),
83 chunkIndex: make([]byte, 8),
84 packetTag: packetTypeAEADEncrypted,
85 },
86 reader: ae.Contents,
87 chunkBytes: chunkBytes,
88 peekedBytes: peekedBytes,
89 }, nil
90}
91
92// associatedData for chunks: tag, version, cipher, mode, chunk size byte
93func (ae *AEADEncrypted) associatedData() []byte {
94 return []byte{
95 0xD4,
96 aeadEncryptedVersion,
97 byte(ae.cipher),
98 byte(ae.mode),
99 ae.chunkSizeByte}
100}