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