Skip to content

Commit

Permalink
arc_castable!, box_castable!, ref_castable! macros
Browse files Browse the repository at this point in the history
Throughout the project we follow a pattern of:

1. Creating a `snake_case_named` opaque struct[0].
2. Implementing `Castable` for the struct:
  a. specifying a `RustType` (some native Rust type).
  b. specifying an `Ownership` (`OwnershipBox`, `OwnershipArc`, `OwnershipRef`).

In the case of `OwnershipRef` we may also need to add a `PhantomData`
field referencing a lifetime to the opaque struct.

To reduce the boilerplate of doing all of the above over and over this
commit adds three new macros that accomplish the task with less
ceremony:
  `arc_castable!`, `box_castable!`, and `ref_castable!`.

Existing instances of the pattern are updated to use these macros with
one exception: the `rustls_client_hello` type implements `Castable` with
`OwnershipRef` for `rustls_client_hello<'a>`, but it isn't the same
opaque struct pattern as used elsewhere and so is best implemented by
hand.

[0]: https://github.com/rustls/rustls-ffi/blob/main/CONTRIBUTING.md#opaque-struct-pattern
  • Loading branch information
cpu committed Apr 2, 2024
1 parent 3a39676 commit 5802aef
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 224 deletions.
87 changes: 36 additions & 51 deletions src/acceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,43 @@ use crate::io::{
use crate::rslice::{rustls_slice_bytes, rustls_str};
use crate::server::rustls_server_config;
use crate::{
ffi_panic_boundary, free_box, rustls_result, set_boxed_mut_ptr, to_box, to_boxed_mut_ptr,
try_callback, try_clone_arc, try_mut_from_ptr, try_mut_from_ptr_ptr, try_ref_from_ptr,
try_take, Castable, OwnershipBox,
box_castable, ffi_panic_boundary, free_box, rustls_result, set_boxed_mut_ptr, to_box,
to_boxed_mut_ptr, try_callback, try_clone_arc, try_mut_from_ptr, try_mut_from_ptr_ptr,
try_ref_from_ptr, try_take,
};

/// A buffer and parser for ClientHello bytes. This allows reading ClientHello
/// before choosing a rustls_server_config. It's useful when the server
/// config will be based on parameters in the ClientHello: server name
/// indication (SNI), ALPN protocols, signature schemes, and cipher suites. In
/// particular, if a server wants to do some potentially expensive work to load a
/// certificate for a given hostname, rustls_acceptor allows doing that asynchronously,
/// as opposed to rustls_server_config_builder_set_hello_callback(), which doesn't
/// work well for asynchronous I/O.
///
/// The general flow is:
/// - rustls_acceptor_new()
/// - Loop:
/// - Read bytes from the network it with rustls_acceptor_read_tls().
/// - If successful, parse those bytes with rustls_acceptor_accept().
/// - If that returns RUSTLS_RESULT_ACCEPTOR_NOT_READY, continue.
/// - Otherwise, break.
/// - If rustls_acceptor_accept() returned RUSTLS_RESULT_OK:
/// - Examine the resulting rustls_accepted.
/// - Create or select a rustls_server_config.
/// - Call rustls_accepted_into_connection().
/// - Otherwise, there was a problem with the ClientHello data and the
/// connection should be rejected.
pub struct rustls_acceptor {
_private: [u8; 0],
box_castable! {
/// A buffer and parser for ClientHello bytes. This allows reading ClientHello
/// before choosing a rustls_server_config. It's useful when the server
/// config will be based on parameters in the ClientHello: server name
/// indication (SNI), ALPN protocols, signature schemes, and cipher suites. In
/// particular, if a server wants to do some potentially expensive work to load a
/// certificate for a given hostname, rustls_acceptor allows doing that asynchronously,
/// as opposed to rustls_server_config_builder_set_hello_callback(), which doesn't
/// work well for asynchronous I/O.
///
/// The general flow is:
/// - rustls_acceptor_new()
/// - Loop:
/// - Read bytes from the network it with rustls_acceptor_read_tls().
/// - If successful, parse those bytes with rustls_acceptor_accept().
/// - If that returns RUSTLS_RESULT_ACCEPTOR_NOT_READY, continue.
/// - Otherwise, break.
/// - If rustls_acceptor_accept() returned RUSTLS_RESULT_OK:
/// - Examine the resulting rustls_accepted.
/// - Create or select a rustls_server_config.
/// - Call rustls_accepted_into_connection().
/// - Otherwise, there was a problem with the ClientHello data and the
/// connection should be rejected.
pub struct rustls_acceptor(Acceptor);
}

impl Castable for rustls_acceptor {
type Ownership = OwnershipBox;
type RustType = Acceptor;
}

/// A parsed ClientHello produced by a rustls_acceptor. It is used to check
/// server name indication (SNI), ALPN protocols, signature schemes, and
/// cipher suites. It can be combined with a rustls_server_config to build a
/// rustls_connection.
pub struct rustls_accepted {
_private: [u8; 0],
}

impl Castable for rustls_accepted {
type Ownership = OwnershipBox;
type RustType = Option<Accepted>;
box_castable! {
/// A parsed ClientHello produced by a rustls_acceptor. It is used to check
/// server name indication (SNI), ALPN protocols, signature schemes, and
/// cipher suites. It can be combined with a rustls_server_config to build a
/// rustls_connection.
pub struct rustls_accepted(Option<Accepted>);
}

impl rustls_acceptor {
Expand Down Expand Up @@ -452,14 +442,9 @@ impl rustls_accepted {
}
}

/// Represents a TLS alert resulting from accepting a client.
pub struct rustls_accepted_alert {
_private: [u8; 0],
}

impl Castable for rustls_accepted_alert {
type Ownership = OwnershipBox;
type RustType = AcceptedAlert;
box_castable! {
/// Represents a TLS alert resulting from accepting a client.
pub struct rustls_accepted_alert(AcceptedAlert);
}

impl rustls_accepted_alert {
Expand Down
179 changes: 68 additions & 111 deletions src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,18 @@ use webpki::{RevocationCheckDepth, UnknownStatusPolicy};
use crate::error::{self, rustls_result};
use crate::rslice::{rustls_slice_bytes, rustls_str};
use crate::{
ffi_panic_boundary, free_arc, free_box, set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_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, Castable, OwnershipArc, OwnershipBox, OwnershipRef,
arc_castable, box_castable, ffi_panic_boundary, free_arc, free_box, ref_castable,
set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_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,
};
use rustls_result::{AlreadyUsed, NullParameter};

/// An X.509 certificate, as used in rustls.
/// Corresponds to `CertificateDer` in the Rust pki-types API.
/// <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.CertificateDer.html>
pub struct rustls_certificate<'a> {
_private: [u8; 0],
_marker: PhantomData<&'a ()>,
}

impl<'a> Castable for rustls_certificate<'a> {
type Ownership = OwnershipRef;
type RustType = CertificateDer<'a>;
ref_castable! {
/// An X.509 certificate, as used in rustls.
/// Corresponds to `CertificateDer` in the Rust pki-types API.
/// <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.CertificateDer.html>
pub struct rustls_certificate(CertificateDer<'a>);
}

/// Get the DER data of the certificate itself.
Expand All @@ -62,14 +57,9 @@ pub extern "C" fn rustls_certificate_get_der(
}
}

/// A cipher suite supported by rustls.
pub struct rustls_supported_ciphersuite {
_private: [u8; 0],
}

impl Castable for rustls_supported_ciphersuite {
type Ownership = OwnershipRef;
type RustType = SupportedCipherSuite;
ref_castable! {
/// A cipher suite supported by rustls.
pub struct rustls_supported_ciphersuite(SupportedCipherSuite);
}

impl rustls_supported_ciphersuite {
Expand Down Expand Up @@ -257,17 +247,12 @@ mod tests {
}
}

/// The complete chain of certificates to send during a TLS handshake,
/// plus a private key that matches the end-entity (leaf) certificate.
/// Corresponds to `CertifiedKey` in the Rust API.
/// <https://docs.rs/rustls/latest/rustls/sign/struct.CertifiedKey.html>
pub struct rustls_certified_key {
_private: [u8; 0],
}

impl Castable for rustls_certified_key {
type Ownership = OwnershipArc;
type RustType = CertifiedKey;
arc_castable! {
/// The complete chain of certificates to send during a TLS handshake,
/// plus a private key that matches the end-entity (leaf) certificate.
/// Corresponds to `CertifiedKey` in the Rust API.
/// <https://docs.rs/rustls/latest/rustls/sign/struct.CertifiedKey.html>
pub struct rustls_certified_key(CertifiedKey);
}

impl rustls_certified_key {
Expand Down Expand Up @@ -435,25 +420,20 @@ impl rustls_certified_key {
}
}

/// A `rustls_root_cert_store` being constructed.
///
/// A builder can be modified by adding trust anchor root certificates with
/// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates,
/// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`.
/// This object is not safe for concurrent mutation.
pub struct rustls_root_cert_store_builder {
_private: [u8; 0],
box_castable! {
/// A `rustls_root_cert_store` being constructed.
///
/// A builder can be modified by adding trust anchor root certificates with
/// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates,
/// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`.
/// This object is not safe for concurrent mutation.
pub struct rustls_root_cert_store_builder(Option<RootCertStoreBuilder>);
}

pub(crate) struct RootCertStoreBuilder {
roots: RootCertStore,
}

impl Castable for rustls_root_cert_store_builder {
type Ownership = OwnershipBox;
type RustType = Option<RootCertStoreBuilder>;
}

impl rustls_root_cert_store_builder {
/// Create a `rustls_root_cert_store_builder`.
///
Expand Down Expand Up @@ -612,15 +592,10 @@ impl rustls_root_cert_store_builder {
}
}

/// A root certificate store.
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html>
pub struct rustls_root_cert_store {
_private: [u8; 0],
}

impl Castable for rustls_root_cert_store {
type Ownership = OwnershipArc;
type RustType = RootCertStore;
arc_castable! {
/// A root certificate store.
/// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html>
pub struct rustls_root_cert_store(RootCertStore);
}

impl rustls_root_cert_store {
Expand All @@ -634,19 +609,15 @@ impl rustls_root_cert_store {
}
}

/// A built client certificate verifier that can be provided to a `rustls_server_config_builder`
/// with `rustls_server_config_builder_set_client_verifier`.
pub struct rustls_client_cert_verifier {
_private: [u8; 0],
}

/// Rustls' ConfigBuilder requires an `Arc<dyn ClientCertVerifier>` here, meaning we
/// must follow the pattern described in CONTRIBUTING.md[0] for handling dynamically sized
/// types (DSTs) across the FFI boundary.
/// [0] <https://github.com/rustls/rustls-ffi/blob/main/CONTRIBUTING.md#dynamically-sized-types>
impl Castable for rustls_client_cert_verifier {
type Ownership = OwnershipBox;
type RustType = Arc<dyn ClientCertVerifier>;
box_castable! {
/// A built client certificate verifier that can be provided to a `rustls_server_config_builder`
/// with `rustls_server_config_builder_set_client_verifier`.
//
// Rustls' ConfigBuilder requires an `Arc<dyn ClientCertVerifier>` here, meaning we
// must follow the pattern described in CONTRIBUTING.md[^0] for handling dynamically sized
// types (DSTs) across the FFI boundary.
// [^0]: <https://github.com/rustls/rustls-ffi/blob/main/CONTRIBUTING.md#dynamically-sized-types>
pub struct rustls_client_cert_verifier(Arc<dyn ClientCertVerifier>);
}

impl rustls_client_cert_verifier {
Expand All @@ -661,18 +632,6 @@ impl rustls_client_cert_verifier {
}
}

/// A client certificate verifier being constructed. A builder can be modified by,
/// e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're
/// done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build`
/// to turn it into a `rustls_client_cert_verifier`. This object is not safe
/// for concurrent mutation.
///
/// See <https://docs.rs/rustls/latest/rustls/server/struct.ClientCertVerifierBuilder.html>
/// for more information.
pub struct rustls_web_pki_client_cert_verifier_builder {
_private: [u8; 0],
}

pub(crate) struct ClientCertVerifierBuilder {
roots: Arc<RootCertStore>,
root_hint_subjects: Vec<DistinguishedName>,
Expand All @@ -682,9 +641,16 @@ pub(crate) struct ClientCertVerifierBuilder {
allow_unauthenticated: bool,
}

impl Castable for rustls_web_pki_client_cert_verifier_builder {
type Ownership = OwnershipBox;
type RustType = Option<ClientCertVerifierBuilder>;
box_castable! {
/// A client certificate verifier being constructed. A builder can be modified by,
/// e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're
/// done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build`
/// to turn it into a `rustls_client_cert_verifier`. This object is not safe
/// for concurrent mutation.
///
/// See <https://docs.rs/rustls/latest/rustls/server/struct.ClientCertVerifierBuilder.html>
/// for more information.
pub struct rustls_web_pki_client_cert_verifier_builder(Option<ClientCertVerifierBuilder>);
}

impl rustls_web_pki_client_cert_verifier_builder {
Expand Down Expand Up @@ -943,16 +909,16 @@ impl rustls_web_pki_client_cert_verifier_builder {
}
}

/// A server certificate verifier being constructed. A builder can be modified by,
/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're
/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build`
/// to turn it into a `rustls_server_cert_verifier`. This object is not safe
/// for concurrent mutation.
///
/// See <https://docs.rs/rustls/latest/rustls/client/struct.ServerCertVerifierBuilder.html>
/// for more information.
pub struct rustls_web_pki_server_cert_verifier_builder {
_private: [u8; 0],
box_castable! {
/// A server certificate verifier being constructed. A builder can be modified by,
/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're
/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build`
/// to turn it into a `rustls_server_cert_verifier`. This object is not safe
/// for concurrent mutation.
///
/// See <https://docs.rs/rustls/latest/rustls/client/struct.ServerCertVerifierBuilder.html>
/// for more information.
pub(crate) struct rustls_web_pki_server_cert_verifier_builder(Option<ServerCertVerifierBuilder>);
}

pub(crate) struct ServerCertVerifierBuilder {
Expand All @@ -962,11 +928,6 @@ pub(crate) struct ServerCertVerifierBuilder {
revocation_policy: UnknownStatusPolicy,
}

impl Castable for rustls_web_pki_server_cert_verifier_builder {
type Ownership = OwnershipBox;
type RustType = Option<ServerCertVerifierBuilder>;
}

impl ServerCertVerifierBuilder {
/// Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may
/// free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether
Expand Down Expand Up @@ -1145,19 +1106,15 @@ impl ServerCertVerifierBuilder {
}
}

/// A built server certificate verifier that can be provided to a `rustls_client_config_builder`
/// with `rustls_client_config_builder_set_server_verifier`.
pub struct rustls_server_cert_verifier {
_private: [u8; 0],
}

/// Rustls' ConfigBuilder requires an `Arc<dyn ServerCertVerifier>` here, meaning we
/// must follow the pattern described in CONTRIBUTING.md[0] for handling dynamically sized
/// types (DSTs) across the FFI boundary.
/// [0] <https://github.com/rustls/rustls-ffi/blob/main/CONTRIBUTING.md#dynamically-sized-types>
impl Castable for rustls_server_cert_verifier {
type Ownership = OwnershipBox;
type RustType = Arc<dyn ServerCertVerifier>;
box_castable! {
/// A built server certificate verifier that can be provided to a `rustls_client_config_builder`
/// with `rustls_client_config_builder_set_server_verifier`.
//
// Rustls' ConfigBuilder requires an `Arc<dyn ServerCertVerifier>` here, meaning we
// must follow the pattern described in CONTRIBUTING.md[^0] for handling dynamically sized
// types (DSTs) across the FFI boundary.
// [^0]: <https://github.com/rustls/rustls-ffi/blob/main/CONTRIBUTING.md#dynamically-sized-types>
pub struct rustls_server_cert_verifier(Arc<dyn ServerCertVerifier>);
}

impl rustls_server_cert_verifier {
Expand Down
Loading

0 comments on commit 5802aef

Please sign in to comment.