// Copyright 2024 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! ML-KEM
//!
//! ML-KEM is a post-quantum key encapsulation mechanism, specified in
//! [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final).
//! A KEM works like public-key encryption, except that the encrypted message is
//! always a random key chosen by the algorithm.
//!
//! ```
//! use bssl_crypto::mlkem;
//!
//! // Generate a key pair.
//! let (serialized_public_key, private_key, private_seed) = mlkem::PrivateKey768::generate();
//!
//! // Send `serialized_public_key` to the sender. The sender parses it:
//! let public_key = mlkem::PublicKey768::parse(&serialized_public_key).unwrap();
//!
//! // Generate and encrypt a shared secret key to that public key.
//! let (ciphertext, shared_key) = public_key.encapsulate();
//!
//! // Send `ciphertext` to the holder of the private key.
//! let shared_key2 = private_key.decapsulate(&ciphertext).unwrap();
//! assert_eq!(shared_key, shared_key2);
//!
//! // `shared_key` would then be used with an algorithm from the `aead` module
//! // to encrypt and authenticate data. The two keys may not match and so it's
//! // critical to use an authenticated encryption mechanism to confirm the key.
//! ```

use crate::{
    as_cbs, cbb_to_vec, initialized_boxed_struct, initialized_boxed_struct_fallible,
    with_output_array_fallible, FfiSlice,
};
use alloc::{boxed::Box, vec::Vec};
use core::mem::MaybeUninit;

/// An ML-KEM-768 public key.
pub struct PublicKey768(Box<bssl_sys::MLKEM768_public_key>);

/// An ML-KEM-768 private key.
pub struct PrivateKey768(Box<bssl_sys::MLKEM768_private_key>);

/// An ML-KEM-1024 public key.
///
/// Use ML-KEM-768 unless you have a good reason to need this larger size.
pub struct PublicKey1024(Box<bssl_sys::MLKEM1024_public_key>);

/// An ML-KEM-1024 private key.
///
/// Use ML-KEM-768 unless you have a good reason to need this larger size.
pub struct PrivateKey1024(Box<bssl_sys::MLKEM1024_private_key>);

/// The number of bytes in an encoded ML-KEM-768 public key.
pub const PUBLIC_KEY_BYTES_768: usize = bssl_sys::MLKEM768_PUBLIC_KEY_BYTES as usize;

/// The number of bytes in an ML-KEM seed.
pub const SEED_BYTES: usize = bssl_sys::MLKEM_SEED_BYTES as usize;

/// The number of bytes in the ML-KEM-768 ciphertext.
pub const CIPHERTEXT_BYTES_768: usize = bssl_sys::MLKEM768_CIPHERTEXT_BYTES as usize;

/// The number of bytes in an ML-KEM shared secret.
pub const SHARED_SECRET_BYTES: usize = bssl_sys::MLKEM_SHARED_SECRET_BYTES as usize;

/// The number of bytes in an encoded ML-KEM-1024 public key.
pub const PUBLIC_KEY_BYTES_1024: usize = bssl_sys::MLKEM1024_PUBLIC_KEY_BYTES as usize;

/// The number of bytes in the ML-KEM-1024 ciphertext.
pub const CIPHERTEXT_BYTES_1024: usize = bssl_sys::MLKEM1024_CIPHERTEXT_BYTES as usize;

impl PublicKey768 {
    /// Parse a public key from NIST's defined format.
    pub fn parse(encoded: &[u8]) -> Option<Self> {
        let mut cbs = as_cbs(encoded);
        unsafe {
            initialized_boxed_struct_fallible(|pub_key| {
                // Safety: `pub_key` is the correct size via the type system and
                // is fully written if this function returns 1.
                bssl_sys::MLKEM768_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0
            })
        }
        .map(Self)
    }

    /// Return the serialization of this public key.
    pub fn to_bytes(&self) -> Vec<u8> {
        unsafe {
            cbb_to_vec(PUBLIC_KEY_BYTES_768, |cbb| {
                let ok = bssl_sys::MLKEM768_marshal_public_key(cbb, &*self.0);
                // `MLKEM768_marshal_public_key` only fails if it cannot
                // allocate memory, but `cbb_to_vec` handles allocation.
                assert_eq!(ok, 1);
            })
        }
    }

    /// Generate a secret key and encrypt it to this public key, returning the
    /// ciphertext and the shared secret key.
    pub fn encapsulate(&self) -> (Vec<u8>, [u8; SHARED_SECRET_BYTES]) {
        let mut ciphertext = Box::new_uninit_slice(CIPHERTEXT_BYTES_768);
        let mut shared_secret = MaybeUninit::<[u8; SHARED_SECRET_BYTES]>::uninit();

        unsafe {
            // Safety: the two buffer arguments are sized correctly and
            // always fully written.
            bssl_sys::MLKEM768_encap(
                ciphertext.as_mut_ptr() as *mut u8,
                shared_secret.as_mut_ptr() as *mut u8,
                &*self.0,
            );
            (ciphertext.assume_init().into(), shared_secret.assume_init())
        }
    }
}

