use crate::error::Unspecified;
use crate::{derive_debug_via_id, hkdf};
use aead_ctx::AeadCtx;
use core::fmt::Debug;
use core::ops::RangeFrom;
use core::stringify;
mod aead_ctx;
mod aes_gcm;
mod chacha;
pub mod chacha20_poly1305_openssh;
mod nonce;
pub mod nonce_sequence;
mod poly1305;
pub mod quic;
mod unbound_key;
pub use self::aes_gcm::{AES_128_GCM, AES_192_GCM, AES_256_GCM};
pub use self::chacha::CHACHA20_POLY1305;
pub use self::nonce::{Nonce, NONCE_LEN};
pub use self::unbound_key::UnboundKey;
pub trait NonceSequence {
fn advance(&mut self) -> Result<Nonce, Unspecified>;
}
pub trait BoundKey<N: NonceSequence>: Debug {
fn new(key: UnboundKey, nonce_sequence: N) -> Self;
fn algorithm(&self) -> &'static Algorithm;
}
pub struct OpeningKey<N: NonceSequence> {
key: UnboundKey,
nonce_sequence: N,
}
impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
fn new(key: UnboundKey, nonce_sequence: N) -> Self {
Self {
key,
nonce_sequence,
}
}
#[inline]
fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl<N: NonceSequence> Debug for OpeningKey<N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("OpeningKey")
.field("algorithm", &self.algorithm())
.finish()
}
}
impl<N: NonceSequence> OpeningKey<N> {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_in_place<'in_out, A>(
&mut self,
aad: Aad<A>,
in_out: &'in_out mut [u8],
) -> Result<&'in_out mut [u8], Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.open_within(self.nonce_sequence.advance()?, aad.as_ref(), in_out, 0..)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_within<'in_out, A>(
&mut self,
aad: Aad<A>,
in_out: &'in_out mut [u8],
ciphertext_and_tag: RangeFrom<usize>,
) -> Result<&'in_out mut [u8], Unspecified>
where
A: AsRef<[u8]>,
{
self.key.open_within(
self.nonce_sequence.advance()?,
aad.as_ref(),
in_out,
ciphertext_and_tag,
)
}
pub fn prepare_nonce(&mut self) -> Result<OpeningKeyPreparedNonce<'_, N>, Unspecified> {
OpeningKeyPreparedNonce::new(self)
}
}
pub struct SealingKey<N: NonceSequence> {
key: UnboundKey,
nonce_sequence: N,
}
impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
fn new(key: UnboundKey, nonce_sequence: N) -> Self {
Self {
key,
nonce_sequence,
}
}
#[inline]
fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl<N: NonceSequence> Debug for SealingKey<N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("SealingKey")
.field("algorithm", &self.algorithm())
.finish()
}
}
impl<N: NonceSequence> SealingKey<N> {
#[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
#[inline]
pub fn seal_in_place<A, InOut>(
&mut self,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.seal_in_place_append_tag(aad, in_out)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_append_tag<A, InOut>(
&mut self,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.key
.seal_in_place_append_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out)
.map(|_| ())
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_separate_tag<A>(
&mut self,
aad: Aad<A>,
in_out: &mut [u8],
) -> Result<Tag, Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.seal_in_place_separate_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out)
.map(|(_, tag)| tag)
}
pub fn prepare_nonce(&mut self) -> Result<SealingKeyPreparedNonce<'_, N>, Unspecified> {
SealingKeyPreparedNonce::new(self)
}
}
macro_rules! nonce_seq_key_op_mut {
($name:ident, $name_prep_nonce:ident) => {
pub struct $name_prep_nonce<'a, N: NonceSequence> {
key: &'a mut $name<N>,
nonce: Nonce,
}
impl<'a, N: NonceSequence> $name_prep_nonce<'a, N> {
fn new(key: &'a mut $name<N>) -> Result<Self, Unspecified> {
let nonce = key.nonce_sequence.advance()?;
Ok(Self { key, nonce })
}
}
impl<N: NonceSequence> $name_prep_nonce<'_, N> {
#[must_use]
pub fn nonce(&self) -> &Nonce {
&self.nonce
}
}
impl<N: NonceSequence> Debug for $name_prep_nonce<'_, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
f.debug_struct(stringify!($name_prep_nonce))
.finish_non_exhaustive()
}
}
};
}
nonce_seq_key_op_mut!(OpeningKey, OpeningKeyPreparedNonce);
nonce_seq_key_op_mut!(SealingKey, SealingKeyPreparedNonce);
impl<N: NonceSequence> OpeningKeyPreparedNonce<'_, N> {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_in_place<A>(self, aad: Aad<A>, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified>
where
A: AsRef<[u8]>,
{
self.open_within(aad, in_out, 0..)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_within<A>(
self,
aad: Aad<A>,
in_out: &mut [u8],
ciphertext_and_tag: RangeFrom<usize>,
) -> Result<&mut [u8], Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.key
.open_within(self.nonce, aad.as_ref(), in_out, ciphertext_and_tag)
}
}
impl<N: NonceSequence> SealingKeyPreparedNonce<'_, N> {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_append_tag<A, InOut>(
self,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.key
.key
.seal_in_place_append_tag(Some(self.nonce), aad.as_ref(), in_out)
.map(|_| ())
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_separate_tag<A>(
self,
aad: Aad<A>,
in_out: &mut [u8],
) -> Result<Tag, Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.key
.seal_in_place_separate_tag(Some(self.nonce), aad.as_ref(), in_out)
.map(|(_, tag)| tag)
}
}
pub struct Aad<A: AsRef<[u8]>>(A);
impl<A: AsRef<[u8]>> Aad<A> {
#[inline]
pub fn from(aad: A) -> Self {
Aad(aad)
}
}
impl<A> AsRef<[u8]> for Aad<A>
where
A: AsRef<[u8]>,
{
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Aad<[u8; 0]> {
#[must_use]
pub fn empty() -> Self {
Self::from([])
}
}
impl hkdf::KeyType for &'static Algorithm {
#[inline]
fn len(&self) -> usize {
self.key_len()
}
}
pub struct LessSafeKey {
key: UnboundKey,
}
impl LessSafeKey {
#[must_use]
pub fn new(key: UnboundKey) -> Self {
Self { key }
}
#[inline]
pub fn open_in_place<'in_out, A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &'in_out mut [u8],
) -> Result<&'in_out mut [u8], Unspecified>
where
A: AsRef<[u8]>,
{
self.open_within(nonce, aad, in_out, 0..)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_within<'in_out, A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &'in_out mut [u8],
ciphertext_and_tag: RangeFrom<usize>,
) -> Result<&'in_out mut [u8], Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.open_within(nonce, aad.as_ref(), in_out, ciphertext_and_tag)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_separate_gather<A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_ciphertext: &[u8],
in_tag: &[u8],
out_plaintext: &mut [u8],
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.open_separate_gather(&nonce, aad.as_ref(), in_ciphertext, in_tag, out_plaintext)
}
#[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
#[inline]
#[allow(clippy::missing_errors_doc)]
pub fn seal_in_place<A, InOut>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.seal_in_place_append_tag(nonce, aad, in_out)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_append_tag<A, InOut>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.key
.seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out)
.map(|_| ())
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_separate_tag<A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &mut [u8],
) -> Result<Tag, Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out)
.map(|(_, tag)| tag)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn seal_in_place_scatter<A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &mut [u8],
extra_in: &[u8],
extra_out_and_tag: &mut [u8],
) -> Result<(), Unspecified>
where
A: AsRef<[u8]>,
{
self.key.seal_in_place_separate_scatter(
nonce,
aad.as_ref(),
in_out,
extra_in,
extra_out_and_tag,
)
}
#[inline]
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl Debug for LessSafeKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("LessSafeKey")
.field("algorithm", self.algorithm())
.finish()
}
}
pub struct Algorithm {
init: fn(key: &[u8], tag_len: usize) -> Result<AeadCtx, Unspecified>,
key_len: usize,
id: AlgorithmID,
max_input_len: u64,
}
impl Algorithm {
#[inline]
#[must_use]
pub fn key_len(&self) -> usize {
self.key_len
}
#[inline]
#[must_use]
pub fn tag_len(&self) -> usize {
TAG_LEN
}
#[inline]
#[must_use]
pub fn nonce_len(&self) -> usize {
NONCE_LEN
}
}
derive_debug_via_id!(Algorithm);
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[allow(non_camel_case_types)]
enum AlgorithmID {
AES_128_GCM,
AES_192_GCM,
AES_256_GCM,
CHACHA20_POLY1305,
}
impl PartialEq for Algorithm {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Algorithm {}
#[must_use]
#[repr(C)]
pub struct Tag([u8; MAX_TAG_LEN], usize);
impl AsRef<[u8]> for Tag {
fn as_ref(&self) -> &[u8] {
self.0[..self.1].as_ref()
}
}
impl core::fmt::Debug for Tag {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Tag").finish()
}
}
const MAX_KEY_LEN: usize = 32;
const TAG_LEN: usize = 16;
pub const MAX_TAG_LEN: usize = TAG_LEN;
#[cfg(test)]
mod tests {
use nonce_sequence::Counter32Builder;
use super::*;
use crate::iv::FixedLength;
use crate::test::from_hex;
#[cfg(feature = "fips")]
mod fips;
#[test]
fn test_aes_128() {
let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
let og_nonce = from_hex("5bf11a0951f0bfc7ea5c9e58").unwrap();
let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap();
let unbound_key = UnboundKey::new(&AES_128_GCM, &key).unwrap();
assert_eq!(&AES_128_GCM, unbound_key.algorithm());
assert_eq!(16, AES_128_GCM.tag_len());
assert_eq!(12, AES_128_GCM.nonce_len());
let less_safe_key = LessSafeKey::new(unbound_key);
let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
let mut in_out = Vec::from(plaintext.as_slice());
#[allow(deprecated)]
less_safe_key
.seal_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
.unwrap();
let mut in_out_clone = in_out.clone();
let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
assert!(less_safe_key
.open_in_place(
Nonce(FixedLength::from(nonce)),
Aad::from("test"),
&mut in_out_clone
)
.is_err());
let mut in_out_clone = in_out.clone();
let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
nonce[0] = 0;
assert!(less_safe_key
.open_in_place(
Nonce(FixedLength::from(nonce)),
Aad::empty(),
&mut in_out_clone
)
.is_err());
let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
less_safe_key
.open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
.unwrap();
assert_eq!(plaintext, in_out[..plaintext.len()]);
}
#[test]
fn debug_prepared_nonce() {
let mut sk = SealingKey::new(
UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
Counter32Builder::new().build(),
);
let mut ok = OpeningKey::new(
UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
Counter32Builder::new().build(),
);
let so = sk.prepare_nonce().unwrap();
let oo = ok.prepare_nonce().unwrap();
assert_eq!("SealingKeyPreparedNonce { .. }", format!("{so:?}"));
assert_eq!("OpeningKeyPreparedNonce { .. }", format!("{oo:?}"));
}
#[test]
fn debug_tag() {
let tag = Tag([0u8; MAX_TAG_LEN], MAX_TAG_LEN);
assert_eq!("Tag", format!("{tag:?}"));
}
}