// Copyright 2021-2025 Sebastian Ramacher
// SPDX-License-Identifier: Apache-2.0 OR MIT

#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]

//! ## Usage
//!
//! Simple usage (allocating, no associated data):
//!
//! ```
//! use ascon_aead::{AsconAead128, AsconAead128Key, AsconAead128Nonce, Key, Nonce};
//! use ascon_aead::aead::{Aead, KeyInit};
//!
//! let key = AsconAead128Key::from_slice(b"very secret key.");
//! let cipher = AsconAead128::new(key);
//!
//! // 128-bits; unique per message
//! let nonce = AsconAead128Nonce::from_slice(b"unique nonce 012");
//!
//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())
//!     .expect("encryption failure!"); // NOTE: handle this error to avoid panics!
//!
//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())
//!     .expect("decryption failure!"); // NOTE: handle this error to avoid panics!
//!
//! assert_eq!(&plaintext, b"plaintext message");
//! ```
//!
//! With randomly sampled keys and nonces (requires `getrandom` feature):
//!
//! ```
//! # #[cfg(feature = "getrandom")] {
//! use ascon_aead::{AsconAead128, AsconAead128Key, AsconAead128Nonce};
//! use ascon_aead::aead::{Aead, AeadCore, Generate, KeyInit};
//!
//! let key = AsconAead128Key::generate();
//! let cipher = AsconAead128::new(&key);
//!
//! // 128 bits; unique per message
//! let nonce = AsconAead128Nonce::generate();
//!
//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())
//!     .expect("encryption failure!"); // NOTE: handle this error to avoid panics!
//!
//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())
//!     .expect("decryption failure!"); // NOTE: handle this error to avoid panics!
//!
//! assert_eq!(&plaintext, b"plaintext message");
//! # }
//! ```
//!
//! ## In-place Usage (eliminates `alloc` requirement)
//!
//! This crate has an optional `alloc` feature which can be disabled in e.g.
//! microcontroller environments that don't have a heap.
//!
//! The [`AeadInOut::encrypt_in_place`] and [`AeadInOut::decrypt_in_place`]
//! methods accept any type that impls the [`aead::Buffer`] trait which
//! contains the plaintext for encryption or ciphertext for decryption.
//!
//! Enabling the `arrayvec` feature of this crate will provide an impl of
//! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as
//! [`aead::arrayvec::ArrayVec`]), and enabling the `bytes` feature of this crate will
//! provide an impl of [`aead::Buffer`] for `bytes::BytesMut` (re-exported from the
//! [`aead`] crate as [`aead::bytes::BytesMut`]).
//!
//! ## Truncated Tags
//!
//! Ascon-AEAD128 also supports truncated tags ranging from 32 to 128 bits.
//! Currently, only byte lengths are supported. Support for truncated tags is
//! available via [`TruncatedAsconAEAD128`].
//!
//! ```
//! use aead::consts::{U5};
//! use ascon_aead::{TruncatedAsconAead128, Key, Nonce};
//! use ascon_aead::aead::{Aead, KeyInit};
//!
//! type TruncatedAscon = TruncatedAsconAead128<U5>;
//! let key = Key::<TruncatedAscon>::from_slice(b"very secret key.");
//! let cipher = TruncatedAscon::new(key);
//!
//! // 128-bits; unique per message
//! let nonce = Nonce::<TruncatedAscon>::from_slice(b"unique nonce 012");
//!
//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())
//!     .expect("encryption failure!"); // NOTE: handle this error to avoid panics!
//!
//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())
//!     .expect("decryption failure!"); // NOTE: handle this error to avoid panics!
//!
//! assert_eq!(&plaintext, b"plaintext message");
//! ```

pub use aead::{self, Error, Key, Nonce, Tag};
use aead::{
    AeadCore, AeadInOut, KeyInit, KeySizeUser, TagPosition,
    array::{
        ArraySize,
        typenum::{IsGreaterOrEqual, IsLessOrEqual},
    },
    consts::{True, U4, U16},
    inout::InOutBuf,
};

mod asconcore;

use asconcore::{AsconCore, Parameters, Parameters128};

/// Ascon generic over some Parameters
///
/// This type is generic to support substituting various Ascon parameter sets. It is not intended to
/// be used directly. Use the [`AsconAead128`] type aliases instead.
#[derive(Clone)]
struct Ascon<P: Parameters> {
    key: P::InternalKey,
}

impl<P: Parameters> KeySizeUser for Ascon<P> {
    type KeySize = P::KeySize;
}

impl<P: Parameters> KeyInit for Ascon<P> {
    fn new(key: &Key<Self>) -> Self {
        Self {
            key: P::InternalKey::from(key),
        }
    }
}

impl<P: Parameters> AeadCore for Ascon<P> {
    type NonceSize = U16;
    type TagSize = P::TagSize;
    const TAG_POSITION: TagPosition = TagPosition::Postfix;
}

impl<P: Parameters> AeadInOut for Ascon<P> {
    fn encrypt_inout_detached(
        &self,
        nonce: &Nonce<Self>,
        associated_data: &[u8],
        buffer: InOutBuf<'_, '_, u8>,
    ) -> Result<Tag<Self>, Error> {
        if (buffer.len() as u64)
            .checked_add(associated_data.len() as u64)
            .is_none()
        {
            return Err(Error);
        }

        let mut core = AsconCore::<P>::new(&self.key, nonce);
        Ok(core.encrypt_inout(buffer, associated_data))
    }

