arx-kw 0.3.0

An implementation of the ARX-KW-8-2-4 Key Wrap constructions
Documentation
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![allow(clippy::doc_markdown)]
#![warn(clippy::all)]
#![warn(missing_docs)]
//! # ARX-KW
//! [![crates.io](https://img.shields.io/crates/v/arx-kw.svg)](https://crates.io/crates/arx-kw)
//! [![Docs.rs](https://docs.rs/arx-kw/badge.svg)](https://docs.rs/arx-kw)
//! [![Rust Report Card](https://rust-reportcard.xuri.me/badge/github.com/mcaveniathor/arx-kw)](https://rust-reportcard.xuri.me/report/github.com/mcaveniathor/arx-kw)
//! [![dependency status](https://deps.rs/crate/arx-kw/0.2.12/status.svg)](https://deps.rs/crate/arx-kw/0.2.12)
//! [![Build Status](https://www.travis-ci.com/mcaveniathor/arx-kw.svg?branch=main)](https://www.travis-ci.com/mcaveniathor/arx-kw)
//! [![codecov](https://codecov.io/gh/mcaveniathor/arx-kw/branch/main/graph/badge.svg?token=OVCFNGQDSH)](https://codecov.io/gh/mcaveniathor/arx-kw)
//! [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)
//!
//!
//! This library features implementations of the ARX-KW family of novel [Key Wrap](https://wikipedia.org/wiki/Key_Wrap) constructions.
//!
//! The version number for this crate will be incremented in compliance with [Semantic Versioning](https://semver.org/). 
//!
//! ---
//!
//! # Background
//!
//! ARX-KW was first presented in [this paper](https://ia.cr/2020/059) written by Satō Shinichi and submitted to the IACR Cryptology ePrint Archive in January 2020. As the name
//! suggests, these constructions make extensive use of add-rotate-xor algorithms: each of the four
//! variants specified involves both the [SipHash-2-4](https://wikipedia.org/wiki/SipHash) pseudorandom function with 128-bit output and
//! a stream cipher from the [`ChaCha`](https://en.wikipedia.org/wiki/Salsa20) family of stream ciphers.
//!
//! ARX-KW is a cipher for deteministic, authenticated encryption which aims to provide strong
//! authenticity and confidentiality while minimizing the storage overhead and simplicity of use
//! when compared to existing constructions using the ChaCha cipher which require either keeping state for a nonce and 
//! a block counter or having a substantial storage overhead in order to manage the nonce
//! statelessly.
//!
//! ARX-KW has a static overhead of 128 bits for each of its four variants without the need to keep
//! state for the nonce used by ChaCha, making the storage overhead only 50% for a 256-bit key
//!
//! ---
//!
//! # Use
//!
//! ## When
//!
//! As noted above, the ARX-KW constructions are **Key Wrap** algorithms, designed and intended to
//! protect other cryptographic keys using [symmetric encryption](https://wikipedia.org/wiki/Symmetric_encryption). It is important to note that as ARX-KW, like all Key Wrap constructions, 
//! was designed with the expectation that its input data is highly [entropic](https://wikipedia.org/wiki/Entropic_security), as is the case with secret keys. This is because it is
//! a [deterministic encryption](https://wikipedia.org/wiki/Deterministic_encryption) scheme and
//! will always yield the same ciphertext output for a given input; if used to encrypt low-entropy
//! data (as with general-purpose encryption schemes), it is vulnerable to "leakage", described here:
//!
//! > Deterministic encryption can leak information to an eavesdropper, who may recognize known ciphertexts. For example, when an adversary learns that a given ciphertext corresponds to some interesting message, they can learn something every time that ciphertext is transmitted. To gain information about the meaning of various ciphertexts, an adversary might perform a statistical analysis of messages transmitted over an encrypted channel, or attempt to correlate ciphertexts with observed actions (e.g., noting that a given ciphertext is always received immediately before a submarine dive).
//! 
//! If used to store secret key material (by nature high entropy), this is not an issue as an attacker gains no information about the key encapsulated within. 
//!

//! ## Features
//!
//! #### Nightly
//!
//! Use the `nightly` feature to enable SIMD parallelization of the ChaCha computations (nightly Rust required):
//!
//! *Cargo.toml*

