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