use crate::der::{self, FromDer};
use crate::error::{
DerTypeId, Error, UnsupportedSignatureAlgorithmContext,
UnsupportedSignatureAlgorithmForPublicKeyContext,
};
use crate::verify_cert::Budget;
use pki_types::SignatureVerificationAlgorithm;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)]
pub(crate) struct OwnedSignedData {
pub(crate) data: Vec<u8>,
pub(crate) algorithm: Vec<u8>,
pub(crate) signature: Vec<u8>,
}
#[cfg(feature = "alloc")]
impl OwnedSignedData {
pub(crate) fn borrow(&self) -> SignedData<'_> {
SignedData {
data: untrusted::Input::from(&self.data),
algorithm: untrusted::Input::from(&self.algorithm),
signature: untrusted::Input::from(&self.signature),
}
}
}
#[derive(Debug)]
pub(crate) struct SignedData<'a> {
pub(crate) data: untrusted::Input<'a>,
pub(crate) algorithm: untrusted::Input<'a>,
pub(crate) signature: untrusted::Input<'a>,
}
impl<'a> SignedData<'a> {
pub(crate) fn from_der(
der: &mut untrusted::Reader<'a>,
size_limit: usize,
) -> Result<(untrusted::Input<'a>, Self), Error> {
let (data, tbs) = der.read_partial(|input| {
der::expect_tag_and_get_value_limited(input, der::Tag::Sequence, size_limit)
})?;
let algorithm = der::expect_tag(der, der::Tag::Sequence)?;
let signature = der::bit_string_with_no_unused_bits(der)?;
Ok((
tbs,
SignedData {
data,
algorithm,
signature,
},
))
}
#[cfg(feature = "alloc")]
pub(crate) fn to_owned(&self) -> OwnedSignedData {
OwnedSignedData {
data: self.data.as_slice_less_safe().to_vec(),
algorithm: self.algorithm.as_slice_less_safe().to_vec(),
signature: self.signature.as_slice_less_safe().to_vec(),
}
}
}
pub(crate) fn verify_signed_data(
supported_algorithms: &[&dyn SignatureVerificationAlgorithm],
spki_value: untrusted::Input<'_>,
signed_data: &SignedData<'_>,
budget: &mut Budget,
) -> Result<(), Error> {
budget.consume_signature()?;
let mut invalid_for_public_key = None;
for supported_alg in supported_algorithms
.iter()
.filter(|alg| alg.signature_alg_id().as_ref() == signed_data.algorithm.as_slice_less_safe())
{
match verify_signature(
*supported_alg,
spki_value,
signed_data.data,
signed_data.signature,
) {
Err(Error::UnsupportedSignatureAlgorithmForPublicKeyContext(cx)) => {
invalid_for_public_key = Some(cx);
continue;
}
result => return result,
}
}
if let Some(cx) = invalid_for_public_key {
return Err(Error::UnsupportedSignatureAlgorithmForPublicKeyContext(cx));
}
Err(Error::UnsupportedSignatureAlgorithmContext(
UnsupportedSignatureAlgorithmContext {
#[cfg(feature = "alloc")]
signature_algorithm_id: signed_data.algorithm.as_slice_less_safe().to_vec(),
#[cfg(feature = "alloc")]
supported_algorithms: supported_algorithms
.iter()
.map(|&alg| alg.signature_alg_id())
.collect(),
},
))
}
pub(crate) fn verify_signature(
signature_alg: &dyn SignatureVerificationAlgorithm,
spki_value: untrusted::Input<'_>,
msg: untrusted::Input<'_>,
signature: untrusted::Input<'_>,
) -> Result<(), Error> {
let spki = der::read_all::<SubjectPublicKeyInfo<'_>>(spki_value)?;
if signature_alg.public_key_alg_id().as_ref() != spki.algorithm_id_value.as_slice_less_safe() {
return Err(Error::UnsupportedSignatureAlgorithmForPublicKeyContext(
UnsupportedSignatureAlgorithmForPublicKeyContext {
#[cfg(feature = "alloc")]
signature_algorithm_id: signature_alg.signature_alg_id().as_ref().to_vec(),
#[cfg(feature = "alloc")]
public_key_algorithm_id: spki.algorithm_id_value.as_slice_less_safe().to_vec(),
},
));
}
signature_alg
.verify_signature(
spki.key_value.as_slice_less_safe(),
msg.as_slice_less_safe(),
signature.as_slice_less_safe(),
)
.map_err(|_| Error::InvalidSignatureForPublicKey)
}
pub(crate) struct SubjectPublicKeyInfo<'a> {
algorithm_id_value: untrusted::Input<'a>,
key_value: untrusted::Input<'a>,
}
impl<'a> FromDer<'a> for SubjectPublicKeyInfo<'a> {
fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
let algorithm_id_value = der::expect_tag(reader, der::Tag::Sequence)?;
let key_value = der::bit_string_with_no_unused_bits(reader)?;
Ok(SubjectPublicKeyInfo {
algorithm_id_value,
key_value,
})
}
const TYPE_ID: DerTypeId = DerTypeId::SubjectPublicKeyInfo;
}