1use alloc::boxed::Box;
2
3use super::{ActiveKeyExchange, hmac};
4use crate::error::Error;
5use crate::version::TLS12;
6
7#[allow(clippy::exhaustive_structs)]
9pub struct PrfUsingHmac<'a>(pub &'a dyn hmac::Hmac);
10
11impl Prf for PrfUsingHmac<'_> {
12 fn for_key_exchange(
13 &self,
14 output: &mut [u8; 48],
15 kx: Box<dyn ActiveKeyExchange>,
16 peer_pub_key: &[u8],
17 label: &[u8],
18 seed: &[u8],
19 ) -> Result<(), Error> {
20 prf(
21 output,
22 self.0
23 .with_key(
24 kx.complete_for_tls_version(peer_pub_key, &TLS12)?
25 .secret_bytes(),
26 )
27 .as_ref(),
28 label,
29 seed,
30 );
31 Ok(())
32 }
33
34 fn new_secret(&self, secret: &[u8; 48]) -> Box<dyn PrfSecret> {
35 Box::new(PrfSecretUsingHmac(self.0.with_key(secret)))
36 }
37}
38
39struct PrfSecretUsingHmac(Box<dyn hmac::Key>);
40
41impl PrfSecret for PrfSecretUsingHmac {
42 fn prf(&self, output: &mut [u8], label: &[u8], seed: &[u8]) {
43 prf(output, &*self.0, label, seed)
44 }
45}
46
47pub trait Prf: Send + Sync {
54 fn for_key_exchange(
63 &self,
64 output: &mut [u8; 48],
65 kx: Box<dyn ActiveKeyExchange>,
66 peer_pub_key: &[u8],
67 label: &[u8],
68 seed: &[u8],
69 ) -> Result<(), Error>;
70
71 fn new_secret(&self, master_secret: &[u8; 48]) -> Box<dyn PrfSecret>;
77
78 fn fips(&self) -> bool {
80 false
81 }
82}
83
84pub trait PrfSecret: Send + Sync {
86 fn prf(&self, output: &mut [u8], label: &[u8], seed: &[u8]);
92}
93
94pub(crate) fn prf(out: &mut [u8], hmac_key: &dyn hmac::Key, label: &[u8], seed: &[u8]) {
95 let mut previous_a: Option<hmac::Tag> = None;
96
97 let chunk_size = hmac_key.tag_len();
98 for chunk in out.chunks_mut(chunk_size) {
99 let a_i = match previous_a {
100 None => hmac_key.sign(&[label, seed]),
102 Some(previous_a) => hmac_key.sign(&[previous_a.as_ref()]),
104 };
105
106 let p_term = hmac_key.sign(&[a_i.as_ref(), label, seed]);
108 chunk.copy_from_slice(&p_term.as_ref()[..chunk.len()]);
109
110 previous_a = Some(a_i);
111 }
112}
113
114#[cfg(all(test, feature = "ring"))]
115mod tests {
116 use crate::crypto::hmac::Hmac;
117 use crate::crypto::ring::hmac;
120
121 #[test]
124 fn check_sha256() {
125 let secret = b"\x9b\xbe\x43\x6b\xa9\x40\xf0\x17\xb1\x76\x52\x84\x9a\x71\xdb\x35";
126 let seed = b"\xa0\xba\x9f\x93\x6c\xda\x31\x18\x27\xa6\xf7\x96\xff\xd5\x19\x8c";
127 let label = b"test label";
128 let expect = include_bytes!("../testdata/prf-result.1.bin");
129 let mut output = [0u8; 100];
130
131 super::prf(
132 &mut output,
133 &*hmac::HMAC_SHA256.with_key(secret),
134 label,
135 seed,
136 );
137 assert_eq!(expect.len(), output.len());
138 assert_eq!(expect.to_vec(), output.to_vec());
139 }
140
141 #[test]
142 fn check_sha512() {
143 let secret = b"\xb0\x32\x35\x23\xc1\x85\x35\x99\x58\x4d\x88\x56\x8b\xbb\x05\xeb";
144 let seed = b"\xd4\x64\x0e\x12\xe4\xbc\xdb\xfb\x43\x7f\x03\xe6\xae\x41\x8e\xe5";
145 let label = b"test label";
146 let expect = include_bytes!("../testdata/prf-result.2.bin");
147 let mut output = [0u8; 196];
148
149 super::prf(
150 &mut output,
151 &*hmac::HMAC_SHA512.with_key(secret),
152 label,
153 seed,
154 );
155 assert_eq!(expect.len(), output.len());
156 assert_eq!(expect.to_vec(), output.to_vec());
157 }
158
159 #[test]
160 fn check_sha384() {
161 let secret = b"\xb8\x0b\x73\x3d\x6c\xee\xfc\xdc\x71\x56\x6e\xa4\x8e\x55\x67\xdf";
162 let seed = b"\xcd\x66\x5c\xf6\xa8\x44\x7d\xd6\xff\x8b\x27\x55\x5e\xdb\x74\x65";
163 let label = b"test label";
164 let expect = include_bytes!("../testdata/prf-result.3.bin");
165 let mut output = [0u8; 148];
166
167 super::prf(
168 &mut output,
169 &*hmac::HMAC_SHA384.with_key(secret),
170 label,
171 seed,
172 );
173 assert_eq!(expect.len(), output.len());
174 assert_eq!(expect.to_vec(), output.to_vec());
175 }
176}
177
178#[cfg(all(bench, feature = "ring"))]
179mod benchmarks {
180 #[bench]
181 fn bench_sha256(b: &mut test::Bencher) {
182 use crate::crypto::hmac::Hmac;
183 use crate::crypto::ring::hmac;
184
185 let label = &b"extended master secret"[..];
186 let seed = [0u8; 32];
187 let key = &b"secret"[..];
188
189 b.iter(|| {
190 let mut out = [0u8; 48];
191 super::prf(&mut out, &*hmac::HMAC_SHA256.with_key(key), &label, &seed);
192 test::black_box(out);
193 });
194 }
195}