mirror of https://github.com/ctz/rustls
Expose FIPS "service indicator"
This means a `ClientConfig` and `ServerConfig` can be asked whether it is in fips mode, and it answers by asking the same of all its constituent cryptography.
This commit is contained in:
parent
afe43b0213
commit
327444fdb8
|
@ -284,6 +284,12 @@ impl ClientConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return true if connections made with this `ClientConfig` will
|
||||
/// operate in FIPS mode.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
self.provider.fips_mode()
|
||||
}
|
||||
|
||||
/// We support a given TLS version if it's quoted in the configured
|
||||
/// versions *and* at least one ciphersuite for this version is
|
||||
/// also configured.
|
||||
|
|
|
@ -36,8 +36,17 @@ pub(crate) mod tls13;
|
|||
/// A `CryptoProvider` backed by aws-lc-rs.
|
||||
pub fn default_provider() -> CryptoProvider {
|
||||
CryptoProvider {
|
||||
cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(),
|
||||
kx_groups: ALL_KX_GROUPS.to_vec(),
|
||||
// TODO: make this filtering conditional on fips feature
|
||||
cipher_suites: DEFAULT_CIPHER_SUITES
|
||||
.iter()
|
||||
.filter(|cs| cs.fips_mode())
|
||||
.copied()
|
||||
.collect(),
|
||||
kx_groups: ALL_KX_GROUPS
|
||||
.iter()
|
||||
.filter(|kx| kx.fips_mode())
|
||||
.copied()
|
||||
.collect(),
|
||||
signature_verification_algorithms: SUPPORTED_SIG_ALGS,
|
||||
secure_random: &AwsLcRs,
|
||||
key_provider: &AwsLcRs,
|
||||
|
@ -55,6 +64,10 @@ impl SecureRandom for AwsLcRs {
|
|||
.fill(buf)
|
||||
.map_err(|_| GetRandomFailed)
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
aws_lc_rs::try_fips_mode().is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyProvider for AwsLcRs {
|
||||
|
@ -64,6 +77,10 @@ impl KeyProvider for AwsLcRs {
|
|||
) -> Result<Arc<dyn SigningKey>, Error> {
|
||||
sign::any_supported_type(&key_der)
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
aws_lc_rs::try_fips_mode().is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// The cipher suite configuration that an application should use by default.
|
||||
|
@ -207,4 +224,10 @@ mod ring_shim {
|
|||
}
|
||||
|
||||
/// AEAD algorithm that is used by `mod ticketer`.
|
||||
pub(super) static TICKETER_AEAD: &'static ring_like::aead::Algorithm = &ring_like::aead::AES_256_GCM;
|
||||
pub(super) static TICKETER_AEAD: &'static ring_like::aead::Algorithm =
|
||||
&ring_like::aead::AES_256_GCM;
|
||||
|
||||
/// Are we in FIPS mode?
|
||||
pub(super) fn fips_mode() -> bool {
|
||||
aws_lc_rs::try_fips_mode().is_ok()
|
||||
}
|
||||
|
|
|
@ -188,6 +188,10 @@ impl Tls12AeadAlgorithm for GcmAlgorithm {
|
|||
iv: gcm_iv(write_iv, explicit),
|
||||
})
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ChaCha20Poly1305;
|
||||
|
@ -234,6 +238,10 @@ impl Tls12AeadAlgorithm for ChaCha20Poly1305 {
|
|||
iv: Iv::new(iv[..].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
false // not FIPS approved
|
||||
}
|
||||
}
|
||||
|
||||
/// A `MessageEncrypter` for AES-GCM AEAD ciphersuites. TLS 1.2 only.
|
||||
|
@ -433,4 +441,8 @@ impl Prf for Tls12Prf {
|
|||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,10 @@ impl Tls13AeadAlgorithm for Chacha20Poly1305Aead {
|
|||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
|
||||
Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv })
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
false // not FIPS approved
|
||||
}
|
||||
}
|
||||
|
||||
struct Aes256GcmAead(AeadAlgorithm);
|
||||
|
@ -130,6 +134,10 @@ impl Tls13AeadAlgorithm for Aes256GcmAead {
|
|||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
|
||||
Ok(ConnectionTrafficSecrets::Aes256Gcm { key, iv })
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
struct Aes128GcmAead(AeadAlgorithm);
|
||||
|
@ -154,6 +162,10 @@ impl Tls13AeadAlgorithm for Aes128GcmAead {
|
|||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
|
||||
Ok(ConnectionTrafficSecrets::Aes128Gcm { key, iv })
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
// common encrypter/decrypter/key_len items for above Tls13AeadAlgorithm impls
|
||||
|
@ -346,6 +358,10 @@ impl Hkdf for RingHkdf {
|
|||
fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> crypto::hmac::Tag {
|
||||
crypto::hmac::Tag::new(hmac::sign(&hmac::Key::new(self.1, key.as_ref()), message).as_ref())
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
struct RingHkdfExpander {
|
||||
|
|
|
@ -31,6 +31,11 @@ pub trait Tls13AeadAlgorithm: Send + Sync {
|
|||
key: AeadKey,
|
||||
iv: Iv,
|
||||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.2 cipher suite.
|
||||
|
@ -72,6 +77,11 @@ pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
|
|||
iv: &[u8],
|
||||
explicit: &[u8],
|
||||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// An error indicating that the AEAD algorithm does not support the requested operation.
|
||||
|
|
|
@ -18,6 +18,11 @@ pub trait Hash: Send + Sync {
|
|||
|
||||
/// Which hash function this is, eg, `HashAlgorithm::SHA256`.
|
||||
fn algorithm(&self) -> HashAlgorithm;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A hash output, stored as a value.
|
||||
|
|
|
@ -12,6 +12,11 @@ pub trait Hmac: Send + Sync {
|
|||
|
||||
/// Give the length of the underlying hash function. In RFC2104 terminology this is `L`.
|
||||
fn hash_output_len(&self) -> usize;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A HMAC tag, stored as a value.
|
||||
|
|
|
@ -186,6 +186,21 @@ pub struct CryptoProvider {
|
|||
pub key_provider: &'static dyn KeyProvider,
|
||||
}
|
||||
|
||||
impl CryptoProvider {
|
||||
/// Returns if this `CryptoProvider` is operating in FIPS mode.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
self.cipher_suites
|
||||
.iter()
|
||||
.all(|cs| cs.fips_mode())
|
||||
&& self
|
||||
.kx_groups
|
||||
.iter()
|
||||
.all(|kx| kx.fips_mode())
|
||||
&& self.secure_random.fips_mode()
|
||||
&& self.key_provider.fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
/// A source of cryptographically secure randomness.
|
||||
pub trait SecureRandom: Send + Sync + Debug {
|
||||
/// Fill the given buffer with random bytes.
|
||||
|
@ -199,6 +214,11 @@ pub trait SecureRandom: Send + Sync + Debug {
|
|||
/// an ephemeral key exchange key, but this is not included in the interface with
|
||||
/// rustls: it is assumed that the cryptography library provides for this itself.
|
||||
fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed>;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A mechanism for loading private [SigningKey]s from [PrivateKeyDer].
|
||||
|
@ -214,6 +234,14 @@ pub trait KeyProvider: Send + Sync + Debug {
|
|||
&self,
|
||||
key_der: PrivateKeyDer<'static>,
|
||||
) -> Result<Arc<dyn SigningKey>, Error>;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
///
|
||||
/// If this returns true, that must be the case for all possible key types
|
||||
/// supported by [`load_private_key()`].
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A supported key exchange group.
|
||||
|
@ -240,6 +268,11 @@ pub trait SupportedKxGroup: Send + Sync + Debug {
|
|||
/// If the `NamedGroup` enum does not have a name for the algorithm you are implementing,
|
||||
/// you can use [`NamedGroup::Unknown`].
|
||||
fn name(&self) -> NamedGroup;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// An in-progress key exchange originating from a [`SupportedKxGroup`].
|
||||
|
|
|
@ -29,6 +29,10 @@ impl crypto::hash::Hash for Hash {
|
|||
fn algorithm(&self) -> HashAlgorithm {
|
||||
self.1
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
struct Context(digest::Context);
|
||||
|
|
|
@ -23,6 +23,10 @@ impl crypto::hmac::Hmac for Hmac {
|
|||
fn hash_output_len(&self) -> usize {
|
||||
self.0.digest_algorithm().output_len()
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
struct Key(ring_like::hmac::Key);
|
||||
|
|
|
@ -44,6 +44,10 @@ impl SupportedKxGroup for KxGroup {
|
|||
fn name(&self) -> NamedGroup {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for KxGroup {
|
||||
|
|
|
@ -205,3 +205,7 @@ mod ring_shim {
|
|||
/// AEAD algorithm that is used by `mod ticketer`.
|
||||
pub(super) static TICKETER_AEAD: &'static ring_like::aead::Algorithm =
|
||||
&ring_like::aead::CHACHA20_POLY1305;
|
||||
|
||||
pub(super) fn fips_mode() -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -177,6 +177,10 @@ impl crate::quic::Algorithm for KeyBuilder {
|
|||
fn aead_key_len(&self) -> usize {
|
||||
self.0.key_len()
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -173,6 +173,10 @@ impl Tls12AeadAlgorithm for GcmAlgorithm {
|
|||
iv: gcm_iv(write_iv, explicit),
|
||||
})
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ChaCha20Poly1305;
|
||||
|
@ -219,6 +223,10 @@ impl Tls12AeadAlgorithm for ChaCha20Poly1305 {
|
|||
iv: Iv::new(iv[..].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
false // not fips approved
|
||||
}
|
||||
}
|
||||
|
||||
/// A `MessageEncrypter` for AES-GCM AEAD ciphersuites. TLS 1.2 only.
|
||||
|
|
|
@ -94,6 +94,10 @@ impl Tls13AeadAlgorithm for Chacha20Poly1305Aead {
|
|||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
|
||||
Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv })
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
false // chacha20poly1305 not FIPS approved
|
||||
}
|
||||
}
|
||||
|
||||
struct Aes256GcmAead(AeadAlgorithm);
|
||||
|
@ -118,6 +122,10 @@ impl Tls13AeadAlgorithm for Aes256GcmAead {
|
|||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
|
||||
Ok(ConnectionTrafficSecrets::Aes256Gcm { key, iv })
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
struct Aes128GcmAead(AeadAlgorithm);
|
||||
|
@ -142,6 +150,10 @@ impl Tls13AeadAlgorithm for Aes128GcmAead {
|
|||
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
|
||||
Ok(ConnectionTrafficSecrets::Aes128Gcm { key, iv })
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
// common encrypter/decrypter/key_len items for above Tls13AeadAlgorithm impls
|
||||
|
@ -263,6 +275,10 @@ impl Hkdf for RingHkdf {
|
|||
fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> crypto::hmac::Tag {
|
||||
crypto::hmac::Tag::new(hmac::sign(&hmac::Key::new(self.1, key.as_ref()), message).as_ref())
|
||||
}
|
||||
|
||||
fn fips_mode(&self) -> bool {
|
||||
super::fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
struct RingHkdfExpander {
|
||||
|
|
|
@ -63,6 +63,11 @@ pub trait Prf: Send + Sync {
|
|||
///
|
||||
/// The caller guarantees that `secret`, `label`, and `seed` are non-empty.
|
||||
fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]);
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn prf(out: &mut [u8], hmac_key: &dyn hmac::Key, label: &[u8], seed: &[u8]) {
|
||||
|
|
|
@ -176,6 +176,11 @@ pub trait Hkdf: Send + Sync {
|
|||
/// See [RFC2104](https://datatracker.ietf.org/doc/html/rfc2104) for the
|
||||
/// definition of HMAC.
|
||||
fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag;
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// `HKDF-Expand(PRK, info, L)` to construct any type from a byte array.
|
||||
|
|
|
@ -584,6 +584,11 @@ pub trait Algorithm: Send + Sync {
|
|||
///
|
||||
/// This controls the size of `AeadKey`s presented to `packet_key()` and `header_protection_key()`.
|
||||
fn aead_key_len(&self) -> usize;
|
||||
|
||||
/// Whether this algorithm is FIPS-approved.
|
||||
fn fips_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A QUIC header protection key
|
||||
|
|
|
@ -387,6 +387,12 @@ impl ServerConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return true if connections made with this `ServerConfig` will
|
||||
/// operate in FIPS mode.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
self.provider.fips_mode()
|
||||
}
|
||||
|
||||
/// We support a given TLS version if it's quoted in the configured
|
||||
/// versions *and* at least one ciphersuite for this version is
|
||||
/// also configured.
|
||||
|
|
|
@ -41,6 +41,15 @@ pub struct CipherSuiteCommon {
|
|||
pub integrity_limit: u64,
|
||||
}
|
||||
|
||||
impl CipherSuiteCommon {
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
///
|
||||
/// This means all the constituent parts that do cryptography return true for `fips_mode()`.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
self.hash_provider.fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
/// A cipher suite supported by rustls.
|
||||
///
|
||||
/// This type carries both configuration and implementation. Compare with
|
||||
|
@ -117,6 +126,15 @@ impl SupportedCipherSuite {
|
|||
.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
match self {
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(cs) => cs.fips_mode(),
|
||||
Self::Tls13(cs) => cs.fips_mode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SupportedCipherSuite {
|
||||
|
|
|
@ -62,6 +62,13 @@ impl Tls12CipherSuite {
|
|||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
///
|
||||
/// This means all the constituent parts that do cryptography return true for `fips_mode()`.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
self.common.fips_mode() && self.prf_provider.fips_mode() && self.aead_alg.fips_mode()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static Tls12CipherSuite> for SupportedCipherSuite {
|
||||
|
|
|
@ -42,6 +42,19 @@ impl Tls13CipherSuite {
|
|||
(prev.common.hash_provider.algorithm() == self.common.hash_provider.algorithm())
|
||||
.then(|| prev)
|
||||
}
|
||||
|
||||
/// Return true if this is backed by a FIPS-approved implementation.
|
||||
///
|
||||
/// This means all the constituent parts that do cryptography return true for `fips_mode()`.
|
||||
pub fn fips_mode(&self) -> bool {
|
||||
self.common.fips_mode()
|
||||
&& self.hkdf_provider.fips_mode()
|
||||
&& self.aead_alg.fips_mode()
|
||||
&& self
|
||||
.quic
|
||||
.map(|q| q.fips_mode())
|
||||
.unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static Tls13CipherSuite> for SupportedCipherSuite {
|
||||
|
|
|
@ -5707,3 +5707,27 @@ fn test_client_removes_tls12_session_if_server_sends_undecryptable_first_message
|
|||
ClientStorageOp::RemoveTls12Session(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
#[test]
|
||||
fn test_client_fips_service_indicator() {
|
||||
assert_eq!(make_client_config(KeyType::Rsa).fips_mode(), false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
#[test]
|
||||
fn test_server_fips_service_indicator() {
|
||||
assert_eq!(make_server_config(KeyType::Rsa).fips_mode(), false);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "ring"), feature = "aws_lc_rs"))]
|
||||
#[test]
|
||||
fn test_client_fips_service_indicator() {
|
||||
assert_eq!(make_client_config(KeyType::Rsa).fips_mode(), true);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "ring"), feature = "aws_lc_rs"))]
|
||||
#[test]
|
||||
fn test_server_fips_service_indicator() {
|
||||
assert_eq!(make_server_config(KeyType::Rsa).fips_mode(), true);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue