Skip to content

Commit

Permalink
transports/tls-quic: Implement Libp2pCertificateVerifier for rustls
Browse files Browse the repository at this point in the history
  • Loading branch information
kpp committed Jul 19, 2021
1 parent cf80fa7 commit f1a961f
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 4 deletions.
3 changes: 2 additions & 1 deletion transports/tls-quic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ rcgen = "0.8.11"
x509-parser = "0.9.2"
der-parser = { version = "5.1.0", features = ["serialize"] }
yasna = "0.4.0"
rustls = "0.19.1"
rustls = { version = "0.19.1", features = ["dangerous_configuration"] }
ring = "0.16.20"
webpki = "0.21.0"
6 changes: 3 additions & 3 deletions transports/tls-quic/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Certificate handling for libp2p
//! X.509 certificate handling for libp2p
//!
//! This module handles generation, signing, and verification of certificates.
Expand Down Expand Up @@ -116,7 +116,7 @@ pub struct P2pCertificate<'a> {
}

/// Parse TLS certificate from DER input that includes a libp2p-specific
/// certificate extension containing the public key of the given keypair.
/// certificate extension containing a public key of a peer.
pub fn parse_certificate(der_input: &[u8]) -> Result<P2pCertificate, X509Error> {
let x509 = X509Certificate::from_der(der_input)
.map(|(_rest_input, x509)| x509)
Expand Down Expand Up @@ -272,7 +272,7 @@ impl P2pCertificate<'_> {

/// Return the signature scheme corresponding to [`AlgorithmIdentifier`]s
/// of `subject_pki` and `signature_algorithm`
/// according to https://tools.ietf.org/id/draft-ietf-tls-tls13-21.html#rfc.section.4.2.3.
/// according to `<https://tools.ietf.org/id/draft-ietf-tls-tls13-21.html#rfc.section.4.2.3>`.
pub fn signature_scheme(&self) -> Option<rustls::SignatureScheme> {
// Certificates MUST use the NamedCurve encoding for elliptic curve parameters.
// Endpoints MUST abort the connection attempt if it is not used.
Expand Down
1 change: 1 addition & 0 deletions transports/tls-quic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
// DEALINGS IN THE SOFTWARE.

pub mod certificate;
pub mod verifier;
211 changes: 211 additions & 0 deletions transports/tls-quic/src/verifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! TLS 1.3 certificates and handshakes handling for libp2p
//!
//! This module handles a verification of a client/server certificate chain
//! and signatures allegedly by the given certificates.
use rustls::{
internal::msgs::handshake::DigitallySignedStruct,
Certificate, ClientCertVerified, DistinguishedNames,
HandshakeSignatureValid, RootCertStore, ServerCertVerified,
SignatureScheme, TLSError,
};

/// Implementation of the `rustls` certificate verification traits for libp2p.
///
/// Only TLS 1.3 is supported. TLS 1.2 should be disabled in the configuration of `rustls`.
pub struct Libp2pCertificateVerifier;

impl Libp2pCertificateVerifier {
/// Return the list of SignatureSchemes that this verifier will handle,
/// in `verify_tls12_signature` and `verify_tls13_signature` calls.
///
/// This should be in priority order, with the most preferred first.
pub fn verification_schemes() -> Vec<SignatureScheme> {
vec![
// TODO SignatureScheme::ECDSA_NISTP521_SHA512 is not supported by `ring` yet
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::ECDSA_NISTP256_SHA256,
// TODO SignatureScheme::ED448 is not supported by `ring` yet
SignatureScheme::ED25519,

// In particular, RSA SHOULD NOT be used unless
// no elliptic curve algorithms are supported.
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA256,
]
}
}

impl rustls::ServerCertVerifier for Libp2pCertificateVerifier {
fn verify_server_cert(
&self,
_roots: &RootCertStore,
presented_certs: &[Certificate],
_dns_name: webpki::DNSNameRef<'_>,
_ocsp_response: &[u8],
) -> Result<ServerCertVerified, TLSError> {
verify_certs(presented_certs).map(|_| ServerCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &Certificate,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
// The libp2p handshake uses TLS 1.3 (and higher).
// Endpoints MUST NOT negotiate lower TLS versions.
Err(TLSError::PeerIncompatibleError("Only TLS 1.3 certificates are supported".to_string()))
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &Certificate,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
verify_tls13_signature(cert, dss.scheme, message, dss.sig.0.as_ref())
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
Self::verification_schemes()
}
}

impl rustls::ClientCertVerifier for Libp2pCertificateVerifier {
fn offer_client_auth(&self) -> bool {
true
}

fn client_auth_root_subjects(
&self,
_dns_name: Option<&webpki::DNSName>,
) -> Option<DistinguishedNames> {
Some(vec![])
}

fn verify_client_cert(
&self,
presented_certs: &[Certificate],
_dns_name: Option<&webpki::DNSName>,
) -> Result<ClientCertVerified, TLSError> {
verify_certs(presented_certs).map(|_| ClientCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &Certificate,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
// The libp2p handshake uses TLS 1.3 (and higher).
// Endpoints MUST NOT negotiate lower TLS versions.
Err(TLSError::PeerIncompatibleError("Only TLS 1.3 certificates are supported".to_string()))
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &Certificate,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
verify_tls13_signature(cert, dss.scheme, message, dss.sig.0.as_ref())
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
Self::verification_schemes()
}
}

/// When receiving the certificate chain, an endpoint
/// MUST check these conditions and abort the connection attempt if
/// (a) the presented certificate is not yet valid, OR
/// (b) if it is expired.
/// Endpoints MUST abort the connection attempt if more than one certificate is received,
/// or if the certificate’s self-signature is not valid.
fn verify_certs(presented_certs: &[Certificate]) -> Result<(), TLSError> {
if presented_certs.len() != 1 {
return Err(TLSError::NoCertificatesPresented);
}
let certificate = crate::certificate::parse_certificate(presented_certs[0].as_ref())
.map_err(|_| {
// There are multiple reasons for it:
// - Invalid DER
// - Incomplete DER
// - DuplicateExtensions
// - InvalidExtensions
// - and so on
// But there is no reason to distinguish them at the moment.
// Let's assume the ceritiface was badly constructed:
TLSError::WebPKIError(webpki::Error::BadDER)
})?;

if certificate.is_valid() {
Ok(())
} else {
// There are multiple reasons for it:
// - InvalidCertValidity
// - BadDER
// - UnknownIssuer
// - UnsupportedSignatureAlgorithm
// - UnsupportedSignatureAlgorithmForPublicKey
// - InvalidSignatureForPublicKey
// But there is no reason to distinguish them at the moment.
// Let's assume a worst-case scenario:
Err(TLSError::WebPKIError(webpki::Error::InvalidSignatureForPublicKey))
}
}

fn verify_tls13_signature(
cert: &Certificate,
signature_scheme: SignatureScheme,
message: &[u8],
signature: &[u8],
) -> Result<HandshakeSignatureValid, TLSError> {
let certificate = crate::certificate::parse_certificate(cert.as_ref())
.map_err(|_| {
// There are multiple reasons for it:
// - Invalid DER
// - Incomplete DER
// - DuplicateExtensions
// - InvalidExtensions
// - and so on
// But there is no reason to distinguish them at the moment.
// Let's assume the ceritiface was badly constructed:
TLSError::WebPKIError(webpki::Error::BadDER)
})?;
if certificate.verify_signature(signature_scheme, message, signature) {
Ok(HandshakeSignatureValid::assertion())
} else {
// There are multiple reasons for it:
// - UnsupportedSignatureAlgorithmForPublicKey
// - InvalidSignatureForPublicKey
// But there is no reason to distinguish them at the moment.
// Let's assume a worst-case scenario:
Err(TLSError::WebPKIError(webpki::Error::InvalidSignatureForPublicKey))
}
}

0 comments on commit f1a961f

Please sign in to comment.