1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Tools for generating test vector data in a repeatable way. 16 //! 17 //! The common approach of just using an RNG means that even a small change to how test vectors are 18 //! created will regenerate everything. Instead, by using a predictable seed and deriving data from 19 //! that with per-datum names, incremental changes can be made without regenerating everything. 20 //! 21 //! # Examples 22 //! 23 //! Generating 100 groups of data: 24 //! 25 //! ``` 26 //! use crypto_provider_default::CryptoProviderImpl; 27 //! use test_vector_hkdf::TestVectorHkdf; 28 //! 29 //! for i in 0_u32..100 { 30 //! let hkdf = TestVectorHkdf::<CryptoProviderImpl>::new( 31 //! "Spiffy test vectors - random seed: hunter2", 32 //! i.to_be_bytes().as_slice()); 33 //! 34 //! let vec = hkdf.derive_vec( 35 //! "fun data", 36 //! hkdf.derive_range_element("fun data length", 3..=18).try_into().unwrap()); 37 //! let array = hkdf.derive_array::<16>("array data"); 38 //! // store the generated data somewhere 39 //! } 40 //! ``` 41 42 #![allow(clippy::unwrap_used)] 43 44 use crypto_provider::hkdf::Hkdf; 45 use crypto_provider::CryptoProvider; 46 use crypto_provider_default::CryptoProviderImpl; 47 use std::ops; 48 49 /// Typical usage would generate one instance per loop, and derive all data needed for that loop 50 /// iteration. 51 /// 52 /// The `description` passed to all `derive_` calls should be distinct, unless identical data is 53 /// desired. 54 pub struct TestVectorHkdf<C: CryptoProvider = CryptoProviderImpl> { 55 hkdf: C::HkdfSha256, 56 } 57 58 impl<C: CryptoProvider> TestVectorHkdf<C> { 59 /// Create a new instance for the provided namespace and iteration. 60 /// 61 /// The namespace should contain a blob of randomly generated data. If reshuffling the generated 62 /// data is desired, just change the blob of random data. new(namespace: &str, iteration: &[u8]) -> Self63 pub fn new(namespace: &str, iteration: &[u8]) -> Self { 64 Self { hkdf: C::HkdfSha256::new(Some(namespace.as_bytes()), iteration) } 65 } 66 67 /// Derive an array of `N` bytes. derive_array<const N: usize>(&self, description: &str) -> [u8; N]68 pub fn derive_array<const N: usize>(&self, description: &str) -> [u8; N] { 69 let mut arr = [0; N]; 70 self.hkdf.expand(description.as_bytes(), &mut arr).unwrap(); 71 arr 72 } 73 74 /// Derive a Vec of the specified `len`. 75 /// 76 /// # Panics 77 /// 78 /// Panics if `len` is too long for an HKDF output. derive_vec(&self, description: &str, len: usize) -> Vec<u8>79 pub fn derive_vec(&self, description: &str, len: usize) -> Vec<u8> { 80 let mut vec = vec![0; len]; 81 self.hkdf.expand(description.as_bytes(), &mut vec).unwrap(); 82 vec 83 } 84 85 /// Generated a biased element in a range using a sloppy sampling technique. 86 /// Will be increasingly biased as the range gets bigger. derive_range_element(&self, description: &str, range: ops::RangeInclusive<u64>) -> u6487 pub fn derive_range_element(&self, description: &str, range: ops::RangeInclusive<u64>) -> u64 { 88 let num = u64::from_be_bytes(self.derive_array(description)); 89 num % (range.end() - range.start() + 1) + range.start() 90 } 91 } 92