Skip to content

Commit

Permalink
Turn webpki::SignatureAlgorithm into a trait
Browse files Browse the repository at this point in the history
Rename it to `SignatureVerificationAlgorithm`.

`RingAlgorithm` exists as the previous type, and implements
the trait.

This is a breaking change:

- top level functions now need to pass a &[&dyn SignatureVerificationAlgorithm]
- objects like `ECDSA_P256_SHA256` are now a `&dyn SignatureVerificationAlgorithm`
  so callers don't see the internal `RingAlgorithm` type.
  • Loading branch information
ctz committed Jul 28, 2023
1 parent d4c38f8 commit deca338
Show file tree
Hide file tree
Showing 14 changed files with 258 additions and 206 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ license = "ISC"
name = "rustls-webpki"
readme = "README.md"
repository = "https://github.com/rustls/webpki"
version = "0.101.2"
version = "0.102.0-alpha.0"

include = [
"Cargo.toml",
Expand Down
10 changes: 5 additions & 5 deletions src/crl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::cert::lenient_certificate_serial_number;
use crate::der::{self, Tag, CONSTRUCTED, CONTEXT_SPECIFIC};
use crate::signed_data::{self, SignedData};
use crate::x509::{remember_extension, set_extension_once, DistributionPointName, Extension};
use crate::{Error, SignatureAlgorithm, Time};
use crate::{Error, SignatureVerificationAlgorithm, Time};

#[cfg(feature = "alloc")]
use std::collections::HashMap;
Expand All @@ -40,10 +40,10 @@ pub trait CertRevocationList: Sealed {
fn find_serial(&self, serial: &[u8]) -> Result<Option<BorrowedRevokedCert>, Error>;

/// Verify the CRL signature using the issuer's subject public key information (SPKI)
/// and a list of supported signature algorithms.
/// and a list of supported signature verification algorithms.
fn verify_signature(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
supported_sig_algs: &[&dyn SignatureVerificationAlgorithm],
issuer_spki: &[u8],
) -> Result<(), Error>;
}
Expand Down Expand Up @@ -91,7 +91,7 @@ impl CertRevocationList for OwnedCertRevocationList {

fn verify_signature(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
supported_sig_algs: &[&dyn SignatureVerificationAlgorithm],
issuer_spki: &[u8],
) -> Result<(), Error> {
signed_data::verify_signed_data(
Expand Down Expand Up @@ -346,7 +346,7 @@ impl CertRevocationList for BorrowedCertRevocationList<'_> {

fn verify_signature(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
supported_sig_algs: &[&dyn SignatureVerificationAlgorithm],
issuer_spki: &[u8],
) -> Result<(), Error> {
signed_data::verify_signed_data(
Expand Down
10 changes: 5 additions & 5 deletions src/end_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use crate::subject_name::GeneralDnsNameRef;
use crate::{
cert, signed_data, subject_name, verify_cert, CertRevocationList, Error, KeyUsage,
SignatureAlgorithm, SubjectNameRef, Time, TrustAnchor,
SignatureVerificationAlgorithm, SubjectNameRef, Time, TrustAnchor,
};

/// An end-entity certificate.
Expand Down Expand Up @@ -91,7 +91,7 @@ impl<'a> EndEntityCert<'a> {
/// the certificate against.
pub fn verify_for_usage(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
supported_sig_algs: &[&dyn SignatureVerificationAlgorithm],
trust_anchors: &[TrustAnchor],
intermediate_certs: &[&[u8]],
time: Time,
Expand Down Expand Up @@ -130,18 +130,18 @@ impl<'a> EndEntityCert<'a> {
/// `DigitallySigned.signature` and `signature_alg` corresponds to TLS's
/// `DigitallySigned.algorithm` of TLS type `SignatureAndHashAlgorithm`. In
/// TLS 1.2 a single `SignatureAndHashAlgorithm` may map to multiple
/// `SignatureAlgorithm`s. For example, a TLS 1.2
/// `SignatureVerificationAlgorithm`s. For example, a TLS 1.2
/// `SignatureAndHashAlgorithm` of (ECDSA, SHA-256) may map to any or all
/// of {`ECDSA_P256_SHA256`, `ECDSA_P384_SHA256`}, depending on how the TLS
/// implementation is configured.
///
/// For current TLS 1.3 drafts, `signature_alg` corresponds to TLS's
/// `algorithm` fields of type `SignatureScheme`. There is (currently) a
/// one-to-one correspondence between TLS 1.3's `SignatureScheme` and
/// `SignatureAlgorithm`.
/// `SignatureVerificationAlgorithm`.
pub fn verify_signature(
&self,
signature_alg: &SignatureAlgorithm,
signature_alg: &dyn SignatureVerificationAlgorithm,
msg: &[u8],
signature: &[u8],
) -> Result<(), Error> {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ pub use {
end_entity::EndEntityCert,
error::Error,
signed_data::{
alg_id, SignatureAlgorithm, ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256,
ECDSA_P384_SHA384, ED25519,
alg_id, InvalidSignature, SignatureVerificationAlgorithm, ECDSA_P256_SHA256,
ECDSA_P256_SHA384, ECDSA_P384_SHA256, ECDSA_P384_SHA384, ED25519,
},
subject_name::{
AddrParseError, DnsNameRef, InvalidDnsNameError, InvalidSubjectNameError, IpAddrRef,
Expand Down
146 changes: 99 additions & 47 deletions src/signed_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl<'a> SignedData<'a> {
/// but generally more common algorithms should go first, as it is scanned
/// linearly for matches.
pub(crate) fn verify_signed_data(
supported_algorithms: &[&SignatureAlgorithm],
supported_algorithms: &[&dyn SignatureVerificationAlgorithm],
spki_value: untrusted::Input,
signed_data: &SignedData,
) -> Result<(), Error> {
Expand All @@ -174,11 +174,11 @@ pub(crate) fn verify_signed_data(
//
let mut found_signature_alg_match = false;
for supported_alg in supported_algorithms.iter().filter(|alg| {
alg.signature_alg_id
alg.signature_alg_id()
.matches_algorithm_id_value(signed_data.algorithm)
}) {
match verify_signature(
supported_alg,
*supported_alg,
spki_value,
signed_data.data,
signed_data.signature,
Expand All @@ -201,14 +201,14 @@ pub(crate) fn verify_signed_data(
}

pub(crate) fn verify_signature(
signature_alg: &SignatureAlgorithm,
signature_alg: &dyn SignatureVerificationAlgorithm,
spki_value: untrusted::Input,
msg: untrusted::Input,
signature: untrusted::Input,
) -> Result<(), Error> {
let spki = SubjectPublicKeyInfo::from_der(spki_value)?;
if !signature_alg
.public_key_alg_id
.public_key_alg_id()
.matches_algorithm_id_value(spki.algorithm_id_value)
{
return Err(Error::UnsupportedSignatureAlgorithmForPublicKey);
Expand All @@ -231,7 +231,7 @@ struct SubjectPublicKeyInfo<'a> {
impl<'a> SubjectPublicKeyInfo<'a> {
// Parse the public key into an algorithm OID, an optional curve OID, and the
// key value. The caller needs to check whether these match the
// `PublicKeyAlgorithm` for the `SignatureAlgorithm` that is matched when
// `PublicKeyAlgorithm` for the `SignatureVerificationAlgorithm` that is matched when
// parsing the signature.
fn from_der(input: untrusted::Input<'a>) -> Result<Self, Error> {
input.read_all(Error::BadDer, |input| {
Expand All @@ -245,14 +245,63 @@ impl<'a> SubjectPublicKeyInfo<'a> {
}
}

/// A signature algorithm.
pub struct SignatureAlgorithm {
/// An abstract signature verification algorithm.
///
/// One of these is needed per supported pair of public key type (identified
/// with `public_key_alg_id()`) and `signatureAlgorithm` (identified with
/// `signature_alg_id()`). Note that both of these `AlgorithmIdentifier`s include
/// the parameters encoding, so separate `SignatureVerificationAlgorithm`s are needed
/// for each possible public key or signature parameters.
pub trait SignatureVerificationAlgorithm: Send + Sync {
/// Return the `AlgorithmIdentifier` that must be present on a `subjectPublicKeyInfo`
/// for this `SignatureVerificationAlgorithm` to be considered for verification.
fn public_key_alg_id(&self) -> alg_id::AlgorithmIdentifier;

/// Return the `AlgorithmIdentifier` that must be present as the `signatureAlgorithm`
/// on the data to be verified for this `SignatureVerificationAlgorithm` to be considered
/// for this `SignatureVerificationAlgorithm` to be considered.
fn signature_alg_id(&self) -> alg_id::AlgorithmIdentifier;

/// Verify a signature.
///
/// `public_key` is the `subjectPublicKey` value from a `SubjectPublicKeyInfo` encoding
/// and is untrusted.
///
/// `message` is the data over which the signature was allegedly computed.
/// It is not hashed; implementations of this trait function must do hashing
/// if that is required by the algorithm they implement.
///
/// `signature` is the signature allegedly over `message`.
///
/// Return `Ok(())` only if `signature` is a valid signature on `message`.
///
/// Return `Err(InvalidSignature)` if the signature is invalid, including if the `public_key`
/// encoding is invalid. There is no need or opportunity to produce errors
/// that are more specific than this.
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignature>;
}

/// A `SignatureVerificationAlgorithm` implemented using *ring*.
struct RingAlgorithm {
public_key_alg_id: alg_id::AlgorithmIdentifier,
signature_alg_id: alg_id::AlgorithmIdentifier,
verification_alg: &'static dyn signature::VerificationAlgorithm,
}

impl SignatureAlgorithm {
impl SignatureVerificationAlgorithm for RingAlgorithm {
fn public_key_alg_id(&self) -> alg_id::AlgorithmIdentifier {
self.public_key_alg_id
}

fn signature_alg_id(&self) -> alg_id::AlgorithmIdentifier {
self.signature_alg_id
}

fn verify_signature(
&self,
public_key: &[u8],
Expand All @@ -266,28 +315,28 @@ impl SignatureAlgorithm {
}

/// ECDSA signatures using the P-256 curve and SHA-256.
pub static ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm {
pub static ECDSA_P256_SHA256: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::ECDSA_P256,
signature_alg_id: alg_id::ECDSA_SHA256,
verification_alg: &signature::ECDSA_P256_SHA256_ASN1,
};

/// ECDSA signatures using the P-256 curve and SHA-384. Deprecated.
pub static ECDSA_P256_SHA384: SignatureAlgorithm = SignatureAlgorithm {
pub static ECDSA_P256_SHA384: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::ECDSA_P256,
signature_alg_id: alg_id::ECDSA_SHA384,
verification_alg: &signature::ECDSA_P256_SHA384_ASN1,
};

/// ECDSA signatures using the P-384 curve and SHA-256. Deprecated.
pub static ECDSA_P384_SHA256: SignatureAlgorithm = SignatureAlgorithm {
pub static ECDSA_P384_SHA256: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::ECDSA_P384,
signature_alg_id: alg_id::ECDSA_SHA256,
verification_alg: &signature::ECDSA_P384_SHA256_ASN1,
};

/// ECDSA signatures using the P-384 curve and SHA-384.
pub static ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm {
pub static ECDSA_P384_SHA384: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::ECDSA_P384,
signature_alg_id: alg_id::ECDSA_SHA384,
verification_alg: &signature::ECDSA_P384_SHA384_ASN1,
Expand All @@ -297,7 +346,7 @@ pub static ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm {
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_2048_8192_SHA256: SignatureAlgorithm = SignatureAlgorithm {
pub static RSA_PKCS1_2048_8192_SHA256: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA256,
verification_alg: &signature::RSA_PKCS1_2048_8192_SHA256,
Expand All @@ -307,7 +356,7 @@ pub static RSA_PKCS1_2048_8192_SHA256: SignatureAlgorithm = SignatureAlgorithm {
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_2048_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
pub static RSA_PKCS1_2048_8192_SHA384: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA384,
verification_alg: &signature::RSA_PKCS1_2048_8192_SHA384,
Expand All @@ -317,7 +366,7 @@ pub static RSA_PKCS1_2048_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_2048_8192_SHA512: SignatureAlgorithm = SignatureAlgorithm {
pub static RSA_PKCS1_2048_8192_SHA512: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA512,
verification_alg: &signature::RSA_PKCS1_2048_8192_SHA512,
Expand All @@ -327,7 +376,7 @@ pub static RSA_PKCS1_2048_8192_SHA512: SignatureAlgorithm = SignatureAlgorithm {
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_3072_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
pub static RSA_PKCS1_3072_8192_SHA384: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA384,
verification_alg: &signature::RSA_PKCS1_3072_8192_SHA384,
Expand All @@ -340,11 +389,12 @@ pub static RSA_PKCS1_3072_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA256,
verification_alg: &signature::RSA_PSS_2048_8192_SHA256,
};
pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: &dyn SignatureVerificationAlgorithm =
&RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA256,
verification_alg: &signature::RSA_PSS_2048_8192_SHA256,
};

/// RSA PSS signatures using SHA-384 for keys of 2048-8192 bits and of
/// type rsaEncryption; see [RFC 4055 Section 1.2].
Expand All @@ -353,11 +403,12 @@ pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: SignatureAlgorithm = SignatureAl
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA384,
verification_alg: &signature::RSA_PSS_2048_8192_SHA384,
};
pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: &dyn SignatureVerificationAlgorithm =
&RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA384,
verification_alg: &signature::RSA_PSS_2048_8192_SHA384,
};

/// RSA PSS signatures using SHA-512 for keys of 2048-8192 bits and of
/// type rsaEncryption; see [RFC 4055 Section 1.2].
Expand All @@ -366,22 +417,23 @@ pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: SignatureAlgorithm = SignatureAl
///
/// Requires the `alloc` feature.
#[cfg(feature = "alloc")]
pub static RSA_PSS_2048_8192_SHA512_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA512,
verification_alg: &signature::RSA_PSS_2048_8192_SHA512,
};
pub static RSA_PSS_2048_8192_SHA512_LEGACY_KEY: &dyn SignatureVerificationAlgorithm =
&RingAlgorithm {
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA512,
verification_alg: &signature::RSA_PSS_2048_8192_SHA512,
};

/// ED25519 signatures according to RFC 8410
pub static ED25519: SignatureAlgorithm = SignatureAlgorithm {
pub static ED25519: &dyn SignatureVerificationAlgorithm = &RingAlgorithm {
public_key_alg_id: alg_id::ED25519,
signature_alg_id: alg_id::ED25519,
verification_alg: &signature::ED25519,
};

/// A detail-less error when a signature is not valid.
#[derive(Debug, Copy, Clone)]
struct InvalidSignature;
pub struct InvalidSignature;

/// Encodings of the PKIX AlgorithmIdentifier type:
///
Expand Down Expand Up @@ -881,28 +933,28 @@ mod tests {
general_purpose::STANDARD.decode(&base64).unwrap()
}

static SUPPORTED_ALGORITHMS_IN_TESTS: &[&signed_data::SignatureAlgorithm] = &[
static SUPPORTED_ALGORITHMS_IN_TESTS: &[&dyn signed_data::SignatureVerificationAlgorithm] = &[
// Reasonable algorithms.
&signed_data::ECDSA_P256_SHA256,
&signed_data::ECDSA_P384_SHA384,
&signed_data::ED25519,
signed_data::ECDSA_P256_SHA256,
signed_data::ECDSA_P384_SHA384,
signed_data::ED25519,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_2048_8192_SHA256,
signed_data::RSA_PKCS1_2048_8192_SHA256,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_2048_8192_SHA384,
signed_data::RSA_PKCS1_2048_8192_SHA384,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_2048_8192_SHA512,
signed_data::RSA_PKCS1_2048_8192_SHA512,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_3072_8192_SHA384,
signed_data::RSA_PKCS1_3072_8192_SHA384,
#[cfg(feature = "alloc")]
&signed_data::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
signed_data::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
#[cfg(feature = "alloc")]
&signed_data::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
signed_data::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
#[cfg(feature = "alloc")]
&signed_data::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
signed_data::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
// Algorithms deprecated because they are annoying (P-521) or because
// they are nonsensical combinations.
&signed_data::ECDSA_P256_SHA384, // Truncates digest.
&signed_data::ECDSA_P384_SHA256, // Digest is unnecessarily short.
signed_data::ECDSA_P256_SHA384, // Truncates digest.
signed_data::ECDSA_P384_SHA256, // Digest is unnecessarily short.
];
}
Loading

0 comments on commit deca338

Please sign in to comment.