From 840a56ffe935eaa0b8f3c7a79719f3dcad3e74e2 Mon Sep 17 00:00:00 2001 From: Javier Sagredo Date: Mon, 1 Jun 2026 00:58:02 +0200 Subject: [PATCH] Change to rustls and expose optional client certificate --- Cargo.lock | 110 ++++++++++++++- Cargo.toml | 8 +- build/instructions_template.rs | 2 +- src/lib/tls.pl | 48 +++---- src/machine/streams.rs | 69 +++++++++- src/machine/system_calls.rs | 236 ++++++++++++++++++++++++++++----- 6 files changed, 396 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fbb9e680..e1ad87f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,6 +145,29 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "backtrace" version = "0.3.76" @@ -284,6 +307,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -373,6 +398,15 @@ dependencies = [ "error-code", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -921,6 +955,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -1201,6 +1241,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "home" version = "0.5.12" @@ -1565,6 +1611,16 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.99" @@ -1888,10 +1944,10 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.2.1", "openssl-sys", "schannel", - "security-framework", + "security-framework 3.7.0", "security-framework-sys", "tempfile", ] @@ -2050,6 +2106,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + [[package]] name = "openssl-probe" version = "0.2.1" @@ -2574,7 +2636,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -2640,6 +2702,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ + "aws-lc-rs", "log", "ring", "rustls-pki-types", @@ -2648,6 +2711,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe 0.1.6", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework 2.11.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2681,9 +2757,10 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -2777,6 +2854,7 @@ name = "scryer-prolog" version = "0.10.0" dependencies = [ "arcu", + "aws-lc-rs", "base64 0.22.1", "bit-set", "bitvec", @@ -2799,6 +2877,7 @@ dependencies = [ "fxhash", "getrandom 0.2.17", "git-version", + "hex", "hostname", "iai-callgrind", "indexmap", @@ -2809,7 +2888,6 @@ dependencies = [ "libloading", "maplit", "modular-bitfield", - "native-tls", "num-order", "ordered-float", "ouroboros", @@ -2824,6 +2902,9 @@ dependencies = [ "ring", "ripemd", "roxmltree", + "rustls", + "rustls-native-certs", + "rustls-pemfile 2.2.0", "rustyline", "ryu", "scraper", @@ -2855,6 +2936,19 @@ version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework" version = "3.7.0" @@ -3622,6 +3716,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index dc17a2d9..37dcaf5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ all-simple-cross = ["all-pure", "ffi", "crypto-full"] ffi = ["dep:libffi"] repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"] hostname = ["dep:hostname"] -tls = ["dep:native-tls"] +tls = ["dep:rustls", "dep:rustls-native-certs", "dep:rustls-pemfile", "dep:aws-lc-rs", "dep:hex"] http = ["dep:warp", "dep:reqwest"] # crypto function that require non pure-rust dependencies crypto-full = ["dep:ring"] @@ -108,10 +108,14 @@ crossterm = { version = "0.28.1", optional = true } ctrlc = { version = "3.4.4", optional = true } hostname = { version = "0.4.0", optional = true } libffi = { version = "5.1.0", optional = true } -native-tls = { version = "0.2.12", optional = true } +rustls-pemfile = { version = "2", optional = true } # the version requirement of reqwest is kept low for compatibility with old deno versions # that pin reqwest to 0.11.20 reqwest = { version = "0.11.0", optional = true } +rustls = { version = "0.22", optional = true, features = ["aws_lc_rs"] } +aws-lc-rs = { version = "1.15", optional = true } +hex = { version = "0.4", optional = true } +rustls-native-certs = { version = "0.7", optional = true } rustyline = { version = "18.0.0", optional = true } tokio = { version = "1.39.2", features = ["full"] } warp = { version = "0.3.7", features = ["tls"], optional = true } diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 1a0f6cce..457de967 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -469,7 +469,7 @@ enum SystemClauseType { SocketServerClose, #[strum_discriminants(strum(props(Arity = "2", Name = "$copy_stream")))] CopyStream, - #[strum_discriminants(strum(props(Arity = "4", Name = "$tls_accept_client")))] + #[strum_discriminants(strum(props(Arity = "5", Name = "$tls_accept_client")))] TLSAcceptClient, #[strum_discriminants(strum(props(Arity = "3", Name = "$tls_client_connect")))] TLSClientConnect, diff --git a/src/lib/tls.pl b/src/lib/tls.pl index 3fae7aeb..0f3feb62 100644 --- a/src/lib/tls.pl +++ b/src/lib/tls.pl @@ -7,7 +7,7 @@ :- module(tls, [tls_client_context/2, % -Context, +Options tls_client_negotiate/3, % +Context, +Stream0, -Stream tls_server_context/2, % -Context, +Options - tls_server_negotiate/3 % +Context, +Stream0, -Stream + tls_server_negotiate/4 % +Context, +Stream0, -Stream, -ClientCert ]). :- use_module(library(lists)). @@ -52,31 +52,24 @@ tls_client_negotiate(tls_context(Host), S0, S) :- Use tls_server_context/2 to create a TLS context, for example with: - tls_server_context(Context, [pkcs12(Chars)]) + tls_server_context(Context, [certificate(Cert),key(Key)]) - where Chars is a list of characters with the contents of a - DER-formatted PKCS #12 archive. The option password(Ps) can be used - to specify the password Ps (also a string) for decrypting the key. - On some versions of OSX, and potentially also on other platforms, - empty passwords are not supported. + where Cert is a list of characters with the contents of a + PEM-encoded certificate chain, and Key is a list of characters with + the contents of the corresponding PEM-encoded private key. The + private key may be in PKCS #8, PKCS #1 (RSA) or SEC1 (EC) format. - The archive should contain a leaf certificate and its private key, - as well any intermediate certificates that should be sent to - clients to allow them to build a chain to a trusted root. The chain + The certificate chain should contain a leaf certificate followed by + any intermediate certificates that should be sent to clients to + allow them to build a chain to a trusted root. The chain certificates should be in order from the leaf certificate towards the root. - PKCS #12 archives typically have the file extension .p12 or .pfx, - and can be created with the OpenSSL pkcs12 tool: - - $ openssl pkcs12 -export -out identity.pfx \ - -inkey key.pem -in cert.pem -certfile chain_certs.pem - - You can use phrase_from_file/3 from library(pio) and seq//1 from - library(dcgs) to read the contents of "identity.pfx" into a string: + library(dcgs) to read the contents of "cert.pem" and "key.pem" into + strings: - phrase_from_file(seq(Chars), "identity.pfx", [type(binary)]) + phrase_from_file(seq(Cert), "cert.pem"), phrase_from_file(seq(Key), "key.pem") The obtained context should be treated as an opaque Prolog term. @@ -95,16 +88,15 @@ tls_client_negotiate(tls_context(Host), S0, S) :- is created for every connection, using the specified parameters. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -tls_server_context(tls_context(Cert,Password), Options) :- - ( member(pcks12(Cert), Options) -> +tls_server_context(tls_context(Cert,Key), Options) :- + ( member(certificate(Cert), Options) -> must_be(chars, Cert) - ; domain_error(contains_pcks12, Options, tls_server_context/2) + ; domain_error(contains_certificate, Options, tls_server_context/2) ), - ( member(password(Password), Options) -> - must_be(chars, Password) - ; Password = "" + ( member(key(Key), Options) -> + must_be(chars, Key) + ; domain_error(contains_key, Options, tls_server_context/2) ). -tls_server_negotiate(tls_context(Cert,Password), S0, S) :- - '$tls_accept_client'(Cert, Password, S0, S). - +tls_server_negotiate(tls_context(Cert,Key), S0, S, ClientCert) :- + '$tls_accept_client'(Cert, Key, S0, S, ClientCert). diff --git a/src/machine/streams.rs b/src/machine/streams.rs index a956a00c..3f6adc38 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -35,7 +35,7 @@ use std::sync::mpsc::Receiver; use std::sync::mpsc::TryRecvError; #[cfg(feature = "tls")] -use native_tls::TlsStream; +use rustls::{ClientConnection, ServerConnection, StreamOwned as RustlsStreamOwned}; #[cfg(feature = "http")] use warp::hyper; @@ -249,10 +249,65 @@ impl Write for NamedTcpStream { } #[cfg(feature = "tls")] -#[derive(Debug)] +pub enum TlsStream { + Client(RustlsStreamOwned), + Server(RustlsStreamOwned), +} + +#[cfg(feature = "tls")] +impl TlsStream { + #[inline] + pub fn send_close_notify(&mut self) { + match self { + TlsStream::Client(s) => s.conn.send_close_notify(), + TlsStream::Server(s) => s.conn.send_close_notify(), + } + } +} + +#[cfg(feature = "tls")] +impl Read for TlsStream { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self { + TlsStream::Client(s) => s.read(buf), + TlsStream::Server(s) => s.read(buf), + } + } +} + +#[cfg(feature = "tls")] +impl Write for TlsStream { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match self { + TlsStream::Client(s) => s.write(buf), + TlsStream::Server(s) => s.write(buf), + } + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + match self { + TlsStream::Client(s) => s.flush(), + TlsStream::Server(s) => s.flush(), + } + } +} + +#[cfg(feature = "tls")] pub struct NamedTlsStream { address: Atom, - tls_stream: TlsStream, + tls_stream: TlsStream, +} + +#[cfg(feature = "tls")] +impl fmt::Debug for NamedTlsStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NamedTlsStream") + .field("address", &self.address) + .finish_non_exhaustive() + } } #[cfg(feature = "tls")] @@ -1454,7 +1509,7 @@ impl Stream { #[inline] pub(crate) fn from_tls_stream( address: Atom, - tls_stream: TlsStream, + tls_stream: TlsStream, arena: &mut Arena, ) -> Self { Stream::NamedTls(arena_alloc!( @@ -1534,7 +1589,11 @@ impl Stream { tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both) } #[cfg(feature = "tls")] - Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(), + Stream::NamedTls(ref mut tls_stream) => { + let inner = tls_stream.inner_mut(); + inner.tls_stream.send_close_notify(); + inner.tls_stream.flush() + } #[cfg(feature = "http")] Stream::HttpRead(ref mut http_stream) => { http_stream.drop_payload(); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 68e3f79e..ffd7a29f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -59,8 +59,10 @@ use std::process::Stdio; #[cfg(feature = "http")] use std::str::FromStr; use std::sync::LazyLock; +#[cfg(any(feature = "http", feature = "tls"))] +use std::sync::Arc; #[cfg(feature = "http")] -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Condvar, Mutex}; use chrono::{offset::Local, DateTime}; #[cfg(not(target_arch = "wasm32"))] @@ -86,7 +88,13 @@ use crrl::{ed25519, secp256k1, x25519}; pub(crate) mod special_math; #[cfg(feature = "tls")] -use native_tls::{Identity, TlsAcceptor, TlsConnector}; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName}; +#[cfg(feature = "tls")] +use rustls::{DistinguishedName, ClientConfig, ClientConnection, RootCertStore, ServerConfig, ServerConnection, SignatureScheme}; +#[cfg(feature = "tls")] +use aws_lc_rs; +#[cfg(feature = "tls")] +use rustls::server::danger::*; use base64; use roxmltree; @@ -7219,9 +7227,30 @@ impl Machine { 3, )?; - let connector = TlsConnector::new().unwrap(); - let stream = match connector.connect(&hostname.as_str(), stream0) { - Ok(tls_stream) => tls_stream, + let mut roots = RootCertStore::empty(); + if let Ok(certs) = rustls_native_certs::load_native_certs() { + for cert in certs { + let _ = roots.add(cert); + } + } + + let config = ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + + let server_name = match ServerName::try_from(hostname.as_str().to_string()) { + Ok(name) => name, + Err(_) => { + return Err(self.machine_st.open_permission_error( + self.machine_st.registers[1], + atom!("tls_client_negotiate"), + 3, + )); + } + }; + + let mut conn = match ClientConnection::new(Arc::new(config), server_name) { + Ok(conn) => conn, Err(_) => { return Err(self.machine_st.open_permission_error( self.machine_st.registers[1], @@ -7231,8 +7260,21 @@ impl Machine { } }; + let mut sock = stream0; + if conn.complete_io(&mut sock).is_err() { + return Err(self.machine_st.open_permission_error( + self.machine_st.registers[1], + atom!("tls_client_negotiate"), + 3, + )); + } + + let tls_stream = crate::machine::streams::TlsStream::Client( + rustls::StreamOwned::new(conn, sock), + ); + let addr = atom!("TLS"); - let stream = Stream::from_tls_stream(addr, stream, &mut self.machine_st.arena); + let stream = Stream::from_tls_stream(addr, tls_stream, &mut self.machine_st.arena); self.indices .add_stream(stream, atom!("tls_client_negotiate"), 3) @@ -7251,15 +7293,13 @@ impl Machine { #[cfg(feature = "tls")] #[inline(always)] pub(crate) fn tls_accept_client(&mut self) -> CallResult { - let pkcs12 = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); + let cert_pem = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); + let key_pem = self.string_encoding_bytes(self.machine_st.registers[2], atom!("octet")); - if let Some(password) = self - .machine_st - .value_to_str_like(self.machine_st.registers[2]) - { - let identity = match Identity::from_pkcs12(&pkcs12, &password.as_str()) { - Ok(identity) => identity, - Err(_) => { + let cert_chain: Vec> = + match rustls_pemfile::certs(&mut &cert_pem[..]).collect::, _>>() { + Ok(certs) if !certs.is_empty() => certs, + _ => { return Err(self.machine_st.open_permission_error( self.machine_st.registers[1], atom!("tls_server_negotiate"), @@ -7268,39 +7308,103 @@ impl Machine { } }; - let stream0 = self.machine_st.get_stream_or_alias( - self.machine_st.registers[3], - &self.indices, - atom!("tls_server_negotiate"), - 3, - )?; - - let acceptor = TlsAcceptor::new(identity).unwrap(); - - let stream = match acceptor.accept(stream0) { - Ok(tls_stream) => tls_stream, - Err(_) => { + let private_key: PrivateKeyDer<'static> = + match rustls_pemfile::private_key(&mut &key_pem[..]) { + Ok(Some(key)) => key, + _ => { return Err(self.machine_st.open_permission_error( - self.machine_st.registers[3], + self.machine_st.registers[2], atom!("tls_server_negotiate"), 3, )); } }; - let stream = Stream::from_tls_stream(atom!("TLS"), stream, &mut self.machine_st.arena); + let stream0 = self.machine_st.get_stream_or_alias( + self.machine_st.registers[3], + &self.indices, + atom!("tls_server_negotiate"), + 3, + )?; - self.indices - .add_stream(stream, atom!("tls_server_negotiate"), 3) - .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + let config = match ServerConfig::builder() + .with_client_cert_verifier(Arc::new(GeminiClientVerifier)) + .with_single_cert(cert_chain, private_key) + { + Ok(config) => config, + Err(_) => { + return Err(self.machine_st.open_permission_error( + self.machine_st.registers[1], + atom!("tls_server_negotiate"), + 3, + )); + } + }; - let stream_addr = self.deref_register(4); - self.machine_st - .bind(stream_addr.as_var().unwrap(), stream.into()); - } else { - unreachable!(); + let mut conn = match ServerConnection::new(Arc::new(config)) { + Ok(conn) => conn, + Err(_) => { + return Err(self.machine_st.open_permission_error( + self.machine_st.registers[3], + atom!("tls_server_negotiate"), + 3, + )); + } + }; + + + + + let mut sock = stream0; + if conn.complete_io(&mut sock).is_err() { + return Err(self.machine_st.open_permission_error( + self.machine_st.registers[3], + atom!("tls_server_negotiate"), + 3, + )); } + + let client_digest = conn.peer_certificates().and_then(|peer_certs| { + peer_certs.first().map(|client_cert| { + let cert_bytes = client_cert.as_ref(); + aws_lc_rs::digest::digest(&aws_lc_rs::digest::SHA256, cert_bytes) + }) + }); + + let tls_stream = crate::machine::streams::TlsStream::Server( + rustls::StreamOwned::new(conn, sock), + ); + let stream = Stream::from_tls_stream(atom!("TLS"), tls_stream, &mut self.machine_st.arena); + + self.indices + .add_stream(stream, atom!("tls_server_negotiate"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + + let stream_addr = self.deref_register(4); + self.machine_st + .bind(stream_addr.as_var().unwrap(), stream.into()); + + let digest_cell = match client_digest { + Some(digest) => { + let bytes = digest.as_ref(); + resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + bytes.len(), + bytes + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) + } + None => atom_as_cell!(atom!("none")), + }; + + self.machine_st + .bind(self.deref_register(5).as_var().unwrap(), digest_cell); + Ok(()) } @@ -9672,3 +9776,63 @@ impl hkdf::KeyType for MyKey { self.0 } } + +#[derive(Debug)] +struct GeminiClientVerifier; + +impl ClientCertVerifier for GeminiClientVerifier { + // 1. Indica al servidor que solicite el certificado de cliente + fn offer_client_auth(&self) -> bool { + true + } + + // 2. Crucial para Gemini: No hace obligatorio el certificado (permite anónimos) + fn client_auth_mandatory(&self) -> bool { + false + } + + // 3. No enviamos ninguna pista de CA al cliente (le sirve cualquier certificado) + fn root_hint_subjects(&self) -> &[DistinguishedName] { + &[] + } + + // 4. Aceptamos el certificado tal cual venga, sin validar firmas ni caducidad + fn verify_client_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _now: rustls::pki_types::UnixTime, + ) -> Result { + // En Gemini, confiamos en el certificado "as-is" (TOFU) + Ok(ClientCertVerified::assertion()) + } + + // Requerido por el trait para saber qué algoritmos soporta el verificador + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + // Obtenemos el proveedor criptográfico por defecto (AWS-LC) + let provider = rustls::crypto::aws_lc_rs::default_provider(); + + // Devolvemos todos los esquemas de firma que el proveedor sabe manejar + provider + .signature_verification_algorithms + .supported_schemes() + } +} -- 2.54.0