This library features Rust implementations of the ARX-KW family of novel Key Wrap constructions.
The version number for this crate will be incremented in compliance with Semantic Versioning.
ARX-KW was first presented in this paper 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 pseudorandom function with 128-bit output and a stream cipher from the ChaCha 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 the nightly
feature to enable SIMD parallelization of the ChaCha computations (nightly Rust required):
Cargo.toml
[dependencies]
arx-kw = {version = "0.3", features = ["nightly"]}
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"] }
As noted above, the ARX-KW constructions are Key Wrap algorithms, designed and intended to protect other cryptographic keys using symmetric encryption. It is important to note that ARX-KW, like all Key Wrap constructions, was designed with the expectation that its input data is highly entropic, as is the case with secret cryptographic keys. This is because it is a 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.
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 two methods treat authentication tags and ciphertexts as separate
entities; if you need the flexibility of handling them separately, use them -- 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
Ḑ̷͉͎̺̳̭͖̗̦̪͓̂͗͒̓̅̆̋̐́̓̓̎̊͐̍̂̈͂̇͆̇͐̉̈̄̈́̈́̓̓̾͒̕͠à̸̢̛̤̠̺̩̱̤̭̪̮̙͈̱̀̍͂̋̓̓͊̈́͊̋̀̾͌͂͘͘̚n̶̡̡̢̪̼̲̫̪̯͖̟͕͚̬̠̥̫̱̮̖̼̪͚̜͙̥̬̙̪̩̮̞̰̼̲̭̏̀̀ģ̸̨̧̳̟͙͙̳̘̥͖̮̼̻͍̯̦̖͋͆̃̏͛̒̌̅͊̃̿̄̒̋͜͜͝͝ͅ ̸̧̟̼͉̳̰̥̮̙͈͖͙͎͇̙͍͚͔͒͋͋̋̒̚͠ͅͅͅè̵̡̘̲̪͔̪̥̹̟̾̅̓͛̐̐̽̅͌̊̓̔̍̓̿̊̆̂̈́͑̽̅̿̚͝͝r̵̛̭̺̠̙̞̫̗̞̪̗̹͎͌͌͌̒̏̌̅̇̉̑̂͋̅̅̀̔̉̾̋̅̏̓͘̚ờ̸̢̡̢̥̟̗̘͉̠̣͕̮͈͍͉̳̫̲̖͖̻̝̯̟͂̊̈́͑̇́͛̏͜͠u̷̎͋͂̽̉͒́̈́̑̋́̌͂̿̋̆́͜͝͝͝s̸̡̡̡̞̞͇͖̖͍̝͖̣̪͓͖̥̟͙̫̪̗͙̯̞͍̽̃̆̒̐̐̊̓̾̚̚ͅĺ̴͕͖͎̣̞͕̙̹̓͒y̷̢̠̠͇͉̘̠̩̳̲͗̑͐̿̿̐͗͊̀̽̀͐̀̿̔̈́͘͝͝
br>
```rust extern crate hex; use hex::FromHex; extern crate arxkw; use arxkw::{ 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) assertcteq, };
fn main() -> Result<(), Box
/* * Inputs */ // The encryption key we are using to wrap the plaintext secret key let key = <[u8; 32]>::fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?; // The plaintext secret key we want to store/transport securely let plaintext = <[u8; 32]>::fromhex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
/*
* Expected output:
* A Vec
/* * Key wrapping performed in one line, simply passing the * encryption key and the plaintext to be encrypted. */ let blob = GX::encryptblob(&key, &plaintext)?; assertcteq!(blob, &blobexpected);
/* * 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 decryptedplaintext = GX::decryptblob(&key, &blob)?; assertcteq!(plaintext, &decrypted_plaintext); Ok(()) } ```
The benches directory contains encrypt and decrypt benchmarks for each ARX-KW variant. This crate uses the criterion
crate
for benchmarking, so the benchmarks can be run on stable or nightly Rust and offer more detailed output.
Conducted using the criterion
crate on my machine using the nightly
feature with a Ryzen 1700 @ 3.8 GHz and 8GB of RAM at 3000MHz.
- Benchmarks
cargo bench
cargo --features nightly bench
If you run the benchmarks without the nightly feature and then with it, the output will show you the change in execution time, for those curious.
Tests for encryption and decryption are provided for each of the four variants and use the test vectors from the original ARX-KW paper, along with a couple of doctests. They can be run using cargo test
Documentation for the latest crate version is available here: - docs.rs
Or for the latest commit to the main branch of this repository: - Main Branch