From 499cb354280f6d08477fb0482edc5168bdeafff2 Mon Sep 17 00:00:00 2001 From: Myriachan Date: Thu, 18 Dec 2025 12:08:47 -0800 Subject: [PATCH 1/4] Docs: CA cert files are only supported in OpenSSL. Added a note saying that `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE` is only supported in OpenSSL. It's supported in QuicTLS too, but that isn't mentioned for the other options. --- docs/api/QUIC_CREDENTIAL_CONFIG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/QUIC_CREDENTIAL_CONFIG.md b/docs/api/QUIC_CREDENTIAL_CONFIG.md index 0a8ed546fc..e4d183ff1f 100644 --- a/docs/api/QUIC_CREDENTIAL_CONFIG.md +++ b/docs/api/QUIC_CREDENTIAL_CONFIG.md @@ -160,6 +160,7 @@ Obtain the peer certificate using a faster in-process API call. Only available o `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE` Enable CA certificate file provided in the `CaCertificateFile` member. +Only valid for OpenSSL. `QUIC_CREDENTIAL_FLAG_DISABLE_AIA` From 9d4a8dfe57383786ed3014662dca2f41e9448deb Mon Sep 17 00:00:00 2001 From: Myriachan Date: Tue, 23 Dec 2025 15:53:49 -0800 Subject: [PATCH 2/4] Add support for in-memory certificate root stores. This adds new credential flag `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB` and new `QUIC_CREDENTIAL_CONFIG` fields `CaCertificateBlob` and `CaCertificateBlobLength`. Only the OpenSSL and quictls backends are supported for now. --- docs/api/QUIC_CREDENTIAL_CONFIG.md | 25 +++++- src/inc/msquic.h | 3 + src/platform/tls_openssl.c | 117 +++++++++++++++++++++++++++-- src/platform/tls_quictls.c | 117 +++++++++++++++++++++++++++-- src/platform/tls_schannel.c | 3 +- 5 files changed, 250 insertions(+), 15 deletions(-) diff --git a/docs/api/QUIC_CREDENTIAL_CONFIG.md b/docs/api/QUIC_CREDENTIAL_CONFIG.md index e4d183ff1f..cc58668ee6 100644 --- a/docs/api/QUIC_CREDENTIAL_CONFIG.md +++ b/docs/api/QUIC_CREDENTIAL_CONFIG.md @@ -22,6 +22,8 @@ typedef struct QUIC_CREDENTIAL_CONFIG { QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER AsyncHandler; // Optional QUIC_ALLOWED_CIPHER_SUITE_FLAGS AllowedCipherSuites;// Optional const char* CaCertificateFile; // Optional + const uint8_t* CaCertificateBlob; // Optional + uint32_t CaCertificateBlobLength; // Optional } QUIC_CREDENTIAL_CONFIG; ``` @@ -159,7 +161,15 @@ Obtain the peer certificate using a faster in-process API call. Only available o `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE` -Enable CA certificate file provided in the `CaCertificateFile` member. +Enable CA certificate store file whose filename is provided in the `CaCertificateFile` member. +This is a text file containing certificates in `-----BEGIN CERTIFICATE-----` / `-----END CERTIFICATE-----` blocks. +The file may also contain certificate revocation lists (CRLs) in `-----BEGIN X509 CRL-----` / `-----END X509 CRL-----` blocks. +Only valid for OpenSSL. + +`QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB` + +Enable CA certificate store blob provided in memory by the `CaCertificateBlob` and `CaCertificateBlobLength` members. +Its format is the same as used for `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE`. Only valid for OpenSSL. `QUIC_CREDENTIAL_FLAG_DISABLE_AIA` @@ -204,10 +214,21 @@ A set of flags indicating which cipher suites are available to negotiate. Must b #### `CaCertificateFile` -Optional pointer to CA certificate file that will be used when +Optional pointer to CA certificate filename that will be used when validating the peer certificate. This allows the use of a private CA. Must be used with `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE`. +#### `CaCertificateBlob` + +Optional pointer to CA certificate data that will be used when +validating the peer certificate. This allows the use of a private CA. +Must be used with `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB` and `CaCertificateBlobLength`. + +#### `CaCertificateBlobLength` + +Optional length of CA certificate data pointed to by `CaCertificateBlob`. +Must be used with `QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB` and `CaCertificateBlob`. + # Remarks TODO diff --git a/src/inc/msquic.h b/src/inc/msquic.h index a60a58892c..2501aaeef2 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -147,6 +147,7 @@ typedef enum QUIC_CREDENTIAL_FLAGS { QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE = 0x00080000, // Schannel only QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE = 0x00100000, // OpenSSL only currently QUIC_CREDENTIAL_FLAG_DISABLE_AIA = 0x00200000, // Schannel only currently + QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB = 0x00400000, // OpenSSL only currently } QUIC_CREDENTIAL_FLAGS; DEFINE_ENUM_FLAG_OPERATORS(QUIC_CREDENTIAL_FLAGS) @@ -416,6 +417,8 @@ typedef struct QUIC_CREDENTIAL_CONFIG { QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER AsyncHandler; // Optional QUIC_ALLOWED_CIPHER_SUITE_FLAGS AllowedCipherSuites;// Optional const char* CaCertificateFile; // Optional + const uint8_t* CaCertificateBlob; // Optional + uint32_t CaCertificateBlobLength; // Optional } QUIC_CREDENTIAL_CONFIG; // diff --git a/src/platform/tls_openssl.c b/src/platform/tls_openssl.c index c6c79db5e9..1fdbedf539 100644 --- a/src/platform/tls_openssl.c +++ b/src/platform/tls_openssl.c @@ -2135,20 +2135,125 @@ CxPlatTlsSecConfigCreate( } } - if (CredConfigFlags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE && + if ((CredConfigFlags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE) && CredConfig->CaCertificateFile) { + X509_STORE* CertStore = SSL_CTX_get_cert_store(SecurityConfig->SSLCtx); + if (CertStore == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "SSL_CTX_get_cert_store failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto Exit; + } + Ret = - SSL_CTX_load_verify_locations( - SecurityConfig->SSLCtx, - CredConfig->CaCertificateFile, - NULL); + X509_STORE_load_file( + CertStore, + CredConfig->CaCertificateFile); if (Ret != 1) { QuicTraceEvent( LibraryErrorStatus, "[ lib] ERROR, %u, %s.", ERR_get_error(), - "SSL_CTX_load_verify_locations failed"); + "X509_STORE_load_file failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto Exit; + } + } + + if (CredConfigFlags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB && + CredConfig->CaCertificateBlob) { + X509_STORE* CertStore = NULL; + BIO* CertBio = NULL; + STACK_OF(X509_INFO)* CertStack = NULL; + + Status = QUIC_STATUS_TLS_ERROR; + + // BIO_new_mem_buf() takes 'int' length + if (CredConfig->CaCertificateBlobLength > (unsigned)INT_MAX) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "CaCertificateBlobLength too large"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + CertStore = SSL_CTX_get_cert_store(SecurityConfig->SSLCtx); + if (CertStore == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "SSL_CTX_get_cert_store failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + CertBio = BIO_new_mem_buf(CredConfig->CaCertificateBlob, (int)CredConfig->CaCertificateBlobLength); + if (CertBio == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "BIO_new_mem_buf failed"); Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + CertStack = PEM_X509_INFO_read_bio(CertBio, NULL, NULL, NULL); + if (!CertStack) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "PEM_X509_INFO_read_bio failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + for (int i = 0, max = sk_X509_INFO_num(CertStack); i < max; i++) { + X509_INFO* CertInfo = sk_X509_INFO_value(CertStack, i); + if (CertInfo->x509) { + Ret = X509_STORE_add_cert(CertStore, CertInfo->x509); + if (!Ret) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "X509_STORE_add_cert failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + } + if (CertInfo->crl) { + Ret = X509_STORE_add_crl(CertStore, CertInfo->crl); + if (!Ret) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "X509_STORE_add_crl failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + } + } + + Status = QUIC_STATUS_SUCCESS; + + BlobExit: + if (CertStack != NULL) { + sk_X509_INFO_pop_free(CertStack, X509_INFO_free); + } + if (CertBio != NULL) { + BIO_free(CertBio); + } + + if (!QUIC_SUCCEEDED(Status)) { goto Exit; } } diff --git a/src/platform/tls_quictls.c b/src/platform/tls_quictls.c index 65131bf2ad..0a208a3f05 100644 --- a/src/platform/tls_quictls.c +++ b/src/platform/tls_quictls.c @@ -1484,20 +1484,125 @@ CxPlatTlsSecConfigCreate( } } - if (CredConfigFlags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE && + if ((CredConfigFlags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE) && CredConfig->CaCertificateFile) { + X509_STORE* CertStore = SSL_CTX_get_cert_store(SecurityConfig->SSLCtx); + if (CertStore == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "SSL_CTX_get_cert_store failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto Exit; + } + Ret = - SSL_CTX_load_verify_locations( - SecurityConfig->SSLCtx, - CredConfig->CaCertificateFile, - NULL); + X509_STORE_load_file( + CertStore, + CredConfig->CaCertificateFile); if (Ret != 1) { QuicTraceEvent( LibraryErrorStatus, "[ lib] ERROR, %u, %s.", ERR_get_error(), - "SSL_CTX_load_verify_locations failed"); + "X509_STORE_load_file failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto Exit; + } + } + + if (CredConfigFlags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB && + CredConfig->CaCertificateBlob) { + X509_STORE* CertStore = NULL; + BIO* CertBio = NULL; + STACK_OF(X509_INFO)* CertStack = NULL; + + Status = QUIC_STATUS_TLS_ERROR; + + // BIO_new_mem_buf() takes 'int' length + if (CredConfig->CaCertificateBlobLength > (unsigned)INT_MAX) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "CaCertificateBlobLength too large"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + CertStore = SSL_CTX_get_cert_store(SecurityConfig->SSLCtx); + if (CertStore == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "SSL_CTX_get_cert_store failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + CertBio = BIO_new_mem_buf(CredConfig->CaCertificateBlob, (int)CredConfig->CaCertificateBlobLength); + if (CertBio == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "BIO_new_mem_buf failed"); Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + CertStack = PEM_X509_INFO_read_bio(CertBio, NULL, NULL, NULL); + if (!CertStack) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "PEM_X509_INFO_read_bio failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + + for (int i = 0, max = sk_X509_INFO_num(CertStack); i < max; i++) { + X509_INFO* CertInfo = sk_X509_INFO_value(CertStack, i); + if (CertInfo->x509) { + Ret = X509_STORE_add_cert(CertStore, CertInfo->x509); + if (!Ret) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "X509_STORE_add_cert failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + } + if (CertInfo->crl) { + Ret = X509_STORE_add_crl(CertStore, CertInfo->crl); + if (!Ret) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + ERR_get_error(), + "X509_STORE_add_crl failed"); + Status = QUIC_STATUS_TLS_ERROR; + goto BlobExit; + } + } + } + + Status = QUIC_STATUS_SUCCESS; + + BlobExit: + if (CertStack != NULL) { + sk_X509_INFO_pop_free(CertStack, X509_INFO_free); + } + if (CertBio != NULL) { + BIO_free(CertBio); + } + + if (!QUIC_SUCCEEDED(Status)) { goto Exit; } } diff --git a/src/platform/tls_schannel.c b/src/platform/tls_schannel.c index 0661ccad1e..505371a877 100644 --- a/src/platform/tls_schannel.c +++ b/src/platform/tls_schannel.c @@ -1008,7 +1008,8 @@ CxPlatTlsSecConfigCreate( return QUIC_STATUS_INVALID_PARAMETER; } - if (CredConfig->Flags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE) { + if ((CredConfig->Flags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE) || + (CredConfig->Flags & QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB)) { return QUIC_STATUS_NOT_SUPPORTED; } From 8fb6faf8fd693ffdddc5882a04c0a0d560a5b7e7 Mon Sep 17 00:00:00 2001 From: Joshua Chapman Date: Mon, 12 Jan 2026 16:52:34 -0800 Subject: [PATCH 3/4] Ran codegen for C#/Rust --- src/cs/lib/msquic_generated.cs | 7 +++++++ src/rs/ffi/linux_bindings.rs | 10 +++++++++- src/rs/ffi/win_bindings.rs | 10 +++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 73f7059ff6..bdda6f065b 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -100,6 +100,7 @@ internal enum QUIC_CREDENTIAL_FLAGS INPROC_PEER_CERTIFICATE = 0x00080000, SET_CA_CERTIFICATE_FILE = 0x00100000, DISABLE_AIA = 0x00200000, + SET_CA_CERTIFICATE_BLOB = 0x00400000, } [System.Flags] @@ -328,6 +329,12 @@ internal unsafe partial struct QUIC_CREDENTIAL_CONFIG [NativeTypeName("const char *")] internal sbyte* CaCertificateFile; + [NativeTypeName("const uint8_t *")] + internal byte* CaCertificateBlob; + + [NativeTypeName("uint32_t")] + internal uint CaCertificateBlobLength; + internal ref QUIC_CERTIFICATE_HASH* CertificateHash { get diff --git a/src/rs/ffi/linux_bindings.rs b/src/rs/ffi/linux_bindings.rs index 93157fdee3..fb47cd6906 100644 --- a/src/rs/ffi/linux_bindings.rs +++ b/src/rs/ffi/linux_bindings.rs @@ -343,6 +343,8 @@ pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE: pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE: QUIC_CREDENTIAL_FLAGS = 1048576; pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_DISABLE_AIA: QUIC_CREDENTIAL_FLAGS = 2097152; +pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB: + QUIC_CREDENTIAL_FLAGS = 4194304; pub type QUIC_CREDENTIAL_FLAGS = ::std::os::raw::c_uint; pub const QUIC_ALLOWED_CIPHER_SUITE_FLAGS_QUIC_ALLOWED_CIPHER_SUITE_NONE: QUIC_ALLOWED_CIPHER_SUITE_FLAGS = 0; @@ -626,6 +628,8 @@ pub struct QUIC_CREDENTIAL_CONFIG { pub AsyncHandler: QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER, pub AllowedCipherSuites: QUIC_ALLOWED_CIPHER_SUITE_FLAGS, pub CaCertificateFile: *const ::std::os::raw::c_char, + pub CaCertificateBlob: *const u8, + pub CaCertificateBlobLength: u32, } #[repr(C)] #[derive(Copy, Clone)] @@ -662,7 +666,7 @@ const _: () = { }; #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of QUIC_CREDENTIAL_CONFIG"][::std::mem::size_of::() - 56usize]; + ["Size of QUIC_CREDENTIAL_CONFIG"][::std::mem::size_of::() - 72usize]; ["Alignment of QUIC_CREDENTIAL_CONFIG"] [::std::mem::align_of::() - 8usize]; ["Offset of field: QUIC_CREDENTIAL_CONFIG::Type"] @@ -679,6 +683,10 @@ const _: () = { [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, AllowedCipherSuites) - 40usize]; ["Offset of field: QUIC_CREDENTIAL_CONFIG::CaCertificateFile"] [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, CaCertificateFile) - 48usize]; + ["Offset of field: QUIC_CREDENTIAL_CONFIG::CaCertificateBlob"] + [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, CaCertificateBlob) - 56usize]; + ["Offset of field: QUIC_CREDENTIAL_CONFIG::CaCertificateBlobLength"] + [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, CaCertificateBlobLength) - 64usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/src/rs/ffi/win_bindings.rs b/src/rs/ffi/win_bindings.rs index 3097ac9c9e..b57fe49363 100644 --- a/src/rs/ffi/win_bindings.rs +++ b/src/rs/ffi/win_bindings.rs @@ -342,6 +342,8 @@ pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE: pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE: QUIC_CREDENTIAL_FLAGS = 1048576; pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_DISABLE_AIA: QUIC_CREDENTIAL_FLAGS = 2097152; +pub const QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB: + QUIC_CREDENTIAL_FLAGS = 4194304; pub type QUIC_CREDENTIAL_FLAGS = ::std::os::raw::c_int; pub const QUIC_ALLOWED_CIPHER_SUITE_FLAGS_QUIC_ALLOWED_CIPHER_SUITE_NONE: QUIC_ALLOWED_CIPHER_SUITE_FLAGS = 0; @@ -620,6 +622,8 @@ pub struct QUIC_CREDENTIAL_CONFIG { pub AsyncHandler: QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER, pub AllowedCipherSuites: QUIC_ALLOWED_CIPHER_SUITE_FLAGS, pub CaCertificateFile: *const ::std::os::raw::c_char, + pub CaCertificateBlob: *const u8, + pub CaCertificateBlobLength: u32, } #[repr(C)] #[derive(Copy, Clone)] @@ -656,7 +660,7 @@ const _: () = { }; #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of QUIC_CREDENTIAL_CONFIG"][::std::mem::size_of::() - 56usize]; + ["Size of QUIC_CREDENTIAL_CONFIG"][::std::mem::size_of::() - 72usize]; ["Alignment of QUIC_CREDENTIAL_CONFIG"] [::std::mem::align_of::() - 8usize]; ["Offset of field: QUIC_CREDENTIAL_CONFIG::Type"] @@ -673,6 +677,10 @@ const _: () = { [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, AllowedCipherSuites) - 40usize]; ["Offset of field: QUIC_CREDENTIAL_CONFIG::CaCertificateFile"] [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, CaCertificateFile) - 48usize]; + ["Offset of field: QUIC_CREDENTIAL_CONFIG::CaCertificateBlob"] + [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, CaCertificateBlob) - 56usize]; + ["Offset of field: QUIC_CREDENTIAL_CONFIG::CaCertificateBlobLength"] + [::std::mem::offset_of!(QUIC_CREDENTIAL_CONFIG, CaCertificateBlobLength) - 64usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] From 7aef9a2df6901bf5514e36a542335557715773ef Mon Sep 17 00:00:00 2001 From: Joshua Chapman Date: Mon, 12 Jan 2026 17:53:31 -0800 Subject: [PATCH 4/4] Added new field to config.rs --- src/rs/config.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/rs/config.rs b/src/rs/config.rs index fb6e9fe463..c8ffda1651 100644 --- a/src/rs/config.rs +++ b/src/rs/config.rs @@ -76,6 +76,7 @@ pub struct CredentialConfig { principal: Option, // TODO: support async handler. allowed_cipher_suites: AllowedCipherSuiteFlags, ca_certificate_file: Option, + ca_certificate_blob: Option, } impl CredentialConfig { @@ -111,6 +112,12 @@ impl CredentialConfig { self } + pub fn set_ca_certificate_blob(mut self, value: String) -> Self { + self.credential_flags |= CredentialFlags::SET_CA_CERTIFICATE_BLOB; + self.ca_certificate_blob = Some(CString::new(value).unwrap()); + self + } + /// # Safety /// ffi type returned needs to have the lifetime of self. pub(crate) unsafe fn as_ffi(&self) -> QUIC_CREDENTIAL_CONFIG { @@ -160,6 +167,13 @@ impl CredentialConfig { .as_ref() .map(|s| s.as_ptr()) .unwrap_or(std::ptr::null()); + let blob = self + .ca_certificate_blob + .as_ref() + .map(|s| s.as_bytes_with_nul()) + .unwrap_or([]); + ffi_cfg.CaCertificateBlob = blob.as_ptr(); + ffi_cfg.CaCertificateBlobLength = blob.len() as u32; ffi_cfg } @@ -381,6 +395,7 @@ pub struct CredentialFlags: crate::ffi::QUIC_CREDENTIAL_FLAGS { const INPROC_PEER_CERTIFICATE = crate::ffi::QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE; const SET_CA_CERTIFICATE_FILE = crate::ffi::QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE; const DISABLE_AIA = crate::ffi::QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_DISABLE_AIA; + const SET_CA_CERTIFICATE_BLOB = crate::ffi::QUIC_CREDENTIAL_FLAGS_QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_BLOB; // reject undefined flags. const _ = !0; }