/rust/git/checkouts/nss-rs-71e20fe79ef91440/9b94ca3/src/ech.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 | | use std::{ |
8 | | convert::TryFrom as _, |
9 | | ffi::CString, |
10 | | os::raw::{c_char, c_uint}, |
11 | | ptr::{addr_of_mut, null_mut}, |
12 | | }; |
13 | | |
14 | | use log::trace; |
15 | | use pkcs11_bindings::{CKF_DERIVE, CKM_EC_KEY_PAIR_GEN}; |
16 | | |
17 | | use crate::{ |
18 | | SECItem, SECItemBorrowed, SECItemMut, |
19 | | err::{Error, Res, ssl::SSL_ERROR_ECH_RETRY_WITH_ECH}, |
20 | | experimental_api, null_safe_slice, |
21 | | p11::{self, PrivateKey, PublicKey, SECKEYPrivateKey, SECKEYPublicKey, Slot}, |
22 | | prio::PRFileDesc, |
23 | | ssl::PRBool, |
24 | | }; |
25 | | pub use crate::{ |
26 | | p11::{HpkeAeadId as AeadId, HpkeKdfId as KdfId, HpkeKemId as KemId}, |
27 | | ssl::HpkeSymmetricSuite as SymmetricSuite, |
28 | | }; |
29 | | |
30 | | experimental_api!(SSL_EnableTls13GreaseEch( |
31 | | fd: *mut PRFileDesc, |
32 | | enabled: PRBool, |
33 | | )); |
34 | | |
35 | | experimental_api!(SSL_GetEchRetryConfigs( |
36 | | fd: *mut PRFileDesc, |
37 | | config: *mut SECItem, |
38 | | )); |
39 | | |
40 | | experimental_api!(SSL_SetClientEchConfigs( |
41 | | fd: *mut PRFileDesc, |
42 | | config_list: *const u8, |
43 | | config_list_len: c_uint, |
44 | | )); |
45 | | |
46 | | experimental_api!(SSL_SetServerEchConfigs( |
47 | | fd: *mut PRFileDesc, |
48 | | pk: *const SECKEYPublicKey, |
49 | | sk: *const SECKEYPrivateKey, |
50 | | record: *const u8, |
51 | | record_len: c_uint, |
52 | | )); |
53 | | |
54 | | experimental_api!(SSL_EncodeEchConfigId( |
55 | | config_id: u8, |
56 | | public_name: *const c_char, |
57 | | max_name_len: c_uint, |
58 | | kem_id: KemId::Type, |
59 | | pk: *const SECKEYPublicKey, |
60 | | hpke_suites: *const SymmetricSuite, |
61 | | hpke_suite_count: c_uint, |
62 | | out: *mut u8, |
63 | | out_len: *mut c_uint, |
64 | | max_len: c_uint, |
65 | | )); |
66 | | |
67 | | /// Convert any result that contains an ECH error into a result with an `EchRetry`. |
68 | 0 | pub fn convert_ech_error(fd: *mut PRFileDesc, err: Error) -> Error { |
69 | | if let Error::Nss { |
70 | | code: SSL_ERROR_ECH_RETRY_WITH_ECH, |
71 | | .. |
72 | 0 | } = &err |
73 | | { |
74 | 0 | let mut item = SECItemMut::make_empty(); |
75 | 0 | if unsafe { SSL_GetEchRetryConfigs(fd, item.as_mut()).is_err() } { |
76 | 0 | return Error::Internal; |
77 | 0 | } |
78 | 0 | let buf = unsafe { |
79 | 0 | let slc = null_safe_slice(item.as_ref().data, item.as_ref().len as usize); |
80 | 0 | Vec::from(slc) |
81 | | }; |
82 | 0 | Error::EchRetry(buf) |
83 | | } else { |
84 | 0 | err |
85 | | } |
86 | 0 | } |
87 | | |
88 | | /// Generate a key pair for encrypted client hello (ECH). |
89 | | /// |
90 | | /// # Errors |
91 | | /// |
92 | | /// When NSS fails to generate a key pair or when the KEM is not supported. |
93 | | /// |
94 | | /// # Panics |
95 | | /// |
96 | | /// When underlying types aren't large enough to hold keys. So never. |
97 | 0 | pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { |
98 | 0 | let slot = Slot::internal()?; |
99 | | |
100 | 0 | let oid_data = unsafe { p11::SECOID_FindOIDByTag(p11::SECOidTag::SEC_OID_CURVE25519) }; |
101 | 0 | let oid = unsafe { oid_data.as_ref() }.ok_or(Error::Internal)?; |
102 | 0 | let oid_slc = unsafe { null_safe_slice(oid.oid.data, oid.oid.len) }; |
103 | 0 | let mut params: Vec<u8> = Vec::with_capacity(oid_slc.len() + 2); |
104 | 0 | params.push(u8::try_from(p11::SEC_ASN1_OBJECT_ID)?); |
105 | 0 | params.push(u8::try_from(oid.oid.len)?); |
106 | 0 | params.extend_from_slice(oid_slc); |
107 | | |
108 | 0 | let mut public_ptr: *mut SECKEYPublicKey = null_mut(); |
109 | 0 | let mut param_item = SECItemBorrowed::wrap(¶ms)?; |
110 | | |
111 | | // If we have tracing on, try to ensure that key data can be read. |
112 | 0 | let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) { |
113 | | unsafe { |
114 | 0 | p11::PK11_GenerateKeyPairWithOpFlags( |
115 | 0 | *slot, |
116 | 0 | p11::CK_MECHANISM_TYPE::from(CKM_EC_KEY_PAIR_GEN), |
117 | 0 | addr_of_mut!(param_item).cast(), |
118 | 0 | &raw mut public_ptr, |
119 | 0 | p11::PK11_ATTR_SESSION | p11::PK11_ATTR_INSENSITIVE | p11::PK11_ATTR_PUBLIC, |
120 | 0 | p11::CK_FLAGS::from(CKF_DERIVE), |
121 | 0 | p11::CK_FLAGS::from(CKF_DERIVE), |
122 | 0 | null_mut(), |
123 | | ) |
124 | | } |
125 | | } else { |
126 | 0 | null_mut() |
127 | | }; |
128 | 0 | assert_eq!(insensitive_secret_ptr.is_null(), public_ptr.is_null()); |
129 | 0 | let secret_ptr = if insensitive_secret_ptr.is_null() { |
130 | | unsafe { |
131 | 0 | p11::PK11_GenerateKeyPairWithOpFlags( |
132 | 0 | *slot, |
133 | 0 | p11::CK_MECHANISM_TYPE::from(CKM_EC_KEY_PAIR_GEN), |
134 | 0 | addr_of_mut!(param_item).cast(), |
135 | 0 | &raw mut public_ptr, |
136 | 0 | p11::PK11_ATTR_SESSION | p11::PK11_ATTR_SENSITIVE | p11::PK11_ATTR_PRIVATE, |
137 | 0 | p11::CK_FLAGS::from(CKF_DERIVE), |
138 | 0 | p11::CK_FLAGS::from(CKF_DERIVE), |
139 | 0 | null_mut(), |
140 | | ) |
141 | | } |
142 | | } else { |
143 | 0 | insensitive_secret_ptr |
144 | | }; |
145 | 0 | assert_eq!(secret_ptr.is_null(), public_ptr.is_null()); |
146 | 0 | let sk = PrivateKey::from_ptr(secret_ptr)?; |
147 | 0 | let pk = PublicKey::from_ptr(public_ptr)?; |
148 | 0 | trace!("Generated key pair: sk={sk:?} pk={pk:?}"); |
149 | 0 | Ok((sk, pk)) |
150 | 0 | } |
151 | | |
152 | | /// Encode a configuration for encrypted client hello (ECH). |
153 | | /// |
154 | | /// # Errors |
155 | | /// |
156 | | /// When NSS fails to generate a valid configuration encoding (i.e., unlikely). |
157 | 0 | pub fn encode_config(config: u8, public_name: &str, pk: &PublicKey) -> Res<Vec<u8>> { |
158 | | // A sensible fixed value for the maximum length of a name. |
159 | | const MAX_NAME_LEN: c_uint = 64; |
160 | | // Enable a selection of suites. |
161 | | // NSS supports SHA-512 as well, which could be added here. |
162 | | const SUITES: &[SymmetricSuite] = &[ |
163 | | SymmetricSuite { |
164 | | kdfId: KdfId::HpkeKdfHkdfSha256, |
165 | | aeadId: AeadId::HpkeAeadAes128Gcm, |
166 | | }, |
167 | | SymmetricSuite { |
168 | | kdfId: KdfId::HpkeKdfHkdfSha256, |
169 | | aeadId: AeadId::HpkeAeadChaCha20Poly1305, |
170 | | }, |
171 | | SymmetricSuite { |
172 | | kdfId: KdfId::HpkeKdfHkdfSha384, |
173 | | aeadId: AeadId::HpkeAeadAes128Gcm, |
174 | | }, |
175 | | SymmetricSuite { |
176 | | kdfId: KdfId::HpkeKdfHkdfSha384, |
177 | | aeadId: AeadId::HpkeAeadChaCha20Poly1305, |
178 | | }, |
179 | | ]; |
180 | | |
181 | 0 | let name = CString::new(public_name)?; |
182 | 0 | let mut encoded = [0; 1024]; |
183 | 0 | let mut encoded_len = 0; |
184 | | unsafe { |
185 | 0 | SSL_EncodeEchConfigId( |
186 | 0 | config, |
187 | 0 | name.as_ptr(), |
188 | | MAX_NAME_LEN, |
189 | | KemId::HpkeDhKemX25519Sha256, |
190 | 0 | **pk, |
191 | 0 | SUITES.as_ptr(), |
192 | 0 | c_uint::try_from(SUITES.len())?, |
193 | 0 | encoded.as_mut_ptr(), |
194 | 0 | &raw mut encoded_len, |
195 | 0 | c_uint::try_from(encoded.len())?, |
196 | 0 | )?; |
197 | | } |
198 | 0 | Ok(Vec::from(&encoded[..usize::try_from(encoded_len)?])) |
199 | 0 | } |