use core::fmt;
use openssl::{
bn::BigNumContext,
ec::{EcGroup, EcKey, EcPoint},
hash::MessageDigest,
nid::Nid,
pkey::{Id, PKey, Public},
rsa::{Padding, Rsa},
sign::{RsaPssSaltlen, Verifier},
};
use rustls::pki_types::alg_id;
use rustls::{
SignatureScheme,
crypto::WebPkiSupportedAlgorithms,
pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm},
};
pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
all: &[
ECDSA_P256_SHA256,
ECDSA_P256_SHA384,
ECDSA_P384_SHA256,
ECDSA_P384_SHA384,
ECDSA_P521_SHA256,
ECDSA_P521_SHA384,
ECDSA_P521_SHA512,
#[cfg(not(feature = "fips"))]
ED25519,
RSA_PSS_SHA512,
RSA_PSS_SHA384,
RSA_PSS_SHA256,
RSA_PKCS1_SHA512,
RSA_PKCS1_SHA384,
RSA_PKCS1_SHA256,
],
mapping: &[
(
SignatureScheme::ECDSA_NISTP384_SHA384,
&[ECDSA_P384_SHA384, ECDSA_P256_SHA384, ECDSA_P521_SHA384],
),
(
SignatureScheme::ECDSA_NISTP256_SHA256,
&[ECDSA_P256_SHA256, ECDSA_P384_SHA256, ECDSA_P521_SHA256],
),
(SignatureScheme::ECDSA_NISTP521_SHA512, &[ECDSA_P521_SHA512]),
#[cfg(not(feature = "fips"))]
(SignatureScheme::ED25519, &[ED25519]),
(SignatureScheme::RSA_PSS_SHA512, &[RSA_PSS_SHA512]),
(SignatureScheme::RSA_PSS_SHA384, &[RSA_PSS_SHA384]),
(SignatureScheme::RSA_PSS_SHA256, &[RSA_PSS_SHA256]),
(SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]),
(SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]),
(SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]),
],
};
pub(crate) static RSA_PKCS1_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "RSA_PKCS1_SHA256",
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA256,
};
pub(crate) static RSA_PKCS1_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "RSA_PKCS1_SHA384",
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA384,
};
pub(crate) static RSA_PKCS1_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "RSA_PKCS1_SHA512",
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PKCS1_SHA512,
};
pub(crate) static RSA_PSS_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "RSA_PSS_SHA256",
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA256,
};
pub(crate) static RSA_PSS_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "RSA_PSS_SHA384",
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA384,
};
pub(crate) static RSA_PSS_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "RSA_PSS_SHA512",
public_key_alg_id: alg_id::RSA_ENCRYPTION,
signature_alg_id: alg_id::RSA_PSS_SHA512,
};
#[cfg(not(feature = "fips"))]
pub(crate) static ED25519: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ED25519",
public_key_alg_id: alg_id::ED25519,
signature_alg_id: alg_id::ED25519,
};
pub(crate) static ECDSA_P256_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P256_SHA256",
public_key_alg_id: alg_id::ECDSA_P256,
signature_alg_id: alg_id::ECDSA_SHA256,
};
pub(crate) static ECDSA_P256_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P256_SHA384",
public_key_alg_id: alg_id::ECDSA_P256,
signature_alg_id: alg_id::ECDSA_SHA384,
};
pub(crate) static ECDSA_P384_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P384_SHA256",
public_key_alg_id: alg_id::ECDSA_P384,
signature_alg_id: alg_id::ECDSA_SHA256,
};
pub(crate) static ECDSA_P384_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P384_SHA384",
public_key_alg_id: alg_id::ECDSA_P384,
signature_alg_id: alg_id::ECDSA_SHA384,
};
pub(crate) static ECDSA_P521_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P521_SHA256",
public_key_alg_id: alg_id::ECDSA_P521,
signature_alg_id: alg_id::ECDSA_SHA256,
};
pub(crate) static ECDSA_P521_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P521_SHA384",
public_key_alg_id: alg_id::ECDSA_P521,
signature_alg_id: alg_id::ECDSA_SHA384,
};
pub(crate) static ECDSA_P521_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm {
display_name: "ECDSA_P521_SHA512",
public_key_alg_id: alg_id::ECDSA_P521,
signature_alg_id: alg_id::ECDSA_SHA512,
};
struct OpenSslAlgorithm {
display_name: &'static str,
public_key_alg_id: AlgorithmIdentifier,
signature_alg_id: AlgorithmIdentifier,
}
impl fmt::Debug for OpenSslAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"rustls_openssl Signature Verification Algorithm: {}",
self.display_name
)
}
}
fn ecdsa_public_key(curve_name: Nid, public_key: &[u8]) -> Result<PKey<Public>, InvalidSignature> {
EcGroup::from_curve_name(curve_name)
.and_then(|group| {
let mut ctx = BigNumContext::new()?;
let point = EcPoint::from_bytes(&group, public_key, &mut ctx)?;
let key = EcKey::from_public_key(&group, &point)?;
key.try_into()
})
.map_err(|_| InvalidSignature)
}
impl OpenSslAlgorithm {
fn public_key(&self, public_key: &[u8]) -> Result<PKey<Public>, InvalidSignature> {
match self.public_key_alg_id {
alg_id::RSA_ENCRYPTION => Rsa::public_key_from_der_pkcs1(public_key)
.and_then(std::convert::TryInto::try_into)
.map_err(|_| InvalidSignature),
alg_id::ECDSA_P521 => ecdsa_public_key(Nid::SECP521R1, public_key),
alg_id::ECDSA_P384 => ecdsa_public_key(Nid::SECP384R1, public_key),
alg_id::ECDSA_P256 => ecdsa_public_key(Nid::X9_62_PRIME256V1, public_key),
alg_id::ED25519 => PKey::public_key_from_raw_bytes(public_key, Id::ED25519)
.map_err(|_| InvalidSignature),
_ => Err(InvalidSignature),
}
}
fn message_digest(&self) -> Option<MessageDigest> {
match self.signature_alg_id {
alg_id::RSA_PKCS1_SHA256 | alg_id::ECDSA_SHA256 | alg_id::RSA_PSS_SHA256 => {
Some(MessageDigest::sha256())
}
alg_id::RSA_PKCS1_SHA384 | alg_id::ECDSA_SHA384 | alg_id::RSA_PSS_SHA384 => {
Some(MessageDigest::sha384())
}
alg_id::RSA_PKCS1_SHA512 | alg_id::ECDSA_SHA512 | alg_id::RSA_PSS_SHA512 => {
Some(MessageDigest::sha512())
}
_ => None,
}
}
fn mgf1(&self) -> Option<MessageDigest> {
match self.signature_alg_id {
alg_id::RSA_PSS_SHA256 => Some(MessageDigest::sha256()),
alg_id::RSA_PSS_SHA384 => Some(MessageDigest::sha384()),
alg_id::RSA_PSS_SHA512 => Some(MessageDigest::sha512()),
_ => None,
}
}
fn pss_salt_len(&self) -> Option<RsaPssSaltlen> {
match self.signature_alg_id {
alg_id::RSA_PSS_SHA256 | alg_id::RSA_PSS_SHA384 | alg_id::RSA_PSS_SHA512 => {
Some(RsaPssSaltlen::DIGEST_LENGTH)
}
_ => None,
}
}
fn rsa_padding(&self) -> Option<Padding> {
match self.signature_alg_id {
alg_id::RSA_PSS_SHA512 | alg_id::RSA_PSS_SHA384 | alg_id::RSA_PSS_SHA256 => {
Some(Padding::PKCS1_PSS)
}
alg_id::RSA_PKCS1_SHA512 | alg_id::RSA_PKCS1_SHA384 | alg_id::RSA_PKCS1_SHA256 => {
Some(Padding::PKCS1)
}
_ => None,
}
}
}
impl SignatureVerificationAlgorithm for OpenSslAlgorithm {
fn public_key_alg_id(&self) -> AlgorithmIdentifier {
self.public_key_alg_id
}
fn signature_alg_id(&self) -> AlgorithmIdentifier {
self.signature_alg_id
}
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignature> {
if matches!(
self.public_key_alg_id,
alg_id::ECDSA_P256 | alg_id::ECDSA_P384 | alg_id::ECDSA_P521
) {
match public_key.first() {
Some(0x02..=0x04) => {}
_ => {
return Err(InvalidSignature);
}
};
}
let pkey = self.public_key(public_key)?;
if let Some(message_digest) = self.message_digest() {
Verifier::new(message_digest, &pkey).and_then(|mut verifier| {
if let Some(padding) = self.rsa_padding() {
verifier.set_rsa_padding(padding)?;
}
if let Some(mgf1_md) = self.mgf1() {
verifier.set_rsa_mgf1_md(mgf1_md)?;
}
if let Some(salt_len) = self.pss_salt_len() {
verifier.set_rsa_pss_saltlen(salt_len)?;
}
verifier.update(message)?;
verifier.verify(signature)
})
} else {
Verifier::new_without_digest(&pkey)
.and_then(|mut verifier| verifier.verify_oneshot(signature, message))
}
.map_err(|e| {
std::dbg!(e);
InvalidSignature
})
.and_then(|valid| if valid { Ok(()) } else { Err(InvalidSignature) })
}
fn fips(&self) -> bool {
crate::fips::enabled()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_open_ssl_algorithm_debug() {
assert_eq!(
format!("{:?}", ECDSA_P256_SHA256),
"rustls_openssl Signature Verification Algorithm: ECDSA_P256_SHA256"
);
assert_eq!(
format!("{:?}", RSA_PSS_SHA256),
"rustls_openssl Signature Verification Algorithm: RSA_PSS_SHA256"
);
}
}