mirror of
https://github.com/tlsnotary/tlsn.git
synced 2026-01-08 21:08:04 -05:00
refactor: clean up web pki (#967)
* refactor: clean up web pki * fix time import * clippy * fix wasm
This commit is contained in:
38
Cargo.lock
generated
38
Cargo.lock
generated
@@ -5931,6 +5931,7 @@ dependencies = [
|
|||||||
"rangeset",
|
"rangeset",
|
||||||
"rstest",
|
"rstest",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"semver 1.0.26",
|
"semver 1.0.26",
|
||||||
"serde",
|
"serde",
|
||||||
"serio",
|
"serio",
|
||||||
@@ -5951,7 +5952,7 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"uid-mux",
|
"uid-mux",
|
||||||
"web-spawn",
|
"web-spawn",
|
||||||
"webpki-roots 0.26.11",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5975,7 +5976,6 @@ dependencies = [
|
|||||||
"tlsn-core",
|
"tlsn-core",
|
||||||
"tlsn-data-fixtures",
|
"tlsn-data-fixtures",
|
||||||
"tlsn-tls-core",
|
"tlsn-tls-core",
|
||||||
"webpki-roots 0.26.11",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6013,6 +6013,8 @@ dependencies = [
|
|||||||
"rangeset",
|
"rangeset",
|
||||||
"rs_merkle",
|
"rs_merkle",
|
||||||
"rstest",
|
"rstest",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
@@ -6021,7 +6023,9 @@ dependencies = [
|
|||||||
"tlsn-tls-core",
|
"tlsn-tls-core",
|
||||||
"tlsn-utils",
|
"tlsn-utils",
|
||||||
"web-time 0.2.4",
|
"web-time 0.2.4",
|
||||||
"webpki-roots 0.26.11",
|
"webpki-root-certs",
|
||||||
|
"webpki-roots",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6068,11 +6072,9 @@ dependencies = [
|
|||||||
"spansy",
|
"spansy",
|
||||||
"tls-server-fixture",
|
"tls-server-fixture",
|
||||||
"tlsn",
|
"tlsn",
|
||||||
"tlsn-core",
|
|
||||||
"tlsn-formats",
|
"tlsn-formats",
|
||||||
"tlsn-server-fixture",
|
"tlsn-server-fixture",
|
||||||
"tlsn-server-fixture-certs",
|
"tlsn-server-fixture-certs",
|
||||||
"tlsn-tls-core",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -6119,10 +6121,8 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serio",
|
"serio",
|
||||||
"tlsn",
|
"tlsn",
|
||||||
"tlsn-core",
|
|
||||||
"tlsn-harness-core",
|
"tlsn-harness-core",
|
||||||
"tlsn-server-fixture-certs",
|
"tlsn-server-fixture-certs",
|
||||||
"tlsn-tls-core",
|
|
||||||
"tlsn-wasm",
|
"tlsn-wasm",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -6250,6 +6250,8 @@ dependencies = [
|
|||||||
"rand 0.9.1",
|
"rand 0.9.1",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"rstest",
|
"rstest",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"serde",
|
"serde",
|
||||||
"serio",
|
"serio",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
@@ -6324,14 +6326,15 @@ dependencies = [
|
|||||||
"ring 0.17.14",
|
"ring 0.17.14",
|
||||||
"rustls 0.20.9",
|
"rustls 0.20.9",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"sct",
|
"sct",
|
||||||
"sha2",
|
"sha2",
|
||||||
"tlsn-tls-backend",
|
"tlsn-tls-backend",
|
||||||
"tlsn-tls-core",
|
"tlsn-tls-core",
|
||||||
"tokio",
|
"tokio",
|
||||||
"web-time 0.2.4",
|
"web-time 0.2.4",
|
||||||
"webpki",
|
"webpki-roots",
|
||||||
"webpki-roots 0.26.11",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6344,6 +6347,8 @@ dependencies = [
|
|||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rstest",
|
"rstest",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tls-server-fixture",
|
"tls-server-fixture",
|
||||||
"tlsn-tls-client",
|
"tlsn-tls-client",
|
||||||
@@ -6361,13 +6366,14 @@ dependencies = [
|
|||||||
"rand 0.9.1",
|
"rand 0.9.1",
|
||||||
"ring 0.17.14",
|
"ring 0.17.14",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"sct",
|
"sct",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time 0.2.4",
|
"web-time 0.2.4",
|
||||||
"webpki",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7064,19 +7070,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-root-certs"
|
||||||
version = "0.26.11"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"webpki-roots 1.0.1",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ rs_merkle = { git = "https://github.com/tlsnotary/rs-merkle.git", rev = "85f3e82
|
|||||||
rstest = { version = "0.17" }
|
rstest = { version = "0.17" }
|
||||||
rustls = { version = "0.21" }
|
rustls = { version = "0.21" }
|
||||||
rustls-pemfile = { version = "1.0" }
|
rustls-pemfile = { version = "1.0" }
|
||||||
|
rustls-webpki = { version = "0.103" }
|
||||||
|
rustls-pki-types = { version = "1.12" }
|
||||||
sct = { version = "0.7" }
|
sct = { version = "0.7" }
|
||||||
semver = { version = "1.0" }
|
semver = { version = "1.0" }
|
||||||
serde = { version = "1.0" }
|
serde = { version = "1.0" }
|
||||||
@@ -157,7 +159,8 @@ wasm-bindgen = { version = "0.2" }
|
|||||||
wasm-bindgen-futures = { version = "0.4" }
|
wasm-bindgen-futures = { version = "0.4" }
|
||||||
web-spawn = { version = "0.2" }
|
web-spawn = { version = "0.2" }
|
||||||
web-time = { version = "0.2" }
|
web-time = { version = "0.2" }
|
||||||
webpki = { version = "0.22" }
|
webpki-roots = { version = "1.0" }
|
||||||
webpki-roots = { version = "0.26" }
|
webpki-root-certs = { version = "1.0" }
|
||||||
# Use the patched ws_stream_wasm to fix the issue https://github.com/najamelan/ws_stream_wasm/issues/12#issuecomment-1711902958
|
# Use the patched ws_stream_wasm to fix the issue https://github.com/najamelan/ws_stream_wasm/issues/12#issuecomment-1711902958
|
||||||
ws_stream_wasm = { git = "https://github.com/tlsnotary/ws_stream_wasm", rev = "2ed12aad9f0236e5321f577672f309920b2aef51" }
|
ws_stream_wasm = { git = "https://github.com/tlsnotary/ws_stream_wasm", rev = "2ed12aad9f0236e5321f577672f309920b2aef51" }
|
||||||
|
zeroize = { version = "1.8" }
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ rand = { workspace = true }
|
|||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tiny-keccak = { workspace = true, features = ["keccak"] }
|
tiny-keccak = { workspace = true, features = ["keccak"] }
|
||||||
webpki-roots = { workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
alloy-primitives = { version = "0.8.22", default-features = false }
|
alloy-primitives = { version = "0.8.22", default-features = false }
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ impl std::fmt::Display for AttestationBuilderError {
|
|||||||
mod test {
|
mod test {
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{HandshakeData, HandshakeDataV1_2},
|
connection::{CertBinding, CertBindingV1_2},
|
||||||
fixtures::{ConnectionFixture, encoding_provider},
|
fixtures::{ConnectionFixture, encoding_provider},
|
||||||
hash::Blake3,
|
hash::Blake3,
|
||||||
transcript::Transcript,
|
transcript::Transcript,
|
||||||
@@ -399,10 +399,10 @@ mod test {
|
|||||||
server_cert_data, ..
|
server_cert_data, ..
|
||||||
} = connection;
|
} = connection;
|
||||||
|
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
let CertBinding::V1_2(CertBindingV1_2 {
|
||||||
server_ephemeral_key,
|
server_ephemeral_key,
|
||||||
..
|
..
|
||||||
}) = server_cert_data.handshake
|
}) = server_cert_data.binding
|
||||||
else {
|
else {
|
||||||
panic!("expected v1.2 handshake data");
|
panic!("expected v1.2 handshake data");
|
||||||
};
|
};
|
||||||
@@ -470,10 +470,10 @@ mod test {
|
|||||||
..
|
..
|
||||||
} = connection;
|
} = connection;
|
||||||
|
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
let CertBinding::V1_2(CertBindingV1_2 {
|
||||||
server_ephemeral_key,
|
server_ephemeral_key,
|
||||||
..
|
..
|
||||||
}) = server_cert_data.handshake
|
}) = server_cert_data.binding
|
||||||
else {
|
else {
|
||||||
panic!("expected v1.2 handshake data");
|
panic!("expected v1.2 handshake data");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{CertificateVerificationError, ServerCertData, ServerEphemKey, ServerName},
|
connection::{HandshakeData, HandshakeVerificationError, ServerEphemKey, ServerName},
|
||||||
hash::{Blinded, HashAlgorithm, HashProviderError, TypedHash},
|
hash::{Blinded, HashAlgorithm, HashProviderError, TypedHash},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,14 +30,14 @@ use crate::{CryptoProvider, hash::HashAlgorithmExt, serialize::impl_domain_separ
|
|||||||
|
|
||||||
/// Opens a [`ServerCertCommitment`].
|
/// Opens a [`ServerCertCommitment`].
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct ServerCertOpening(Blinded<ServerCertData>);
|
pub struct ServerCertOpening(Blinded<HandshakeData>);
|
||||||
|
|
||||||
impl_domain_separator!(ServerCertOpening);
|
impl_domain_separator!(ServerCertOpening);
|
||||||
|
|
||||||
opaque_debug::implement!(ServerCertOpening);
|
opaque_debug::implement!(ServerCertOpening);
|
||||||
|
|
||||||
impl ServerCertOpening {
|
impl ServerCertOpening {
|
||||||
pub(crate) fn new(data: ServerCertData) -> Self {
|
pub(crate) fn new(data: HandshakeData) -> Self {
|
||||||
Self(Blinded::new(data))
|
Self(Blinded::new(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ impl ServerCertOpening {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the server identity data.
|
/// Returns the server identity data.
|
||||||
pub fn data(&self) -> &ServerCertData {
|
pub fn data(&self) -> &HandshakeData {
|
||||||
self.0.data()
|
self.0.data()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +122,8 @@ impl From<HashProviderError> for ServerIdentityProofError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CertificateVerificationError> for ServerIdentityProofError {
|
impl From<HandshakeVerificationError> for ServerIdentityProofError {
|
||||||
fn from(err: CertificateVerificationError) -> Self {
|
fn from(err: HandshakeVerificationError) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ErrorKind::Certificate,
|
kind: ErrorKind::Certificate,
|
||||||
message: err.to_string(),
|
message: err.to_string(),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Attestation fixtures.
|
//! Attestation fixtures.
|
||||||
|
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{HandshakeData, HandshakeDataV1_2},
|
connection::{CertBinding, CertBindingV1_2},
|
||||||
fixtures::ConnectionFixture,
|
fixtures::ConnectionFixture,
|
||||||
hash::HashAlgorithm,
|
hash::HashAlgorithm,
|
||||||
transcript::{
|
transcript::{
|
||||||
@@ -67,7 +67,7 @@ pub fn request_fixture(
|
|||||||
let mut request_builder = Request::builder(&request_config);
|
let mut request_builder = Request::builder(&request_config);
|
||||||
request_builder
|
request_builder
|
||||||
.server_name(server_name)
|
.server_name(server_name)
|
||||||
.server_cert_data(server_cert_data)
|
.handshake_data(server_cert_data)
|
||||||
.transcript(transcript);
|
.transcript(transcript);
|
||||||
|
|
||||||
let (request, _) = request_builder.build(&provider).unwrap();
|
let (request, _) = request_builder.build(&provider).unwrap();
|
||||||
@@ -91,12 +91,12 @@ pub fn attestation_fixture(
|
|||||||
..
|
..
|
||||||
} = connection;
|
} = connection;
|
||||||
|
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
let CertBinding::V1_2(CertBindingV1_2 {
|
||||||
server_ephemeral_key,
|
server_ephemeral_key,
|
||||||
..
|
..
|
||||||
}) = server_cert_data.handshake
|
}) = server_cert_data.binding
|
||||||
else {
|
else {
|
||||||
panic!("expected v1.2 handshake data");
|
panic!("expected v1.2 binding data");
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut provider = CryptoProvider::default();
|
let mut provider = CryptoProvider::default();
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
use tls_core::{
|
use tlsn_core::{hash::HashProvider, webpki::ServerCertVerifier};
|
||||||
anchors::{OwnedTrustAnchor, RootCertStore},
|
|
||||||
verify::WebPkiVerifier,
|
|
||||||
};
|
|
||||||
use tlsn_core::hash::HashProvider;
|
|
||||||
|
|
||||||
use crate::signing::{SignatureVerifierProvider, SignerProvider};
|
use crate::signing::{SignatureVerifierProvider, SignerProvider};
|
||||||
|
|
||||||
@@ -28,7 +24,7 @@ pub struct CryptoProvider {
|
|||||||
/// This is used to verify the server's certificate chain.
|
/// This is used to verify the server's certificate chain.
|
||||||
///
|
///
|
||||||
/// The default verifier uses the Mozilla root certificates.
|
/// The default verifier uses the Mozilla root certificates.
|
||||||
pub cert: WebPkiVerifier,
|
pub cert: ServerCertVerifier,
|
||||||
/// Signer provider.
|
/// Signer provider.
|
||||||
///
|
///
|
||||||
/// This is used for signing attestations.
|
/// This is used for signing attestations.
|
||||||
@@ -45,21 +41,9 @@ impl Default for CryptoProvider {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
hash: Default::default(),
|
hash: Default::default(),
|
||||||
cert: default_cert_verifier(),
|
cert: ServerCertVerifier::mozilla(),
|
||||||
signer: Default::default(),
|
signer: Default::default(),
|
||||||
signature: Default::default(),
|
signature: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_cert_verifier() -> WebPkiVerifier {
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
|
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
ta.subject.as_ref(),
|
|
||||||
ta.subject_public_key_info.as_ref(),
|
|
||||||
ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
WebPkiVerifier::new(root_store, None)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{ServerCertData, ServerName},
|
connection::{HandshakeData, ServerName},
|
||||||
transcript::{Transcript, TranscriptCommitment, TranscriptSecret},
|
transcript::{Transcript, TranscriptCommitment, TranscriptSecret},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ use crate::{
|
|||||||
pub struct RequestBuilder<'a> {
|
pub struct RequestBuilder<'a> {
|
||||||
config: &'a RequestConfig,
|
config: &'a RequestConfig,
|
||||||
server_name: Option<ServerName>,
|
server_name: Option<ServerName>,
|
||||||
server_cert_data: Option<ServerCertData>,
|
handshake_data: Option<HandshakeData>,
|
||||||
transcript: Option<Transcript>,
|
transcript: Option<Transcript>,
|
||||||
transcript_commitments: Vec<TranscriptCommitment>,
|
transcript_commitments: Vec<TranscriptCommitment>,
|
||||||
transcript_commitment_secrets: Vec<TranscriptSecret>,
|
transcript_commitment_secrets: Vec<TranscriptSecret>,
|
||||||
@@ -25,7 +25,7 @@ impl<'a> RequestBuilder<'a> {
|
|||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
server_name: None,
|
server_name: None,
|
||||||
server_cert_data: None,
|
handshake_data: None,
|
||||||
transcript: None,
|
transcript: None,
|
||||||
transcript_commitments: Vec::new(),
|
transcript_commitments: Vec::new(),
|
||||||
transcript_commitment_secrets: Vec::new(),
|
transcript_commitment_secrets: Vec::new(),
|
||||||
@@ -38,9 +38,9 @@ impl<'a> RequestBuilder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the server identity data.
|
/// Sets the handshake data.
|
||||||
pub fn server_cert_data(&mut self, data: ServerCertData) -> &mut Self {
|
pub fn handshake_data(&mut self, data: HandshakeData) -> &mut Self {
|
||||||
self.server_cert_data = Some(data);
|
self.handshake_data = Some(data);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ impl<'a> RequestBuilder<'a> {
|
|||||||
let Self {
|
let Self {
|
||||||
config,
|
config,
|
||||||
server_name,
|
server_name,
|
||||||
server_cert_data,
|
handshake_data: server_cert_data,
|
||||||
transcript,
|
transcript,
|
||||||
transcript_commitments,
|
transcript_commitments,
|
||||||
transcript_commitment_secrets,
|
transcript_commitment_secrets,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub(crate) use impl_domain_separator;
|
|||||||
|
|
||||||
impl_domain_separator!(tlsn_core::connection::ServerEphemKey);
|
impl_domain_separator!(tlsn_core::connection::ServerEphemKey);
|
||||||
impl_domain_separator!(tlsn_core::connection::ConnectionInfo);
|
impl_domain_separator!(tlsn_core::connection::ConnectionInfo);
|
||||||
impl_domain_separator!(tlsn_core::connection::HandshakeData);
|
impl_domain_separator!(tlsn_core::connection::CertBinding);
|
||||||
impl_domain_separator!(tlsn_core::transcript::TranscriptCommitment);
|
impl_domain_separator!(tlsn_core::transcript::TranscriptCommitment);
|
||||||
impl_domain_separator!(tlsn_core::transcript::TranscriptSecret);
|
impl_domain_separator!(tlsn_core::transcript::TranscriptSecret);
|
||||||
impl_domain_separator!(tlsn_core::transcript::encoding::EncodingCommitment);
|
impl_domain_separator!(tlsn_core::transcript::encoding::EncodingCommitment);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use tlsn_attestation::{
|
|||||||
signing::SignatureAlgId,
|
signing::SignatureAlgId,
|
||||||
};
|
};
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{HandshakeData, HandshakeDataV1_2},
|
connection::{CertBinding, CertBindingV1_2},
|
||||||
fixtures::{self, ConnectionFixture, encoder_secret},
|
fixtures::{self, ConnectionFixture, encoder_secret},
|
||||||
hash::Blake3,
|
hash::Blake3,
|
||||||
transcript::{
|
transcript::{
|
||||||
@@ -36,10 +36,10 @@ fn test_api() {
|
|||||||
server_cert_data,
|
server_cert_data,
|
||||||
} = ConnectionFixture::tlsnotary(transcript.length());
|
} = ConnectionFixture::tlsnotary(transcript.length());
|
||||||
|
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
let CertBinding::V1_2(CertBindingV1_2 {
|
||||||
server_ephemeral_key,
|
server_ephemeral_key,
|
||||||
..
|
..
|
||||||
}) = server_cert_data.handshake.clone()
|
}) = server_cert_data.binding.clone()
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
@@ -72,7 +72,7 @@ fn test_api() {
|
|||||||
|
|
||||||
request_builder
|
request_builder
|
||||||
.server_name(server_name.clone())
|
.server_name(server_name.clone())
|
||||||
.server_cert_data(server_cert_data)
|
.handshake_data(server_cert_data)
|
||||||
.transcript(transcript)
|
.transcript(transcript)
|
||||||
.transcript_commitments(
|
.transcript_commitments(
|
||||||
vec![TranscriptSecret::Encoding(encoding_tree)],
|
vec![TranscriptSecret::Encoding(encoding_tree)],
|
||||||
|
|||||||
@@ -36,10 +36,14 @@ thiserror = { workspace = true }
|
|||||||
tiny-keccak = { workspace = true, features = ["keccak"] }
|
tiny-keccak = { workspace = true, features = ["keccak"] }
|
||||||
web-time = { workspace = true }
|
web-time = { workspace = true }
|
||||||
webpki-roots = { workspace = true }
|
webpki-roots = { workspace = true }
|
||||||
|
rustls-webpki = { workspace = true, features = ["ring"] }
|
||||||
|
rustls-pki-types = { workspace = true }
|
||||||
itybity = { workspace = true }
|
itybity = { workspace = true }
|
||||||
|
zeroize = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bincode = { workspace = true }
|
bincode = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
rstest = { workspace = true }
|
rstest = { workspace = true }
|
||||||
tlsn-data-fixtures = { workspace = true }
|
tlsn-data-fixtures = { workspace = true }
|
||||||
|
webpki-root-certs = { workspace = true }
|
||||||
|
|||||||
@@ -2,16 +2,11 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use rustls_pki_types as webpki_types;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tls_core::{
|
use tls_core::msgs::{codec::Codec, enums::NamedGroup, handshake::ServerECDHParams};
|
||||||
msgs::{
|
|
||||||
codec::Codec,
|
use crate::webpki::{CertificateDer, ServerCertVerifier, ServerCertVerifierError};
|
||||||
enums::NamedGroup,
|
|
||||||
handshake::{DigitallySignedStruct, ServerECDHParams},
|
|
||||||
},
|
|
||||||
verify::{ServerCertVerifier as _, WebPkiVerifier},
|
|
||||||
};
|
|
||||||
use web_time::{Duration, UNIX_EPOCH};
|
|
||||||
|
|
||||||
/// TLS version.
|
/// TLS version.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
@@ -35,40 +30,82 @@ impl TryFrom<tls_core::msgs::enums::ProtocolVersion> for TlsVersion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Server's name, a.k.a. the DNS name.
|
/// Server's name.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct ServerName(String);
|
pub enum ServerName {
|
||||||
|
/// DNS name.
|
||||||
|
Dns(DnsName),
|
||||||
|
}
|
||||||
|
|
||||||
impl ServerName {
|
impl ServerName {
|
||||||
/// Creates a new server name.
|
pub(crate) fn to_webpki(&self) -> webpki_types::ServerName<'static> {
|
||||||
pub fn new(name: String) -> Self {
|
match self {
|
||||||
Self(name)
|
ServerName::Dns(name) => webpki_types::ServerName::DnsName(
|
||||||
}
|
webpki_types::DnsName::try_from(name.0.as_str())
|
||||||
|
.expect("name was validated")
|
||||||
/// Returns the name as a string.
|
.to_owned(),
|
||||||
pub fn as_str(&self) -> &str {
|
),
|
||||||
&self.0
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for ServerName {
|
|
||||||
fn from(name: &str) -> Self {
|
|
||||||
Self(name.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for ServerName {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ServerName {
|
impl fmt::Display for ServerName {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ServerName::Dns(name) => write!(f, "{name}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DNS name.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
|
pub struct DnsName(String);
|
||||||
|
|
||||||
|
impl DnsName {
|
||||||
|
/// Returns the DNS name as a string.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DnsName {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
write!(f, "{}", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for DnsName {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error returned when a DNS name is invalid.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("invalid DNS name")]
|
||||||
|
pub struct InvalidDnsNameError {}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for DnsName {
|
||||||
|
type Error = InvalidDnsNameError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
// Borrow validation from rustls
|
||||||
|
match webpki_types::DnsName::try_from_str(value) {
|
||||||
|
Ok(_) => Ok(DnsName(value.to_string())),
|
||||||
|
Err(_) => Err(InvalidDnsNameError {}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for DnsName {
|
||||||
|
type Error = InvalidDnsNameError;
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
Self::try_from(value.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Type of a public key.
|
/// Type of a public key.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
@@ -98,6 +135,25 @@ pub enum SignatureScheme {
|
|||||||
ED25519 = 0x0807,
|
ED25519 = 0x0807,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SignatureScheme {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA1 => write!(f, "RSA_PKCS1_SHA1"),
|
||||||
|
SignatureScheme::ECDSA_SHA1_Legacy => write!(f, "ECDSA_SHA1_Legacy"),
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA256 => write!(f, "RSA_PKCS1_SHA256"),
|
||||||
|
SignatureScheme::ECDSA_NISTP256_SHA256 => write!(f, "ECDSA_NISTP256_SHA256"),
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA384 => write!(f, "RSA_PKCS1_SHA384"),
|
||||||
|
SignatureScheme::ECDSA_NISTP384_SHA384 => write!(f, "ECDSA_NISTP384_SHA384"),
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA512 => write!(f, "RSA_PKCS1_SHA512"),
|
||||||
|
SignatureScheme::ECDSA_NISTP521_SHA512 => write!(f, "ECDSA_NISTP521_SHA512"),
|
||||||
|
SignatureScheme::RSA_PSS_SHA256 => write!(f, "RSA_PSS_SHA256"),
|
||||||
|
SignatureScheme::RSA_PSS_SHA384 => write!(f, "RSA_PSS_SHA384"),
|
||||||
|
SignatureScheme::RSA_PSS_SHA512 => write!(f, "RSA_PSS_SHA512"),
|
||||||
|
SignatureScheme::ED25519 => write!(f, "ED25519"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<tls_core::msgs::enums::SignatureScheme> for SignatureScheme {
|
impl TryFrom<tls_core::msgs::enums::SignatureScheme> for SignatureScheme {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
@@ -142,16 +198,6 @@ impl From<SignatureScheme> for tls_core::msgs::enums::SignatureScheme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// X.509 certificate, DER encoded.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct Certificate(pub Vec<u8>);
|
|
||||||
|
|
||||||
impl From<tls_core::key::Certificate> for Certificate {
|
|
||||||
fn from(cert: tls_core::key::Certificate) -> Self {
|
|
||||||
Self(cert.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Server's signature of the key exchange parameters.
|
/// Server's signature of the key exchange parameters.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ServerSignature {
|
pub struct ServerSignature {
|
||||||
@@ -220,9 +266,9 @@ pub struct TranscriptLength {
|
|||||||
pub received: u32,
|
pub received: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TLS 1.2 handshake data.
|
/// TLS 1.2 certificate binding.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct HandshakeDataV1_2 {
|
pub struct CertBindingV1_2 {
|
||||||
/// Client random.
|
/// Client random.
|
||||||
pub client_random: [u8; 32],
|
pub client_random: [u8; 32],
|
||||||
/// Server random.
|
/// Server random.
|
||||||
@@ -231,13 +277,18 @@ pub struct HandshakeDataV1_2 {
|
|||||||
pub server_ephemeral_key: ServerEphemKey,
|
pub server_ephemeral_key: ServerEphemKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TLS handshake data.
|
/// TLS certificate binding.
|
||||||
|
///
|
||||||
|
/// This is the data that the server signs using its public key in the
|
||||||
|
/// certificate it presents during the TLS handshake. This provides a binding
|
||||||
|
/// between the server's identity and the ephemeral keys used to authenticate
|
||||||
|
/// the TLS session.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum HandshakeData {
|
pub enum CertBinding {
|
||||||
/// TLS 1.2 handshake data.
|
/// TLS 1.2 certificate binding.
|
||||||
V1_2(HandshakeDataV1_2),
|
V1_2(CertBindingV1_2),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify data from the TLS handshake finished messages.
|
/// Verify data from the TLS handshake finished messages.
|
||||||
@@ -249,19 +300,19 @@ pub struct VerifyData {
|
|||||||
pub server_finished: Vec<u8>,
|
pub server_finished: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Server certificate and handshake data.
|
/// TLS handshake data.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ServerCertData {
|
pub struct HandshakeData {
|
||||||
/// Certificate chain.
|
/// Server certificate chain.
|
||||||
pub certs: Vec<Certificate>,
|
pub certs: Vec<CertificateDer>,
|
||||||
/// Server signature of the key exchange parameters.
|
/// Server certificate signature over the binding message.
|
||||||
pub sig: ServerSignature,
|
pub sig: ServerSignature,
|
||||||
/// TLS handshake data.
|
/// Certificate binding.
|
||||||
pub handshake: HandshakeData,
|
pub binding: CertBinding,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerCertData {
|
impl HandshakeData {
|
||||||
/// Verifies the server certificate data.
|
/// Verifies the handshake data.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@@ -271,53 +322,35 @@ impl ServerCertData {
|
|||||||
/// * `server_name` - The server name.
|
/// * `server_name` - The server name.
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
&self,
|
&self,
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
time: u64,
|
time: u64,
|
||||||
server_ephemeral_key: &ServerEphemKey,
|
server_ephemeral_key: &ServerEphemKey,
|
||||||
server_name: &ServerName,
|
server_name: &ServerName,
|
||||||
) -> Result<(), CertificateVerificationError> {
|
) -> Result<(), HandshakeVerificationError> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
let CertBinding::V1_2(CertBindingV1_2 {
|
||||||
client_random,
|
client_random,
|
||||||
server_random,
|
server_random,
|
||||||
server_ephemeral_key: expected_server_ephemeral_key,
|
server_ephemeral_key: expected_server_ephemeral_key,
|
||||||
}) = &self.handshake
|
}) = &self.binding
|
||||||
else {
|
else {
|
||||||
unreachable!("only TLS 1.2 is implemented")
|
unreachable!("only TLS 1.2 is implemented")
|
||||||
};
|
};
|
||||||
|
|
||||||
if server_ephemeral_key != expected_server_ephemeral_key {
|
if server_ephemeral_key != expected_server_ephemeral_key {
|
||||||
return Err(CertificateVerificationError::InvalidServerEphemeralKey);
|
return Err(HandshakeVerificationError::InvalidServerEphemeralKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify server name.
|
let (end_entity, intermediates) = self
|
||||||
let server_name = tls_core::dns::ServerName::try_from(server_name.as_ref())
|
|
||||||
.map_err(|_| CertificateVerificationError::InvalidIdentity(server_name.clone()))?;
|
|
||||||
|
|
||||||
// Verify server certificate.
|
|
||||||
let cert_chain = self
|
|
||||||
.certs
|
.certs
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|cert| tls_core::key::Certificate(cert.0))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let (end_entity, intermediates) = cert_chain
|
|
||||||
.split_first()
|
.split_first()
|
||||||
.ok_or(CertificateVerificationError::MissingCerts)?;
|
.ok_or(HandshakeVerificationError::MissingCerts)?;
|
||||||
|
|
||||||
// Verify the end entity cert is valid for the provided server name
|
// Verify the end entity cert is valid for the provided server name
|
||||||
// and that it chains to at least one of the roots we trust.
|
// and that it chains to at least one of the roots we trust.
|
||||||
verifier
|
verifier
|
||||||
.verify_server_cert(
|
.verify_server_cert(end_entity, intermediates, server_name, time)
|
||||||
end_entity,
|
.map_err(HandshakeVerificationError::ServerCert)?;
|
||||||
intermediates,
|
|
||||||
&server_name,
|
|
||||||
&mut [].into_iter(),
|
|
||||||
&[],
|
|
||||||
UNIX_EPOCH + Duration::from_secs(time),
|
|
||||||
)
|
|
||||||
.map_err(|_| CertificateVerificationError::InvalidCert)?;
|
|
||||||
|
|
||||||
// Verify the signature matches the certificate and key exchange parameters.
|
// Verify the signature matches the certificate and key exchange parameters.
|
||||||
let mut message = Vec::new();
|
let mut message = Vec::new();
|
||||||
@@ -325,11 +358,31 @@ impl ServerCertData {
|
|||||||
message.extend_from_slice(server_random);
|
message.extend_from_slice(server_random);
|
||||||
message.extend_from_slice(&server_ephemeral_key.kx_params());
|
message.extend_from_slice(&server_ephemeral_key.kx_params());
|
||||||
|
|
||||||
let dss = DigitallySignedStruct::new(self.sig.scheme.into(), self.sig.sig.clone());
|
use webpki::ring as alg;
|
||||||
|
let sig_alg = match self.sig.scheme {
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA256 => alg::RSA_PKCS1_2048_8192_SHA256,
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA384 => alg::RSA_PKCS1_2048_8192_SHA384,
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA512 => alg::RSA_PKCS1_2048_8192_SHA512,
|
||||||
|
SignatureScheme::RSA_PSS_SHA256 => alg::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
|
||||||
|
SignatureScheme::RSA_PSS_SHA384 => alg::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
|
||||||
|
SignatureScheme::RSA_PSS_SHA512 => alg::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
|
||||||
|
SignatureScheme::ECDSA_NISTP256_SHA256 => alg::ECDSA_P256_SHA256,
|
||||||
|
SignatureScheme::ECDSA_NISTP384_SHA384 => alg::ECDSA_P384_SHA384,
|
||||||
|
SignatureScheme::ED25519 => alg::ED25519,
|
||||||
|
scheme => {
|
||||||
|
return Err(HandshakeVerificationError::UnsupportedSignatureScheme(
|
||||||
|
scheme,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
verifier
|
let end_entity = webpki_types::CertificateDer::from(end_entity.0.as_slice());
|
||||||
.verify_tls12_signature(&message, end_entity, &dss)
|
let end_entity = webpki::EndEntityCert::try_from(&end_entity)
|
||||||
.map_err(|_| CertificateVerificationError::InvalidServerSignature)?;
|
.map_err(|_| HandshakeVerificationError::InvalidEndEntityCertificate)?;
|
||||||
|
|
||||||
|
end_entity
|
||||||
|
.verify_signature(sig_alg, &message, &self.sig.sig)
|
||||||
|
.map_err(|_| HandshakeVerificationError::InvalidServerSignature)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -338,58 +391,51 @@ impl ServerCertData {
|
|||||||
/// Errors that can occur when verifying a certificate chain or signature.
|
/// Errors that can occur when verifying a certificate chain or signature.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum CertificateVerificationError {
|
pub enum HandshakeVerificationError {
|
||||||
#[error("invalid server identity: {0}")]
|
#[error("invalid end entity certificate")]
|
||||||
InvalidIdentity(ServerName),
|
InvalidEndEntityCertificate,
|
||||||
#[error("missing server certificates")]
|
#[error("missing server certificates")]
|
||||||
MissingCerts,
|
MissingCerts,
|
||||||
#[error("invalid server certificate")]
|
|
||||||
InvalidCert,
|
|
||||||
#[error("invalid server signature")]
|
#[error("invalid server signature")]
|
||||||
InvalidServerSignature,
|
InvalidServerSignature,
|
||||||
#[error("invalid server ephemeral key")]
|
#[error("invalid server ephemeral key")]
|
||||||
InvalidServerEphemeralKey,
|
InvalidServerEphemeralKey,
|
||||||
|
#[error("server certificate verification failed: {0}")]
|
||||||
|
ServerCert(ServerCertVerifierError),
|
||||||
|
#[error("unsupported signature scheme: {0}")]
|
||||||
|
UnsupportedSignatureScheme(SignatureScheme),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{fixtures::ConnectionFixture, transcript::Transcript};
|
use crate::{fixtures::ConnectionFixture, transcript::Transcript, webpki::RootCertStore};
|
||||||
|
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
use rstest::*;
|
use rstest::*;
|
||||||
use tls_core::{
|
|
||||||
anchors::{OwnedTrustAnchor, RootCertStore},
|
|
||||||
verify::WebPkiVerifier,
|
|
||||||
};
|
|
||||||
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
|
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
|
||||||
|
|
||||||
#[fixture]
|
#[fixture]
|
||||||
#[once]
|
#[once]
|
||||||
fn verifier() -> WebPkiVerifier {
|
fn verifier() -> ServerCertVerifier {
|
||||||
let mut root_store = RootCertStore::empty();
|
let mut root_store = RootCertStore {
|
||||||
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
|
roots: webpki_root_certs::TLS_SERVER_ROOT_CERTS
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
.iter()
|
||||||
ta.subject.as_ref(),
|
.map(|c| CertificateDer(c.to_vec()))
|
||||||
ta.subject_public_key_info.as_ref(),
|
.collect(),
|
||||||
ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
|
};
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Add a cert which is no longer included in the Mozilla root store.
|
// Add a cert which is no longer included in the Mozilla root store.
|
||||||
let cert = tls_core::key::Certificate(
|
root_store.roots.push(
|
||||||
appliedzkp()
|
appliedzkp()
|
||||||
.server_cert_data
|
.server_cert_data
|
||||||
.certs
|
.certs
|
||||||
.last()
|
.last()
|
||||||
.expect("chain is valid")
|
.expect("chain is valid")
|
||||||
.0
|
|
||||||
.clone(),
|
.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
root_store.add(&cert).unwrap();
|
ServerCertVerifier::new(&root_store).unwrap()
|
||||||
|
|
||||||
WebPkiVerifier::new(root_store, None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tlsnotary() -> ConnectionFixture {
|
fn tlsnotary() -> ConnectionFixture {
|
||||||
@@ -405,7 +451,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_sucess_ca_implicit(
|
fn test_verify_cert_chain_sucess_ca_implicit(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
// Remove the CA cert
|
// Remove the CA cert
|
||||||
@@ -417,7 +463,7 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
@@ -428,7 +474,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_success_ca_explicit(
|
fn test_verify_cert_chain_success_ca_explicit(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] data: ConnectionFixture,
|
#[case] data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
assert!(data
|
assert!(data
|
||||||
@@ -437,7 +483,7 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
@@ -447,7 +493,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_fail_bad_time(
|
fn test_verify_cert_chain_fail_bad_time(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] data: ConnectionFixture,
|
#[case] data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
// unix time when the cert chain was NOT valid
|
// unix time when the cert chain was NOT valid
|
||||||
@@ -457,12 +503,12 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
bad_time,
|
bad_time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidCert
|
HandshakeVerificationError::ServerCert(_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +517,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_fail_no_interm_cert(
|
fn test_verify_cert_chain_fail_no_interm_cert(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
// Remove the CA cert
|
// Remove the CA cert
|
||||||
@@ -483,12 +529,12 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidCert
|
HandshakeVerificationError::ServerCert(_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,7 +544,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
|
fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
// Remove the intermediate cert
|
// Remove the intermediate cert
|
||||||
@@ -508,12 +554,12 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidCert
|
HandshakeVerificationError::ServerCert(_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,24 +568,24 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_fail_bad_ee_cert(
|
fn test_verify_cert_chain_fail_bad_ee_cert(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
|
let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
|
||||||
|
|
||||||
// Change the end entity cert
|
// Change the end entity cert
|
||||||
data.server_cert_data.certs[0] = Certificate(ee.to_vec());
|
data.server_cert_data.certs[0] = CertificateDer(ee.to_vec());
|
||||||
|
|
||||||
let err = data.server_cert_data.verify(
|
let err = data.server_cert_data.verify(
|
||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidCert
|
HandshakeVerificationError::ServerCert(_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,23 +594,23 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_sig_ke_params_fail_bad_client_random(
|
fn test_verify_sig_ke_params_fail_bad_client_random(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 { client_random, .. }) =
|
let CertBinding::V1_2(CertBindingV1_2 { client_random, .. }) =
|
||||||
&mut data.server_cert_data.handshake;
|
&mut data.server_cert_data.binding;
|
||||||
client_random[31] = client_random[31].wrapping_add(1);
|
client_random[31] = client_random[31].wrapping_add(1);
|
||||||
|
|
||||||
let err = data.server_cert_data.verify(
|
let err = data.server_cert_data.verify(
|
||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidServerSignature
|
HandshakeVerificationError::InvalidServerSignature
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +619,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_sig_ke_params_fail_bad_sig(
|
fn test_verify_sig_ke_params_fail_bad_sig(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
|
data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
|
||||||
@@ -582,12 +628,12 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidServerSignature
|
HandshakeVerificationError::InvalidServerSignature
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,10 +642,10 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_check_dns_name_present_in_cert_fail_bad_host(
|
fn test_check_dns_name_present_in_cert_fail_bad_host(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] data: ConnectionFixture,
|
#[case] data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
let bad_name = ServerName::from("badhost.com");
|
let bad_name = ServerName::Dns(DnsName::try_from("badhost.com").unwrap());
|
||||||
|
|
||||||
let err = data.server_cert_data.verify(
|
let err = data.server_cert_data.verify(
|
||||||
verifier,
|
verifier,
|
||||||
@@ -610,7 +656,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidCert
|
HandshakeVerificationError::ServerCert(_)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +664,7 @@ mod tests {
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_invalid_ephemeral_key(verifier: &WebPkiVerifier, #[case] data: ConnectionFixture) {
|
fn test_invalid_ephemeral_key(verifier: &ServerCertVerifier, #[case] data: ConnectionFixture) {
|
||||||
let wrong_ephemeral_key = ServerEphemKey {
|
let wrong_ephemeral_key = ServerEphemKey {
|
||||||
typ: KeyType::SECP256R1,
|
typ: KeyType::SECP256R1,
|
||||||
key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
|
key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
|
||||||
@@ -628,12 +674,12 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
&wrong_ephemeral_key,
|
&wrong_ephemeral_key,
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::InvalidServerEphemeralKey
|
HandshakeVerificationError::InvalidServerEphemeralKey
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,7 +688,7 @@ mod tests {
|
|||||||
#[case::tlsnotary(tlsnotary())]
|
#[case::tlsnotary(tlsnotary())]
|
||||||
#[case::appliedzkp(appliedzkp())]
|
#[case::appliedzkp(appliedzkp())]
|
||||||
fn test_verify_cert_chain_fail_no_cert(
|
fn test_verify_cert_chain_fail_no_cert(
|
||||||
verifier: &WebPkiVerifier,
|
verifier: &ServerCertVerifier,
|
||||||
#[case] mut data: ConnectionFixture,
|
#[case] mut data: ConnectionFixture,
|
||||||
) {
|
) {
|
||||||
// Empty certs
|
// Empty certs
|
||||||
@@ -652,12 +698,12 @@ mod tests {
|
|||||||
verifier,
|
verifier,
|
||||||
data.connection_info.time,
|
data.connection_info.time,
|
||||||
data.server_ephemeral_key(),
|
data.server_ephemeral_key(),
|
||||||
&ServerName::from(data.server_name.as_ref()),
|
&data.server_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err.unwrap_err(),
|
err.unwrap_err(),
|
||||||
CertificateVerificationError::MissingCerts
|
HandshakeVerificationError::MissingCerts
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ use hex::FromHex;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::{
|
connection::{
|
||||||
Certificate, ConnectionInfo, HandshakeData, HandshakeDataV1_2, KeyType, ServerCertData,
|
CertBinding, CertBindingV1_2, ConnectionInfo, DnsName, HandshakeData, KeyType,
|
||||||
ServerEphemKey, ServerName, ServerSignature, SignatureScheme, TlsVersion, TranscriptLength,
|
ServerEphemKey, ServerName, ServerSignature, SignatureScheme, TlsVersion, TranscriptLength,
|
||||||
},
|
},
|
||||||
transcript::{
|
transcript::{
|
||||||
encoding::{EncoderSecret, EncodingProvider},
|
encoding::{EncoderSecret, EncodingProvider},
|
||||||
Transcript,
|
Transcript,
|
||||||
},
|
},
|
||||||
|
webpki::CertificateDer,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A fixture containing various TLS connection data.
|
/// A fixture containing various TLS connection data.
|
||||||
@@ -23,24 +24,26 @@ use crate::{
|
|||||||
pub struct ConnectionFixture {
|
pub struct ConnectionFixture {
|
||||||
pub server_name: ServerName,
|
pub server_name: ServerName,
|
||||||
pub connection_info: ConnectionInfo,
|
pub connection_info: ConnectionInfo,
|
||||||
pub server_cert_data: ServerCertData,
|
pub server_cert_data: HandshakeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionFixture {
|
impl ConnectionFixture {
|
||||||
/// Returns a connection fixture for tlsnotary.org.
|
/// Returns a connection fixture for tlsnotary.org.
|
||||||
pub fn tlsnotary(transcript_length: TranscriptLength) -> Self {
|
pub fn tlsnotary(transcript_length: TranscriptLength) -> Self {
|
||||||
ConnectionFixture {
|
ConnectionFixture {
|
||||||
server_name: ServerName::new("tlsnotary.org".to_string()),
|
server_name: ServerName::Dns(DnsName::try_from("tlsnotary.org").unwrap()),
|
||||||
connection_info: ConnectionInfo {
|
connection_info: ConnectionInfo {
|
||||||
time: 1671637529,
|
time: 1671637529,
|
||||||
version: TlsVersion::V1_2,
|
version: TlsVersion::V1_2,
|
||||||
transcript_length,
|
transcript_length,
|
||||||
},
|
},
|
||||||
server_cert_data: ServerCertData {
|
server_cert_data: HandshakeData {
|
||||||
certs: vec![
|
certs: vec![
|
||||||
Certificate(include_bytes!("fixtures/data/tlsnotary.org/ee.der").to_vec()),
|
CertificateDer(include_bytes!("fixtures/data/tlsnotary.org/ee.der").to_vec()),
|
||||||
Certificate(include_bytes!("fixtures/data/tlsnotary.org/inter.der").to_vec()),
|
CertificateDer(
|
||||||
Certificate(include_bytes!("fixtures/data/tlsnotary.org/ca.der").to_vec()),
|
include_bytes!("fixtures/data/tlsnotary.org/inter.der").to_vec(),
|
||||||
|
),
|
||||||
|
CertificateDer(include_bytes!("fixtures/data/tlsnotary.org/ca.der").to_vec()),
|
||||||
],
|
],
|
||||||
sig: ServerSignature {
|
sig: ServerSignature {
|
||||||
scheme: SignatureScheme::RSA_PKCS1_SHA256,
|
scheme: SignatureScheme::RSA_PKCS1_SHA256,
|
||||||
@@ -49,7 +52,7 @@ impl ConnectionFixture {
|
|||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
},
|
},
|
||||||
handshake: HandshakeData::V1_2(HandshakeDataV1_2 {
|
binding: CertBinding::V1_2(CertBindingV1_2 {
|
||||||
client_random: <[u8; 32]>::from_hex(include_bytes!(
|
client_random: <[u8; 32]>::from_hex(include_bytes!(
|
||||||
"fixtures/data/tlsnotary.org/client_random"
|
"fixtures/data/tlsnotary.org/client_random"
|
||||||
))
|
))
|
||||||
@@ -73,17 +76,19 @@ impl ConnectionFixture {
|
|||||||
/// Returns a connection fixture for appliedzkp.org.
|
/// Returns a connection fixture for appliedzkp.org.
|
||||||
pub fn appliedzkp(transcript_length: TranscriptLength) -> Self {
|
pub fn appliedzkp(transcript_length: TranscriptLength) -> Self {
|
||||||
ConnectionFixture {
|
ConnectionFixture {
|
||||||
server_name: ServerName::new("appliedzkp.org".to_string()),
|
server_name: ServerName::Dns(DnsName::try_from("appliedzkp.org").unwrap()),
|
||||||
connection_info: ConnectionInfo {
|
connection_info: ConnectionInfo {
|
||||||
time: 1671637529,
|
time: 1671637529,
|
||||||
version: TlsVersion::V1_2,
|
version: TlsVersion::V1_2,
|
||||||
transcript_length,
|
transcript_length,
|
||||||
},
|
},
|
||||||
server_cert_data: ServerCertData {
|
server_cert_data: HandshakeData {
|
||||||
certs: vec![
|
certs: vec![
|
||||||
Certificate(include_bytes!("fixtures/data/appliedzkp.org/ee.der").to_vec()),
|
CertificateDer(include_bytes!("fixtures/data/appliedzkp.org/ee.der").to_vec()),
|
||||||
Certificate(include_bytes!("fixtures/data/appliedzkp.org/inter.der").to_vec()),
|
CertificateDer(
|
||||||
Certificate(include_bytes!("fixtures/data/appliedzkp.org/ca.der").to_vec()),
|
include_bytes!("fixtures/data/appliedzkp.org/inter.der").to_vec(),
|
||||||
|
),
|
||||||
|
CertificateDer(include_bytes!("fixtures/data/appliedzkp.org/ca.der").to_vec()),
|
||||||
],
|
],
|
||||||
sig: ServerSignature {
|
sig: ServerSignature {
|
||||||
scheme: SignatureScheme::ECDSA_NISTP256_SHA256,
|
scheme: SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||||
@@ -92,7 +97,7 @@ impl ConnectionFixture {
|
|||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
},
|
},
|
||||||
handshake: HandshakeData::V1_2(HandshakeDataV1_2 {
|
binding: CertBinding::V1_2(CertBindingV1_2 {
|
||||||
client_random: <[u8; 32]>::from_hex(include_bytes!(
|
client_random: <[u8; 32]>::from_hex(include_bytes!(
|
||||||
"fixtures/data/appliedzkp.org/client_random"
|
"fixtures/data/appliedzkp.org/client_random"
|
||||||
))
|
))
|
||||||
@@ -115,10 +120,10 @@ impl ConnectionFixture {
|
|||||||
|
|
||||||
/// Returns the server_ephemeral_key fixture.
|
/// Returns the server_ephemeral_key fixture.
|
||||||
pub fn server_ephemeral_key(&self) -> &ServerEphemKey {
|
pub fn server_ephemeral_key(&self) -> &ServerEphemKey {
|
||||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
let CertBinding::V1_2(CertBindingV1_2 {
|
||||||
server_ephemeral_key,
|
server_ephemeral_key,
|
||||||
..
|
..
|
||||||
}) = &self.server_cert_data.handshake;
|
}) = &self.server_cert_data.binding;
|
||||||
server_ephemeral_key
|
server_ephemeral_key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ pub mod fixtures;
|
|||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod merkle;
|
pub mod merkle;
|
||||||
pub mod transcript;
|
pub mod transcript;
|
||||||
|
pub mod webpki;
|
||||||
|
|
||||||
use rangeset::ToRangeSet;
|
use rangeset::ToRangeSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::{ServerCertData, ServerName},
|
connection::{HandshakeData, ServerName},
|
||||||
transcript::{
|
transcript::{
|
||||||
Direction, Idx, PartialTranscript, Transcript, TranscriptCommitConfig,
|
Direction, Idx, PartialTranscript, Transcript, TranscriptCommitConfig,
|
||||||
TranscriptCommitRequest, TranscriptCommitment, TranscriptSecret,
|
TranscriptCommitRequest, TranscriptCommitment, TranscriptSecret,
|
||||||
@@ -200,8 +201,8 @@ enum VerifyConfigBuilderErrorRepr {}
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ProvePayload {
|
pub struct ProvePayload {
|
||||||
/// Server identity data.
|
/// Handshake data.
|
||||||
pub server_identity: Option<(ServerName, ServerCertData)>,
|
pub handshake: Option<(ServerName, HandshakeData)>,
|
||||||
/// Transcript data.
|
/// Transcript data.
|
||||||
pub transcript: Option<PartialTranscript>,
|
pub transcript: Option<PartialTranscript>,
|
||||||
/// Transcript commitment configuration.
|
/// Transcript commitment configuration.
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::{
|
connection::{
|
||||||
Certificate, HandshakeData, HandshakeDataV1_2, ServerEphemKey, ServerSignature, TlsVersion,
|
CertBinding, CertBindingV1_2, ServerEphemKey, ServerSignature, TlsVersion, VerifyData,
|
||||||
VerifyData,
|
|
||||||
},
|
},
|
||||||
transcript::{Direction, Transcript},
|
transcript::{Direction, Transcript},
|
||||||
|
webpki::CertificateDer,
|
||||||
};
|
};
|
||||||
use tls_core::msgs::{
|
use tls_core::msgs::{
|
||||||
alert::AlertMessagePayload,
|
alert::AlertMessagePayload,
|
||||||
@@ -19,9 +19,9 @@ use tls_core::msgs::{
|
|||||||
pub struct TlsTranscript {
|
pub struct TlsTranscript {
|
||||||
time: u64,
|
time: u64,
|
||||||
version: TlsVersion,
|
version: TlsVersion,
|
||||||
server_cert_chain: Option<Vec<Certificate>>,
|
server_cert_chain: Option<Vec<CertificateDer>>,
|
||||||
server_signature: Option<ServerSignature>,
|
server_signature: Option<ServerSignature>,
|
||||||
handshake_data: HandshakeData,
|
certificate_binding: CertBinding,
|
||||||
sent: Vec<Record>,
|
sent: Vec<Record>,
|
||||||
recv: Vec<Record>,
|
recv: Vec<Record>,
|
||||||
}
|
}
|
||||||
@@ -32,9 +32,9 @@ impl TlsTranscript {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
time: u64,
|
time: u64,
|
||||||
version: TlsVersion,
|
version: TlsVersion,
|
||||||
server_cert_chain: Option<Vec<Certificate>>,
|
server_cert_chain: Option<Vec<CertificateDer>>,
|
||||||
server_signature: Option<ServerSignature>,
|
server_signature: Option<ServerSignature>,
|
||||||
handshake_data: HandshakeData,
|
certificate_binding: CertBinding,
|
||||||
verify_data: VerifyData,
|
verify_data: VerifyData,
|
||||||
sent: Vec<Record>,
|
sent: Vec<Record>,
|
||||||
recv: Vec<Record>,
|
recv: Vec<Record>,
|
||||||
@@ -198,7 +198,7 @@ impl TlsTranscript {
|
|||||||
version,
|
version,
|
||||||
server_cert_chain,
|
server_cert_chain,
|
||||||
server_signature,
|
server_signature,
|
||||||
handshake_data,
|
certificate_binding,
|
||||||
sent,
|
sent,
|
||||||
recv,
|
recv,
|
||||||
})
|
})
|
||||||
@@ -215,7 +215,7 @@ impl TlsTranscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the server certificate chain.
|
/// Returns the server certificate chain.
|
||||||
pub fn server_cert_chain(&self) -> Option<&[Certificate]> {
|
pub fn server_cert_chain(&self) -> Option<&[CertificateDer]> {
|
||||||
self.server_cert_chain.as_deref()
|
self.server_cert_chain.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,17 +226,17 @@ impl TlsTranscript {
|
|||||||
|
|
||||||
/// Returns the server ephemeral key used in the TLS handshake.
|
/// Returns the server ephemeral key used in the TLS handshake.
|
||||||
pub fn server_ephemeral_key(&self) -> &ServerEphemKey {
|
pub fn server_ephemeral_key(&self) -> &ServerEphemKey {
|
||||||
match &self.handshake_data {
|
match &self.certificate_binding {
|
||||||
HandshakeData::V1_2(HandshakeDataV1_2 {
|
CertBinding::V1_2(CertBindingV1_2 {
|
||||||
server_ephemeral_key,
|
server_ephemeral_key,
|
||||||
..
|
..
|
||||||
}) => server_ephemeral_key,
|
}) => server_ephemeral_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the handshake data.
|
/// Returns the certificate binding data.
|
||||||
pub fn handshake_data(&self) -> &HandshakeData {
|
pub fn certificate_binding(&self) -> &CertBinding {
|
||||||
&self.handshake_data
|
&self.certificate_binding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the sent records.
|
/// Returns the sent records.
|
||||||
|
|||||||
168
crates/core/src/webpki.rs
Normal file
168
crates/core/src/webpki.rs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
//! Web PKI types.
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use rustls_pki_types::{self as webpki_types, pem::PemObject};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::connection::ServerName;
|
||||||
|
|
||||||
|
/// X.509 certificate, DER encoded.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CertificateDer(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl CertificateDer {
|
||||||
|
/// Creates a DER-encoded certificate from a PEM-encoded certificate.
|
||||||
|
pub fn from_pem_slice(pem: &[u8]) -> Result<Self, PemError> {
|
||||||
|
let der = webpki_types::CertificateDer::from_pem_slice(pem).map_err(|_| PemError {})?;
|
||||||
|
|
||||||
|
Ok(Self(der.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Private key, DER encoded.
|
||||||
|
#[derive(Debug, Clone, zeroize::ZeroizeOnDrop, Serialize, Deserialize)]
|
||||||
|
pub struct PrivateKeyDer(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl PrivateKeyDer {
|
||||||
|
/// Creates a DER-encoded private key from a PEM-encoded private key.
|
||||||
|
pub fn from_pem_slice(pem: &[u8]) -> Result<Self, PemError> {
|
||||||
|
let der = webpki_types::PrivateKeyDer::from_pem_slice(pem).map_err(|_| PemError {})?;
|
||||||
|
|
||||||
|
Ok(Self(der.secret_der().to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PEM parsing error.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("failed to parse PEM object")]
|
||||||
|
pub struct PemError {}
|
||||||
|
|
||||||
|
/// Root certificate store.
|
||||||
|
///
|
||||||
|
/// This stores root certificates which are used to verify end-entity
|
||||||
|
/// certificates presented by a TLS server.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RootCertStore {
|
||||||
|
/// Unvalidated DER-encoded X.509 root certificates.
|
||||||
|
pub roots: Vec<CertificateDer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RootCertStore {
|
||||||
|
/// Creates an empty root certificate store.
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self { roots: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Server certificate verifier.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ServerCertVerifier {
|
||||||
|
roots: Vec<webpki_types::TrustAnchor<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCertVerifier {
|
||||||
|
/// Creates a new server certificate verifier.
|
||||||
|
pub fn new(roots: &RootCertStore) -> Result<Self, ServerCertVerifierError> {
|
||||||
|
let roots = roots
|
||||||
|
.roots
|
||||||
|
.iter()
|
||||||
|
.map(|cert| {
|
||||||
|
webpki::anchor_from_trusted_cert(&webpki_types::CertificateDer::from(
|
||||||
|
cert.0.as_slice(),
|
||||||
|
))
|
||||||
|
.map(|anchor| anchor.to_owned())
|
||||||
|
.map_err(|err| ServerCertVerifierError::InvalidRootCertificate {
|
||||||
|
cert: cert.clone(),
|
||||||
|
reason: err.to_string(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok(Self { roots })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new server certificate verifier with Mozilla root
|
||||||
|
/// certificates.
|
||||||
|
pub fn mozilla() -> Self {
|
||||||
|
Self {
|
||||||
|
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies the server certificate was valid at the given time of
|
||||||
|
/// presentation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `end_entity` - End-entity certificate to verify.
|
||||||
|
/// * `intermediates` - Intermediate certificates to a trust anchor.
|
||||||
|
/// * `server_name` - Server DNS name.
|
||||||
|
/// * `time` - Unix time the certificate was presented.
|
||||||
|
pub fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
end_entity: &CertificateDer,
|
||||||
|
intermediates: &[CertificateDer],
|
||||||
|
server_name: &ServerName,
|
||||||
|
time: u64,
|
||||||
|
) -> Result<(), ServerCertVerifierError> {
|
||||||
|
let cert = webpki_types::CertificateDer::from(end_entity.0.as_slice());
|
||||||
|
let cert = webpki::EndEntityCert::try_from(&cert).map_err(|e| {
|
||||||
|
ServerCertVerifierError::InvalidEndEntityCertificate {
|
||||||
|
cert: end_entity.clone(),
|
||||||
|
reason: e.to_string(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
let intermediates = intermediates
|
||||||
|
.iter()
|
||||||
|
.map(|c| webpki_types::CertificateDer::from(c.0.as_slice()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let server_name = server_name.to_webpki();
|
||||||
|
let time = webpki_types::UnixTime::since_unix_epoch(Duration::from_secs(time));
|
||||||
|
|
||||||
|
cert.verify_for_usage(
|
||||||
|
webpki::ALL_VERIFICATION_ALGS,
|
||||||
|
&self.roots,
|
||||||
|
&intermediates,
|
||||||
|
time,
|
||||||
|
webpki::KeyUsage::server_auth(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| ServerCertVerifierError::InvalidPath)?;
|
||||||
|
|
||||||
|
cert.verify_is_valid_for_subject_name(&server_name)
|
||||||
|
.map_err(|_| ServerCertVerifierError::InvalidServerName)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error for [`ServerCertVerifier`].
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("server certificate verification failed: {0}")]
|
||||||
|
pub enum ServerCertVerifierError {
|
||||||
|
/// Root certificate store contains invalid certificate.
|
||||||
|
#[error("root certificate store contains invalid certificate: {reason}")]
|
||||||
|
InvalidRootCertificate {
|
||||||
|
/// Invalid certificate.
|
||||||
|
cert: CertificateDer,
|
||||||
|
/// Reason for invalidity.
|
||||||
|
reason: String,
|
||||||
|
},
|
||||||
|
/// End-entity certificate is invalid.
|
||||||
|
#[error("end-entity certificate is invalid: {reason}")]
|
||||||
|
InvalidEndEntityCertificate {
|
||||||
|
/// Invalid certificate.
|
||||||
|
cert: CertificateDer,
|
||||||
|
/// Reason for invalidity.
|
||||||
|
reason: String,
|
||||||
|
},
|
||||||
|
/// Failed to verify certificate path to provided trust anchors.
|
||||||
|
#[error("failed to verify certificate path to provided trust anchors")]
|
||||||
|
InvalidPath,
|
||||||
|
/// Failed to verify certificate is valid for provided server name.
|
||||||
|
#[error("failed to verify certificate is valid for provided server name")]
|
||||||
|
InvalidServerName,
|
||||||
|
}
|
||||||
@@ -8,10 +8,8 @@ version = "0.0.0"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tlsn-core = { workspace = true }
|
|
||||||
tlsn = { workspace = true }
|
tlsn = { workspace = true }
|
||||||
tlsn-formats = { workspace = true }
|
tlsn-formats = { workspace = true }
|
||||||
tlsn-tls-core = { workspace = true }
|
|
||||||
tls-server-fixture = { workspace = true }
|
tls-server-fixture = { workspace = true }
|
||||||
tlsn-server-fixture = { workspace = true }
|
tlsn-server-fixture = { workspace = true }
|
||||||
tlsn-server-fixture-certs = { workspace = true }
|
tlsn-server-fixture-certs = { workspace = true }
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ use tracing::instrument;
|
|||||||
|
|
||||||
use tls_server_fixture::CA_CERT_DER;
|
use tls_server_fixture::CA_CERT_DER;
|
||||||
use tlsn::{
|
use tlsn::{
|
||||||
config::{ProtocolConfig, ProtocolConfigValidator},
|
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
|
||||||
|
connection::ServerName,
|
||||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||||
transcript::PartialTranscript,
|
transcript::PartialTranscript,
|
||||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||||
@@ -72,18 +73,16 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
|||||||
// Create a root certificate store with the server-fixture's self-signed
|
// Create a root certificate store with the server-fixture's self-signed
|
||||||
// certificate. This is only required for offline testing with the
|
// certificate. This is only required for offline testing with the
|
||||||
// server-fixture.
|
// server-fixture.
|
||||||
let mut root_store = tls_core::anchors::RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
let mut tls_config_builder = TlsConfig::builder();
|
let mut tls_config_builder = TlsConfig::builder();
|
||||||
tls_config_builder.root_store(root_store);
|
tls_config_builder.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
});
|
||||||
let tls_config = tls_config_builder.build().unwrap();
|
let tls_config = tls_config_builder.build().unwrap();
|
||||||
|
|
||||||
// Set up protocol configuration for prover.
|
// Set up protocol configuration for prover.
|
||||||
let mut prover_config_builder = ProverConfig::builder();
|
let mut prover_config_builder = ProverConfig::builder();
|
||||||
prover_config_builder
|
prover_config_builder
|
||||||
.server_name(server_domain)
|
.server_name(ServerName::Dns(server_domain.try_into().unwrap()))
|
||||||
.tls_config(tls_config)
|
.tls_config(tls_config)
|
||||||
.protocol_config(
|
.protocol_config(
|
||||||
ProtocolConfig::builder()
|
ProtocolConfig::builder()
|
||||||
@@ -194,13 +193,10 @@ async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
|||||||
// Create a root certificate store with the server-fixture's self-signed
|
// Create a root certificate store with the server-fixture's self-signed
|
||||||
// certificate. This is only required for offline testing with the
|
// certificate. This is only required for offline testing with the
|
||||||
// server-fixture.
|
// server-fixture.
|
||||||
let mut root_store = tls_core::anchors::RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let verifier_config = VerifierConfig::builder()
|
let verifier_config = VerifierConfig::builder()
|
||||||
.root_store(root_store)
|
.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
})
|
||||||
.protocol_config_validator(config_validator)
|
.protocol_config_validator(config_validator)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -234,6 +230,7 @@ async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
|||||||
.unwrap_or_else(|| panic!("Expected valid data from {SERVER_DOMAIN}"));
|
.unwrap_or_else(|| panic!("Expected valid data from {SERVER_DOMAIN}"));
|
||||||
|
|
||||||
// Check Session info: server name.
|
// Check Session info: server name.
|
||||||
|
let ServerName::Dns(server_name) = server_name;
|
||||||
assert_eq!(server_name.as_str(), SERVER_DOMAIN);
|
assert_eq!(server_name.as_str(), SERVER_DOMAIN);
|
||||||
|
|
||||||
transcript
|
transcript
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ wasm-opt = ["-O3"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tlsn-harness-core = { workspace = true }
|
tlsn-harness-core = { workspace = true }
|
||||||
tlsn = { workspace = true }
|
tlsn = { workspace = true }
|
||||||
tlsn-core = { workspace = true }
|
|
||||||
tlsn-tls-core = { workspace = true }
|
|
||||||
tlsn-server-fixture-certs = { workspace = true }
|
tlsn-server-fixture-certs = { workspace = true }
|
||||||
|
|
||||||
inventory = { workspace = true }
|
inventory = { workspace = true }
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use futures::{AsyncReadExt, AsyncWriteExt, TryFutureExt};
|
|||||||
|
|
||||||
use harness_core::bench::{Bench, ProverMetrics};
|
use harness_core::bench::{Bench, ProverMetrics};
|
||||||
use tlsn::{
|
use tlsn::{
|
||||||
config::ProtocolConfig,
|
config::{CertificateDer, ProtocolConfig, RootCertStore},
|
||||||
|
connection::ServerName,
|
||||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||||
};
|
};
|
||||||
use tlsn_server_fixture_certs::{CA_CERT_DER, SERVER_DOMAIN};
|
use tlsn_server_fixture_certs::{CA_CERT_DER, SERVER_DOMAIN};
|
||||||
@@ -32,20 +33,17 @@ pub async fn bench_prover(provider: &IoProvider, config: &Bench) -> Result<Prove
|
|||||||
|
|
||||||
let protocol_config = builder.build()?;
|
let protocol_config = builder.build()?;
|
||||||
|
|
||||||
let mut root_store = tls_core::anchors::RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut tls_config_builder = TlsConfig::builder();
|
let mut tls_config_builder = TlsConfig::builder();
|
||||||
tls_config_builder.root_store(root_store);
|
tls_config_builder.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
});
|
||||||
let tls_config = tls_config_builder.build()?;
|
let tls_config = tls_config_builder.build()?;
|
||||||
|
|
||||||
let prover = Prover::new(
|
let prover = Prover::new(
|
||||||
ProverConfig::builder()
|
ProverConfig::builder()
|
||||||
.tls_config(tls_config)
|
.tls_config(tls_config)
|
||||||
.protocol_config(protocol_config)
|
.protocol_config(protocol_config)
|
||||||
.server_name(SERVER_DOMAIN)
|
.server_name(ServerName::Dns(SERVER_DOMAIN.try_into().unwrap()))
|
||||||
.build()?,
|
.build()?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use anyhow::Result;
|
|||||||
|
|
||||||
use harness_core::bench::Bench;
|
use harness_core::bench::Bench;
|
||||||
use tlsn::{
|
use tlsn::{
|
||||||
config::ProtocolConfigValidator,
|
config::{CertificateDer, ProtocolConfigValidator, RootCertStore},
|
||||||
verifier::{Verifier, VerifierConfig, VerifyConfig},
|
verifier::{Verifier, VerifierConfig, VerifyConfig},
|
||||||
};
|
};
|
||||||
use tlsn_server_fixture_certs::CA_CERT_DER;
|
use tlsn_server_fixture_certs::CA_CERT_DER;
|
||||||
@@ -17,14 +17,11 @@ pub async fn bench_verifier(provider: &IoProvider, config: &Bench) -> Result<()>
|
|||||||
|
|
||||||
let protocol_config = builder.build()?;
|
let protocol_config = builder.build()?;
|
||||||
|
|
||||||
let mut root_store = tls_core::anchors::RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let verifier = Verifier::new(
|
let verifier = Verifier::new(
|
||||||
VerifierConfig::builder()
|
VerifierConfig::builder()
|
||||||
.root_store(root_store)
|
.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
})
|
||||||
.protocol_config_validator(protocol_config)
|
.protocol_config_validator(protocol_config)
|
||||||
.build()?,
|
.build()?,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use tls_core::anchors::RootCertStore;
|
|
||||||
use tlsn::{
|
use tlsn::{
|
||||||
config::{ProtocolConfig, ProtocolConfigValidator},
|
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
|
||||||
|
connection::ServerName,
|
||||||
hash::HashAlgId,
|
hash::HashAlgId,
|
||||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||||
transcript::{TranscriptCommitConfig, TranscriptCommitment, TranscriptCommitmentKind},
|
transcript::{TranscriptCommitConfig, TranscriptCommitment, TranscriptCommitmentKind},
|
||||||
@@ -21,19 +21,17 @@ const MAX_RECV_DATA: usize = 1 << 11;
|
|||||||
crate::test!("basic", prover, verifier);
|
crate::test!("basic", prover, verifier);
|
||||||
|
|
||||||
async fn prover(provider: &IoProvider) {
|
async fn prover(provider: &IoProvider) {
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut tls_config_builder = TlsConfig::builder();
|
let mut tls_config_builder = TlsConfig::builder();
|
||||||
tls_config_builder.root_store(root_store);
|
tls_config_builder.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
});
|
||||||
|
|
||||||
let tls_config = tls_config_builder.build().unwrap();
|
let tls_config = tls_config_builder.build().unwrap();
|
||||||
|
|
||||||
|
let server_name = ServerName::Dns(SERVER_DOMAIN.try_into().unwrap());
|
||||||
let prover = Prover::new(
|
let prover = Prover::new(
|
||||||
ProverConfig::builder()
|
ProverConfig::builder()
|
||||||
.server_name(SERVER_DOMAIN)
|
.server_name(server_name)
|
||||||
.tls_config(tls_config)
|
.tls_config(tls_config)
|
||||||
.protocol_config(
|
.protocol_config(
|
||||||
ProtocolConfig::builder()
|
ProtocolConfig::builder()
|
||||||
@@ -114,11 +112,6 @@ async fn prover(provider: &IoProvider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn verifier(provider: &IoProvider) {
|
async fn verifier(provider: &IoProvider) {
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let config = VerifierConfig::builder()
|
let config = VerifierConfig::builder()
|
||||||
.protocol_config_validator(
|
.protocol_config_validator(
|
||||||
ProtocolConfigValidator::builder()
|
ProtocolConfigValidator::builder()
|
||||||
@@ -127,7 +120,9 @@ async fn verifier(provider: &IoProvider) {
|
|||||||
.build()
|
.build()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.root_store(root_store)
|
.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
})
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -145,7 +140,9 @@ async fn verifier(provider: &IoProvider) {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(server_name.unwrap().as_str(), SERVER_DOMAIN);
|
let ServerName::Dns(server_name) = server_name.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(server_name.as_str(), SERVER_DOMAIN);
|
||||||
assert!(
|
assert!(
|
||||||
transcript_commitments
|
transcript_commitments
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -72,3 +72,5 @@ tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] }
|
|||||||
tokio-util = { workspace = true, features = ["compat"] }
|
tokio-util = { workspace = true, features = ["compat"] }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
uid-mux = { workspace = true, features = ["serio", "test-utils"] }
|
uid-mux = { workspace = true, features = ["serio", "test-utils"] }
|
||||||
|
rustls-pki-types = { workspace = true }
|
||||||
|
rustls-webpki = { workspace = true }
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use serio::stream::IoStreamExt;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use tls_core::msgs::enums::NamedGroup;
|
use tls_core::msgs::enums::NamedGroup;
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{HandshakeData, HandshakeDataV1_2, TlsVersion, VerifyData},
|
connection::{CertBinding, CertBindingV1_2, TlsVersion, VerifyData},
|
||||||
transcript::TlsTranscript,
|
transcript::TlsTranscript,
|
||||||
};
|
};
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
@@ -405,7 +405,7 @@ impl MpcTlsFollower {
|
|||||||
let cf_vd = cf_vd.ok_or(MpcTlsError::hs("client finished VD not computed"))?;
|
let cf_vd = cf_vd.ok_or(MpcTlsError::hs("client finished VD not computed"))?;
|
||||||
let sf_vd = sf_vd.ok_or(MpcTlsError::hs("server finished VD not computed"))?;
|
let sf_vd = sf_vd.ok_or(MpcTlsError::hs("server finished VD not computed"))?;
|
||||||
|
|
||||||
let handshake_data = HandshakeData::V1_2(HandshakeDataV1_2 {
|
let handshake_data = CertBinding::V1_2(CertBindingV1_2 {
|
||||||
client_random,
|
client_random,
|
||||||
server_random,
|
server_random,
|
||||||
server_ephemeral_key: server_key
|
server_ephemeral_key: server_key
|
||||||
|
|||||||
@@ -43,10 +43,9 @@ use tls_core::{
|
|||||||
suites::SupportedCipherSuite,
|
suites::SupportedCipherSuite,
|
||||||
};
|
};
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
connection::{
|
connection::{CertBinding, CertBindingV1_2, ServerSignature, TlsVersion, VerifyData},
|
||||||
Certificate, HandshakeData, HandshakeDataV1_2, ServerSignature, TlsVersion, VerifyData,
|
|
||||||
},
|
|
||||||
transcript::TlsTranscript,
|
transcript::TlsTranscript,
|
||||||
|
webpki::CertificateDer,
|
||||||
};
|
};
|
||||||
use tracing::{debug, instrument, trace, warn};
|
use tracing::{debug, instrument, trace, warn};
|
||||||
|
|
||||||
@@ -325,7 +324,7 @@ impl MpcTlsLeader {
|
|||||||
let server_cert_chain = server_cert_details
|
let server_cert_chain = server_cert_details
|
||||||
.cert_chain()
|
.cert_chain()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cert| Certificate(cert.0.clone()))
|
.map(|cert| CertificateDer(cert.0.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let server_signature = ServerSignature {
|
let server_signature = ServerSignature {
|
||||||
@@ -337,7 +336,7 @@ impl MpcTlsLeader {
|
|||||||
sig: server_kx_details.kx_sig().sig.0.clone(),
|
sig: server_kx_details.kx_sig().sig.0.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let handshake_data = HandshakeData::V1_2(HandshakeDataV1_2 {
|
let handshake_data = CertBinding::V1_2(CertBindingV1_2 {
|
||||||
client_random: client_random.0,
|
client_random: client_random.0,
|
||||||
server_random: server_random.0,
|
server_random: server_random.0,
|
||||||
server_ephemeral_key: server_key
|
server_ephemeral_key: server_key
|
||||||
|
|||||||
@@ -12,11 +12,15 @@ use mpz_ot::{
|
|||||||
rcot::shared::{SharedRCOTReceiver, SharedRCOTSender},
|
rcot::shared::{SharedRCOTReceiver, SharedRCOTSender},
|
||||||
};
|
};
|
||||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||||
use tls_client::Certificate;
|
use rustls_pki_types::CertificateDer;
|
||||||
|
use tls_client::RootCertStore;
|
||||||
use tls_client_async::bind_client;
|
use tls_client_async::bind_client;
|
||||||
use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN};
|
use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio_util::compat::TokioAsyncReadCompatExt;
|
use tokio_util::compat::TokioAsyncReadCompatExt;
|
||||||
|
use webpki::anchor_from_trusted_cert;
|
||||||
|
|
||||||
|
const CA_CERT: CertificateDer = CertificateDer::from_slice(CA_CERT_DER);
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
#[ignore = "expensive"]
|
#[ignore = "expensive"]
|
||||||
@@ -48,11 +52,11 @@ async fn leader_task(mut leader: MpcTlsLeader) {
|
|||||||
let (leader_ctrl, leader_fut) = leader.run();
|
let (leader_ctrl, leader_fut) = leader.run();
|
||||||
tokio::spawn(async { leader_fut.await.unwrap() });
|
tokio::spawn(async { leader_fut.await.unwrap() });
|
||||||
|
|
||||||
let mut root_store = tls_client::RootCertStore::empty();
|
|
||||||
root_store.add(&Certificate(CA_CERT_DER.to_vec())).unwrap();
|
|
||||||
let config = tls_client::ClientConfig::builder()
|
let config = tls_client::ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
.with_safe_defaults()
|
||||||
.with_root_certificates(root_store)
|
.with_root_certificates(RootCertStore {
|
||||||
|
roots: vec![anchor_from_trusted_cert(&CA_CERT).unwrap().to_owned()],
|
||||||
|
})
|
||||||
.with_no_client_auth();
|
.with_no_client_auth();
|
||||||
|
|
||||||
let server_name = SERVER_DOMAIN.try_into().unwrap();
|
let server_name = SERVER_DOMAIN.try_into().unwrap();
|
||||||
|
|||||||
@@ -35,3 +35,5 @@ hyper = { workspace = true, features = ["client", "http1"] }
|
|||||||
hyper-util = { workspace = true, features = ["full"] }
|
hyper-util = { workspace = true, features = ["full"] }
|
||||||
rstest = { workspace = true }
|
rstest = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] }
|
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
|
rustls-webpki = { workspace = true }
|
||||||
|
rustls-pki-types = { workspace = true }
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use http_body_util::{BodyExt as _, Full};
|
|||||||
use hyper::{body::Bytes, Request, StatusCode};
|
use hyper::{body::Bytes, Request, StatusCode};
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use tls_client::{Certificate, ClientConfig, ClientConnection, RustCryptoBackend, ServerName};
|
use rustls_pki_types::CertificateDer;
|
||||||
|
use tls_client::{ClientConfig, ClientConnection, RustCryptoBackend, ServerName};
|
||||||
use tls_client_async::{bind_client, ClosedConnection, ConnectionError, TlsConnection};
|
use tls_client_async::{bind_client, ClosedConnection, ConnectionError, TlsConnection};
|
||||||
use tls_server_fixture::{
|
use tls_server_fixture::{
|
||||||
bind_test_server, bind_test_server_hyper, APP_RECORD_LENGTH, CA_CERT_DER, CLOSE_DELAY,
|
bind_test_server, bind_test_server_hyper, APP_RECORD_LENGTH, CA_CERT_DER, CLOSE_DELAY,
|
||||||
@@ -14,6 +15,9 @@ use tls_server_fixture::{
|
|||||||
};
|
};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
|
use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
|
||||||
|
use webpki::anchor_from_trusted_cert;
|
||||||
|
|
||||||
|
const CA_CERT: CertificateDer = CertificateDer::from_slice(CA_CERT_DER);
|
||||||
|
|
||||||
// An established client TLS connection
|
// An established client TLS connection
|
||||||
struct TlsFixture {
|
struct TlsFixture {
|
||||||
@@ -30,7 +34,9 @@ async fn set_up_tls() -> TlsFixture {
|
|||||||
let _server_task = tokio::spawn(bind_test_server(server_socket.compat()));
|
let _server_task = tokio::spawn(bind_test_server(server_socket.compat()));
|
||||||
|
|
||||||
let mut root_store = tls_client::RootCertStore::empty();
|
let mut root_store = tls_client::RootCertStore::empty();
|
||||||
root_store.add(&Certificate(CA_CERT_DER.to_vec())).unwrap();
|
root_store
|
||||||
|
.roots
|
||||||
|
.push(anchor_from_trusted_cert(&CA_CERT).unwrap().to_owned());
|
||||||
let config = ClientConfig::builder()
|
let config = ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
.with_safe_defaults()
|
||||||
.with_root_certificates(root_store)
|
.with_root_certificates(root_store)
|
||||||
@@ -75,7 +81,9 @@ async fn test_hyper_ok() {
|
|||||||
let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat()));
|
let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat()));
|
||||||
|
|
||||||
let mut root_store = tls_client::RootCertStore::empty();
|
let mut root_store = tls_client::RootCertStore::empty();
|
||||||
root_store.add(&Certificate(CA_CERT_DER.to_vec())).unwrap();
|
root_store
|
||||||
|
.roots
|
||||||
|
.push(anchor_from_trusted_cert(&CA_CERT).unwrap().to_owned());
|
||||||
let config = ClientConfig::builder()
|
let config = ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
.with_safe_defaults()
|
||||||
.with_root_certificates(root_store)
|
.with_root_certificates(root_store)
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ async-trait = { workspace = true }
|
|||||||
log = { workspace = true, optional = true }
|
log = { workspace = true, optional = true }
|
||||||
ring = { workspace = true }
|
ring = { workspace = true }
|
||||||
sct = { workspace = true }
|
sct = { workspace = true }
|
||||||
webpki = { workspace = true, features = ["alloc", "std"] }
|
rustls-pki-types = { workspace = true }
|
||||||
|
rustls-webpki = { workspace = true }
|
||||||
aes-gcm = { workspace = true }
|
aes-gcm = { workspace = true }
|
||||||
p256 = { workspace = true, features = ["ecdh"] }
|
p256 = { workspace = true, features = ["ecdh"] }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use crate::{
|
|||||||
conn::{CommonState, ConnectionRandoms, State},
|
conn::{CommonState, ConnectionRandoms, State},
|
||||||
error::Error,
|
error::Error,
|
||||||
hash_hs::HandshakeHashBuffer,
|
hash_hs::HandshakeHashBuffer,
|
||||||
msgs::persist,
|
|
||||||
ticketer::TimeBase,
|
ticketer::TimeBase,
|
||||||
};
|
};
|
||||||
use tls_core::{
|
use tls_core::{
|
||||||
@@ -42,35 +41,6 @@ pub(super) type NextState = Box<dyn State<ClientConnectionData>>;
|
|||||||
pub(super) type NextStateOrError = Result<NextState, Error>;
|
pub(super) type NextStateOrError = Result<NextState, Error>;
|
||||||
pub(super) type ClientContext<'a> = crate::conn::Context<'a>;
|
pub(super) type ClientContext<'a> = crate::conn::Context<'a>;
|
||||||
|
|
||||||
fn find_session(
|
|
||||||
server_name: &ServerName,
|
|
||||||
config: &ClientConfig,
|
|
||||||
) -> Option<persist::Retrieved<persist::ClientSessionValue>> {
|
|
||||||
let key = persist::ClientSessionKey::session_for_server_name(server_name);
|
|
||||||
let key_buf = key.get_encoding();
|
|
||||||
|
|
||||||
let value = config.session_storage.get(&key_buf).or_else(|| {
|
|
||||||
debug!("No cached session for {:?}", server_name);
|
|
||||||
None
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut reader = Reader::init(&value[2..]);
|
|
||||||
#[allow(clippy::bind_instead_of_map)] // https://github.com/rust-lang/rust-clippy/issues/8082
|
|
||||||
CipherSuite::read_bytes(&value[..2])
|
|
||||||
.and_then(|suite| {
|
|
||||||
persist::ClientSessionValue::read(&mut reader, suite, &config.cipher_suites)
|
|
||||||
})
|
|
||||||
.and_then(|resuming| {
|
|
||||||
let retrieved = persist::Retrieved::new(resuming, TimeBase::now().ok()?);
|
|
||||||
match retrieved.has_expired() {
|
|
||||||
false => Some(retrieved),
|
|
||||||
true => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.and_then(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) async fn start_handshake(
|
pub(super) async fn start_handshake(
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
extra_exts: Vec<ClientExtension>,
|
extra_exts: Vec<ClientExtension>,
|
||||||
@@ -123,7 +93,6 @@ pub(super) async fn start_handshake(
|
|||||||
emit_client_hello_for_retry(
|
emit_client_hello_for_retry(
|
||||||
config,
|
config,
|
||||||
cx,
|
cx,
|
||||||
None,
|
|
||||||
random,
|
random,
|
||||||
false,
|
false,
|
||||||
transcript_buffer,
|
transcript_buffer,
|
||||||
@@ -142,7 +111,6 @@ pub(super) async fn start_handshake(
|
|||||||
|
|
||||||
struct ExpectServerHello {
|
struct ExpectServerHello {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Retrieved<persist::ClientSessionValue>>,
|
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
random: Random,
|
random: Random,
|
||||||
using_ems: bool,
|
using_ems: bool,
|
||||||
@@ -162,7 +130,6 @@ struct ExpectServerHelloOrHelloRetryRequest {
|
|||||||
async fn emit_client_hello_for_retry(
|
async fn emit_client_hello_for_retry(
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
cx: &mut ClientContext<'_>,
|
cx: &mut ClientContext<'_>,
|
||||||
resuming_session: Option<persist::Retrieved<persist::ClientSessionValue>>,
|
|
||||||
random: Random,
|
random: Random,
|
||||||
using_ems: bool,
|
using_ems: bool,
|
||||||
mut transcript_buffer: HandshakeHashBuffer,
|
mut transcript_buffer: HandshakeHashBuffer,
|
||||||
@@ -176,25 +143,6 @@ async fn emit_client_hello_for_retry(
|
|||||||
may_send_sct_list: bool,
|
may_send_sct_list: bool,
|
||||||
suite: Option<SupportedCipherSuite>,
|
suite: Option<SupportedCipherSuite>,
|
||||||
) -> Result<NextState, Error> {
|
) -> Result<NextState, Error> {
|
||||||
// For now we do not support session resumption
|
|
||||||
//
|
|
||||||
// Do we have a SessionID or ticket cached for this host?
|
|
||||||
// let (ticket, resume_version) = if let Some(resuming) = &resuming_session {
|
|
||||||
// match &resuming.value {
|
|
||||||
// persist::ClientSessionValue::Tls13(inner) => {
|
|
||||||
// (inner.ticket().to_vec(), ProtocolVersion::TLSv1_3)
|
|
||||||
// }
|
|
||||||
// #[cfg(feature = "tls12")]
|
|
||||||
// persist::ClientSessionValue::Tls12(inner) => {
|
|
||||||
// (inner.ticket().to_vec(), ProtocolVersion::TLSv1_2)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// (Vec::new(), ProtocolVersion::Unknown(0))
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let (ticket, resume_version) = (Vec::new(), ProtocolVersion::Unknown(0));
|
|
||||||
|
|
||||||
let support_tls12 = config.supports_version(ProtocolVersion::TLSv1_2);
|
let support_tls12 = config.supports_version(ProtocolVersion::TLSv1_2);
|
||||||
let support_tls13 = config.supports_version(ProtocolVersion::TLSv1_3);
|
let support_tls13 = config.supports_version(ProtocolVersion::TLSv1_3);
|
||||||
|
|
||||||
@@ -256,48 +204,6 @@ async fn emit_client_hello_for_retry(
|
|||||||
// Extra extensions must be placed before the PSK extension
|
// Extra extensions must be placed before the PSK extension
|
||||||
exts.extend(extra_exts.iter().cloned());
|
exts.extend(extra_exts.iter().cloned());
|
||||||
|
|
||||||
// let fill_in_binder = if support_tls13
|
|
||||||
// && config.enable_tickets
|
|
||||||
// && resume_version == ProtocolVersion::TLSv1_3
|
|
||||||
// && !ticket.is_empty()
|
|
||||||
// {
|
|
||||||
// let resuming =
|
|
||||||
// resuming_session
|
|
||||||
// .as_ref()
|
|
||||||
// .and_then(|resuming| match (suite, resuming.tls13()) {
|
|
||||||
// (Some(suite), Some(resuming)) => {
|
|
||||||
// suite.tls13()?.can_resume_from(resuming.suite())?;
|
|
||||||
// Some(resuming)
|
|
||||||
// }
|
|
||||||
// (None, Some(resuming)) => Some(resuming),
|
|
||||||
// _ => None,
|
|
||||||
// });
|
|
||||||
// if let Some(ref resuming) = resuming {
|
|
||||||
// tls13::prepare_resumption(
|
|
||||||
// &config,
|
|
||||||
// cx,
|
|
||||||
// ticket,
|
|
||||||
// &resuming,
|
|
||||||
// &mut exts,
|
|
||||||
// retryreq.is_some(),
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// }
|
|
||||||
// resuming
|
|
||||||
// } else if config.enable_tickets {
|
|
||||||
// // If we have a ticket, include it. Otherwise, request one.
|
|
||||||
// if ticket.is_empty() {
|
|
||||||
// exts.push(ClientExtension::SessionTicket(ClientSessionTicket::Request));
|
|
||||||
// } else {
|
|
||||||
// exts.push(ClientExtension::SessionTicket(ClientSessionTicket::Offer(
|
|
||||||
// Payload::new(ticket),
|
|
||||||
// )));
|
|
||||||
// }
|
|
||||||
// None
|
|
||||||
// } else {
|
|
||||||
// None
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Note what extensions we sent.
|
// Note what extensions we sent.
|
||||||
hello.sent_extensions = exts.iter().map(ClientExtension::get_type).collect();
|
hello.sent_extensions = exts.iter().map(ClientExtension::get_type).collect();
|
||||||
|
|
||||||
@@ -319,8 +225,8 @@ async fn emit_client_hello_for_retry(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// let early_key_schedule = if let Some(resuming) = fill_in_binder {
|
// let early_key_schedule = if let Some(resuming) = fill_in_binder {
|
||||||
// let schedule = tls13::fill_in_psk_binder(&resuming, &transcript_buffer, &mut chp);
|
// let schedule = tls13::fill_in_psk_binder(&resuming, &transcript_buffer,
|
||||||
// Some((resuming.suite(), schedule))
|
// &mut chp); Some((resuming.suite(), schedule))
|
||||||
// } else {
|
// } else {
|
||||||
// None
|
// None
|
||||||
// };
|
// };
|
||||||
@@ -350,7 +256,6 @@ async fn emit_client_hello_for_retry(
|
|||||||
|
|
||||||
let next = ExpectServerHello {
|
let next = ExpectServerHello {
|
||||||
config,
|
config,
|
||||||
resuming_session,
|
|
||||||
server_name,
|
server_name,
|
||||||
random,
|
random,
|
||||||
using_ems,
|
using_ems,
|
||||||
@@ -551,19 +456,10 @@ impl State<ClientConnectionData> for ExpectServerHello {
|
|||||||
// handshake_traffic_secret.
|
// handshake_traffic_secret.
|
||||||
match suite {
|
match suite {
|
||||||
SupportedCipherSuite::Tls13(suite) => {
|
SupportedCipherSuite::Tls13(suite) => {
|
||||||
let resuming_session =
|
|
||||||
self.resuming_session
|
|
||||||
.and_then(|resuming| match resuming.value {
|
|
||||||
persist::ClientSessionValue::Tls13(inner) => Some(inner),
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
persist::ClientSessionValue::Tls12(_) => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
tls13::handle_server_hello(
|
tls13::handle_server_hello(
|
||||||
self.config,
|
self.config,
|
||||||
cx,
|
cx,
|
||||||
server_hello,
|
server_hello,
|
||||||
resuming_session,
|
|
||||||
self.server_name,
|
self.server_name,
|
||||||
randoms,
|
randoms,
|
||||||
suite,
|
suite,
|
||||||
@@ -577,16 +473,8 @@ impl State<ClientConnectionData> for ExpectServerHello {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "tls12")]
|
#[cfg(feature = "tls12")]
|
||||||
SupportedCipherSuite::Tls12(suite) => {
|
SupportedCipherSuite::Tls12(suite) => {
|
||||||
let resuming_session =
|
|
||||||
self.resuming_session
|
|
||||||
.and_then(|resuming| match resuming.value {
|
|
||||||
persist::ClientSessionValue::Tls12(inner) => Some(inner),
|
|
||||||
persist::ClientSessionValue::Tls13(_) => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
tls12::CompleteServerHelloHandling {
|
tls12::CompleteServerHelloHandling {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session,
|
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms,
|
randoms,
|
||||||
using_ems: self.using_ems,
|
using_ems: self.using_ems,
|
||||||
@@ -723,7 +611,6 @@ impl ExpectServerHelloOrHelloRetryRequest {
|
|||||||
emit_client_hello_for_retry(
|
emit_client_hello_for_retry(
|
||||||
self.next.config,
|
self.next.config,
|
||||||
cx,
|
cx,
|
||||||
self.next.resuming_session,
|
|
||||||
self.next.random,
|
self.next.random,
|
||||||
self.next.using_ems,
|
self.next.using_ems,
|
||||||
transcript_buffer,
|
transcript_buffer,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use crate::{
|
|||||||
conn::{CommonState, ConnectionRandoms, State},
|
conn::{CommonState, ConnectionRandoms, State},
|
||||||
error::Error,
|
error::Error,
|
||||||
hash_hs::HandshakeHash,
|
hash_hs::HandshakeHash,
|
||||||
msgs::persist,
|
|
||||||
sign::Signer,
|
sign::Signer,
|
||||||
ticketer::TimeBase,
|
ticketer::TimeBase,
|
||||||
verify,
|
verify,
|
||||||
@@ -49,7 +48,6 @@ mod server_hello {
|
|||||||
|
|
||||||
pub(in crate::client) struct CompleteServerHelloHandling {
|
pub(in crate::client) struct CompleteServerHelloHandling {
|
||||||
pub(in crate::client) config: Arc<ClientConfig>,
|
pub(in crate::client) config: Arc<ClientConfig>,
|
||||||
pub(in crate::client) resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
pub(in crate::client) server_name: ServerName,
|
pub(in crate::client) server_name: ServerName,
|
||||||
pub(in crate::client) randoms: ConnectionRandoms,
|
pub(in crate::client) randoms: ConnectionRandoms,
|
||||||
pub(in crate::client) using_ems: bool,
|
pub(in crate::client) using_ems: bool,
|
||||||
@@ -113,76 +111,8 @@ mod server_hello {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// See if we're successfully resuming.
|
|
||||||
if let Some(ref _resuming) = self.resuming_session {
|
|
||||||
return Err(Error::General(
|
|
||||||
"client does not support resumption".to_string(),
|
|
||||||
));
|
|
||||||
// if resuming.session_id == server_hello.session_id {
|
|
||||||
// debug!("Server agreed to resume");
|
|
||||||
|
|
||||||
// // Is the server telling lies about the ciphersuite?
|
|
||||||
// if resuming.suite() != suite {
|
|
||||||
// let error_msg =
|
|
||||||
// "abbreviated handshake offered, but with varied cs".to_string();
|
|
||||||
// return Err(Error::PeerMisbehavedError(error_msg));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // And about EMS support?
|
|
||||||
// if resuming.extended_ms() != self.using_ems {
|
|
||||||
// let error_msg = "server varied ems support over resume".to_string();
|
|
||||||
// return Err(Error::PeerMisbehavedError(error_msg));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let secrets =
|
|
||||||
// ConnectionSecrets::new_resume(self.randoms, suite, resuming.secret());
|
|
||||||
// self.config.key_log.log(
|
|
||||||
// "CLIENT_RANDOM",
|
|
||||||
// &secrets.randoms.client,
|
|
||||||
// &secrets.master_secret,
|
|
||||||
// );
|
|
||||||
// cx.common.start_encryption_tls12(&secrets, Side::Client);
|
|
||||||
|
|
||||||
// // Since we're resuming, we verified the certificate and
|
|
||||||
// // proof of possession in the prior session.
|
|
||||||
// cx.common.peer_certificates = Some(resuming.server_cert_chain().to_vec());
|
|
||||||
// let cert_verified = verify::ServerCertVerified::assertion();
|
|
||||||
// let sig_verified = verify::HandshakeSignatureValid::assertion();
|
|
||||||
|
|
||||||
// return if must_issue_new_ticket {
|
|
||||||
// Ok(Box::new(ExpectNewTicket {
|
|
||||||
// config: self.config,
|
|
||||||
// secrets,
|
|
||||||
// resuming_session: self.resuming_session,
|
|
||||||
// session_id: server_hello.session_id,
|
|
||||||
// server_name: self.server_name,
|
|
||||||
// using_ems: self.using_ems,
|
|
||||||
// transcript: self.transcript,
|
|
||||||
// resuming: true,
|
|
||||||
// cert_verified,
|
|
||||||
// sig_verified,
|
|
||||||
// }))
|
|
||||||
// } else {
|
|
||||||
// Ok(Box::new(ExpectCcs {
|
|
||||||
// config: self.config,
|
|
||||||
// secrets,
|
|
||||||
// resuming_session: self.resuming_session,
|
|
||||||
// session_id: server_hello.session_id,
|
|
||||||
// server_name: self.server_name,
|
|
||||||
// using_ems: self.using_ems,
|
|
||||||
// transcript: self.transcript,
|
|
||||||
// ticket: None,
|
|
||||||
// resuming: true,
|
|
||||||
// cert_verified,
|
|
||||||
// sig_verified,
|
|
||||||
// }))
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Box::new(ExpectCertificate {
|
Ok(Box::new(ExpectCertificate {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: server_hello.session_id,
|
session_id: server_hello.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -199,7 +129,6 @@ mod server_hello {
|
|||||||
|
|
||||||
struct ExpectCertificate {
|
struct ExpectCertificate {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -228,7 +157,6 @@ impl State<ClientConnectionData> for ExpectCertificate {
|
|||||||
if self.may_send_cert_status {
|
if self.may_send_cert_status {
|
||||||
Ok(Box::new(ExpectCertificateStatusOrServerKx {
|
Ok(Box::new(ExpectCertificateStatusOrServerKx {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -250,7 +178,6 @@ impl State<ClientConnectionData> for ExpectCertificate {
|
|||||||
|
|
||||||
Ok(Box::new(ExpectServerKx {
|
Ok(Box::new(ExpectServerKx {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -266,7 +193,6 @@ impl State<ClientConnectionData> for ExpectCertificate {
|
|||||||
|
|
||||||
struct ExpectCertificateStatusOrServerKx {
|
struct ExpectCertificateStatusOrServerKx {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -303,7 +229,6 @@ impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx {
|
|||||||
|
|
||||||
Box::new(ExpectServerKx {
|
Box::new(ExpectServerKx {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -322,7 +247,6 @@ impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx {
|
|||||||
}) => {
|
}) => {
|
||||||
Box::new(ExpectCertificateStatus {
|
Box::new(ExpectCertificateStatus {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -350,7 +274,6 @@ impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx {
|
|||||||
|
|
||||||
struct ExpectCertificateStatus {
|
struct ExpectCertificateStatus {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -395,7 +318,6 @@ impl State<ClientConnectionData> for ExpectCertificateStatus {
|
|||||||
|
|
||||||
Ok(Box::new(ExpectServerKx {
|
Ok(Box::new(ExpectServerKx {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -410,7 +332,6 @@ impl State<ClientConnectionData> for ExpectCertificateStatus {
|
|||||||
|
|
||||||
struct ExpectServerKx {
|
struct ExpectServerKx {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -458,7 +379,6 @@ impl State<ClientConnectionData> for ExpectServerKx {
|
|||||||
|
|
||||||
Ok(Box::new(ExpectServerDoneOrCertReq {
|
Ok(Box::new(ExpectServerDoneOrCertReq {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -570,7 +490,6 @@ async fn emit_finished(
|
|||||||
// client auth. Otherwise we go straight to ServerHelloDone.
|
// client auth. Otherwise we go straight to ServerHelloDone.
|
||||||
struct ExpectServerDoneOrCertReq {
|
struct ExpectServerDoneOrCertReq {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -598,7 +517,6 @@ impl State<ClientConnectionData> for ExpectServerDoneOrCertReq {
|
|||||||
) {
|
) {
|
||||||
Box::new(ExpectCertificateRequest {
|
Box::new(ExpectCertificateRequest {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -616,7 +534,6 @@ impl State<ClientConnectionData> for ExpectServerDoneOrCertReq {
|
|||||||
|
|
||||||
Box::new(ExpectServerDone {
|
Box::new(ExpectServerDone {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -636,7 +553,6 @@ impl State<ClientConnectionData> for ExpectServerDoneOrCertReq {
|
|||||||
|
|
||||||
struct ExpectCertificateRequest {
|
struct ExpectCertificateRequest {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -679,7 +595,6 @@ impl State<ClientConnectionData> for ExpectCertificateRequest {
|
|||||||
|
|
||||||
Ok(Box::new(ExpectServerDone {
|
Ok(Box::new(ExpectServerDone {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
randoms: self.randoms,
|
randoms: self.randoms,
|
||||||
@@ -696,7 +611,6 @@ impl State<ClientConnectionData> for ExpectCertificateRequest {
|
|||||||
|
|
||||||
struct ExpectServerDone {
|
struct ExpectServerDone {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
@@ -745,6 +659,7 @@ impl State<ClientConnectionData> for ExpectServerDone {
|
|||||||
// 3. Verify that the top certificate signed their kx.
|
// 3. Verify that the top certificate signed their kx.
|
||||||
// 4. If doing client auth, send our Certificate.
|
// 4. If doing client auth, send our Certificate.
|
||||||
// 5. Complete the key exchange:
|
// 5. Complete the key exchange:
|
||||||
|
//
|
||||||
// a) generate our kx pair
|
// a) generate our kx pair
|
||||||
// b) emit a ClientKeyExchange containing it
|
// b) emit a ClientKeyExchange containing it
|
||||||
// c) if doing client auth, emit a CertificateVerify
|
// c) if doing client auth, emit a CertificateVerify
|
||||||
@@ -891,7 +806,6 @@ impl State<ClientConnectionData> for ExpectServerDone {
|
|||||||
if st.must_issue_new_ticket {
|
if st.must_issue_new_ticket {
|
||||||
Ok(Box::new(ExpectNewTicket {
|
Ok(Box::new(ExpectNewTicket {
|
||||||
config: st.config,
|
config: st.config,
|
||||||
resuming_session: st.resuming_session,
|
|
||||||
session_id: st.session_id,
|
session_id: st.session_id,
|
||||||
server_name: st.server_name,
|
server_name: st.server_name,
|
||||||
using_ems: st.using_ems,
|
using_ems: st.using_ems,
|
||||||
@@ -903,7 +817,6 @@ impl State<ClientConnectionData> for ExpectServerDone {
|
|||||||
} else {
|
} else {
|
||||||
Ok(Box::new(ExpectCcs {
|
Ok(Box::new(ExpectCcs {
|
||||||
config: st.config,
|
config: st.config,
|
||||||
resuming_session: st.resuming_session,
|
|
||||||
session_id: st.session_id,
|
session_id: st.session_id,
|
||||||
server_name: st.server_name,
|
server_name: st.server_name,
|
||||||
using_ems: st.using_ems,
|
using_ems: st.using_ems,
|
||||||
@@ -919,7 +832,6 @@ impl State<ClientConnectionData> for ExpectServerDone {
|
|||||||
|
|
||||||
struct ExpectNewTicket {
|
struct ExpectNewTicket {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
using_ems: bool,
|
using_ems: bool,
|
||||||
@@ -946,7 +858,6 @@ impl State<ClientConnectionData> for ExpectNewTicket {
|
|||||||
|
|
||||||
Ok(Box::new(ExpectCcs {
|
Ok(Box::new(ExpectCcs {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
using_ems: self.using_ems,
|
using_ems: self.using_ems,
|
||||||
@@ -962,7 +873,6 @@ impl State<ClientConnectionData> for ExpectNewTicket {
|
|||||||
// -- Waiting for their CCS --
|
// -- Waiting for their CCS --
|
||||||
struct ExpectCcs {
|
struct ExpectCcs {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
using_ems: bool,
|
using_ems: bool,
|
||||||
@@ -998,7 +908,6 @@ impl State<ClientConnectionData> for ExpectCcs {
|
|||||||
|
|
||||||
Ok(Box::new(ExpectFinished {
|
Ok(Box::new(ExpectFinished {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
resuming_session: self.resuming_session,
|
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
server_name: self.server_name,
|
server_name: self.server_name,
|
||||||
using_ems: self.using_ems,
|
using_ems: self.using_ems,
|
||||||
@@ -1013,7 +922,6 @@ impl State<ClientConnectionData> for ExpectCcs {
|
|||||||
|
|
||||||
struct ExpectFinished {
|
struct ExpectFinished {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
using_ems: bool,
|
using_ems: bool,
|
||||||
@@ -1024,60 +932,6 @@ struct ExpectFinished {
|
|||||||
sig_verified: verify::HandshakeSignatureValid,
|
sig_verified: verify::HandshakeSignatureValid,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl ExpectFinished {
|
|
||||||
// // -- Waiting for their finished --
|
|
||||||
// fn save_session(&mut self, cx: &mut ClientContext<'_>) {
|
|
||||||
// // Save a ticket. If we got a new ticket, save that. Otherwise, save the
|
|
||||||
// // original ticket again.
|
|
||||||
// let (mut ticket, lifetime) = match self.ticket.take() {
|
|
||||||
// Some(nst) => (nst.ticket.0, nst.lifetime_hint),
|
|
||||||
// None => (Vec::new(), 0),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// if ticket.is_empty() {
|
|
||||||
// if let Some(resuming_session) = &mut self.resuming_session {
|
|
||||||
// ticket = resuming_session.take_ticket();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if self.session_id.is_empty() && ticket.is_empty() {
|
|
||||||
// debug!("Session not saved: server didn't allocate id or ticket");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let time_now = match TimeBase::now() {
|
|
||||||
// Ok(time_now) => time_now,
|
|
||||||
// Err(e) => {
|
|
||||||
// debug!("Session not saved: {}", e);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let key = persist::ClientSessionKey::session_for_server_name(&self.server_name);
|
|
||||||
// let value = persist::Tls12ClientSessionValue::new(
|
|
||||||
// self.secrets.suite(),
|
|
||||||
// self.session_id,
|
|
||||||
// ticket,
|
|
||||||
// self.secrets.get_master_secret(),
|
|
||||||
// cx.common.peer_certificates.clone().unwrap_or_default(),
|
|
||||||
// time_now,
|
|
||||||
// lifetime,
|
|
||||||
// self.using_ems,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let worked = self
|
|
||||||
// .config
|
|
||||||
// .session_storage
|
|
||||||
// .put(key.get_encoding(), value.get_encoding());
|
|
||||||
|
|
||||||
// if worked {
|
|
||||||
// debug!("Session saved");
|
|
||||||
// } else {
|
|
||||||
// debug!("Session not saved");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl State<ClientConnectionData> for ExpectFinished {
|
impl State<ClientConnectionData> for ExpectFinished {
|
||||||
async fn handle(
|
async fn handle(
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use crate::{
|
|||||||
conn::{CommonState, ConnectionRandoms, State},
|
conn::{CommonState, ConnectionRandoms, State},
|
||||||
error::Error,
|
error::Error,
|
||||||
hash_hs::{HandshakeHash, HandshakeHashBuffer},
|
hash_hs::{HandshakeHash, HandshakeHashBuffer},
|
||||||
msgs::persist,
|
|
||||||
sign, verify, KeyLog,
|
sign, verify, KeyLog,
|
||||||
};
|
};
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@@ -60,7 +59,6 @@ pub(super) async fn handle_server_hello(
|
|||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
cx: &mut ClientContext<'_>,
|
cx: &mut ClientContext<'_>,
|
||||||
server_hello: &ServerHelloPayload,
|
server_hello: &ServerHelloPayload,
|
||||||
resuming_session: Option<persist::Tls13ClientSessionValue>,
|
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
suite: &'static Tls13CipherSuite,
|
suite: &'static Tls13CipherSuite,
|
||||||
@@ -102,8 +100,8 @@ pub(super) async fn handle_server_hello(
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
// if server_hello.get_psk_index() != Some(0) {
|
// if server_hello.get_psk_index() != Some(0) {
|
||||||
// return Err(cx.common.illegal_param("server selected invalid psk").await);
|
// return Err(cx.common.illegal_param("server selected invalid
|
||||||
// }
|
// psk").await); }
|
||||||
|
|
||||||
// debug!("Resuming using PSK");
|
// debug!("Resuming using PSK");
|
||||||
// // The key schedule has been initialized and set in fill_in_psk_binder()
|
// // The key schedule has been initialized and set in fill_in_psk_binder()
|
||||||
@@ -143,7 +141,6 @@ pub(super) async fn handle_server_hello(
|
|||||||
|
|
||||||
Ok(Box::new(ExpectEncryptedExtensions {
|
Ok(Box::new(ExpectEncryptedExtensions {
|
||||||
config,
|
config,
|
||||||
resuming_session,
|
|
||||||
server_name,
|
server_name,
|
||||||
randoms,
|
randoms,
|
||||||
suite,
|
suite,
|
||||||
@@ -170,69 +167,6 @@ async fn validate_server_hello(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn save_kx_hint(config: &ClientConfig, server_name: &ServerName, group: NamedGroup) {
|
|
||||||
// let key = persist::ClientSessionKey::hint_for_server_name(server_name);
|
|
||||||
|
|
||||||
// config
|
|
||||||
// .session_storage
|
|
||||||
// .put(key.get_encoding(), group.get_encoding());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// This implements the horrifying TLS1.3 hack where PSK binders have a
|
|
||||||
// /// data dependency on the message they are contained within.
|
|
||||||
// pub(super) fn fill_in_psk_binder(
|
|
||||||
// resuming: &persist::Tls13ClientSessionValue,
|
|
||||||
// transcript: &HandshakeHashBuffer,
|
|
||||||
// hmp: &mut HandshakeMessagePayload,
|
|
||||||
// ) -> KeyScheduleEarly {
|
|
||||||
// // We need to know the hash function of the suite we're trying to resume into.
|
|
||||||
// let hkdf_alg = &resuming.suite().hkdf_algorithm;
|
|
||||||
// let suite_hash = resuming.suite().hash_algorithm();
|
|
||||||
|
|
||||||
// // The binder is calculated over the clienthello, but doesn't include itself or its
|
|
||||||
// // length, or the length of its container.
|
|
||||||
// let binder_plaintext = hmp.get_encoding_for_binder_signing();
|
|
||||||
// let handshake_hash = transcript.get_hash_given(suite_hash, &binder_plaintext);
|
|
||||||
|
|
||||||
// // Run a fake key_schedule to simulate what the server will do if it chooses
|
|
||||||
// // to resume.
|
|
||||||
// let key_schedule = KeyScheduleEarly::new(hkdf_alg, resuming.secret());
|
|
||||||
// let real_binder = key_schedule.resumption_psk_binder_key_and_sign_verify_data(&handshake_hash);
|
|
||||||
|
|
||||||
// if let HandshakePayload::ClientHello(ref mut ch) = hmp.payload {
|
|
||||||
// ch.set_psk_binder(real_binder.as_ref());
|
|
||||||
// };
|
|
||||||
|
|
||||||
// key_schedule
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(super) async fn prepare_resumption(
|
|
||||||
// config: &ClientConfig,
|
|
||||||
// cx: &mut ClientContext<'_>,
|
|
||||||
// ticket: Vec<u8>,
|
|
||||||
// resuming_session: &persist::Retrieved<&persist::Tls13ClientSessionValue>,
|
|
||||||
// exts: &mut Vec<ClientExtension>,
|
|
||||||
// doing_retry: bool,
|
|
||||||
// ) {
|
|
||||||
// let resuming_suite = resuming_session.suite();
|
|
||||||
// cx.common.suite = Some(resuming_suite.into());
|
|
||||||
// cx.data.resumption_ciphersuite = Some(resuming_suite.into());
|
|
||||||
|
|
||||||
// // Finally, and only for TLS1.3 with a ticket resumption, include a binder
|
|
||||||
// // for our ticket. This must go last.
|
|
||||||
// //
|
|
||||||
// // Include an empty binder. It gets filled in below because it depends on
|
|
||||||
// // the message it's contained in (!!!).
|
|
||||||
// let obfuscated_ticket_age = resuming_session.obfuscated_ticket_age();
|
|
||||||
|
|
||||||
// let binder_len = resuming_suite.hash_algorithm().output_len();
|
|
||||||
// let binder = vec![0u8; binder_len];
|
|
||||||
|
|
||||||
// let psk_identity = PresharedKeyIdentity::new(ticket, obfuscated_ticket_age);
|
|
||||||
// let psk_ext = PresharedKeyOffer::new(psk_identity, binder);
|
|
||||||
// exts.push(ClientExtension::PresharedKey(psk_ext));
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub(super) async fn emit_fake_ccs(
|
pub(super) async fn emit_fake_ccs(
|
||||||
sent_tls13_fake_ccs: &mut bool,
|
sent_tls13_fake_ccs: &mut bool,
|
||||||
common: &mut CommonState,
|
common: &mut CommonState,
|
||||||
@@ -287,7 +221,6 @@ async fn validate_encrypted_extensions(
|
|||||||
|
|
||||||
struct ExpectEncryptedExtensions {
|
struct ExpectEncryptedExtensions {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
resuming_session: Option<persist::Tls13ClientSessionValue>,
|
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
randoms: ConnectionRandoms,
|
randoms: ConnectionRandoms,
|
||||||
suite: &'static Tls13CipherSuite,
|
suite: &'static Tls13CipherSuite,
|
||||||
@@ -313,52 +246,19 @@ impl State<ClientConnectionData> for ExpectEncryptedExtensions {
|
|||||||
validate_encrypted_extensions(cx.common, &self.hello, exts).await?;
|
validate_encrypted_extensions(cx.common, &self.hello, exts).await?;
|
||||||
hs::process_alpn_protocol(cx.common, &self.config, exts.get_alpn_protocol()).await?;
|
hs::process_alpn_protocol(cx.common, &self.config, exts.get_alpn_protocol()).await?;
|
||||||
|
|
||||||
if let Some(resuming_session) = self.resuming_session {
|
if exts.early_data_extension_offered() {
|
||||||
let was_early_traffic = cx.common.early_traffic;
|
let msg = "server sent early data extension without resumption".to_string();
|
||||||
if was_early_traffic {
|
return Err(Error::PeerMisbehavedError(msg));
|
||||||
if exts.early_data_extension_offered() {
|
|
||||||
cx.data.early_data.accepted();
|
|
||||||
} else {
|
|
||||||
cx.data.early_data.rejected();
|
|
||||||
cx.common.early_traffic = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if was_early_traffic && !cx.common.early_traffic {
|
|
||||||
// If no early traffic, set the encryption key for handshakes
|
|
||||||
cx.common.record_layer.set_message_encrypter();
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.common.peer_certificates = Some(resuming_session.server_cert_chain().to_vec());
|
|
||||||
|
|
||||||
// We *don't* reverify the certificate chain here: resumption is a
|
|
||||||
// continuation of the previous session in terms of security policy.
|
|
||||||
let cert_verified = verify::ServerCertVerified::assertion();
|
|
||||||
let sig_verified = verify::HandshakeSignatureValid::assertion();
|
|
||||||
Ok(Box::new(ExpectFinished {
|
|
||||||
config: self.config,
|
|
||||||
server_name: self.server_name,
|
|
||||||
randoms: self.randoms,
|
|
||||||
suite: self.suite,
|
|
||||||
transcript: self.transcript,
|
|
||||||
client_auth: None,
|
|
||||||
cert_verified,
|
|
||||||
sig_verified,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
if exts.early_data_extension_offered() {
|
|
||||||
let msg = "server sent early data extension without resumption".to_string();
|
|
||||||
return Err(Error::PeerMisbehavedError(msg));
|
|
||||||
}
|
|
||||||
Ok(Box::new(ExpectCertificateOrCertReq {
|
|
||||||
config: self.config,
|
|
||||||
server_name: self.server_name,
|
|
||||||
randoms: self.randoms,
|
|
||||||
suite: self.suite,
|
|
||||||
transcript: self.transcript,
|
|
||||||
may_send_sct_list: self.hello.server_may_send_sct_list(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Box::new(ExpectCertificateOrCertReq {
|
||||||
|
config: self.config,
|
||||||
|
server_name: self.server_name,
|
||||||
|
randoms: self.randoms,
|
||||||
|
suite: self.suite,
|
||||||
|
transcript: self.transcript,
|
||||||
|
may_send_sct_list: self.hello.server_may_send_sct_list(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,9 +322,9 @@ impl State<ClientConnectionData> for ExpectCertificateOrCertReq {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLS1.3 version of CertificateRequest handling. We then move to expecting the server
|
// TLS1.3 version of CertificateRequest handling. We then move to expecting the
|
||||||
// Certificate. Unfortunately the CertificateRequest type changed in an annoying way
|
// server Certificate. Unfortunately the CertificateRequest type changed in an
|
||||||
// in TLS1.3.
|
// annoying way in TLS1.3.
|
||||||
struct ExpectCertificateRequest {
|
struct ExpectCertificateRequest {
|
||||||
config: Arc<ClientConfig>,
|
config: Arc<ClientConfig>,
|
||||||
server_name: ServerName,
|
server_name: ServerName,
|
||||||
@@ -787,8 +687,8 @@ impl State<ClientConnectionData> for ExpectFinished {
|
|||||||
|
|
||||||
st.transcript.add_message(&m);
|
st.transcript.add_message(&m);
|
||||||
|
|
||||||
/* The EndOfEarlyData message to server is still encrypted with early data keys,
|
/* The EndOfEarlyData message to server is still encrypted with early data
|
||||||
* but appears in the transcript after the server Finished. */
|
* keys, but appears in the transcript after the server Finished. */
|
||||||
if cx.common.early_traffic {
|
if cx.common.early_traffic {
|
||||||
emit_end_of_early_data_tls13(&mut st.transcript, cx.common).await?;
|
emit_end_of_early_data_tls13(&mut st.transcript, cx.common).await?;
|
||||||
cx.common.early_traffic = false;
|
cx.common.early_traffic = false;
|
||||||
@@ -893,42 +793,6 @@ impl ExpectTraffic {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// let handshake_hash = self.transcript.get_current_hash();
|
|
||||||
// let secret = self
|
|
||||||
// .key_schedule
|
|
||||||
// .resumption_master_secret_and_derive_ticket_psk(&handshake_hash, &nst.nonce.0);
|
|
||||||
|
|
||||||
// let time_now = match TimeBase::now() {
|
|
||||||
// Ok(t) => t,
|
|
||||||
// #[allow(unused_variables)]
|
|
||||||
// Err(e) => {
|
|
||||||
// debug!("Session not saved: {}", e);
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let value = persist::Tls13ClientSessionValue::new(
|
|
||||||
// self.suite,
|
|
||||||
// nst.ticket.0.clone(),
|
|
||||||
// secret,
|
|
||||||
// cx.common.peer_certificates.clone().unwrap_or_default(),
|
|
||||||
// time_now,
|
|
||||||
// nst.lifetime,
|
|
||||||
// nst.age_add,
|
|
||||||
// nst.get_max_early_data_size().unwrap_or_default(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let key = persist::ClientSessionKey::session_for_server_name(&self.server_name);
|
|
||||||
// #[allow(unused_mut)]
|
|
||||||
// let mut ticket = value.get_encoding();
|
|
||||||
|
|
||||||
// let worked = self.session_storage.put(key.get_encoding(), ticket);
|
|
||||||
|
|
||||||
// if worked {
|
|
||||||
// debug!("Ticket saved");
|
|
||||||
// } else {
|
|
||||||
// debug!("Ticket not saved");
|
|
||||||
// }
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -948,27 +812,6 @@ impl ExpectTraffic {
|
|||||||
Err(Error::General(
|
Err(Error::General(
|
||||||
"received unsupported key update request from peer".to_string(),
|
"received unsupported key update request from peer".to_string(),
|
||||||
))
|
))
|
||||||
|
|
||||||
// match kur {
|
|
||||||
// KeyUpdateRequest::UpdateNotRequested => {}
|
|
||||||
// KeyUpdateRequest::UpdateRequested => {
|
|
||||||
// self.want_write_key_update = true;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// common
|
|
||||||
// .send_fatal_alert(AlertDescription::IllegalParameter)
|
|
||||||
// .await;
|
|
||||||
// return Err(Error::CorruptMessagePayload(ContentType::Handshake));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Update our read-side keys.
|
|
||||||
// let new_read_key = self.key_schedule.next_server_application_traffic_secret();
|
|
||||||
// common
|
|
||||||
// .record_layer
|
|
||||||
// .set_message_decrypter(self.suite.derive_decrypter(&new_read_key));
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1022,10 +865,11 @@ impl State<ClientConnectionData> for ExpectTraffic {
|
|||||||
// .send_msg_encrypt(Message::build_key_update_notify().into())
|
// .send_msg_encrypt(Message::build_key_update_notify().into())
|
||||||
// .await;
|
// .await;
|
||||||
|
|
||||||
// let write_key = self.key_schedule.next_client_application_traffic_secret();
|
// let write_key =
|
||||||
|
// self.key_schedule.next_client_application_traffic_secret();
|
||||||
// common
|
// common
|
||||||
// .record_layer
|
// .record_layer
|
||||||
// .set_message_encrypter(self.suite.derive_encrypter(&write_key));
|
// .set_message_encrypter(self.suite.derive_encrypter(&
|
||||||
// }
|
// write_key)); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,15 +301,11 @@ mod conn;
|
|||||||
mod error;
|
mod error;
|
||||||
mod hash_hs;
|
mod hash_hs;
|
||||||
mod limited_cache;
|
mod limited_cache;
|
||||||
mod msgs;
|
|
||||||
mod rand;
|
mod rand;
|
||||||
mod record_layer;
|
mod record_layer;
|
||||||
//mod stream;
|
//mod stream;
|
||||||
mod vecbuf;
|
mod vecbuf;
|
||||||
pub(crate) use tls_core::verify;
|
pub(crate) use tls_core::{verify, x509};
|
||||||
#[cfg(test)]
|
|
||||||
mod verifybench;
|
|
||||||
pub(crate) use tls_core::x509;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod check;
|
mod check;
|
||||||
mod bs_debug;
|
mod bs_debug;
|
||||||
@@ -330,7 +326,7 @@ pub mod internal {
|
|||||||
|
|
||||||
// The public interface is:
|
// The public interface is:
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
anchors::{OwnedTrustAnchor, RootCertStore},
|
anchors::RootCertStore,
|
||||||
builder::{ConfigBuilder, WantsCipherSuites, WantsKxGroups, WantsVerifier, WantsVersions},
|
builder::{ConfigBuilder, WantsCipherSuites, WantsKxGroups, WantsVerifier, WantsVersions},
|
||||||
conn::{CommonState, ConnectionCommon, IoState, Reader, SideData},
|
conn::{CommonState, ConnectionCommon, IoState, Reader, SideData},
|
||||||
error::Error,
|
error::Error,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
pub(crate) mod persist;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod persist_test;
|
|
||||||
@@ -1,526 +0,0 @@
|
|||||||
use crate::{client::ServerName, ticketer::TimeBase};
|
|
||||||
use std::cmp;
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
use std::mem;
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
use tls_core::suites::Tls12CipherSuite;
|
|
||||||
use tls_core::{
|
|
||||||
msgs::{
|
|
||||||
base::{PayloadU16, PayloadU8},
|
|
||||||
codec::{Codec, Reader},
|
|
||||||
enums::{CipherSuite, ProtocolVersion},
|
|
||||||
handshake::{CertificatePayload, SessionID},
|
|
||||||
},
|
|
||||||
suites::{SupportedCipherSuite, Tls13CipherSuite},
|
|
||||||
};
|
|
||||||
|
|
||||||
// These are the keys and values we store in session storage.
|
|
||||||
|
|
||||||
// --- Client types ---
|
|
||||||
/// Keys for session resumption and tickets.
|
|
||||||
/// Matching value is a `ClientSessionValue`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ClientSessionKey {
|
|
||||||
kind: &'static [u8],
|
|
||||||
name: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Codec for ClientSessionKey {
|
|
||||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
||||||
bytes.extend_from_slice(self.kind);
|
|
||||||
bytes.extend_from_slice(&self.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't need to read these.
|
|
||||||
fn read(_r: &mut Reader) -> Option<Self> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientSessionKey {
|
|
||||||
pub fn session_for_server_name(server_name: &ServerName) -> Self {
|
|
||||||
Self {
|
|
||||||
kind: b"session",
|
|
||||||
name: server_name.encode(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hint_for_server_name(server_name: &ServerName) -> Self {
|
|
||||||
Self {
|
|
||||||
kind: b"kx-hint",
|
|
||||||
name: server_name.encode(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ClientSessionValue {
|
|
||||||
Tls13(Tls13ClientSessionValue),
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
Tls12(Tls12ClientSessionValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientSessionValue {
|
|
||||||
pub fn read(
|
|
||||||
reader: &mut Reader<'_>,
|
|
||||||
suite: CipherSuite,
|
|
||||||
supported: &[SupportedCipherSuite],
|
|
||||||
) -> Option<Self> {
|
|
||||||
match supported.iter().find(|s| s.suite() == suite)? {
|
|
||||||
SupportedCipherSuite::Tls13(inner) => {
|
|
||||||
Tls13ClientSessionValue::read(inner, reader).map(ClientSessionValue::Tls13)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
SupportedCipherSuite::Tls12(inner) => {
|
|
||||||
Tls12ClientSessionValue::read(inner, reader).map(ClientSessionValue::Tls12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn common(&self) -> &ClientSessionCommon {
|
|
||||||
match self {
|
|
||||||
Self::Tls13(inner) => &inner.common,
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
Self::Tls12(inner) => &inner.common,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Tls13ClientSessionValue> for ClientSessionValue {
|
|
||||||
fn from(v: Tls13ClientSessionValue) -> Self {
|
|
||||||
Self::Tls13(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
impl From<Tls12ClientSessionValue> for ClientSessionValue {
|
|
||||||
fn from(v: Tls12ClientSessionValue) -> Self {
|
|
||||||
Self::Tls12(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Retrieved<T> {
|
|
||||||
pub value: T,
|
|
||||||
retrieved_at: TimeBase,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Retrieved<T> {
|
|
||||||
pub fn new(value: T, retrieved_at: TimeBase) -> Self {
|
|
||||||
Self {
|
|
||||||
value,
|
|
||||||
retrieved_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Retrieved<&Tls13ClientSessionValue> {
|
|
||||||
pub fn obfuscated_ticket_age(&self) -> u32 {
|
|
||||||
let age_secs = self
|
|
||||||
.retrieved_at
|
|
||||||
.as_secs()
|
|
||||||
.saturating_sub(self.value.common.epoch);
|
|
||||||
let age_millis = age_secs as u32 * 1000;
|
|
||||||
age_millis.wrapping_add(self.value.age_add)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Retrieved<ClientSessionValue> {
|
|
||||||
pub fn tls13(&self) -> Option<Retrieved<&Tls13ClientSessionValue>> {
|
|
||||||
match &self.value {
|
|
||||||
ClientSessionValue::Tls13(value) => Some(Retrieved::new(value, self.retrieved_at)),
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
ClientSessionValue::Tls12(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_expired(&self) -> bool {
|
|
||||||
let common = self.value.common();
|
|
||||||
common.lifetime_secs != 0
|
|
||||||
&& common.epoch + u64::from(common.lifetime_secs) < self.retrieved_at.as_secs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::Deref for Retrieved<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Tls13ClientSessionValue {
|
|
||||||
suite: &'static Tls13CipherSuite,
|
|
||||||
age_add: u32,
|
|
||||||
max_early_data_size: u32,
|
|
||||||
pub common: ClientSessionCommon,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tls13ClientSessionValue {
|
|
||||||
pub fn new(
|
|
||||||
suite: &'static Tls13CipherSuite,
|
|
||||||
ticket: Vec<u8>,
|
|
||||||
secret: Vec<u8>,
|
|
||||||
server_cert_chain: Vec<tls_core::key::Certificate>,
|
|
||||||
time_now: TimeBase,
|
|
||||||
lifetime_secs: u32,
|
|
||||||
age_add: u32,
|
|
||||||
max_early_data_size: u32,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
suite,
|
|
||||||
age_add,
|
|
||||||
max_early_data_size,
|
|
||||||
common: ClientSessionCommon::new(
|
|
||||||
ticket,
|
|
||||||
secret,
|
|
||||||
time_now,
|
|
||||||
lifetime_secs,
|
|
||||||
server_cert_chain,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`Codec::read()`] with an extra `suite` argument.
|
|
||||||
///
|
|
||||||
/// We decode the `suite` argument separately because it allows us to
|
|
||||||
/// decide whether we're decoding an 1.2 or 1.3 session value.
|
|
||||||
pub fn read(suite: &'static Tls13CipherSuite, r: &mut Reader) -> Option<Self> {
|
|
||||||
Some(Self {
|
|
||||||
suite,
|
|
||||||
age_add: u32::read(r)?,
|
|
||||||
max_early_data_size: u32::read(r)?,
|
|
||||||
common: ClientSessionCommon::read(r)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inherent implementation of the [`Codec::get_encoding()`] method.
|
|
||||||
///
|
|
||||||
/// (See `read()` for why this is inherent here.)
|
|
||||||
pub fn get_encoding(&self) -> Vec<u8> {
|
|
||||||
let mut bytes = Vec::with_capacity(16);
|
|
||||||
self.suite.common.suite.encode(&mut bytes);
|
|
||||||
self.age_add.encode(&mut bytes);
|
|
||||||
self.max_early_data_size.encode(&mut bytes);
|
|
||||||
self.common.encode(&mut bytes);
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_early_data_size(&self) -> u32 {
|
|
||||||
self.max_early_data_size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn suite(&self) -> &'static Tls13CipherSuite {
|
|
||||||
self.suite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for Tls13ClientSessionValue {
|
|
||||||
type Target = ClientSessionCommon;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Tls12ClientSessionValue {
|
|
||||||
suite: &'static Tls12CipherSuite,
|
|
||||||
pub session_id: SessionID,
|
|
||||||
extended_ms: bool,
|
|
||||||
pub common: ClientSessionCommon,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
impl Tls12ClientSessionValue {
|
|
||||||
pub fn new(
|
|
||||||
suite: &'static Tls12CipherSuite,
|
|
||||||
session_id: SessionID,
|
|
||||||
ticket: Vec<u8>,
|
|
||||||
master_secret: Vec<u8>,
|
|
||||||
server_cert_chain: Vec<tls_core::key::Certificate>,
|
|
||||||
time_now: TimeBase,
|
|
||||||
lifetime_secs: u32,
|
|
||||||
extended_ms: bool,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
suite,
|
|
||||||
session_id,
|
|
||||||
extended_ms,
|
|
||||||
common: ClientSessionCommon::new(
|
|
||||||
ticket,
|
|
||||||
master_secret,
|
|
||||||
time_now,
|
|
||||||
lifetime_secs,
|
|
||||||
server_cert_chain,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`Codec::read()`] with an extra `suite` argument.
|
|
||||||
///
|
|
||||||
/// We decode the `suite` argument separately because it allows us to
|
|
||||||
/// decide whether we're decoding an 1.2 or 1.3 session value.
|
|
||||||
fn read(suite: &'static Tls12CipherSuite, r: &mut Reader) -> Option<Self> {
|
|
||||||
Some(Self {
|
|
||||||
suite,
|
|
||||||
session_id: SessionID::read(r)?,
|
|
||||||
extended_ms: u8::read(r)? == 1,
|
|
||||||
common: ClientSessionCommon::read(r)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inherent implementation of the [`Codec::get_encoding()`] method.
|
|
||||||
///
|
|
||||||
/// (See `read()` for why this is inherent here.)
|
|
||||||
pub fn get_encoding(&self) -> Vec<u8> {
|
|
||||||
let mut bytes = Vec::with_capacity(16);
|
|
||||||
self.suite.common.suite.encode(&mut bytes);
|
|
||||||
self.session_id.encode(&mut bytes);
|
|
||||||
(if self.extended_ms { 1u8 } else { 0u8 }).encode(&mut bytes);
|
|
||||||
self.common.encode(&mut bytes);
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn take_ticket(&mut self) -> Vec<u8> {
|
|
||||||
mem::take(&mut self.common.ticket.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extended_ms(&self) -> bool {
|
|
||||||
self.extended_ms
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn suite(&self) -> &'static Tls12CipherSuite {
|
|
||||||
self.suite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "tls12")]
|
|
||||||
impl std::ops::Deref for Tls12ClientSessionValue {
|
|
||||||
type Target = ClientSessionCommon;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ClientSessionCommon {
|
|
||||||
ticket: PayloadU16,
|
|
||||||
secret: PayloadU8,
|
|
||||||
epoch: u64,
|
|
||||||
lifetime_secs: u32,
|
|
||||||
server_cert_chain: CertificatePayload,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientSessionCommon {
|
|
||||||
fn new(
|
|
||||||
ticket: Vec<u8>,
|
|
||||||
secret: Vec<u8>,
|
|
||||||
time_now: TimeBase,
|
|
||||||
lifetime_secs: u32,
|
|
||||||
server_cert_chain: Vec<tls_core::key::Certificate>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
ticket: PayloadU16(ticket),
|
|
||||||
secret: PayloadU8(secret),
|
|
||||||
epoch: time_now.as_secs(),
|
|
||||||
lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME),
|
|
||||||
server_cert_chain,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`Codec::read()`] is inherent here to avoid leaking the [`Codec`]
|
|
||||||
/// implementation through [`Deref`] implementations on
|
|
||||||
/// [`Tls12ClientSessionValue`] and [`Tls13ClientSessionValue`].
|
|
||||||
fn read(r: &mut Reader) -> Option<Self> {
|
|
||||||
Some(Self {
|
|
||||||
ticket: PayloadU16::read(r)?,
|
|
||||||
secret: PayloadU8::read(r)?,
|
|
||||||
epoch: u64::read(r)?,
|
|
||||||
lifetime_secs: u32::read(r)?,
|
|
||||||
server_cert_chain: CertificatePayload::read(r)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`Codec::encode()`] is inherent here to avoid leaking the [`Codec`]
|
|
||||||
/// implementation through [`Deref`] implementations on
|
|
||||||
/// [`Tls12ClientSessionValue`] and [`Tls13ClientSessionValue`].
|
|
||||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
||||||
self.ticket.encode(bytes);
|
|
||||||
self.secret.encode(bytes);
|
|
||||||
self.epoch.encode(bytes);
|
|
||||||
self.lifetime_secs.encode(bytes);
|
|
||||||
self.server_cert_chain.encode(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn server_cert_chain(&self) -> &[tls_core::key::Certificate] {
|
|
||||||
self.server_cert_chain.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn secret(&self) -> &[u8] {
|
|
||||||
self.secret.0.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ticket(&self) -> &[u8] {
|
|
||||||
self.ticket.0.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test only: wind back epoch by delta seconds.
|
|
||||||
pub fn rewind_epoch(&mut self, delta: u32) {
|
|
||||||
self.epoch -= delta as u64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static MAX_TICKET_LIFETIME: u32 = 7 * 24 * 60 * 60;
|
|
||||||
|
|
||||||
/// This is the maximum allowed skew between server and client clocks, over
|
|
||||||
/// the maximum ticket lifetime period. This encompasses TCP retransmission
|
|
||||||
/// times in case packet loss occurs when the client sends the ClientHello
|
|
||||||
/// or receives the NewSessionTicket, _and_ actual clock skew over this period.
|
|
||||||
static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000;
|
|
||||||
|
|
||||||
// --- Server types ---
|
|
||||||
pub type ServerSessionKey = SessionID;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ServerSessionValue {
|
|
||||||
pub sni: Option<webpki::DnsName>,
|
|
||||||
pub version: ProtocolVersion,
|
|
||||||
pub cipher_suite: CipherSuite,
|
|
||||||
pub master_secret: PayloadU8,
|
|
||||||
pub extended_ms: bool,
|
|
||||||
pub client_cert_chain: Option<CertificatePayload>,
|
|
||||||
pub alpn: Option<PayloadU8>,
|
|
||||||
pub application_data: PayloadU16,
|
|
||||||
pub creation_time_sec: u64,
|
|
||||||
pub age_obfuscation_offset: u32,
|
|
||||||
freshness: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Codec for ServerSessionValue {
|
|
||||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
||||||
if let Some(ref sni) = self.sni {
|
|
||||||
1u8.encode(bytes);
|
|
||||||
let sni_bytes: &str = sni.as_ref().into();
|
|
||||||
PayloadU8::new(Vec::from(sni_bytes)).encode(bytes);
|
|
||||||
} else {
|
|
||||||
0u8.encode(bytes);
|
|
||||||
}
|
|
||||||
self.version.encode(bytes);
|
|
||||||
self.cipher_suite.encode(bytes);
|
|
||||||
self.master_secret.encode(bytes);
|
|
||||||
(if self.extended_ms { 1u8 } else { 0u8 }).encode(bytes);
|
|
||||||
if let Some(ref chain) = self.client_cert_chain {
|
|
||||||
1u8.encode(bytes);
|
|
||||||
chain.encode(bytes);
|
|
||||||
} else {
|
|
||||||
0u8.encode(bytes);
|
|
||||||
}
|
|
||||||
if let Some(ref alpn) = self.alpn {
|
|
||||||
1u8.encode(bytes);
|
|
||||||
alpn.encode(bytes);
|
|
||||||
} else {
|
|
||||||
0u8.encode(bytes);
|
|
||||||
}
|
|
||||||
self.application_data.encode(bytes);
|
|
||||||
self.creation_time_sec.encode(bytes);
|
|
||||||
self.age_obfuscation_offset.encode(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(r: &mut Reader) -> Option<Self> {
|
|
||||||
let has_sni = u8::read(r)?;
|
|
||||||
let sni = if has_sni == 1 {
|
|
||||||
let dns_name = PayloadU8::read(r)?;
|
|
||||||
let dns_name = webpki::DnsNameRef::try_from_ascii(&dns_name.0).ok()?;
|
|
||||||
Some(dns_name.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let v = ProtocolVersion::read(r)?;
|
|
||||||
let cs = CipherSuite::read(r)?;
|
|
||||||
let ms = PayloadU8::read(r)?;
|
|
||||||
let ems = u8::read(r)?;
|
|
||||||
let has_ccert = u8::read(r)? == 1;
|
|
||||||
let ccert = if has_ccert {
|
|
||||||
Some(CertificatePayload::read(r)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let has_alpn = u8::read(r)? == 1;
|
|
||||||
let alpn = if has_alpn {
|
|
||||||
Some(PayloadU8::read(r)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let application_data = PayloadU16::read(r)?;
|
|
||||||
let creation_time_sec = u64::read(r)?;
|
|
||||||
let age_obfuscation_offset = u32::read(r)?;
|
|
||||||
|
|
||||||
Some(Self {
|
|
||||||
sni,
|
|
||||||
version: v,
|
|
||||||
cipher_suite: cs,
|
|
||||||
master_secret: ms,
|
|
||||||
extended_ms: ems == 1u8,
|
|
||||||
client_cert_chain: ccert,
|
|
||||||
alpn,
|
|
||||||
application_data,
|
|
||||||
creation_time_sec,
|
|
||||||
age_obfuscation_offset,
|
|
||||||
freshness: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerSessionValue {
|
|
||||||
pub fn new(
|
|
||||||
sni: Option<&webpki::DnsName>,
|
|
||||||
v: ProtocolVersion,
|
|
||||||
cs: CipherSuite,
|
|
||||||
ms: Vec<u8>,
|
|
||||||
client_cert_chain: Option<CertificatePayload>,
|
|
||||||
alpn: Option<Vec<u8>>,
|
|
||||||
application_data: Vec<u8>,
|
|
||||||
creation_time: TimeBase,
|
|
||||||
age_obfuscation_offset: u32,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
sni: sni.cloned(),
|
|
||||||
version: v,
|
|
||||||
cipher_suite: cs,
|
|
||||||
master_secret: PayloadU8::new(ms),
|
|
||||||
extended_ms: false,
|
|
||||||
client_cert_chain,
|
|
||||||
alpn: alpn.map(PayloadU8::new),
|
|
||||||
application_data: PayloadU16::new(application_data),
|
|
||||||
creation_time_sec: creation_time.as_secs(),
|
|
||||||
age_obfuscation_offset,
|
|
||||||
freshness: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_extended_ms_used(&mut self) {
|
|
||||||
self.extended_ms = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_freshness(mut self, obfuscated_client_age_ms: u32, time_now: TimeBase) -> Self {
|
|
||||||
let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset);
|
|
||||||
let server_age_ms =
|
|
||||||
(time_now.as_secs().saturating_sub(self.creation_time_sec) as u32).saturating_mul(1000);
|
|
||||||
|
|
||||||
let age_difference = if client_age_ms < server_age_ms {
|
|
||||||
server_age_ms - client_age_ms
|
|
||||||
} else {
|
|
||||||
client_age_ms - server_age_ms
|
|
||||||
};
|
|
||||||
|
|
||||||
self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_fresh(&self) -> bool {
|
|
||||||
self.freshness.unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
use super::persist::*;
|
|
||||||
use crate::ticketer::TimeBase;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use tls_core::{
|
|
||||||
key::Certificate,
|
|
||||||
msgs::{
|
|
||||||
codec::{Codec, Reader},
|
|
||||||
enums::*,
|
|
||||||
},
|
|
||||||
suites::TLS13_AES_128_GCM_SHA256,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clientsessionkey_is_debug() {
|
|
||||||
let name = "hello".try_into().unwrap();
|
|
||||||
let csk = ClientSessionKey::session_for_server_name(&name);
|
|
||||||
println!("{:?}", csk);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clientsessionkey_cannot_be_read() {
|
|
||||||
let bytes = [0; 1];
|
|
||||||
let mut rd = Reader::init(&bytes);
|
|
||||||
assert!(ClientSessionKey::read(&mut rd).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clientsessionvalue_is_debug() {
|
|
||||||
let csv = ClientSessionValue::from(Tls13ClientSessionValue::new(
|
|
||||||
TLS13_AES_128_GCM_SHA256.tls13().unwrap(),
|
|
||||||
vec![],
|
|
||||||
vec![1, 2, 3],
|
|
||||||
vec![Certificate(b"abc".to_vec()), Certificate(b"def".to_vec())],
|
|
||||||
TimeBase::now().unwrap(),
|
|
||||||
15,
|
|
||||||
10,
|
|
||||||
128,
|
|
||||||
));
|
|
||||||
println!("{:?}", csv);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serversessionvalue_is_debug() {
|
|
||||||
let ssv = ServerSessionValue::new(
|
|
||||||
None,
|
|
||||||
ProtocolVersion::TLSv1_3,
|
|
||||||
CipherSuite::TLS13_AES_128_GCM_SHA256,
|
|
||||||
vec![1, 2, 3],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
vec![4, 5, 6],
|
|
||||||
TimeBase::now().unwrap(),
|
|
||||||
0x12345678,
|
|
||||||
);
|
|
||||||
println!("{:?}", ssv);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serversessionvalue_no_sni() {
|
|
||||||
let bytes = [
|
|
||||||
0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
|
|
||||||
0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
|
|
||||||
];
|
|
||||||
let mut rd = Reader::init(&bytes);
|
|
||||||
let ssv = ServerSessionValue::read(&mut rd).unwrap();
|
|
||||||
assert_eq!(ssv.get_encoding(), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serversessionvalue_with_cert() {
|
|
||||||
let bytes = [
|
|
||||||
0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
|
|
||||||
0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
|
|
||||||
];
|
|
||||||
let mut rd = Reader::init(&bytes);
|
|
||||||
let ssv = ServerSessionValue::read(&mut rd).unwrap();
|
|
||||||
assert_eq!(ssv.get_encoding(), bytes);
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ use ring::{
|
|||||||
io::der,
|
io::der,
|
||||||
signature::{self, EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair},
|
signature::{self, EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair},
|
||||||
};
|
};
|
||||||
|
use rustls_pki_types as pki_types;
|
||||||
use std::{convert::TryFrom, error::Error as StdError, fmt, sync::Arc};
|
use std::{convert::TryFrom, error::Error as StdError, fmt, sync::Arc};
|
||||||
use tls_core::{
|
use tls_core::{
|
||||||
key,
|
key,
|
||||||
@@ -71,51 +72,6 @@ impl CertifiedKey {
|
|||||||
pub fn end_entity_cert(&self) -> Result<&key::Certificate, SignError> {
|
pub fn end_entity_cert(&self) -> Result<&key::Certificate, SignError> {
|
||||||
self.cert.first().ok_or(SignError(()))
|
self.cert.first().ok_or(SignError(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check the certificate chain for validity:
|
|
||||||
/// - it should be non-empty list
|
|
||||||
/// - the first certificate should be parsable as a x509v3,
|
|
||||||
/// - the first certificate should quote the given server name
|
|
||||||
/// (if provided)
|
|
||||||
///
|
|
||||||
/// These checks are not security-sensitive. They are the
|
|
||||||
/// *server* attempting to detect accidental misconfiguration.
|
|
||||||
pub(crate) fn cross_check_end_entity_cert(
|
|
||||||
&self,
|
|
||||||
name: Option<webpki::DnsNameRef>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// Always reject an empty certificate chain.
|
|
||||||
let end_entity_cert = self.end_entity_cert().map_err(|SignError(())| {
|
|
||||||
Error::General("No end-entity certificate in certificate chain".to_string())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Reject syntactically-invalid end-entity certificates.
|
|
||||||
let end_entity_cert =
|
|
||||||
webpki::EndEntityCert::try_from(end_entity_cert.as_ref()).map_err(|_| {
|
|
||||||
Error::General(
|
|
||||||
"End-entity certificate in certificate \
|
|
||||||
chain is syntactically invalid"
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if let Some(name) = name {
|
|
||||||
// If SNI was offered then the certificate must be valid for
|
|
||||||
// that hostname. Note that this doesn't fully validate that the
|
|
||||||
// certificate is valid; it only validates that the name is one
|
|
||||||
// that the certificate is valid for, if the certificate is
|
|
||||||
// valid.
|
|
||||||
if end_entity_cert.verify_is_valid_for_dns_name(name).is_err() {
|
|
||||||
return Err(Error::General(
|
|
||||||
"The server certificate is not \
|
|
||||||
valid for the given name"
|
|
||||||
.to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse `der` as any supported key encoding/type, returning
|
/// Parse `der` as any supported key encoding/type, returning
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
// This program does benchmarking of the functions in verify.rs,
|
|
||||||
// that do certificate chain validation and signature verification.
|
|
||||||
//
|
|
||||||
// Note: we don't use any of the standard 'cargo bench', 'test::Bencher',
|
|
||||||
// etc. because it's unstable at the time of writing.
|
|
||||||
|
|
||||||
use crate::{anchors, verify, verify::ServerCertVerifier, OwnedTrustAnchor};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use web_time::{Duration, Instant, SystemTime};
|
|
||||||
|
|
||||||
use webpki_roots;
|
|
||||||
|
|
||||||
fn duration_nanos(d: Duration) -> u64 {
|
|
||||||
((d.as_secs() as f64) * 1e9 + (d.subsec_nanos() as f64)) as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_reddit_cert() {
|
|
||||||
Context::new(
|
|
||||||
"reddit",
|
|
||||||
"reddit.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-reddit.0.der"),
|
|
||||||
include_bytes!("testdata/cert-reddit.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_github_cert() {
|
|
||||||
Context::new(
|
|
||||||
"github",
|
|
||||||
"github.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-github.0.der"),
|
|
||||||
include_bytes!("testdata/cert-github.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arstechnica_cert() {
|
|
||||||
Context::new(
|
|
||||||
"arstechnica",
|
|
||||||
"arstechnica.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-arstechnica.0.der"),
|
|
||||||
include_bytes!("testdata/cert-arstechnica.1.der"),
|
|
||||||
include_bytes!("testdata/cert-arstechnica.2.der"),
|
|
||||||
include_bytes!("testdata/cert-arstechnica.3.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_twitter_cert() {
|
|
||||||
Context::new(
|
|
||||||
"twitter",
|
|
||||||
"twitter.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-twitter.0.der"),
|
|
||||||
include_bytes!("testdata/cert-twitter.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wikipedia_cert() {
|
|
||||||
Context::new(
|
|
||||||
"wikipedia",
|
|
||||||
"wikipedia.org",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-wikipedia.0.der"),
|
|
||||||
include_bytes!("testdata/cert-wikipedia.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_google_cert() {
|
|
||||||
Context::new(
|
|
||||||
"google",
|
|
||||||
"www.google.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-google.0.der"),
|
|
||||||
include_bytes!("testdata/cert-google.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hn_cert() {
|
|
||||||
Context::new(
|
|
||||||
"hn",
|
|
||||||
"news.ycombinator.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-hn.0.der"),
|
|
||||||
include_bytes!("testdata/cert-hn.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_stackoverflow_cert() {
|
|
||||||
Context::new(
|
|
||||||
"stackoverflow",
|
|
||||||
"stackoverflow.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-stackoverflow.0.der"),
|
|
||||||
include_bytes!("testdata/cert-stackoverflow.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_duckduckgo_cert() {
|
|
||||||
Context::new(
|
|
||||||
"duckduckgo",
|
|
||||||
"duckduckgo.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-duckduckgo.0.der"),
|
|
||||||
include_bytes!("testdata/cert-duckduckgo.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustlang_cert() {
|
|
||||||
Context::new(
|
|
||||||
"rustlang",
|
|
||||||
"www.rust-lang.org",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-rustlang.0.der"),
|
|
||||||
include_bytes!("testdata/cert-rustlang.1.der"),
|
|
||||||
include_bytes!("testdata/cert-rustlang.2.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wapo_cert() {
|
|
||||||
Context::new(
|
|
||||||
"wapo",
|
|
||||||
"www.washingtonpost.com",
|
|
||||||
&[
|
|
||||||
include_bytes!("testdata/cert-wapo.0.der"),
|
|
||||||
include_bytes!("testdata/cert-wapo.1.der"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bench(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Context {
|
|
||||||
name: &'static str,
|
|
||||||
domain: &'static str,
|
|
||||||
roots: anchors::RootCertStore,
|
|
||||||
chain: Vec<tls_core::key::Certificate>,
|
|
||||||
now: SystemTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
fn new(name: &'static str, domain: &'static str, certs: &[&'static [u8]]) -> Self {
|
|
||||||
let mut roots = anchors::RootCertStore::empty();
|
|
||||||
roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
|
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
ta.subject.as_ref(),
|
|
||||||
ta.subject_public_key_info.as_ref(),
|
|
||||||
ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
domain,
|
|
||||||
roots,
|
|
||||||
chain: certs
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.map(|bytes| tls_core::key::Certificate(bytes.to_vec()))
|
|
||||||
.collect(),
|
|
||||||
now: SystemTime::UNIX_EPOCH + Duration::from_secs(1640870720),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench(&self, count: usize) {
|
|
||||||
let verifier = verify::WebPkiVerifier::new(self.roots.clone(), None);
|
|
||||||
const SCTS: &[&[u8]] = &[];
|
|
||||||
const OCSP_RESPONSE: &[u8] = &[];
|
|
||||||
let mut times = Vec::new();
|
|
||||||
|
|
||||||
let (end_entity, intermediates) = self.chain.split_first().unwrap();
|
|
||||||
for _ in 0..count {
|
|
||||||
let start = Instant::now();
|
|
||||||
let server_name = self.domain.try_into().unwrap();
|
|
||||||
verifier
|
|
||||||
.verify_server_cert(
|
|
||||||
end_entity,
|
|
||||||
intermediates,
|
|
||||||
&server_name,
|
|
||||||
&mut SCTS.iter().copied(),
|
|
||||||
OCSP_RESPONSE,
|
|
||||||
self.now,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
times.push(duration_nanos(Instant::now().duration_since(start)));
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"verify_server_cert({}): min {:?}us",
|
|
||||||
self.name,
|
|
||||||
times.iter().min().unwrap() / 1000
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -780,14 +780,12 @@ async fn client_checks_server_certificate_with_given_name() {
|
|||||||
let mut server = ServerConnection::new(Arc::clone(&server_config)).unwrap();
|
let mut server = ServerConnection::new(Arc::clone(&server_config)).unwrap();
|
||||||
|
|
||||||
let err = do_handshake_until_error(&mut client, &mut server).await;
|
let err = do_handshake_until_error(&mut client, &mut server).await;
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
err,
|
err,
|
||||||
Err(ErrorFromPeer::Client(Error::CoreError(
|
Err(ErrorFromPeer::Client(Error::CoreError(
|
||||||
tls_core::Error::InvalidCertificateData(
|
tls_core::Error::InvalidCertificateData(_)
|
||||||
"invalid peer certificate: CertNotValidForName".into(),
|
|
||||||
)
|
|
||||||
)))
|
)))
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use futures::{AsyncRead, AsyncWrite};
|
use futures::{AsyncRead, AsyncWrite};
|
||||||
use rustls::{server::AllowAnyAuthenticatedClient, ServerConfig, ServerConnection};
|
use rustls::{server::AllowAnyAuthenticatedClient, ServerConfig, ServerConnection};
|
||||||
|
use rustls_pki_types::CertificateDer;
|
||||||
use std::{
|
use std::{
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
io,
|
io,
|
||||||
@@ -15,6 +16,7 @@ use tls_client::{
|
|||||||
Certificate, ClientConfig, ClientConnection, Error, PrivateKey, RootCertStore,
|
Certificate, ClientConfig, ClientConnection, Error, PrivateKey, RootCertStore,
|
||||||
RustCryptoBackend,
|
RustCryptoBackend,
|
||||||
};
|
};
|
||||||
|
use webpki::anchor_from_trusted_cert;
|
||||||
|
|
||||||
macro_rules! embed_files {
|
macro_rules! embed_files {
|
||||||
(
|
(
|
||||||
@@ -409,9 +411,17 @@ pub fn finish_client_config(
|
|||||||
kt: KeyType,
|
kt: KeyType,
|
||||||
config: tls_client::ConfigBuilder<tls_client::WantsVerifier>,
|
config: tls_client::ConfigBuilder<tls_client::WantsVerifier>,
|
||||||
) -> ClientConfig {
|
) -> ClientConfig {
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
let mut rootbuf = io::BufReader::new(kt.bytes_for("ca.cert"));
|
let mut rootbuf = io::BufReader::new(kt.bytes_for("ca.cert"));
|
||||||
root_store.add_parsable_certificates(&rustls_pemfile::certs(&mut rootbuf).unwrap());
|
let roots = rustls_pemfile::certs(&mut rootbuf)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|cert| {
|
||||||
|
let der = CertificateDer::from_slice(&cert);
|
||||||
|
anchor_from_trusted_cert(&der).unwrap().to_owned()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let root_store = RootCertStore { roots };
|
||||||
|
|
||||||
config
|
config
|
||||||
.with_root_certificates(root_store)
|
.with_root_certificates(root_store)
|
||||||
@@ -422,9 +432,17 @@ pub fn finish_client_config_with_creds(
|
|||||||
kt: KeyType,
|
kt: KeyType,
|
||||||
config: tls_client::ConfigBuilder<tls_client::WantsVerifier>,
|
config: tls_client::ConfigBuilder<tls_client::WantsVerifier>,
|
||||||
) -> ClientConfig {
|
) -> ClientConfig {
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
let mut rootbuf = io::BufReader::new(kt.bytes_for("ca.cert"));
|
let mut rootbuf = io::BufReader::new(kt.bytes_for("ca.cert"));
|
||||||
root_store.add_parsable_certificates(&rustls_pemfile::certs(&mut rootbuf).unwrap());
|
let roots = rustls_pemfile::certs(&mut rootbuf)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|cert| {
|
||||||
|
let der = CertificateDer::from_slice(&cert);
|
||||||
|
anchor_from_trusted_cert(&der).unwrap().to_owned()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let root_store = RootCertStore { roots };
|
||||||
|
|
||||||
config
|
config
|
||||||
.with_root_certificates(root_store)
|
.with_root_certificates(root_store)
|
||||||
|
|||||||
@@ -35,4 +35,5 @@ sha2 = { workspace = true, optional = true }
|
|||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true, optional = true }
|
tracing = { workspace = true, optional = true }
|
||||||
web-time = { workspace = true }
|
web-time = { workspace = true }
|
||||||
webpki = { workspace = true, features = ["alloc", "std"] }
|
rustls-webpki = { workspace = true, features = ["ring"] }
|
||||||
|
rustls-pki-types = { workspace = true }
|
||||||
|
|||||||
@@ -1,65 +1,16 @@
|
|||||||
|
use rustls_pki_types::TrustAnchor;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
msgs::handshake::{DistinguishedName, DistinguishedNames},
|
msgs::handshake::{DistinguishedName, DistinguishedNames},
|
||||||
x509,
|
x509,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A trust anchor, commonly known as a "Root Certificate."
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct OwnedTrustAnchor {
|
|
||||||
subject: Vec<u8>,
|
|
||||||
spki: Vec<u8>,
|
|
||||||
name_constraints: Option<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnedTrustAnchor {
|
|
||||||
/// Get a `webpki::TrustAnchor` by borrowing the owned elements.
|
|
||||||
pub(crate) fn to_trust_anchor(&self) -> webpki::TrustAnchor<'_> {
|
|
||||||
webpki::TrustAnchor {
|
|
||||||
subject: &self.subject,
|
|
||||||
spki: &self.spki,
|
|
||||||
name_constraints: self.name_constraints.as_deref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an `OwnedTrustAnchor` from its components.
|
|
||||||
///
|
|
||||||
/// `subject` is the subject field of the trust anchor.
|
|
||||||
///
|
|
||||||
/// `spki` is the `subjectPublicKeyInfo` field of the trust anchor.
|
|
||||||
///
|
|
||||||
/// `name_constraints` is the value of a DER-encoded name constraints to
|
|
||||||
/// apply for this trust anchor, if any.
|
|
||||||
pub fn from_subject_spki_name_constraints(
|
|
||||||
subject: impl Into<Vec<u8>>,
|
|
||||||
spki: impl Into<Vec<u8>>,
|
|
||||||
name_constraints: Option<impl Into<Vec<u8>>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
subject: subject.into(),
|
|
||||||
spki: spki.into(),
|
|
||||||
name_constraints: name_constraints.map(|x| x.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors that can occur during operations with RootCertStore
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum RootCertStoreError {
|
|
||||||
#[error(transparent)]
|
|
||||||
WebpkiError(#[from] webpki::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
IOError(#[from] std::io::Error),
|
|
||||||
#[error("Unexpected PEM certificate count. Expected 1 certificate, got {0}")]
|
|
||||||
PemCertUnexpectedCount(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A container for root certificates able to provide a root-of-trust
|
/// A container for root certificates able to provide a root-of-trust
|
||||||
/// for connection authentication.
|
/// for connection authentication.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RootCertStore {
|
pub struct RootCertStore {
|
||||||
/// The list of roots.
|
/// The list of roots.
|
||||||
pub roots: Vec<OwnedTrustAnchor>,
|
pub roots: Vec<TrustAnchor<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RootCertStore {
|
impl RootCertStore {
|
||||||
@@ -91,100 +42,4 @@ impl RootCertStore {
|
|||||||
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a single DER-encoded certificate to the store.
|
|
||||||
pub fn add(&mut self, der: &crate::key::Certificate) -> Result<(), RootCertStoreError> {
|
|
||||||
let ta = webpki::TrustAnchor::try_from_cert_der(&der.0)?;
|
|
||||||
let ota = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
ta.subject,
|
|
||||||
ta.spki,
|
|
||||||
ta.name_constraints,
|
|
||||||
);
|
|
||||||
self.roots.push(ota);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a single PEM-encoded certificate to the store.
|
|
||||||
pub fn add_pem(&mut self, pem: &str) -> Result<(), RootCertStoreError> {
|
|
||||||
let mut certificates = rustls_pemfile::certs(&mut pem.as_bytes())?;
|
|
||||||
|
|
||||||
if certificates.len() != 1 {
|
|
||||||
return Err(RootCertStoreError::PemCertUnexpectedCount(
|
|
||||||
certificates.len(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.add(&crate::key::Certificate(certificates.remove(0)))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds all the given TrustAnchors `anchors`. This does not
|
|
||||||
/// fail.
|
|
||||||
pub fn add_server_trust_anchors(
|
|
||||||
&mut self,
|
|
||||||
trust_anchors: impl Iterator<Item = OwnedTrustAnchor>,
|
|
||||||
) {
|
|
||||||
self.roots.extend(trust_anchors)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse the given DER-encoded certificates and add all that can be parsed
|
|
||||||
/// in a best-effort fashion.
|
|
||||||
///
|
|
||||||
/// This is because large collections of root certificates often
|
|
||||||
/// include ancient or syntactically invalid certificates.
|
|
||||||
///
|
|
||||||
/// Returns the number of certificates added, and the number that were ignored.
|
|
||||||
pub fn add_parsable_certificates(&mut self, der_certs: &[Vec<u8>]) -> (usize, usize) {
|
|
||||||
let mut valid_count = 0;
|
|
||||||
let mut invalid_count = 0;
|
|
||||||
|
|
||||||
for der_cert in der_certs {
|
|
||||||
match self.add(&crate::key::Certificate(der_cert.clone())) {
|
|
||||||
Ok(_) => valid_count += 1,
|
|
||||||
Err(_err) => invalid_count += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(valid_count, invalid_count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
const CA_PEM_CERT: &[u8] = include_bytes!("../testdata/cert-digicert.pem");
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_pem_ok() {
|
|
||||||
let pem = std::str::from_utf8(CA_PEM_CERT).unwrap();
|
|
||||||
assert!(RootCertStore::empty().add_pem(pem).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_pem_err_bad_cert() {
|
|
||||||
assert_eq!(
|
|
||||||
RootCertStore::empty()
|
|
||||||
.add_pem("bad pem")
|
|
||||||
.err()
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
"Unexpected PEM certificate count. Expected 1 certificate, got 0"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_pem_err_more_than_one_cert() {
|
|
||||||
let pem1 = std::str::from_utf8(CA_PEM_CERT).unwrap();
|
|
||||||
let pem2 = pem1;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
RootCertStore::empty()
|
|
||||||
.add_pem((pem1.to_owned() + pem2).as_str())
|
|
||||||
.err()
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
"Unexpected PEM certificate count. Expected 1 certificate, got 2"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{error::Error as StdError, fmt};
|
use std::{error::Error as StdError, fmt};
|
||||||
|
|
||||||
use crate::verify;
|
use rustls_pki_types as pki_types;
|
||||||
|
|
||||||
/// Encodes ways a client can know the expected name of the server.
|
/// Encodes ways a client can know the expected name of the server.
|
||||||
///
|
///
|
||||||
@@ -26,20 +26,16 @@ use crate::verify;
|
|||||||
/// ```
|
/// ```
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum ServerName {
|
pub struct ServerName(pub(crate) pki_types::ServerName<'static>);
|
||||||
/// The server is identified by a DNS name. The name
|
|
||||||
/// is sent in the TLS Server Name Indication (SNI)
|
|
||||||
/// extension.
|
|
||||||
DnsName(verify::DnsName),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerName {
|
impl ServerName {
|
||||||
/// Return the name that should go in the SNI extension.
|
/// Return the name that should go in the SNI extension.
|
||||||
/// If [`None`] is returned, the SNI extension is not included
|
/// If [`None`] is returned, the SNI extension is not included
|
||||||
/// in the handshake.
|
/// in the handshake.
|
||||||
pub fn for_sni(&self) -> Option<webpki::DnsNameRef<'_>> {
|
pub fn for_sni(&self) -> Option<pki_types::DnsName<'static>> {
|
||||||
match self {
|
match &self.0 {
|
||||||
Self::DnsName(dns_name) => Some(dns_name.0.as_ref()),
|
pki_types::ServerName::DnsName(dns_name) => Some(dns_name.clone()),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +45,17 @@ impl ServerName {
|
|||||||
DnsName = 0x01,
|
DnsName = 0x01,
|
||||||
}
|
}
|
||||||
|
|
||||||
let Self::DnsName(dns_name) = self;
|
let bytes: &[u8] = match &self.0 {
|
||||||
let bytes = dns_name.0.as_ref();
|
pki_types::ServerName::DnsName(dns_name) => dns_name.as_ref().as_ref(),
|
||||||
|
pki_types::ServerName::IpAddress(pki_types::IpAddr::V4(ip)) => ip.as_ref(),
|
||||||
|
pki_types::ServerName::IpAddress(pki_types::IpAddr::V6(ip)) => ip.as_ref(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut r = Vec::with_capacity(2 + bytes.as_ref().len());
|
let mut r = Vec::with_capacity(2 + bytes.len());
|
||||||
r.push(UniqueTypeCode::DnsName as u8);
|
r.push(UniqueTypeCode::DnsName as u8);
|
||||||
r.push(bytes.as_ref().len() as u8);
|
r.push(bytes.len() as u8);
|
||||||
r.extend_from_slice(bytes.as_ref());
|
r.extend_from_slice(bytes);
|
||||||
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
@@ -66,9 +66,9 @@ impl ServerName {
|
|||||||
impl TryFrom<&str> for ServerName {
|
impl TryFrom<&str> for ServerName {
|
||||||
type Error = InvalidDnsNameError;
|
type Error = InvalidDnsNameError;
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
match webpki::DnsNameRef::try_from_ascii_str(s) {
|
match pki_types::DnsName::try_from(s) {
|
||||||
Ok(dns) => Ok(Self::DnsName(verify::DnsName(dns.into()))),
|
Ok(dns) => Ok(Self(pki_types::ServerName::DnsName(dns.to_owned()))),
|
||||||
Err(webpki::InvalidDnsNameError) => Err(InvalidDnsNameError),
|
Err(_) => Err(InvalidDnsNameError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::msgs::enums::{AlertDescription, ContentType, HandshakeType};
|
use crate::msgs::enums::{AlertDescription, ContentType, HandshakeType};
|
||||||
use std::{error::Error as StdError, fmt, time::SystemTimeError};
|
use std::{error::Error as StdError, fmt};
|
||||||
|
use web_time::SystemTimeError;
|
||||||
|
|
||||||
/// rustls reports protocol errors using this type.
|
/// rustls reports protocol errors using this type.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@@ -41,8 +42,9 @@ pub enum Error {
|
|||||||
/// We couldn't decrypt a message. This is invariably fatal.
|
/// We couldn't decrypt a message. This is invariably fatal.
|
||||||
DecryptError,
|
DecryptError,
|
||||||
|
|
||||||
/// We couldn't encrypt a message because it was larger than the allowed message size.
|
/// We couldn't encrypt a message because it was larger than the allowed
|
||||||
/// This should never happen if the application is using valid record sizes.
|
/// message size. This should never happen if the application is using
|
||||||
|
/// valid record sizes.
|
||||||
EncryptError,
|
EncryptError,
|
||||||
|
|
||||||
/// The peer doesn't support a protocol version/feature we require.
|
/// The peer doesn't support a protocol version/feature we require.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use rustls_pki_types as pki_types;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
key,
|
key,
|
||||||
msgs::{
|
msgs::{
|
||||||
@@ -249,14 +251,14 @@ impl DecomposedSignatureScheme for SignatureScheme {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ServerNamePayload {
|
pub enum ServerNamePayload {
|
||||||
// Stored twice, bytes so we can round-trip, and DnsName for use
|
// Stored twice, bytes so we can round-trip, and DnsName for use
|
||||||
HostName((PayloadU16, webpki::DnsName)),
|
HostName((PayloadU16, pki_types::DnsName<'static>)),
|
||||||
Unknown(Payload),
|
Unknown(Payload),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerNamePayload {
|
impl ServerNamePayload {
|
||||||
pub fn new_hostname(hostname: webpki::DnsName) -> Self {
|
pub fn new_hostname(hostname: pki_types::DnsName<'static>) -> Self {
|
||||||
let raw = {
|
let raw = {
|
||||||
let s: &str = hostname.as_ref().into();
|
let s: &str = hostname.as_ref();
|
||||||
PayloadU16::new(s.as_bytes().into())
|
PayloadU16::new(s.as_bytes().into())
|
||||||
};
|
};
|
||||||
Self::HostName((raw, hostname))
|
Self::HostName((raw, hostname))
|
||||||
@@ -266,8 +268,8 @@ impl ServerNamePayload {
|
|||||||
let raw = PayloadU16::read(r)?;
|
let raw = PayloadU16::read(r)?;
|
||||||
|
|
||||||
let dns_name = {
|
let dns_name = {
|
||||||
match webpki::DnsNameRef::try_from_ascii(&raw.0) {
|
match pki_types::DnsName::try_from(raw.0.as_slice()) {
|
||||||
Ok(dns_name) => dns_name.into(),
|
Ok(dns_name) => dns_name.to_owned(),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
warn!("Illegal SNI hostname received {:?}", raw.0);
|
warn!("Illegal SNI hostname received {:?}", raw.0);
|
||||||
return None;
|
return None;
|
||||||
@@ -313,11 +315,12 @@ declare_u16_vec!(ServerNameRequest, ServerName);
|
|||||||
|
|
||||||
pub trait ConvertServerNameList {
|
pub trait ConvertServerNameList {
|
||||||
fn has_duplicate_names_for_type(&self) -> bool;
|
fn has_duplicate_names_for_type(&self) -> bool;
|
||||||
fn get_single_hostname(&self) -> Option<webpki::DnsNameRef<'_>>;
|
fn get_single_hostname(&self) -> Option<pki_types::DnsName<'static>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConvertServerNameList for ServerNameRequest {
|
impl ConvertServerNameList for ServerNameRequest {
|
||||||
/// RFC6066: "The ServerNameList MUST NOT contain more than one name of the same name_type."
|
/// RFC6066: "The ServerNameList MUST NOT contain more than one name of the
|
||||||
|
/// same name_type."
|
||||||
fn has_duplicate_names_for_type(&self) -> bool {
|
fn has_duplicate_names_for_type(&self) -> bool {
|
||||||
let mut seen = collections::HashSet::new();
|
let mut seen = collections::HashSet::new();
|
||||||
|
|
||||||
@@ -330,10 +333,10 @@ impl ConvertServerNameList for ServerNameRequest {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_single_hostname(&self) -> Option<webpki::DnsNameRef<'_>> {
|
fn get_single_hostname(&self) -> Option<pki_types::DnsName<'static>> {
|
||||||
fn only_dns_hostnames(name: &ServerName) -> Option<webpki::DnsNameRef<'_>> {
|
fn only_dns_hostnames(name: &ServerName) -> Option<pki_types::DnsName<'static>> {
|
||||||
if let ServerNamePayload::HostName((_, ref dns)) = name.payload {
|
if let ServerNamePayload::HostName((_, ref dns)) = name.payload {
|
||||||
Some(dns.as_ref())
|
Some(dns.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -694,16 +697,16 @@ impl Codec for ClientExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_hostname_trailing_dot_for_sni(dns_name: webpki::DnsNameRef) -> webpki::DnsName {
|
fn trim_hostname_trailing_dot_for_sni(
|
||||||
let dns_name_str: &str = dns_name.into();
|
dns_name: pki_types::DnsName<'static>,
|
||||||
|
) -> pki_types::DnsName<'static> {
|
||||||
|
let dns_name_str: &str = dns_name.as_ref();
|
||||||
|
|
||||||
// RFC6066: "The hostname is represented as a byte string using
|
// RFC6066: "The hostname is represented as a byte string using
|
||||||
// ASCII encoding without a trailing dot"
|
// ASCII encoding without a trailing dot"
|
||||||
if dns_name_str.ends_with('.') {
|
if dns_name_str.ends_with('.') {
|
||||||
let trimmed = &dns_name_str[0..dns_name_str.len() - 1];
|
let trimmed = &dns_name_str[0..dns_name_str.len() - 1];
|
||||||
webpki::DnsNameRef::try_from_ascii_str(trimmed)
|
pki_types::DnsName::try_from(trimmed).unwrap().to_owned()
|
||||||
.unwrap()
|
|
||||||
.to_owned()
|
|
||||||
} else {
|
} else {
|
||||||
dns_name.to_owned()
|
dns_name.to_owned()
|
||||||
}
|
}
|
||||||
@@ -711,7 +714,7 @@ fn trim_hostname_trailing_dot_for_sni(dns_name: webpki::DnsNameRef) -> webpki::D
|
|||||||
|
|
||||||
impl ClientExtension {
|
impl ClientExtension {
|
||||||
/// Make a basic SNI ServerNameRequest quoting `hostname`.
|
/// Make a basic SNI ServerNameRequest quoting `hostname`.
|
||||||
pub fn make_sni(dns_name: webpki::DnsNameRef) -> Self {
|
pub fn make_sni(dns_name: pki_types::DnsName<'static>) -> Self {
|
||||||
let name = ServerName {
|
let name = ServerName {
|
||||||
typ: ServerNameType::HostName,
|
typ: ServerNameType::HostName,
|
||||||
payload: ServerNamePayload::new_hostname(trim_hostname_trailing_dot_for_sni(dns_name)),
|
payload: ServerNamePayload::new_hostname(trim_hostname_trailing_dot_for_sni(dns_name)),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use rustls_pki_types as pki_types;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
base::{Payload, PayloadU16, PayloadU24, PayloadU8},
|
base::{Payload, PayloadU16, PayloadU24, PayloadU8},
|
||||||
codec::{put_u16, Codec, Reader},
|
codec::{put_u16, Codec, Reader},
|
||||||
@@ -5,7 +7,6 @@ use super::{
|
|||||||
handshake::*,
|
handshake::*,
|
||||||
};
|
};
|
||||||
use crate::key::Certificate;
|
use crate::key::Certificate;
|
||||||
use webpki::DnsNameRef;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rejects_short_random() {
|
fn rejects_short_random() {
|
||||||
@@ -186,7 +187,8 @@ fn can_roundtrip_multiname_sni() {
|
|||||||
|
|
||||||
assert!(req.has_duplicate_names_for_type());
|
assert!(req.has_duplicate_names_for_type());
|
||||||
|
|
||||||
let dns_name_str: &str = req.get_single_hostname().unwrap().into();
|
let dns_name = req.get_single_hostname().unwrap();
|
||||||
|
let dns_name_str: &str = dns_name.as_ref();
|
||||||
assert_eq!(dns_name_str, "hi");
|
assert_eq!(dns_name_str, "hi");
|
||||||
|
|
||||||
assert_eq!(req[0].typ, ServerNameType::HostName);
|
assert_eq!(req[0].typ, ServerNameType::HostName);
|
||||||
@@ -363,7 +365,7 @@ fn get_sample_clienthellopayload() -> ClientHelloPayload {
|
|||||||
ClientExtension::ECPointFormats(ECPointFormatList::supported()),
|
ClientExtension::ECPointFormats(ECPointFormatList::supported()),
|
||||||
ClientExtension::NamedGroups(vec![NamedGroup::X25519]),
|
ClientExtension::NamedGroups(vec![NamedGroup::X25519]),
|
||||||
ClientExtension::SignatureAlgorithms(vec![SignatureScheme::ECDSA_NISTP256_SHA256]),
|
ClientExtension::SignatureAlgorithms(vec![SignatureScheme::ECDSA_NISTP256_SHA256]),
|
||||||
ClientExtension::make_sni(DnsNameRef::try_from_ascii_str("hello").unwrap()),
|
ClientExtension::make_sni(pki_types::DnsName::try_from("hello").unwrap().to_owned()),
|
||||||
ClientExtension::SessionTicket(ClientSessionTicket::Request),
|
ClientExtension::SessionTicket(ClientSessionTicket::Request),
|
||||||
ClientExtension::SessionTicket(ClientSessionTicket::Offer(Payload(vec![]))),
|
ClientExtension::SessionTicket(ClientSessionTicket::Offer(Payload(vec![]))),
|
||||||
ClientExtension::Protocols(vec![PayloadU8(vec![0])]),
|
ClientExtension::Protocols(vec![PayloadU8(vec![0])]),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
anchors::{OwnedTrustAnchor, RootCertStore},
|
anchors::RootCertStore,
|
||||||
dns::ServerName,
|
dns::ServerName,
|
||||||
error::Error,
|
error::Error,
|
||||||
key::Certificate,
|
key::Certificate,
|
||||||
@@ -9,27 +9,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ring::digest::Digest;
|
use ring::digest::Digest;
|
||||||
use std::convert::TryFrom;
|
use rustls_pki_types as pki_types;
|
||||||
use web_time::SystemTime;
|
use web_time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
type SignatureAlgorithms = &'static [&'static webpki::SignatureAlgorithm];
|
type SignatureAlgorithms = &'static [&'static dyn pki_types::SignatureVerificationAlgorithm];
|
||||||
|
|
||||||
/// Which signature verification mechanisms we support. No particular
|
|
||||||
/// order.
|
|
||||||
static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[
|
|
||||||
&webpki::ECDSA_P256_SHA256,
|
|
||||||
&webpki::ECDSA_P256_SHA384,
|
|
||||||
&webpki::ECDSA_P384_SHA256,
|
|
||||||
&webpki::ECDSA_P384_SHA384,
|
|
||||||
&webpki::ED25519,
|
|
||||||
&webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
|
|
||||||
&webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
|
|
||||||
&webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
|
|
||||||
&webpki::RSA_PKCS1_2048_8192_SHA256,
|
|
||||||
&webpki::RSA_PKCS1_2048_8192_SHA384,
|
|
||||||
&webpki::RSA_PKCS1_2048_8192_SHA512,
|
|
||||||
&webpki::RSA_PKCS1_3072_8192_SHA384,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Marker types. These are used to bind the fact some verification
|
// Marker types. These are used to bind the fact some verification
|
||||||
// (certificate chain or handshake signature) has taken place into
|
// (certificate chain or handshake signature) has taken place into
|
||||||
@@ -170,7 +153,7 @@ pub trait ServerCertVerifier: Send + Sync {
|
|||||||
|
|
||||||
/// A type which encapsuates a string that is a syntactically valid DNS name.
|
/// A type which encapsuates a string that is a syntactically valid DNS name.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct DnsName(pub(crate) webpki::DnsName);
|
pub struct DnsName(pub(crate) pki_types::DnsName<'static>);
|
||||||
|
|
||||||
impl AsRef<str> for DnsName {
|
impl AsRef<str> for DnsName {
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
@@ -289,35 +272,33 @@ impl ServerCertVerifier for WebPkiVerifier {
|
|||||||
_ocsp_response: &[u8],
|
_ocsp_response: &[u8],
|
||||||
now: SystemTime,
|
now: SystemTime,
|
||||||
) -> Result<ServerCertVerified, Error> {
|
) -> Result<ServerCertVerified, Error> {
|
||||||
let (cert, chain, trustroots) = prepare(end_entity, intermediates, &self.roots)?;
|
let cert = pki_types::CertificateDer::from(end_entity.0.as_slice());
|
||||||
// `webpki::Time::try_from` does not work with `web_time::SystemTime`.
|
let cert = webpki::EndEntityCert::try_from(&cert).map_err(pki_error)?;
|
||||||
// To workaround this we convert `SystemTime` to seconds and use
|
let intermediates = intermediates
|
||||||
// `webpki::Time::from_seconds_since_unix_epoch` instead.
|
.iter()
|
||||||
let duration_since_epoch = now
|
.map(|c| pki_types::CertificateDer::from(c.0.as_slice()))
|
||||||
.duration_since(web_time::UNIX_EPOCH)
|
.collect::<Vec<_>>();
|
||||||
.map_err(|_| Error::FailedToGetCurrentTime)?;
|
let time = pki_types::UnixTime::since_unix_epoch(now.duration_since(UNIX_EPOCH)?);
|
||||||
let seconds_since_unix_epoch = duration_since_epoch.as_secs();
|
|
||||||
let webpki_now = webpki::Time::from_seconds_since_unix_epoch(seconds_since_unix_epoch);
|
|
||||||
|
|
||||||
let ServerName::DnsName(dns_name) = server_name;
|
cert.verify_for_usage(
|
||||||
|
webpki::ALL_VERIFICATION_ALGS,
|
||||||
let cert = cert
|
&self.roots.roots,
|
||||||
.verify_is_valid_tls_server_cert(
|
&intermediates,
|
||||||
SUPPORTED_SIG_ALGS,
|
time,
|
||||||
&webpki::TlsServerTrustAnchors(&trustroots),
|
webpki::KeyUsage::server_auth(),
|
||||||
&chain,
|
None,
|
||||||
webpki_now,
|
None,
|
||||||
)
|
)
|
||||||
.map_err(pki_error)
|
.map(|_| ())
|
||||||
.map(|_| cert)?;
|
.map_err(pki_error)?;
|
||||||
|
|
||||||
if let Some(policy) = &self.ct_policy {
|
if let Some(policy) = &self.ct_policy {
|
||||||
policy.verify(end_entity, now, scts)?;
|
policy.verify(end_entity, now, scts)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
cert.verify_is_valid_for_dns_name(dns_name.0.as_ref())
|
cert.verify_is_valid_for_subject_name(&server_name.0)
|
||||||
.map_err(pki_error)
|
|
||||||
.map(|_| ServerCertVerified::assertion())
|
.map(|_| ServerCertVerified::assertion())
|
||||||
|
.map_err(pki_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,31 +410,6 @@ impl CertificateTransparencyPolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CertChainAndRoots<'a, 'b> = (
|
|
||||||
webpki::EndEntityCert<'a>,
|
|
||||||
Vec<&'a [u8]>,
|
|
||||||
Vec<webpki::TrustAnchor<'b>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn prepare<'a, 'b>(
|
|
||||||
end_entity: &'a Certificate,
|
|
||||||
intermediates: &'a [Certificate],
|
|
||||||
roots: &'b RootCertStore,
|
|
||||||
) -> Result<CertChainAndRoots<'a, 'b>, Error> {
|
|
||||||
// EE cert must appear first.
|
|
||||||
let cert = webpki::EndEntityCert::try_from(end_entity.0.as_ref()).map_err(pki_error)?;
|
|
||||||
|
|
||||||
let intermediates: Vec<&'a [u8]> = intermediates.iter().map(|cert| cert.0.as_ref()).collect();
|
|
||||||
|
|
||||||
let trustroots: Vec<webpki::TrustAnchor> = roots
|
|
||||||
.roots
|
|
||||||
.iter()
|
|
||||||
.map(OwnedTrustAnchor::to_trust_anchor)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok((cert, intermediates, trustroots))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn pki_error(error: webpki::Error) -> Error {
|
pub(crate) fn pki_error(error: webpki::Error) -> Error {
|
||||||
use webpki::Error::*;
|
use webpki::Error::*;
|
||||||
match error {
|
match error {
|
||||||
@@ -466,20 +422,24 @@ pub(crate) fn pki_error(error: webpki::Error) -> Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ECDSA_SHA256: SignatureAlgorithms =
|
static ECDSA_SHA256: SignatureAlgorithms = &[
|
||||||
&[&webpki::ECDSA_P256_SHA256, &webpki::ECDSA_P384_SHA256];
|
webpki::ring::ECDSA_P256_SHA256,
|
||||||
|
webpki::ring::ECDSA_P384_SHA256,
|
||||||
|
];
|
||||||
|
|
||||||
static ECDSA_SHA384: SignatureAlgorithms =
|
static ECDSA_SHA384: SignatureAlgorithms = &[
|
||||||
&[&webpki::ECDSA_P256_SHA384, &webpki::ECDSA_P384_SHA384];
|
webpki::ring::ECDSA_P256_SHA384,
|
||||||
|
webpki::ring::ECDSA_P384_SHA384,
|
||||||
|
];
|
||||||
|
|
||||||
static ED25519: SignatureAlgorithms = &[&webpki::ED25519];
|
static ED25519: SignatureAlgorithms = &[webpki::ring::ED25519];
|
||||||
|
|
||||||
static RSA_SHA256: SignatureAlgorithms = &[&webpki::RSA_PKCS1_2048_8192_SHA256];
|
static RSA_SHA256: SignatureAlgorithms = &[webpki::ring::RSA_PKCS1_2048_8192_SHA256];
|
||||||
static RSA_SHA384: SignatureAlgorithms = &[&webpki::RSA_PKCS1_2048_8192_SHA384];
|
static RSA_SHA384: SignatureAlgorithms = &[webpki::ring::RSA_PKCS1_2048_8192_SHA384];
|
||||||
static RSA_SHA512: SignatureAlgorithms = &[&webpki::RSA_PKCS1_2048_8192_SHA512];
|
static RSA_SHA512: SignatureAlgorithms = &[webpki::ring::RSA_PKCS1_2048_8192_SHA512];
|
||||||
static RSA_PSS_SHA256: SignatureAlgorithms = &[&webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY];
|
static RSA_PSS_SHA256: SignatureAlgorithms = &[webpki::ring::RSA_PSS_2048_8192_SHA256_LEGACY_KEY];
|
||||||
static RSA_PSS_SHA384: SignatureAlgorithms = &[&webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY];
|
static RSA_PSS_SHA384: SignatureAlgorithms = &[webpki::ring::RSA_PSS_2048_8192_SHA384_LEGACY_KEY];
|
||||||
static RSA_PSS_SHA512: SignatureAlgorithms = &[&webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY];
|
static RSA_PSS_SHA512: SignatureAlgorithms = &[webpki::ring::RSA_PSS_2048_8192_SHA512_LEGACY_KEY];
|
||||||
|
|
||||||
fn convert_scheme(scheme: SignatureScheme) -> Result<SignatureAlgorithms, Error> {
|
fn convert_scheme(scheme: SignatureScheme) -> Result<SignatureAlgorithms, Error> {
|
||||||
match scheme {
|
match scheme {
|
||||||
@@ -514,7 +474,7 @@ fn verify_sig_using_any_alg(
|
|||||||
// webpki::SignatureAlgorithm. Therefore, convert_algs maps to several and
|
// webpki::SignatureAlgorithm. Therefore, convert_algs maps to several and
|
||||||
// we try them all.
|
// we try them all.
|
||||||
for alg in algs {
|
for alg in algs {
|
||||||
match cert.verify_signature(alg, message, sig) {
|
match cert.verify_signature(*alg, message, sig) {
|
||||||
Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue,
|
Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue,
|
||||||
res => return res,
|
res => return res,
|
||||||
}
|
}
|
||||||
@@ -529,7 +489,9 @@ fn verify_signed_struct(
|
|||||||
dss: &DigitallySignedStruct,
|
dss: &DigitallySignedStruct,
|
||||||
) -> Result<HandshakeSignatureValid, Error> {
|
) -> Result<HandshakeSignatureValid, Error> {
|
||||||
let possible_algs = convert_scheme(dss.scheme)?;
|
let possible_algs = convert_scheme(dss.scheme)?;
|
||||||
let cert = webpki::EndEntityCert::try_from(cert.0.as_ref()).map_err(pki_error)?;
|
|
||||||
|
let cert = pki_types::CertificateDer::from(cert.0.as_slice());
|
||||||
|
let cert = webpki::EndEntityCert::try_from(&cert).map_err(pki_error)?;
|
||||||
|
|
||||||
verify_sig_using_any_alg(&cert, possible_algs, message, &dss.sig.0)
|
verify_sig_using_any_alg(&cert, possible_algs, message, &dss.sig.0)
|
||||||
.map_err(pki_error)
|
.map_err(pki_error)
|
||||||
@@ -538,16 +500,16 @@ fn verify_signed_struct(
|
|||||||
|
|
||||||
fn convert_alg_tls13(
|
fn convert_alg_tls13(
|
||||||
scheme: SignatureScheme,
|
scheme: SignatureScheme,
|
||||||
) -> Result<&'static webpki::SignatureAlgorithm, Error> {
|
) -> Result<&'static dyn pki_types::SignatureVerificationAlgorithm, Error> {
|
||||||
use crate::msgs::enums::SignatureScheme::*;
|
use crate::msgs::enums::SignatureScheme::*;
|
||||||
|
|
||||||
match scheme {
|
match scheme {
|
||||||
ECDSA_NISTP256_SHA256 => Ok(&webpki::ECDSA_P256_SHA256),
|
ECDSA_NISTP256_SHA256 => Ok(webpki::ring::ECDSA_P256_SHA256),
|
||||||
ECDSA_NISTP384_SHA384 => Ok(&webpki::ECDSA_P384_SHA384),
|
ECDSA_NISTP384_SHA384 => Ok(webpki::ring::ECDSA_P384_SHA384),
|
||||||
ED25519 => Ok(&webpki::ED25519),
|
ED25519 => Ok(webpki::ring::ED25519),
|
||||||
RSA_PSS_SHA256 => Ok(&webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY),
|
RSA_PSS_SHA256 => Ok(webpki::ring::RSA_PSS_2048_8192_SHA256_LEGACY_KEY),
|
||||||
RSA_PSS_SHA384 => Ok(&webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY),
|
RSA_PSS_SHA384 => Ok(webpki::ring::RSA_PSS_2048_8192_SHA384_LEGACY_KEY),
|
||||||
RSA_PSS_SHA512 => Ok(&webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY),
|
RSA_PSS_SHA512 => Ok(webpki::ring::RSA_PSS_2048_8192_SHA512_LEGACY_KEY),
|
||||||
_ => {
|
_ => {
|
||||||
let error_msg = format!("received unsupported sig scheme {scheme:?}");
|
let error_msg = format!("received unsupported sig scheme {scheme:?}");
|
||||||
Err(Error::PeerMisbehavedError(error_msg))
|
Err(Error::PeerMisbehavedError(error_msg))
|
||||||
@@ -583,7 +545,8 @@ fn verify_tls13(
|
|||||||
) -> Result<HandshakeSignatureValid, Error> {
|
) -> Result<HandshakeSignatureValid, Error> {
|
||||||
let alg = convert_alg_tls13(dss.scheme)?;
|
let alg = convert_alg_tls13(dss.scheme)?;
|
||||||
|
|
||||||
let cert = webpki::EndEntityCert::try_from(cert.0.as_ref()).map_err(pki_error)?;
|
let cert = pki_types::CertificateDer::from(cert.0.as_slice());
|
||||||
|
let cert = webpki::EndEntityCert::try_from(&cert).map_err(pki_error)?;
|
||||||
|
|
||||||
cert.verify_signature(alg, msg, &dss.sig.0)
|
cert.verify_signature(alg, msg, &dss.sig.0)
|
||||||
.map_err(pki_error)
|
.map_err(pki_error)
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ derive_builder = { workspace = true }
|
|||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
opaque-debug = { workspace = true }
|
opaque-debug = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
rustls-pki-types = "1.12.0"
|
rustls-pki-types = { workspace = true }
|
||||||
|
rustls-webpki = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["sync"] }
|
tokio = { workspace = true, features = ["sync"] }
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use semver::Version;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub use tlsn_core::webpki::{CertificateDer, PrivateKeyDer, RootCertStore};
|
||||||
|
|
||||||
// Default is 32 bytes to decrypt the TLS protocol messages.
|
// Default is 32 bytes to decrypt the TLS protocol messages.
|
||||||
const DEFAULT_MAX_RECV_ONLINE: usize = 32;
|
const DEFAULT_MAX_RECV_ONLINE: usize = 32;
|
||||||
// Default maximum number of TLS records to allow.
|
// Default maximum number of TLS records to allow.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use tlsn_core::connection::{ServerCertData, ServerName};
|
use tlsn_core::connection::{HandshakeData, ServerName};
|
||||||
|
|
||||||
/// Message sent from Prover to Verifier to prove the server identity.
|
/// Message sent from Prover to Verifier to prove the server identity.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@@ -10,5 +10,5 @@ pub(crate) struct ServerIdentityProof {
|
|||||||
/// Server name.
|
/// Server name.
|
||||||
pub name: ServerName,
|
pub name: ServerName,
|
||||||
/// Server identity data.
|
/// Server identity data.
|
||||||
pub data: ServerCertData,
|
pub data: HandshakeData,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ pub mod state;
|
|||||||
pub use config::{ProverConfig, ProverConfigBuilder, TlsConfig, TlsConfigBuilder};
|
pub use config::{ProverConfig, ProverConfigBuilder, TlsConfig, TlsConfigBuilder};
|
||||||
pub use error::ProverError;
|
pub use error::ProverError;
|
||||||
pub use future::ProverFuture;
|
pub use future::ProverFuture;
|
||||||
|
use rustls_pki_types::CertificateDer;
|
||||||
pub use tlsn_core::{ProveConfig, ProveConfigBuilder, ProveConfigBuilderError, ProverOutput};
|
pub use tlsn_core::{ProveConfig, ProveConfigBuilder, ProveConfigBuilderError, ProverOutput};
|
||||||
|
|
||||||
use mpz_common::Context;
|
use mpz_common::Context;
|
||||||
use mpz_core::Block;
|
use mpz_core::Block;
|
||||||
use mpz_garble_core::Delta;
|
use mpz_garble_core::Delta;
|
||||||
use mpz_vm_core::prelude::*;
|
use mpz_vm_core::prelude::*;
|
||||||
|
use webpki::anchor_from_trusted_cert;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Role,
|
Role,
|
||||||
@@ -39,7 +41,7 @@ use tls_client_async::{TlsConnection, bind_client};
|
|||||||
use tls_core::msgs::enums::ContentType;
|
use tls_core::msgs::enums::ContentType;
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
ProvePayload,
|
ProvePayload,
|
||||||
connection::ServerCertData,
|
connection::{HandshakeData, ServerName},
|
||||||
hash::{Blake3, HashAlgId, HashAlgorithm, Keccak256, Sha256},
|
hash::{Blake3, HashAlgId, HashAlgorithm, Keccak256, Sha256},
|
||||||
transcript::{TlsTranscript, Transcript, TranscriptCommitment, TranscriptSecret},
|
transcript::{TlsTranscript, Transcript, TranscriptCommitment, TranscriptSecret},
|
||||||
};
|
};
|
||||||
@@ -179,21 +181,40 @@ impl Prover<state::Setup> {
|
|||||||
|
|
||||||
let (mpc_ctrl, mpc_fut) = mpc_tls.run();
|
let (mpc_ctrl, mpc_fut) = mpc_tls.run();
|
||||||
|
|
||||||
|
let ServerName::Dns(server_name) = self.config.server_name();
|
||||||
let server_name =
|
let server_name =
|
||||||
TlsServerName::try_from(self.config.server_name().as_str()).map_err(|_| {
|
TlsServerName::try_from(server_name.as_ref()).expect("name was validated");
|
||||||
ProverError::config(format!(
|
|
||||||
"invalid server name: {}",
|
let root_store = if let Some(root_store) = self.config.tls_config().root_store() {
|
||||||
self.config.server_name()
|
let roots = root_store
|
||||||
))
|
.roots
|
||||||
})?;
|
.iter()
|
||||||
|
.map(|cert| {
|
||||||
|
let der = CertificateDer::from_slice(&cert.0);
|
||||||
|
anchor_from_trusted_cert(&der)
|
||||||
|
.map(|anchor| anchor.to_owned())
|
||||||
|
.map_err(ProverError::config)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
tls_client::RootCertStore { roots }
|
||||||
|
} else {
|
||||||
|
tls_client::RootCertStore {
|
||||||
|
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let config = tls_client::ClientConfig::builder()
|
let config = tls_client::ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
.with_safe_defaults()
|
||||||
.with_root_certificates(self.config.tls_config().root_store().clone());
|
.with_root_certificates(root_store);
|
||||||
|
|
||||||
let config = if let Some((cert, key)) = self.config.tls_config().client_auth() {
|
let config = if let Some((cert, key)) = self.config.tls_config().client_auth() {
|
||||||
config
|
config
|
||||||
.with_single_cert(cert.clone(), key.clone())
|
.with_single_cert(
|
||||||
|
cert.iter()
|
||||||
|
.map(|cert| tls_client::Certificate(cert.0.clone()))
|
||||||
|
.collect(),
|
||||||
|
tls_client::PrivateKey(key.0.clone()),
|
||||||
|
)
|
||||||
.map_err(ProverError::config)?
|
.map_err(ProverError::config)?
|
||||||
} else {
|
} else {
|
||||||
config.with_no_client_auth()
|
config.with_no_client_auth()
|
||||||
@@ -350,10 +371,10 @@ impl Prover<state::Committed> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let payload = ProvePayload {
|
let payload = ProvePayload {
|
||||||
server_identity: config.server_identity().then(|| {
|
handshake: config.server_identity().then(|| {
|
||||||
(
|
(
|
||||||
self.config.server_name().clone(),
|
self.config.server_name().clone(),
|
||||||
ServerCertData {
|
HandshakeData {
|
||||||
certs: tls_transcript
|
certs: tls_transcript
|
||||||
.server_cert_chain()
|
.server_cert_chain()
|
||||||
.expect("server cert chain is present")
|
.expect("server cert chain is present")
|
||||||
@@ -362,7 +383,7 @@ impl Prover<state::Committed> {
|
|||||||
.server_signature()
|
.server_signature()
|
||||||
.expect("server signature is present")
|
.expect("server signature is present")
|
||||||
.clone(),
|
.clone(),
|
||||||
handshake: tls_transcript.handshake_data().clone(),
|
binding: tls_transcript.certificate_binding().clone(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use crate::config::{NetworkSetting, ProtocolConfig};
|
|
||||||
use mpc_tls::Config;
|
use mpc_tls::Config;
|
||||||
use rustls_pki_types::{CertificateDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer, pem::PemObject};
|
use tlsn_core::{
|
||||||
use tls_core::{
|
connection::ServerName,
|
||||||
anchors::{OwnedTrustAnchor, RootCertStore},
|
webpki::{CertificateDer, PrivateKeyDer, RootCertStore},
|
||||||
key,
|
|
||||||
};
|
};
|
||||||
use tlsn_core::connection::ServerName;
|
|
||||||
|
use crate::config::{NetworkSetting, ProtocolConfig};
|
||||||
|
|
||||||
/// Configuration for the prover.
|
/// Configuration for the prover.
|
||||||
#[derive(Debug, Clone, derive_builder::Builder)]
|
#[derive(Debug, Clone, derive_builder::Builder)]
|
||||||
@@ -67,31 +66,13 @@ impl ProverConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration for the prover's TLS connection.
|
/// Configuration for the prover's TLS connection.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct TlsConfig {
|
pub struct TlsConfig {
|
||||||
/// Root certificates.
|
/// Root certificates.
|
||||||
root_store: RootCertStore,
|
root_store: Option<RootCertStore>,
|
||||||
/// Certificate chain and a matching private key for client
|
/// Certificate chain and a matching private key for client
|
||||||
/// authentication.
|
/// authentication.
|
||||||
client_auth: Option<(Vec<key::Certificate>, key::PrivateKey)>,
|
client_auth: Option<(Vec<CertificateDer>, PrivateKeyDer)>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TlsConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
|
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
ta.subject.as_ref(),
|
|
||||||
ta.subject_public_key_info.as_ref(),
|
|
||||||
ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
root_store,
|
|
||||||
client_auth: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TlsConfig {
|
impl TlsConfig {
|
||||||
@@ -100,13 +81,13 @@ impl TlsConfig {
|
|||||||
TlsConfigBuilder::default()
|
TlsConfigBuilder::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn root_store(&self) -> &RootCertStore {
|
pub(crate) fn root_store(&self) -> Option<&RootCertStore> {
|
||||||
&self.root_store
|
self.root_store.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a certificate chain and a matching private key for client
|
/// Returns a certificate chain and a matching private key for client
|
||||||
/// authentication.
|
/// authentication.
|
||||||
pub fn client_auth(&self) -> &Option<(Vec<key::Certificate>, key::PrivateKey)> {
|
pub fn client_auth(&self) -> &Option<(Vec<CertificateDer>, PrivateKeyDer)> {
|
||||||
&self.client_auth
|
&self.client_auth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +96,7 @@ impl TlsConfig {
|
|||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct TlsConfigBuilder {
|
pub struct TlsConfigBuilder {
|
||||||
root_store: Option<RootCertStore>,
|
root_store: Option<RootCertStore>,
|
||||||
client_auth: Option<(Vec<key::Certificate>, key::PrivateKey)>,
|
client_auth: Option<(Vec<CertificateDer>, PrivateKeyDer)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TlsConfigBuilder {
|
impl TlsConfigBuilder {
|
||||||
@@ -138,74 +119,16 @@ impl TlsConfigBuilder {
|
|||||||
///
|
///
|
||||||
/// - Each certificate in the chain must be in the X.509 format.
|
/// - Each certificate in the chain must be in the X.509 format.
|
||||||
/// - The key must be in the ASN.1 format (either PKCS#8 or PKCS#1).
|
/// - The key must be in the ASN.1 format (either PKCS#8 or PKCS#1).
|
||||||
pub fn client_auth(&mut self, cert_key: (Vec<Vec<u8>>, Vec<u8>)) -> &mut Self {
|
pub fn client_auth(&mut self, cert_key: (Vec<CertificateDer>, PrivateKeyDer)) -> &mut Self {
|
||||||
let certs = cert_key
|
self.client_auth = Some(cert_key);
|
||||||
.0
|
|
||||||
.into_iter()
|
|
||||||
.map(key::Certificate)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
self.client_auth = Some((certs, key::PrivateKey(cert_key.1)));
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a PEM-encoded certificate chain and a matching private key for
|
|
||||||
/// client authentication.
|
|
||||||
///
|
|
||||||
/// Often the chain will consist of a single end-entity certificate.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `cert_key` - A tuple containing the certificate chain and the private
|
|
||||||
/// key.
|
|
||||||
///
|
|
||||||
/// - Each certificate in the chain must be in the X.509 format.
|
|
||||||
/// - The key must be in the ASN.1 format (either PKCS#8 or PKCS#1).
|
|
||||||
pub fn client_auth_pem(
|
|
||||||
&mut self,
|
|
||||||
cert_key: (Vec<Vec<u8>>, Vec<u8>),
|
|
||||||
) -> Result<&mut Self, TlsConfigError> {
|
|
||||||
let key = match PrivatePkcs8KeyDer::from_pem_slice(&cert_key.1) {
|
|
||||||
// Try to parse as PEM PKCS#8.
|
|
||||||
Ok(key) => (*key.secret_pkcs8_der()).to_vec(),
|
|
||||||
// Otherwise, try to parse as PEM PKCS#1.
|
|
||||||
Err(_) => match PrivatePkcs1KeyDer::from_pem_slice(&cert_key.1) {
|
|
||||||
Ok(key) => (*key.secret_pkcs1_der()).to_vec(),
|
|
||||||
Err(_) => return Err(ErrorRepr::InvalidKey.into()),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let certs = cert_key
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.map(|c| {
|
|
||||||
let c =
|
|
||||||
CertificateDer::from_pem_slice(c).map_err(|_| ErrorRepr::InvalidCertificate)?;
|
|
||||||
Ok::<key::Certificate, TlsConfigError>(key::Certificate(c.as_ref().to_vec()))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
self.client_auth = Some((certs, key::PrivateKey(key)));
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the TLS configuration.
|
/// Builds the TLS configuration.
|
||||||
pub fn build(&self) -> Result<TlsConfig, TlsConfigError> {
|
pub fn build(self) -> Result<TlsConfig, TlsConfigError> {
|
||||||
Ok(TlsConfig {
|
Ok(TlsConfig {
|
||||||
root_store: self.root_store.clone().unwrap_or_else(|| {
|
root_store: self.root_store,
|
||||||
let mut root_store = RootCertStore::empty();
|
client_auth: self.client_auth,
|
||||||
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(
|
|
||||||
|ta| {
|
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
ta.subject.as_ref(),
|
|
||||||
ta.subject_public_key_info.as_ref(),
|
|
||||||
ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
));
|
|
||||||
root_store
|
|
||||||
}),
|
|
||||||
client_auth: self.client_auth.clone(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,10 +139,5 @@ impl TlsConfigBuilder {
|
|||||||
pub struct TlsConfigError(#[from] ErrorRepr);
|
pub struct TlsConfigError(#[from] ErrorRepr);
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error("tls config error: {0}")]
|
#[error("tls config error")]
|
||||||
enum ErrorRepr {
|
enum ErrorRepr {}
|
||||||
#[error("the certificate for client authentication is invalid")]
|
|
||||||
InvalidCertificate,
|
|
||||||
#[error("the private key for client authentication is invalid")]
|
|
||||||
InvalidKey,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
|
pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
|
||||||
pub use error::VerifierError;
|
pub use error::VerifierError;
|
||||||
pub use tlsn_core::{VerifierOutput, VerifyConfig, VerifyConfigBuilder, VerifyConfigBuilderError};
|
pub use tlsn_core::{
|
||||||
|
VerifierOutput, VerifyConfig, VerifyConfigBuilder, VerifyConfigBuilderError,
|
||||||
|
webpki::ServerCertVerifier,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Role,
|
Role,
|
||||||
@@ -31,7 +34,7 @@ use mpz_core::Block;
|
|||||||
use mpz_garble_core::Delta;
|
use mpz_garble_core::Delta;
|
||||||
use mpz_vm_core::prelude::*;
|
use mpz_vm_core::prelude::*;
|
||||||
use serio::stream::IoStreamExt;
|
use serio::stream::IoStreamExt;
|
||||||
use tls_core::{msgs::enums::ContentType, verify::WebPkiVerifier};
|
use tls_core::msgs::enums::ContentType;
|
||||||
use tlsn_core::{
|
use tlsn_core::{
|
||||||
ProvePayload,
|
ProvePayload,
|
||||||
connection::{ConnectionInfo, ServerName},
|
connection::{ConnectionInfo, ServerName},
|
||||||
@@ -305,15 +308,20 @@ impl Verifier<state::Committed> {
|
|||||||
} = &mut self.state;
|
} = &mut self.state;
|
||||||
|
|
||||||
let ProvePayload {
|
let ProvePayload {
|
||||||
server_identity,
|
handshake,
|
||||||
transcript,
|
transcript,
|
||||||
transcript_commit,
|
transcript_commit,
|
||||||
} = mux_fut
|
} = mux_fut
|
||||||
.poll_with(ctx.io_mut().expect_next().map_err(VerifierError::from))
|
.poll_with(ctx.io_mut().expect_next().map_err(VerifierError::from))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let verifier = WebPkiVerifier::new(self.config.root_store().clone(), None);
|
let verifier = if let Some(root_store) = self.config.root_store() {
|
||||||
let server_name = if let Some((name, cert_data)) = server_identity {
|
ServerCertVerifier::new(root_store).map_err(VerifierError::config)?
|
||||||
|
} else {
|
||||||
|
ServerCertVerifier::mozilla()
|
||||||
|
};
|
||||||
|
|
||||||
|
let server_name = if let Some((name, cert_data)) = handshake {
|
||||||
cert_data
|
cert_data
|
||||||
.verify(
|
.verify(
|
||||||
&verifier,
|
&verifier,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::fmt::{Debug, Formatter, Result};
|
|||||||
|
|
||||||
use crate::config::{NetworkSetting, ProtocolConfig, ProtocolConfigValidator};
|
use crate::config::{NetworkSetting, ProtocolConfig, ProtocolConfigValidator};
|
||||||
use mpc_tls::Config;
|
use mpc_tls::Config;
|
||||||
use tls_core::anchors::{OwnedTrustAnchor, RootCertStore};
|
use tlsn_core::webpki::RootCertStore;
|
||||||
|
|
||||||
/// Configuration for the [`Verifier`](crate::tls::Verifier).
|
/// Configuration for the [`Verifier`](crate::tls::Verifier).
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
@@ -10,8 +10,8 @@ use tls_core::anchors::{OwnedTrustAnchor, RootCertStore};
|
|||||||
#[builder(pattern = "owned")]
|
#[builder(pattern = "owned")]
|
||||||
pub struct VerifierConfig {
|
pub struct VerifierConfig {
|
||||||
protocol_config_validator: ProtocolConfigValidator,
|
protocol_config_validator: ProtocolConfigValidator,
|
||||||
#[builder(default = "default_root_store()")]
|
#[builder(setter(strip_option))]
|
||||||
root_store: RootCertStore,
|
root_store: Option<RootCertStore>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for VerifierConfig {
|
impl Debug for VerifierConfig {
|
||||||
@@ -34,8 +34,8 @@ impl VerifierConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the root certificate store.
|
/// Returns the root certificate store.
|
||||||
pub fn root_store(&self) -> &RootCertStore {
|
pub fn root_store(&self) -> Option<&RootCertStore> {
|
||||||
&self.root_store
|
self.root_store.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_mpc_tls_config(&self, protocol_config: &ProtocolConfig) -> Config {
|
pub(crate) fn build_mpc_tls_config(&self, protocol_config: &ProtocolConfig) -> Config {
|
||||||
@@ -61,16 +61,3 @@ impl VerifierConfig {
|
|||||||
builder.build().unwrap()
|
builder.build().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_root_store() -> RootCertStore {
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
|
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
ta.subject.as_ref(),
|
|
||||||
ta.subject_public_key_info.as_ref(),
|
|
||||||
ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
root_store
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ impl VerifierError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn config<E>(source: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<Box<dyn Error + Send + Sync + 'static>>,
|
||||||
|
{
|
||||||
|
Self::new(ErrorKind::Config, source)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn mpc<E>(source: E) -> Self
|
pub(crate) fn mpc<E>(source: E) -> Self
|
||||||
where
|
where
|
||||||
E: Into<Box<dyn Error + Send + Sync + 'static>>,
|
E: Into<Box<dyn Error + Send + Sync + 'static>>,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use futures::{AsyncReadExt, AsyncWriteExt};
|
use futures::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tlsn::{
|
use tlsn::{
|
||||||
config::{ProtocolConfig, ProtocolConfigValidator},
|
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
|
||||||
|
connection::ServerName,
|
||||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||||
transcript::{TranscriptCommitConfig, TranscriptCommitment},
|
transcript::{TranscriptCommitConfig, TranscriptCommitment},
|
||||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||||
@@ -37,19 +38,17 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(verifier_soc
|
|||||||
|
|
||||||
let server_task = tokio::spawn(bind(server_socket.compat()));
|
let server_task = tokio::spawn(bind(server_socket.compat()));
|
||||||
|
|
||||||
let mut root_store = tls_core::anchors::RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut tls_config_builder = TlsConfig::builder();
|
let mut tls_config_builder = TlsConfig::builder();
|
||||||
tls_config_builder.root_store(root_store);
|
tls_config_builder.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
});
|
||||||
|
|
||||||
let tls_config = tls_config_builder.build().unwrap();
|
let tls_config = tls_config_builder.build().unwrap();
|
||||||
|
|
||||||
|
let server_name = ServerName::Dns(SERVER_DOMAIN.try_into().unwrap());
|
||||||
let prover = Prover::new(
|
let prover = Prover::new(
|
||||||
ProverConfig::builder()
|
ProverConfig::builder()
|
||||||
.server_name(SERVER_DOMAIN)
|
.server_name(server_name)
|
||||||
.tls_config(tls_config)
|
.tls_config(tls_config)
|
||||||
.protocol_config(
|
.protocol_config(
|
||||||
ProtocolConfig::builder()
|
ProtocolConfig::builder()
|
||||||
@@ -110,11 +109,6 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(verifier_soc
|
|||||||
|
|
||||||
#[instrument(skip(socket))]
|
#[instrument(skip(socket))]
|
||||||
async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(socket: T) {
|
async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(socket: T) {
|
||||||
let mut root_store = tls_core::anchors::RootCertStore::empty();
|
|
||||||
root_store
|
|
||||||
.add(&tls_core::key::Certificate(CA_CERT_DER.to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let config_validator = ProtocolConfigValidator::builder()
|
let config_validator = ProtocolConfigValidator::builder()
|
||||||
.max_sent_data(MAX_SENT_DATA)
|
.max_sent_data(MAX_SENT_DATA)
|
||||||
.max_recv_data(MAX_RECV_DATA)
|
.max_recv_data(MAX_RECV_DATA)
|
||||||
@@ -123,7 +117,9 @@ async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(soc
|
|||||||
|
|
||||||
let verifier = Verifier::new(
|
let verifier = Verifier::new(
|
||||||
VerifierConfig::builder()
|
VerifierConfig::builder()
|
||||||
.root_store(root_store)
|
.root_store(RootCertStore {
|
||||||
|
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||||
|
})
|
||||||
.protocol_config_validator(config_validator)
|
.protocol_config_validator(config_validator)
|
||||||
.build()
|
.build()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@@ -140,7 +136,9 @@ async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(soc
|
|||||||
|
|
||||||
let transcript = transcript.unwrap();
|
let transcript = transcript.unwrap();
|
||||||
|
|
||||||
assert_eq!(server_name.unwrap().as_str(), SERVER_DOMAIN);
|
let ServerName::Dns(server_name) = server_name.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(server_name.as_str(), SERVER_DOMAIN);
|
||||||
assert!(!transcript.is_complete());
|
assert!(!transcript.is_complete());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
transcript.sent_authed().iter_ranges().next().unwrap(),
|
transcript.sent_authed().iter_ranges().next().unwrap(),
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
use crate::types::NetworkSetting;
|
use crate::types::NetworkSetting;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tlsn::config::ProtocolConfig;
|
use tlsn::{
|
||||||
|
config::{CertificateDer, PrivateKeyDer, ProtocolConfig},
|
||||||
|
connection::ServerName,
|
||||||
|
};
|
||||||
use tsify_next::Tsify;
|
use tsify_next::Tsify;
|
||||||
|
use wasm_bindgen::JsError;
|
||||||
|
|
||||||
#[derive(Debug, Tsify, Deserialize)]
|
#[derive(Debug, Tsify, Deserialize)]
|
||||||
#[tsify(from_wasm_abi)]
|
#[tsify(from_wasm_abi)]
|
||||||
@@ -17,8 +21,10 @@ pub struct ProverConfig {
|
|||||||
pub client_auth: Option<(Vec<Vec<u8>>, Vec<u8>)>,
|
pub client_auth: Option<(Vec<Vec<u8>>, Vec<u8>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ProverConfig> for tlsn::prover::ProverConfig {
|
impl TryFrom<ProverConfig> for tlsn::prover::ProverConfig {
|
||||||
fn from(value: ProverConfig) -> Self {
|
type Error = JsError;
|
||||||
|
|
||||||
|
fn try_from(value: ProverConfig) -> Result<Self, Self::Error> {
|
||||||
let mut builder = ProtocolConfig::builder();
|
let mut builder = ProtocolConfig::builder();
|
||||||
|
|
||||||
builder.max_sent_data(value.max_sent_data);
|
builder.max_sent_data(value.max_sent_data);
|
||||||
@@ -44,21 +50,36 @@ impl From<ProverConfig> for tlsn::prover::ProverConfig {
|
|||||||
let protocol_config = builder.build().unwrap();
|
let protocol_config = builder.build().unwrap();
|
||||||
|
|
||||||
let mut builder = tlsn::prover::TlsConfig::builder();
|
let mut builder = tlsn::prover::TlsConfig::builder();
|
||||||
if let Some(cert_key) = value.client_auth {
|
if let Some((certs, key)) = value.client_auth {
|
||||||
// Try to parse as PEM-encoded.
|
let certs = certs
|
||||||
if builder.client_auth_pem(cert_key.clone()).is_err() {
|
.into_iter()
|
||||||
// Otherwise assume DER encoding.
|
.map(|cert| {
|
||||||
builder.client_auth(cert_key);
|
// Try to parse as PEM-encoded, otherwise assume DER.
|
||||||
}
|
if let Ok(cert) = CertificateDer::from_pem_slice(&cert) {
|
||||||
|
cert
|
||||||
|
} else {
|
||||||
|
CertificateDer(cert)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let key = PrivateKeyDer(key);
|
||||||
|
builder.client_auth((certs, key));
|
||||||
}
|
}
|
||||||
let tls_config = builder.build().unwrap();
|
let tls_config = builder.build().unwrap();
|
||||||
|
|
||||||
|
let server_name = ServerName::Dns(
|
||||||
|
value
|
||||||
|
.server_name
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| JsError::new("invalid server name"))?,
|
||||||
|
);
|
||||||
|
|
||||||
let mut builder = tlsn::prover::ProverConfig::builder();
|
let mut builder = tlsn::prover::ProverConfig::builder();
|
||||||
builder
|
builder
|
||||||
.server_name(value.server_name.as_ref())
|
.server_name(server_name)
|
||||||
.protocol_config(protocol_config)
|
.protocol_config(protocol_config)
|
||||||
.tls_config(tls_config);
|
.tls_config(tls_config);
|
||||||
|
|
||||||
builder.build().unwrap()
|
Ok(builder.build().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ impl State {
|
|||||||
#[wasm_bindgen(js_class = Prover)]
|
#[wasm_bindgen(js_class = Prover)]
|
||||||
impl JsProver {
|
impl JsProver {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(config: ProverConfig) -> JsProver {
|
pub fn new(config: ProverConfig) -> Result<JsProver> {
|
||||||
JsProver {
|
Ok(JsProver {
|
||||||
state: State::Initialized(Prover::new(config.into())),
|
state: State::Initialized(Prover::new(config.try_into()?)),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up the prover.
|
/// Set up the prover.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pub use config::VerifierConfig;
|
|||||||
use enum_try_as_inner::EnumTryAsInner;
|
use enum_try_as_inner::EnumTryAsInner;
|
||||||
use tls_core::msgs::enums::ContentType;
|
use tls_core::msgs::enums::ContentType;
|
||||||
use tlsn::{
|
use tlsn::{
|
||||||
connection::{ConnectionInfo, TranscriptLength},
|
connection::{ConnectionInfo, ServerName, TranscriptLength},
|
||||||
verifier::{
|
verifier::{
|
||||||
state::{self, Initialized},
|
state::{self, Initialized},
|
||||||
Verifier, VerifyConfig,
|
Verifier, VerifyConfig,
|
||||||
@@ -106,7 +106,10 @@ impl JsVerifier {
|
|||||||
self.state = State::Complete;
|
self.state = State::Complete;
|
||||||
|
|
||||||
Ok(VerifierOutput {
|
Ok(VerifierOutput {
|
||||||
server_name: output.server_name.map(|s| s.as_str().to_string()),
|
server_name: output.server_name.map(|name| {
|
||||||
|
let ServerName::Dns(name) = name;
|
||||||
|
name.to_string()
|
||||||
|
}),
|
||||||
connection_info: connection_info.into(),
|
connection_info: connection_info.into(),
|
||||||
transcript: output.transcript.map(|t| t.into()),
|
transcript: output.transcript.map(|t| t.into()),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user