use pkcs1::{RsaPublicKey as Pkcs1RsaPublicKey, der::Decode};
use rsa::{
BigUint, RsaPublicKey,
pss::{Signature, VerifyingKey},
sha2::{Sha256, Sha384, Sha512},
signature::Verifier,
};
use spki::SubjectPublicKeyInfoRef;
use crate::{RawSignatureValidationError, RawSignatureValidator};
#[non_exhaustive]
pub enum RsaValidator {
Ps256,
Ps384,
Ps512,
}
impl RawSignatureValidator for RsaValidator {
fn validate(
&self,
sig: &[u8],
data: &[u8],
public_key: &[u8],
) -> Result<(), RawSignatureValidationError> {
let signature: Signature = sig
.try_into()
.map_err(|_| RawSignatureValidationError::InvalidSignature)?;
let spki = SubjectPublicKeyInfoRef::try_from(public_key)
.map_err(|_| RawSignatureValidationError::InvalidPublicKey)?;
let rsa_pub = Pkcs1RsaPublicKey::from_der(spki.subject_public_key.raw_bytes())
.map_err(|_| RawSignatureValidationError::InvalidPublicKey)?;
let modulus = BigUint::from_bytes_be(rsa_pub.modulus.as_bytes());
let exp = BigUint::from_bytes_be(rsa_pub.public_exponent.as_bytes());
let public_key = RsaPublicKey::new(modulus, exp)
.map_err(|_| RawSignatureValidationError::InvalidPublicKey)?;
let result = match self {
Self::Ps256 => {
let vk = VerifyingKey::<Sha256>::new(public_key);
vk.verify(data, &signature)
}
Self::Ps384 => {
let vk = VerifyingKey::<Sha384>::new(public_key);
vk.verify(data, &signature)
}
Self::Ps512 => {
let vk = VerifyingKey::<Sha512>::new(public_key);
vk.verify(data, &signature)
}
};
result.map_err(|_| RawSignatureValidationError::SignatureMismatch)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
use crate::RawSignatureValidator;
const RSA_SPKI_EMPTY_SEQUENCE: &[u8] = &[
0x30, 0x14, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x03, 0x00, 0x30, 0x00, ];
const RSA_SPKI_SINGLE_ELEMENT_SEQUENCE: &[u8] = &[
0x30, 0x17, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x06, 0x00, 0x30, 0x03, 0x02, 0x01, 0x01, ];
const DUMMY_SIG_512: &[u8] = &[0u8; 512];
const SAMPLE_DATA: &[u8] = b"some sample content to sign";
#[test]
fn ps256_empty_sequence_public_key_rejected() {
assert_eq!(
RsaValidator::Ps256
.validate(DUMMY_SIG_512, SAMPLE_DATA, RSA_SPKI_EMPTY_SEQUENCE)
.unwrap_err(),
RawSignatureValidationError::InvalidPublicKey
);
}
#[test]
fn ps256_single_element_sequence_public_key_rejected() {
assert_eq!(
RsaValidator::Ps256
.validate(DUMMY_SIG_512, SAMPLE_DATA, RSA_SPKI_SINGLE_ELEMENT_SEQUENCE)
.unwrap_err(),
RawSignatureValidationError::InvalidPublicKey
);
}
}