use crate::{der, equal, Error};
use ring::signature;
pub struct SignedData<'a> {
data: untrusted::Input<'a>,
pub(crate) algorithm: untrusted::Input<'a>,
signature: untrusted::Input<'a>,
}
pub(crate) fn parse_signed_data<'a>(
der: &mut untrusted::Reader<'a>,
) -> Result<(untrusted::Input<'a>, SignedData<'a>), Error> {
let (data, tbs) =
der.read_partial(|input| der::expect_tag_and_get_value(input, der::Tag::Sequence))?;
let algorithm = der::expect_tag_and_get_value(der, der::Tag::Sequence)?;
let signature = der::bit_string_with_no_unused_bits(der)?;
Ok((
tbs,
SignedData {
data,
algorithm,
signature,
},
))
}
pub(crate) fn verify_signed_data(
supported_algorithms: &[&SignatureAlgorithm],
spki_value: untrusted::Input,
signed_data: &SignedData,
) -> Result<(), Error> {
let mut found_signature_alg_match = false;
for supported_alg in supported_algorithms.iter().filter(|alg| {
alg.signature_alg_id
.matches_algorithm_id_value(signed_data.algorithm)
}) {
match verify_signature(
supported_alg,
spki_value,
signed_data.data,
signed_data.signature,
) {
Err(Error::UnsupportedSignatureAlgorithmForPublicKey) => {
found_signature_alg_match = true;
continue;
}
result => {
return result;
}
}
}
if found_signature_alg_match {
Err(Error::UnsupportedSignatureAlgorithmForPublicKey)
} else {
Err(Error::UnsupportedSignatureAlgorithm)
}
}
pub(crate) fn verify_signature(
signature_alg: &SignatureAlgorithm,
spki_value: untrusted::Input,
msg: untrusted::Input,
signature: untrusted::Input,
) -> Result<(), Error> {
let spki = parse_spki_value(spki_value)?;
if !signature_alg
.public_key_alg_id
.matches_algorithm_id_value(spki.algorithm_id_value)
{
return Err(Error::UnsupportedSignatureAlgorithmForPublicKey);
}
signature::UnparsedPublicKey::new(
signature_alg.verification_alg,
spki.key_value.as_slice_less_safe(),
)
.verify(msg.as_slice_less_safe(), signature.as_slice_less_safe())
.map_err(|_| Error::InvalidSignatureForPublicKey)
}
struct SubjectPublicKeyInfo<'a> {
algorithm_id_value: untrusted::Input<'a>,
key_value: untrusted::Input<'a>,
}
fn parse_spki_value(input: untrusted::Input) -> Result<SubjectPublicKeyInfo, Error> {
input.read_all(Error::BadDer, |input| {
let algorithm_id_value = der::expect_tag_and_get_value(input, der::Tag::Sequence)?;
let key_value = der::bit_string_with_no_unused_bits(input)?;
Ok(SubjectPublicKeyInfo {
algorithm_id_value,
key_value,
})
})
}
pub struct SignatureAlgorithm {
public_key_alg_id: AlgorithmIdentifier,
signature_alg_id: AlgorithmIdentifier,
verification_alg: &'static dyn signature::VerificationAlgorithm,
}
pub static ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: ECDSA_P256,
signature_alg_id: ECDSA_SHA256,
verification_alg: &signature::ECDSA_P256_SHA256_ASN1,
};
pub static ECDSA_P256_SHA384: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: ECDSA_P256,
signature_alg_id: ECDSA_SHA384,
verification_alg: &signature::ECDSA_P256_SHA384_ASN1,
};
pub static ECDSA_P384_SHA256: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: ECDSA_P384,
signature_alg_id: ECDSA_SHA256,
verification_alg: &signature::ECDSA_P384_SHA256_ASN1,
};
pub static ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: ECDSA_P384,
signature_alg_id: ECDSA_SHA384,
verification_alg: &signature::ECDSA_P384_SHA384_ASN1,
};
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_2048_8192_SHA256: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PKCS1_SHA256,
verification_alg: &signature::RSA_PKCS1_2048_8192_SHA256,
};
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_2048_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PKCS1_SHA384,
verification_alg: &signature::RSA_PKCS1_2048_8192_SHA384,
};
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_2048_8192_SHA512: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PKCS1_SHA512,
verification_alg: &signature::RSA_PKCS1_2048_8192_SHA512,
};
#[cfg(feature = "alloc")]
pub static RSA_PKCS1_3072_8192_SHA384: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PKCS1_SHA384,
verification_alg: &signature::RSA_PKCS1_3072_8192_SHA384,
};
#[cfg(feature = "alloc")]
pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PSS_SHA256,
verification_alg: &signature::RSA_PSS_2048_8192_SHA256,
};
#[cfg(feature = "alloc")]
pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PSS_SHA384,
verification_alg: &signature::RSA_PSS_2048_8192_SHA384,
};
#[cfg(feature = "alloc")]
pub static RSA_PSS_2048_8192_SHA512_LEGACY_KEY: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: RSA_ENCRYPTION,
signature_alg_id: RSA_PSS_SHA512,
verification_alg: &signature::RSA_PSS_2048_8192_SHA512,
};
pub static ED25519: SignatureAlgorithm = SignatureAlgorithm {
public_key_alg_id: ED_25519,
signature_alg_id: ED_25519,
verification_alg: &signature::ED25519,
};
struct AlgorithmIdentifier {
asn1_id_value: untrusted::Input<'static>,
}
impl AlgorithmIdentifier {
fn matches_algorithm_id_value(&self, encoded: untrusted::Input) -> bool {
equal(encoded, self.asn1_id_value)
}
}
const ECDSA_P256: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-p256.der")),
};
const ECDSA_P384: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-p384.der")),
};
const ECDSA_SHA256: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-sha256.der")),
};
const ECDSA_SHA384: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ecdsa-sha384.der")),
};
#[cfg(feature = "alloc")]
const RSA_ENCRYPTION: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-encryption.der")),
};
#[cfg(feature = "alloc")]
const RSA_PKCS1_SHA256: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha256.der")),
};
#[cfg(feature = "alloc")]
const RSA_PKCS1_SHA384: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha384.der")),
};
#[cfg(feature = "alloc")]
const RSA_PKCS1_SHA512: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pkcs1-sha512.der")),
};
#[cfg(feature = "alloc")]
const RSA_PSS_SHA256: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha256.der")),
};
#[cfg(feature = "alloc")]
const RSA_PSS_SHA384: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha384.der")),
};
#[cfg(feature = "alloc")]
const RSA_PSS_SHA512: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-rsa-pss-sha512.der")),
};
const ED_25519: AlgorithmIdentifier = AlgorithmIdentifier {
asn1_id_value: untrusted::Input::from(include_bytes!("data/alg-ed25519.der")),
};
#[cfg(test)]
mod tests {
use crate::{der, signed_data, Error};
use alloc::{string::String, vec::Vec};
macro_rules! test_file_bytes {
( $file_name:expr ) => {
include_bytes!(concat!(
"../third-party/chromium/data/verify_signed_data/",
$file_name
))
};
}
macro_rules! test_verify_signed_data {
($fn_name:ident, $file_name:expr, $expected_result:expr) => {
#[test]
fn $fn_name() {
test_verify_signed_data(test_file_bytes!($file_name), $expected_result);
}
};
}
fn test_verify_signed_data(file_contents: &[u8], expected_result: Result<(), Error>) {
let tsd = parse_test_signed_data(file_contents);
let spki_value = untrusted::Input::from(&tsd.spki);
let spki_value = spki_value
.read_all(Error::BadDer, |input| {
der::expect_tag_and_get_value(input, der::Tag::Sequence)
})
.unwrap();
let algorithm = untrusted::Input::from(&tsd.algorithm);
let algorithm = algorithm
.read_all(Error::BadDer, |input| {
der::expect_tag_and_get_value(input, der::Tag::Sequence)
})
.unwrap();
let signature = untrusted::Input::from(&tsd.signature);
let signature = signature
.read_all(Error::BadDer, |input| {
der::bit_string_with_no_unused_bits(input)
})
.unwrap();
let signed_data = signed_data::SignedData {
data: untrusted::Input::from(&tsd.data),
algorithm,
signature,
};
assert_eq!(
expected_result,
signed_data::verify_signed_data(
SUPPORTED_ALGORITHMS_IN_TESTS,
spki_value,
&signed_data
)
);
}
macro_rules! test_verify_signed_data_signature_outer {
($fn_name:ident, $file_name:expr, $expected_result:expr) => {
#[test]
fn $fn_name() {
test_verify_signed_data_signature_outer(
test_file_bytes!($file_name),
$expected_result,
);
}
};
}
fn test_verify_signed_data_signature_outer(file_contents: &[u8], expected_error: Error) {
let tsd = parse_test_signed_data(file_contents);
let signature = untrusted::Input::from(&tsd.signature);
assert_eq!(
expected_error,
signature
.read_all(Error::BadDer, |input| {
der::bit_string_with_no_unused_bits(input)
})
.unwrap_err()
);
}
macro_rules! test_parse_spki_bad_outer {
($fn_name:ident, $file_name:expr, $error:expr) => {
#[test]
fn $fn_name() {
test_parse_spki_bad_outer(test_file_bytes!($file_name), $error)
}
};
}
fn test_parse_spki_bad_outer(file_contents: &[u8], expected_error: Error) {
let tsd = parse_test_signed_data(file_contents);
let spki = untrusted::Input::from(&tsd.spki);
assert_eq!(
expected_error,
spki.read_all(Error::BadDer, |input| {
der::expect_tag_and_get_value(input, der::Tag::Sequence)
})
.unwrap_err()
);
}
const UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY: Error = if cfg!(feature = "alloc") {
Error::UnsupportedSignatureAlgorithmForPublicKey
} else {
Error::UnsupportedSignatureAlgorithm
};
const INVALID_SIGNATURE_FOR_RSA_KEY: Error = if cfg!(feature = "alloc") {
Error::InvalidSignatureForPublicKey
} else {
Error::UnsupportedSignatureAlgorithm
};
const OK_IF_RSA_AVAILABLE: Result<(), Error> = if cfg!(feature = "alloc") {
Ok(())
} else {
Err(Error::UnsupportedSignatureAlgorithm)
};
test_verify_signed_data!(
test_ecdsa_prime256v1_sha512_spki_params_null,
"ecdsa-prime256v1-sha512-spki-params-null.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data_signature_outer!(
test_ecdsa_prime256v1_sha512_unused_bits_signature,
"ecdsa-prime256v1-sha512-unused-bits-signature.pem",
Error::BadDer
);
test_verify_signed_data!(
test_ecdsa_prime256v1_sha512_using_ecdh_key,
"ecdsa-prime256v1-sha512-using-ecdh-key.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_ecdsa_prime256v1_sha512_using_ecmqv_key,
"ecdsa-prime256v1-sha512-using-ecmqv-key.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_ecdsa_prime256v1_sha512_using_rsa_algorithm,
"ecdsa-prime256v1-sha512-using-rsa-algorithm.pem",
Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_ecdsa_prime256v1_sha512_wrong_signature_format,
"ecdsa-prime256v1-sha512-wrong-signature-format.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_ecdsa_prime256v1_sha512,
"ecdsa-prime256v1-sha512.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_ecdsa_secp384r1_sha256_corrupted_data,
"ecdsa-secp384r1-sha256-corrupted-data.pem",
Err(Error::InvalidSignatureForPublicKey)
);
test_verify_signed_data!(
test_ecdsa_secp384r1_sha256,
"ecdsa-secp384r1-sha256.pem",
Ok(())
);
test_verify_signed_data!(
test_ecdsa_using_rsa_key,
"ecdsa-using-rsa-key.pem",
Err(Error::UnsupportedSignatureAlgorithmForPublicKey)
);
test_parse_spki_bad_outer!(
test_rsa_pkcs1_sha1_bad_key_der_length,
"rsa-pkcs1-sha1-bad-key-der-length.pem",
Error::BadDer
);
test_parse_spki_bad_outer!(
test_rsa_pkcs1_sha1_bad_key_der_null,
"rsa-pkcs1-sha1-bad-key-der-null.pem",
Error::BadDer
);
test_verify_signed_data!(
test_rsa_pkcs1_sha1_key_params_absent,
"rsa-pkcs1-sha1-key-params-absent.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pkcs1_sha1_using_pss_key_no_params,
"rsa-pkcs1-sha1-using-pss-key-no-params.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pkcs1_sha1_wrong_algorithm,
"rsa-pkcs1-sha1-wrong-algorithm.pem",
Err(INVALID_SIGNATURE_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa_pkcs1_sha1,
"rsa-pkcs1-sha1.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pkcs1_sha256,
"rsa-pkcs1-sha256.pem",
Err(INVALID_SIGNATURE_FOR_RSA_KEY)
);
test_parse_spki_bad_outer!(
test_rsa_pkcs1_sha256_key_encoded_ber,
"rsa-pkcs1-sha256-key-encoded-ber.pem",
Error::BadDer
);
test_verify_signed_data!(
test_rsa_pkcs1_sha256_spki_non_null_params,
"rsa-pkcs1-sha256-spki-non-null-params.pem",
Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa_pkcs1_sha256_using_ecdsa_algorithm,
"rsa-pkcs1-sha256-using-ecdsa-algorithm.pem",
Err(Error::UnsupportedSignatureAlgorithmForPublicKey)
);
test_verify_signed_data!(
test_rsa_pkcs1_sha256_using_id_ea_rsa,
"rsa-pkcs1-sha256-using-id-ea-rsa.pem",
Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa_pss_sha1_salt20_using_pss_key_no_params,
"rsa-pss-sha1-salt20-using-pss-key-no-params.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha1_salt20_using_pss_key_with_null_params,
"rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha1_salt20,
"rsa-pss-sha1-salt20.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha1_wrong_salt,
"rsa-pss-sha1-wrong-salt.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha256_mgf1_sha512_salt33,
"rsa-pss-sha256-mgf1-sha512-salt33.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha256_salt10_using_pss_key_with_params,
"rsa-pss-sha256-salt10-using-pss-key-with-params.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha256_salt10_using_pss_key_with_wrong_params,
"rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha256_salt10,
"rsa-pss-sha256-salt10.pem",
Err(Error::UnsupportedSignatureAlgorithm)
);
test_verify_signed_data!(
test_rsa_pss_sha256_salt32,
"ours/rsa-pss-sha256-salt32.pem",
OK_IF_RSA_AVAILABLE
);
test_verify_signed_data!(
test_rsa_pss_sha384_salt48,
"ours/rsa-pss-sha384-salt48.pem",
OK_IF_RSA_AVAILABLE
);
test_verify_signed_data!(
test_rsa_pss_sha512_salt64,
"ours/rsa-pss-sha512-salt64.pem",
OK_IF_RSA_AVAILABLE
);
test_verify_signed_data!(
test_rsa_pss_sha256_salt32_corrupted_data,
"ours/rsa-pss-sha256-salt32-corrupted-data.pem",
Err(INVALID_SIGNATURE_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa_pss_sha384_salt48_corrupted_data,
"ours/rsa-pss-sha384-salt48-corrupted-data.pem",
Err(INVALID_SIGNATURE_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa_pss_sha512_salt64_corrupted_data,
"ours/rsa-pss-sha512-salt64-corrupted-data.pem",
Err(INVALID_SIGNATURE_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa_using_ec_key,
"rsa-using-ec-key.pem",
Err(UNSUPPORTED_SIGNATURE_ALGORITHM_FOR_RSA_KEY)
);
test_verify_signed_data!(
test_rsa2048_pkcs1_sha512,
"rsa2048-pkcs1-sha512.pem",
OK_IF_RSA_AVAILABLE
);
struct TestSignedData {
spki: Vec<u8>,
data: Vec<u8>,
algorithm: Vec<u8>,
signature: Vec<u8>,
}
fn parse_test_signed_data(file_contents: &[u8]) -> TestSignedData {
let mut lines = core::str::from_utf8(file_contents).unwrap().lines();
let spki = read_pem_section(&mut lines, "PUBLIC KEY");
let algorithm = read_pem_section(&mut lines, "ALGORITHM");
let data = read_pem_section(&mut lines, "DATA");
let signature = read_pem_section(&mut lines, "SIGNATURE");
TestSignedData {
spki,
data,
algorithm,
signature,
}
}
use alloc::str::Lines;
fn read_pem_section(lines: &mut Lines, section_name: &str) -> Vec<u8> {
let begin_section = format!("-----BEGIN {}-----", section_name);
loop {
let line = lines.next().unwrap();
if line == begin_section {
break;
}
}
let mut base64 = String::new();
let end_section = format!("-----END {}-----", section_name);
loop {
let line = lines.next().unwrap();
if line == end_section {
break;
}
base64.push_str(line);
}
base64::decode(&base64).unwrap()
}
static SUPPORTED_ALGORITHMS_IN_TESTS: &[&signed_data::SignatureAlgorithm] = &[
&signed_data::ECDSA_P256_SHA256,
&signed_data::ECDSA_P384_SHA384,
&signed_data::ED25519,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_2048_8192_SHA256,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_2048_8192_SHA384,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_2048_8192_SHA512,
#[cfg(feature = "alloc")]
&signed_data::RSA_PKCS1_3072_8192_SHA384,
#[cfg(feature = "alloc")]
&signed_data::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
#[cfg(feature = "alloc")]
&signed_data::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
#[cfg(feature = "alloc")]
&signed_data::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
&signed_data::ECDSA_P256_SHA384, &signed_data::ECDSA_P384_SHA256, ];
}