cheat/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aea...

157 lines
4.8 KiB
Go

// Copyright 2023 Proton AG. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package packet
import (
"crypto/cipher"
"crypto/sha256"
"io"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/hkdf"
)
// parseAead parses a V2 SEIPD packet (AEAD) as specified in
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error {
headerData := make([]byte, 3)
if n, err := io.ReadFull(r, headerData); n < 3 {
return errors.StructuralError("could not read aead header: " + err.Error())
}
// Cipher
se.Cipher = CipherFunction(headerData[0])
// cipherFunc must have block size 16 to use AEAD
if se.Cipher.blockSize() != 16 {
return errors.UnsupportedError("invalid aead cipher: " + string(se.Cipher))
}
// Mode
se.Mode = AEADMode(headerData[1])
if se.Mode.TagLength() == 0 {
return errors.UnsupportedError("unknown aead mode: " + string(se.Mode))
}
// Chunk size
se.ChunkSizeByte = headerData[2]
if se.ChunkSizeByte > 16 {
return errors.UnsupportedError("invalid aead chunk size byte: " + string(se.ChunkSizeByte))
}
// Salt
if n, err := io.ReadFull(r, se.Salt[:]); n < aeadSaltSize {
return errors.StructuralError("could not read aead salt: " + err.Error())
}
return nil
}
// associatedData for chunks: tag, version, cipher, mode, chunk size byte
func (se *SymmetricallyEncrypted) associatedData() []byte {
return []byte{
0xD2,
symmetricallyEncryptedVersionAead,
byte(se.Cipher),
byte(se.Mode),
se.ChunkSizeByte,
}
}
// decryptAead decrypts a V2 SEIPD packet (AEAD) as specified in
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, error) {
aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
// Carry the first tagLen bytes
tagLen := se.Mode.TagLength()
peekedBytes := make([]byte, tagLen)
n, err := io.ReadFull(se.Contents, peekedBytes)
if n < tagLen || (err != nil && err != io.EOF) {
return nil, errors.StructuralError("not enough data to decrypt:" + err.Error())
}
return &aeadDecrypter{
aeadCrypter: aeadCrypter{
aead: aead,
chunkSize: decodeAEADChunkSize(se.ChunkSizeByte),
initialNonce: nonce,
associatedData: se.associatedData(),
chunkIndex: make([]byte, 8),
packetTag: packetTypeSymmetricallyEncryptedIntegrityProtected,
},
reader: se.Contents,
peekedBytes: peekedBytes,
}, nil
}
// serializeSymmetricallyEncryptedAead encrypts to a writer a V2 SEIPD packet (AEAD) as specified in
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite CipherSuite, chunkSizeByte byte, rand io.Reader, inputKey []byte) (Contents io.WriteCloser, err error) {
// cipherFunc must have block size 16 to use AEAD
if cipherSuite.Cipher.blockSize() != 16 {
return nil, errors.InvalidArgumentError("invalid aead cipher function")
}
if cipherSuite.Cipher.KeySize() != len(inputKey) {
return nil, errors.InvalidArgumentError("error in aead serialization: bad key length")
}
// Data for en/decryption: tag, version, cipher, aead mode, chunk size
prefix := []byte{
0xD2,
symmetricallyEncryptedVersionAead,
byte(cipherSuite.Cipher),
byte(cipherSuite.Mode),
chunkSizeByte,
}
// Write header (that correspond to prefix except first byte)
n, err := ciphertext.Write(prefix[1:])
if err != nil || n < 4 {
return nil, err
}
// Random salt
salt := make([]byte, aeadSaltSize)
if _, err := rand.Read(salt); err != nil {
return nil, err
}
if _, err := ciphertext.Write(salt); err != nil {
return nil, err
}
aead, nonce := getSymmetricallyEncryptedAeadInstance(cipherSuite.Cipher, cipherSuite.Mode, inputKey, salt, prefix)
return &aeadEncrypter{
aeadCrypter: aeadCrypter{
aead: aead,
chunkSize: decodeAEADChunkSize(chunkSizeByte),
associatedData: prefix,
chunkIndex: make([]byte, 8),
initialNonce: nonce,
packetTag: packetTypeSymmetricallyEncryptedIntegrityProtected,
},
writer: ciphertext,
}, nil
}
func getSymmetricallyEncryptedAeadInstance(c CipherFunction, mode AEADMode, inputKey, salt, associatedData []byte) (aead cipher.AEAD, nonce []byte) {
hkdfReader := hkdf.New(sha256.New, inputKey, salt, associatedData)
encryptionKey := make([]byte, c.KeySize())
_, _ = readFull(hkdfReader, encryptionKey)
// Last 64 bits of nonce are the counter
nonce = make([]byte, mode.IvLength()-8)
_, _ = readFull(hkdfReader, nonce)
blockCipher := c.new(encryptionKey)
aead = mode.new(blockCipher)
return
}