use std::fmt;
use der::Decode;
use der::oid::ObjectIdentifier;
use rsa::pkcs1::DecodeRsaPrivateKey;
use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePublicKey};
use rsa::traits::PublicKeyParts;
use rsa::{RsaPrivateKey, RsaPublicKey, pkcs1v15, pss};
use sha1::Sha1;
use sha2::{Digest, Sha256, Sha384, Sha512};
use signature::hazmat::{PrehashSigner, PrehashVerifier};
use signature::{SignatureEncoding, Signer, Verifier};
use zeroize::Zeroizing;
use super::{DigestAlgorithm, KeyError};
use crate::digest::ContentDigest;
const RSA_ENCRYPTION: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
const EC_PUBLIC_KEY: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
const SECP256R1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
const SECP384R1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.132.0.34");
pub const MIN_RSA_SIGNING_BITS: u32 = 2048;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum KeyKind {
Rsa { bits: u32 },
EcP256,
EcP384,
}
impl fmt::Display for KeyKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rsa { bits } => write!(f, "{bits}-bit RSA"),
Self::EcP256 => f.write_str("P-256 EC"),
Self::EcP384 => f.write_str("P-384 EC"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum SignatureAlgorithm {
RsaPssSha256,
RsaPssSha512,
RsaPkcs1Sha256,
RsaPkcs1Sha512,
EcdsaSha256,
EcdsaSha512,
DsaSha256,
VerityRsaPkcs1Sha256,
VerityEcdsaSha256,
VerityDsaSha256,
}
impl SignatureAlgorithm {
const ALL: [(Self, u32); 10] = [
(Self::RsaPssSha256, 0x0101),
(Self::RsaPssSha512, 0x0102),
(Self::RsaPkcs1Sha256, 0x0103),
(Self::RsaPkcs1Sha512, 0x0104),
(Self::EcdsaSha256, 0x0201),
(Self::EcdsaSha512, 0x0202),
(Self::DsaSha256, 0x0301),
(Self::VerityRsaPkcs1Sha256, 0x0421),
(Self::VerityEcdsaSha256, 0x0423),
(Self::VerityDsaSha256, 0x0425),
];
pub fn id(self) -> u32 {
Self::ALL.iter().find(|(a, _)| *a == self).map(|(_, id)| *id).expect("every algorithm has an ID")
}
pub fn from_id(id: u32) -> Option<Self> {
Self::ALL.iter().find(|(_, i)| *i == id).map(|(a, _)| *a)
}
pub(crate) fn content_digest(self) -> Option<ContentDigest> {
match self {
Self::RsaPssSha256 | Self::RsaPkcs1Sha256 | Self::EcdsaSha256 | Self::DsaSha256 => {
Some(ContentDigest::ChunkedSha256)
}
Self::RsaPssSha512 | Self::RsaPkcs1Sha512 | Self::EcdsaSha512 => Some(ContentDigest::ChunkedSha512),
Self::VerityRsaPkcs1Sha256 | Self::VerityEcdsaSha256 | Self::VerityDsaSha256 => None,
}
}
pub(crate) fn strength(self) -> u8 {
match self {
Self::RsaPssSha512 | Self::RsaPkcs1Sha512 | Self::EcdsaSha512 => 3,
Self::VerityRsaPkcs1Sha256 | Self::VerityEcdsaSha256 | Self::VerityDsaSha256 => 2,
Self::RsaPssSha256 | Self::RsaPkcs1Sha256 | Self::EcdsaSha256 | Self::DsaSha256 => 1,
}
}
pub fn is_verifiable(self) -> bool {
!matches!(self, Self::DsaSha256 | Self::VerityRsaPkcs1Sha256 | Self::VerityEcdsaSha256 | Self::VerityDsaSha256)
}
}
impl fmt::Display for SignatureAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?} ({:#06x})", self.id())
}
}
#[derive(Clone)]
enum Private {
Rsa(RsaPrivateKey),
P256(p256::ecdsa::SigningKey),
P384(p384::ecdsa::SigningKey),
}
#[derive(Clone)]
pub struct PrivateKey(Private);
impl fmt::Debug for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PrivateKey({})", self.kind())
}
}
impl PrivateKey {
pub fn from_pkcs8_der(der: &[u8]) -> Result<Self, KeyError> {
let info = pkcs8::PrivateKeyInfoRef::from_der(der).map_err(pkcs8::Error::from)?;
let algorithm = info.algorithm.oid;
let private = match algorithm {
RSA_ENCRYPTION => Private::Rsa(RsaPrivateKey::from_pkcs8_der(der)?),
EC_PUBLIC_KEY => match info.algorithm.parameters_oid().map_err(pkcs8::Error::from)? {
SECP256R1 => Private::P256(p256::ecdsa::SigningKey::from_pkcs8_der(der)?),
SECP384R1 => Private::P384(p384::ecdsa::SigningKey::from_pkcs8_der(der)?),
curve => return Err(KeyError::UnsupportedCurve { oid: curve }),
},
oid => return Err(KeyError::UnsupportedAlgorithm { oid }),
};
Self::checked(private)
}
pub fn from_pkcs1_der(der: &[u8]) -> Result<Self, KeyError> {
Self::checked(Private::Rsa(RsaPrivateKey::from_pkcs1_der(der)?))
}
pub fn from_sec1_der(der: &[u8]) -> Result<Self, KeyError> {
if let Ok(key) = p256::SecretKey::from_sec1_der(der) {
return Ok(Self(Private::P256(key.into())));
}
match p384::SecretKey::from_sec1_der(der) {
Ok(key) => Ok(Self(Private::P384(key.into()))),
Err(_) => Err(KeyError::InvalidEcKey),
}
}
fn checked(private: Private) -> Result<Self, KeyError> {
if let Private::Rsa(key) = &private {
key.validate()?;
let bits = key.n().bits_vartime();
if bits < MIN_RSA_SIGNING_BITS {
return Err(KeyError::RsaKeyTooSmall { bits, min: MIN_RSA_SIGNING_BITS });
}
}
Ok(Self(private))
}
pub fn kind(&self) -> KeyKind {
self.public_key().kind()
}
pub fn to_pkcs8_der(&self) -> Result<Zeroizing<Vec<u8>>, KeyError> {
use pkcs8::EncodePrivateKey;
let document = match &self.0 {
Private::Rsa(key) => key.to_pkcs8_der()?,
Private::P256(key) => key.to_pkcs8_der()?,
Private::P384(key) => key.to_pkcs8_der()?,
};
Ok(Zeroizing::new(document.as_bytes().to_vec()))
}
pub fn public_key(&self) -> PublicKey {
PublicKey(match &self.0 {
Private::Rsa(key) => Public::Rsa(key.to_public_key()),
Private::P256(key) => Public::P256(*key.verifying_key()),
Private::P384(key) => Public::P384(*key.verifying_key()),
})
}
pub fn signature_algorithm(&self) -> SignatureAlgorithm {
match self.kind() {
KeyKind::Rsa { bits } if bits <= 3072 => SignatureAlgorithm::RsaPkcs1Sha256,
KeyKind::Rsa { .. } => SignatureAlgorithm::RsaPkcs1Sha512,
KeyKind::EcP256 => SignatureAlgorithm::EcdsaSha256,
KeyKind::EcP384 => SignatureAlgorithm::EcdsaSha512,
}
}
pub(crate) fn sign(&self, algorithm: SignatureAlgorithm, message: &[u8]) -> Result<Vec<u8>, KeyError> {
let mismatch = || KeyError::AlgorithmMismatch { algorithm, key: self.kind() };
match (&self.0, algorithm) {
(Private::Rsa(key), SignatureAlgorithm::RsaPkcs1Sha256) => rsa_pkcs1::<Sha256>(key, message),
(Private::Rsa(key), SignatureAlgorithm::RsaPkcs1Sha512) => rsa_pkcs1::<Sha512>(key, message),
(Private::P256(key), SignatureAlgorithm::EcdsaSha256) => sign_p256::<Sha256>(key, message),
(Private::P256(key), SignatureAlgorithm::EcdsaSha512) => sign_p256::<Sha512>(key, message),
(Private::P384(key), SignatureAlgorithm::EcdsaSha256) => sign_p384::<Sha256>(key, message),
(Private::P384(key), SignatureAlgorithm::EcdsaSha512) => sign_p384::<Sha512>(key, message),
_ => Err(mismatch()),
}
}
pub(crate) fn sign_jar(&self, digest: DigestAlgorithm, message: &[u8]) -> Result<Vec<u8>, KeyError> {
match digest {
DigestAlgorithm::Sha1 => self.sign_jar_with::<Sha1>(message),
DigestAlgorithm::Sha256 => self.sign_jar_with::<Sha256>(message),
DigestAlgorithm::Sha384 => self.sign_jar_with::<Sha384>(message),
DigestAlgorithm::Sha512 => self.sign_jar_with::<Sha512>(message),
}
}
fn sign_jar_with<D: Digest + der::oid::AssociatedOid>(&self, message: &[u8]) -> Result<Vec<u8>, KeyError> {
match &self.0 {
Private::Rsa(key) => rsa_pkcs1::<D>(key, message),
Private::P256(key) => sign_p256::<D>(key, message),
Private::P384(key) => sign_p384::<D>(key, message),
}
}
}
fn rsa_pkcs1<D>(key: &RsaPrivateKey, message: &[u8]) -> Result<Vec<u8>, KeyError>
where
D: Digest + der::oid::AssociatedOid,
{
let signer = pkcs1v15::SigningKey::<D>::new(key.clone());
Ok(signer.try_sign(message).map_err(KeyError::Signing)?.to_vec())
}
macro_rules! ecdsa_curve {
($curve:ident, $sign:ident, $verify:ident) => {
fn $sign<D: Digest>(key: &$curve::ecdsa::SigningKey, message: &[u8]) -> Result<Vec<u8>, KeyError> {
let signature: $curve::ecdsa::Signature =
key.sign_prehash(&D::digest(message)).map_err(KeyError::Signing)?;
Ok(signature.to_der().to_vec())
}
fn $verify<D: Digest>(
key: &$curve::ecdsa::VerifyingKey,
message: &[u8],
signature: &[u8],
) -> Result<(), SignatureError> {
let signature = $curve::ecdsa::Signature::from_der(signature).map_err(|_| SignatureError::Malformed)?;
key.verify_prehash(&D::digest(message), &signature).map_err(|_| SignatureError::Invalid)
}
};
}
ecdsa_curve!(p256, sign_p256, verify_p256);
ecdsa_curve!(p384, sign_p384, verify_p384);
#[derive(Clone, Debug, Eq, PartialEq)]
enum Public {
Rsa(RsaPublicKey),
P256(p256::ecdsa::VerifyingKey),
P384(p384::ecdsa::VerifyingKey),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey(Public);
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum SignatureError {
#[error("{algorithm} cannot be verified with a {key} key")]
AlgorithmMismatch { algorithm: SignatureAlgorithm, key: KeyKind },
#[error("{algorithm} is not supported for verification")]
Unsupported { algorithm: SignatureAlgorithm },
#[error("signature is malformed")]
Malformed,
#[error("signature does not match")]
Invalid,
}
impl PublicKey {
pub fn from_spki_der(der: &[u8]) -> Result<Self, KeyError> {
let info = spki::SubjectPublicKeyInfoRef::from_der(der).map_err(spki::Error::from)?;
Ok(Self(match info.algorithm.oid {
RSA_ENCRYPTION => Public::Rsa(RsaPublicKey::from_public_key_der(der)?),
EC_PUBLIC_KEY => match info.algorithm.parameters_oid()? {
SECP256R1 => Public::P256(p256::ecdsa::VerifyingKey::from_public_key_der(der)?),
SECP384R1 => Public::P384(p384::ecdsa::VerifyingKey::from_public_key_der(der)?),
curve => return Err(KeyError::UnsupportedCurve { oid: curve }),
},
oid => return Err(KeyError::UnsupportedAlgorithm { oid }),
}))
}
pub fn to_spki_der(&self) -> Result<Vec<u8>, KeyError> {
let document = match &self.0 {
Public::Rsa(key) => key.to_public_key_der()?,
Public::P256(key) => key.to_public_key_der()?,
Public::P384(key) => key.to_public_key_der()?,
};
Ok(document.into_vec())
}
pub fn kind(&self) -> KeyKind {
match &self.0 {
Public::Rsa(key) => KeyKind::Rsa { bits: key.n().bits_vartime() },
Public::P256(_) => KeyKind::EcP256,
Public::P384(_) => KeyKind::EcP384,
}
}
pub fn verify(
&self,
algorithm: SignatureAlgorithm,
message: &[u8],
signature: &[u8],
) -> Result<(), SignatureError> {
use SignatureAlgorithm as A;
let mismatch = SignatureError::AlgorithmMismatch { algorithm, key: self.kind() };
match (&self.0, algorithm) {
(Public::Rsa(key), A::RsaPkcs1Sha256) => verify_rsa_pkcs1::<Sha256>(key, message, signature),
(Public::Rsa(key), A::RsaPkcs1Sha512) => verify_rsa_pkcs1::<Sha512>(key, message, signature),
(Public::Rsa(key), A::RsaPssSha256) => verify_rsa_pss::<Sha256>(key, message, signature),
(Public::Rsa(key), A::RsaPssSha512) => verify_rsa_pss::<Sha512>(key, message, signature),
(Public::P256(key), A::EcdsaSha256) => verify_p256::<Sha256>(key, message, signature),
(Public::P256(key), A::EcdsaSha512) => verify_p256::<Sha512>(key, message, signature),
(Public::P384(key), A::EcdsaSha256) => verify_p384::<Sha256>(key, message, signature),
(Public::P384(key), A::EcdsaSha512) => verify_p384::<Sha512>(key, message, signature),
(_, algorithm) if !algorithm.is_verifiable() => Err(SignatureError::Unsupported { algorithm }),
_ => Err(mismatch),
}
}
pub(crate) fn verify_jar(
&self,
digest: DigestAlgorithm,
message: &[u8],
signature: &[u8],
) -> Result<(), SignatureError> {
match digest {
DigestAlgorithm::Sha1 => self.verify_jar_with::<Sha1>(message, signature),
DigestAlgorithm::Sha256 => self.verify_jar_with::<Sha256>(message, signature),
DigestAlgorithm::Sha384 => self.verify_jar_with::<Sha384>(message, signature),
DigestAlgorithm::Sha512 => self.verify_jar_with::<Sha512>(message, signature),
}
}
fn verify_jar_with<D: Digest + der::oid::AssociatedOid>(
&self,
message: &[u8],
signature: &[u8],
) -> Result<(), SignatureError> {
match &self.0 {
Public::Rsa(key) => verify_rsa_pkcs1::<D>(key, message, signature),
Public::P256(key) => verify_p256::<D>(key, message, signature),
Public::P384(key) => verify_p384::<D>(key, message, signature),
}
}
}
fn verify_rsa_pkcs1<D>(key: &RsaPublicKey, message: &[u8], signature: &[u8]) -> Result<(), SignatureError>
where
D: Digest + der::oid::AssociatedOid,
{
let signature = pkcs1v15::Signature::try_from(signature).map_err(|_| SignatureError::Malformed)?;
pkcs1v15::VerifyingKey::<D>::new(key.clone()).verify(message, &signature).map_err(|_| SignatureError::Invalid)
}
fn verify_rsa_pss<D>(key: &RsaPublicKey, message: &[u8], signature: &[u8]) -> Result<(), SignatureError>
where
D: Digest + sha2::digest::FixedOutputReset,
{
let signature = pss::Signature::try_from(signature).map_err(|_| SignatureError::Malformed)?;
pss::VerifyingKey::<D>::new(key.clone()).verify(message, &signature).map_err(|_| SignatureError::Invalid)
}