arx_kw/
lib.rs

1#![warn(clippy::pedantic)]
2#![warn(clippy::nursery)]
3#![allow(clippy::doc_markdown)]
4#![warn(clippy::all)]
5#![warn(missing_docs)]
6//! # ARX-KW
7//! [![crates.io](https://img.shields.io/crates/v/arx-kw.svg)](https://crates.io/crates/arx-kw)
8//! [![Docs.rs](https://docs.rs/arx-kw/badge.svg)](https://docs.rs/arx-kw)
9//! [![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)
10//! [![dependency status](https://deps.rs/crate/arx-kw/0.2.12/status.svg)](https://deps.rs/crate/arx-kw/0.2.12)
11//! [![Build Status](https://www.travis-ci.com/mcaveniathor/arx-kw.svg?branch=main)](https://www.travis-ci.com/mcaveniathor/arx-kw)
12//! [![codecov](https://codecov.io/gh/mcaveniathor/arx-kw/branch/main/graph/badge.svg?token=OVCFNGQDSH)](https://codecov.io/gh/mcaveniathor/arx-kw)
13//! [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)
14//!
15//!
16//! This library features implementations of the ARX-KW family of novel [Key Wrap](https://wikipedia.org/wiki/Key_Wrap) constructions.
17//!
18//! The version number for this crate will be incremented in compliance with [Semantic Versioning](https://semver.org/). 
19//!
20//! ---
21//!
22//! # Background
23//!
24//! 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
25//! suggests, these constructions make extensive use of add-rotate-xor algorithms: each of the four
26//! variants specified involves both the [SipHash-2-4](https://wikipedia.org/wiki/SipHash) pseudorandom function with 128-bit output and
27//! a stream cipher from the [`ChaCha`](https://en.wikipedia.org/wiki/Salsa20) family of stream ciphers.
28//!
29//! ARX-KW is a cipher for deteministic, authenticated encryption which aims to provide strong
30//! authenticity and confidentiality while minimizing the storage overhead and simplicity of use
31//! when compared to existing constructions using the ChaCha cipher which require either keeping state for a nonce and 
32//! a block counter or having a substantial storage overhead in order to manage the nonce
33//! statelessly.
34//!
35//! ARX-KW has a static overhead of 128 bits for each of its four variants without the need to keep
36//! state for the nonce used by ChaCha, making the storage overhead only 50% for a 256-bit key
37//!
38//! ---
39//!
40//! # Use
41//!
42//! ## When
43//!
44//! As noted above, the ARX-KW constructions are **Key Wrap** algorithms, designed and intended to
45//! 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, 
46//! 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
47//! a [deterministic encryption](https://wikipedia.org/wiki/Deterministic_encryption) scheme and
48//! will always yield the same ciphertext output for a given input; if used to encrypt low-entropy
49//! data (as with general-purpose encryption schemes), it is vulnerable to "leakage", described here:
50//!
51//! > 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).
52//! 
53//! 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. 
54//!
55
56//! ## Features
57//!
58//! #### Nightly
59//!
60//! Use the `nightly` feature to enable SIMD parallelization of the ChaCha computations (nightly Rust required):
61//!
62//! *Cargo.toml*
63
64//! ```toml
65//! [dependencies]
66//! arx-kw = {version = "0.3", features = ["nightly"]}
67//! ```
68//!
69//!
70//! #### Variants
71//!
72//! The four variants are gated under individual features ("e", "g", "ex", and "gx") for conditional compilation if not all
73//! are going to be used. All are enabled by default, but for example if you only want to use
74//! the [`gx::GX`] variant:
75//!
76//! *Cargo.toml*
77//!
78//! ```toml
79//! [dependencies]
80//! arx-kw = { version = "0.3", default-features=false, features=["gx"] }
81//! ```
82//!
83//! ## How
84//!
85//!
86//! 
87//! Each public module of this crate contains a struct corresponding to one of the four specified
88//! 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
89//! sure which to use, [`gx::GX`] is recommended. The functionality is provided by the `ArxKW` trait,
90//! so that will need to be in scope to use the [`ArxKW::encrypt`]/[`ArxKW::encrypt_blob`] and [`ArxKW::decrypt`]/[`ArxKW::decrypt_blob`] methods. The
91//! [`ConstantTimeEq`] trait from the `subtle` crate is re-exported by this crate and is implemented
92//! on the [`AuthTag`] type as well as those covered by the blanket implementations `subtle`
93//! provides.
94//!
95//! - Encryption and decryption of secret plaintext can be performed using the [`ArxKW::encrypt`]
96//! and [`ArxKW::decrypt`] methods, which remove the need to keep track of nonces and how to
97//! store/transport them. These methods treat authentication tags and ciphertexts as separate
98//! entities; if you need the flexibility of handling them separately, use these -- otherwise, the
99//! [`ArxKW::encrypt_blob`] and [`ArxKW::decrypt_blob`] methods described below offer a further layer of abstraction and
100//! ease of use at no performance cost.
101//!
102//! - The [`ArxKW::encrypt_blob`] and [`ArxKW::decrypt_blob`] methods further improve ease of use by allowing the
103//! user to treat a [`Vec<u8>`] consisting of an authentication tag  followed by the corresponding
104//! ciphertext as a single opaque blob. Consequently, not only is the issue of nonce management
105//! addressed by ARX-KW, but management of authentication tags as well! The blob can be stored or
106//! transported in one piece, saving headache, database retrievals, and making it easy to perform
107//! key wrapping in a safe and simple way.
108//! 
109//! [`Eq`] and [`PartialEq`] are by design *not* implemented for [`AuthTag`] to discourage equality
110//! checking that is not O(1), but the internal `[u8;16]` is public should you want to live 
111//!
112//!
113//!
114//!
115//!
116//!>
117//!> <br><br><br><br><br>
118//!>  
119//!> Ḑ̷͉͎̺̳̭͖̗̦̪͓̂͗͒̓̅̆̋̐́̓̓̎̊͐̍̂̈͂̇͆̇͐̉̈̄̈́̈́̓̓̾͒̕͠à̸̢̛̤̠̺̩̱̤̭̪̮̙͈̱̀̍͂̋̓̓͊̈́͊̋̀̾͌͂͘͘̚n̶̡̡̢̪̼̲̫̪̯͖̟͕͚̬̠̥̫̱̮̖̼̪͚̜͙̥̬̙̪̩̮̞̰̼̲̭̏̀̀ģ̸̨̧̳̟͙͙̳̘̥͖̮̼̻͍̯̦̖͋͆̃̏͛̒̌̅͊̃̿̄̒̋͜͜͝͝ͅ ̸̧̟̼͉̳̰̥̮̙͈͖͙͎͇̙͍͚͔͒͋͋̋̒̚͠ͅͅͅè̵̡̘̲̪͔̪̥̹̟̾̅̓͛̐̐̽̅͌̊̓̔̍̓̿̊̆̂̈́͑̽̅̿̚͝͝r̵̛̭̺̠̙̞̫̗̞̪̗̹͎͌͌͌̒̏̌̅̇̉̑̂͋̅̅̀̔̉̾̋̅̏̓͘̚ờ̸̢̡̢̥̟̗̘͉̠̣͕̮͈͍͉̳̫̲̖͖̻̝̯̟͂̊̈́͑̇́͛̏͜͠u̷̎͋͂̽̉͒́̈́̑̋́̌͂̿̋̆́͜͝͝͝s̸̡̡̡̞̞͇͖̖͍̝͖̣̪͓͖̥̟͙̫̪̗͙̯̞͍̽̃̆̒̐̐̊̓̾̚̚ͅĺ̴͕͖͎̣̞͕̙̹̓͒y̷̢̠̠͇͉̘̠̩̳̲͗̑͐̿̿̐͗͊̀̽̀͐̀̿̔̈́͘͝͝
120//!> 
121//!<br><br><br><br><br>
122//!
123//! ---
124//!
125//! ### Encrypt a key
126//!
127//! ```
128//! # extern crate anyhow;
129//! # use anyhow::Result;
130//! extern crate hex;
131//! use hex::FromHex;
132//!
133//! use arx_kw::{
134//!     ArxKW,
135//!     gx::GX,
136//!     ConstantTimeEq, // From the subtle crate, allows for equality checking in constant time
137//!                     // (impl'd for AuthTag and re-exported by this crate)
138//!     assert_ct_eq,
139//! };
140//!
141//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
142//! // Encrypt a key using ARX-KW-8-2-4-GX with the encrypt_blob method
143//!
144//! // The values used here are from the test vectors in the original ARX-KW paper.
145//! /* 
146//!  * Inputs
147//!  */ 
148//!// The encryption key we are using to wrap the plaintext secret key
149//! let key = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?; 
150//! // The plaintext secret key we want to store/transport securely
151//! let plaintext = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?; 
152//!
153//! /*
154//!  * Expected output: 
155//!  * A Vec<u8> containing the authentication tag followed by the ciphertext containing the
156//!  * wrapped key. We can treat this as an opaque blob when using the encrypt_blob and decrypt_blob
157//!  * methods, meaning we don't have to manually manage authentication tags or nonces.
158//!  */
159//! let blob_expected = <[u8; 48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc652f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;
160//!
161//! /*
162//!  * Key wrapping performed in one line, simply passing the 
163//!  * encryption key and the plaintext to be encrypted.
164//!  */
165//! let blob = GX::encrypt_blob(&key, &plaintext)?; 
166//! assert_ct_eq!(blob, &blob_expected);
167//!
168//! /*
169//!  * Decryption likewise is done in one line, passing the key and the blob to be decrypted.
170//!  * The authentication tag is checked to match the ciphertext
171//!  * during decryption and will return an error if the tags do not match.
172//!  * Returns the decrypted plaintext if successful, otherwise an error.
173//!  */
174//! let decrypted_plaintext = GX::decrypt_blob(&key, &blob)?;
175//! assert_ct_eq!(plaintext, &decrypted_plaintext);
176//! # Ok(())
177//! # }
178//! ```
179//!
180//!
181//!
182//!
183extern crate subtle;
184extern crate chacha;
185extern crate siphasher;
186extern crate byteorder;
187#[macro_use] extern crate arrayref;
188extern crate thiserror;
189use thiserror::Error;
190mod lqb;
191mod util;
192mod generate;
193
194#[cfg(feature="e")]
195/// Module containing items related to the ARX-KW-8-2-4-E variant
196pub mod e;
197#[cfg(feature="g")]
198/// Module containing items related to the ARX-KW-8-2-4-G variant
199pub mod g;
200#[cfg(feature="ex")]
201/// Module containing items related to the ARX-KW-8-2-4-EX variant
202pub mod ex;
203#[cfg(feature="gx")]
204/// Module containing items related to the ARX-KW-8-2-4-GX variant
205pub mod gx;
206pub use subtle::{ConstantTimeEq,Choice};
207
208#[derive(Error,Debug)]
209/// An error denoting that a value was of an invalid length. Typically this will be used as a
210/// variant of [`ArxKwError`] rather than on its own.
211pub enum InvalidLengthError {
212    #[error("Invalid length: {0} (expected {1})")]
213    /// Invalid length in a context expecting a fixed length.
214    _Fixed(usize,usize),
215    /// Invalid length in a context which accepts a variable length (e.g. plaintext and ciphertext
216    /// inputs)
217    #[error("Invalid length: {0} (Maximum: {1}")]
218    UpTo(usize,usize)
219}
220#[derive(Error,Debug)]
221/// The error type used by this crate.
222pub enum ArxKwError {
223    #[error("Invalid length: {0}")]
224    /// See [`InvalidLengthError`]
225    InvalidLength(#[from] InvalidLengthError),
226    #[error("Reached end of {0}ChaCha8 stream.")]
227    /// Occurs if a function using either ChaCha8 or XChaCha8 reaches the end of the stream
228    ChaChaError(String), // Use "X" if it occurs while using an extended stream or "" otherwise
229    #[error("Authentication tag does not match {0:x?} (Expected {1:x?})")]
230    /// Returns if an authentication tag mismatch occurs during decryption
231    BadTags(AuthTag,AuthTag)
232}
233
234/// The type used as the authentication tag (unencrypted data to be stored alongside encrypted keys) 
235/// This is the same for all variants at time of writing (a single, static 128 bits), making
236/// for a 50% storage overhead for a 256-bit key like those used for `ChaCha`
237///
238/// The [`ConstantTimeEq`] trait is implemented (and re-exported from the `subtle` crate by this crate) for constant
239/// time equality checking of `AuthTag`s
240#[derive(Debug,Clone,Copy,)]
241pub struct AuthTag(pub [u8; 16]);
242impl std::convert::AsRef<[u8;16]> for AuthTag {
243    #[cfg(not(tarpaulin_include))]
244    fn as_ref(&self) -> &[u8;16] {
245        &self.0
246    }
247}
248
249impl ConstantTimeEq for AuthTag {
250    fn ct_eq(&self, other: &Self) -> Choice {
251        self.0.ct_eq(&other.0)
252    }
253}
254/// Macro which provides an equivalent of [`assert_eq`] in constant time using the [`ConstantTimeEq`]
255/// trait. Accordingly, ConstantTimeEq must be in scope and implemented on the types of $x and $y
256/// for this to work. It is implemented for [`AuthTag`] and many primitives.
257#[macro_export]
258macro_rules! assert_ct_eq {
259    ($x:expr, $y:expr) => {
260        if bool::from($x.ct_eq($y)) {
261        }
262        else {
263            panic!("")
264        }
265    }
266}
267
268
269/// Provides encryption and decryption capabilites
270///
271/// The ArxKW trait requires a fixed-length array reference for keys and authentication tags
272/// but the ciphertext and plaintext inputs can be slices (their lengths are verified to be valid
273/// if used with the E and G variants).
274///
275/// The [`ArxKW::encrypt_blob`] and [`ArxKW::decrypt_blob`] are preferable to [`ArxKW::encrypt`]  and [`ArxKW::decrypt`] in
276/// most cases, as they eliminate the need to manually manage authentication tags without a
277/// performance penalty, keeping with the
278/// spirit of ARX-KW (which was designed with removing the burden of nonce and block counter management as a primary
279/// goal.)
280pub trait ArxKW {
281    /// The type of data which is used as a key for the type that `impl`s this trait.
282    /// Note that this is not the same for all variants of ARX-KW. all of the currently-defined variants use the same-sized keys
283    type Key;
284    /// Encrypts the plaintext using ARX-KW and returns the encrypted ciphertext and an [`AuthTag`]
285    ///
286    /// The authentication tag can be stored/transported alongside it and is needed (along with the
287    /// key used to encrypt the plaintext) in order to decrypt the wrapped key.
288    ///
289    /// # Errors
290    ///
291    /// Returns an error if the key or plaintext is
292    /// of invalid length or if the end of the ChaCha cipher is reached unexpectedly.
293    fn encrypt(key: &Self::Key, plaintext: &[u8]) -> Result<(Vec<u8>, AuthTag), ArxKwError>;
294    /// Attempts to decrypt the ciphertext using ARX-KW and returns the decrypted plaintext if
295    /// successful. As ARX-KW is a form of authenticated encryption, the authenticity of the
296    /// decrypted text is verified if the function returns an `Ok` value.
297    /// which can be stored/transported alongside it. 
298    ///
299    ///# Errors
300    ///
301    /// Returns an error if the key or ciphertext is of invalid length or if the end of the ChaCha cipher is reached unexpectedly.
302    fn decrypt(key: &Self::Key, ciphertext: &[u8], authentication_tag: &AuthTag) -> Result<Vec<u8>, ArxKwError>;
303
304
305    /// Encrypts the plaintext and returns a [`Vec<u8>`] containing both the authentication tag
306    /// and the ciphertext. 
307    ///
308    /// While ARX-KW by design eliminates the need for nonce management, it can
309    /// be further used to eliminate the complexity of managing authentication tags as well without
310    /// incurring a large storage overhead. The `encrypt_blob` and `decrypt_blob` methods allow for
311    /// this abstraction; the ciphertext and authentication tag can be treated as one opaque "blob" of bytes
312    /// and so authentication and decryption of that blob can be done with just the key,
313    /// eliminating the need to separately store a nonce or authentication tag. This gives a
314    /// user-friendly interface to deterministic and authenticated encryption.
315    ///
316    /// # Errors
317    ///
318    /// Returns an error if the key or plaintext is of invalid length or if the end of the
319    /// \[X\]ChaCha stream is reached unexpectedly
320    ///
321    ///```
322    /// extern crate arx_kw;
323    /// use arx_kw::{ArxKW,gx::GX,assert_ct_eq,ConstantTimeEq};
324    /// extern crate hex;
325    /// use hex::FromHex;
326    /// # extern crate anyhow;
327    ///
328    /// # fn main() -> anyhow::Result<()> {
329    /// let key = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?; // The key being used to encrypt the plaintext
330    /// let plaintext = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?; // The key that we are trying to encrypt, as plaintext
331    /// // 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
332    /// let blob_expected = <[u8; 48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc652f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;   
333    /// let blob = GX::encrypt_blob(&key, &plaintext)?; // The output of encrypt_blob, a Vec<u8>
334    /// assert_ct_eq!(blob, &blob_expected);
335    /// # Ok(())
336    /// # }
337    ///```
338    fn encrypt_blob(key: &Self::Key, plaintext: &[u8]) -> Result<Vec<u8>, ArxKwError> {
339        let (mut ciphertext, authentication_tag) = Self::encrypt(key, plaintext)?;
340        let mut blob = Vec::with_capacity(ciphertext.len()+16);
341        blob.append(&mut authentication_tag.as_ref().to_vec());
342        blob.append(&mut ciphertext);
343        Ok(blob)
344    }
345
346    /// Decrypts a blob containing an authentication tag followed by the corresponding ciphertext
347    ///
348    /// If decryption is successful, returns a [`Vec<u8>`] containing the decrypted plaintext.
349    ///
350    /// While ARX-KW by design eliminates the need for nonce management, it can
351    /// be further used to eliminate the complexity of managing authentication tags as well without
352    /// incurring a large storage overhead. The `encrypt_blob` and `decrypt_blob` methods allow for
353    /// this abstraction; the ciphertext and authentication tag can be treated as one opaque "blob" of bytes
354    /// and so authentication and decryption of that blob can be done with just the key,
355    /// eliminating the need to separately store a nonce or authentication tag. This gives a
356    /// user-friendly interface to deterministic and authenticated encryption.
357    ///
358    /// # Errors
359    ///
360    /// Returns an error if the key or ciphertext is of invalid length, the authentication tag does
361    /// not match the ciphertext that follows it, or if the end of the \[X\]ChaCha stream is reached unexpectedly
362    ///
363    /// ```
364    /// extern crate arx_kw;
365    /// use arx_kw::{ArxKW,e::E,assert_ct_eq,ConstantTimeEq};
366    /// extern crate hex;
367    /// use hex::FromHex;
368    /// # extern crate anyhow;
369    ///
370    /// # fn main() -> anyhow::Result<()> {
371    /// let key = <[u8; 48]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f")?;
372    /// let blob = <[u8; 48]>::from_hex("c4f21d3b4dbcc566c3a73bbc59790f2fe6457d24abaf7c2ebdb91416a18366d31a66db61a4e45c9f42a119c353bb1eb1")?;
373    /// let plaintext_expected = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
374    /// let plaintext = E::decrypt_blob(&key, &blob)?;
375    /// assert_ct_eq!(plaintext, &plaintext_expected);
376    /// # Ok(())
377    /// # }
378    ///```
379    ///
380    fn decrypt_blob(key: &Self::Key, blob: &[u8]) -> Result<Vec<u8>, ArxKwError> {
381        let authentication_tag = AuthTag(*array_ref![blob,0,16]);
382        let ciphertext = &blob[16..];
383        Self::decrypt(key, ciphertext, &authentication_tag)
384    }
385}