1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
#![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)
    }
}