use aes_gcm::{AeadCore, AeadInPlace, Aes256Gcm, KeyInit, KeySizeUser};
use byteorder::{BigEndian, ByteOrder};
use digest::typenum::Unsigned;
use generic_array::GenericArray;
use rand::RngCore;
use super::super::Error;
use crate::mac::MacAlgorithm;
pub struct GcmCipher {}
type KeySize = <Aes256Gcm as KeySizeUser>::KeySize;
type NonceSize = <Aes256Gcm as AeadCore>::NonceSize;
type TagSize = <Aes256Gcm as AeadCore>::TagSize;
impl super::Cipher for GcmCipher {
fn key_len(&self) -> usize {
Aes256Gcm::key_size()
}
fn nonce_len(&self) -> usize {
GenericArray::<u8, NonceSize>::default().len()
}
fn make_opening_key(
&self,
k: &[u8],
n: &[u8],
_: &[u8],
_: &dyn MacAlgorithm,
) -> Box<dyn super::OpeningKey + Send> {
let mut key = GenericArray::<u8, KeySize>::default();
key.clone_from_slice(k);
let mut nonce = GenericArray::<u8, NonceSize>::default();
nonce.clone_from_slice(n);
Box::new(OpeningKey {
nonce,
cipher: Aes256Gcm::new(&key),
})
}
fn make_sealing_key(
&self,
k: &[u8],
n: &[u8],
_: &[u8],
_: &dyn MacAlgorithm,
) -> Box<dyn super::SealingKey + Send> {
let mut key = GenericArray::<u8, KeySize>::default();
key.clone_from_slice(k);
let mut nonce = GenericArray::<u8, NonceSize>::default();
nonce.clone_from_slice(n);
Box::new(SealingKey {
nonce,
cipher: Aes256Gcm::new(&key),
})
}
}
pub struct OpeningKey {
nonce: GenericArray<u8, NonceSize>,
cipher: Aes256Gcm,
}
pub struct SealingKey {
nonce: GenericArray<u8, NonceSize>,
cipher: Aes256Gcm,
}
const GCM_COUNTER_OFFSET: u64 = 3;
fn make_nonce(
nonce: &GenericArray<u8, NonceSize>,
sequence_number: u32,
) -> GenericArray<u8, NonceSize> {
let mut new_nonce = GenericArray::<u8, NonceSize>::default();
new_nonce.clone_from_slice(nonce);
let i0 = new_nonce.len() - 8;
#[allow(clippy::indexing_slicing)] let ctr = BigEndian::read_u64(&new_nonce[i0..]);
#[allow(clippy::indexing_slicing)] BigEndian::write_u64(
&mut new_nonce[i0..],
ctr + sequence_number as u64 - GCM_COUNTER_OFFSET,
);
new_nonce
}
impl super::OpeningKey for OpeningKey {
fn decrypt_packet_length(
&self,
_sequence_number: u32,
encrypted_packet_length: [u8; 4],
) -> [u8; 4] {
encrypted_packet_length
}
fn tag_len(&self) -> usize {
TagSize::to_usize()
}
fn open<'a>(
&mut self,
sequence_number: u32,
ciphertext_in_plaintext_out: &'a mut [u8],
tag: &[u8],
) -> Result<&'a [u8], Error> {
let mut packet_length = [0; super::PACKET_LENGTH_LEN];
#[allow(clippy::indexing_slicing)] packet_length.clone_from_slice(&ciphertext_in_plaintext_out[..super::PACKET_LENGTH_LEN]);
let mut buffer = vec![0; ciphertext_in_plaintext_out.len() - super::PACKET_LENGTH_LEN];
#[allow(clippy::indexing_slicing)] buffer.copy_from_slice(&ciphertext_in_plaintext_out[super::PACKET_LENGTH_LEN..]);
let nonce = make_nonce(&self.nonce, sequence_number);
let mut tag_buf = GenericArray::<u8, TagSize>::default();
tag_buf.clone_from_slice(tag);
#[allow(clippy::indexing_slicing)]
self.cipher
.decrypt_in_place_detached(
&nonce,
&packet_length,
&mut ciphertext_in_plaintext_out[super::PACKET_LENGTH_LEN..],
&tag_buf,
)
.map_err(|_| Error::DecryptionError)?;
Ok(ciphertext_in_plaintext_out)
}
}
impl super::SealingKey for SealingKey {
fn padding_length(&self, payload: &[u8]) -> usize {
let block_size = 16;
let extra_len = super::PACKET_LENGTH_LEN + super::PADDING_LENGTH_LEN;
let padding_len = if payload.len() + extra_len <= super::MINIMUM_PACKET_LEN {
super::MINIMUM_PACKET_LEN - payload.len() - super::PADDING_LENGTH_LEN
} else {
block_size - ((super::PADDING_LENGTH_LEN + payload.len()) % block_size)
};
if padding_len < super::PACKET_LENGTH_LEN {
padding_len + block_size
} else {
padding_len
}
}
fn fill_padding(&self, padding_out: &mut [u8]) {
rand::thread_rng().fill_bytes(padding_out);
}
fn tag_len(&self) -> usize {
TagSize::to_usize()
}
fn seal(
&mut self,
sequence_number: u32,
plaintext_in_ciphertext_out: &mut [u8],
tag: &mut [u8],
) {
let mut packet_length = [0; super::PACKET_LENGTH_LEN];
#[allow(clippy::indexing_slicing)] packet_length.clone_from_slice(&plaintext_in_ciphertext_out[..super::PACKET_LENGTH_LEN]);
let nonce = make_nonce(&self.nonce, sequence_number);
#[allow(clippy::indexing_slicing, clippy::unwrap_used)]
let tag_out = self
.cipher
.encrypt_in_place_detached(
&nonce,
&packet_length,
&mut plaintext_in_ciphertext_out[super::PACKET_LENGTH_LEN..],
)
.unwrap();
tag.clone_from_slice(&tag_out)
}
}