diff options
Diffstat (limited to 'third_party/rust/neqo-crypto/src')
-rw-r--r-- | third_party/rust/neqo-crypto/src/aead.rs | 1 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/agent.rs | 12 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/agentio.rs | 9 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/cert.rs | 18 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/ech.rs | 10 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/ext.rs | 4 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/hkdf.rs | 23 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/hp.rs | 8 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/lib.rs | 132 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/once.rs | 44 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/p11.rs | 114 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/replay.rs | 1 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/selfencrypt.rs | 2 | ||||
-rw-r--r-- | third_party/rust/neqo-crypto/src/time.rs | 74 |
14 files changed, 244 insertions, 208 deletions
diff --git a/third_party/rust/neqo-crypto/src/aead.rs b/third_party/rust/neqo-crypto/src/aead.rs index a2f009a403..bf7d7fe9d7 100644 --- a/third_party/rust/neqo-crypto/src/aead.rs +++ b/third_party/rust/neqo-crypto/src/aead.rs @@ -5,7 +5,6 @@ // except according to those terms. use std::{ - convert::{TryFrom, TryInto}, fmt, ops::{Deref, DerefMut}, os::raw::{c_char, c_uint}, diff --git a/third_party/rust/neqo-crypto/src/agent.rs b/third_party/rust/neqo-crypto/src/agent.rs index cd0bb4cb12..82a6dacd48 100644 --- a/third_party/rust/neqo-crypto/src/agent.rs +++ b/third_party/rust/neqo-crypto/src/agent.rs @@ -6,7 +6,6 @@ use std::{ cell::RefCell, - convert::TryFrom, ffi::{CStr, CString}, mem::{self, MaybeUninit}, ops::{Deref, DerefMut}, @@ -33,6 +32,7 @@ use crate::{ ech, err::{is_blocked, secstatus_to_res, Error, PRErrorCode, Res}, ext::{ExtensionHandler, ExtensionTracker}, + null_safe_slice, p11::{self, PrivateKey, PublicKey}, prio, replay::AntiReplay, @@ -897,7 +897,7 @@ impl Client { let resumption = arg.cast::<Vec<ResumptionToken>>().as_mut().unwrap(); let len = usize::try_from(len).unwrap(); let mut v = Vec::with_capacity(len); - v.extend_from_slice(std::slice::from_raw_parts(token, len)); + v.extend_from_slice(null_safe_slice(token, len)); qinfo!( [format!("{fd:p}")], "Got resumption token {}", @@ -1015,7 +1015,7 @@ pub enum ZeroRttCheckResult { Accept, /// Reject 0-RTT, but continue the handshake normally. Reject, - /// Send HelloRetryRequest (probably not needed for QUIC). + /// Send `HelloRetryRequest` (probably not needed for QUIC). HelloRetryRequest(Vec<u8>), /// Fail the handshake. Fail, @@ -1105,11 +1105,7 @@ impl Server { } let check_state = arg.cast::<ZeroRttCheckState>().as_mut().unwrap(); - let token = if client_token.is_null() { - &[] - } else { - std::slice::from_raw_parts(client_token, usize::try_from(client_token_len).unwrap()) - }; + let token = null_safe_slice(client_token, usize::try_from(client_token_len).unwrap()); match check_state.checker.check(token) { ZeroRttCheckResult::Accept => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_accept, ZeroRttCheckResult::Fail => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_fail, diff --git a/third_party/rust/neqo-crypto/src/agentio.rs b/third_party/rust/neqo-crypto/src/agentio.rs index 2bcc540530..7c57a0ef45 100644 --- a/third_party/rust/neqo-crypto/src/agentio.rs +++ b/third_party/rust/neqo-crypto/src/agentio.rs @@ -6,13 +6,11 @@ use std::{ cmp::min, - convert::{TryFrom, TryInto}, fmt, mem, ops::Deref, os::raw::{c_uint, c_void}, pin::Pin, ptr::{null, null_mut}, - vec::Vec, }; use neqo_common::{hex, hex_with_len, qtrace}; @@ -20,7 +18,7 @@ use neqo_common::{hex, hex_with_len, qtrace}; use crate::{ constants::{ContentType, Epoch}, err::{nspr, Error, PR_SetError, Res}, - prio, ssl, + null_safe_slice, prio, ssl, }; // Alias common types. @@ -100,7 +98,7 @@ impl RecordList { ) -> ssl::SECStatus { let records = arg.cast::<Self>().as_mut().unwrap(); - let slice = std::slice::from_raw_parts(data, len as usize); + let slice = null_safe_slice(data, len); records.append(epoch, ContentType::try_from(ct).unwrap(), slice); ssl::SECSuccess } @@ -178,6 +176,7 @@ impl AgentIoInput { return Err(Error::NoDataAvailable); } + #[allow(clippy::disallowed_methods)] // We just checked if this was empty. let src = unsafe { std::slice::from_raw_parts(self.input, amount) }; qtrace!([self], "read {}", hex(src)); let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) }; @@ -232,7 +231,7 @@ impl AgentIo { // Stage output from TLS into the output buffer. fn save_output(&mut self, buf: *const u8, count: usize) { - let slice = unsafe { std::slice::from_raw_parts(buf, count) }; + let slice = unsafe { null_safe_slice(buf, count) }; qtrace!([self], "save output {}", hex(slice)); self.output.extend_from_slice(slice); } diff --git a/third_party/rust/neqo-crypto/src/cert.rs b/third_party/rust/neqo-crypto/src/cert.rs index 64e63ec71a..2836b5237c 100644 --- a/third_party/rust/neqo-crypto/src/cert.rs +++ b/third_party/rust/neqo-crypto/src/cert.rs @@ -4,16 +4,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::{ - convert::TryFrom, - ptr::{addr_of, NonNull}, - slice, -}; +use std::ptr::{addr_of, NonNull}; use neqo_common::qerror; use crate::{ err::secstatus_to_res, + null_safe_slice, p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray}, ssl::{ PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps, @@ -24,7 +21,7 @@ use crate::{ pub struct CertificateInfo { certs: CertList, cursor: *const CERTCertListNode, - /// stapled_ocsp_responses and signed_cert_timestamp are properties + /// `stapled_ocsp_responses` and `signed_cert_timestamp` are properties /// associated with each of the certificates. Right now, NSS only /// reports the value for the end-entity certificate (the first). stapled_ocsp_responses: Option<Vec<Vec<u8>>>, @@ -52,7 +49,7 @@ fn stapled_ocsp_responses(fd: *mut PRFileDesc) -> Option<Vec<Vec<u8>>> { }; for idx in 0..len { let itemp: *const SECItem = unsafe { ocsp_ptr.as_ref().items.offset(idx).cast() }; - let item = unsafe { slice::from_raw_parts((*itemp).data, (*itemp).len as usize) }; + let item = unsafe { null_safe_slice((*itemp).data, (*itemp).len) }; ocsp_helper.push(item.to_owned()); } Some(ocsp_helper) @@ -68,9 +65,8 @@ fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> { if unsafe { sct_ptr.as_ref().len == 0 || sct_ptr.as_ref().data.is_null() } { Some(Vec::new()) } else { - let sct_slice = unsafe { - slice::from_raw_parts(sct_ptr.as_ref().data, sct_ptr.as_ref().len as usize) - }; + let sct_slice = + unsafe { null_safe_slice(sct_ptr.as_ref().data, sct_ptr.as_ref().len) }; Some(sct_slice.to_owned()) } } @@ -105,7 +101,7 @@ impl<'a> Iterator for &'a mut CertificateInfo { let cert = unsafe { *self.cursor }.cert; secstatus_to_res(unsafe { CERT_GetCertificateDer(cert, &mut item) }) .expect("getting DER from certificate should work"); - Some(unsafe { std::slice::from_raw_parts(item.data, item.len as usize) }) + Some(unsafe { null_safe_slice(item.data, item.len) }) } } diff --git a/third_party/rust/neqo-crypto/src/ech.rs b/third_party/rust/neqo-crypto/src/ech.rs index 1f54c4592e..4ff2cda7e8 100644 --- a/third_party/rust/neqo-crypto/src/ech.rs +++ b/third_party/rust/neqo-crypto/src/ech.rs @@ -5,7 +5,6 @@ // except according to those terms. use std::{ - convert::TryFrom, ffi::CString, os::raw::{c_char, c_uint}, ptr::{addr_of_mut, null_mut}, @@ -15,7 +14,7 @@ use neqo_common::qtrace; use crate::{ err::{ssl::SSL_ERROR_ECH_RETRY_WITH_ECH, Error, Res}, - experimental_api, + experimental_api, null_safe_slice, p11::{ self, Item, PrivateKey, PublicKey, SECITEM_FreeItem, SECItem, SECKEYPrivateKey, SECKEYPublicKey, Slot, @@ -76,7 +75,7 @@ pub fn convert_ech_error(fd: *mut PRFileDesc, err: Error) -> Error { return Error::InternalError; } let buf = unsafe { - let slc = std::slice::from_raw_parts(item.data, usize::try_from(item.len).unwrap()); + let slc = null_safe_slice(item.data, item.len); let buf = Vec::from(slc); SECITEM_FreeItem(&mut item, PRBool::from(false)); buf @@ -101,8 +100,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { let oid_data = unsafe { p11::SECOID_FindOIDByTag(p11::SECOidTag::SEC_OID_CURVE25519) }; let oid = unsafe { oid_data.as_ref() }.ok_or(Error::InternalError)?; - let oid_slc = - unsafe { std::slice::from_raw_parts(oid.oid.data, usize::try_from(oid.oid.len).unwrap()) }; + let oid_slc = unsafe { null_safe_slice(oid.oid.data, oid.oid.len) }; let mut params: Vec<u8> = Vec::with_capacity(oid_slc.len() + 2); params.push(u8::try_from(p11::SEC_ASN1_OBJECT_ID).unwrap()); params.push(u8::try_from(oid.oid.len).unwrap()); @@ -113,7 +111,6 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { // If we have tracing on, try to ensure that key data can be read. let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) { - #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. unsafe { p11::PK11_GenerateKeyPairWithOpFlags( *slot, @@ -131,7 +128,6 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { }; assert_eq!(insensitive_secret_ptr.is_null(), public_ptr.is_null()); let secret_ptr = if insensitive_secret_ptr.is_null() { - #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. unsafe { p11::PK11_GenerateKeyPairWithOpFlags( *slot, diff --git a/third_party/rust/neqo-crypto/src/ext.rs b/third_party/rust/neqo-crypto/src/ext.rs index 310e87a1b7..02ee6340c1 100644 --- a/third_party/rust/neqo-crypto/src/ext.rs +++ b/third_party/rust/neqo-crypto/src/ext.rs @@ -6,7 +6,6 @@ use std::{ cell::RefCell, - convert::TryFrom, os::raw::{c_uint, c_void}, pin::Pin, rc::Rc, @@ -16,6 +15,7 @@ use crate::{ agentio::as_c_void, constants::{Extension, HandshakeMessage, TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS}, err::Res, + null_safe_slice, ssl::{ PRBool, PRFileDesc, SECFailure, SECStatus, SECSuccess, SSLAlertDescription, SSLExtensionHandler, SSLExtensionWriter, SSLHandshakeType, @@ -105,7 +105,7 @@ impl ExtensionTracker { alert: *mut SSLAlertDescription, arg: *mut c_void, ) -> SECStatus { - let d = std::slice::from_raw_parts(data, len as usize); + let d = null_safe_slice(data, len); #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] Self::wrap_handler_call(arg, |handler| { // Cast is safe here because the message type is always part of the enum diff --git a/third_party/rust/neqo-crypto/src/hkdf.rs b/third_party/rust/neqo-crypto/src/hkdf.rs index e3cf77418c..3706be6c3b 100644 --- a/third_party/rust/neqo-crypto/src/hkdf.rs +++ b/third_party/rust/neqo-crypto/src/hkdf.rs @@ -5,7 +5,6 @@ // except according to those terms. use std::{ - convert::TryFrom, os::raw::{c_char, c_uint}, ptr::null_mut, }; @@ -17,9 +16,10 @@ use crate::{ }, err::{Error, Res}, p11::{ - random, Item, PK11Origin, PK11SymKey, PK11_ImportDataKey, Slot, SymKey, CKA_DERIVE, + Item, PK11Origin, PK11SymKey, PK11_ImportDataKey, Slot, SymKey, CKA_DERIVE, CKM_HKDF_DERIVE, CK_ATTRIBUTE_TYPE, CK_MECHANISM_TYPE, }, + random, }; experimental_api!(SSL_HkdfExtract( @@ -40,24 +40,32 @@ experimental_api!(SSL_HkdfExpandLabel( secret: *mut *mut PK11SymKey, )); -fn key_size(version: Version, cipher: Cipher) -> Res<usize> { +const MAX_KEY_SIZE: usize = 48; +const fn key_size(version: Version, cipher: Cipher) -> Res<usize> { if version != TLS_VERSION_1_3 { return Err(Error::UnsupportedVersion); } - Ok(match cipher { + let size = match cipher { TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 => 32, TLS_AES_256_GCM_SHA384 => 48, _ => return Err(Error::UnsupportedCipher), - }) + }; + debug_assert!(size <= MAX_KEY_SIZE); + Ok(size) } /// Generate a random key of the right size for the given suite. /// /// # Errors /// -/// Only if NSS fails. +/// If the ciphersuite or protocol version is not supported. pub fn generate_key(version: Version, cipher: Cipher) -> Res<SymKey> { - import_key(version, &random(key_size(version, cipher)?)) + // With generic_const_expr, this becomes: + // import_key(version, &random::<{ key_size(version, cipher) }>()) + import_key( + version, + &random::<MAX_KEY_SIZE>()[0..key_size(version, cipher)?], + ) } /// Import a symmetric key for use with HKDF. @@ -70,7 +78,6 @@ pub fn import_key(version: Version, buf: &[u8]) -> Res<SymKey> { return Err(Error::UnsupportedVersion); } let slot = Slot::internal()?; - #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. let key_ptr = unsafe { PK11_ImportDataKey( *slot, diff --git a/third_party/rust/neqo-crypto/src/hp.rs b/third_party/rust/neqo-crypto/src/hp.rs index 2479eff8f5..1eba6a9cb5 100644 --- a/third_party/rust/neqo-crypto/src/hp.rs +++ b/third_party/rust/neqo-crypto/src/hp.rs @@ -6,7 +6,6 @@ use std::{ cell::RefCell, - convert::TryFrom, fmt::{self, Debug}, os::raw::{c_char, c_int, c_uint}, ptr::{addr_of_mut, null, null_mut}, @@ -46,7 +45,7 @@ pub enum HpKey { /// track references using `Rc`. `PK11Context` can't be used with `PK11_CloneContext` /// as that is not supported for these contexts. Aes(Rc<RefCell<Context>>), - /// The ChaCha20 mask has to invoke a new PK11_Encrypt every time as it needs to + /// The `ChaCha20` mask has to invoke a new `PK11_Encrypt` every time as it needs to /// change the counter and nonce on each invocation. Chacha(SymKey), } @@ -76,7 +75,6 @@ impl HpKey { let l = label.as_bytes(); let mut secret: *mut PK11SymKey = null_mut(); - #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. let (mech, key_size) = match cipher { TLS_AES_128_GCM_SHA256 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 16), TLS_AES_256_GCM_SHA384 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 32), @@ -104,8 +102,6 @@ impl HpKey { let res = match cipher { TLS_AES_128_GCM_SHA256 | TLS_AES_256_GCM_SHA384 => { - // TODO: Remove when we bump the MSRV to 1.74.0. - #[allow(clippy::useless_conversion)] let context_ptr = unsafe { PK11_CreateContextBySymKey( mech, @@ -181,8 +177,6 @@ impl HpKey { }; let mut output_len: c_uint = 0; let mut param_item = Item::wrap_struct(¶ms); - // TODO: Remove when we bump the MSRV to 1.74.0. - #[allow(clippy::useless_conversion)] secstatus_to_res(unsafe { PK11_Encrypt( **key, diff --git a/third_party/rust/neqo-crypto/src/lib.rs b/third_party/rust/neqo-crypto/src/lib.rs index 05424ee1f3..2ec1b4a3ea 100644 --- a/third_party/rust/neqo-crypto/src/lib.rs +++ b/third_party/rust/neqo-crypto/src/lib.rs @@ -4,13 +4,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![cfg_attr(feature = "deny-warnings", deny(warnings))] -#![warn(clippy::pedantic)] -// Bindgen auto generated code -// won't adhere to the clippy rules below -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::unseparated_literal_suffix)] -#![allow(clippy::used_underscore_binding)] +#![allow(clippy::module_name_repetitions)] // This lint doesn't work here. +#![allow(clippy::unseparated_literal_suffix, clippy::used_underscore_binding)] // For bindgen code. mod aead; #[cfg(feature = "fuzzing")] @@ -27,7 +22,6 @@ mod exp; pub mod ext; pub mod hkdf; pub mod hp; -mod once; #[macro_use] mod p11; mod prio; @@ -37,11 +31,7 @@ pub mod selfencrypt; mod ssl; mod time; -use std::{ - ffi::CString, - path::{Path, PathBuf}, - ptr::null, -}; +use std::{ffi::CString, path::PathBuf, ptr::null, sync::OnceLock}; #[cfg(not(feature = "fuzzing"))] pub use self::aead::RealAead as Aead; @@ -49,7 +39,6 @@ pub use self::aead::RealAead as Aead; pub use self::aead::RealAead; #[cfg(feature = "fuzzing")] pub use self::aead_fuzzing::FuzzingAead as Aead; -use self::once::OnceResult; pub use self::{ agent::{ Agent, AllowZeroRtt, Client, HandshakeState, Record, RecordList, ResumptionToken, @@ -64,7 +53,7 @@ pub use self::{ }, err::{Error, PRErrorCode, Res}, ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult}, - p11::{random, PrivateKey, PublicKey, SymKey}, + p11::{random, randomize, PrivateKey, PublicKey, SymKey}, replay::AntiReplay, secrets::SecretDirection, ssl::Opt, @@ -87,7 +76,7 @@ fn secstatus_to_res(code: nss::SECStatus) -> Res<()> { enum NssLoaded { External, NoDb, - Db(Box<Path>), + Db, } impl Drop for NssLoaded { @@ -100,7 +89,7 @@ impl Drop for NssLoaded { } } -static mut INITIALIZED: OnceResult<NssLoaded> = OnceResult::new(); +static INITIALIZED: OnceLock<NssLoaded> = OnceLock::new(); fn already_initialized() -> bool { unsafe { nss::NSS_IsInitialized() != 0 } @@ -124,19 +113,18 @@ fn version_check() { pub fn init() { // Set time zero. time::init(); - unsafe { - INITIALIZED.call_once(|| { - version_check(); - if already_initialized() { - return NssLoaded::External; - } + _ = INITIALIZED.get_or_init(|| { + version_check(); + if already_initialized() { + return NssLoaded::External; + } - secstatus_to_res(nss::NSS_NoDB_Init(null())).expect("NSS_NoDB_Init failed"); - secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed"); + secstatus_to_res(unsafe { nss::NSS_NoDB_Init(null()) }).expect("NSS_NoDB_Init failed"); + secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() }) + .expect("NSS_SetDomesticPolicy failed"); - NssLoaded::NoDb - }); - } + NssLoaded::NoDb + }); } /// This enables SSLTRACE by calling a simple, harmless function to trigger its @@ -158,51 +146,71 @@ fn enable_ssl_trace() { /// If NSS cannot be initialized. pub fn init_db<P: Into<PathBuf>>(dir: P) { time::init(); - unsafe { - INITIALIZED.call_once(|| { - version_check(); - if already_initialized() { - return NssLoaded::External; - } + _ = INITIALIZED.get_or_init(|| { + version_check(); + if already_initialized() { + return NssLoaded::External; + } - let path = dir.into(); - assert!(path.is_dir()); - let pathstr = path.to_str().expect("path converts to string").to_string(); - let dircstr = CString::new(pathstr).unwrap(); - let empty = CString::new("").unwrap(); - secstatus_to_res(nss::NSS_Initialize( + let path = dir.into(); + assert!(path.is_dir()); + let pathstr = path.to_str().expect("path converts to string").to_string(); + let dircstr = CString::new(pathstr).unwrap(); + let empty = CString::new("").unwrap(); + secstatus_to_res(unsafe { + nss::NSS_Initialize( dircstr.as_ptr(), empty.as_ptr(), empty.as_ptr(), nss::SECMOD_DB.as_ptr().cast(), nss::NSS_INIT_READONLY, - )) - .expect("NSS_Initialize failed"); - - secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed"); - secstatus_to_res(ssl::SSL_ConfigServerSessionIDCache( - 1024, - 0, - 0, - dircstr.as_ptr(), - )) - .expect("SSL_ConfigServerSessionIDCache failed"); - - #[cfg(debug_assertions)] - enable_ssl_trace(); - - NssLoaded::Db(path.into_boxed_path()) - }); - } + ) + }) + .expect("NSS_Initialize failed"); + + secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() }) + .expect("NSS_SetDomesticPolicy failed"); + secstatus_to_res(unsafe { + ssl::SSL_ConfigServerSessionIDCache(1024, 0, 0, dircstr.as_ptr()) + }) + .expect("SSL_ConfigServerSessionIDCache failed"); + + #[cfg(debug_assertions)] + enable_ssl_trace(); + + NssLoaded::Db + }); } /// # Panics /// /// If NSS isn't initialized. pub fn assert_initialized() { - unsafe { - INITIALIZED.call_once(|| { - panic!("NSS not initialized with init or init_db"); - }); + INITIALIZED + .get() + .expect("NSS not initialized with init or init_db"); +} + +/// NSS tends to return empty "slices" with a null pointer, which will cause +/// `std::slice::from_raw_parts` to panic if passed directly. This wrapper avoids +/// that issue. It also performs conversion for lengths, as a convenience. +/// +/// # Panics +/// If the provided length doesn't fit into a `usize`. +/// +/// # Safety +/// The caller must adhere to the safety constraints of `std::slice::from_raw_parts`, +/// except that this will accept a null value for `data`. +unsafe fn null_safe_slice<'a, T>(data: *const u8, len: T) -> &'a [u8] +where + usize: TryFrom<T>, +{ + if data.is_null() { + &[] + } else if let Ok(len) = usize::try_from(len) { + #[allow(clippy::disallowed_methods)] + std::slice::from_raw_parts(data, len) + } else { + panic!("null_safe_slice: size overflow"); } } diff --git a/third_party/rust/neqo-crypto/src/once.rs b/third_party/rust/neqo-crypto/src/once.rs deleted file mode 100644 index 80657cfe26..0000000000 --- a/third_party/rust/neqo-crypto/src/once.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::sync::Once; - -#[allow(clippy::module_name_repetitions)] -pub struct OnceResult<T> { - once: Once, - v: Option<T>, -} - -impl<T> OnceResult<T> { - #[must_use] - pub const fn new() -> Self { - Self { - once: Once::new(), - v: None, - } - } - - pub fn call_once<F: FnOnce() -> T>(&mut self, f: F) -> &T { - let v = &mut self.v; - self.once.call_once(|| { - *v = Some(f()); - }); - self.v.as_ref().unwrap() - } -} - -#[cfg(test)] -mod test { - use super::OnceResult; - - static mut STATIC_ONCE_RESULT: OnceResult<u64> = OnceResult::new(); - - #[test] - fn static_update() { - assert_eq!(*unsafe { STATIC_ONCE_RESULT.call_once(|| 23) }, 23); - assert_eq!(*unsafe { STATIC_ONCE_RESULT.call_once(|| 24) }, 23); - } -} diff --git a/third_party/rust/neqo-crypto/src/p11.rs b/third_party/rust/neqo-crypto/src/p11.rs index 508d240062..5552882e2e 100644 --- a/third_party/rust/neqo-crypto/src/p11.rs +++ b/third_party/rust/neqo-crypto/src/p11.rs @@ -10,7 +10,7 @@ #![allow(non_snake_case)] use std::{ - convert::TryFrom, + cell::RefCell, mem, ops::{Deref, DerefMut}, os::raw::{c_int, c_uint}, @@ -19,7 +19,10 @@ use std::{ use neqo_common::hex_with_len; -use crate::err::{secstatus_to_res, Error, Res}; +use crate::{ + err::{secstatus_to_res, Error, Res}, + null_safe_slice, +}; #[allow(clippy::upper_case_acronyms)] #[allow(clippy::unreadable_literal)] @@ -139,7 +142,6 @@ impl PrivateKey { /// When the values are too large to fit. So never. pub fn key_data(&self) -> Res<Vec<u8>> { let mut key_item = Item::make_empty(); - #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. secstatus_to_res(unsafe { PK11_ReadRawAttribute( PK11ObjectType::PK11_TypePrivKey, @@ -148,9 +150,7 @@ impl PrivateKey { &mut key_item, ) })?; - let slc = unsafe { - std::slice::from_raw_parts(key_item.data, usize::try_from(key_item.len).unwrap()) - }; + let slc = unsafe { null_safe_slice(key_item.data, key_item.len) }; let key = Vec::from(slc); // The data that `key_item` refers to needs to be freed, but we can't // use the scoped `Item` implementation. This is OK as long as nothing @@ -206,7 +206,7 @@ impl SymKey { // This is accessing a value attached to the key, so we can treat this as a borrow. match unsafe { key_item.as_mut() } { None => Err(Error::InternalError), - Some(key) => Ok(unsafe { std::slice::from_raw_parts(key.data, key.len as usize) }), + Some(key) => Ok(unsafe { null_safe_slice(key.data, key.len) }), } } } @@ -285,36 +285,112 @@ impl Item { let b = self.ptr.as_ref().unwrap(); // Sanity check the type, as some types don't count bytes in `Item::len`. assert_eq!(b.type_, SECItemType::siBuffer); - let slc = std::slice::from_raw_parts(b.data, usize::try_from(b.len).unwrap()); + let slc = null_safe_slice(b.data, b.len); Vec::from(slc) } } -/// Generate a randomized buffer. +/// Fill a buffer with randomness. /// /// # Panics /// /// When `size` is too large or NSS fails. -#[must_use] -pub fn random(size: usize) -> Vec<u8> { - let mut buf = vec![0; size]; - secstatus_to_res(unsafe { - PK11_GenerateRandom(buf.as_mut_ptr(), c_int::try_from(buf.len()).unwrap()) - }) - .unwrap(); +pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B { + let m_buf = buf.as_mut(); + let len = c_int::try_from(m_buf.len()).unwrap(); + secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).unwrap(); buf } +struct RandomCache { + cache: [u8; Self::SIZE], + used: usize, +} + +impl RandomCache { + const SIZE: usize = 256; + const CUTOFF: usize = 32; + + fn new() -> Self { + RandomCache { + cache: [0; Self::SIZE], + used: Self::SIZE, + } + } + + fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B { + let m_buf = buf.as_mut(); + debug_assert!(m_buf.len() <= Self::CUTOFF); + let avail = Self::SIZE - self.used; + if m_buf.len() <= avail { + m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]); + self.used += m_buf.len(); + } else { + if avail > 0 { + m_buf[..avail].copy_from_slice(&self.cache[self.used..]); + } + randomize(&mut self.cache[..]); + self.used = m_buf.len() - avail; + m_buf[avail..].copy_from_slice(&self.cache[..self.used]); + } + buf + } +} + +/// Generate a randomized array. +/// +/// # Panics +/// +/// When `size` is too large or NSS fails. +#[must_use] +pub fn random<const N: usize>() -> [u8; N] { + thread_local!(static CACHE: RefCell<RandomCache> = RefCell::new(RandomCache::new())); + + let buf = [0; N]; + if N <= RandomCache::CUTOFF { + CACHE.with_borrow_mut(|c| c.randomize(buf)) + } else { + randomize(buf) + } +} + #[cfg(test)] mod test { use test_fixture::fixture_init; - use super::random; + use super::RandomCache; + use crate::{random, randomize}; #[test] fn randomness() { fixture_init(); - // If this ever fails, there is either a bug, or it's time to buy a lottery ticket. - assert_ne!(random(16), random(16)); + // If any of these ever fail, there is either a bug, or it's time to buy a lottery ticket. + assert_ne!(random::<16>(), randomize([0; 16])); + assert_ne!([0; 16], random::<16>()); + assert_ne!([0; 64], random::<64>()); + } + + #[test] + fn cache_random_lengths() { + const ZERO: [u8; 256] = [0; 256]; + + fixture_init(); + let mut cache = RandomCache::new(); + let mut buf = [0; 256]; + let bits = usize::BITS - (RandomCache::CUTOFF - 1).leading_zeros(); + let mask = 0xff >> (u8::BITS - bits); + + for _ in 0..100 { + let len = loop { + let len = usize::from(random::<1>()[0] & mask) + 1; + if len <= RandomCache::CUTOFF { + break len; + } + }; + buf.fill(0); + if len >= 16 { + assert_ne!(&cache.randomize(&mut buf[..len])[..len], &ZERO[..len]); + } + } } } diff --git a/third_party/rust/neqo-crypto/src/replay.rs b/third_party/rust/neqo-crypto/src/replay.rs index d4d3677f5c..5fd6fd1250 100644 --- a/third_party/rust/neqo-crypto/src/replay.rs +++ b/third_party/rust/neqo-crypto/src/replay.rs @@ -5,7 +5,6 @@ // except according to those terms. use std::{ - convert::{TryFrom, TryInto}, ops::{Deref, DerefMut}, os::raw::c_uint, ptr::null_mut, diff --git a/third_party/rust/neqo-crypto/src/selfencrypt.rs b/third_party/rust/neqo-crypto/src/selfencrypt.rs index b8a63153fd..1130c35250 100644 --- a/third_party/rust/neqo-crypto/src/selfencrypt.rs +++ b/third_party/rust/neqo-crypto/src/selfencrypt.rs @@ -82,7 +82,7 @@ impl SelfEncrypt { // opaque aead_encrypted(plaintext)[length as expanded]; // }; // AAD covers the entire header, plus the value of the AAD parameter that is provided. - let salt = random(Self::SALT_LENGTH); + let salt = random::<{ Self::SALT_LENGTH }>(); let cipher = self.make_aead(&self.key, &salt)?; let encoded_len = 2 + salt.len() + plaintext.len() + cipher.expansion(); diff --git a/third_party/rust/neqo-crypto/src/time.rs b/third_party/rust/neqo-crypto/src/time.rs index 84dbfdb4a5..0e59c4f5e2 100644 --- a/third_party/rust/neqo-crypto/src/time.rs +++ b/third_party/rust/neqo-crypto/src/time.rs @@ -7,18 +7,16 @@ #![allow(clippy::upper_case_acronyms)] use std::{ - boxed::Box, - convert::{TryFrom, TryInto}, ops::Deref, os::raw::c_void, pin::Pin, + sync::OnceLock, time::{Duration, Instant}, }; use crate::{ agentio::as_c_void, err::{Error, Res}, - once::OnceResult, ssl::{PRFileDesc, SSLTimeFunc}, }; @@ -67,14 +65,13 @@ impl TimeZero { } } -static mut BASE_TIME: OnceResult<TimeZero> = OnceResult::new(); +static BASE_TIME: OnceLock<TimeZero> = OnceLock::new(); fn get_base() -> &'static TimeZero { - let f = || TimeZero { + BASE_TIME.get_or_init(|| TimeZero { instant: Instant::now(), prtime: unsafe { PR_Now() }, - }; - unsafe { BASE_TIME.call_once(f) } + }) } pub(crate) fn init() { @@ -97,9 +94,8 @@ impl Deref for Time { impl From<Instant> for Time { /// Convert from an Instant into a Time. fn from(t: Instant) -> Self { - // Call `TimeZero::baseline(t)` so that time zero can be set. - let f = || TimeZero::baseline(t); - _ = unsafe { BASE_TIME.call_once(f) }; + // Initialize `BASE_TIME` using `TimeZero::baseline(t)`. + BASE_TIME.get_or_init(|| TimeZero::baseline(t)); Self { t } } } @@ -108,14 +104,17 @@ impl TryFrom<PRTime> for Time { type Error = Error; fn try_from(prtime: PRTime) -> Res<Self> { let base = get_base(); - if let Some(delta) = prtime.checked_sub(base.prtime) { - let d = Duration::from_micros(delta.try_into()?); - base.instant - .checked_add(d) - .map_or(Err(Error::TimeTravelError), |t| Ok(Self { t })) + let delta = prtime + .checked_sub(base.prtime) + .ok_or(Error::TimeTravelError)?; + let d = Duration::from_micros(u64::try_from(delta.abs())?); + let t = if delta >= 0 { + base.instant.checked_add(d) } else { - Err(Error::TimeTravelError) - } + base.instant.checked_sub(d) + }; + let t = t.ok_or(Error::TimeTravelError)?; + Ok(Self { t }) } } @@ -123,14 +122,21 @@ impl TryInto<PRTime> for Time { type Error = Error; fn try_into(self) -> Res<PRTime> { let base = get_base(); - let delta = self - .t - .checked_duration_since(base.instant) - .ok_or(Error::TimeTravelError)?; - if let Ok(d) = PRTime::try_from(delta.as_micros()) { - d.checked_add(base.prtime).ok_or(Error::TimeTravelError) + + if let Some(delta) = self.t.checked_duration_since(base.instant) { + if let Ok(d) = PRTime::try_from(delta.as_micros()) { + d.checked_add(base.prtime).ok_or(Error::TimeTravelError) + } else { + Err(Error::TimeTravelError) + } } else { - Err(Error::TimeTravelError) + // Try to go backwards from the base time. + let backwards = base.instant - self.t; // infallible + if let Ok(d) = PRTime::try_from(backwards.as_micros()) { + base.prtime.checked_sub(d).ok_or(Error::TimeTravelError) + } else { + Err(Error::TimeTravelError) + } } } } @@ -207,10 +213,7 @@ impl Default for TimeHolder { #[cfg(test)] mod test { - use std::{ - convert::{TryFrom, TryInto}, - time::{Duration, Instant}, - }; + use std::time::{Duration, Instant}; use super::{get_base, init, Interval, PRTime, Time}; use crate::err::Res; @@ -228,16 +231,23 @@ mod test { } #[test] - fn past_time() { + fn past_prtime() { + const DELTA: Duration = Duration::from_secs(1); init(); let base = get_base(); - assert!(Time::try_from(base.prtime - 1).is_err()); + let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap(); + println!("{} - {}", base.prtime, delta_micros); + let t = Time::try_from(base.prtime - delta_micros).unwrap(); + assert_eq!(Instant::from(t) + DELTA, base.instant); } #[test] - fn negative_time() { + fn past_instant() { + const DELTA: Duration = Duration::from_secs(1); init(); - assert!(Time::try_from(-1).is_err()); + let base = get_base(); + let t = Time::from(base.instant.checked_sub(DELTA).unwrap()); + assert_eq!(Instant::from(t) + DELTA, base.instant); } #[test] |