main
Raw Download raw file
  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}