diff --git a/.travis.yml b/.travis.yml index 9e5fd68..36650f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,12 +32,15 @@ matrix: - language: rust rust: nightly before_script: cd rust + env: + - RUSTFLAGS=-Ctarget-feature=+aes - language: rust rust: nightly before_script: cd rust/tests/ffi env: - CC=clang - LDFLAGS=-rtlib=compiler-rt + - RUSTFLAGS=-Ctarget-feature=+aes script: - make - ./ffi_test diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4027496..dd42add 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,6 +1,6 @@ [[package]] name = "aesni" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -20,6 +20,20 @@ dependencies = [ "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "block-modes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.2.1" @@ -142,15 +156,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "miscreant" version = "0.3.0" dependencies = [ - "aesni 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aesni 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-modes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cmac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-mac 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "dbl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pmac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -263,9 +276,11 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum aesni 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7ba47de7c13f758a674d0685118346945e8bb0e2e906da6e36e403788a73662" +"checksum aesni 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f167a87ec3aab2a7b5084689d2c6e7342dc0d865de8cd895cf95f820bf98164" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum block-cipher-trait 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6136d803280ae3532efa36114335255ea94f3d75d735ddedd66b0d7cd30bad3" +"checksum block-modes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "862511b40f91a3305dc119fdfdc25b561779f78828495e41b71d360b8c9f56df" +"checksum block-padding 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75bc2cfa52dc218b47ea000b15e6e5d00ca2f831db31e41592383c14d8802907" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 54efd60..70b1f31 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -14,14 +14,13 @@ keywords = ["cryptography", "encryption", "security", "streaming"] crate-type = ["rlib", "staticlib"] [dependencies] -aesni = "0.2" +aesni = "0.3" crypto-mac = "0.6" -block-cipher-trait = "0.5" +block-modes = "0.1" byteorder = { version = "1.2", default-features = false } clear_on_drop = { version = "0.2", features = ["nightly"] } cmac = "0.1" dbl = "0.1" -generic-array = "0.9" pmac = "0.1" ring = { version = "0.11", optional = true } subtle = { version = "0.3", default-features = false } diff --git a/rust/README.md b/rust/README.md index 30cfc6c..9261540 100644 --- a/rust/README.md +++ b/rust/README.md @@ -48,19 +48,28 @@ For more information, see the [toplevel README.md]. ## Requirements -This library presently requires the following: +miscreant.rs presently requires the following: * **x86_64** CPU architecture * Rust **nightly** compiler -This library implements the AES cipher using the [aesni] crate, which -uses the [Intel AES-NI] CPU instructions to provide a fast, constant-time -hardware-based implementation. No software-only implementation of AES is -provided. Additionally it includes Intel assembly language implementations of -certain secret-dependent functions which have verified constant-time operation. +This is because it depends on the `aesni` crate which uses the `core::arch` API +for (soon-to-be) stable access to CPU intrinsics, namely the [Intel AES-NI] CPU +instructions which provide a hardware implementation of AES. -Supporting stable Rust will require upstream changes in the [aesni] crate, -which is nightly-only due to its use of inline assembly. +To access these features, you will need both a relatively recent +Rust nightly and to pass the following as RUSTFLAGS: + +``` +RUSTFLAGS=-C target-feature=+aes +``` + +You can configure your `~/.cargo/config` to always pass these flags: + +```toml +[build] +rustflags = ["-C", "target-feature=+aes"] +``` [aesni]: https://github.com/RustCrypto/block-ciphers [Intel AES-NI]: https://software.intel.com/en-us/blogs/2012/01/11/aes-ni-in-laymens-terms diff --git a/rust/src/aead.rs b/rust/src/aead.rs index 8424cc2..31a2c9d 100644 --- a/rust/src/aead.rs +++ b/rust/src/aead.rs @@ -3,6 +3,9 @@ //! and authenticity. use aesni::{Aes128, Aes256}; +use aesni::block_cipher_trait::BlockCipher; +use aesni::block_cipher_trait::generic_array::{ArrayLength, GenericArray}; +use aesni::block_cipher_trait::generic_array::typenum::{U16, U32, U64}; use cmac::Cmac; use core::marker::PhantomData; use crypto_mac::Mac; @@ -10,8 +13,6 @@ use ctr::{Aes128Ctr, Aes256Ctr, Ctr}; #[cfg(feature = "std")] use ctr::IV_SIZE; use error::Error; -use generic_array::ArrayLength; -use generic_array::typenum::{U16, U32, U64}; use pmac::Pmac; use siv::Siv; @@ -96,26 +97,36 @@ pub trait Algorithm { } /// AEAD interface provider for AES-(PMAC-)SIV types -pub struct SivAlgorithm, K: ArrayLength> { - siv: Siv, +pub struct SivAlgorithm +where + B: BlockCipher, + B::ParBlocks: ArrayLength>, + C: Ctr, + M: Mac, + K: ArrayLength, +{ + block_cipher: PhantomData, + siv: Siv, key_size: PhantomData, } /// AES-CMAC-SIV in AEAD mode with 256-bit key size (128-bit security) -pub type Aes128Siv = SivAlgorithm, U32>; +pub type Aes128Siv = SivAlgorithm, U32>; /// AES-CMAC-SIV in AEAD mode with 512-bit key size (256-bit security) -pub type Aes256Siv = SivAlgorithm, U64>; +pub type Aes256Siv = SivAlgorithm, U64>; /// AES-PMAC-SIV in AEAD mode with 256-bit key size (128-bit security) -pub type Aes128PmacSiv = SivAlgorithm, U32>; +pub type Aes128PmacSiv = SivAlgorithm, U32>; /// AES-PMAC-SIV in AEAD mode with 512-bit key size (256-bit security) -pub type Aes256PmacSiv = SivAlgorithm, U64>; +pub type Aes256PmacSiv = SivAlgorithm, U64>; -impl Algorithm for SivAlgorithm +impl Algorithm for SivAlgorithm where - C: Ctr, + B: BlockCipher, + B::ParBlocks: ArrayLength>, + C: Ctr, M: Mac, K: ArrayLength, { @@ -124,6 +135,7 @@ where fn new(key: &[u8]) -> Self { Self { + block_cipher: PhantomData, siv: Siv::new(key), key_size: PhantomData, } diff --git a/rust/src/bench.rs b/rust/src/bench.rs index a91001c..55565ae 100644 --- a/rust/src/bench.rs +++ b/rust/src/bench.rs @@ -1,7 +1,7 @@ extern crate ring; use self::ring::aead; -use siv::{Aes128Siv, Aes128PmacSiv}; +use siv::{Aes128PmacSiv, Aes128Siv}; use test::Bencher; // WARNING: Do not ever actually use a key of all zeroes @@ -22,7 +22,9 @@ fn bench_aes_siv_128_encrypt_128_bytes(b: &mut Bencher) { let mut buffer = vec![0u8; 144]; b.bytes = 128; - b.iter(|| { siv.seal_in_place(&[NONCE], &mut buffer); }); + b.iter(|| { + siv.seal_in_place(&[NONCE], &mut buffer); + }); } #[bench] @@ -33,7 +35,9 @@ fn bench_aes_siv_128_encrypt_1024_bytes(b: &mut Bencher) { let mut buffer = vec![0u8; 1040]; b.bytes = 1024; - b.iter(|| { siv.seal_in_place(&[NONCE], &mut buffer); }); + b.iter(|| { + siv.seal_in_place(&[NONCE], &mut buffer); + }); } #[bench] @@ -44,7 +48,9 @@ fn bench_aes_siv_128_encrypt_16384_bytes(b: &mut Bencher) { let mut buffer = vec![0u8; 16400]; b.bytes = 16384; - b.iter(|| { siv.seal_in_place(&[NONCE], &mut buffer); }); + b.iter(|| { + siv.seal_in_place(&[NONCE], &mut buffer); + }); } // @@ -59,7 +65,9 @@ fn bench_aes_pmac_siv_128_encrypt_128_bytes(b: &mut Bencher) { let mut buffer = vec![0u8; 144]; b.bytes = 128; - b.iter(|| { siv.seal_in_place(&[NONCE], &mut buffer); }); + b.iter(|| { + siv.seal_in_place(&[NONCE], &mut buffer); + }); } #[bench] @@ -70,7 +78,9 @@ fn bench_aes_pmac_siv_128_encrypt_1024_bytes(b: &mut Bencher) { let mut buffer = vec![0u8; 1040]; b.bytes = 1024; - b.iter(|| { siv.seal_in_place(&[NONCE], &mut buffer); }); + b.iter(|| { + siv.seal_in_place(&[NONCE], &mut buffer); + }); } #[bench] @@ -81,7 +91,9 @@ fn bench_aes_pmac_siv_128_encrypt_16384_bytes(b: &mut Bencher) { let mut buffer = vec![0u8; 16400]; b.bytes = 16384; - b.iter(|| { siv.seal_in_place(&[NONCE], &mut buffer); }); + b.iter(|| { + siv.seal_in_place(&[NONCE], &mut buffer); + }); } // @@ -90,8 +102,8 @@ fn bench_aes_pmac_siv_128_encrypt_16384_bytes(b: &mut Bencher) { #[bench] fn bench_aes_gcm_128_encrypt_128_bytes(b: &mut Bencher) { - let sealing_key = aead::SealingKey::new(&aead::AES_128_GCM, &KEY_128_BIT[..]) - .expect("valid key"); + let sealing_key = + aead::SealingKey::new(&aead::AES_128_GCM, &KEY_128_BIT[..]).expect("valid key"); // 128 bytes input + 16 bytes tag let mut buffer = [0u8; 144]; @@ -110,8 +122,8 @@ fn bench_aes_gcm_128_encrypt_128_bytes(b: &mut Bencher) { #[bench] fn bench_aes_gcm_128_encrypt_1024_bytes(b: &mut Bencher) { - let sealing_key = aead::SealingKey::new(&aead::AES_128_GCM, &KEY_128_BIT[..]) - .expect("valid key"); + let sealing_key = + aead::SealingKey::new(&aead::AES_128_GCM, &KEY_128_BIT[..]).expect("valid key"); // 1024 bytes input + 16 bytes tag let mut buffer = [0u8; 1040]; @@ -130,8 +142,8 @@ fn bench_aes_gcm_128_encrypt_1024_bytes(b: &mut Bencher) { #[bench] fn bench_aes_gcm_128_encrypt_16384_bytes(b: &mut Bencher) { - let sealing_key = aead::SealingKey::new(&aead::AES_128_GCM, &KEY_128_BIT[..]) - .expect("valid key"); + let sealing_key = + aead::SealingKey::new(&aead::AES_128_GCM, &KEY_128_BIT[..]).expect("valid key"); // 16384 bytes input + 16 bytes tag let mut buffer = [0u8; 16400]; diff --git a/rust/src/ctr.rs b/rust/src/ctr.rs index eceee26..267efc3 100644 --- a/rust/src/ctr.rs +++ b/rust/src/ctr.rs @@ -1,19 +1,50 @@ -//! `internals/ctr.rs`: Counter Mode encryption/decryption (128-bit IV size) - -use aesni::CtrAes128 as CtrAesNi128; -use aesni::CtrAes256 as CtrAesNi256; +//! `ctr.rs`: Counter Mode encryption/decryption (128-bit IV size) +//! +//! TODO: this whole module is a legacy wrapper around aesni's former internal +//! AES-CTR implementation. We should really get rid of it and leverage the +//! `Ctr` types in the `block-modes` crate directly. + +use aesni::{Aes128, Aes256, BlockCipher}; +use aesni::block_cipher_trait::generic_array::{ArrayLength, GenericArray}; +use aesni::block_cipher_trait::generic_array::typenum::consts::U16; +use block_modes::{BlockMode, BlockModeIv, Ctr128}; +use block_modes::block_padding::ZeroPadding; use clear_on_drop::clear::Clear; /// Size of the initial counter value in bytes pub const IV_SIZE: usize = 16; +/// Size of an AES block +const BLOCK_SIZE: usize = 16; + /// Common interface to counter mode encryption/decryption -pub trait Ctr { +pub trait Ctr +where + C: BlockCipher, + C::ParBlocks: ArrayLength>, +{ /// Create a new CTR instance fn new(key: &[u8]) -> Self; + /// Hax: Obtain a new cipher instance + fn cipher(&self) -> C; + /// XOR the CTR keystream into the given buffer - fn xor_in_place(&self, iv: &[u8; IV_SIZE], buf: &mut [u8]); + fn xor_in_place(&self, iv: &[u8; IV_SIZE], buf: &mut [u8]) { + let cipher = self.cipher(); + let mut ctr = Ctr128::::new(cipher, &GenericArray::clone_from_slice(iv)); + + let offset = buf.len() % BLOCK_SIZE; + let aligned = buf.len() - offset; + + ctr.encrypt_nopad(&mut buf[..aligned]).unwrap(); + + if offset != 0 { + let mut block = [0u8; BLOCK_SIZE]; + ctr.encrypt_nopad(&mut block).unwrap(); + xor_in_place(&mut buf[aligned..], &block[..offset]); + } + } } /// AES-CTR with a 128-bit key @@ -22,7 +53,7 @@ pub struct Aes128Ctr { key: [u8; 16], } -impl Ctr for Aes128Ctr { +impl Ctr for Aes128Ctr { #[inline] fn new(key: &[u8]) -> Self { debug_assert_eq!(key.len(), 16, "expected 16-byte key, got {}", key.len()); @@ -33,8 +64,9 @@ impl Ctr for Aes128Ctr { Self { key: k } } - fn xor_in_place(&self, iv: &[u8; IV_SIZE], buf: &mut [u8]) { - CtrAesNi128::new(&self.key, iv).xor(buf); + #[inline] + fn cipher(&self) -> Aes128 { + Aes128::new_varkey(&self.key).unwrap() } } @@ -50,7 +82,7 @@ pub struct Aes256Ctr { key: [u8; 32], } -impl Ctr for Aes256Ctr { +impl Ctr for Aes256Ctr { #[inline] fn new(key: &[u8]) -> Self { debug_assert_eq!(key.len(), 32, "expected 32-byte key, got {}", key.len()); @@ -61,8 +93,9 @@ impl Ctr for Aes256Ctr { Self { key: k } } - fn xor_in_place(&self, iv: &[u8; IV_SIZE], buf: &mut [u8]) { - CtrAesNi256::new(&self.key, iv).xor(buf); + #[inline] + fn cipher(&self) -> Aes256 { + Aes256::new_varkey(&self.key).unwrap() } } @@ -71,3 +104,10 @@ impl Drop for Aes256Ctr { self.key.clear() } } + +#[inline] +fn xor_in_place(a: &mut [u8], b: &[u8]) { + for i in 0..b.len() { + a[i] ^= b[i]; + } +} diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index 0d6466a..0d4f382 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -1,12 +1,11 @@ //! `ffi.rs`: Foreign Function Interface providing C ABI // This is the only code in Miscreant allowed to be unsafe -#![allow(unsafe_code)] -#![allow(non_upper_case_globals)] +#![allow(unsafe_code, non_upper_case_globals, unknown_lints, too_many_arguments)] use aead; +use aesni::block_cipher_trait::generic_array::typenum::Unsigned; use core::{ptr, slice}; -use generic_array::typenum::Unsigned; // // AES-128-SIV AEAD @@ -25,17 +24,7 @@ pub unsafe extern "C" fn crypto_aead_aes128siv_encrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_encrypt::( - ct, - ctlen_p, - msg, - msglen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_encrypt::(ct, ctlen_p, msg, msglen, nonce, noncelen, ad, adlen, key) } /// AES-128-SIV AEAD: authenticated decryption @@ -51,17 +40,7 @@ pub unsafe extern "C" fn crypto_aead_aes128siv_decrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_decrypt::( - msg, - msglen_p, - ct, - ctlen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_decrypt::(msg, msglen_p, ct, ctlen, nonce, noncelen, ad, adlen, key) } /// AES-128-SIV key size @@ -89,17 +68,7 @@ pub unsafe extern "C" fn crypto_aead_aes256siv_encrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_encrypt::( - ct, - ctlen_p, - msg, - msglen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_encrypt::(ct, ctlen_p, msg, msglen, nonce, noncelen, ad, adlen, key) } /// AES-256-SIV AEAD: authenticated decryption @@ -115,17 +84,7 @@ pub unsafe extern "C" fn crypto_aead_aes256siv_decrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_decrypt::( - msg, - msglen_p, - ct, - ctlen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_decrypt::(msg, msglen_p, ct, ctlen, nonce, noncelen, ad, adlen, key) } /// AES-128-SIV key size @@ -153,17 +112,7 @@ pub unsafe extern "C" fn crypto_aead_aes128pmacsiv_encrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_encrypt::( - ct, - ctlen_p, - msg, - msglen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_encrypt::(ct, ctlen_p, msg, msglen, nonce, noncelen, ad, adlen, key) } /// AES-128-PMAC-SIV AEAD: authenticated decryption @@ -179,17 +128,7 @@ pub unsafe extern "C" fn crypto_aead_aes128pmacsiv_decrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_decrypt::( - msg, - msglen_p, - ct, - ctlen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_decrypt::(msg, msglen_p, ct, ctlen, nonce, noncelen, ad, adlen, key) } /// AES-128-PMAC-SIV key size @@ -217,17 +156,7 @@ pub unsafe extern "C" fn crypto_aead_aes256pmacsiv_encrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_encrypt::( - ct, - ctlen_p, - msg, - msglen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_encrypt::(ct, ctlen_p, msg, msglen, nonce, noncelen, ad, adlen, key) } /// AES-256-PMAC-SIV AEAD: authenticated decryption @@ -243,17 +172,7 @@ pub unsafe extern "C" fn crypto_aead_aes256pmacsiv_decrypt( adlen: u64, key: *const u8, ) -> i32 { - aead_decrypt::( - msg, - msglen_p, - ct, - ctlen, - nonce, - noncelen, - ad, - adlen, - key, - ) + aead_decrypt::(msg, msglen_p, ct, ctlen, nonce, noncelen, ad, adlen, key) } /// AES-128-SIV key size @@ -296,7 +215,7 @@ unsafe fn aead_encrypt( A::new(key_slice).seal_in_place(nonce_slice, ad_slice, ct_slice); - return 0; + 0 } /// Generic C-like interface to AEAD decryption @@ -345,5 +264,5 @@ unsafe fn aead_decrypt( *c = 0; } - return 0; + 0 } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c19f9a0..850b2b9 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,24 +1,38 @@ //! `Miscreant`: Misuse resistant symmetric encryption library providing the //! AES-SIV (RFC 5297), AES-PMAC-SIV, and STREAM constructions - +//! +//! # Build Notes +//! +//! This crate depends on the `aesni` crate, which uses the new `core::arch` +//! API to invoke CPU instructions for performing AES in hardware. +//! +//! To access these features, you will need both a relatively recent +//! Rust nightly and to pass the following as RUSTFLAGS: +//! +//! `RUSTFLAGS=-C target-feature=+aes` +//! +//! You can configure your `~/.cargo/config` to always pass these flags: +//! +//! ```toml +//! [build] +//! rustflags = ["-C", "target-feature=+aes"] +//! ``` +//! #![crate_name = "miscreant"] #![crate_type = "lib"] - #![deny(warnings, missing_docs, trivial_casts, trivial_numeric_casts)] #![deny(unsafe_code, unused_import_braces, unused_qualifications)] - #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "bench", feature(test))] #![cfg_attr(feature = "staticlib", feature(lang_items))] extern crate aesni; +extern crate block_modes; extern crate byteorder; -extern crate block_cipher_trait; extern crate clear_on_drop; extern crate cmac; extern crate crypto_mac; extern crate dbl; -extern crate generic_array; extern crate pmac; extern crate subtle; diff --git a/rust/src/s2v.rs b/rust/src/s2v.rs index 2654048..e5ab9aa 100644 --- a/rust/src/s2v.rs +++ b/rust/src/s2v.rs @@ -1,7 +1,7 @@ +use aesni::block_cipher_trait::generic_array::GenericArray; +use aesni::block_cipher_trait::generic_array::typenum::{U16, Unsigned}; use crypto_mac::Mac; use dbl::Dbl; -use generic_array::GenericArray; -use generic_array::typenum::{U16, Unsigned}; /// Maximum number of associated data items pub const MAX_ASSOCIATED_DATA: usize = 126; diff --git a/rust/src/siv.rs b/rust/src/siv.rs index c2fc341..2c609fd 100644 --- a/rust/src/siv.rs +++ b/rust/src/siv.rs @@ -1,35 +1,51 @@ //! `siv.rs`: The SIV misuse resistant block cipher mode of operation use aesni::{Aes128, Aes256}; +use aesni::block_cipher_trait::BlockCipher; +use aesni::block_cipher_trait::generic_array::ArrayLength; +use aesni::block_cipher_trait::generic_array::GenericArray; +use aesni::block_cipher_trait::generic_array::typenum::{U16, Unsigned}; use cmac::Cmac; +use core::marker::PhantomData; use crypto_mac::Mac; use ctr::{Aes128Ctr, Aes256Ctr, Ctr, IV_SIZE}; use error::Error; -use generic_array::GenericArray; -use generic_array::typenum::{U16, Unsigned}; use pmac::Pmac; use s2v::s2v; use subtle; /// The SIV misuse resistant block cipher mode of operation -pub struct Siv { +pub struct Siv +where + B: BlockCipher, + B::ParBlocks: ArrayLength>, + C: Ctr, + M: Mac, +{ + block_cipher: PhantomData, mac: M, ctr: C, } /// AES-CMAC-SIV with a 128-bit key -pub type Aes128Siv = Siv>; +pub type Aes128Siv = Siv>; /// AES-CMAC-SIV with a 256-bit key -pub type Aes256Siv = Siv>; +pub type Aes256Siv = Siv>; /// AES-PMAC-SIV with a 128-bit key -pub type Aes128PmacSiv = Siv>; +pub type Aes128PmacSiv = Siv>; /// AES-PMAC-SIV with a 256-bit key -pub type Aes256PmacSiv = Siv>; - -impl> Siv { +pub type Aes256PmacSiv = Siv>; + +impl Siv +where + B: BlockCipher, + B::ParBlocks: ArrayLength>, + C: Ctr, + M: Mac, +{ /// Create a new AES-SIV instance /// /// Panics if the key is the wrong length @@ -45,6 +61,7 @@ impl> Siv { ); Self { + block_cipher: PhantomData, mac: M::new(GenericArray::from_slice(&key[..(key_size / 2)])), ctr: C::new(&key[(key_size / 2)..]), } @@ -87,10 +104,8 @@ impl> Siv { // Compute the synthetic IV for this plaintext let iv = s2v(&mut self.mac, associated_data, &plaintext[IV_SIZE..]); plaintext[..IV_SIZE].copy_from_slice(iv.as_slice()); - self.ctr.xor_in_place( - &zero_iv_bits(&iv), - &mut plaintext[IV_SIZE..], - ); + self.ctr + .xor_in_place(&zero_iv_bits(&iv), &mut plaintext[IV_SIZE..]); } /// Decrypt the given ciphertext in-place, authenticating it against the diff --git a/rust/src/stream.rs b/rust/src/stream.rs index 1cffa84..81b996e 100644 --- a/rust/src/stream.rs +++ b/rust/src/stream.rs @@ -1,7 +1,7 @@ //! `stream.rs`: The STREAM online authenticated encryption construction. //! See for definition. -use aead::{self, Aes128Siv, Aes128PmacSiv, Aes256Siv, Aes256PmacSiv}; +use aead::{self, Aes128PmacSiv, Aes128Siv, Aes256PmacSiv, Aes256Siv}; use byteorder::{BigEndian, ByteOrder}; use error::Error; @@ -183,9 +183,9 @@ impl NonceEncoder32 { /// Increment the nonce value in-place pub fn increment(&mut self) { - self.counter = self.counter.checked_add(1).expect( - "STREAM nonce counter overflowed", - ); + self.counter = self.counter + .checked_add(1) + .expect("STREAM nonce counter overflowed"); BigEndian::write_u32(&mut self.value[NONCE_SIZE..(NONCE_SIZE + 4)], self.counter); } diff --git a/rust/tests/aead_test.rs b/rust/tests/aead_test.rs index f303dcc..3d9cd7e 100644 --- a/rust/tests/aead_test.rs +++ b/rust/tests/aead_test.rs @@ -3,7 +3,7 @@ extern crate miscreant; mod aead_vectors; use aead_vectors::AesSivAeadExample; -use miscreant::aead::{Aes128Siv, Aes256Siv, Aes128PmacSiv, Aes256PmacSiv, Algorithm}; +use miscreant::aead::{Aes128PmacSiv, Aes128Siv, Aes256PmacSiv, Aes256Siv, Algorithm}; #[test] fn aes_siv_aead_examples_seal() { @@ -11,44 +11,32 @@ fn aes_siv_aead_examples_seal() { for example in examples { let ciphertext = match example.alg.as_ref() { - "AES-SIV" => { - match example.key.len() { - 32 => { - Aes128Siv::new(&example.key).seal( - &example.nonce, - &example.ad, - &example.plaintext, - ) - } - 64 => { - Aes256Siv::new(&example.key).seal( - &example.nonce, - &example.ad, - &example.plaintext, - ) - } - _ => panic!("unexpected key size: {}", example.key.len()), - } - } - "AES-PMAC-SIV" => { - match example.key.len() { - 32 => { - Aes128PmacSiv::new(&example.key).seal( - &example.nonce, - &example.ad, - &example.plaintext, - ) - } - 64 => { - Aes256PmacSiv::new(&example.key).seal( - &example.nonce, - &example.ad, - &example.plaintext, - ) - } - _ => panic!("unexpected key size: {}", example.key.len()), - } - } + "AES-SIV" => match example.key.len() { + 32 => Aes128Siv::new(&example.key).seal( + &example.nonce, + &example.ad, + &example.plaintext, + ), + 64 => Aes256Siv::new(&example.key).seal( + &example.nonce, + &example.ad, + &example.plaintext, + ), + _ => panic!("unexpected key size: {}", example.key.len()), + }, + "AES-PMAC-SIV" => match example.key.len() { + 32 => Aes128PmacSiv::new(&example.key).seal( + &example.nonce, + &example.ad, + &example.plaintext, + ), + 64 => Aes256PmacSiv::new(&example.key).seal( + &example.nonce, + &example.ad, + &example.plaintext, + ), + _ => panic!("unexpected key size: {}", example.key.len()), + }, _ => panic!("unexpected algorithm: {}", example.alg), }; @@ -62,44 +50,32 @@ fn aes_siv_aead_examples_open() { for example in examples { let plaintext = match example.alg.as_ref() { - "AES-SIV" => { - match example.key.len() { - 32 => { - Aes128Siv::new(&example.key).open( - &example.nonce, - &example.ad, - &example.ciphertext, - ) - } - 64 => { - Aes256Siv::new(&example.key).open( - &example.nonce, - &example.ad, - &example.ciphertext, - ) - } - _ => panic!("unexpected key size: {}", example.key.len()), - } - } - "AES-PMAC-SIV" => { - match example.key.len() { - 32 => { - Aes128PmacSiv::new(&example.key).open( - &example.nonce, - &example.ad, - &example.ciphertext, - ) - } - 64 => { - Aes256PmacSiv::new(&example.key).open( - &example.nonce, - &example.ad, - &example.ciphertext, - ) - } - _ => panic!("unexpected key size: {}", example.key.len()), - } - } + "AES-SIV" => match example.key.len() { + 32 => Aes128Siv::new(&example.key).open( + &example.nonce, + &example.ad, + &example.ciphertext, + ), + 64 => Aes256Siv::new(&example.key).open( + &example.nonce, + &example.ad, + &example.ciphertext, + ), + _ => panic!("unexpected key size: {}", example.key.len()), + }, + "AES-PMAC-SIV" => match example.key.len() { + 32 => Aes128PmacSiv::new(&example.key).open( + &example.nonce, + &example.ad, + &example.ciphertext, + ), + 64 => Aes256PmacSiv::new(&example.key).open( + &example.nonce, + &example.ad, + &example.ciphertext, + ), + _ => panic!("unexpected key size: {}", example.key.len()), + }, _ => panic!("unexpected algorithm: {}", example.alg), }.expect("decrypt failure"); diff --git a/rust/tests/aead_vectors/mod.rs b/rust/tests/aead_vectors/mod.rs index 3cc79ff..a80ac0e 100644 --- a/rust/tests/aead_vectors/mod.rs +++ b/rust/tests/aead_vectors/mod.rs @@ -30,53 +30,50 @@ impl AesSivAeadExample { let mut file = File::open(&path).expect("valid aes_siv_aead.tjson"); let mut tjson_string = String::new(); - file.read_to_string(&mut tjson_string).expect( - "aes_siv_aead.tjson read successfully", - ); + file.read_to_string(&mut tjson_string) + .expect("aes_siv_aead.tjson read successfully"); let tjson: serde_json::Value = serde_json::from_str(&tjson_string).expect("aes_siv.tjson parses successfully"); - let examples = &tjson["examples:A"].as_array().expect( - "aes_siv_aead.tjson examples array", - ); + let examples = &tjson["examples:A"] + .as_array() + .expect("aes_siv_aead.tjson examples array"); examples .into_iter() - .map(|ex| { - Self { - alg: ex["alg:s"].as_str().expect("algorithm name").to_owned(), - key: HEXLOWER - .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) - .expect("hex encoded"), - ad: HEXLOWER - .decode(ex["ad:d16"].as_str().expect("encoded example").as_bytes()) - .expect("hex encoded"), - nonce: HEXLOWER - .decode( - ex["nonce:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - plaintext: HEXLOWER - .decode( - ex["plaintext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - ciphertext: HEXLOWER - .decode( - ex["ciphertext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - } + .map(|ex| Self { + alg: ex["alg:s"].as_str().expect("algorithm name").to_owned(), + key: HEXLOWER + .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) + .expect("hex encoded"), + ad: HEXLOWER + .decode(ex["ad:d16"].as_str().expect("encoded example").as_bytes()) + .expect("hex encoded"), + nonce: HEXLOWER + .decode( + ex["nonce:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + plaintext: HEXLOWER + .decode( + ex["plaintext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + ciphertext: HEXLOWER + .decode( + ex["ciphertext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), }) .collect() } diff --git a/rust/tests/siv_test.rs b/rust/tests/siv_test.rs index 8eb41f0..30c68d0 100644 --- a/rust/tests/siv_test.rs +++ b/rust/tests/siv_test.rs @@ -2,8 +2,8 @@ extern crate miscreant; mod siv_vectors; -use miscreant::siv::{Aes128Siv, Aes256Siv, Aes128PmacSiv, Aes256PmacSiv}; -use siv_vectors::{AesSivExample, AesPmacSivExample}; +use miscreant::siv::{Aes128PmacSiv, Aes128Siv, Aes256PmacSiv, Aes256Siv}; +use siv_vectors::{AesPmacSivExample, AesSivExample}; #[test] fn aes_siv_examples_seal() { diff --git a/rust/tests/siv_vectors/mod.rs b/rust/tests/siv_vectors/mod.rs index 8a2afa4..7b190f6 100644 --- a/rust/tests/siv_vectors/mod.rs +++ b/rust/tests/siv_vectors/mod.rs @@ -27,50 +27,47 @@ impl AesSivExample { pub fn load_from_file(path: &Path) -> Vec { let mut file = File::open(&path).expect("valid aes_siv.tjson"); let mut tjson_string = String::new(); - file.read_to_string(&mut tjson_string).expect( - "aes_siv.tjson read successfully", - ); + file.read_to_string(&mut tjson_string) + .expect("aes_siv.tjson read successfully"); let tjson: serde_json::Value = serde_json::from_str(&tjson_string).expect("aes_siv.tjson parses successfully"); - let examples = &tjson["examples:A"].as_array().expect( - "aes_siv.tjson examples array", - ); + let examples = &tjson["examples:A"] + .as_array() + .expect("aes_siv.tjson examples array"); examples .into_iter() - .map(|ex| { - Self { - key: HEXLOWER - .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) - .expect("hex encoded"), - ad: ex["ad:A"] - .as_array() - .expect("encoded example") - .iter() - .map(|ex| { - HEXLOWER - .decode(ex.as_str().expect("encoded example").as_bytes()) - .expect("hex encoded") - }) - .collect(), - plaintext: HEXLOWER - .decode( - ex["plaintext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - ciphertext: HEXLOWER - .decode( - ex["ciphertext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - } + .map(|ex| Self { + key: HEXLOWER + .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) + .expect("hex encoded"), + ad: ex["ad:A"] + .as_array() + .expect("encoded example") + .iter() + .map(|ex| { + HEXLOWER + .decode(ex.as_str().expect("encoded example").as_bytes()) + .expect("hex encoded") + }) + .collect(), + plaintext: HEXLOWER + .decode( + ex["plaintext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + ciphertext: HEXLOWER + .decode( + ex["ciphertext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), }) .collect() } @@ -96,50 +93,47 @@ impl AesPmacSivExample { pub fn load_from_file(path: &Path) -> Vec { let mut file = File::open(&path).expect("valid aes_pmac_siv.tjson"); let mut tjson_string = String::new(); - file.read_to_string(&mut tjson_string).expect( - "aes_pmac_siv.tjson read successfully", - ); + file.read_to_string(&mut tjson_string) + .expect("aes_pmac_siv.tjson read successfully"); let tjson: serde_json::Value = serde_json::from_str(&tjson_string).expect("aes_pmac_siv.tjson parses successfully"); - let examples = &tjson["examples:A"].as_array().expect( - "aes_pmac_siv.tjson examples array", - ); + let examples = &tjson["examples:A"] + .as_array() + .expect("aes_pmac_siv.tjson examples array"); examples .into_iter() - .map(|ex| { - Self { - key: HEXLOWER - .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) - .expect("hex encoded"), - ad: ex["ad:A"] - .as_array() - .expect("encoded example") - .iter() - .map(|ex| { - HEXLOWER - .decode(ex.as_str().expect("encoded example").as_bytes()) - .expect("hex encoded") - }) - .collect(), - plaintext: HEXLOWER - .decode( - ex["plaintext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - ciphertext: HEXLOWER - .decode( - ex["ciphertext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - } + .map(|ex| Self { + key: HEXLOWER + .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) + .expect("hex encoded"), + ad: ex["ad:A"] + .as_array() + .expect("encoded example") + .iter() + .map(|ex| { + HEXLOWER + .decode(ex.as_str().expect("encoded example").as_bytes()) + .expect("hex encoded") + }) + .collect(), + plaintext: HEXLOWER + .decode( + ex["plaintext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + ciphertext: HEXLOWER + .decode( + ex["ciphertext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), }) .collect() } diff --git a/rust/tests/stream_test.rs b/rust/tests/stream_test.rs index 058aa1a..0722ba9 100644 --- a/rust/tests/stream_test.rs +++ b/rust/tests/stream_test.rs @@ -1,38 +1,29 @@ extern crate miscreant; -extern crate generic_array; mod stream_vectors; use miscreant::aead; -use miscreant::stream::{Aes128PmacSivEncryptor, Aes128PmacSivDecryptor}; -use miscreant::stream::{Aes128SivEncryptor, Aes128SivDecryptor}; -use miscreant::stream::{Aes256PmacSivEncryptor, Aes256PmacSivDecryptor}; -use miscreant::stream::{Aes256SivEncryptor, Aes256SivDecryptor}; -use miscreant::stream::{Encryptor, Decryptor}; +use miscreant::stream::{Aes128PmacSivDecryptor, Aes128PmacSivEncryptor}; +use miscreant::stream::{Aes128SivDecryptor, Aes128SivEncryptor}; +use miscreant::stream::{Aes256PmacSivDecryptor, Aes256PmacSivEncryptor}; +use miscreant::stream::{Aes256SivDecryptor, Aes256SivEncryptor}; +use miscreant::stream::{Decryptor, Encryptor}; use stream_vectors::{AesSivStreamExample, Block}; #[test] fn aes_siv_stream_examples_seal() { for ex in AesSivStreamExample::load_all() { match ex.alg.as_ref() { - "AES-SIV" => { - match ex.key.len() { - 32 => test_encryptor(Aes128SivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks), - 64 => test_encryptor(Aes256SivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks), - _ => panic!("unexpected key size: {}", ex.key.len()), - } - } - "AES-PMAC-SIV" => { - match ex.key.len() { - 32 => { - test_encryptor(Aes128PmacSivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks) - } - 64 => { - test_encryptor(Aes256PmacSivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks) - } - _ => panic!("unexpected key size: {}", ex.key.len()), - } - } + "AES-SIV" => match ex.key.len() { + 32 => test_encryptor(Aes128SivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks), + 64 => test_encryptor(Aes256SivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks), + _ => panic!("unexpected key size: {}", ex.key.len()), + }, + "AES-PMAC-SIV" => match ex.key.len() { + 32 => test_encryptor(Aes128PmacSivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks), + 64 => test_encryptor(Aes256PmacSivEncryptor::new(&ex.key, &ex.nonce), &ex.blocks), + _ => panic!("unexpected key size: {}", ex.key.len()), + }, _ => panic!("unexpected algorithm: {}", ex.alg), } } @@ -55,24 +46,16 @@ fn test_encryptor(mut encryptor: Encryptor, blocks: &[Blo fn aes_siv_stream_examples_open() { for ex in AesSivStreamExample::load_all() { match ex.alg.as_ref() { - "AES-SIV" => { - match ex.key.len() { - 32 => test_decryptor(Aes128SivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks), - 64 => test_decryptor(Aes256SivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks), - _ => panic!("unexpected key size: {}", ex.key.len()), - } - } - "AES-PMAC-SIV" => { - match ex.key.len() { - 32 => { - test_decryptor(Aes128PmacSivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks) - } - 64 => { - test_decryptor(Aes256PmacSivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks) - } - _ => panic!("unexpected key size: {}", ex.key.len()), - } - } + "AES-SIV" => match ex.key.len() { + 32 => test_decryptor(Aes128SivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks), + 64 => test_decryptor(Aes256SivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks), + _ => panic!("unexpected key size: {}", ex.key.len()), + }, + "AES-PMAC-SIV" => match ex.key.len() { + 32 => test_decryptor(Aes128PmacSivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks), + 64 => test_decryptor(Aes256PmacSivDecryptor::new(&ex.key, &ex.nonce), &ex.blocks), + _ => panic!("unexpected key size: {}", ex.key.len()), + }, _ => panic!("unexpected algorithm: {}", ex.alg), } } @@ -81,15 +64,15 @@ fn aes_siv_stream_examples_open() { fn test_decryptor(mut decryptor: Decryptor, blocks: &[Block]) { for (i, block) in blocks.iter().enumerate() { if i < blocks.len() - 1 { - let plaintext = decryptor.open_next(&block.ad, &block.ciphertext).expect( - "decrypt failure", - ); + let plaintext = decryptor + .open_next(&block.ad, &block.ciphertext) + .expect("decrypt failure"); assert_eq!(plaintext, block.plaintext); } else { - let plaintext = decryptor.open_last(&block.ad, &block.ciphertext).expect( - "decrypt failure", - ); + let plaintext = decryptor + .open_last(&block.ad, &block.ciphertext) + .expect("decrypt failure"); assert_eq!(plaintext, block.plaintext); return; diff --git a/rust/tests/stream_vectors/mod.rs b/rust/tests/stream_vectors/mod.rs index d6df9fe..307f875 100644 --- a/rust/tests/stream_vectors/mod.rs +++ b/rust/tests/stream_vectors/mod.rs @@ -35,64 +35,57 @@ impl AesSivStreamExample { let mut file = File::open(&path).expect("valid aes_siv_stream.tjson"); let mut tjson_string = String::new(); - file.read_to_string(&mut tjson_string).expect( - "aes_siv_stream.tjson read successfully", - ); + file.read_to_string(&mut tjson_string) + .expect("aes_siv_stream.tjson read successfully"); let tjson: serde_json::Value = serde_json::from_str(&tjson_string).expect("aes_siv_stream.tjson parses successfully"); - let examples = &tjson["examples:A"].as_array().expect( - "aes_siv_stream.tjson examples array", - ); + let examples = &tjson["examples:A"] + .as_array() + .expect("aes_siv_stream.tjson examples array"); examples .into_iter() - .map(|ex| { - Self { - alg: ex["alg:s"].as_str().expect("algorithm name").to_owned(), - key: HEXLOWER - .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) - .expect("hex encoded"), - nonce: HEXLOWER - .decode( - ex["nonce:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - blocks: ex["blocks:A"] - .as_array() - .expect("encoded example") - .iter() - .map(|ex| { - Block { - ad: HEXLOWER - .decode( - ex["ad:d16"].as_str().expect("encoded example").as_bytes(), - ) - .expect("hex encoded"), - plaintext: HEXLOWER - .decode( - ex["plaintext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - ciphertext: HEXLOWER - .decode( - ex["ciphertext:d16"] - .as_str() - .expect("encoded example") - .as_bytes(), - ) - .expect("hex encoded"), - } - }) - .collect(), - } + .map(|ex| Self { + alg: ex["alg:s"].as_str().expect("algorithm name").to_owned(), + key: HEXLOWER + .decode(ex["key:d16"].as_str().expect("encoded example").as_bytes()) + .expect("hex encoded"), + nonce: HEXLOWER + .decode( + ex["nonce:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + blocks: ex["blocks:A"] + .as_array() + .expect("encoded example") + .iter() + .map(|ex| Block { + ad: HEXLOWER + .decode(ex["ad:d16"].as_str().expect("encoded example").as_bytes()) + .expect("hex encoded"), + plaintext: HEXLOWER + .decode( + ex["plaintext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + ciphertext: HEXLOWER + .decode( + ex["ciphertext:d16"] + .as_str() + .expect("encoded example") + .as_bytes(), + ) + .expect("hex encoded"), + }) + .collect(), }) .collect() }