Revert "Revert "Remove support for SCT stapling""

This reverts commit 777cc07a4b.
This commit is contained in:
Joseph Birr-Pixton 2023-07-06 11:08:05 +01:00 committed by ctz
parent 06b44be5d2
commit 1d659a4689
25 changed files with 46 additions and 529 deletions

View File

@ -23,6 +23,8 @@ If you'd like to help out, please see [CONTRIBUTING.md](CONTRIBUTING.md).
* Next release: * Next release:
- `RootCertStore::add_parsable_certificates` now takes a - `RootCertStore::add_parsable_certificates` now takes a
`impl IntoIterator<Item = impl AsRef<[u8]>>`. `impl IntoIterator<Item = impl AsRef<[u8]>>`.
- *Breaking change*: remove support for SCT stapling. Ecosystem support for this is rare compared to
inclusion of SCTs in certificates.
* Release 0.21.3 (2023-07-05) * Release 0.21.3 (2023-07-05)
- Added `with_crls` function to `AllowAnyAuthenticatedClient` and - Added `with_crls` function to `AllowAnyAuthenticatedClient` and
`AllowAnyAnonymousOrAuthenticatedClient` client certificate verifiers to `AllowAnyAnonymousOrAuthenticatedClient` client certificate verifiers to
@ -120,8 +122,6 @@ obsolete cryptography.
* Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)). * Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)).
* Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)). * Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)).
* OCSP stapling by servers. * OCSP stapling by servers.
* SCT stapling by servers.
* SCT verification by clients.
## Possible future features ## Possible future features

View File

@ -40,6 +40,11 @@
"EmptyExtensions-ServerHello-TLS12": "", "EmptyExtensions-ServerHello-TLS12": "",
"Server-JDK11*": "workarounds for oracle engineering quality", "Server-JDK11*": "workarounds for oracle engineering quality",
"Client-RejectJDK11DowngradeRandom": "", "Client-RejectJDK11DowngradeRandom": "",
"SendUnsolicitedSCTOnCertificate-TLS13": "SCT stapling not supported",
"SignedCertificateTimestampListEmpty-Client-*": "",
"SignedCertificateTimestampListEmptySCT-Client-*": "",
"SendSCTListOnResume-TLS-TLS12": "",
"IgnoreExtensionsOnIntermediates-TLS13": "assumes SCT support",
"CBCRecordSplitting*": "insane ciphersuites", "CBCRecordSplitting*": "insane ciphersuites",
"*CBCPadding*": "", "*CBCPadding*": "",
"RSAEphemeralKey": "", "RSAEphemeralKey": "",
@ -322,7 +327,6 @@
"NegotiatePSKResumption-TLS13": ":PEER_MISBEHAVIOUR:", "NegotiatePSKResumption-TLS13": ":PEER_MISBEHAVIOUR:",
"PointFormat-Client-MissingUncompressed": ":PEER_MISBEHAVIOUR:", "PointFormat-Client-MissingUncompressed": ":PEER_MISBEHAVIOUR:",
"SendUnsolicitedOCSPOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:", "SendUnsolicitedOCSPOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:",
"SendUnsolicitedSCTOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:",
"UnsolicitedServerNameAck-TLS-TLS12": ":PEER_MISBEHAVIOUR:", "UnsolicitedServerNameAck-TLS-TLS12": ":PEER_MISBEHAVIOUR:",
"UnsolicitedServerNameAck-TLS-TLS13": ":PEER_MISBEHAVIOUR:", "UnsolicitedServerNameAck-TLS-TLS13": ":PEER_MISBEHAVIOUR:",
"TicketSessionIDLength-33-TLS-TLS12": ":BAD_HANDSHAKE_MSG:", "TicketSessionIDLength-33-TLS-TLS12": ":BAD_HANDSHAKE_MSG:",
@ -332,10 +336,6 @@
"Ed25519DefaultDisable-NoAccept": ":PEER_MISBEHAVIOUR:", "Ed25519DefaultDisable-NoAccept": ":PEER_MISBEHAVIOUR:",
"SendUnknownExtensionOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:", "SendUnknownExtensionOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:",
"SendDuplicateExtensionsOnCerts-TLS13": ":PEER_MISBEHAVIOUR:", "SendDuplicateExtensionsOnCerts-TLS13": ":PEER_MISBEHAVIOUR:",
"SignedCertificateTimestampListEmpty-Client-TLS-TLS12": ":PEER_MISBEHAVIOUR:",
"SignedCertificateTimestampListEmpty-Client-TLS-TLS13": ":PEER_MISBEHAVIOUR:",
"SignedCertificateTimestampListEmptySCT-Client-TLS-TLS12": ":PEER_MISBEHAVIOUR:",
"SignedCertificateTimestampListEmptySCT-Client-TLS-TLS13": ":PEER_MISBEHAVIOUR:",
"EMS-Forbidden-TLS13": ":PEER_MISBEHAVIOUR:", "EMS-Forbidden-TLS13": ":PEER_MISBEHAVIOUR:",
"Unclean-Shutdown": ":CLOSE_WITHOUT_CLOSE_NOTIFY:", "Unclean-Shutdown": ":CLOSE_WITHOUT_CLOSE_NOTIFY:",
"SendExtensionOnClientCertificate-TLS13": ":PEER_MISBEHAVIOUR:", "SendExtensionOnClientCertificate-TLS13": ":PEER_MISBEHAVIOUR:",

View File