impl PrivateKey768 {
    /// Generates a random public/private key pair returning a serialized public
    /// key, a private key, and a private seed value that can be used to
    /// regenerate the same private key in the future.
    pub fn generate() -> (Vec<u8>, PrivateKey768, [u8; SEED_BYTES]) {
        let mut public_key = Box::new_uninit_slice(PUBLIC_KEY_BYTES_768);
        let mut private_key = Box::new(MaybeUninit::uninit());
        let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit();

        unsafe {
            // Safety: the two buffer arguments are sized correctly and
            // always fully written. `private_key` is sized correctly via
            // the type system.
            bssl_sys::MLKEM768_generate_key(
                public_key.as_mut_ptr() as *mut u8,
                seed.as_mut_ptr() as *mut u8,
                private_key.as_mut_ptr(),
            );

            (
                public_key.assume_init().into(),
                Self(private_key.assume_init()),
                seed.assume_init(),
            )
        }
    }

    /// Regenerate a private key from a seed value.
    pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self {
        Self(unsafe {
            initialized_boxed_struct(|priv_key| {
                // Safety: `priv_key` is correctly sized by the type system and
                // is always fully written.
                let ok = bssl_sys::MLKEM768_private_key_from_seed(
                    priv_key,
                    seed.as_ffi_ptr(),
                    seed.len(),
                );
                // Since the seed value has the correct length, this function can
                // never fail.
                assert_eq!(ok, 1);
            })
        })
    }

    /// Derives the public key corresponding to this private key.
    pub fn to_public_key(&self) -> PublicKey768 {
        PublicKey768(unsafe {
            initialized_boxed_struct(|pub_key| {
                bssl_sys::MLKEM768_public_from_private(pub_key, &*self.0);
            })
        })
    }

    /// Decapsulates a shared secret from a ciphertext. This function only
    /// returns `None` if ciphertext is the wrong length. For invalid
    /// ciphertexts it returns a key that will always be the same for the
    /// same `ciphertext` and private key, but which appears to be random
    /// unless one has access to the private key. These alternatives occur in
    /// constant time. Any subsequent symmetric encryption using the result
    /// must use an authenticated encryption scheme in order to discover the
    /// decapsulation failure.
    pub fn decapsulate(&self, ciphertext: &[u8]) -> Option<[u8; SHARED_SECRET_BYTES]> {
        unsafe {
            with_output_array_fallible(|out, _| {
                // Safety: `out` is the correct size via the type system and is
                // always fully written if the return value is one.
                bssl_sys::MLKEM768_decap(out, ciphertext.as_ffi_ptr(), ciphertext.len(), &*self.0)
                    == 1
            })
        }
    }
}

impl PublicKey1024 {
    /// Parse a public key from NIST's defined format.
    pub fn parse(encoded: &[u8]) -> Option<Self> {
        let mut cbs = as_cbs(encoded);
        unsafe {
            initialized_boxed_struct_fallible(|pub_key| {
                // Safety: `pub_key` is the correct size via the type system and
                // is fully written if this function returns 1.
                bssl_sys::MLKEM1024_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0
            })
        }
        .map(Self)
    }

    /// Return the serialization of this public key.
    pub fn to_bytes(&self) -> Vec<u8> {
        unsafe {
            cbb_to_vec(PUBLIC_KEY_BYTES_1024, |cbb| {
                let ok = bssl_sys::MLKEM1024_marshal_public_key(cbb, &*self.0);
                // `MLKEM1024_marshal_public_key` only fails if it cannot
                // allocate memory, but `cbb_to_vec` handles allocation.
                assert_eq!(ok, 1);
            })
        }
    }

    /// Generate a secret key and encrypt it to this public key, returning the
    /// ciphertext and the shared secret key.
    pub fn encapsulate(&self) -> (Vec<u8>, [u8; SHARED_SECRET_BYTES]) {
        let mut ciphertext = Box::new_uninit_slice(CIPHERTEXT_BYTES_1024);
        let mut shared_secret = MaybeUninit::<[u8; SHARED_SECRET_BYTES]>::uninit();

        unsafe {
            // Safety: the two buffer arguments are sized correctly and
            // always fully written.
            bssl_sys::MLKEM1024_encap(
                ciphertext.as_mut_ptr() as *mut u8,
                shared_secret.as_mut_ptr() as *mut u8,
                &*self.0,
            );
            (ciphertext.assume_init().into(), shared_secret.assume_init())
        }
    }
}

