...

Source file src/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go

Documentation: github.com/ProtonMail/go-crypto/openpgp/packet

     1  // Copyright 2023 Proton AG. 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  
     5  package packet
     6  
     7  import (
     8  	"crypto/cipher"
     9  	"crypto/sha256"
    10  	"io"
    11  
    12  	"github.com/ProtonMail/go-crypto/openpgp/errors"
    13  	"golang.org/x/crypto/hkdf"
    14  )
    15  
    16  // parseAead parses a V2 SEIPD packet (AEAD) as specified in
    17  // https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
    18  func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error {
    19  	headerData := make([]byte, 3)
    20  	if n, err := io.ReadFull(r, headerData); n < 3 {
    21  		return errors.StructuralError("could not read aead header: " + err.Error())
    22  	}
    23  
    24  	// Cipher
    25  	se.Cipher = CipherFunction(headerData[0])
    26  	// cipherFunc must have block size 16 to use AEAD
    27  	if se.Cipher.blockSize() != 16 {
    28  		return errors.UnsupportedError("invalid aead cipher: " + string(se.Cipher))
    29  	}
    30  
    31  	// Mode
    32  	se.Mode = AEADMode(headerData[1])
    33  	if se.Mode.TagLength() == 0 {
    34  		return errors.UnsupportedError("unknown aead mode: " + string(se.Mode))
    35  	}
    36  
    37  	// Chunk size
    38  	se.ChunkSizeByte = headerData[2]
    39  	if se.ChunkSizeByte > 16 {
    40  		return errors.UnsupportedError("invalid aead chunk size byte: " + string(se.ChunkSizeByte))
    41  	}
    42  
    43  	// Salt
    44  	if n, err := io.ReadFull(r, se.Salt[:]); n < aeadSaltSize {
    45  		return errors.StructuralError("could not read aead salt: " + err.Error())
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  // associatedData for chunks: tag, version, cipher, mode, chunk size byte
    52  func (se *SymmetricallyEncrypted) associatedData() []byte {
    53  	return []byte{
    54  		0xD2,
    55  		symmetricallyEncryptedVersionAead,
    56  		byte(se.Cipher),
    57  		byte(se.Mode),
    58  		se.ChunkSizeByte,
    59  	}
    60  }
    61  
    62  // decryptAead decrypts a V2 SEIPD packet (AEAD) as specified in
    63  // https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
    64  func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, error) {
    65  	aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
    66  
    67  	// Carry the first tagLen bytes
    68  	tagLen := se.Mode.TagLength()
    69  	peekedBytes := make([]byte, tagLen)
    70  	n, err := io.ReadFull(se.Contents, peekedBytes)
    71  	if n < tagLen || (err != nil && err != io.EOF) {
    72  		return nil, errors.StructuralError("not enough data to decrypt:" + err.Error())
    73  	}
    74  
    75  	return &aeadDecrypter{
    76  		aeadCrypter: aeadCrypter{
    77  			aead:           aead,
    78  			chunkSize:      decodeAEADChunkSize(se.ChunkSizeByte),
    79  			initialNonce:   nonce,
    80  			associatedData: se.associatedData(),
    81  			chunkIndex:     make([]byte, 8),
    82  			packetTag:      packetTypeSymmetricallyEncryptedIntegrityProtected,
    83  		},
    84  		reader:      se.Contents,
    85  		peekedBytes: peekedBytes,
    86  	}, nil
    87  }
    88  
    89  // serializeSymmetricallyEncryptedAead encrypts to a writer a V2 SEIPD packet (AEAD) as specified in
    90  // https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
    91  func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite CipherSuite, chunkSizeByte byte, rand io.Reader, inputKey []byte) (Contents io.WriteCloser, err error) {
    92  	// cipherFunc must have block size 16 to use AEAD
    93  	if cipherSuite.Cipher.blockSize() != 16 {
    94  		return nil, errors.InvalidArgumentError("invalid aead cipher function")
    95  	}
    96  
    97  	if cipherSuite.Cipher.KeySize() != len(inputKey) {
    98  		return nil, errors.InvalidArgumentError("error in aead serialization: bad key length")
    99  	}
   100  
   101  	// Data for en/decryption: tag, version, cipher, aead mode, chunk size
   102  	prefix := []byte{
   103  		0xD2,
   104  		symmetricallyEncryptedVersionAead,
   105  		byte(cipherSuite.Cipher),
   106  		byte(cipherSuite.Mode),
   107  		chunkSizeByte,
   108  	}
   109  
   110  	// Write header (that correspond to prefix except first byte)
   111  	n, err := ciphertext.Write(prefix[1:])
   112  	if err != nil || n < 4 {
   113  		return nil, err
   114  	}
   115  
   116  	// Random salt
   117  	salt := make([]byte, aeadSaltSize)
   118  	if _, err := rand.Read(salt); err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	if _, err := ciphertext.Write(salt); err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	aead, nonce := getSymmetricallyEncryptedAeadInstance(cipherSuite.Cipher, cipherSuite.Mode, inputKey, salt, prefix)
   127  
   128  	return &aeadEncrypter{
   129  		aeadCrypter: aeadCrypter{
   130  			aead:           aead,
   131  			chunkSize:      decodeAEADChunkSize(chunkSizeByte),
   132  			associatedData: prefix,
   133  			chunkIndex:     make([]byte, 8),
   134  			initialNonce:   nonce,
   135  			packetTag:      packetTypeSymmetricallyEncryptedIntegrityProtected,
   136  		},
   137  		writer: ciphertext,
   138  	}, nil
   139  }
   140  
   141  func getSymmetricallyEncryptedAeadInstance(c CipherFunction, mode AEADMode, inputKey, salt, associatedData []byte) (aead cipher.AEAD, nonce []byte) {
   142  	hkdfReader := hkdf.New(sha256.New, inputKey, salt, associatedData)
   143  
   144  	encryptionKey := make([]byte, c.KeySize())
   145  	_, _ = readFull(hkdfReader, encryptionKey)
   146  
   147  	// Last 64 bits of nonce are the counter
   148  	nonce = make([]byte, mode.IvLength()-8)
   149  
   150  	_, _ = readFull(hkdfReader, nonce)
   151  
   152  	blockCipher := c.new(encryptionKey)
   153  	aead = mode.new(blockCipher)
   154  
   155  	return
   156  }
   157  

View as plain text