mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-10-19 07:55:07 +00:00
157 lines
4.8 KiB
Go
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
|
|
}
|