impl PrivateKey1024 {
    /// Generates a random public/private key pair returning a serialized public
    /// key, a private key, and a private seed value that can be used to
    /// regenerate the same private key in the future.
    pub fn generate() -> (Vec<u8>, PrivateKey1024, [u8; SEED_BYTES]) {
        let mut public_key = Box::new_uninit_slice(PUBLIC_KEY_BYTES_1024);
        let mut private_key = Box::new(MaybeUninit::uninit());
        let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit();

        unsafe {
            // Safety: the two buffer arguments are sized correctly and
            // always fully written. `private_key` is sized correctly via
            // the type system.
            bssl_sys::MLKEM1024_generate_key(
                public_key.as_mut_ptr() as *mut u8,
                seed.as_mut_ptr() as *mut u8,
                private_key.as_mut_ptr(),
            );

            (
                public_key.assume_init().into(),
                Self(private_key.assume_init()),
                seed.assume_init(),
            )
        }
    }

    /// Regenerate a private key from a seed value.
    pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self {
        Self(unsafe {
            initialized_boxed_struct(|priv_key| {
                // Safety: `priv_key` is correctly sized by the type system and
                // is always fully written.
                let ok = bssl_sys::MLKEM1024_private_key_from_seed(
                    priv_key,
                    seed.as_ffi_ptr(),
                    seed.len(),
                );
                // Since the seed value has the correct length, this function can
                // never fail.
                assert_eq!(ok, 1);
            })
        })
    }

    /// Derives the public key corresponding to this private key.
    pub fn to_public_key(&self) -> PublicKey1024 {
        PublicKey1024(unsafe {
            initialized_boxed_struct(|pub_key| {
                bssl_sys::MLKEM1024_public_from_private(pub_key, &*self.0);
            })
        })
    }

    /// Decapsulates a shared secret from a ciphertext. This function only
    /// returns `None` if ciphertext is the wrong length. For invalid
    /// ciphertexts it returns a key that will always be the same for the
    /// same `ciphertext` and private key, but which appears to be random
    /// unless one has access to the private key. These alternatives occur in
    /// constant time. Any subsequent symmetric encryption using the result
    /// must use an authenticated encryption scheme in order to discover the
    /// decapsulation failure.
    pub fn decapsulate(&self, ciphertext: &[u8]) -> Option<[u8; SHARED_SECRET_BYTES]> {
        unsafe {
            with_output_array_fallible(|out, _| {
                // Safety: `out` is the correct size via the type system and is
                // always fully written if the return value is one.
                bssl_sys::MLKEM1024_decap(out, ciphertext.as_ffi_ptr(), ciphertext.len(), &*self.0)
                    == 1
            })
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn basic_768() {
        let (serialized_public_key, _private_key, private_seed) = PrivateKey768::generate();
        let public_key = PublicKey768::parse(&serialized_public_key).unwrap();
        let (ciphertext, shared_key) = public_key.encapsulate();
        let private_key2 = PrivateKey768::from_seed(&private_seed);
        let shared_key2 = private_key2.decapsulate(&ciphertext).unwrap();
        assert_eq!(shared_key, shared_key2);
    }

    #[test]
    fn basic_1024() {
        let (serialized_public_key, _private_key, private_seed) = PrivateKey1024::generate();
        let public_key = PublicKey1024::parse(&serialized_public_key).unwrap();
        let (ciphertext, shared_key) = public_key.encapsulate();
        let private_key2 = PrivateKey1024::from_seed(&private_seed);
        let shared_key2 = private_key2.decapsulate(&ciphertext).unwrap();
        assert_eq!(shared_key, shared_key2);
    }

    #[test]
    fn wrong_length_ciphertext() {
        let (_serialized_public_key, private_key, _private_seed) = PrivateKey768::generate();
        assert!(matches!(private_key.decapsulate(&[0u8, 1, 2, 3]), None));

        let (_serialized_public_key, private_key, _private_seed) = PrivateKey1024::generate();
        assert!(matches!(private_key.decapsulate(&[0u8, 1, 2, 3]), None));
    }

    #[test]
    fn wrong_length_public_key() {
        assert!(matches!(PublicKey768::parse(&[0u8, 1, 2, 3]), None));
        assert!(matches!(PublicKey1024::parse(&[0u8, 1, 2, 3]), None));
    }

    #[test]
    fn marshal_public_key_768() {
        let (serialized_public_key, private_key, _) = PrivateKey768::generate();
        let public_key = PublicKey768::parse(&serialized_public_key).unwrap();
        assert_eq!(serialized_public_key, public_key.to_bytes());
        assert_eq!(
            serialized_public_key,
            private_key.to_public_key().to_bytes()
        );
    }

    #[test]
    fn marshal_public_key_1024() {
        let (serialized_public_key, private_key, _) = PrivateKey1024::generate();
        let public_key = PublicKey1024::parse(&serialized_public_key).unwrap();
        assert_eq!(serialized_public_key, public_key.to_bytes());
        assert_eq!(
            serialized_public_key,
            private_key.to_public_key().to_bytes()
        );
    }
}
