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