    fn decrypt_inout_detached(
        &self,
        nonce: &Nonce<Self>,
        associated_data: &[u8],
        buffer: InOutBuf<'_, '_, u8>,
        tag: &Tag<Self>,
    ) -> Result<(), Error> {
        if (buffer.len() as u64)
            .checked_add(associated_data.len() as u64)
            .is_none()
        {
            return Err(Error);
        }

        let mut core = AsconCore::<P>::new(&self.key, nonce);
        core.decrypt_inout(buffer, associated_data, tag)
    }
}

/// Ascon-AEAD128
pub struct AsconAead128(Ascon<Parameters128<U16>>);
/// Key for Ascon-AEAD128
pub type AsconAead128Key = Key<AsconAead128>;
/// Nonce for Ascon-AEAD128
pub type AsconAead128Nonce = Nonce<AsconAead128>;
/// Tag for Ascon-AEAD128
pub type AsconAead128Tag = Tag<AsconAead128>;

impl KeySizeUser for AsconAead128 {
    type KeySize = U16;
}

impl KeyInit for AsconAead128 {
    fn new(key: &Key<Self>) -> Self {
        Self(Ascon::new(key))
    }
}

impl AeadCore for AsconAead128 {
    type NonceSize = U16;
    type TagSize = U16;
    const TAG_POSITION: TagPosition = TagPosition::Postfix;
}

impl AeadInOut for AsconAead128 {
    #[inline(always)]
    fn encrypt_inout_detached(
        &self,
        nonce: &Nonce<Self>,
        associated_data: &[u8],
        buffer: InOutBuf<'_, '_, u8>,
    ) -> Result<Tag<Self>, Error> {
        self.0
            .encrypt_inout_detached(nonce, associated_data, buffer)
    }

    #[inline(always)]
    fn decrypt_inout_detached(
        &self,
        nonce: &Nonce<Self>,
        associated_data: &[u8],
        buffer: InOutBuf<'_, '_, u8>,
        tag: &Tag<Self>,
    ) -> Result<(), Error> {
        self.0
            .decrypt_inout_detached(nonce, associated_data, buffer, tag)
    }
}

/// Truncated Ascon-AEAD128
///
/// Tag sizes of 4 to 16 bytes are supported.
pub struct TruncatedAsconAead128<TagSize = U16>(Ascon<Parameters128<TagSize>>)
where
    TagSize: ArraySize + IsLessOrEqual<U16, Output = True> + IsGreaterOrEqual<U4, Output = True>;
/// Key for Truncated Ascon-AEAD128
pub type TruncatedAsconAead128Key<TagSize = U16> = Key<TruncatedAsconAead128<TagSize>>;
/// Nonce for Truncated Ascon-AEAD128
pub type TruncatedAsconAead128Nonce<TagSize = U16> = Nonce<TruncatedAsconAead128<TagSize>>;
/// Tag for Truncated  Ascon-AEAD128
pub type TruncatedAsconAead128Tag<TagSize = U16> = Tag<TruncatedAsconAead128<TagSize>>;

impl<TagSize> KeySizeUser for TruncatedAsconAead128<TagSize>
where
    TagSize: ArraySize + IsLessOrEqual<U16, Output = True> + IsGreaterOrEqual<U4, Output = True>,
{
    type KeySize = U16;
}

impl<TagSize> KeyInit for TruncatedAsconAead128<TagSize>
where
    TagSize: ArraySize + IsLessOrEqual<U16, Output = True> + IsGreaterOrEqual<U4, Output = True>,
{
    fn new(key: &Key<Self>) -> Self {
        Self(Ascon::new(key))
    }
}

impl<TagSize> AeadCore for TruncatedAsconAead128<TagSize>
where
    TagSize: ArraySize + IsLessOrEqual<U16, Output = True> + IsGreaterOrEqual<U4, Output = True>,
{
    type NonceSize = U16;
    type TagSize = TagSize;
    const TAG_POSITION: TagPosition = TagPosition::Postfix;
}

impl<TagSize> AeadInOut for TruncatedAsconAead128<TagSize>
where
    TagSize: ArraySize + IsLessOrEqual<U16, Output = True> + IsGreaterOrEqual<U4, Output = True>,
{
    #[inline(always)]
    fn encrypt_inout_detached(
        &self,
        nonce: &Nonce<Self>,
        associated_data: &[u8],
        buffer: InOutBuf<'_, '_, u8>,
    ) -> Result<Tag<Self>, Error> {
        self.0
            .encrypt_inout_detached(nonce, associated_data, buffer)
    }

    #[inline(always)]
    fn decrypt_inout_detached(
        &self,
        nonce: &Nonce<Self>,
        associated_data: &[u8],
        buffer: InOutBuf<'_, '_, u8>,
        tag: &Tag<Self>,
    ) -> Result<(), Error> {
        self.0
            .decrypt_inout_detached(nonce, associated_data, buffer, tag)
    }
}
