Coverage Report

Created: 2025-07-12 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/hkdf.rs
Line
Count
Source (jump to first uncovered line)
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 AUTHOR DISCLAIMS ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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]
30
0
    pub fn hmac_algorithm(&self) -> hmac::Algorithm {
31
0
        self.0
32
0
    }
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 {
49
0
    fn len(&self) -> usize {
50
0
        self.0.digest_algorithm().output_len()
51
0
    }
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.
64
0
    pub fn new(algorithm: Algorithm, value: &[u8]) -> Self {
65
0
        Self(hmac::Key::new(algorithm.0, value))
66
0
    }
67
68
    /// The [HKDF-Extract] operation.
69
    ///
70
    /// [HKDF-Extract]: https://tools.ietf.org/html/rfc5869#section-2.2
71
0
    pub fn extract(&self, secret: &[u8]) -> Prk {
72
0
        // The spec says that if no salt is provided then a key of
73
0
        // `digest_alg.output_len` bytes of zeros is used. But, HMAC keys are
74
0
        // already zero-padded to the block length, which is larger than the output
75
0
        // length of the extract step (the length of the digest). Consequently the
76
0
        // `Key` constructor will automatically do the right thing for a
77
0
        // zero-length string.
78
0
        let salt = &self.0;
79
0
        let prk = hmac::sign(salt, secret);
80
0
        Prk(hmac::Key::new(salt.algorithm(), prk.as_ref()))
81
0
    }
82
83
    /// The algorithm used to derive this salt.
84
    #[inline]
85
0
    pub fn algorithm(&self) -> Algorithm {
86
0
        Algorithm(self.0.algorithm())
87
0
    }
88
}
89
90
impl From<Okm<'_, Algorithm>> for Salt {
91
0
    fn from(okm: Okm<'_, Algorithm>) -> Self {
92
0
        Self(hmac::Key::from(Okm {
93
0
            prk: okm.prk,
94
0
            info: okm.info,
95
0
            len: okm.len().0,
96
0
            len_cached: okm.len_cached,
97
0
        }))
98
0
    }
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.
104
    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.
117
0
    pub fn new_less_safe(algorithm: Algorithm, value: &[u8]) -> Self {
118
0
        Self(hmac::Key::new(algorithm.hmac_algorithm(), value))
119
0
    }
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]
127
0
    pub fn expand<'a, L: KeyType>(
128
0
        &'a self,
129
0
        info: &'a [&'a [u8]],
130
0
        len: L,
131
0
    ) -> Result<Okm<'a, L>, error::Unspecified> {
132
0
        let len_cached = len.len();
133
0
        if len_cached > 255 * self.0.algorithm().digest_algorithm().output_len() {
134
0
            return Err(error::Unspecified);
135
0
        }
136
0
        Ok(Okm {
137
0
            prk: self,
138
0
            info,
139
0
            len,
140
0
            len_cached,
141
0
        })
142
0
    }
143
}
144
145
impl From<Okm<'_, Algorithm>> for Prk {
146
0
    fn from(okm: Okm<Algorithm>) -> Self {
147
0
        Self(hmac::Key::from(Okm {
148
0
            prk: okm.prk,
149
0
            info: okm.info,
150
0
            len: okm.len().0,
151
0
            len_cached: okm.len_cached,
152
0
        }))
153
0
    }
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]
171
0
    pub fn len(&self) -> &L {
172
0
        &self.len
173
0
    }
Unexecuted instantiation: <ring::hkdf::Okm<ring::hkdf::Algorithm>>::len
Unexecuted instantiation: <ring::hkdf::Okm<ring::hmac::Algorithm>>::len
Unexecuted instantiation: <ring::hkdf::Okm<&ring::aead::quic::Algorithm>>::len
Unexecuted instantiation: <ring::hkdf::Okm<&ring::aead::algorithm::Algorithm>>::len
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]
183
0
    pub fn fill(self, out: &mut [u8]) -> Result<(), error::Unspecified> {
184
0
        fill_okm(self.prk, self.info, out, self.len_cached)
185
0
    }
Unexecuted instantiation: <ring::hkdf::Okm<ring::hmac::Algorithm>>::fill
Unexecuted instantiation: <ring::hkdf::Okm<&ring::aead::quic::Algorithm>>::fill
Unexecuted instantiation: <ring::hkdf::Okm<&ring::aead::algorithm::Algorithm>>::fill
186
}
187
188
0
fn fill_okm(
189
0
    prk: &Prk,
190
0
    info: &[&[u8]],
191
0
    out: &mut [u8],
192
0
    len: usize,
193
0
) -> Result<(), error::Unspecified> {
194
0
    if out.len() != len {
195
0
        return Err(error::Unspecified);
196
0
    }
197
0
198
0
    let digest_alg = prk.0.algorithm().digest_algorithm();
199
0
    assert!(digest_alg.block_len() >= digest_alg.output_len());
200
201
0
    let mut ctx = hmac::Context::with_key(&prk.0);
202
0
203
0
    let mut n = 1u8;
204
0
    let mut out = out;
205
    loop {
206
0
        for info in info {
207
0
            ctx.update(info);
208
0
        }
209
0
        ctx.update(&[n]);
210
0
211
0
        let t = ctx.sign();
212
0
        let t = t.as_ref();
213
0
214
0
        // Append `t` to the output.
215
0
        out = if out.len() < digest_alg.output_len() {
216
0
            let len = out.len();
217
0
            out.copy_from_slice(&t[..len]);
218
0
            &mut []
219
        } else {
220
0
            let (this_chunk, rest) = out.split_at_mut(digest_alg.output_len());
221
0
            this_chunk.copy_from_slice(t);
222
0
            rest
223
        };
224
225
0
        if out.is_empty() {
226
0
            return Ok(());
227
0
        }
228
0
229
0
        ctx = hmac::Context::with_key(&prk.0);
230
0
        ctx.update(t);
231
0
        n = n.checked_add(1).unwrap();
232
    }
233
0
}