diff --git a/rustls-wolfcrypt-provider/build.rs b/rustls-wolfcrypt-provider/build.rs new file mode 100644 index 0000000..32948d0 --- /dev/null +++ b/rustls-wolfcrypt-provider/build.rs @@ -0,0 +1,10 @@ +// Turns on the `fips` cfg when wolfssl-wolfcrypt is built against a FIPS +// wolfSSL. The crate publishes that as FIPS=1 in its build metadata, which +// Cargo hands to crates depending on it directly as DEP_WOLFSSL_FIPS. +fn main() { + println!("cargo::rustc-check-cfg=cfg(fips)"); + println!("cargo::rerun-if-env-changed=DEP_WOLFSSL_FIPS"); + if std::env::var("DEP_WOLFSSL_FIPS").as_deref() == Ok("1") { + println!("cargo::rustc-cfg=fips"); + } +} diff --git a/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs b/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs index f97b0c8..948fc89 100644 --- a/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs +++ b/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs @@ -16,6 +16,10 @@ const GCM_TAG_LENGTH: usize = 16; pub struct Aes128Gcm; impl Tls12AeadAlgorithm for Aes128Gcm { + fn fips(&self) -> bool { + crate::fips() + } + fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box { let mut iv_as_array = [0u8; GCM_NONCE_LENGTH]; iv_as_array[..(GCM_NONCE_LENGTH - 8)].copy_from_slice(iv); // implicit @@ -177,6 +181,10 @@ impl MessageDecrypter for WCTls12Decrypter { } impl Tls13AeadAlgorithm for Aes128Gcm { + fn fips(&self) -> bool { + crate::fips() + } + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box { Box::new(WCTls13Cipher { cipher: new_cipher(&key), diff --git a/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs b/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs index 9fc3fb0..42185f3 100644 --- a/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs +++ b/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs @@ -16,6 +16,10 @@ const GCM_TAG_LENGTH: usize = 16; pub struct Aes256Gcm; impl Tls12AeadAlgorithm for Aes256Gcm { + fn fips(&self) -> bool { + crate::fips() + } + fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box { let mut iv_as_array = [0u8; GCM_NONCE_LENGTH]; iv_as_array[..(GCM_NONCE_LENGTH - 8)].copy_from_slice(iv); // implicit @@ -177,6 +181,10 @@ impl MessageDecrypter for WCTls12Decrypter { } impl Tls13AeadAlgorithm for Aes256Gcm { + fn fips(&self) -> bool { + crate::fips() + } + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box { Box::new(WCTls13Cipher { cipher: new_cipher(&key), diff --git a/rustls-wolfcrypt-provider/src/aead/quic.rs b/rustls-wolfcrypt-provider/src/aead/quic.rs index a3259bc..ad02a54 100644 --- a/rustls-wolfcrypt-provider/src/aead/quic.rs +++ b/rustls-wolfcrypt-provider/src/aead/quic.rs @@ -4,12 +4,16 @@ use aead::{AeadInPlace, KeyInit}; use alloc::vec; +#[cfg(not(fips))] use core::mem; +#[cfg(not(fips))] use foreign_types::ForeignType; use zeroize::Zeroizing; use crate::alloc::string::ToString; +#[cfg(not(fips))] use crate::error::check_if_zero; +#[cfg(not(fips))] use crate::types::ChaChaObject; use alloc::boxed::Box; use alloc::vec::Vec; @@ -18,8 +22,10 @@ use rustls::crypto::cipher::{Iv, Nonce}; use rustls::quic::Tag; use rustls::{crypto::cipher::AeadKey, quic, Error}; use wolfssl_wolfcrypt::aes::{Aes128Gcm, Aes256Gcm, ECB}; +#[cfg(not(fips))] use wolfssl_wolfcrypt::chacha20_poly1305::ChaCha20Poly1305Aead; // The crate has no safe wrapper for plain ChaCha20, used for header protection. +#[cfg(not(fips))] use wolfssl_wolfcrypt::sys::{ wc_Chacha_Process, wc_Chacha_SetIV, wc_Chacha_SetKey, word32, ChaCha, }; @@ -37,6 +43,7 @@ macro_rules! mask_array { } pub enum Cipher { Aes(AesCipher), + #[cfg(not(fips))] ChaCha20(ChaChaCipher), } @@ -48,6 +55,7 @@ pub(crate) const TAG_LEN: usize = 16; pub const AES_128_KEY_LEN: usize = 128 / 8; pub const AES_256_KEY_LEN: usize = 256 / 8; +#[cfg(not(fips))] pub const CHACHA_KEY_LEN: usize = 32; pub const SAMPLE_LEN: usize = TAG_LEN; pub const MASK_LEN: usize = 5; @@ -82,6 +90,7 @@ impl HPAlgorithm { pub enum HPAlgorithmID { Aes128, Aes256, + #[cfg(not(fips))] ChaCha20, } @@ -118,6 +127,7 @@ fn init_hp_aes_cipher(key: &[u8]) -> Result { fn generate_mask_aes(hp_cipher: &Cipher, sample: &[u8]) -> Result<[u8; MASK_LEN], Error> { let aes_cipher = match hp_cipher { Cipher::Aes(c) => c, + #[cfg(not(fips))] _ => return Err(Error::General("Invalid cipher type".into())), }; @@ -128,6 +138,7 @@ fn generate_mask_aes(hp_cipher: &Cipher, sample: &[u8]) -> Result<[u8; MASK_LEN] } /// ChaCha20. +#[cfg(not(fips))] pub static CHACHA20: HPAlgorithm = HPAlgorithm { key_len: CHACHA_KEY_LEN, init: init_hp_chacha20_cipher, @@ -135,12 +146,14 @@ pub static CHACHA20: HPAlgorithm = HPAlgorithm { id: HPAlgorithmID::ChaCha20, }; +#[cfg(not(fips))] fn init_hp_chacha20_cipher(key: &[u8]) -> Result { let mut chacha_cipher = ChaChaCipher::new(None)?; chacha_cipher.set_key(key)?; Ok(Cipher::ChaCha20(chacha_cipher)) } +#[cfg(not(fips))] fn generate_mask_chacha20(hp_cipher: &Cipher, sample: &[u8]) -> Result<[u8; MASK_LEN], Error> { let chacha20_cipher = match hp_cipher { Cipher::ChaCha20(c) => c, @@ -260,6 +273,7 @@ impl quic::HeaderProtectionKey for HeaderProtectionKey { pub(crate) enum PacketKeyAlgorithmID { Aes128Gcm, Aes256Gcm, + #[cfg(not(fips))] ChaCha20Poly1305, } @@ -334,6 +348,7 @@ fn encrypt_aes_gcm( ) -> Result { let aes_cipher = match packet_cipher { Cipher::Aes(c) => c, + #[cfg(not(fips))] _ => return Err(Error::General("Invalid cipher type".into())), }; aes_cipher.encrypt_separate_tag(nonce, aad, in_out) @@ -347,6 +362,7 @@ pub(super) fn decrypt_aes_gcm( ) -> Result<(), Error> { let aes_cipher = match packet_cipher { Cipher::Aes(aes_key) => aes_key, + #[cfg(not(fips))] _ => return Err(Error::General("Invalid cipher type".into())), }; aes_cipher.decrypt(nonce, aad, in_out) @@ -357,6 +373,7 @@ pub(super) fn decrypt_aes_gcm( /// The keys are 256 bits long and the nonces are 96 bits long. /// /// [RFC 8439]: https://tools.ietf.org/html/rfc8439 +#[cfg(not(fips))] pub static CHACHA20_POLY1305: AeadAlgorithm = AeadAlgorithm { init: init_chacha20_poly1305_cipher, encrypt: encrypt_chacha20_poly1305, @@ -365,6 +382,7 @@ pub static CHACHA20_POLY1305: AeadAlgorithm = AeadAlgorithm { id: PacketKeyAlgorithmID::ChaCha20Poly1305, }; +#[cfg(not(fips))] fn init_chacha20_poly1305_cipher(key: &[u8]) -> Result { let key_array = <[u8; 32]>::try_from(key) .map_err(|_| Error::General("Invalid key length for ChaCha20-Poly1305".into()))?; @@ -372,6 +390,7 @@ fn init_chacha20_poly1305_cipher(key: &[u8]) -> Result { Ok(Cipher::ChaCha20(chacha_cipher)) } +#[cfg(not(fips))] fn encrypt_chacha20_poly1305( packet_cipher: &Cipher, nonce: &[u8], @@ -385,6 +404,7 @@ fn encrypt_chacha20_poly1305( chacha_cipher.encrypt_separate_tag(nonce, aad, in_out) } +#[cfg(not(fips))] fn decrypt_chacha20_poly1305( packet_cipher: &Cipher, nonce: &[u8], @@ -523,7 +543,7 @@ impl quic::Algorithm for KeyFactory { } fn fips(&self) -> bool { - false + crate::fips() } } @@ -628,10 +648,12 @@ impl AesCipher { } } +#[cfg(not(fips))] pub struct ChaChaCipher { key: Option>, } +#[cfg(not(fips))] impl ChaChaCipher { pub fn new(key: Option<[u8; CHACHA_KEY_LEN]>) -> Result { Ok(Self { @@ -764,16 +786,21 @@ impl ChaChaCipher { #[cfg(test)] mod tests { use hex_literal::hex; + #[cfg(not(fips))] use rustls::crypto::tls13::HkdfExpander; use std::prelude::v1::Vec; use std::vec; use crate::aead; + #[cfg(not(fips))] use rustls::crypto::cipher::{AeadKey, Iv, NONCE_LEN}; use rustls::quic::*; use crate::default_provider; - use crate::{TLS13_AES_128_GCM_SHA256, TLS13_CHACHA20_POLY1305_SHA256}; + use crate::TLS13_AES_128_GCM_SHA256; + #[cfg(not(fips))] + use crate::TLS13_CHACHA20_POLY1305_SHA256; + #[cfg(not(fips))] use rustls::crypto::tls13::OkmBlock; use rustls::internal::msgs::codec::Codec; use rustls::{ClientConfig, Error, ServerConfig, Side, SideData}; @@ -973,6 +1000,7 @@ mod tests { pub body: Vec, } + #[cfg(not(fips))] #[derive(Debug, Clone)] struct ChaCha20TestVector { key: [u8; 32], @@ -993,6 +1021,7 @@ mod tests { }, } + #[cfg(not(fips))] fn hkdf_expand_label( expander: &Box, label: &[u8], @@ -1018,6 +1047,7 @@ mod tests { let _ = expander.expand_slice(info, output); } + #[cfg(not(fips))] fn test_short_packet(version: rustls::quic::Version, expected: &[u8]) { // Code taken from rustls with modification let chacha_key_len = TLS13_CHACHA20_POLY1305_SHA256 @@ -1132,6 +1162,7 @@ mod tests { assert_eq!(plain, &PLAIN[4..]); } + #[cfg(not(fips))] #[test] fn short_packet_header_protection() { // https://www.rfc-editor.org/rfc/rfc9001.html#name-chacha20-poly1305-short-hea @@ -1144,6 +1175,7 @@ mod tests { ); } + #[cfg(not(fips))] #[test] fn short_packet_header_protection_v2() { // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-chacha20-poly1305-short-head @@ -1550,6 +1582,7 @@ mod tests { } } + #[cfg(not(fips))] #[test] fn test_chacha_mask_generation() { //Test idea taken from ring @@ -1571,9 +1604,10 @@ mod tests { #[test] fn test_sample_len() { - let hp_algs: Vec<&aead::quic::HPAlgorithm> = vec![ + let hp_algs: &[&aead::quic::HPAlgorithm] = &[ &aead::quic::AES_128, &aead::quic::AES_256, + #[cfg(not(fips))] &aead::quic::CHACHA20, ]; let mut first = vec![0u8; 1]; @@ -1623,9 +1657,10 @@ mod tests { #[test] fn test_key_len() { - let hp_algs: Vec<&aead::quic::HPAlgorithm> = vec![ + let hp_algs: &[&aead::quic::HPAlgorithm] = &[ &aead::quic::AES_128, &aead::quic::AES_256, + #[cfg(not(fips))] &aead::quic::CHACHA20, ]; for alg in hp_algs { diff --git a/rustls-wolfcrypt-provider/src/hash/sha256.rs b/rustls-wolfcrypt-provider/src/hash/sha256.rs index 6af6760..89152d1 100644 --- a/rustls-wolfcrypt-provider/src/hash/sha256.rs +++ b/rustls-wolfcrypt-provider/src/hash/sha256.rs @@ -7,6 +7,10 @@ use wolfssl_wolfcrypt::sha::SHA256; pub struct WCSha256; impl hash::Hash for WCSha256 { + fn fips(&self) -> bool { + crate::fips() + } + fn start(&self) -> Box { let sha256 = SHA256::new().expect("SHA256::new failed"); Box::new(WCSha256Context::new(sha256)) diff --git a/rustls-wolfcrypt-provider/src/hash/sha384.rs b/rustls-wolfcrypt-provider/src/hash/sha384.rs index 0893138..6dd4fbd 100644 --- a/rustls-wolfcrypt-provider/src/hash/sha384.rs +++ b/rustls-wolfcrypt-provider/src/hash/sha384.rs @@ -7,6 +7,10 @@ use wolfssl_wolfcrypt::sha::SHA384; pub struct WCSha384; impl hash::Hash for WCSha384 { + fn fips(&self) -> bool { + crate::fips() + } + fn start(&self) -> Box { let sha384 = SHA384::new().expect("SHA384::new failed"); Box::new(WCSha384Context::new(sha384)) diff --git a/rustls-wolfcrypt-provider/src/hkdf.rs b/rustls-wolfcrypt-provider/src/hkdf.rs index 4013752..8616678 100644 --- a/rustls-wolfcrypt-provider/src/hkdf.rs +++ b/rustls-wolfcrypt-provider/src/hkdf.rs @@ -5,12 +5,19 @@ use rustls::crypto::hmac::Hmac as RustlsHmac; use rustls::crypto::tls13::{self, Hkdf as RustlsHkdf}; use wolfssl_wolfcrypt::hkdf::{hkdf_expand, hkdf_extract}; +#[cfg(fips)] +use wolfssl_wolfcrypt::fips::PrivateKeyRead; + use crate::hmac::WCShaHmac; use zeroize::Zeroizing; pub struct WCHkdfUsingHmac(pub WCShaHmac); impl RustlsHkdf for WCHkdfUsingHmac { + fn fips(&self) -> bool { + crate::fips() + } + fn extract_from_zero_ikm( &self, salt: Option<&[u8]>, @@ -30,6 +37,8 @@ impl RustlsHkdf for WCHkdfUsingHmac { let zero_salt = vec![0u8; hash_len]; let salt_bytes = salt.unwrap_or(&zero_salt); + #[cfg(fips)] + let _read = PrivateKeyRead::enable().ok(); hkdf_extract( self.0.hash_type(), Some(salt_bytes), @@ -94,6 +103,8 @@ impl tls13::HkdfExpander for WolfHkdfExpander { return Err(tls13::OutputLengthError); } + #[cfg(fips)] + let _read = PrivateKeyRead::enable().ok(); hkdf_expand( self.hash_type, &self.extracted_key, @@ -118,9 +129,9 @@ impl tls13::HkdfExpander for WolfHkdfExpander { #[cfg(test)] mod tests { use super::*; - use crate::{ - TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, - }; + #[cfg(not(fips))] + use crate::TLS13_CHACHA20_POLY1305_SHA256; + use crate::{TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384}; use hex_literal::hex; use wycheproof::{hkdf::TestName, TestResult}; @@ -237,8 +248,11 @@ mod tests { #[test] fn test_hkdf_wycheproof_sha256() { - let suites: &[rustls::SupportedCipherSuite] = - &[TLS13_AES_128_GCM_SHA256, TLS13_CHACHA20_POLY1305_SHA256]; + let suites: &[rustls::SupportedCipherSuite] = &[ + TLS13_AES_128_GCM_SHA256, + #[cfg(not(fips))] + TLS13_CHACHA20_POLY1305_SHA256, + ]; let test_name: TestName = TestName::HkdfSha256; diff --git a/rustls-wolfcrypt-provider/src/hmac/mod.rs b/rustls-wolfcrypt-provider/src/hmac/mod.rs index 17ad664..09d0b5c 100644 --- a/rustls-wolfcrypt-provider/src/hmac/mod.rs +++ b/rustls-wolfcrypt-provider/src/hmac/mod.rs @@ -34,6 +34,10 @@ impl WCShaHmac { } impl crypto::hmac::Hmac for WCShaHmac { + fn fips(&self) -> bool { + crate::fips() + } + fn with_key(&self, key: &[u8]) -> Box { Box::new(WCHmacKey { key: Zeroizing::new(key.to_vec()), diff --git a/rustls-wolfcrypt-provider/src/kx.rs b/rustls-wolfcrypt-provider/src/kx.rs index ab4d90f..e81a821 100644 --- a/rustls-wolfcrypt-provider/src/kx.rs +++ b/rustls-wolfcrypt-provider/src/kx.rs @@ -5,9 +5,16 @@ use rustls::ffdhe_groups::FfdheGroup; mod sec256r1; mod sec384r1; mod sec521r1; +#[cfg(not(fips))] mod x25519; -pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X25519, &SECP256R1, &SECP384R1, &SECP521R1]; +pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ + #[cfg(not(fips))] + &X25519, + &SECP256R1, + &SECP384R1, + &SECP521R1, +]; macro_rules! define_kx_group { ($name:ident, $kx_type:ty, $kx_func:ident, $named_group:expr) => { @@ -15,6 +22,10 @@ macro_rules! define_kx_group { pub struct $name; impl crypto::SupportedKxGroup for $name { + fn fips(&self) -> bool { + crate::fips() + } + fn start(&self) -> Result, rustls::Error> { Ok(Box::new(<$kx_type>::$kx_func()?)) } @@ -31,6 +42,7 @@ macro_rules! define_kx_group { } // Define Supported KeyExchange groups +#[cfg(not(fips))] define_kx_group!( X25519, x25519::KeyExchangeX25519, diff --git a/rustls-wolfcrypt-provider/src/kx/sec256r1.rs b/rustls-wolfcrypt-provider/src/kx/sec256r1.rs index 80aa7e2..f0a01b2 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec256r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec256r1.rs @@ -20,6 +20,8 @@ impl KeyExchangeSecP256r1 { let mut priv_key_raw: Zeroizing> = Zeroizing::new(alloc::vec![0u8; 32].into_boxed_slice()); + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); key.export_private(&mut priv_key_raw) .map_err(|_| rustls::Error::General("wc_ecc_export_private_only failed".into()))?; @@ -56,6 +58,8 @@ impl KeyExchangeSecP256r1 { .map_err(|_| rustls::Error::General("Failed to set RNG on private key".into()))?; let mut out = [0u8; 32]; + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); priv_key .shared_secret(&mut pub_key, &mut out) .map_err(|_| rustls::Error::General("Failed to compute ECC shared secret".into()))?; diff --git a/rustls-wolfcrypt-provider/src/kx/sec384r1.rs b/rustls-wolfcrypt-provider/src/kx/sec384r1.rs index cf559fb..95a7631 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec384r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec384r1.rs @@ -20,6 +20,8 @@ impl KeyExchangeSecP384r1 { let mut priv_key_raw: Zeroizing> = Zeroizing::new(alloc::vec![0u8; 48].into_boxed_slice()); + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); key.export_private(&mut priv_key_raw) .map_err(|_| rustls::Error::General("wc_ecc_export_private_only failed".into()))?; @@ -56,6 +58,8 @@ impl KeyExchangeSecP384r1 { .map_err(|_| rustls::Error::General("Failed to set RNG on private key".into()))?; let mut out = [0u8; 48]; + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); priv_key .shared_secret(&mut pub_key, &mut out) .map_err(|_| rustls::Error::General("Failed to compute ECC shared secret".into()))?; diff --git a/rustls-wolfcrypt-provider/src/kx/sec521r1.rs b/rustls-wolfcrypt-provider/src/kx/sec521r1.rs index 1df062b..081e409 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec521r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec521r1.rs @@ -20,6 +20,8 @@ impl KeyExchangeSecP521r1 { let mut priv_key_raw: Zeroizing> = Zeroizing::new(alloc::vec![0u8; 66].into_boxed_slice()); + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); key.export_private(&mut priv_key_raw) .map_err(|_| rustls::Error::General("wc_ecc_export_private_only failed".into()))?; @@ -56,6 +58,8 @@ impl KeyExchangeSecP521r1 { .map_err(|_| rustls::Error::General("Failed to set RNG on private key".into()))?; let mut out = [0u8; 66]; + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); priv_key .shared_secret(&mut pub_key, &mut out) .map_err(|_| rustls::Error::General("Failed to compute ECC shared secret".into()))?; diff --git a/rustls-wolfcrypt-provider/src/lib.rs b/rustls-wolfcrypt-provider/src/lib.rs index 49eef77..c362c97 100644 --- a/rustls-wolfcrypt-provider/src/lib.rs +++ b/rustls-wolfcrypt-provider/src/lib.rs @@ -21,6 +21,7 @@ use crate::prf::WCPrfUsingHmac; pub mod aead { pub mod aes128gcm; pub mod aes256gcm; + #[cfg(not(fips))] pub mod chacha20; #[cfg(feature = "quic")] pub mod quic; @@ -30,9 +31,11 @@ pub mod sign { pub mod eddsa; pub mod rsa; } +#[cfg(not(fips))] +use crate::aead::chacha20; #[cfg(feature = "quic")] use crate::aead::quic::KeyFactory; -use crate::aead::{aes128gcm, aes256gcm, chacha20}; +use crate::aead::{aes128gcm, aes256gcm}; pub mod hash { pub mod sha256; @@ -54,6 +57,7 @@ type SigningAlgorithms = Vec>; * Crypto provider struct that we populate with our own crypto backend (wolfcrypt). * */ pub fn default_provider() -> CryptoProvider { + run_fips_self_tests(); CryptoProvider { cipher_suites: ALL_CIPHER_SUITES.to_vec(), kx_groups: kx::ALL_KX_GROUPS.to_vec(), @@ -66,6 +70,7 @@ pub fn default_provider() -> CryptoProvider { pub fn provider_with_specified_ciphers( ciphers: Vec, ) -> CryptoProvider { + run_fips_self_tests(); CryptoProvider { cipher_suites: ciphers, kx_groups: kx::ALL_KX_GROUPS.to_vec(), @@ -75,10 +80,42 @@ pub fn provider_with_specified_ciphers( } } +/// Runs wolfCrypt's FIPS self tests before the provider is used, so threads +/// using an algorithm for the first time at once don't race on its self test. +/// A failure leaves the failed algorithms refused, so it shows up on use. +/// Does nothing in non-FIPS builds. +fn run_fips_self_tests() { + #[cfg(fips)] + if let Err(rc) = wolfssl_wolfcrypt::fips::run_all_self_tests() { + log::error!("wolfCrypt FIPS self tests failed: {}", rc); + } +} + +/// The answer every fips method in this provider gives. In FIPS builds it asks +/// wolfSSL at run time, so a failed self test or integrity check answers false. +pub(crate) fn fips() -> bool { + #[cfg(fips)] + { + use wolfssl_wolfcrypt::sys; + // NORMAL is the only healthy mode, DEGRADED and FAILED mean a self test + // or the integrity check failed. + let mode = unsafe { sys::wolfCrypt_GetMode_fips() }; + mode == sys::FipsModeId_FIPS_MODE_NORMAL as core::ffi::c_int + } + #[cfg(not(fips))] + { + false + } +} + #[derive(Debug)] struct Provider; impl rustls::crypto::SecureRandom for Provider { + fn fips(&self) -> bool { + crate::fips() + } + fn fill(&self, bytes: &mut [u8]) -> Result<(), rustls::crypto::GetRandomFailed> { match random::wolfcrypt_random_buffer_generator(bytes) { Ok(()) => Ok(()), @@ -88,6 +125,10 @@ impl rustls::crypto::SecureRandom for Provider { } impl rustls::crypto::KeyProvider for Provider { + fn fips(&self) -> bool { + crate::fips() + } + fn load_private_key( &self, key_der: PrivateKeyDer<'static>, @@ -116,12 +157,15 @@ impl rustls::crypto::KeyProvider for Provider { pub static ALL_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[ TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256, + #[cfg(not(fips))] TLS13_CHACHA20_POLY1305_SHA256, TLS12_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS12_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + #[cfg(not(fips))] TLS12_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS12_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS12_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + #[cfg(not(fips))] TLS12_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, ]; @@ -141,6 +185,7 @@ static ALL_ECDSA_SCHEMES: &[rustls::SignatureScheme] = &[ rustls::SignatureScheme::ED25519, ]; +#[cfg(not(fips))] pub static TLS13_CHACHA20_POLY1305_SHA256: rustls::SupportedCipherSuite = rustls::SupportedCipherSuite::Tls13(&rustls::Tls13CipherSuite { common: rustls::crypto::CipherSuiteCommon { @@ -207,6 +252,7 @@ pub static TLS13_AES_256_GCM_SHA384: rustls::SupportedCipherSuite = }), }); +#[cfg(not(fips))] pub static TLS12_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: rustls::SupportedCipherSuite = rustls::SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { common: rustls::crypto::CipherSuiteCommon { @@ -246,6 +292,7 @@ pub static TLS12_ECDHE_RSA_WITH_AES_256_GCM_SHA384: rustls::SupportedCipherSuite sign: ALL_RSA_SCHEMES, }); +#[cfg(not(fips))] pub static TLS12_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: rustls::SupportedCipherSuite = rustls::SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { common: rustls::crypto::CipherSuiteCommon { @@ -284,3 +331,13 @@ pub static TLS12_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: rustls::SupportedCipherSui kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, sign: ALL_ECDSA_SCHEMES, }); + +#[cfg(test)] +mod tests { + // rustls calls the provider FIPS only if every part of it says so, + // QUIC included when that feature is on. + #[test] + fn test_fips_matches_build() { + assert_eq!(crate::default_provider().fips(), cfg!(fips)); + } +} diff --git a/rustls-wolfcrypt-provider/src/prf.rs b/rustls-wolfcrypt-provider/src/prf.rs index 67afa1c..7c682e5 100644 --- a/rustls-wolfcrypt-provider/src/prf.rs +++ b/rustls-wolfcrypt-provider/src/prf.rs @@ -7,6 +7,10 @@ use crate::hmac::*; pub struct WCPrfUsingHmac(pub WCShaHmac); impl crypto::tls12::Prf for WCPrfUsingHmac { + fn fips(&self) -> bool { + crate::fips() + } + fn for_key_exchange( &self, output: &mut [u8; 48], @@ -39,6 +43,8 @@ fn wc_prf( // The TLS 1.2 PRF is P_hash(secret, label || seed) (RFC 5246, section 5). let label_seed = [label, seed].concat(); + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); prf(secret, &label_seed, hash_type, output) .map_err(|_| rustls::Error::General("TLS 1.2 PRF failed".into())) } diff --git a/rustls-wolfcrypt-provider/src/sign/ecdsa.rs b/rustls-wolfcrypt-provider/src/sign/ecdsa.rs index 627d28c..61afef5 100644 --- a/rustls-wolfcrypt-provider/src/sign/ecdsa.rs +++ b/rustls-wolfcrypt-provider/src/sign/ecdsa.rs @@ -66,6 +66,8 @@ impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKey { // The export writes exactly the curve's key size, so a buffer big // enough for P-521 also tells us which curve the key is on. let mut priv_key_bytes = Zeroizing::new(vec![0u8; MAX_ECC_KEY_SIZE]); + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().ok(); let key_size = key .export_private(&mut priv_key_bytes) .map_err(|_| rustls::Error::General("wc_ecc_export_private_only failed".into()))?; diff --git a/rustls-wolfcrypt-provider/src/types/mod.rs b/rustls-wolfcrypt-provider/src/types/mod.rs index d085e8b..9177ce1 100644 --- a/rustls-wolfcrypt-provider/src/types/mod.rs +++ b/rustls-wolfcrypt-provider/src/types/mod.rs @@ -150,4 +150,5 @@ define_foreign_type!( ); define_foreign_type_no_copy!(RsaKeyObject, RsaKeyObjectRef, RsaKey, drop(wc_FreeRsaKey)); +#[cfg(not(fips))] define_foreign_type_no_copy!(ChaChaObject, ChaChaObjectRef, ChaCha); diff --git a/rustls-wolfcrypt-provider/src/verify/ecdsa.rs b/rustls-wolfcrypt-provider/src/verify/ecdsa.rs index 455c0a9..429a40d 100644 --- a/rustls-wolfcrypt-provider/src/verify/ecdsa.rs +++ b/rustls-wolfcrypt-provider/src/verify/ecdsa.rs @@ -29,6 +29,10 @@ impl EcdsaVerifier { } impl SignatureVerificationAlgorithm for EcdsaVerifier { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { match self.scheme { SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_P256, diff --git a/rustls-wolfcrypt-provider/src/verify/eddsa.rs b/rustls-wolfcrypt-provider/src/verify/eddsa.rs index 6711751..1b1423c 100644 --- a/rustls-wolfcrypt-provider/src/verify/eddsa.rs +++ b/rustls-wolfcrypt-provider/src/verify/eddsa.rs @@ -6,6 +6,10 @@ use wolfssl_wolfcrypt::ed25519::Ed25519 as WCEd25519; pub struct Ed25519; impl SignatureVerificationAlgorithm for Ed25519 { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::ED25519 } diff --git a/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs b/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs index b773e56..cc6917c 100644 --- a/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs +++ b/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs @@ -16,6 +16,10 @@ use wolfssl_wolfcrypt::sys::*; pub struct RsaPkcs1Sha256Verify; impl SignatureVerificationAlgorithm for RsaPkcs1Sha256Verify { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::RSA_ENCRYPTION } @@ -78,6 +82,10 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha256Verify { pub struct RsaPkcs1Sha384Verify; impl SignatureVerificationAlgorithm for RsaPkcs1Sha384Verify { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::RSA_ENCRYPTION } @@ -140,6 +148,10 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha384Verify { pub struct RsaPkcs1Sha512Verify; impl SignatureVerificationAlgorithm for RsaPkcs1Sha512Verify { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::RSA_ENCRYPTION } diff --git a/rustls-wolfcrypt-provider/src/verify/rsapss.rs b/rustls-wolfcrypt-provider/src/verify/rsapss.rs index fd92643..4cff56d 100644 --- a/rustls-wolfcrypt-provider/src/verify/rsapss.rs +++ b/rustls-wolfcrypt-provider/src/verify/rsapss.rs @@ -32,6 +32,10 @@ fn verify_pss( pub struct RsaPssSha256Verify; impl SignatureVerificationAlgorithm for RsaPssSha256Verify { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::RSA_ENCRYPTION } @@ -61,6 +65,10 @@ impl SignatureVerificationAlgorithm for RsaPssSha256Verify { pub struct RsaPssSha384Verify; impl SignatureVerificationAlgorithm for RsaPssSha384Verify { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::RSA_ENCRYPTION } @@ -90,6 +98,10 @@ impl SignatureVerificationAlgorithm for RsaPssSha384Verify { pub struct RsaPssSha512Verify; impl SignatureVerificationAlgorithm for RsaPssSha512Verify { + fn fips(&self) -> bool { + crate::fips() + } + fn public_key_alg_id(&self) -> AlgorithmIdentifier { alg_id::RSA_ENCRYPTION } diff --git a/rustls-wolfcrypt-provider/tests/e2e.rs b/rustls-wolfcrypt-provider/tests/e2e.rs index 837373b..27d4478 100644 --- a/rustls-wolfcrypt-provider/tests/e2e.rs +++ b/rustls-wolfcrypt-provider/tests/e2e.rs @@ -7,8 +7,11 @@ use rustls_wolfcrypt_provider::error::*; use rustls_wolfcrypt_provider::types::*; use rustls_wolfcrypt_provider::{ TLS12_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS12_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS12_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS13_AES_128_GCM_SHA256, - TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, +}; +#[cfg(not(fips))] +use rustls_wolfcrypt_provider::{ + TLS12_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS13_CHACHA20_POLY1305_SHA256, }; use std::env; use std::fs::File; @@ -101,6 +104,7 @@ mod tests { let current_dir_string = current_dir.to_string_lossy().into_owned(); let ciphers = [ + #[cfg(not(fips))] TLS12_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS12_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS12_ECDHE_RSA_WITH_AES_256_GCM_SHA384, @@ -194,6 +198,7 @@ mod tests { let current_dir_string = current_dir.to_string_lossy().into_owned(); let ciphers = [ + #[cfg(not(fips))] TLS13_CHACHA20_POLY1305_SHA256, TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, @@ -283,6 +288,7 @@ mod tests { #[test] fn test_tl12_against_website() { let ciphers = [ + #[cfg(not(fips))] TLS12_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS12_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS12_ECDHE_RSA_WITH_AES_256_GCM_SHA384, @@ -343,6 +349,7 @@ mod tests { #[test] fn test_tl13_against_website() { let ciphers = [ + #[cfg(not(fips))] TLS13_CHACHA20_POLY1305_SHA256, TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, @@ -488,6 +495,8 @@ mod tests { // Export private key in DER format let mut priv_key_raw = vec![0u8; key_size]; + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().unwrap(); key.export_private(&mut priv_key_raw).unwrap(); let der_ecc_key = ecc_private_key_to_der(&priv_key_raw, &pub_key_bytes, curve_id); @@ -525,6 +534,8 @@ mod tests { // Export private key let mut priv_key_raw = [0u8; Ed25519::KEY_SIZE]; + #[cfg(fips)] + let _read = wolfssl_wolfcrypt::fips::PrivateKeyRead::enable().unwrap(); key.export_private_only(&mut priv_key_raw).unwrap(); // Export public key