//! ```toml
//! [dependencies]
//! arx-kw = {version = "0.3", features = ["nightly"]}
//! ```
//!
//!
//! #### Variants
//!
//! The four variants are gated under individual features ("e", "g", "ex", and "gx") for conditional compilation if not all
//! are going to be used. All are enabled by default, but for example if you only want to use
//! the [`gx::GX`] variant:
//!
//! *Cargo.toml*
//!
//! ```toml
//! [dependencies]
//! arx-kw = { version = "0.3", default-features=false, features=["gx"] }
//! ```
//!
//! ## How
//!
//!
//! 
//! Each public module of this crate contains a struct corresponding to one of the four specified
//! ARX-KW-8-2-4 variants: ARX-8-2-4-`E`, ARX-8-2-4-`G`, ARX-8-2-4-`EX`, and ARX-8-2-4-`GX`. If you're not
//! sure which to use, [`gx::GX`] is recommended. The functionality is provided by the `ArxKW` trait,
//! so that will need to be in scope to use the [`ArxKW::encrypt`]/[`ArxKW::encrypt_blob`] and [`ArxKW::decrypt`]/[`ArxKW::decrypt_blob`] methods. The
//! [`ConstantTimeEq`] trait from the `subtle` crate is re-exported by this crate and is implemented
//! on the [`AuthTag`] type as well as those covered by the blanket implementations `subtle`
//! provides.
//!
//! - Encryption and decryption of secret plaintext can be performed using the [`ArxKW::encrypt`]
//! and [`ArxKW::decrypt`] methods, which remove the need to keep track of nonces and how to
//! store/transport them. These methods treat authentication tags and ciphertexts as separate
//! entities; if you need the flexibility of handling them separately, use these -- otherwise, the
//! [`ArxKW::encrypt_blob`] and [`ArxKW::decrypt_blob`] methods described below offer a further layer of abstraction and
//! ease of use at no performance cost.
//!
//! - The [`ArxKW::encrypt_blob`] and [`ArxKW::decrypt_blob`] methods further improve ease of use by allowing the
//! user to treat a [`Vec<u8>`] consisting of an authentication tag  followed by the corresponding
//! ciphertext as a single opaque blob. Consequently, not only is the issue of nonce management
//! addressed by ARX-KW, but management of authentication tags as well! The blob can be stored or
//! transported in one piece, saving headache, database retrievals, and making it easy to perform
//! key wrapping in a safe and simple way.
//! 
//! [`Eq`] and [`PartialEq`] are by design *not* implemented for [`AuthTag`] to discourage equality
//! checking that is not O(1), but the internal `[u8;16]` is public should you want to live 
//!
//!
//!
//!
//!
//!>
//!> <br><br><br><br><br>
//!>  
//!> Ḑ̷͉͎̺̳̭͖̗̦̪͓̂͗͒̓̅̆̋̐́̓̓̎̊͐̍̂̈͂̇͆̇͐̉̈̄̈́̈́̓̓̾͒̕͠à̸̢̛̤̠̺̩̱̤̭̪̮̙͈̱̀̍͂̋̓̓͊̈́͊̋̀̾͌͂͘͘̚n̶̡̡̢̪̼̲̫̪̯͖̟͕͚̬̠̥̫̱̮̖̼̪͚̜͙̥̬̙̪̩̮̞̰̼̲̭̏̀̀ģ̸̨̧̳̟͙͙̳̘̥͖̮̼̻͍̯̦̖͋͆̃̏͛̒̌̅͊̃̿̄̒̋͜͜͝͝ͅ ̸̧̟̼͉̳̰̥̮̙͈͖͙͎͇̙͍͚͔͒͋͋̋̒̚͠ͅͅͅè̵̡̘̲̪͔̪̥̹̟̾̅̓͛̐̐̽̅͌̊̓̔̍̓̿̊̆̂̈́͑̽̅̿̚͝͝r̵̛̭̺̠̙̞̫̗̞̪̗̹͎͌͌͌̒̏̌̅̇̉̑̂͋̅̅̀̔̉̾̋̅̏̓͘̚ờ̸̢̡̢̥̟̗̘͉̠̣͕̮͈͍͉̳̫̲̖͖̻̝̯̟͂̊̈́͑̇́͛̏͜͠u̷̎͋͂̽̉͒́̈́̑̋́̌͂̿̋̆́͜͝͝͝s̸̡̡̡̞̞͇͖̖͍̝͖̣̪͓͖̥̟͙̫̪̗͙̯̞͍̽̃̆̒̐̐̊̓̾̚̚ͅĺ̴͕͖͎̣̞͕̙̹̓͒y̷̢̠̠͇͉̘̠̩̳̲͗̑͐̿̿̐͗͊̀̽̀͐̀̿̔̈́͘͝͝
//!> 
//!<br><br><br><br><br>
//!
//! ---
//!
//! ### Encrypt a key
//!
//! ```
//! # extern crate anyhow;
//! # use anyhow::Result;
//! extern crate hex;
//! use hex::FromHex;
//!
//! use arx_kw::{
//!     ArxKW,
//!     gx::GX,
//!     ConstantTimeEq, // From the subtle crate, allows for equality checking in constant time
//!                     // (impl'd for AuthTag and re-exported by this crate)
//!     assert_ct_eq,
//! };
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Encrypt a key using ARX-KW-8-2-4-GX with the encrypt_blob method
//!
//! // The values used here are from the test vectors in the original ARX-KW paper.
//! /* 
//!  * Inputs
//!  */ 
//!// The encryption key we are using to wrap the plaintext secret key
//! let key = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?; 
//! // The plaintext secret key we want to store/transport securely
//! let plaintext = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?; 
//!
//! /*
//!  * Expected output: 
//!  * A Vec<u8> containing the authentication tag followed by the ciphertext containing the
//!  * wrapped key. We can treat this as an opaque blob when using the encrypt_blob and decrypt_blob
//!  * methods, meaning we don't have to manually manage authentication tags or nonces.
//!  */
//! let blob_expected = <[u8; 48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc652f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;
//!
//! /*
//!  * Key wrapping performed in one line, simply passing the 
//!  * encryption key and the plaintext to be encrypted.
//!  */
//! let blob = GX::encrypt_blob(&key, &plaintext)?; 
//! assert_ct_eq!(blob, &blob_expected);
//!
//! /*
//!  * Decryption likewise is done in one line, passing the key and the blob to be decrypted.
//!  * The authentication tag is checked to match the ciphertext
//!  * during decryption and will return an error if the tags do not match.
//!  * Returns the decrypted plaintext if successful, otherwise an error.
//!  */
//! let decrypted_plaintext = GX::decrypt_blob(&key, &blob)?;
//! assert_ct_eq!(plaintext, &decrypted_plaintext);
//! # Ok(())
//! # }
//! ```
//!
//!
//!
//!
extern crate subtle;
extern crate chacha;
extern crate siphasher;
extern crate byteorder;
#[macro_use] extern crate arrayref;
extern crate thiserror;
use thiserror::Error;
mod lqb;
mod util;
mod generate;

