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