aws_lc_rs/
tls_prf.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4//! TLS 1.2 PRF API's for usage in [RFC 5246](https://www.rfc-editor.org/rfc/rfc5246) and [RFC 7627](https://www.rfc-editor.org/rfc/rfc7627).
5//!
6//! # Example
7//!
8//! ```rust
9//! # use std::error::Error;
10//! #
11//! # fn main() -> Result<(), Box<dyn Error>> {
12//! use aws_lc_rs::tls_prf::{Secret, P_SHA256};
13//!
14//! let pre_master_secret = &[42; 32]; // Value is established during key exchange
15//! let session_hash = &[7; 32]; // Session hash of handshake log
16//!
17//! let secret = Secret::new(&P_SHA256, pre_master_secret)?;
18//!
19//! let derived_secret = secret.derive(b"extended master secret", session_hash, 48)?;
20//!
21//! let derived_secret_bytes = derived_secret.as_ref();
22//!
23//! assert_eq!(derived_secret_bytes.len(), 48);
24//! # Ok(())
25//! # }
26//! ```
27
28use core::fmt::Debug;
29
30use crate::digest::{match_digest_type, AlgorithmID};
31use crate::error::Unspecified;
32use crate::fips::indicator_check;
33use core::ptr::null;
34
35use crate::aws_lc::CRYPTO_tls1_prf;
36
37/// The TLS PRF `P_hash` Algorithm
38pub struct Algorithm(AlgorithmID);
39
40impl Debug for Algorithm {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        Debug::fmt(&self.0, f)
43    }
44}
45
46/// SHA-256 `P_hash` algorithm
47pub const P_SHA256: Algorithm = Algorithm(AlgorithmID::SHA256);
48
49/// SHA-384 `P_hash` algorithm
50pub const P_SHA384: Algorithm = Algorithm(AlgorithmID::SHA384);
51
52/// SHA-512 `P_hash` algorithm
53pub const P_SHA512: Algorithm = Algorithm(AlgorithmID::SHA512);
54
55/// Encapsulates a PRF algorithm and secret bytes to be used to derive output.
56pub struct Secret {
57    algorithm: &'static Algorithm,
58    secret: Box<[u8]>,
59}
60
61impl Secret {
62    /// Constructs a new `Secret` for use with the associated `P_hash` `Algorithm`.
63    ///
64    /// # Errors
65    /// * `Unspecified`: If `secret.is_empty() == true`.
66    pub fn new(algorithm: &'static Algorithm, secret: &[u8]) -> Result<Self, Unspecified> {
67        if secret.is_empty() {
68            return Err(Unspecified);
69        }
70
71        let secret = Vec::from(secret).into_boxed_slice();
72
73        Ok(Self { algorithm, secret })
74    }
75
76    /// Calculates `len` bytes of TLS PRF using the configured [`Algorithm`], and returns [`Secret`] of length `len`.
77    /// See [RFC5246](https://datatracker.ietf.org/doc/html/rfc5246#section-5)
78    ///
79    /// # Errors
80    /// * `Unspecified`: Returned if the PRF derivation fails.
81    pub fn derive(self, label: &[u8], seed: &[u8], output: usize) -> Result<Secret, Unspecified> {
82        prf(self.algorithm, &self.secret, label, seed, None, output)
83    }
84
85    /// Calculates `len` bytes of TLS PRF using the configured [`Algorithm`], and returns [`Secret`] of length `len`.
86    ///
87    /// In this method, `seed1` and `seed2` will be concatenated together: `seed1 || seed2`.
88    ///
89    /// See [RFC5246](https://datatracker.ietf.org/doc/html/rfc5246#section-5)
90    ///
91    /// # Errors
92    /// * `Unspecified`: Returned if the PRF derivation fails.
93    pub fn derive_with_seed_concatination(
94        self,
95        label: &[u8],
96        seed1: &[u8],
97        seed2: &[u8],
98        len: usize,
99    ) -> Result<Secret, Unspecified> {
100        prf(self.algorithm, &self.secret, label, seed1, Some(seed2), len)
101    }
102}
103
104impl Drop for Secret {
105    fn drop(&mut self) {
106        use zeroize::Zeroize;
107        self.secret.zeroize();
108    }
109}
110
111impl AsRef<[u8]> for Secret {
112    fn as_ref(&self) -> &[u8] {
113        &self.secret
114    }
115}
116
117impl<const L: usize> TryFrom<Secret> for [u8; L] {
118    type Error = Unspecified;
119
120    fn try_from(value: Secret) -> Result<Self, Self::Error> {
121        if value.secret.len() != L {
122            return Err(Unspecified);
123        }
124
125        let mut ret = [0u8; L];
126        ret.copy_from_slice(&value.secret);
127
128        Ok(ret)
129    }
130}
131
132#[allow(clippy::missing_fields_in_debug)]
133impl Debug for Secret {
134    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135        f.debug_struct("Secret")
136            .field("algorithm", &self.algorithm)
137            .finish()
138    }
139}
140
141fn prf(
142    algorithm: &'static Algorithm,
143    secret: &[u8],
144    label: &[u8],
145    seed1: &[u8],
146    seed2: Option<&[u8]>,
147    output: usize,
148) -> Result<Secret, Unspecified> {
149    if output == 0 {
150        return Err(Unspecified);
151    }
152
153    let mut output = vec![0u8; output];
154
155    let digest = match_digest_type(&algorithm.0);
156
157    let (seed2, seed2_len) = if let Some(seed2) = seed2 {
158        (seed2.as_ptr(), seed2.len())
159    } else {
160        (null(), 0usize)
161    };
162
163    if 1 != indicator_check!(unsafe {
164        CRYPTO_tls1_prf(
165            *digest,
166            output.as_mut_ptr(),
167            output.len(),
168            secret.as_ptr(),
169            secret.len(),
170            label.as_ptr().cast(),
171            label.len(),
172            seed1.as_ptr(),
173            seed1.len(),
174            seed2,
175            seed2_len,
176        )
177    }) {
178        return Err(Unspecified);
179    }
180
181    Ok(Secret {
182        algorithm,
183        secret: output.into_boxed_slice(),
184    })
185}
186
187#[cfg(test)]
188mod tests {
189    use alloc::ffi::CString;
190
191    use super::{Secret, P_SHA256, P_SHA384, P_SHA512};
192
193    #[cfg(feature = "fips")]
194    mod fips;
195
196    #[test]
197    fn aws_lc_kat() {
198        // Testings constants from https://github.com/aws/aws-lc/blob/c5ef1079b605acac1068ac362a7008f30fd8c100/crypto/fipsmodule/self_check/self_check.c#L1130
199        const TLS_SECRET: &[u8; 32] = &[
200            0xab, 0xc3, 0x65, 0x7b, 0x09, 0x4c, 0x76, 0x28, 0xa0, 0xb2, 0x82, 0x99, 0x6f, 0xe7,
201            0x5a, 0x75, 0xf4, 0x98, 0x4f, 0xd9, 0x4d, 0x4e, 0xcc, 0x2f, 0xcf, 0x53, 0xa2, 0xc4,
202            0x69, 0xa3, 0xf7, 0x31,
203        ];
204
205        const TLS_LABEL: &str = "FIPS self test";
206
207        const TLS_SEED1: &[u8; 16] = &[
208            0x8f, 0x0d, 0xe8, 0xb6, 0x90, 0x8f, 0xb1, 0xd2, 0x6d, 0x51, 0xf4, 0x79, 0x18, 0x63,
209            0x51, 0x65,
210        ];
211
212        const TLS_SEED2: &[u8; 16] = &[
213            0x7d, 0x24, 0x1a, 0x9d, 0x3c, 0x59, 0xbf, 0x3c, 0x31, 0x1e, 0x2b, 0x21, 0x41, 0x8d,
214            0x32, 0x81,
215        ];
216
217        const TLS_OUTPUT_P_SHA256: &[u8; 32] = &[
218            0xe2, 0x1d, 0xd6, 0xc2, 0x68, 0xc7, 0x57, 0x03, 0x2c, 0x2c, 0xeb, 0xbb, 0xb8, 0xa9,
219            0x7d, 0xe9, 0xee, 0xe6, 0xc9, 0x47, 0x83, 0x0a, 0xbd, 0x11, 0x60, 0x5d, 0xd5, 0x2c,
220            0x47, 0xb6, 0x05, 0x88,
221        ];
222
223        let secret = Secret::new(&P_SHA256, TLS_SECRET).expect("secret created");
224
225        // The AWS-LC KAT annoyingly includes the null terminator in its KAT, so using CString here to handle that for us.
226        let label = CString::new(TLS_LABEL).expect("failed to create CString");
227
228        let output = secret
229            .derive_with_seed_concatination(
230                label.as_bytes_with_nul(),
231                TLS_SEED1,
232                TLS_SEED2,
233                TLS_OUTPUT_P_SHA256.len(),
234            )
235            .unwrap();
236
237        assert_eq!(TLS_OUTPUT_P_SHA256, output.as_ref());
238
239        let mut seed = Vec::<u8>::with_capacity(TLS_SEED1.len() + TLS_SEED2.len());
240        seed.extend(TLS_SEED1);
241        seed.extend(TLS_SEED2);
242
243        let secret = Secret::new(&P_SHA256, TLS_SECRET).expect("secret created");
244        let output = secret
245            .derive(label.as_bytes_with_nul(), &seed, TLS_OUTPUT_P_SHA256.len())
246            .unwrap();
247
248        assert_eq!(TLS_OUTPUT_P_SHA256, output.as_ref());
249    }
250
251    #[test]
252    fn sha256() {
253        // KAT Sourced from https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/Component-Testing
254
255        const SECRET: &[u8] = &[
256            0xf8, 0x93, 0x8e, 0xcc, 0x9e, 0xde, 0xbc, 0x50, 0x30, 0xc0, 0xc6, 0xa4, 0x41, 0xe2,
257            0x13, 0xcd, 0x24, 0xe6, 0xf7, 0x70, 0xa5, 0x0d, 0xda, 0x07, 0x87, 0x6f, 0x8d, 0x55,
258            0xda, 0x06, 0x2b, 0xca, 0xdb, 0x38, 0x6b, 0x41, 0x1f, 0xd4, 0xfe, 0x43, 0x13, 0xa6,
259            0x04, 0xfc, 0xe6, 0xc1, 0x7f, 0xbc,
260        ];
261        const LABEL: &[u8] = b"master secret";
262        const SEED1: &[u8] = &[
263            0x36, 0xc1, 0x29, 0xd0, 0x1a, 0x32, 0x00, 0x89, 0x4b, 0x91, 0x79, 0xfa, 0xac, 0x58,
264            0x9d, 0x98, 0x35, 0xd5, 0x87, 0x75, 0xf9, 0xb5, 0xea, 0x35, 0x87, 0xcb, 0x8f, 0xd0,
265            0x36, 0x4c, 0xae, 0x8c,
266        ];
267        const SEED2: &[u8] = &[
268            0xf6, 0xc9, 0x57, 0x5e, 0xd7, 0xdd, 0xd7, 0x3e, 0x1f, 0x7d, 0x16, 0xec, 0xa1, 0x15,
269            0x41, 0x58, 0x12, 0xa4, 0x3c, 0x2b, 0x74, 0x7d, 0xaa, 0xaa, 0xe0, 0x43, 0xab, 0xfb,
270            0x50, 0x05, 0x3f, 0xce,
271        ];
272        const EXPECT: &[u8] = &[
273            0x20, 0x2c, 0x88, 0xc0, 0x0f, 0x84, 0xa1, 0x7a, 0x20, 0x02, 0x70, 0x79, 0x60, 0x47,
274            0x87, 0x46, 0x11, 0x76, 0x45, 0x55, 0x39, 0xe7, 0x05, 0xbe, 0x73, 0x08, 0x90, 0x60,
275            0x2c, 0x28, 0x9a, 0x50, 0x01, 0xe3, 0x4e, 0xeb, 0x3a, 0x04, 0x3e, 0x5d, 0x52, 0xa6,
276            0x5e, 0x66, 0x12, 0x51, 0x88, 0xbf,
277        ];
278
279        let secret = Secret::new(&P_SHA256, SECRET).expect("secret created");
280
281        let output = secret
282            .derive_with_seed_concatination(LABEL, SEED1, SEED2, EXPECT.len())
283            .expect("derive successful");
284
285        assert_eq!(EXPECT, output.as_ref());
286    }
287
288    #[test]
289    fn sha384() {
290        // KAT Sourced from https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/Component-Testing
291
292        const SECRET: &[u8] = &[
293            0xa5, 0xe2, 0x64, 0x26, 0x33, 0xf5, 0xb8, 0xc8, 0x1a, 0xd3, 0xfe, 0x0c, 0x2f, 0xe3,
294            0xa8, 0xe5, 0xef, 0x80, 0x6b, 0x06, 0x12, 0x1d, 0xd1, 0x0d, 0xf4, 0xbb, 0x0f, 0xe8,
295            0x57, 0xbf, 0xdc, 0xf5, 0x22, 0x55, 0x8e, 0x05, 0xd2, 0x68, 0x2c, 0x9a, 0x80, 0xc7,
296            0x41, 0xa3, 0xaa, 0xb1, 0x71, 0x6f,
297        ];
298        const LABEL: &[u8] = b"master secret";
299        const SEED1: &[u8] = &[
300            0xab, 0xe4, 0xbf, 0x55, 0x27, 0x42, 0x9a, 0xc8, 0xeb, 0x13, 0x57, 0x4d, 0x27, 0x09,
301            0xe8, 0x01, 0x2b, 0xd1, 0xa1, 0x13, 0xc6, 0xd3, 0xb1, 0xd3, 0xaa, 0x2c, 0x38, 0x40,
302            0x51, 0x87, 0x78, 0xac,
303        ];
304        const SEED2: &[u8] = &[
305            0xcb, 0x6e, 0x0b, 0x3e, 0xb0, 0x29, 0x76, 0xb6, 0x46, 0x6d, 0xfa, 0x96, 0x51, 0xc2,
306            0x91, 0x94, 0x14, 0xf1, 0x64, 0x8f, 0xd3, 0xa7, 0x83, 0x8d, 0x02, 0x15, 0x3e, 0x5b,
307            0xd3, 0x95, 0x35, 0xb6,
308        ];
309        const EXPECT: &[u8] = &[
310            0xb4, 0xd4, 0x9b, 0xfa, 0x87, 0x74, 0x7f, 0xe8, 0x15, 0x45, 0x7b, 0xc3, 0xda, 0x15,
311            0x07, 0x3d, 0x6a, 0xc7, 0x33, 0x89, 0xe7, 0x03, 0x07, 0x9a, 0x35, 0x03, 0xc0, 0x9e,
312            0x14, 0xbd, 0x55, 0x9a, 0x5b, 0x3c, 0x7c, 0x60, 0x1c, 0x73, 0x65, 0xf6, 0xea, 0x8c,
313            0x68, 0xd3, 0xd9, 0x59, 0x68, 0x27,
314        ];
315
316        let secret = Secret::new(&P_SHA384, SECRET).expect("secret created");
317
318        let output = secret
319            .derive_with_seed_concatination(LABEL, SEED1, SEED2, EXPECT.len())
320            .expect("derive successful");
321
322        assert_eq!(EXPECT, output.as_ref());
323    }
324
325    #[test]
326    fn sha512() {
327        // KAT Sourced from https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/Component-Testing
328
329        const SECRET: &[u8] = &[
330            0xdf, 0xef, 0x39, 0xaf, 0x25, 0xc1, 0x26, 0x63, 0xa9, 0x1e, 0xe5, 0xd2, 0x70, 0x42,
331            0xb9, 0x64, 0x4a, 0x16, 0xef, 0x55, 0xb8, 0x10, 0x55, 0xd1, 0xbd, 0x7d, 0xcb, 0x0b,
332            0x8f, 0x06, 0xeb, 0x00, 0x17, 0x08, 0xcd, 0xef, 0xcf, 0x82, 0x59, 0x1d, 0xef, 0xca,
333            0x1a, 0x6f, 0x1a, 0xc6, 0x93, 0xab,
334        ];
335        const LABEL: &[u8] = b"master secret";
336        const SEED1: &[u8] = &[
337            0x78, 0xbc, 0x52, 0x98, 0xdf, 0xe9, 0xcf, 0x8e, 0xd3, 0x36, 0xc2, 0xe2, 0xf0, 0xf6,
338            0xb4, 0x6e, 0x24, 0x56, 0xf3, 0x9f, 0x35, 0xf1, 0x14, 0x3c, 0xd2, 0x1e, 0xaa, 0x16,
339            0x27, 0x70, 0x25, 0xb2,
340        ];
341        const SEED2: &[u8] = &[
342            0xe2, 0x33, 0x9a, 0x6c, 0x68, 0x1e, 0xb3, 0x08, 0x08, 0x88, 0x39, 0x71, 0xb1, 0xce,
343            0x5b, 0x9b, 0x1e, 0xce, 0x0f, 0x3d, 0x01, 0x1a, 0x96, 0xa7, 0xff, 0xf1, 0xf5, 0xf9,
344            0xd8, 0x0f, 0xfd, 0x4b,
345        ];
346        const EXPECT: &[u8] = &[
347            0xa7, 0x0c, 0x5f, 0xe8, 0xd3, 0x4b, 0x64, 0x5a, 0x20, 0xce, 0x98, 0x96, 0x9b, 0xd3,
348            0x08, 0x58, 0xe7, 0x29, 0xc7, 0x7c, 0x8a, 0x5f, 0x05, 0xd3, 0xe2, 0x89, 0x21, 0x9d,
349            0x6b, 0x57, 0x52, 0xb7, 0x5b, 0x75, 0xe1, 0xca, 0x00, 0xd3, 0x32, 0x96, 0x58, 0xd7,
350            0xf1, 0x88, 0xed, 0x1a, 0xb7, 0xe0,
351        ];
352
353        let secret = Secret::new(&P_SHA512, SECRET).expect("secret created");
354
355        let output = secret
356            .derive_with_seed_concatination(LABEL, SEED1, SEED2, EXPECT.len())
357            .expect("derive successful");
358
359        assert_eq!(EXPECT, output.as_ref());
360    }
361
362    #[test]
363    fn try_into_array() {
364        let secret = Secret::new(&P_SHA256, &[42u8; 32]).expect("secret creation to succeed");
365
366        let secret = secret
367            .derive("master secret".as_bytes(), &[7u8; 3], 7)
368            .expect("derive to succeed");
369
370        let _secret: [u8; 7] = secret.try_into().expect("try_into to succeed");
371    }
372}