#[cfg(feature="e")]
/// Module containing items related to the ARX-KW-8-2-4-E variant
pub mod e;
#[cfg(feature="g")]
/// Module containing items related to the ARX-KW-8-2-4-G variant
pub mod g;
#[cfg(feature="ex")]
/// Module containing items related to the ARX-KW-8-2-4-EX variant
pub mod ex;
#[cfg(feature="gx")]
/// Module containing items related to the ARX-KW-8-2-4-GX variant
pub mod gx;
pub use subtle::{ConstantTimeEq,Choice};

#[derive(Error,Debug)]
/// An error denoting that a value was of an invalid length. Typically this will be used as a
/// variant of [`ArxKwError`] rather than on its own.
pub enum InvalidLengthError {
    #[error("Invalid length: {0} (expected {1})")]
    /// Invalid length in a context expecting a fixed length.
    _Fixed(usize,usize),
    /// Invalid length in a context which accepts a variable length (e.g. plaintext and ciphertext
    /// inputs)
    #[error("Invalid length: {0} (Maximum: {1}")]
    UpTo(usize,usize)
}
#[derive(Error,Debug)]
/// The error type used by this crate.
pub enum ArxKwError {
    #[error("Invalid length: {0}")]
    /// See [`InvalidLengthError`]
    InvalidLength(#[from] InvalidLengthError),
    #[error("Reached end of {0}ChaCha8 stream.")]
    /// Occurs if a function using either ChaCha8 or XChaCha8 reaches the end of the stream
    ChaChaError(String), // Use "X" if it occurs while using an extended stream or "" otherwise
    #[error("Authentication tag does not match {0:x?} (Expected {1:x?})")]
    /// Returns if an authentication tag mismatch occurs during decryption
    BadTags(AuthTag,AuthTag)
}

/// The type used as the authentication tag (unencrypted data to be stored alongside encrypted keys) 
/// This is the same for all variants at time of writing (a single, static 128 bits), making
/// for a 50% storage overhead for a 256-bit key like those used for `ChaCha`
///
/// The [`ConstantTimeEq`] trait is implemented (and re-exported from the `subtle` crate by this crate) for constant
/// time equality checking of `AuthTag`s
#[derive(Debug,Clone,Copy,)]
pub struct AuthTag(pub [u8; 16]);
impl std::convert::AsRef<[u8;16]> for AuthTag {
    #[cfg(not(tarpaulin_include))]
    fn as_ref(&self) -> &[u8;16] {
        &self.0
    }
}

impl ConstantTimeEq for AuthTag {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.0.ct_eq(&other.0)
    }
}
/// Macro which provides an equivalent of [`assert_eq`] in constant time using the [`ConstantTimeEq`]
/// trait. Accordingly, ConstantTimeEq must be in scope and implemented on the types of $x and $y
/// for this to work. It is implemented for [`AuthTag`] and many primitives.
#[macro_export]
macro_rules! assert_ct_eq {
    ($x:expr, $y:expr) => {
        if bool::from($x.ct_eq($y)) {
        }
        else {
            panic!("")
        }
    }
}