@ -344,7 +344,6 @@ mod danger {
_end_entity: &rustls::Certificate, _end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate], _intermediates: &[rustls::Certificate],
_server_name: &rustls::ServerName, _server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp: &[u8], _ocsp: &[u8],
_now: std::time::SystemTime, _now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> { ) -> Result<rustls::client::ServerCertVerified, rustls::Error> {

View File

@ -622,7 +622,7 @@ fn make_config(args: &Args) -> Arc<rustls::ServerConfig> {
.with_protocol_versions(&versions) .with_protocol_versions(&versions)
.expect("inconsistent cipher-suites/versions specified") .expect("inconsistent cipher-suites/versions specified")
.with_client_cert_verifier(client_auth) .with_client_cert_verifier(client_auth)
.with_single_cert_with_ocsp_and_sct(certs, privkey, ocsp, vec![]) .with_single_cert_with_ocsp(certs, privkey, ocsp)
.expect("bad certificates/private key"); .expect("bad certificates/private key");
config.key_log = Arc::new(rustls::KeyLogFile::new()); config.key_log = Arc::new(rustls::KeyLogFile::new());

View File

@ -18,7 +18,6 @@ rustversion = { version = "1.0.6", optional = true }
[dependencies] [dependencies]
log = { version = "0.4.4", optional = true } log = { version = "0.4.4", optional = true }
ring = "0.16.20" ring = "0.16.20"
sct = "0.7.0"
webpki = { package = "rustls-webpki", version = "0.101.0", features = ["alloc", "std"] } webpki = { package = "rustls-webpki", version = "0.101.0", features = ["alloc", "std"] }
[features] [features]

View File

@ -50,7 +50,6 @@ struct Options {
check_close_notify: bool, check_close_notify: bool,
host_name: String, host_name: String,
use_sni: bool, use_sni: bool,
send_sct: bool,
key_file: String, key_file: String,
cert_file: String, cert_file: String,
protocols: Vec<String>, protocols: Vec<String>,
@ -60,7 +59,6 @@ struct Options {
min_version: Option<ProtocolVersion>, min_version: Option<ProtocolVersion>,
max_version: Option<ProtocolVersion>, max_version: Option<ProtocolVersion>,
server_ocsp_response: Vec<u8>, server_ocsp_response: Vec<u8>,
server_sct_list: Vec<u8>,
use_signing_scheme: u16, use_signing_scheme: u16,
curves: Option<Vec<u16>>, curves: Option<Vec<u16>>,
export_keying_material: usize, export_keying_material: usize,
@ -91,7 +89,6 @@ impl Options {
resume_with_tickets_disabled: false, resume_with_tickets_disabled: false,
host_name: "example.com".to_string(), host_name: "example.com".to_string(),
use_sni: false, use_sni: false,
send_sct: false,
queue_data: false, queue_data: false,
queue_data_on_resume: false, queue_data_on_resume: false,
only_write_one_byte_after_handshake: false, only_write_one_byte_after_handshake: false,
@ -109,7 +106,6 @@ impl Options {
min_version: None, min_version: None,
max_version: None, max_version: None,
server_ocsp_response: vec![], server_ocsp_response: vec![],
server_sct_list: vec![],
use_signing_scheme: 0, use_signing_scheme: 0,
curves: None, curves: None,
export_keying_material: 0, export_keying_material: 0,
@ -215,9 +211,7 @@ impl server::ClientCertVerifier for DummyClientAuth {
} }
} }
struct DummyServerAuth { struct DummyServerAuth {}
send_sct: bool,
}
impl client::ServerCertVerifier for DummyServerAuth { impl client::ServerCertVerifier for DummyServerAuth {
fn verify_server_cert( fn verify_server_cert(
@ -225,16 +219,11 @@ impl client::ServerCertVerifier for DummyServerAuth {
_end_entity: &Certificate, _end_entity: &Certificate,
_certs: &[Certificate], _certs: &[Certificate],
_hostname: &ServerName, _hostname: &ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp: &[u8], _ocsp: &[u8],
_now: SystemTime, _now: SystemTime,
) -> Result<client::ServerCertVerified, Error> { ) -> Result<client::ServerCertVerified, Error> {
Ok(client::ServerCertVerified::assertion()) Ok(client::ServerCertVerified::assertion())
} }
fn request_scts(&self) -> bool {
self.send_sct
}
} }
struct FixedSignatureSchemeSigningKey { struct FixedSignatureSchemeSigningKey {
@ -418,12 +407,7 @@ fn make_server_cfg(opts: &Options) -> Arc<ServerConfig> {
.with_protocol_versions(&opts.supported_versions()) .with_protocol_versions(&opts.supported_versions())
.unwrap() .unwrap()
.with_client_cert_verifier(client_auth) .with_client_cert_verifier(client_auth)
.with_single_cert_with_ocsp_and_sct( .with_single_cert_with_ocsp(cert.clone(), key, opts.server_ocsp_response.clone())
cert.clone(),
key,
opts.server_ocsp_response.clone(),
opts.server_sct_list.clone(),
)
.unwrap(); .unwrap();
cfg.session_storage = ServerCacheWithResumptionDelay::new(opts.resumption_delay); cfg.session_storage = ServerCacheWithResumptionDelay::new(opts.resumption_delay);
@ -538,9 +522,7 @@ fn make_client_cfg(opts: &Options) -> Arc<ClientConfig> {
.with_kx_groups(&kx_groups) .with_kx_groups(&kx_groups)
.with_protocol_versions(&opts.supported_versions()) .with_protocol_versions(&opts.supported_versions())
.expect("inconsistent settings") .expect("inconsistent settings")
.with_custom_certificate_verifier(Arc::new(DummyServerAuth { .with_custom_certificate_verifier(Arc::new(DummyServerAuth {}));
send_sct: opts.send_sct,
}));
let mut cfg = if !opts.cert_file.is_empty() && !opts.key_file.is_empty() { let mut cfg = if !opts.cert_file.is_empty() && !opts.key_file.is_empty() {
let cert = load_cert(&opts.cert_file); let cert = load_cert(&opts.cert_file);
@ -991,6 +973,7 @@ fn main() {
"-on-resume-expect-no-offer-early-data" | "-on-resume-expect-no-offer-early-data" |
"-key-update" | //< we could implement an API for this "-key-update" | //< we could implement an API for this
"-expect-tls13-downgrade" | "-expect-tls13-downgrade" |
"-enable-signed-cert-timestamps" |
"-expect-session-id" => { "-expect-session-id" => {
println!("not checking {}; NYI", arg); println!("not checking {}; NYI", arg);
} }
@ -1020,16 +1003,6 @@ fn main() {
opts.server_ocsp_response = BASE64_STANDARD.decode(args.remove(0).as_bytes()) opts.server_ocsp_response = BASE64_STANDARD.decode(args.remove(0).as_bytes())
.expect("invalid base64"); .expect("invalid base64");
} }
"-signed-cert-timestamps" => {
opts.server_sct_list = BASE64_STANDARD.decode(args.remove(0).as_bytes())
.expect("invalid base64");
if opts.server_sct_list.len() == 2 &&
opts.server_sct_list[0] == 0x00 &&
opts.server_sct_list[1] == 0x00 {
quit(":INVALID_SCT_LIST:");
}
}
"-select-alpn" => { "-select-alpn" => {
opts.protocols.push(args.remove(0)); opts.protocols.push(args.remove(0));
} }
@ -1065,9 +1038,6 @@ fn main() {
"-use-null-client-ca-list" => { "-use-null-client-ca-list" => {
opts.offer_no_client_cas = true; opts.offer_no_client_cas = true;
} }
"-enable-signed-cert-timestamps" => {
opts.send_sct = true;
}
"-enable-early-data" => { "-enable-early-data" => {
opts.tickets = false; opts.tickets = false;
opts.enable_early_data = true; opts.enable_early_data = true;
@ -1200,6 +1170,7 @@ fn main() {
"-wpa-202304" | "-wpa-202304" |
"-srtp-profiles" | "-srtp-profiles" |
"-permute-extensions" | "-permute-extensions" |
"-signed-cert-timestamps" |
"-on-initial-expect-peer-cert-file" => { "-on-initial-expect-peer-cert-file" => {
println!("NYI option {:?}", arg); println!("NYI option {:?}", arg);
process::exit(BOGO_NACK); process::exit(BOGO_NACK);

View File

@ -4,27 +4,26 @@ use crate::error::Error;
use crate::key_log::NoKeyLog; use crate::key_log::NoKeyLog;
use crate::kx::SupportedKxGroup; use crate::kx::SupportedKxGroup;
use crate::suites::SupportedCipherSuite; use crate::suites::SupportedCipherSuite;
use crate::verify::{self, CertificateTransparencyPolicy}; use crate::verify;
use crate::{anchors, key, versions}; use crate::{anchors, key, versions};
use super::client_conn::Resumption; use super::client_conn::Resumption;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime;
impl ConfigBuilder<ClientConfig, WantsVerifier> { impl ConfigBuilder<ClientConfig, WantsVerifier> {
/// Choose how to verify server certificates. /// Choose how to verify server certificates.
pub fn with_root_certificates( pub fn with_root_certificates(
self, self,
root_store: anchors::RootCertStore, root_store: anchors::RootCertStore,
) -> ConfigBuilder<ClientConfig, WantsTransparencyPolicyOrClientCert> { ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
ConfigBuilder { ConfigBuilder {
state: WantsTransparencyPolicyOrClientCert { state: WantsClientCert {
cipher_suites: self.state.cipher_suites, cipher_suites: self.state.cipher_suites,
kx_groups: self.state.kx_groups, kx_groups: self.state.kx_groups,
versions: self.state.versions, versions: self.state.versions,
root_store, verifier: Arc::new(verify::WebPkiVerifier::new(root_store)),
}, },
side: PhantomData, side: PhantomData,
} }
@ -48,90 +47,6 @@ impl ConfigBuilder<ClientConfig, WantsVerifier> {
} }
} }
/// A config builder state where the caller needs to supply a certificate transparency policy or
/// client certificate resolver.
///
/// In this state, the caller can optionally enable certificate transparency, or ignore CT and
/// invoke one of the methods related to client certificates (as in the [`WantsClientCert`] state).
///
/// For more information, see the [`ConfigBuilder`] documentation.
#[derive(Clone, Debug)]
pub struct WantsTransparencyPolicyOrClientCert {
cipher_suites: Vec<SupportedCipherSuite>,
kx_groups: Vec<&'static SupportedKxGroup>,
versions: versions::EnabledVersions,
root_store: anchors::RootCertStore,
}
impl ConfigBuilder<ClientConfig, WantsTransparencyPolicyOrClientCert> {
/// Set Certificate Transparency logs to use for server certificate validation.
///
/// Because Certificate Transparency logs are sharded on a per-year basis and can be trusted or
/// distrusted relatively quickly, rustls stores a validation deadline. Server certificates will
/// be validated against the configured CT logs until the deadline expires. After the deadline,
/// certificates will no longer be validated, and a warning message will be logged. The deadline
/// may vary depending on how often you deploy builds with updated dependencies.
pub fn with_certificate_transparency_logs(
self,
logs: &'static [&'static sct::Log],
validation_deadline: SystemTime,
) -> ConfigBuilder<ClientConfig, WantsClientCert> {
self.with_logs(Some(CertificateTransparencyPolicy::new(
logs,
validation_deadline,
)))
}
/// Sets a single certificate chain and matching private key for use
/// in client authentication.
///
/// `cert_chain` is a vector of DER-encoded certificates.
/// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key.
///
/// This function fails if `key_der` is invalid.
pub fn with_single_cert(
self,
cert_chain: Vec<key::Certificate>,
key_der: key::PrivateKey,
) -> Result<ClientConfig, Error> {
self.with_logs(None)
.with_single_cert(cert_chain, key_der)
}
/// Do not support client auth.
pub fn with_no_client_auth(self) -> ClientConfig {
self.with_logs(None)
.with_client_cert_resolver(Arc::new(handy::FailResolveClientCert {}))
}
/// Sets a custom [`ResolvesClientCert`].
pub fn with_client_cert_resolver(
self,
client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
) -> ClientConfig {
self.with_logs(None)
.with_client_cert_resolver(client_auth_cert_resolver)
}
fn with_logs(
self,
ct_policy: Option<CertificateTransparencyPolicy>,
) -> ConfigBuilder<ClientConfig, WantsClientCert> {
ConfigBuilder {
state: WantsClientCert {
cipher_suites: self.state.cipher_suites,
kx_groups: self.state.kx_groups,
versions: self.state.versions,
verifier: Arc::new(verify::WebPkiVerifier::new(
self.state.root_store,
ct_policy,
)),
},
side: PhantomData,
}
}
}
/// A config builder state where the caller needs to supply whether and how to provide a client /// A config builder state where the caller needs to supply whether and how to provide a client
/// certificate. /// certificate.
/// ///

View File

@ -2,8 +2,8 @@ use super::ResolvesClientCert;
#[cfg(feature = "logging")] #[cfg(feature = "logging")]
use crate::log::{debug, trace}; use crate::log::{debug, trace};
use crate::msgs::enums::ExtensionType; use crate::msgs::enums::ExtensionType;
use crate::msgs::handshake::ServerExtension;
use crate::msgs::handshake::{CertificatePayload, DistinguishedName}; use crate::msgs::handshake::{CertificatePayload, DistinguishedName};
use crate::msgs::handshake::{Sct, ServerExtension};
use crate::{sign, SignatureScheme}; use crate::{sign, SignatureScheme};
use std::sync::Arc; use std::sync::Arc;
@ -12,29 +12,15 @@ use std::sync::Arc;
pub(super) struct ServerCertDetails { pub(super) struct ServerCertDetails {
pub(super) cert_chain: CertificatePayload, pub(super) cert_chain: CertificatePayload,
pub(super) ocsp_response: Vec<u8>, pub(super) ocsp_response: Vec<u8>,
pub(super) scts: Option<Vec<Sct>>,
} }
impl ServerCertDetails { impl ServerCertDetails {
pub(super) fn new( pub(super) fn new(cert_chain: CertificatePayload, ocsp_response: Vec<u8>) -> Self {
cert_chain: CertificatePayload,
ocsp_response: Vec<u8>,
scts: Option<Vec<Sct>>,
) -> Self {
Self { Self {
cert_chain, cert_chain,
ocsp_response, ocsp_response,
scts,
} }
} }
pub(super) fn scts(&self) -> impl Iterator<Item = &[u8]> {
self.scts
.as_deref()
.unwrap_or(&[])
.iter()
.map(|payload| payload.as_ref())
}
} }
pub(super) struct ClientHelloDetails { pub(super) struct ClientHelloDetails {
@ -48,11 +34,6 @@ impl ClientHelloDetails {
} }
} }
pub(super) fn server_may_send_sct_list(&self) -> bool {
self.sent_extensions
.contains(&ExtensionType::SCT)
}
pub(super) fn server_sent_unsolicited_extensions( pub(super) fn server_sent_unsolicited_extensions(
&self, &self,
received_exts: &[ServerExtension], received_exts: &[ServerExtension],

View File

@ -13,7 +13,7 @@ use crate::msgs::base::Payload;
use crate::msgs::enums::{Compression, ExtensionType}; use crate::msgs::enums::{Compression, ExtensionType};
use crate::msgs::enums::{ECPointFormat, PSKKeyExchangeMode}; use crate::msgs::enums::{ECPointFormat, PSKKeyExchangeMode};
use crate::msgs::handshake::ConvertProtocolNameList; use crate::msgs::handshake::ConvertProtocolNameList;
use crate::msgs::handshake::{CertificateStatusRequest, ClientSessionTicket, Sct}; use crate::msgs::handshake::{CertificateStatusRequest, ClientSessionTicket};
use crate::msgs::handshake::{ClientExtension, HasServerExtensions}; use crate::msgs::handshake::{ClientExtension, HasServerExtensions};
use crate::msgs::handshake::{ClientHelloPayload, HandshakeMessagePayload, HandshakePayload}; use crate::msgs::handshake::{ClientHelloPayload, HandshakeMessagePayload, HandshakePayload};
use crate::msgs::handshake::{HelloRetryRequest, KeyShareEntry}; use crate::msgs::handshake::{HelloRetryRequest, KeyShareEntry};
@ -141,13 +141,11 @@ pub(super) fn start_handshake(
None => SessionId::random()?, None => SessionId::random()?,
}; };
let may_send_sct_list = config.verifier.request_scts();
Ok(emit_client_hello_for_retry( Ok(emit_client_hello_for_retry(
transcript_buffer, transcript_buffer,
None, None,
key_share, key_share,
extra_exts, extra_exts,
may_send_sct_list,
None, None,
ClientHelloInput { ClientHelloInput {
config, config,
@ -194,7 +192,6 @@ fn emit_client_hello_for_retry(
retryreq: Option<&HelloRetryRequest>, retryreq: Option<&HelloRetryRequest>,
key_share: Option<kx::KeyExchange>, key_share: Option<kx::KeyExchange>,
extra_exts: Vec<ClientExtension>, extra_exts: Vec<ClientExtension>,
may_send_sct_list: bool,
suite: Option<SupportedCipherSuite>, suite: Option<SupportedCipherSuite>,
mut input: ClientHelloInput, mut input: ClientHelloInput,
cx: &mut ClientContext<'_>, cx: &mut ClientContext<'_>,
@ -238,10 +235,6 @@ fn emit_client_hello_for_retry(
exts.push(ClientExtension::make_sni(sni_name)); exts.push(ClientExtension::make_sni(sni_name));
} }
if may_send_sct_list {
exts.push(ClientExtension::SignedCertificateTimestampRequest);
}
if let Some(key_share) = &key_share { if let Some(key_share) = &key_share {
debug_assert!(support_tls13); debug_assert!(support_tls13);
let key_share = KeyShareEntry::new(key_share.group(), key_share.pubkey.as_ref()); let key_share = KeyShareEntry::new(key_share.group(), key_share.pubkey.as_ref());
@ -479,13 +472,6 @@ pub(super) fn process_alpn_protocol(
Ok(()) Ok(())
} }
pub(super) fn sct_list_is_invalid(scts: &[Sct]) -> bool {
scts.is_empty()
|| scts
.iter()
.any(|sct| sct.as_ref().is_empty())
}
impl State<ClientConnectionData> for ExpectServerHello { impl State<ClientConnectionData> for ExpectServerHello {
fn handle(mut self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> NextStateOrError { fn handle(mut self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> NextStateOrError {
let server_hello = let server_hello =
@ -800,12 +786,6 @@ impl ExpectServerHelloOrHelloRetryRequest {
cx.data.early_data.rejected(); cx.data.early_data.rejected();
} }
let may_send_sct_list = self
.next
.input
.hello
.server_may_send_sct_list();
let key_share = match req_group { let key_share = match req_group {
Some(group) if group != offered_key_share.group() => { Some(group) if group != offered_key_share.group() => {
let group = kx::KeyExchange::choose(group, &config.kx_groups).ok_or_else(|| { let group = kx::KeyExchange::choose(group, &config.kx_groups).ok_or_else(|| {
@ -824,7 +804,6 @@ impl ExpectServerHelloOrHelloRetryRequest {
Some(hrr), Some(hrr),
Some(key_share), Some(key_share),
self.extra_exts, self.extra_exts,
may_send_sct_list,
Some(cs), Some(cs),
self.next.input, self.next.input,
cx, cx,

View File

@ -12,7 +12,7 @@ use crate::msgs::base::{Payload, PayloadU8};
use crate::msgs::ccs::ChangeCipherSpecPayload; use crate::msgs::ccs::ChangeCipherSpecPayload;
use crate::msgs::codec::Codec; use crate::msgs::codec::Codec;
use crate::msgs::handshake::{ use crate::msgs::handshake::{
CertificatePayload, HandshakeMessagePayload, HandshakePayload, NewSessionTicketPayload, Sct, CertificatePayload, HandshakeMessagePayload, HandshakePayload, NewSessionTicketPayload,
ServerECDHParams, SessionId, ServerECDHParams, SessionId,
}; };
use crate::msgs::message::{Message, MessagePayload}; use crate::msgs::message::{Message, MessagePayload};
@ -102,18 +102,6 @@ mod server_hello {
debug!("Server may staple OCSP response"); debug!("Server may staple OCSP response");
} }
// Save any sent SCTs for verification against the certificate.
let server_cert_sct_list = if let Some(sct_list) = server_hello.get_sct_list() {
debug!("Server sent {:?} SCTs", sct_list.len());
if hs::sct_list_is_invalid(sct_list) {
return Err(PeerMisbehaved::InvalidSctList.into());
}
Some(sct_list.to_owned())
} else {
None
};
// See if we're successfully resuming. // See if we're successfully resuming.
if let Some(ref resuming) = self.resuming_session { if let Some(ref resuming) = self.resuming_session {
if resuming.session_id == server_hello.session_id { if resuming.session_id == server_hello.session_id {
@ -187,7 +175,6 @@ mod server_hello {
suite, suite,
may_send_cert_status, may_send_cert_status,
must_issue_new_ticket, must_issue_new_ticket,
server_cert_sct_list,
})) }))
} }
} }
@ -204,7 +191,6 @@ struct ExpectCertificate {
pub(super) suite: &'static Tls12CipherSuite, pub(super) suite: &'static Tls12CipherSuite,
may_send_cert_status: bool, may_send_cert_status: bool,
must_issue_new_ticket: bool, must_issue_new_ticket: bool,
server_cert_sct_list: Option<Vec<Sct>>,
} }
impl State<ClientConnectionData> for ExpectCertificate { impl State<ClientConnectionData> for ExpectCertificate {
@ -230,13 +216,11 @@ impl State<ClientConnectionData> for ExpectCertificate {
using_ems: self.using_ems, using_ems: self.using_ems,
transcript: self.transcript, transcript: self.transcript,
suite: self.suite, suite: self.suite,
server_cert_sct_list: self.server_cert_sct_list,
server_cert_chain, server_cert_chain,
must_issue_new_ticket: self.must_issue_new_ticket, must_issue_new_ticket: self.must_issue_new_ticket,
})) }))
} else { } else {
let server_cert = let server_cert = ServerCertDetails::new(server_cert_chain, vec![]);
ServerCertDetails::new(server_cert_chain, vec![], self.server_cert_sct_list);
Ok(Box::new(ExpectServerKx { Ok(Box::new(ExpectServerKx {
config: self.config, config: self.config,
@ -263,7 +247,6 @@ struct ExpectCertificateStatusOrServerKx {
using_ems: bool, using_ems: bool,
transcript: HandshakeHash, transcript: HandshakeHash,
suite: &'static Tls12CipherSuite, suite: &'static Tls12CipherSuite,
server_cert_sct_list: Option<Vec<Sct>>,
server_cert_chain: CertificatePayload, server_cert_chain: CertificatePayload,
must_issue_new_ticket: bool, must_issue_new_ticket: bool,
} }
@ -287,11 +270,7 @@ impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx {
using_ems: self.using_ems, using_ems: self.using_ems,
transcript: self.transcript, transcript: self.transcript,
suite: self.suite, suite: self.suite,
server_cert: ServerCertDetails::new( server_cert: ServerCertDetails::new(self.server_cert_chain, vec![]),
self.server_cert_chain,
vec![],
self.server_cert_sct_list,
),
must_issue_new_ticket: self.must_issue_new_ticket, must_issue_new_ticket: self.must_issue_new_ticket,
}) })
.handle(cx, m), .handle(cx, m),
@ -311,7 +290,6 @@ impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx {
using_ems: self.using_ems, using_ems: self.using_ems,
transcript: self.transcript, transcript: self.transcript,
suite: self.suite, suite: self.suite,
server_cert_sct_list: self.server_cert_sct_list,
server_cert_chain: self.server_cert_chain, server_cert_chain: self.server_cert_chain,
must_issue_new_ticket: self.must_issue_new_ticket, must_issue_new_ticket: self.must_issue_new_ticket,
}) })
@ -337,7 +315,6 @@ struct ExpectCertificateStatus {
using_ems: bool, using_ems: bool,
transcript: HandshakeHash, transcript: HandshakeHash,
suite: &'static Tls12CipherSuite, suite: &'static Tls12CipherSuite,
server_cert_sct_list: Option<Vec<Sct>>,
server_cert_chain: CertificatePayload, server_cert_chain: CertificatePayload,
must_issue_new_ticket: bool, must_issue_new_ticket: bool,
} }
@ -361,11 +338,7 @@ impl State<ClientConnectionData> for ExpectCertificateStatus {
&server_cert_ocsp_response &server_cert_ocsp_response
); );
let server_cert = ServerCertDetails::new( let server_cert = ServerCertDetails::new(self.server_cert_chain, server_cert_ocsp_response);
self.server_cert_chain,
server_cert_ocsp_response,
self.server_cert_sct_list,
);
Ok(Box::new(ExpectServerKx { Ok(Box::new(ExpectServerKx {
config: self.config, config: self.config,
@ -741,7 +714,6 @@ impl State<ClientConnectionData> for ExpectServerDone {
end_entity, end_entity,
intermediates, intermediates,
&st.server_name, &st.server_name,
&mut st.server_cert.scts(),
&st.server_cert.ocsp_response, &st.server_cert.ocsp_response,
now, now,
) )

View File

@ -448,7 +448,6 @@ impl State<ClientConnectionData> for ExpectEncryptedExtensions {
suite: self.suite, suite: self.suite,
transcript: self.transcript, transcript: self.transcript,
key_schedule: self.key_schedule, key_schedule: self.key_schedule,
may_send_sct_list: self.hello.server_may_send_sct_list(),
})) }))
} }
} }
@ -461,7 +460,6 @@ struct ExpectCertificateOrCertReq {
suite: &'static Tls13CipherSuite, suite: &'static Tls13CipherSuite,
transcript: HandshakeHash, transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake, key_schedule: KeyScheduleHandshake,
may_send_sct_list: bool,
} }
impl State<ClientConnectionData> for ExpectCertificateOrCertReq { impl State<ClientConnectionData> for ExpectCertificateOrCertReq {
@ -481,7 +479,6 @@ impl State<ClientConnectionData> for ExpectCertificateOrCertReq {
suite: self.suite, suite: self.suite,
transcript: self.transcript, transcript: self.transcript,
key_schedule: self.key_schedule, key_schedule: self.key_schedule,
may_send_sct_list: self.may_send_sct_list,
client_auth: None, client_auth: None,
}) })
.handle(cx, m), .handle(cx, m),
@ -499,7 +496,6 @@ impl State<ClientConnectionData> for ExpectCertificateOrCertReq {
suite: self.suite, suite: self.suite,
transcript: self.transcript, transcript: self.transcript,
key_schedule: self.key_schedule, key_schedule: self.key_schedule,
may_send_sct_list: self.may_send_sct_list,
}) })
.handle(cx, m), .handle(cx, m),
payload => Err(inappropriate_handshake_message( payload => Err(inappropriate_handshake_message(
@ -524,7 +520,6 @@ struct ExpectCertificateRequest {
suite: &'static Tls13CipherSuite, suite: &'static Tls13CipherSuite,
transcript: HandshakeHash, transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake, key_schedule: KeyScheduleHandshake,
may_send_sct_list: bool,
} }
impl State<ClientConnectionData> for ExpectCertificateRequest { impl State<ClientConnectionData> for ExpectCertificateRequest {
@ -582,7 +577,6 @@ impl State<ClientConnectionData> for ExpectCertificateRequest {
suite: self.suite, suite: self.suite,
transcript: self.transcript, transcript: self.transcript,
key_schedule: self.key_schedule, key_schedule: self.key_schedule,
may_send_sct_list: self.may_send_sct_list,
client_auth: Some(client_auth), client_auth: Some(client_auth),
})) }))
} }
@ -595,7 +589,6 @@ struct ExpectCertificate {
suite: &'static Tls13CipherSuite, suite: &'static Tls13CipherSuite,
transcript: HandshakeHash, transcript: HandshakeHash,
key_schedule: KeyScheduleHandshake, key_schedule: KeyScheduleHandshake,
may_send_sct_list: bool,
client_auth: Option<ClientAuthDetails>, client_auth: Option<ClientAuthDetails>,
} }
@ -625,23 +618,8 @@ impl State<ClientConnectionData> for ExpectCertificate {
)); ));
} }
let server_cert = ServerCertDetails::new( let server_cert =
cert_chain.convert(), ServerCertDetails::new(cert_chain.convert(), cert_chain.get_end_entity_ocsp());
cert_chain.get_end_entity_ocsp(),
cert_chain
.get_end_entity_scts()
.map(|scts| scts.to_vec()),
);
if let Some(sct_list) = server_cert.scts.as_ref() {
if hs::sct_list_is_invalid(sct_list) {
return Err(PeerMisbehaved::InvalidSctList.into());
}
if !self.may_send_sct_list {
return Err(PeerMisbehaved::UnsolicitedSctList.into());
}
}
Ok(Box::new(ExpectCertificateVerify { Ok(Box::new(ExpectCertificateVerify {
config: self.config, config: self.config,
@ -692,7 +670,6 @@ impl State<ClientConnectionData> for ExpectCertificateVerify {
end_entity, end_entity,
intermediates, intermediates,
&self.server_name, &self.server_name,
&mut self.server_cert.scts(),
&self.server_cert.ocsp_response, &self.server_cert.ocsp_response,
now, now,
) )

View File

@ -66,9 +66,6 @@ pub enum Error {
/// implementation. /// implementation.
InvalidCertificate(CertificateError), InvalidCertificate(CertificateError),
/// The presented SCT(s) were invalid.
InvalidSct(sct::Error),
/// A provided certificate revocation list (CRL) was invalid. /// A provided certificate revocation list (CRL) was invalid.
InvalidCertRevocationList(CertRevocationListError), InvalidCertRevocationList(CertRevocationListError),
@ -188,7 +185,6 @@ pub enum PeerMisbehaved {
IncorrectBinder, IncorrectBinder,
InvalidMaxEarlyDataSize, InvalidMaxEarlyDataSize,
InvalidKeyShare, InvalidKeyShare,
InvalidSctList,
KeyEpochWithPendingFragment, KeyEpochWithPendingFragment,
KeyUpdateReceivedInQuicConnection, KeyUpdateReceivedInQuicConnection,
MessageInterleavedWithHandshakeMessage, MessageInterleavedWithHandshakeMessage,
@ -525,7 +521,6 @@ impl fmt::Display for Error {
Self::PeerSentOversizedRecord => write!(f, "peer sent excess record size"), Self::PeerSentOversizedRecord => write!(f, "peer sent excess record size"),
Self::HandshakeNotComplete => write!(f, "handshake not complete"), Self::HandshakeNotComplete => write!(f, "handshake not complete"),
Self::NoApplicationProtocol => write!(f, "peer doesn't support any known protocol"), Self::NoApplicationProtocol => write!(f, "peer doesn't support any known protocol"),
Self::InvalidSct(ref err) => write!(f, "invalid certificate timestamp: {:?}", err),
Self::FailedToGetCurrentTime => write!(f, "failed to get current time"), Self::FailedToGetCurrentTime => write!(f, "failed to get current time"),
Self::FailedToGetRandomBytes => write!(f, "failed to get random bytes"), Self::FailedToGetRandomBytes => write!(f, "failed to get random bytes"),
Self::BadMaxFragmentSize => { Self::BadMaxFragmentSize => {
@ -653,7 +648,6 @@ mod tests {
#[test] #[test]
fn smoke() { fn smoke() {
use crate::enums::{AlertDescription, ContentType, HandshakeType}; use crate::enums::{AlertDescription, ContentType, HandshakeType};
use sct;
let all = vec![ let all = vec![
Error::InappropriateMessage { Error::InappropriateMessage {
@ -671,7 +665,6 @@ mod tests {
super::PeerMisbehaved::UnsolicitedCertExtension.into(), super::PeerMisbehaved::UnsolicitedCertExtension.into(),
Error::AlertReceived(AlertDescription::ExportRestriction), Error::AlertReceived(AlertDescription::ExportRestriction),
super::CertificateError::Expired.into(), super::CertificateError::Expired.into(),
Error::InvalidSct(sct::Error::MalformedSct),
Error::General("undocumented error".to_string()), Error::General("undocumented error".to_string()),
Error::FailedToGetCurrentTime, Error::FailedToGetCurrentTime,
Error::FailedToGetRandomBytes, Error::FailedToGetRandomBytes,

View File

@ -25,8 +25,6 @@
//! * Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)). //! * Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)).
//! * Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)). //! * Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)).
//! * OCSP stapling by servers. //! * OCSP stapling by servers.
//! * SCT stapling by servers.
//! * SCT verification by clients.
//! //!
//! ## Possible future features //! ## Possible future features
//! //!
@ -417,7 +415,7 @@ pub mod client {
mod tls13; mod tls13;
pub use crate::dns_name::InvalidDnsNameError; pub use crate::dns_name::InvalidDnsNameError;
pub use builder::{WantsClientCert, WantsTransparencyPolicyOrClientCert}; pub use builder::WantsClientCert;
pub use client_conn::{ pub use client_conn::{
ClientConfig, ClientConnection, ClientConnectionData, ClientSessionStore, ClientConfig, ClientConnection, ClientConnectionData, ClientSessionStore,
ResolvesClientCert, Resumption, ServerName, Tls12Resumption, WriteEarlyData, ResolvesClientCert, Resumption, ServerName, Tls12Resumption, WriteEarlyData,
@ -426,9 +424,8 @@ pub mod client {
#[cfg(feature = "dangerous_configuration")] #[cfg(feature = "dangerous_configuration")]
pub use crate::verify::{ pub use crate::verify::{
verify_server_cert_signed_by_trust_anchor, verify_server_name, verify_server_cert_signed_by_trust_anchor, verify_server_name, HandshakeSignatureValid,
CertificateTransparencyPolicy, HandshakeSignatureValid, ServerCertVerified, ServerCertVerified, ServerCertVerifier, WebPkiVerifier,
ServerCertVerifier, WebPkiVerifier,
}; };
#[cfg(feature = "dangerous_configuration")] #[cfg(feature = "dangerous_configuration")]
pub use client_conn::danger::DangerousClientConfig; pub use client_conn::danger::DangerousClientConfig;

View File

@ -516,15 +516,6 @@ impl CertificateStatusRequest {
} }
} }
// ---
// SCTs
wrapped_payload!(Sct, PayloadU16,);
impl TlsListElement for Sct {
const SIZE_LEN: ListLength = ListLength::U16;
}
// --- // ---
impl TlsListElement for PSKKeyExchangeMode { impl TlsListElement for PSKKeyExchangeMode {
@ -554,7 +545,6 @@ pub enum ClientExtension {
Cookie(PayloadU16), Cookie(PayloadU16),
ExtendedMasterSecretRequest, ExtendedMasterSecretRequest,
CertificateStatusRequest(CertificateStatusRequest), CertificateStatusRequest(CertificateStatusRequest),
SignedCertificateTimestampRequest,
TransportParameters(Vec<u8>), TransportParameters(Vec<u8>),
TransportParametersDraft(Vec<u8>), TransportParametersDraft(Vec<u8>),
EarlyData, EarlyData,
@ -577,7 +567,6 @@ impl ClientExtension {
Self::Cookie(_) => ExtensionType::Cookie, Self::Cookie(_) => ExtensionType::Cookie,
Self::ExtendedMasterSecretRequest => ExtensionType::ExtendedMasterSecret, Self::ExtendedMasterSecretRequest => ExtensionType::ExtendedMasterSecret,
Self::CertificateStatusRequest(_) => ExtensionType::StatusRequest, Self::CertificateStatusRequest(_) => ExtensionType::StatusRequest,
Self::SignedCertificateTimestampRequest => ExtensionType::SCT,
Self::TransportParameters(_) => ExtensionType::TransportParameters, Self::TransportParameters(_) => ExtensionType::TransportParameters,
Self::TransportParametersDraft(_) => ExtensionType::TransportParametersDraft, Self::TransportParametersDraft(_) => ExtensionType::TransportParametersDraft,
Self::EarlyData => ExtensionType::EarlyData, Self::EarlyData => ExtensionType::EarlyData,
@ -598,7 +587,6 @@ impl Codec for ClientExtension {
Self::ServerName(ref r) => r.encode(&mut sub), Self::ServerName(ref r) => r.encode(&mut sub),
Self::SessionTicket(ClientSessionTicket::Request) Self::SessionTicket(ClientSessionTicket::Request)
| Self::ExtendedMasterSecretRequest | Self::ExtendedMasterSecretRequest
| Self::SignedCertificateTimestampRequest
| Self::EarlyData => {} | Self::EarlyData => {}
Self::SessionTicket(ClientSessionTicket::Offer(ref r)) => r.encode(&mut sub), Self::SessionTicket(ClientSessionTicket::Offer(ref r)) => r.encode(&mut sub),
Self::Protocols(ref r) => r.encode(&mut sub), Self::Protocols(ref r) => r.encode(&mut sub),
@ -649,7 +637,6 @@ impl Codec for ClientExtension {
let csr = CertificateStatusRequest::read(&mut sub)?; let csr = CertificateStatusRequest::read(&mut sub)?;
Self::CertificateStatusRequest(csr) Self::CertificateStatusRequest(csr)
} }
ExtensionType::SCT if !sub.any_left() => Self::SignedCertificateTimestampRequest,
ExtensionType::TransportParameters => Self::TransportParameters(sub.rest().to_vec()), ExtensionType::TransportParameters => Self::TransportParameters(sub.rest().to_vec()),
ExtensionType::TransportParametersDraft => { ExtensionType::TransportParametersDraft => {
Self::TransportParametersDraft(sub.rest().to_vec()) Self::TransportParametersDraft(sub.rest().to_vec())
@ -707,7 +694,6 @@ pub enum ServerExtension {
PresharedKey(u16), PresharedKey(u16),
ExtendedMasterSecretAck, ExtendedMasterSecretAck,
CertificateStatusAck, CertificateStatusAck,
SignedCertificateTimestamp(Vec<Sct>),
SupportedVersions(ProtocolVersion), SupportedVersions(ProtocolVersion),
TransportParameters(Vec<u8>), TransportParameters(Vec<u8>),
TransportParametersDraft(Vec<u8>), TransportParametersDraft(Vec<u8>),
@ -727,7 +713,6 @@ impl ServerExtension {
Self::PresharedKey(_) => ExtensionType::PreSharedKey, Self::PresharedKey(_) => ExtensionType::PreSharedKey,
Self::ExtendedMasterSecretAck => ExtensionType::ExtendedMasterSecret, Self::ExtendedMasterSecretAck => ExtensionType::ExtendedMasterSecret,
Self::CertificateStatusAck => ExtensionType::StatusRequest, Self::CertificateStatusAck => ExtensionType::StatusRequest,
Self::SignedCertificateTimestamp(_) => ExtensionType::SCT,
Self::SupportedVersions(_) => ExtensionType::SupportedVersions, Self::SupportedVersions(_) => ExtensionType::SupportedVersions,
Self::TransportParameters(_) => ExtensionType::TransportParameters, Self::TransportParameters(_) => ExtensionType::TransportParameters,
Self::TransportParametersDraft(_) => ExtensionType::TransportParametersDraft, Self::TransportParametersDraft(_) => ExtensionType::TransportParametersDraft,
@ -753,7 +738,6 @@ impl Codec for ServerExtension {
Self::Protocols(ref r) => r.encode(&mut sub), Self::Protocols(ref r) => r.encode(&mut sub),
Self::KeyShare(ref r) => r.encode(&mut sub), Self::KeyShare(ref r) => r.encode(&mut sub),
Self::PresharedKey(r) => r.encode(&mut sub), Self::PresharedKey(r) => r.encode(&mut sub),
Self::SignedCertificateTimestamp(ref r) => r.encode(&mut sub),
Self::SupportedVersions(ref r) => r.encode(&mut sub), Self::SupportedVersions(ref r) => r.encode(&mut sub),
Self::TransportParameters(ref r) | Self::TransportParametersDraft(ref r) => { Self::TransportParameters(ref r) | Self::TransportParametersDraft(ref r) => {
sub.extend_from_slice(r); sub.extend_from_slice(r);
@ -780,7 +764,6 @@ impl Codec for ServerExtension {
ExtensionType::KeyShare => Self::KeyShare(KeyShareEntry::read(&mut sub)?), ExtensionType::KeyShare => Self::KeyShare(KeyShareEntry::read(&mut sub)?),
ExtensionType::PreSharedKey => Self::PresharedKey(u16::read(&mut sub)?), ExtensionType::PreSharedKey => Self::PresharedKey(u16::read(&mut sub)?),
ExtensionType::ExtendedMasterSecret => Self::ExtendedMasterSecretAck, ExtensionType::ExtendedMasterSecret => Self::ExtendedMasterSecretAck,
ExtensionType::SCT => Self::SignedCertificateTimestamp(Vec::read(&mut sub)?),
ExtensionType::SupportedVersions => { ExtensionType::SupportedVersions => {
Self::SupportedVersions(ProtocolVersion::read(&mut sub)?) Self::SupportedVersions(ProtocolVersion::read(&mut sub)?)
} }
@ -806,11 +789,6 @@ impl ServerExtension {
let empty = Vec::new(); let empty = Vec::new();
Self::RenegotiationInfo(PayloadU8::new(empty)) Self::RenegotiationInfo(PayloadU8::new(empty))
} }
pub fn make_sct(sctl: Vec<u8>) -> Self {
let scts = Vec::read_bytes(&sctl).expect("invalid SCT list");
Self::SignedCertificateTimestamp(scts)
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -1264,14 +1242,6 @@ impl ServerHelloPayload {
.is_some() .is_some()
} }
pub fn get_sct_list(&self) -> Option<&[Sct]> {
let ext = self.find_extension(ExtensionType::SCT)?;
match *ext {
ServerExtension::SignedCertificateTimestamp(ref sctl) => Some(sctl),
_ => None,
}
}
pub fn get_supported_versions(&self) -> Option<ProtocolVersion> { pub fn get_supported_versions(&self) -> Option<ProtocolVersion> {
let ext = self.find_extension(ExtensionType::SupportedVersions)?; let ext = self.find_extension(ExtensionType::SupportedVersions)?;
match *ext { match *ext {
@ -1294,7 +1264,6 @@ impl TlsListElement for key::Certificate {
#[derive(Debug)] #[derive(Debug)]
pub enum CertificateExtension { pub enum CertificateExtension {
CertificateStatus(CertificateStatus), CertificateStatus(CertificateStatus),
SignedCertificateTimestamp(Vec<Sct>),
Unknown(UnknownExtension), Unknown(UnknownExtension),
} }
@ -1302,29 +1271,16 @@ impl CertificateExtension {
pub fn get_type(&self) -> ExtensionType { pub fn get_type(&self) -> ExtensionType {
match *self { match *self {
Self::CertificateStatus(_) => ExtensionType::StatusRequest, Self::CertificateStatus(_) => ExtensionType::StatusRequest,
Self::SignedCertificateTimestamp(_) => ExtensionType::SCT,
Self::Unknown(ref r) => r.typ, Self::Unknown(ref r) => r.typ,
} }
} }
pub fn make_sct(sct_list: Vec<u8>) -> Self {
let sctl = Vec::read_bytes(&sct_list).expect("invalid SCT list");
Self::SignedCertificateTimestamp(sctl)
}
pub fn get_cert_status(&self) -> Option<&Vec<u8>> { pub fn get_cert_status(&self) -> Option<&Vec<u8>> {
match *self { match *self {
Self::CertificateStatus(ref cs) => Some(&cs.ocsp_response.0), Self::CertificateStatus(ref cs) => Some(&cs.ocsp_response.0),
_ => None, _ => None,
} }
} }
pub fn get_sct_list(&self) -> Option<&[Sct]> {
match *self {
Self::SignedCertificateTimestamp(ref sctl) => Some(sctl),
_ => None,
}
}
} }
impl Codec for CertificateExtension { impl Codec for CertificateExtension {
@ -1334,7 +1290,6 @@ impl Codec for CertificateExtension {
let mut sub: Vec<u8> = Vec::new(); let mut sub: Vec<u8> = Vec::new();
match *self { match *self {
Self::CertificateStatus(ref r) => r.encode(&mut sub), Self::CertificateStatus(ref r) => r.encode(&mut sub),
Self::SignedCertificateTimestamp(ref r) => r.encode(&mut sub),
Self::Unknown(ref r) => r.encode(&mut sub), Self::Unknown(ref r) => r.encode(&mut sub),
} }
@ -1352,7 +1307,6 @@ impl Codec for CertificateExtension {
let st = CertificateStatus::read(&mut sub)?; let st = CertificateStatus::read(&mut sub)?;
Self::CertificateStatus(st) Self::CertificateStatus(st)
} }
ExtensionType::SCT => Self::SignedCertificateTimestamp(Vec::read(&mut sub)?),
_ => Self::Unknown(UnknownExtension::read(typ, &mut sub)), _ => Self::Unknown(UnknownExtension::read(typ, &mut sub)),
}; };
@ -1409,9 +1363,9 @@ impl CertificateEntry {
} }
pub fn has_unknown_extension(&self) -> bool { pub fn has_unknown_extension(&self) -> bool {
self.exts.iter().any(|ext| { self.exts
ext.get_type() != ExtensionType::StatusRequest && ext.get_type() != ExtensionType::SCT .iter()
}) .any(|ext| ext.get_type() != ExtensionType::StatusRequest)
} }
pub fn get_ocsp_response(&self) -> Option<&Vec<u8>> { pub fn get_ocsp_response(&self) -> Option<&Vec<u8>> {
@ -1420,13 +1374,6 @@ impl CertificateEntry {
.find(|ext| ext.get_type() == ExtensionType::StatusRequest) .find(|ext| ext.get_type() == ExtensionType::StatusRequest)
.and_then(CertificateExtension::get_cert_status) .and_then(CertificateExtension::get_cert_status)
} }
pub fn get_scts(&self) -> Option<&[Sct]> {
self.exts
.iter()
.find(|ext| ext.get_type() == ExtensionType::SCT)
.and_then(CertificateExtension::get_sct_list)
}
} }
impl TlsListElement for CertificateEntry { impl TlsListElement for CertificateEntry {
@ -1499,12 +1446,6 @@ impl CertificatePayloadTLS13 {
.unwrap_or_default() .unwrap_or_default()
} }
pub fn get_end_entity_scts(&self) -> Option<&[Sct]> {
self.entries
.first()
.and_then(CertificateEntry::get_scts)
}
pub fn convert(&self) -> CertificatePayload { pub fn convert(&self) -> CertificatePayload {
let mut ret = Vec::new(); let mut ret = Vec::new();
for entry in &self.entries { for entry in &self.entries {

View File

@ -15,7 +15,7 @@ use crate::msgs::handshake::{
ECParameters, HandshakeMessagePayload, HandshakePayload, HasServerExtensions, ECParameters, HandshakeMessagePayload, HandshakePayload, HasServerExtensions,
HelloRetryExtension, HelloRetryRequest, KeyShareEntry, NewSessionTicketExtension, HelloRetryExtension, HelloRetryRequest, KeyShareEntry, NewSessionTicketExtension,
NewSessionTicketPayload, NewSessionTicketPayloadTLS13, PresharedKeyBinder, NewSessionTicketPayload, NewSessionTicketPayloadTLS13, PresharedKeyBinder,
PresharedKeyIdentity, PresharedKeyOffer, ProtocolName, Random, Sct, ServerECDHParams, PresharedKeyIdentity, PresharedKeyOffer, ProtocolName, Random, ServerECDHParams,
ServerExtension, ServerHelloPayload, ServerKeyExchangePayload, SessionId, UnknownExtension, ServerExtension, ServerHelloPayload, ServerKeyExchangePayload, SessionId, UnknownExtension,
}; };
use crate::verify::DigitallySignedStruct; use crate::verify::DigitallySignedStruct;
@ -131,7 +131,7 @@ fn refuses_server_ext_with_unparsed_bytes() {
#[test] #[test]
fn refuses_certificate_ext_with_unparsed_bytes() { fn refuses_certificate_ext_with_unparsed_bytes() {
let bytes = [0x00u8, 0x12, 0x00, 0x03, 0x00, 0x00, 0x01]; let bytes = [0x00u8, 0x05, 0x00, 0x03, 0x00, 0x00, 0x01];
let mut rd = Reader::init(&bytes); let mut rd = Reader::init(&bytes);
assert!(CertificateExtension::read(&mut rd).is_err()); assert!(CertificateExtension::read(&mut rd).is_err());
} }
@ -385,7 +385,6 @@ fn get_sample_clienthellopayload() -> ClientHelloPayload {
ClientExtension::Cookie(PayloadU16(vec![1, 2, 3])), ClientExtension::Cookie(PayloadU16(vec![1, 2, 3])),
ClientExtension::ExtendedMasterSecretRequest, ClientExtension::ExtendedMasterSecretRequest,
ClientExtension::CertificateStatusRequest(CertificateStatusRequest::build_ocsp()), ClientExtension::CertificateStatusRequest(CertificateStatusRequest::build_ocsp()),
ClientExtension::SignedCertificateTimestampRequest,
ClientExtension::TransportParameters(vec![1, 2, 3]), ClientExtension::TransportParameters(vec![1, 2, 3]),
ClientExtension::Unknown(UnknownExtension { ClientExtension::Unknown(UnknownExtension {
typ: ExtensionType::Unknown(12345), typ: ExtensionType::Unknown(12345),
@ -704,11 +703,6 @@ fn server_get_ecpoints_extension() {
}); });
} }
#[test]
fn server_get_sct_list() {
test_server_extension_getter(ExtensionType::SCT, |shp| shp.get_sct_list().is_some());
}
#[test] #[test]
fn server_get_supported_versions() { fn server_get_supported_versions() {
test_server_extension_getter(ExtensionType::SupportedVersions, |shp| { test_server_extension_getter(ExtensionType::SupportedVersions, |shp| {
@ -742,11 +736,6 @@ fn certentry_get_ocsp_response() {
}); });
} }
#[test]
fn certentry_get_scts() {
test_cert_extension_getter(ExtensionType::SCT, |ce| ce.get_scts().is_some());
}
fn get_sample_serverhellopayload() -> ServerHelloPayload { fn get_sample_serverhellopayload() -> ServerHelloPayload {
ServerHelloPayload { ServerHelloPayload {
legacy_version: ProtocolVersion::TLSv1_2, legacy_version: ProtocolVersion::TLSv1_2,
@ -764,7 +753,6 @@ fn get_sample_serverhellopayload() -> ServerHelloPayload {
ServerExtension::PresharedKey(3), ServerExtension::PresharedKey(3),
ServerExtension::ExtendedMasterSecretAck, ServerExtension::ExtendedMasterSecretAck,
ServerExtension::CertificateStatusAck, ServerExtension::CertificateStatusAck,
ServerExtension::SignedCertificateTimestamp(vec![Sct::from(vec![0])]),
ServerExtension::SupportedVersions(ProtocolVersion::TLSv1_2), ServerExtension::SupportedVersions(ProtocolVersion::TLSv1_2),
ServerExtension::TransportParameters(vec![1, 2, 3]), ServerExtension::TransportParameters(vec![1, 2, 3]),
ServerExtension::Unknown(UnknownExtension { ServerExtension::Unknown(UnknownExtension {
@ -811,7 +799,6 @@ fn get_sample_certificatepayloadtls13() -> CertificatePayloadTLS13 {
CertificateExtension::CertificateStatus(CertificateStatus { CertificateExtension::CertificateStatus(CertificateStatus {
ocsp_response: PayloadU24(vec![1, 2, 3]), ocsp_response: PayloadU24(vec![1, 2, 3]),
}), }),
CertificateExtension::SignedCertificateTimestamp(vec![Sct::from(vec![0])]),
CertificateExtension::Unknown(UnknownExtension { CertificateExtension::Unknown(UnknownExtension {
typ: ExtensionType::Unknown(12345), typ: ExtensionType::Unknown(12345),
payload: Payload(vec![1, 2, 3]), payload: Payload(vec![1, 2, 3]),

View File

@ -77,19 +77,15 @@ impl ConfigBuilder<ServerConfig, WantsServerCert> {
/// `cert_chain` is a vector of DER-encoded certificates. /// `cert_chain` is a vector of DER-encoded certificates.
/// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key. /// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key.
/// `ocsp` is a DER-encoded OCSP response. Ignored if zero length. /// `ocsp` is a DER-encoded OCSP response. Ignored if zero length.
/// `scts` is an `SignedCertificateTimestampList` encoding (see RFC6962)
/// and is ignored if empty.
/// ///
/// This function fails if `key_der` is invalid. /// This function fails if `key_der` is invalid.
pub fn with_single_cert_with_ocsp_and_sct( pub fn with_single_cert_with_ocsp(
self, self,
cert_chain: Vec<key::Certificate>, cert_chain: Vec<key::Certificate>,
key_der: key::PrivateKey, key_der: key::PrivateKey,
ocsp: Vec<u8>, ocsp: Vec<u8>,
scts: Vec<u8>,
) -> Result<ServerConfig, Error> { ) -> Result<ServerConfig, Error> {
let resolver = let resolver = handy::AlwaysResolvesChain::new_with_extras(cert_chain, &key_der, ocsp)?;
handy::AlwaysResolvesChain::new_with_extras(cert_chain, &key_der, ocsp, scts)?;
Ok(self.with_cert_resolver(Arc::new(resolver))) Ok(self.with_cert_resolver(Arc::new(resolver)))
} }

View File

@ -5,7 +5,6 @@ use crate::{key, sign};
pub(super) struct ActiveCertifiedKey<'a> { pub(super) struct ActiveCertifiedKey<'a> {
key: &'a sign::CertifiedKey, key: &'a sign::CertifiedKey,
ocsp: Option<&'a [u8]>, ocsp: Option<&'a [u8]>,
sct_list: Option<&'a [u8]>,
} }
impl<'a> ActiveCertifiedKey<'a> { impl<'a> ActiveCertifiedKey<'a> {
@ -13,7 +12,6 @@ impl<'a> ActiveCertifiedKey<'a> {
ActiveCertifiedKey { ActiveCertifiedKey {
key, key,
ocsp: key.ocsp.as_deref(), ocsp: key.ocsp.as_deref(),
sct_list: key.sct_list.as_deref(),
} }
} }
@ -33,9 +31,4 @@ impl<'a> ActiveCertifiedKey<'a> {
pub(super) fn get_ocsp(&self) -> Option<&[u8]> { pub(super) fn get_ocsp(&self) -> Option<&[u8]> {
self.ocsp self.ocsp
} }
#[inline]
pub(super) fn get_sct_list(&self) -> Option<&[u8]> {
self.sct_list
}
} }

View File

@ -112,7 +112,6 @@ impl AlwaysResolvesChain {
chain: Vec<key::Certificate>, chain: Vec<key::Certificate>,
priv_key: &key::PrivateKey, priv_key: &key::PrivateKey,
ocsp: Vec<u8>, ocsp: Vec<u8>,
scts: Vec<u8>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut r = Self::new(chain, priv_key)?; let mut r = Self::new(chain, priv_key)?;
@ -121,9 +120,6 @@ impl AlwaysResolvesChain {
if !ocsp.is_empty() { if !ocsp.is_empty() {
cert.ocsp = Some(ocsp); cert.ocsp = Some(ocsp);
} }
if !scts.is_empty() {
cert.sct_list = Some(scts);
}
} }
Ok(r) Ok(r)

View File

@ -67,7 +67,6 @@ impl ExtensionProcessing {
config: &ServerConfig, config: &ServerConfig,
cx: &mut ServerContext<'_>, cx: &mut ServerContext<'_>,
ocsp_response: &mut Option<&[u8]>, ocsp_response: &mut Option<&[u8]>,
sct_list: &mut Option<&[u8]>,
hello: &ClientHelloPayload, hello: &ClientHelloPayload,
resumedata: Option<&persist::ServerSessionValue>, resumedata: Option<&persist::ServerSessionValue>,
extra_exts: Vec<ServerExtension>, extra_exts: Vec<ServerExtension>,
@ -156,24 +155,6 @@ impl ExtensionProcessing {
ocsp_response.take(); ocsp_response.take();
} }
if !for_resume
&& hello
.find_extension(ExtensionType::SCT)
.is_some()
{
if !cx.common.is_tls13() {
// Take the SCT list, if any, so we don't send it later,
// and put it in the legacy extension.
if let Some(sct_list) = sct_list.take() {
self.exts
.push(ServerExtension::make_sct(sct_list.to_vec()));
}
}
} else {
// Throw away any SCT list so we don't send it later.
sct_list.take();
}
self.exts.extend(extra_exts); self.exts.extend(extra_exts);
Ok(()) Ok(())

View File

@ -193,8 +193,7 @@ mod client_hello {
debug_assert_eq!(ecpoint, ECPointFormat::Uncompressed); debug_assert_eq!(ecpoint, ECPointFormat::Uncompressed);
let (mut ocsp_response, mut sct_list) = let mut ocsp_response = server_key.get_ocsp();
(server_key.get_ocsp(), server_key.get_sct_list());
// If we're not offered a ticket or a potential session ID, allocate a session ID. // If we're not offered a ticket or a potential session ID, allocate a session ID.
if !self.config.session_storage.can_cache() { if !self.config.session_storage.can_cache() {
@ -211,7 +210,6 @@ mod client_hello {
self.suite, self.suite,
self.using_ems, self.using_ems,
&mut ocsp_response, &mut ocsp_response,
&mut sct_list,
client_hello, client_hello,
None, None,
&self.randoms, &self.randoms,
@ -283,7 +281,6 @@ mod client_hello {
self.suite, self.suite,
self.using_ems, self.using_ems,
&mut None, &mut None,
&mut None,
client_hello, client_hello,
Some(&resumedata), Some(&resumedata),
&self.randoms, &self.randoms,
@ -339,22 +336,13 @@ mod client_hello {
suite: &'static Tls12CipherSuite, suite: &'static Tls12CipherSuite,
using_ems: bool, using_ems: bool,
ocsp_response: &mut Option<&[u8]>, ocsp_response: &mut Option<&[u8]>,
sct_list: &mut Option<&[u8]>,
hello: &ClientHelloPayload, hello: &ClientHelloPayload,
resumedata: Option<&persist::ServerSessionValue>, resumedata: Option<&persist::ServerSessionValue>,
randoms: &ConnectionRandoms, randoms: &ConnectionRandoms,
extra_exts: Vec<ServerExtension>, extra_exts: Vec<ServerExtension>,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let mut ep = hs::ExtensionProcessing::new(); let mut ep = hs::ExtensionProcessing::new();
ep.process_common( ep.process_common(config, cx, ocsp_response, hello, resumedata, extra_exts)?;
config,
cx,
ocsp_response,
sct_list,
hello,
resumedata,
extra_exts,
)?;
ep.process_tls12(config, hello, using_ems); ep.process_tls12(config, hello, using_ems);
let sh = Message { let sh = Message {

View File

@ -372,14 +372,12 @@ mod client_hello {
emit_fake_ccs(cx.common); emit_fake_ccs(cx.common);
} }
let (mut ocsp_response, mut sct_list) = let mut ocsp_response = server_key.get_ocsp();
(server_key.get_ocsp(), server_key.get_sct_list());
let doing_early_data = emit_encrypted_extensions( let doing_early_data = emit_encrypted_extensions(
&mut self.transcript, &mut self.transcript,
self.suite, self.suite,
cx, cx,
&mut ocsp_response, &mut ocsp_response,
&mut sct_list,
client_hello, client_hello,
resumedata.as_ref(), resumedata.as_ref(),
self.extra_exts, self.extra_exts,
@ -394,7 +392,6 @@ mod client_hello {
cx.common, cx.common,
server_key.get_cert(), server_key.get_cert(),
ocsp_response, ocsp_response,
sct_list,
); );
emit_certificate_verify_tls13( emit_certificate_verify_tls13(
&mut self.transcript, &mut self.transcript,
@ -666,22 +663,13 @@ mod client_hello {
suite: &'static Tls13CipherSuite, suite: &'static Tls13CipherSuite,
cx: &mut ServerContext<'_>, cx: &mut ServerContext<'_>,
ocsp_response: &mut Option<&[u8]>, ocsp_response: &mut Option<&[u8]>,
sct_list: &mut Option<&[u8]>,
hello: &ClientHelloPayload, hello: &ClientHelloPayload,
resumedata: Option<&persist::ServerSessionValue>, resumedata: Option<&persist::ServerSessionValue>,
extra_exts: Vec<ServerExtension>, extra_exts: Vec<ServerExtension>,
config: &ServerConfig, config: &ServerConfig,
) -> Result<EarlyDataDecision, Error> { ) -> Result<EarlyDataDecision, Error> {
let mut ep = hs::ExtensionProcessing::new(); let mut ep = hs::ExtensionProcessing::new();
ep.process_common( ep.process_common(config, cx, ocsp_response, hello, resumedata, extra_exts)?;
config,
cx,
ocsp_response,
sct_list,
hello,
resumedata,
extra_exts,
)?;
let early_data = decide_if_early_data_allowed(cx, hello, resumedata, suite, config); let early_data = decide_if_early_data_allowed(cx, hello, resumedata, suite, config);
if early_data == EarlyDataDecision::Accepted { if early_data == EarlyDataDecision::Accepted {
@ -751,7 +739,6 @@ mod client_hello {
common: &mut CommonState, common: &mut CommonState,
cert_chain: &[Certificate], cert_chain: &[Certificate],
ocsp_response: Option<&[u8]>, ocsp_response: Option<&[u8]>,
sct_list: Option<&[u8]>,
) { ) {
let mut cert_entries = vec![]; let mut cert_entries = vec![];
for cert in cert_chain { for cert in cert_chain {
@ -772,13 +759,6 @@ mod client_hello {
.exts .exts
.push(CertificateExtension::CertificateStatus(cst)); .push(CertificateExtension::CertificateStatus(cst));
} }
// Likewise, SCT
if let Some(sct_list) = sct_list {
end_entity_cert
.exts
.push(CertificateExtension::make_sct(sct_list.to_owned()));
}
} }
let cert_body = CertificatePayloadTLS13::new(cert_entries); let cert_body = CertificatePayloadTLS13::new(cert_entries);

View File

@ -44,11 +44,6 @@ pub struct CertifiedKey {
/// An optional OCSP response from the certificate issuer, /// An optional OCSP response from the certificate issuer,
/// attesting to its continued validity. /// attesting to its continued validity.
pub ocsp: Option<Vec<u8>>, pub ocsp: Option<Vec<u8>>,
/// An optional collection of SCTs from CT logs, proving the
/// certificate is included on those logs. This must be
/// a `SignedCertificateTimestampList` encoding; see RFC6962.
pub sct_list: Option<Vec<u8>>,
} }
impl CertifiedKey { impl CertifiedKey {
@ -61,7 +56,6 @@ impl CertifiedKey {
cert, cert,
key, key,
ocsp: None, ocsp: None,
sct_list: None,
} }
} }

View File

@ -8,7 +8,7 @@ use crate::error::{
}; };
use crate::key::{Certificate, ParsedCertificate}; use crate::key::{Certificate, ParsedCertificate};
#[cfg(feature = "logging")] #[cfg(feature = "logging")]
use crate::log::{debug, trace, warn}; use crate::log::trace;
use crate::msgs::base::PayloadU16; use crate::msgs::base::PayloadU16;
use crate::msgs::codec::{Codec, Reader}; use crate::msgs::codec::{Codec, Reader};
use crate::msgs::handshake::DistinguishedName; use crate::msgs::handshake::DistinguishedName;
@ -110,16 +110,12 @@ pub trait ServerCertVerifier: Send + Sync {
/// the implementor to handle invalid data. It is recommended that the implementor returns /// the implementor to handle invalid data. It is recommended that the implementor returns
/// [`Error::InvalidCertificate(CertificateError::BadEncoding)`] when these cases are encountered. /// [`Error::InvalidCertificate(CertificateError::BadEncoding)`] when these cases are encountered.
/// ///
/// `scts` contains the Signed Certificate Timestamps (SCTs) the server
/// sent with the end-entity certificate, if any.
///
/// [Certificate]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2 /// [Certificate]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2
fn verify_server_cert( fn verify_server_cert(
&self, &self,
end_entity: &Certificate, end_entity: &Certificate,
intermediates: &[Certificate], intermediates: &[Certificate],
server_name: &ServerName, server_name: &ServerName,
scts: &mut dyn Iterator<Item = &[u8]>,
ocsp_response: &[u8], ocsp_response: &[u8],
now: SystemTime, now: SystemTime,
) -> Result<ServerCertVerified, Error>; ) -> Result<ServerCertVerified, Error>;
@ -187,16 +183,6 @@ pub trait ServerCertVerifier: Send + Sync {
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
WebPkiVerifier::verification_schemes() WebPkiVerifier::verification_schemes()
} }
/// Returns `true` if Rustls should ask the server to send SCTs.
///
/// Signed Certificate Timestamps (SCTs) are used for Certificate
/// Transparency validation.
///
/// The default implementation of this function returns true.
fn request_scts(&self) -> bool {
true
}
} }
impl fmt::Debug for dyn ServerCertVerifier { impl fmt::Debug for dyn ServerCertVerifier {
@ -392,7 +378,6 @@ impl ServerCertVerifier for WebPkiVerifier {
end_entity: &Certificate, end_entity: &Certificate,
intermediates: &[Certificate], intermediates: &[Certificate],
server_name: &ServerName, server_name: &ServerName,
scts: &mut dyn Iterator<Item = &[u8]>,
ocsp_response: &[u8], ocsp_response: &[u8],
now: SystemTime, now: SystemTime,
) -> Result<ServerCertVerified, Error> { ) -> Result<ServerCertVerified, Error> {
@ -400,10 +385,6 @@ impl ServerCertVerifier for WebPkiVerifier {
verify_server_cert_signed_by_trust_anchor(&cert, &self.roots, intermediates, now)?; verify_server_cert_signed_by_trust_anchor(&cert, &self.roots, intermediates, now)?;
if let Some(policy) = &self.ct_policy {
policy.verify(end_entity, now, scts)?;
}
if !ocsp_response.is_empty() { if !ocsp_response.is_empty() {
trace!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec()); trace!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec());
} }
@ -418,7 +399,6 @@ impl ServerCertVerifier for WebPkiVerifier {
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))] #[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
pub struct WebPkiVerifier { pub struct WebPkiVerifier {
roots: RootCertStore, roots: RootCertStore,
ct_policy: Option<CertificateTransparencyPolicy>,
} }
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
@ -426,12 +406,8 @@ impl WebPkiVerifier {
/// Constructs a new `WebPkiVerifier`. /// Constructs a new `WebPkiVerifier`.
/// ///
/// `roots` is the set of trust anchors to trust for issuing server certs. /// `roots` is the set of trust anchors to trust for issuing server certs.
/// pub fn new(roots: RootCertStore) -> Self {
/// `ct_logs` is the list of logs that are trusted for Certificate Self { roots }
/// Transparency. Currently CT log enforcement is opportunistic; see
/// <https://github.com/rustls/rustls/issues/479>.
pub fn new(roots: RootCertStore, ct_policy: Option<CertificateTransparencyPolicy>) -> Self {
Self { roots, ct_policy }
} }
/// Returns the signature verification methods supported by /// Returns the signature verification methods supported by
@ -451,83 +427,6 @@ impl WebPkiVerifier {
} }
} }
/// Policy for enforcing Certificate Transparency.
///
/// Because Certificate Transparency logs are sharded on a per-year basis and can be trusted or
/// distrusted relatively quickly, rustls stores a validation deadline. Server certificates will
/// be validated against the configured CT logs until the deadline expires. After the deadline,
/// certificates will no longer be validated, and a warning message will be logged. The deadline
/// may vary depending on how often you deploy builds with updated dependencies.
#[allow(unreachable_pub)]
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
pub struct CertificateTransparencyPolicy {
logs: &'static [&'static sct::Log<'static>],
validation_deadline: SystemTime,
}
impl CertificateTransparencyPolicy {
/// Create a new policy.
#[allow(unreachable_pub)]
pub fn new(
logs: &'static [&'static sct::Log<'static>],
validation_deadline: SystemTime,
) -> Self {
Self {
logs,
validation_deadline,
}
}
fn verify(
&self,
cert: &Certificate,
now: SystemTime,
scts: &mut dyn Iterator<Item = &[u8]>,
) -> Result<(), Error> {
if self.logs.is_empty() {
return Ok(());
} else if self
.validation_deadline
.duration_since(now)
.is_err()
{
warn!("certificate transparency logs have expired, validation disabled");
return Ok(());
}
let now = unix_time_millis(now)?;
let mut last_sct_error = None;
for sct in scts {
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
match sct::verify_sct(&cert.0, sct, now, self.logs) {
Ok(index) => {
debug!(
"Valid SCT signed by {} on {}",
self.logs[index].operated_by, self.logs[index].description
);
return Ok(());
}
Err(e) => {
if e.should_be_fatal() {
return Err(Error::InvalidSct(e));
}
debug!("SCT ignored because {:?}", e);
last_sct_error = Some(e);
}
}
}
/* If we were supplied with some logs, and some SCTs,
* but couldn't verify any of them, fail the handshake. */
if let Some(last_sct_error) = last_sct_error {
warn!("No valid SCTs provided");
return Err(Error::InvalidSct(last_sct_error));
}
Ok(())
}
}
fn intermediate_chain(intermediates: &[Certificate]) -> Vec<&[u8]> { fn intermediate_chain(intermediates: &[Certificate]) -> Vec<&[u8]> {
intermediates intermediates
.iter() .iter()
@ -920,16 +819,6 @@ fn verify_tls13(
.map(|_| HandshakeSignatureValid::assertion()) .map(|_| HandshakeSignatureValid::assertion())
} }
fn unix_time_millis(now: SystemTime) -> Result<u64, Error> {
now.duration_since(std::time::UNIX_EPOCH)
.map(|dur| dur.as_secs())
.map_err(|_| Error::FailedToGetCurrentTime)
.and_then(|secs| {
secs.checked_mul(1000)
.ok_or(Error::FailedToGetCurrentTime)
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -213,8 +213,7 @@ impl Context {
} }
fn bench(&self, count: usize) { fn bench(&self, count: usize) {
let verifier = verify::WebPkiVerifier::new(self.roots.clone(), None); let verifier = verify::WebPkiVerifier::new(self.roots.clone());
const SCTS: &[&[u8]] = &[];
const OCSP_RESPONSE: &[u8] = &[]; const OCSP_RESPONSE: &[u8] = &[];
let mut times = Vec::new(); let mut times = Vec::new();
@ -227,7 +226,6 @@ impl Context {
end_entity, end_entity,
intermediates, intermediates,
&server_name, &server_name,
&mut SCTS.iter().copied(),
OCSP_RESPONSE, OCSP_RESPONSE,
self.now, self.now,
) )

View File

@ -157,7 +157,6 @@ pub struct MockServerVerifier {
cert_rejection_error: Option<Error>, cert_rejection_error: Option<Error>,
tls12_signature_error: Option<Error>, tls12_signature_error: Option<Error>,
tls13_signature_error: Option<Error>, tls13_signature_error: Option<Error>,
wants_scts: bool,
signature_schemes: Vec<SignatureScheme>, signature_schemes: Vec<SignatureScheme>,
} }
@ -167,14 +166,12 @@ impl ServerCertVerifier for MockServerVerifier {
end_entity: &rustls::Certificate, end_entity: &rustls::Certificate,
intermediates: &[rustls::Certificate], intermediates: &[rustls::Certificate],
server_name: &rustls::ServerName, server_name: &rustls::ServerName,
scts: &mut dyn Iterator<Item = &[u8]>,
oscp_response: &[u8], oscp_response: &[u8],
now: std::time::SystemTime, now: std::time::SystemTime,
) -> Result<ServerCertVerified, Error> { ) -> Result<ServerCertVerified, Error> {
let scts: Vec<Vec<u8>> = scts.map(|x| x.to_owned()).collect();
println!( println!(
"verify_server_cert({:?}, {:?}, {:?}, {:?}, {:?}, {:?})", "verify_server_cert({:?}, {:?}, {:?}, {:?}, {:?})",
end_entity, intermediates, server_name, scts, oscp_response, now end_entity, intermediates, server_name, oscp_response, now
); );
if let Some(error) = &self.cert_rejection_error { if let Some(error) = &self.cert_rejection_error {
Err(error.clone()) Err(error.clone())
@ -220,11 +217,6 @@ impl ServerCertVerifier for MockServerVerifier {
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.signature_schemes.clone() self.signature_schemes.clone()
} }
fn request_scts(&self) -> bool {
println!("request_scts? {:?}", self.wants_scts);
self.wants_scts
}
} }
impl MockServerVerifier { impl MockServerVerifier {
@ -270,7 +262,6 @@ impl Default for MockServerVerifier {
cert_rejection_error: None, cert_rejection_error: None,
tls12_signature_error: None, tls12_signature_error: None,
tls13_signature_error: None, tls13_signature_error: None,
wants_scts: false,
signature_schemes: WebPkiVerifier::verification_schemes(), signature_schemes: WebPkiVerifier::verification_schemes(),
} }
} }