From 99f40a231704b7df7969411052a4e4e13b331ad1 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 9 Sep 2024 15:42:37 -0400 Subject: crypto_provider: expose a way to get CSRNG data This commit adds a `rustls_crypto_provider_random()` fn for filling a buffer with cryptographically secure random data using a specific `rustls_crypto_provider`, and `rustls_default_crypto_provider_random()` for doing the same with the process-wide default. --- CHANGELOG.md | 5 ++- src/crypto_provider.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++- src/error.rs | 4 +++ src/rustls.h | 25 +++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8a713f..8885431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.14.0 (2024-08-01) +## 0.14.0-rc1 (2024-09-09) This release updates to [Rustls 0.23.12][] and changes the rustls-ffi API to allow choosing a cryptography provider to use with Rustls. @@ -30,6 +30,9 @@ requirements. * Ciphersuites supported by the current process-wide default crypto provider (if any) can be retrieved with `rustls_default_crypto_provider_ciphersuites_len()` and `rustls_default_crypto_provider_ciphersuites_get()`. + * A buffer can be filled with cryptographically secure random data from + a specific `rustls_crypto_provider` using `rustls_crypto_provider_random()`, + or the process-wide default provider using `rustls_default_crypto_provider_random()`. * A new `RUSTLS_RESULT_NO_DEFAULT_CRYPTO_PROVIDER` `rustls_result` was added to indicate when an operation that requires a process-wide default crypto diff --git a/src/crypto_provider.rs b/src/crypto_provider.rs index e61e36d..fc99986 100644 --- a/src/crypto_provider.rs +++ b/src/crypto_provider.rs @@ -16,7 +16,8 @@ use crate::error::map_error; use crate::{ arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, rustls_result, set_arc_mut_ptr, set_boxed_mut_ptr, to_boxed_mut_ptr, try_clone_arc, try_mut_from_ptr, - try_mut_from_ptr_ptr, try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, try_take, + try_mut_from_ptr_ptr, try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, try_slice_mut, + try_take, }; box_castable! { @@ -338,6 +339,29 @@ pub extern "C" fn rustls_crypto_provider_load_key( } } +/// Write `len` bytes of cryptographically secure random data to `buff` using the crypto provider. +/// +/// `buff` must point to a buffer of at least `len` bytes. The caller maintains ownership +/// of the buffer. +/// +/// Returns `RUSTLS_RESULT_OK` on success, or `RUSTLS_RESULT_GET_RANDOM_FAILED` on failure. +#[no_mangle] +pub extern "C" fn rustls_crypto_provider_random( + provider: *const rustls_crypto_provider, + buff: *mut u8, + len: size_t, +) -> rustls_result { + ffi_panic_boundary! { + match try_clone_arc!(provider) + .secure_random + .fill(try_slice_mut!(buff, len)) + { + Ok(_) => rustls_result::Ok, + Err(_) => rustls_result::GetRandomFailed, + } + } +} + /// Frees the `rustls_crypto_provider`. /// /// Calling with `NULL` is fine. @@ -388,6 +412,30 @@ pub extern "C" fn rustls_default_crypto_provider_ciphersuites_get( } } +/// Write `len` bytes of cryptographically secure random data to `buff` using the process-wide +/// default crypto provider. +/// +/// `buff` must point to a buffer of at least `len` bytes. The caller maintains ownership +/// of the buffer. +/// +/// Returns `RUSTLS_RESULT_OK` on success, and one of `RUSTLS_RESULT_NO_DEFAULT_CRYPTO_PROVIDER` +/// or `RUSTLS_RESULT_GET_RANDOM_FAILED` on failure. +#[no_mangle] +pub extern "C" fn rustls_default_crypto_provider_random( + buff: *mut u8, + len: size_t, +) -> rustls_result { + ffi_panic_boundary! { + match get_default_or_install_from_crate_features() { + Some(provider) => match provider.secure_random.fill(try_slice_mut!(buff, len)) { + Ok(_) => rustls_result::Ok, + Err(_) => rustls_result::GetRandomFailed, + }, + None => rustls_result::NoDefaultCryptoProvider, + } + } +} + box_castable! { /// A signing key that can be used to construct a certified key. // NOTE: we box cast an arc over the dyn trait per the pattern described @@ -445,3 +493,49 @@ fn provider_from_crate_features() -> Option { #[allow(unreachable_code)] None } + +#[cfg(all(test, not(miri)))] +mod tests { + use std::ptr; + + use super::*; + use rustls_result; + + /// Simple smoketest of CSRNG fill with specific provider. + #[test] + fn random_data() { + let provider = rustls_crypto_provider_default(); + assert!(!provider.is_null()); + + // NULL buffer should return an error. + let result = rustls_crypto_provider_random(provider, ptr::null_mut(), 1337); + assert_eq!(result, rustls_result::NullParameter); + + let mut buff = vec![0; 32]; + + // NULL provider should return an error and not touch buff. + let result = rustls_crypto_provider_random(ptr::null(), buff.as_mut_ptr(), buff.len()); + assert_eq!(buff, vec![0; 32]); + assert_eq!(result, rustls_result::NullParameter); + + // Proper parameters should return OK and overwrite the buffer. + let result = rustls_crypto_provider_random(provider, buff.as_mut_ptr(), buff.len()); + assert_eq!(result, rustls_result::Ok); + assert_ne!(buff, vec![0; 32]); + } + + /// Simple smoketest of CSRNG fill with default provider. + #[test] + fn default_random_data() { + // NULL buffer should return an error. + let result = rustls_default_crypto_provider_random(ptr::null_mut(), 1337); + assert_eq!(result, rustls_result::NullParameter); + + let mut buff = vec![0; 32]; + + // Proper parameters should return OK and overwrite the buffer. + let result = rustls_default_crypto_provider_random(buff.as_mut_ptr(), buff.len()); + assert_eq!(result, rustls_result::Ok); + assert_ne!(buff, vec![0; 32]); + } +} diff --git a/src/error.rs b/src/error.rs index 0dbcb92..af9d466 100644 --- a/src/error.rs +++ b/src/error.rs @@ -62,6 +62,7 @@ u32_enum_builder! { CertificateRevocationListParseError => 7014, NoServerCertVerifier => 7015, NoDefaultCryptoProvider => 7016, + GetRandomFailed => 7017, // From https://docs.rs/rustls/latest/rustls/enum.Error.html NoCertificatesPresented => 7101, @@ -495,6 +496,9 @@ impl Display for rustls_result { "no default process-wide crypto provider has been installed" ) } + GetRandomFailed => { + write!(f, "failed to get random bytes from the crypto provider") + } CertEncodingBad => Error::InvalidCertificate(CertificateError::BadEncoding).fmt(f), CertExpired => Error::InvalidCertificate(CertificateError::Expired).fmt(f), diff --git a/src/rustls.h b/src/rustls.h index 711cb4f..207e3ce 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -25,6 +25,7 @@ enum rustls_result { RUSTLS_RESULT_CERTIFICATE_REVOCATION_LIST_PARSE_ERROR = 7014, RUSTLS_RESULT_NO_SERVER_CERT_VERIFIER = 7015, RUSTLS_RESULT_NO_DEFAULT_CRYPTO_PROVIDER = 7016, + RUSTLS_RESULT_GET_RANDOM_FAILED = 7017, RUSTLS_RESULT_NO_CERTIFICATES_PRESENTED = 7101, RUSTLS_RESULT_DECRYPT_ERROR = 7102, RUSTLS_RESULT_FAILED_TO_GET_CURRENT_TIME = 7103, @@ -2053,6 +2054,18 @@ rustls_result rustls_crypto_provider_load_key(const struct rustls_crypto_provide size_t private_key_len, struct rustls_signing_key **signing_key_out); +/** + * Write `len` bytes of cryptographically secure random data to `buff` using the crypto provider. + * + * `buff` must point to a buffer of at least `len` bytes. The caller maintains ownership + * of the buffer. + * + * Returns `RUSTLS_RESULT_OK` on success, or `RUSTLS_RESULT_GET_RANDOM_FAILED` on failure. + */ +rustls_result rustls_crypto_provider_random(const struct rustls_crypto_provider *provider, + uint8_t *buff, + size_t len); + /** * Frees the `rustls_crypto_provider`. * @@ -2082,6 +2095,18 @@ size_t rustls_default_crypto_provider_ciphersuites_len(void); */ const struct rustls_supported_ciphersuite *rustls_default_crypto_provider_ciphersuites_get(size_t index); +/** + * Write `len` bytes of cryptographically secure random data to `buff` using the process-wide + * default crypto provider. + * + * `buff` must point to a buffer of at least `len` bytes. The caller maintains ownership + * of the buffer. + * + * Returns `RUSTLS_RESULT_OK` on success, and one of `RUSTLS_RESULT_NO_DEFAULT_CRYPTO_PROVIDER` + * or `RUSTLS_RESULT_GET_RANDOM_FAILED` on failure. + */ +rustls_result rustls_default_crypto_provider_random(uint8_t *buff, size_t len); + /** * Frees the `rustls_signing_key`. This is safe to call with a `NULL` argument, but * must not be called twice with the same value. -- cgit v1.2.3-70-g09d2