/// Provides encryption and decryption capabilites
///
/// The ArxKW trait requires a fixed-length array reference for keys and authentication tags
/// but the ciphertext and plaintext inputs can be slices (their lengths are verified to be valid
/// if used with the E and G variants).
///
/// The [`ArxKW::encrypt_blob`] and [`ArxKW::decrypt_blob`] are preferable to [`ArxKW::encrypt`]  and [`ArxKW::decrypt`] in
/// most cases, as they eliminate the need to manually manage authentication tags without a
/// performance penalty, keeping with the
/// spirit of ARX-KW (which was designed with removing the burden of nonce and block counter management as a primary
/// goal.)
pub trait ArxKW {
    /// The type of data which is used as a key for the type that `impl`s this trait.
    /// Note that this is not the same for all variants of ARX-KW. all of the currently-defined variants use the same-sized keys
    type Key;
    /// Encrypts the plaintext using ARX-KW and returns the encrypted ciphertext and an [`AuthTag`]
    ///
    /// The authentication tag can be stored/transported alongside it and is needed (along with the
    /// key used to encrypt the plaintext) in order to decrypt the wrapped key.
    ///
    /// # Errors
    ///
    /// Returns an error if the key or plaintext is
    /// of invalid length or if the end of the ChaCha cipher is reached unexpectedly.
    fn encrypt(key: &Self::Key, plaintext: &[u8]) -> Result<(Vec<u8>, AuthTag), ArxKwError>;
    /// Attempts to decrypt the ciphertext using ARX-KW and returns the decrypted plaintext if
    /// successful. As ARX-KW is a form of authenticated encryption, the authenticity of the
    /// decrypted text is verified if the function returns an `Ok` value.
    /// which can be stored/transported alongside it. 
    ///
    ///# Errors
    ///
    /// Returns an error if the key or ciphertext is of invalid length or if the end of the ChaCha cipher is reached unexpectedly.
    fn decrypt(key: &Self::Key, ciphertext: &[u8], authentication_tag: &AuthTag) -> Result<Vec<u8>, ArxKwError>;


