1 // Copyright 2015 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 //! HMAC-based Extract-and-Expand Key Derivation Function.
16 //!
17 //! HKDF is specified in [RFC 5869].
18 //!
19 //! [RFC 5869]: https://tools.ietf.org/html/rfc5869
20 
21 use crate::{error, hmac};
22 
23 /// An HKDF algorithm.
24 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
25 pub struct Algorithm(hmac::Algorithm);
26 
27 impl Algorithm {
28     /// The underlying HMAC algorithm.
29     #[inline]
hmac_algorithm(&self) -> hmac::Algorithm30     pub fn hmac_algorithm(&self) -> hmac::Algorithm {
31         self.0
32     }
33 }
34 
35 /// HKDF using HMAC-SHA-1. Obsolete.
36 pub static HKDF_SHA1_FOR_LEGACY_USE_ONLY: Algorithm =
37     Algorithm(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY);
38 
39 /// HKDF using HMAC-SHA-256.
40 pub static HKDF_SHA256: Algorithm = Algorithm(hmac::HMAC_SHA256);
41 
42 /// HKDF using HMAC-SHA-384.
43 pub static HKDF_SHA384: Algorithm = Algorithm(hmac::HMAC_SHA384);
44 
45 /// HKDF using HMAC-SHA-512.
46 pub static HKDF_SHA512: Algorithm = Algorithm(hmac::HMAC_SHA512);
47 
48 impl KeyType for Algorithm {
len(&self) -> usize49     fn len(&self) -> usize {
50         self.0.digest_algorithm().output_len
51     }
52 }
53 
54 /// A salt for HKDF operations.
55 #[derive(Debug)]
56 pub struct Salt(hmac::Key);
57 
58 impl Salt {
59     /// Constructs a new `Salt` with the given value based on the given digest
60     /// algorithm.
61     ///
62     /// Constructing a `Salt` is relatively expensive so it is good to reuse a
63     /// `Salt` object instead of re-constructing `Salt`s with the same value.
new(algorithm: Algorithm, value: &[u8]) -> Self64     pub fn new(algorithm: Algorithm, value: &[u8]) -> Self {
65         Salt(hmac::Key::new(algorithm.0, value))
66     }
67 
68     /// The [HKDF-Extract] operation.
69     ///
70     /// [HKDF-Extract]: https://tools.ietf.org/html/rfc5869#section-2.2
extract(&self, secret: &[u8]) -> Prk71     pub fn extract(&self, secret: &[u8]) -> Prk {
72         // The spec says that if no salt is provided then a key of
73         // `digest_alg.output_len` bytes of zeros is used. But, HMAC keys are
74         // already zero-padded to the block length, which is larger than the output
75         // length of the extract step (the length of the digest). Consequently the
76         // `Key` constructor will automatically do the right thing for a
77         // zero-length string.
78         let salt = &self.0;
79         let prk = hmac::sign(salt, secret);
80         Prk(hmac::Key::new(salt.algorithm(), prk.as_ref()))
81     }
82 
83     /// The algorithm used to derive this salt.
84     #[inline]
algorithm(&self) -> Algorithm85     pub fn algorithm(&self) -> Algorithm {
86         Algorithm(self.0.algorithm())
87     }
88 }
89 
90 impl From<Okm<'_, Algorithm>> for Salt {
from(okm: Okm<'_, Algorithm>) -> Self91     fn from(okm: Okm<'_, Algorithm>) -> Self {
92         Self(hmac::Key::from(Okm {
93             prk: okm.prk,
94             info: okm.info,
95             len: okm.len().0,
96             len_cached: okm.len_cached,
97         }))
98     }
99 }
100 
101 /// The length of the OKM (Output Keying Material) for a `Prk::expand()` call.
102 pub trait KeyType {
103     /// The length that `Prk::expand()` should expand its input to.
len(&self) -> usize104     fn len(&self) -> usize;
105 }
106 
107 /// A HKDF PRK (pseudorandom key).
108 #[derive(Clone, Debug)]
109 pub struct Prk(hmac::Key);
110 
111 impl Prk {
112     /// Construct a new `Prk` directly with the given value.
113     ///
114     /// Usually one can avoid using this. It is useful when the application
115     /// intentionally wants to leak the PRK secret, e.g. to implement
116     /// `SSLKEYLOGFILE` functionality.
new_less_safe(algorithm: Algorithm, value: &[u8]) -> Self117     pub fn new_less_safe(algorithm: Algorithm, value: &[u8]) -> Self {
118         Self(hmac::Key::new(algorithm.hmac_algorithm(), value))
119     }
120 
121     /// The [HKDF-Expand] operation.
122     ///
123     /// [HKDF-Expand]: https://tools.ietf.org/html/rfc5869#section-2.3
124     ///
125     /// Fails if (and only if) `len` is too large.
126     #[inline]
expand<'a, L: KeyType>( &'a self, info: &'a [&'a [u8]], len: L, ) -> Result<Okm<'a, L>, error::Unspecified>127     pub fn expand<'a, L: KeyType>(
128         &'a self,
129         info: &'a [&'a [u8]],
130         len: L,
131     ) -> Result<Okm<'a, L>, error::Unspecified> {
132         let len_cached = len.len();
133         if len_cached > 255 * self.0.algorithm().digest_algorithm().output_len {
134             return Err(error::Unspecified);
135         }
136         Ok(Okm {
137             prk: self,
138             info,
139             len,
140             len_cached,
141         })
142     }
143 }
144 
145 impl From<Okm<'_, Algorithm>> for Prk {
from(okm: Okm<Algorithm>) -> Self146     fn from(okm: Okm<Algorithm>) -> Self {
147         Self(hmac::Key::from(Okm {
148             prk: okm.prk,
149             info: okm.info,
150             len: okm.len().0,
151             len_cached: okm.len_cached,
152         }))
153     }
154 }
155 
156 /// An HKDF OKM (Output Keying Material)
157 ///
158 /// Intentionally not `Clone` or `Copy` as an OKM is generally only safe to
159 /// use once.
160 #[derive(Debug)]
161 pub struct Okm<'a, L: KeyType> {
162     prk: &'a Prk,
163     info: &'a [&'a [u8]],
164     len: L,
165     len_cached: usize,
166 }
167 
168 impl<L: KeyType> Okm<'_, L> {
169     /// The `OkmLength` given to `Prk::expand()`.
170     #[inline]
len(&self) -> &L171     pub fn len(&self) -> &L {
172         &self.len
173     }
174 
175     /// Fills `out` with the output of the HKDF-Expand operation for the given
176     /// inputs.
177     ///
178     /// Fails if (and only if) the requested output length is larger than 255
179     /// times the size of the digest algorithm's output. (This is the limit
180     /// imposed by the HKDF specification due to the way HKDF's counter is
181     /// constructed.)
182     #[inline]
fill(self, out: &mut [u8]) -> Result<(), error::Unspecified>183     pub fn fill(self, out: &mut [u8]) -> Result<(), error::Unspecified> {
184         fill_okm(self.prk, self.info, out, self.len_cached)
185     }
186 }
187 
fill_okm( prk: &Prk, info: &[&[u8]], out: &mut [u8], len: usize, ) -> Result<(), error::Unspecified>188 fn fill_okm(
189     prk: &Prk,
190     info: &[&[u8]],
191     out: &mut [u8],
192     len: usize,
193 ) -> Result<(), error::Unspecified> {
194     if out.len() != len {
195         return Err(error::Unspecified);
196     }
197 
198     let digest_alg = prk.0.algorithm().digest_algorithm();
199     assert!(digest_alg.block_len >= digest_alg.output_len);
200 
201     let mut ctx = hmac::Context::with_key(&prk.0);
202 
203     let mut n = 1u8;
204     let mut out = out;
205     loop {
206         for info in info {
207             ctx.update(info);
208         }
209         ctx.update(&[n]);
210 
211         let t = ctx.sign();
212         let t = t.as_ref();
213 
214         // Append `t` to the output.
215         out = if out.len() < digest_alg.output_len {
216             let len = out.len();
217             out.copy_from_slice(&t[..len]);
218             &mut []
219         } else {
220             let (this_chunk, rest) = out.split_at_mut(digest_alg.output_len);
221             this_chunk.copy_from_slice(t);
222             rest
223         };
224 
225         if out.is_empty() {
226             return Ok(());
227         }
228 
229         ctx = hmac::Context::with_key(&prk.0);
230         ctx.update(t);
231         n = n.checked_add(1).unwrap();
232     }
233 }
234