From 365823df3580ed10dc2d1dbc94763e1b5e5a4561 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 21 Oct 2024 14:20:06 -0400 Subject: [PATCH] expose new FIPS related APIs * Ability to instantiate the FIPS default `crypto_provider` using a new function `rustls_default_fips_provider()`, available only when the fips feature is activated. * Ability to determine if a given `crypto_provider` is in FIPS mode using a new function `rustls_crypto_provider_fips()`. * Ability to determine if a given `rustls_client_config` would create connections that are FIPS compatible with a new function `rustls_client_config_fips()`. * Ability to determine if a given `rustls_server_config` would create connections that are FIPS compatible with a new function `rustls_server_config_fips()`. * Ability to determine if a given `rustls_connection` was created from a `rustls_client_config` that was FIPS enabled with a new function `rustls_connection_fips()`. Doing equivalent for a server connection is not presently supported upstream (will be fixed next release). --- cbindgen.toml | 3 ++- src/client.rs | 13 ++++++++++ src/connection.rs | 18 +++++++++++++ src/crypto_provider.rs | 33 ++++++++++++++++++++++++ src/rustls.h | 57 ++++++++++++++++++++++++++++++++++++++++++ src/server.rs | 13 ++++++++++ 6 files changed, 136 insertions(+), 1 deletion(-) diff --git a/cbindgen.toml b/cbindgen.toml index b2bf782f..7c2a5966 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -14,7 +14,8 @@ include = ["rustls_tls_version"] "feature = read_buf" = "DEFINE_READ_BUF" "feature = aws-lc-rs" = "DEFINE_AWS_LC_RS" "feature = ring" = "DEFINE_RING" +"feature = fips" = "DEFINE_FIPS" [parse.expand] crates = ["rustls-ffi"] -features = ["read_buf", "aws-lc-rs", "ring"] +features = ["read_buf", "aws-lc-rs", "ring", "fips"] diff --git a/src/client.rs b/src/client.rs index 363354b8..66b85568 100644 --- a/src/client.rs +++ b/src/client.rs @@ -582,6 +582,19 @@ impl rustls_client_config_builder { } impl rustls_client_config { + /// Returns true if a `rustls_connection` created from the `rustls_client_config` will + /// operate in FIPS mode. + /// + /// This is different from `rustls_crypto_provider_fips` which is concerned + /// only with cryptography, whereas this also covers TLS-level configuration that NIST + /// recommends, as well as ECH HPKE suites if applicable. + #[no_mangle] + pub extern "C" fn rustls_client_config_fips(config: *const rustls_client_config) -> bool { + ffi_panic_boundary! { + try_ref_from_ptr!(config).fips() + } + } + /// "Free" a `rustls_client_config` previously returned from /// `rustls_client_config_builder_build`. /// diff --git a/src/connection.rs b/src/connection.rs index f1dc4fb4..7081820d 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -613,6 +613,24 @@ impl rustls_connection { } } + /// Returns true if the `rustls_connection` was made with a `rustls_client_config` + /// that is FIPS compatible. + /// + /// This is different from `rustls_crypto_provider_fips` which is concerned + /// only with cryptography, whereas this also covers TLS-level configuration that NIST + /// recommends, as well as ECH HPKE suites if applicable. + #[no_mangle] + pub extern "C" fn rustls_connection_fips(conn: *const rustls_connection) -> bool { + ffi_panic_boundary! { + let conn = try_ref_from_ptr!(conn); + match &conn.conn { + rustls::Connection::Client(c) => c.fips(), + // TODO(XXX): investigate why there isn't a ServerConnection::fips(). + _ => false, + } + } + } + /// Free a rustls_connection. Calling with NULL is fine. /// Must not be called twice with the same value. #[no_mangle] diff --git a/src/crypto_provider.rs b/src/crypto_provider.rs index 0d04476b..6da6bccb 100644 --- a/src/crypto_provider.rs +++ b/src/crypto_provider.rs @@ -248,6 +248,26 @@ pub extern "C" fn rustls_aws_lc_rs_crypto_provider() -> *const rustls_crypto_pro } } +/// Return a `rustls_crypto_provider` that uses FIPS140-3 approved cryptography. +/// +/// Using this function expresses in your code that you require FIPS-approved cryptography, +/// and will not compile if you make a mistake with cargo features. +/// +/// See the upstream [rustls FIPS documentation][FIPS] for more information. +/// +/// The caller owns the returned `rustls_crypto_provider` and must free it using +/// `rustls_crypto_provider_free`. +/// +/// [FIPS]: https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html +#[no_mangle] +#[cfg(feature = "fips")] +pub extern "C" fn rustls_default_fips_provider() -> *const rustls_crypto_provider { + ffi_panic_boundary! { + Arc::into_raw(Arc::new(rustls::crypto::default_fips_provider())) + as *const rustls_crypto_provider + } +} + /// Retrieve a pointer to the process default `rustls_crypto_provider`. /// /// This may return `NULL` if no process default provider has been set using @@ -364,6 +384,19 @@ pub extern "C" fn rustls_crypto_provider_random( } } +/// Returns true if the `rustls_crypto_provider` is operating in FIPS mode. +/// +/// This covers only the cryptographic parts of FIPS approval. There are also +/// TLS protocol-level recommendations made by NIST. You should prefer to call +/// `rustls_client_config_fips` or `rustls_server_config_fips` which take these +/// into account. +#[no_mangle] +pub extern "C" fn rustls_crypto_provider_fips(provider: *const rustls_crypto_provider) -> bool { + ffi_panic_boundary! { + try_ref_from_ptr!(provider).fips() + } +} + /// Frees the `rustls_crypto_provider`. /// /// Calling with `NULL` is fine. diff --git a/src/rustls.h b/src/rustls.h index f23fc013..5af9016f 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -1758,6 +1758,16 @@ rustls_result rustls_client_config_builder_build(struct rustls_client_config_bui */ void rustls_client_config_builder_free(struct rustls_client_config_builder *config); +/** + * Returns true if a `rustls_connection` created from the `rustls_client_config` will + * operate in FIPS mode. + * + * This is different from `rustls_crypto_provider_fips` which is concerned + * only with cryptography, whereas this also covers TLS-level configuration that NIST + * recommends, as well as ECH HPKE suites if applicable. + */ +bool rustls_client_config_fips(const struct rustls_client_config *config); + /** * "Free" a `rustls_client_config` previously returned from * `rustls_client_config_builder_build`. @@ -2047,6 +2057,16 @@ rustls_result rustls_connection_read_2(struct rustls_connection *conn, size_t *out_n); #endif +/** + * Returns true if the `rustls_connection` was made with a `rustls_client_config` + * that is FIPS compatible. + * + * This is different from `rustls_crypto_provider_fips` which is concerned + * only with cryptography, whereas this also covers TLS-level configuration that NIST + * recommends, as well as ECH HPKE suites if applicable. + */ +bool rustls_connection_fips(const struct rustls_connection *conn); + /** * Free a rustls_connection. Calling with NULL is fine. * Must not be called twice with the same value. @@ -2172,6 +2192,23 @@ const struct rustls_crypto_provider *rustls_ring_crypto_provider(void); const struct rustls_crypto_provider *rustls_aws_lc_rs_crypto_provider(void); #endif +#if defined(DEFINE_FIPS) +/** + * Return a `rustls_crypto_provider` that uses FIPS140-3 approved cryptography. + * + * Using this function expresses in your code that you require FIPS-approved cryptography, + * and will not compile if you make a mistake with cargo features. + * + * See the upstream [rustls FIPS documentation][FIPS] for more information. + * + * The caller owns the returned `rustls_crypto_provider` and must free it using + * `rustls_crypto_provider_free`. + * + * [FIPS]: https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html + */ +const struct rustls_crypto_provider *rustls_default_fips_provider(void); +#endif + /** * Retrieve a pointer to the process default `rustls_crypto_provider`. * @@ -2233,6 +2270,16 @@ rustls_result rustls_crypto_provider_random(const struct rustls_crypto_provider uint8_t *buff, size_t len); +/** + * Returns true if the `rustls_crypto_provider` is operating in FIPS mode. + * + * This covers only the cryptographic parts of FIPS approval. There are also + * TLS protocol-level recommendations made by NIST. You should prefer to call + * `rustls_client_config_fips` or `rustls_server_config_fips` which take these + * into account. + */ +bool rustls_crypto_provider_fips(const struct rustls_crypto_provider *provider); + /** * Frees the `rustls_crypto_provider`. * @@ -2497,6 +2544,16 @@ rustls_result rustls_server_config_builder_set_certified_keys(struct rustls_serv rustls_result rustls_server_config_builder_build(struct rustls_server_config_builder *builder, const struct rustls_server_config **config_out); +/** + * Returns true if a `rustls_connection` created from the `rustls_server_config` will + * operate in FIPS mode. + * + * This is different from `rustls_crypto_provider_fips` which is concerned + * only with cryptography, whereas this also covers TLS-level configuration that NIST + * recommends, as well as ECH HPKE suites if applicable. + */ +bool rustls_server_config_fips(const struct rustls_server_config *config); + /** * "Free" a rustls_server_config previously returned from * rustls_server_config_builder_build. diff --git a/src/server.rs b/src/server.rs index cf5d001f..f1650f12 100644 --- a/src/server.rs +++ b/src/server.rs @@ -377,6 +377,19 @@ impl rustls_server_config_builder { } impl rustls_server_config { + /// Returns true if a `rustls_connection` created from the `rustls_server_config` will + /// operate in FIPS mode. + /// + /// This is different from `rustls_crypto_provider_fips` which is concerned + /// only with cryptography, whereas this also covers TLS-level configuration that NIST + /// recommends, as well as ECH HPKE suites if applicable. + #[no_mangle] + pub extern "C" fn rustls_server_config_fips(config: *const rustls_server_config) -> bool { + ffi_panic_boundary! { + try_ref_from_ptr!(config).fips() + } + } + /// "Free" a rustls_server_config previously returned from /// rustls_server_config_builder_build. ///