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