/rust/git/checkouts/nss-rs-71e20fe79ef91440/9b94ca3/src/hkdf.rs
Line | Count | Source |
1 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
2 | | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
3 | | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
4 | | // option. This file may not be copied, modified, or distributed |
5 | | // except according to those terms. |
6 | | |
7 | | #![allow(non_camel_case_types, reason = "C enum naming")] |
8 | | |
9 | | use std::{ |
10 | | convert::TryFrom as _, |
11 | | marker::PhantomData, |
12 | | os::raw::{c_char, c_int, c_uint}, |
13 | | ptr::null_mut, |
14 | | }; |
15 | | |
16 | | use pkcs11_bindings::{CK_ULONG, CKA_DERIVE, CKA_SIGN, CKM_HKDF_DERIVE, CKM_HKDF_KEY_GEN}; |
17 | | |
18 | | use crate::{ |
19 | | Error, SECItem, SECItemBorrowed, SECItemType, |
20 | | constants::{ |
21 | | Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, |
22 | | TLS_VERSION_1_3, Version, |
23 | | }, |
24 | | err::Res, |
25 | | p11::{ |
26 | | self, CK_ATTRIBUTE_TYPE, CK_BBOOL, CK_INVALID_HANDLE, CK_MECHANISM_TYPE, |
27 | | CKF_HKDF_SALT_DATA, CKF_HKDF_SALT_NULL, CKM_HKDF_DATA, PK11_ImportDataKey, PK11Origin, |
28 | | PK11SymKey, Slot, SymKey, random, |
29 | | }, |
30 | | ssl::CK_OBJECT_HANDLE, |
31 | | }; |
32 | | |
33 | | experimental_api!(SSL_HkdfExtract( |
34 | | version: Version, |
35 | | cipher: Cipher, |
36 | | salt: *mut PK11SymKey, |
37 | | ikm: *mut PK11SymKey, |
38 | | prk: *mut *mut PK11SymKey, |
39 | | )); |
40 | | experimental_api!(SSL_HkdfExpandLabel( |
41 | | version: Version, |
42 | | cipher: Cipher, |
43 | | prk: *mut PK11SymKey, |
44 | | handshake_hash: *const u8, |
45 | | handshake_hash_len: c_uint, |
46 | | label: *const c_char, |
47 | | label_len: c_uint, |
48 | | secret: *mut *mut PK11SymKey, |
49 | | )); |
50 | | |
51 | | #[derive(Clone, Copy, Debug)] |
52 | | |
53 | | pub enum HkdfError { |
54 | | InvalidPrkLength, |
55 | | InvalidLength, |
56 | | InternalError, |
57 | | } |
58 | | |
59 | | #[derive(Clone, Copy, Debug)] |
60 | | pub enum HkdfAlgorithm { |
61 | | HKDF_SHA2_256, |
62 | | HKDF_SHA2_384, |
63 | | HKDF_SHA2_512, |
64 | | } |
65 | | |
66 | | #[derive(Clone, Copy, Debug)] |
67 | | pub enum KeyMechanism { |
68 | | Hkdf, |
69 | | } |
70 | | |
71 | | impl KeyMechanism { |
72 | 0 | fn mech(self) -> CK_MECHANISM_TYPE { |
73 | 0 | CK_MECHANISM_TYPE::from(match self { |
74 | 0 | Self::Hkdf => CKM_HKDF_DERIVE, |
75 | | }) |
76 | 0 | } |
77 | | |
78 | 0 | const fn len(self) -> usize { |
79 | 0 | match self { |
80 | 0 | Self::Hkdf => 0, // Let the underlying module decide. |
81 | | } |
82 | 0 | } |
83 | | } |
84 | | #[derive(Clone, Copy, Debug)] |
85 | | |
86 | | pub(crate) struct ParamItem<'a, T> { |
87 | | item: SECItem, |
88 | | marker: PhantomData<&'a T>, |
89 | | } |
90 | | |
91 | | impl<'a, T: Sized + 'a> ParamItem<'a, T> { |
92 | 0 | pub fn new(v: &'a mut T) -> Result<Self, HkdfError> { |
93 | 0 | let item = SECItem { |
94 | | type_: SECItemType::siBuffer, |
95 | 0 | data: std::ptr::from_mut::<T>(v).cast::<u8>(), |
96 | 0 | len: c_uint::try_from(size_of::<T>()).map_err(|_| HkdfError::InvalidLength)?, |
97 | | }; |
98 | 0 | Ok(Self { |
99 | 0 | item, |
100 | 0 | marker: PhantomData, |
101 | 0 | }) |
102 | 0 | } |
103 | | |
104 | 0 | pub const fn ptr(&mut self) -> *mut SECItem { |
105 | 0 | std::ptr::addr_of_mut!(self.item) |
106 | 0 | } |
107 | | } |
108 | | |
109 | | const MAX_KEY_SIZE: usize = 48; |
110 | 598 | const fn key_size(version: Version, cipher: Cipher) -> Res<usize> { |
111 | 598 | if version != TLS_VERSION_1_3 { |
112 | 0 | return Err(Error::UnsupportedVersion); |
113 | 598 | } |
114 | 598 | let size = match cipher { |
115 | 598 | TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 => 32, |
116 | 0 | TLS_AES_256_GCM_SHA384 => 48, |
117 | 0 | _ => return Err(Error::UnsupportedCipher), |
118 | | }; |
119 | 598 | debug_assert!(size <= MAX_KEY_SIZE); |
120 | 598 | Ok(size) |
121 | 598 | } |
122 | | |
123 | | /// Generate a random key of the right size for the given suite. |
124 | | /// |
125 | | /// # Errors |
126 | | /// |
127 | | /// If the ciphersuite or protocol version is not supported. |
128 | 598 | pub fn generate_key(version: Version, cipher: Cipher) -> Res<SymKey> { |
129 | | // With generic_const_expr, this becomes: |
130 | | // import_key(version, &random::<{ key_size(version, cipher) }>()) |
131 | 598 | import_key( |
132 | 598 | version, |
133 | 598 | &random::<MAX_KEY_SIZE>()[0..key_size(version, cipher)?], |
134 | | ) |
135 | 598 | } |
136 | | |
137 | | /// Import a symmetric key for use with HKDF. |
138 | | /// |
139 | | /// # Errors |
140 | | /// |
141 | | /// Errors returned if the key buffer is an incompatible size or the NSS functions fail. |
142 | 19.2k | pub fn import_key(version: Version, buf: &[u8]) -> Res<SymKey> { |
143 | 19.2k | if version != TLS_VERSION_1_3 { |
144 | 0 | return Err(Error::UnsupportedVersion); |
145 | 19.2k | } |
146 | 19.2k | let slot = Slot::internal()?; |
147 | 19.2k | let key_ptr = unsafe { |
148 | 19.2k | PK11_ImportDataKey( |
149 | 19.2k | *slot, |
150 | 19.2k | CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE), |
151 | | PK11Origin::PK11_OriginUnwrap, |
152 | 19.2k | CK_ATTRIBUTE_TYPE::from(CKA_DERIVE), |
153 | 19.2k | SECItemBorrowed::wrap(buf)?.as_mut(), |
154 | 19.2k | null_mut(), |
155 | | ) |
156 | | }; |
157 | 19.2k | SymKey::from_ptr(key_ptr) |
158 | 19.2k | } |
159 | | |
160 | | /// Extract a PRK from the given salt and IKM using the algorithm defined in RFC 5869. |
161 | | /// |
162 | | /// # Errors |
163 | | /// |
164 | | /// Errors returned if inputs are too large or the NSS functions fail. |
165 | 9.47k | pub fn extract( |
166 | 9.47k | version: Version, |
167 | 9.47k | cipher: Cipher, |
168 | 9.47k | salt: Option<&SymKey>, |
169 | 9.47k | ikm: &SymKey, |
170 | 9.47k | ) -> Res<SymKey> { |
171 | 9.47k | let mut prk: *mut PK11SymKey = null_mut(); |
172 | 9.47k | let salt_ptr: *mut PK11SymKey = salt.map_or(null_mut(), |s| **s); |
173 | 9.47k | unsafe { SSL_HkdfExtract(version, cipher, salt_ptr, **ikm, &raw mut prk) }?; |
174 | 9.47k | SymKey::from_ptr(prk) |
175 | 9.47k | } |
176 | | |
177 | | /// Expand a PRK using the HKDF-Expand-Label function defined in RFC 8446. |
178 | | /// |
179 | | /// # Errors |
180 | | /// |
181 | | /// Errors returned if inputs are too large or the NSS functions fail. |
182 | 9.65k | pub fn expand_label( |
183 | 9.65k | version: Version, |
184 | 9.65k | cipher: Cipher, |
185 | 9.65k | prk: &SymKey, |
186 | 9.65k | handshake_hash: &[u8], |
187 | 9.65k | label: &str, |
188 | 9.65k | ) -> Res<SymKey> { |
189 | 9.65k | let l = label.as_bytes(); |
190 | 9.65k | let mut secret: *mut PK11SymKey = null_mut(); |
191 | | |
192 | | // Note that this doesn't allow for passing null() for the handshake hash. |
193 | | // A zero-length slice produces an identical result. |
194 | | unsafe { |
195 | 9.65k | SSL_HkdfExpandLabel( |
196 | 9.65k | version, |
197 | 9.65k | cipher, |
198 | 9.65k | **prk, |
199 | 9.65k | handshake_hash.as_ptr(), |
200 | 9.65k | c_uint::try_from(handshake_hash.len())?, |
201 | 9.65k | l.as_ptr().cast(), |
202 | 9.65k | c_uint::try_from(l.len())?, |
203 | 9.65k | &raw mut secret, |
204 | | ) |
205 | 0 | }?; |
206 | 9.65k | SymKey::from_ptr(secret) |
207 | 9.65k | } |
208 | | |
209 | | pub struct Hkdf { |
210 | | kdf: HkdfAlgorithm, |
211 | | } |
212 | | |
213 | | impl Hkdf { |
214 | | #[must_use] |
215 | 0 | pub const fn new(kdf: HkdfAlgorithm) -> Self { |
216 | 0 | Self { kdf } |
217 | 0 | } |
218 | | |
219 | | #[expect(clippy::unused_self)] |
220 | 0 | pub fn import_secret(&self, ikm: &[u8]) -> Result<SymKey, HkdfError> { |
221 | 0 | crate::init().map_err(|_| HkdfError::InternalError)?; |
222 | | |
223 | 0 | let slot = Slot::internal().map_err(|_| HkdfError::InternalError)?; |
224 | 0 | let ikm_item = SECItemBorrowed::wrap(ikm).map_err(|_| HkdfError::InternalError)?; |
225 | 0 | let ikm_item_ptr = std::ptr::from_ref(ikm_item.as_ref()).cast_mut(); |
226 | | |
227 | 0 | let ptr = unsafe { |
228 | 0 | p11::PK11_ImportSymKey( |
229 | 0 | *slot, |
230 | 0 | CK_MECHANISM_TYPE::from(CKM_HKDF_KEY_GEN), |
231 | | PK11Origin::PK11_OriginUnwrap, |
232 | 0 | CK_ATTRIBUTE_TYPE::from(CKA_SIGN), |
233 | 0 | ikm_item_ptr, |
234 | 0 | null_mut(), |
235 | | ) |
236 | | }; |
237 | 0 | let s = SymKey::from_ptr(ptr).map_err(|_| HkdfError::InternalError)?; |
238 | 0 | Ok(s) |
239 | 0 | } |
240 | | |
241 | 0 | fn mech(&self) -> CK_MECHANISM_TYPE { |
242 | 0 | CK_MECHANISM_TYPE::from(match self.kdf { |
243 | 0 | HkdfAlgorithm::HKDF_SHA2_256 => p11::CKM_SHA256, |
244 | 0 | HkdfAlgorithm::HKDF_SHA2_384 => p11::CKM_SHA384, |
245 | 0 | HkdfAlgorithm::HKDF_SHA2_512 => p11::CKM_SHA512, |
246 | | }) |
247 | 0 | } |
248 | | |
249 | 0 | pub fn extract(&self, salt: &[u8], ikm: &SymKey) -> Result<SymKey, HkdfError> { |
250 | 0 | crate::init().map_err(|_| HkdfError::InternalError)?; |
251 | | |
252 | 0 | let salt_type = if salt.is_empty() { |
253 | 0 | CKF_HKDF_SALT_NULL |
254 | | } else { |
255 | 0 | CKF_HKDF_SALT_DATA |
256 | | }; |
257 | 0 | let mut params = p11::CK_HKDF_PARAMS { |
258 | 0 | bExtract: CK_BBOOL::from(true), |
259 | 0 | bExpand: CK_BBOOL::from(false), |
260 | 0 | prfHashMechanism: self.mech(), |
261 | 0 | ulSaltType: CK_ULONG::from(salt_type), |
262 | 0 | pSalt: salt.as_ptr().cast_mut(), // const-cast = bad API |
263 | 0 | ulSaltLen: CK_ULONG::try_from(salt.len()).map_err(|_| HkdfError::InvalidLength)?, |
264 | 0 | hSaltKey: CK_OBJECT_HANDLE::from(CK_INVALID_HANDLE), |
265 | 0 | pInfo: null_mut(), |
266 | | ulInfoLen: 0, |
267 | | }; |
268 | 0 | let mut params_item = ParamItem::new(&mut params)?; |
269 | 0 | let ptr = unsafe { |
270 | 0 | p11::PK11_Derive( |
271 | 0 | **ikm, |
272 | 0 | CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE), |
273 | 0 | params_item.ptr(), |
274 | 0 | CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE), |
275 | 0 | CK_MECHANISM_TYPE::from(CKA_DERIVE), |
276 | | 0, |
277 | | ) |
278 | | }; |
279 | | |
280 | 0 | let prk = SymKey::from_ptr(ptr).map_err(|_| HkdfError::InternalError)?; |
281 | 0 | Ok(prk) |
282 | 0 | } |
283 | | |
284 | | // NB: `info` must outlive the returned value. |
285 | 0 | fn expand_params(&self, info: &[u8]) -> p11::CK_HKDF_PARAMS { |
286 | 0 | p11::CK_HKDF_PARAMS { |
287 | 0 | bExtract: CK_BBOOL::from(false), |
288 | 0 | bExpand: CK_BBOOL::from(true), |
289 | 0 | prfHashMechanism: self.mech(), |
290 | 0 | ulSaltType: CK_ULONG::from(CKF_HKDF_SALT_NULL), |
291 | 0 | pSalt: null_mut(), |
292 | 0 | ulSaltLen: 0, |
293 | 0 | hSaltKey: CK_OBJECT_HANDLE::from(CK_INVALID_HANDLE), |
294 | 0 | pInfo: info.as_ptr().cast_mut(), // const-cast = bad API |
295 | 0 | ulInfoLen: CK_ULONG::try_from(info.len()).expect("Integer overflow"), |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | 0 | pub fn expand_key( |
300 | 0 | &self, |
301 | 0 | prk: &SymKey, |
302 | 0 | info: &[u8], |
303 | 0 | key_mech: KeyMechanism, |
304 | 0 | ) -> Result<SymKey, HkdfError> { |
305 | 0 | crate::init().map_err(|_| HkdfError::InternalError)?; |
306 | | |
307 | 0 | let mut params = self.expand_params(info); |
308 | 0 | let mut params_item = ParamItem::new(&mut params)?; |
309 | 0 | let ptr = unsafe { |
310 | 0 | p11::PK11_Derive( |
311 | 0 | **prk, |
312 | 0 | CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE), |
313 | 0 | params_item.ptr(), |
314 | 0 | key_mech.mech(), |
315 | 0 | CK_MECHANISM_TYPE::from(CKA_DERIVE), |
316 | 0 | c_int::try_from(key_mech.len()).map_err(|_| HkdfError::InvalidLength)?, |
317 | | ) |
318 | | }; |
319 | 0 | let okm = SymKey::from_ptr(ptr).map_err(|_| HkdfError::InternalError)?; |
320 | 0 | Ok(okm) |
321 | 0 | } |
322 | | |
323 | 0 | pub fn expand_data(&self, prk: &SymKey, info: &[u8], len: usize) -> Result<Vec<u8>, HkdfError> { |
324 | 0 | crate::init().map_err(|_| HkdfError::InternalError)?; |
325 | | |
326 | 0 | let mut params = self.expand_params(info); |
327 | 0 | let mut params_item = ParamItem::new(&mut params)?; |
328 | 0 | let ptr = unsafe { |
329 | 0 | p11::PK11_Derive( |
330 | 0 | **prk, |
331 | 0 | CK_MECHANISM_TYPE::from(CKM_HKDF_DATA), |
332 | 0 | params_item.ptr(), |
333 | 0 | CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE), |
334 | 0 | CK_MECHANISM_TYPE::from(CKA_DERIVE), |
335 | 0 | c_int::try_from(len).map_err(|_| HkdfError::InvalidLength)?, |
336 | | ) |
337 | | }; |
338 | 0 | let k = SymKey::from_ptr(ptr).map_err(|_| HkdfError::InternalError)?; |
339 | 0 | let r = Vec::from(k.key_data().map_err(|_| HkdfError::InternalError)?); |
340 | 0 | Ok(r) |
341 | 0 | } |
342 | | } |