1 //! An implementation of HKDF, the [HMAC-based Extract-and-Expand Key Derivation Function][1].
2 //!
3 //! # Usage
4 //!
5 //! ```rust
6 //! # use sha2::Sha256;
7 //! # use hkdf::Hkdf;
8 //! let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
9 //! let salt = hex::decode("000102030405060708090a0b0c").unwrap();
10 //! let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
11 //!
12 //! let h = Hkdf::<Sha256>::new(Some(&salt[..]), &ikm);
13 //! let mut okm = [0u8; 42];
14 //! h.expand(&info, &mut okm).unwrap();
15 //! println!("OKM is {}", hex::encode(&okm[..]));
16 //! ```
17 //!
18 //! [1]: https://tools.ietf.org/html/rfc5869
19 
20 #![no_std]
21 #![warn(rust_2018_idioms)]
22 
23 #[cfg(feature = "std")]
24 extern crate std;
25 
26 use core::fmt;
27 use digest::generic_array::{self, ArrayLength, GenericArray};
28 use digest::{BlockInput, FixedOutput, Reset, Update};
29 use hmac::{Hmac, Mac, NewMac};
30 
31 /// Error that is returned when supplied pseudorandom key (PRK) is not long enough.
32 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
33 pub struct InvalidPrkLength;
34 
35 /// Structure for InvalidLength, used for output error handling.
36 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
37 pub struct InvalidLength;
38 
39 /// Structure representing the streaming context of an HKDF-Extract operation
40 /// ```rust
41 /// # use hkdf::{Hkdf, HkdfExtract};
42 /// # use sha2::Sha256;
43 /// let mut extract_ctx = HkdfExtract::<Sha256>::new(Some(b"mysalt"));
44 /// extract_ctx.input_ikm(b"hello");
45 /// extract_ctx.input_ikm(b" world");
46 /// let (streamed_res, _) = extract_ctx.finalize();
47 ///
48 /// let (oneshot_res, _) = Hkdf::<Sha256>::extract(Some(b"mysalt"), b"hello world");
49 /// assert_eq!(streamed_res, oneshot_res);
50 /// ```
51 #[derive(Clone)]
52 pub struct HkdfExtract<D>
53 where
54     D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
55     D::BlockSize: ArrayLength<u8>,
56     D::OutputSize: ArrayLength<u8>,
57 {
58     hmac: Hmac<D>,
59 }
60 
61 impl<D> HkdfExtract<D>
62 where
63     D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
64     D::BlockSize: ArrayLength<u8>,
65     D::OutputSize: ArrayLength<u8>,
66 {
67     /// Initiates the HKDF-Extract context with the given optional salt
new(salt: Option<&[u8]>) -> HkdfExtract<D>68     pub fn new(salt: Option<&[u8]>) -> HkdfExtract<D> {
69         let default_salt = GenericArray::<u8, D::OutputSize>::default();
70         let salt = salt.unwrap_or(&default_salt);
71         let hmac = Hmac::<D>::new_from_slice(salt).expect("HMAC can take a key of any size");
72         HkdfExtract { hmac }
73     }
74 
75     /// Feeds in additional input key material to the HKDF-Extract context
input_ikm(&mut self, ikm: &[u8])76     pub fn input_ikm(&mut self, ikm: &[u8]) {
77         self.hmac.update(ikm);
78     }
79 
80     /// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and
81     /// `Hkdf` struct for expanding.
finalize(self) -> (GenericArray<u8, D::OutputSize>, Hkdf<D>)82     pub fn finalize(self) -> (GenericArray<u8, D::OutputSize>, Hkdf<D>) {
83         let prk = self.hmac.finalize().into_bytes();
84         let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct");
85         (prk, hkdf)
86     }
87 }
88 
89 /// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations.
90 #[derive(Clone)]
91 pub struct Hkdf<D>
92 where
93     D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
94     D::BlockSize: ArrayLength<u8>,
95     D::OutputSize: ArrayLength<u8>,
96 {
97     hmac: Hmac<D>,
98 }
99 
100 impl<D> Hkdf<D>
101 where
102     D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
103     D::BlockSize: ArrayLength<u8>,
104     D::OutputSize: ArrayLength<u8>,
105 {
106     /// Convenience method for [`extract`][Hkdf::extract] when the generated
107     /// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most
108     /// common constructor.
new(salt: Option<&[u8]>, ikm: &[u8]) -> Hkdf<D>109     pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Hkdf<D> {
110         let (_, hkdf) = Hkdf::extract(salt, ikm);
111         hkdf
112     }
113 
114     /// Create `Hkdf` from an already cryptographically strong pseudorandom key
115     /// as per section 3.3 from RFC5869.
from_prk(prk: &[u8]) -> Result<Hkdf<D>, InvalidPrkLength>116     pub fn from_prk(prk: &[u8]) -> Result<Hkdf<D>, InvalidPrkLength> {
117         use crate::generic_array::typenum::Unsigned;
118 
119         // section 2.3 specifies that prk must be "at least HashLen octets"
120         if prk.len() < D::OutputSize::to_usize() {
121             return Err(InvalidPrkLength);
122         }
123 
124         Ok(Hkdf {
125             hmac: Hmac::new_from_slice(prk).expect("HMAC can take a key of any size"),
126         })
127     }
128 
129     /// The RFC5869 HKDF-Extract operation returning both the generated
130     /// pseudorandom key and `Hkdf` struct for expanding.
extract(salt: Option<&[u8]>, ikm: &[u8]) -> (GenericArray<u8, D::OutputSize>, Hkdf<D>)131     pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (GenericArray<u8, D::OutputSize>, Hkdf<D>) {
132         let mut extract_ctx = HkdfExtract::new(salt);
133         extract_ctx.input_ikm(ikm);
134         extract_ctx.finalize()
135     }
136 
137     /// The RFC5869 HKDF-Expand operation. This is equivalent to calling
138     /// [`expand`][Hkdf::extract] with the `info` argument set equal to the
139     /// concatenation of all the elements of `info_components`.
expand_multi_info( &self, info_components: &[&[u8]], okm: &mut [u8], ) -> Result<(), InvalidLength>140     pub fn expand_multi_info(
141         &self,
142         info_components: &[&[u8]],
143         okm: &mut [u8],
144     ) -> Result<(), InvalidLength> {
145         use crate::generic_array::typenum::Unsigned;
146 
147         let mut prev: Option<GenericArray<u8, <D as digest::FixedOutput>::OutputSize>> = None;
148 
149         let hmac_output_bytes = D::OutputSize::to_usize();
150         if okm.len() > hmac_output_bytes * 255 {
151             return Err(InvalidLength);
152         }
153 
154         let mut hmac = self.hmac.clone();
155         for (blocknum, okm_block) in okm.chunks_mut(hmac_output_bytes).enumerate() {
156             let block_len = okm_block.len();
157 
158             if let Some(ref prev) = prev {
159                 hmac.update(prev)
160             };
161 
162             // Feed in the info components in sequence. This is equivalent to feeding in the
163             // concatenation of all the info components
164             for info in info_components {
165                 hmac.update(info);
166             }
167 
168             hmac.update(&[blocknum as u8 + 1]);
169 
170             let output = hmac.finalize_reset().into_bytes();
171             okm_block.copy_from_slice(&output[..block_len]);
172 
173             prev = Some(output);
174         }
175 
176         Ok(())
177     }
178 
179     /// The RFC5869 HKDF-Expand operation
180     ///
181     /// If you don't have any `info` to pass, use an empty slice.
expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength>182     pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> {
183         self.expand_multi_info(&[info], okm)
184     }
185 }
186 
187 impl fmt::Display for InvalidPrkLength {
fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>188     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
189         f.write_str("invalid pseudorandom key length, too short")
190     }
191 }
192 
193 #[cfg(feature = "std")]
194 impl ::std::error::Error for InvalidPrkLength {}
195 
196 impl fmt::Display for InvalidLength {
fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>197     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
198         f.write_str("invalid number of blocks, too large output")
199     }
200 }
201 
202 #[cfg(feature = "std")]
203 impl ::std::error::Error for InvalidLength {}
204