    /// Encrypts the plaintext and returns a [`Vec<u8>`] containing both the authentication tag
    /// and the ciphertext. 
    ///
    /// While ARX-KW by design eliminates the need for nonce management, it can
    /// be further used to eliminate the complexity of managing authentication tags as well without
    /// incurring a large storage overhead. The `encrypt_blob` and `decrypt_blob` methods allow for
    /// this abstraction; the ciphertext and authentication tag can be treated as one opaque "blob" of bytes
    /// and so authentication and decryption of that blob can be done with just the key,
    /// eliminating the need to separately store a nonce or authentication tag. This gives a
    /// user-friendly interface to deterministic and authenticated encryption.
    ///
    /// # Errors
    ///
    /// Returns an error if the key or plaintext is of invalid length or if the end of the
    /// \[X\]ChaCha stream is reached unexpectedly
    ///
    ///```
    /// extern crate arx_kw;
    /// use arx_kw::{ArxKW,gx::GX,assert_ct_eq,ConstantTimeEq};
    /// extern crate hex;
    /// use hex::FromHex;
    /// # extern crate anyhow;
    ///
    /// # fn main() -> anyhow::Result<()> {
    /// let key = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?; // The key being used to encrypt the plaintext
    /// let plaintext = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?; // The key that we are trying to encrypt, as plaintext
    /// // The expected output: a "blob" consisting of the authentication tag followed by ciphertext. This can be treated as one opaque piece of data when using encrypt_blob and decrypt_blob
    /// let blob_expected = <[u8; 48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc652f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;   
    /// let blob = GX::encrypt_blob(&key, &plaintext)?; // The output of encrypt_blob, a Vec<u8>
    /// assert_ct_eq!(blob, &blob_expected);
    /// # Ok(())
    /// # }
    ///```
    fn encrypt_blob(key: &Self::Key, plaintext: &[u8]) -> Result<Vec<u8>, ArxKwError> {
        let (mut ciphertext, authentication_tag) = Self::encrypt(key, plaintext)?;
        let mut blob = Vec::with_capacity(ciphertext.len()+16);
        blob.append(&mut authentication_tag.as_ref().to_vec());
        blob.append(&mut ciphertext);
        Ok(blob)
    }

    /// Decrypts a blob containing an authentication tag followed by the corresponding ciphertext
    ///
    /// If decryption is successful, returns a [`Vec<u8>`] containing the decrypted plaintext.
    ///
    /// While ARX-KW by design eliminates the need for nonce management, it can
    /// be further used to eliminate the complexity of managing authentication tags as well without
    /// incurring a large storage overhead. The `encrypt_blob` and `decrypt_blob` methods allow for
    /// this abstraction; the ciphertext and authentication tag can be treated as one opaque "blob" of bytes
    /// and so authentication and decryption of that blob can be done with just the key,
    /// eliminating the need to separately store a nonce or authentication tag. This gives a
    /// user-friendly interface to deterministic and authenticated encryption.
    ///
    /// # Errors
    ///
    /// Returns an error if the key or ciphertext is of invalid length, the authentication tag does
    /// not match the ciphertext that follows it, or if the end of the \[X\]ChaCha stream is reached unexpectedly
    ///
    /// ```
    /// extern crate arx_kw;
    /// use arx_kw::{ArxKW,e::E,assert_ct_eq,ConstantTimeEq};
    /// extern crate hex;
    /// use hex::FromHex;
    /// # extern crate anyhow;
    ///
    /// # fn main() -> anyhow::Result<()> {
    /// let key = <[u8; 48]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f")?;
    /// let blob = <[u8; 48]>::from_hex("c4f21d3b4dbcc566c3a73bbc59790f2fe6457d24abaf7c2ebdb91416a18366d31a66db61a4e45c9f42a119c353bb1eb1")?;
    /// let plaintext_expected = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
    /// let plaintext = E::decrypt_blob(&key, &blob)?;
    /// assert_ct_eq!(plaintext, &plaintext_expected);
    /// # Ok(())
    /// # }
    ///```
    ///
    fn decrypt_blob(key: &Self::Key, blob: &[u8]) -> Result<Vec<u8>, ArxKwError> {
        let authentication_tag = AuthTag(*array_ref![blob,0,16]);
        let ciphertext = &blob[16..];
        Self::decrypt(key, ciphertext, &authentication_tag)
    }
}