use crate::base::der::read_element;
use crate::base::id::TrustAnchorId;
use crate::base::tree::{HASH_SIZE, Subtree};
use crate::{Error, Result};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SignatureAlgorithm {
EcdsaP256Sha256,
EcdsaP384Sha384,
Ed25519,
MlDsa44,
MlDsa65,
MlDsa87,
}
impl SignatureAlgorithm {
pub fn from_algorithm_oid(oid: &str) -> Option<Self> {
Some(match oid {
"1.2.840.10045.4.3.2" => Self::EcdsaP256Sha256,
"1.2.840.10045.4.3.3" => Self::EcdsaP384Sha384,
"1.3.101.112" => Self::Ed25519,
"2.16.840.1.101.3.4.3.17" => Self::MlDsa44,
"2.16.840.1.101.3.4.3.18" => Self::MlDsa65,
"2.16.840.1.101.3.4.3.19" => Self::MlDsa87,
_ => return None,
})
}
}
pub(crate) fn cosigned_subtree_message(
cosigner: &TrustAnchorId,
log: &TrustAnchorId,
timestamp: u64,
subtree: Subtree,
subtree_hash: &[u8; HASH_SIZE],
) -> Result<Vec<u8>> {
if timestamp != 0 && subtree.start != 0 {
return Err(Error::Malformed("timestamped non-checkpoint cosignature"));
}
let cosigner_name = cosigner.note_name();
let log_origin = log.note_name();
if cosigner_name.len() > 255 || log_origin.len() > 255 {
return Err(Error::Malformed("cosigner name"));
}
let mut message = Vec::with_capacity(
12 + 1 + cosigner_name.len() + 8 + 1 + log_origin.len() + 16 + HASH_SIZE,
);
message.extend_from_slice(b"subtree/v1\n\0");
message.push(cosigner_name.len() as u8);
message.extend_from_slice(&cosigner_name);
message.extend_from_slice(×tamp.to_be_bytes());
message.push(log_origin.len() as u8);
message.extend_from_slice(&log_origin);
message.extend_from_slice(&subtree.start.to_be_bytes());
message.extend_from_slice(&subtree.end.to_be_bytes());
message.extend_from_slice(subtree_hash);
Ok(message)
}
fn spki_key_bytes(algorithm: SignatureAlgorithm, spki_der: &[u8]) -> Result<&[u8]> {
let (spki, rest) = read_element(spki_der).ok_or(Error::UnsupportedKey("SPKI"))?;
if spki.tag != 0x30 || !rest.is_empty() {
return Err(Error::UnsupportedKey("SPKI"));
}
let (algorithm_element, rest) =
read_element(spki.content).ok_or(Error::UnsupportedKey("SPKI algorithm"))?;
let (key, rest) = read_element(rest).ok_or(Error::UnsupportedKey("SPKI key"))?;
if !rest.is_empty() || key.tag != 0x03 || key.content.is_empty() || key.content[0] != 0 {
return Err(Error::UnsupportedKey("SPKI key"));
}
let (algorithm_oid, rest) = read_element(algorithm_element.content)
.ok_or(Error::UnsupportedKey("SPKI algorithm OID"))?;
if algorithm_oid.tag != 0x06 {
return Err(Error::UnsupportedKey("SPKI algorithm OID"));
}
let dotted = crate::base::id::oid_content_to_dotted(algorithm_oid.content)?;
let expected = match algorithm {
SignatureAlgorithm::EcdsaP256Sha256 => "1.2.840.10045.2.1",
SignatureAlgorithm::EcdsaP384Sha384 => "1.2.840.10045.2.1",
SignatureAlgorithm::Ed25519 => "1.3.101.112",
SignatureAlgorithm::MlDsa44 => "2.16.840.1.101.3.4.3.17",
SignatureAlgorithm::MlDsa65 => "2.16.840.1.101.3.4.3.18",
SignatureAlgorithm::MlDsa87 => "2.16.840.1.101.3.4.3.19",
};
if dotted != expected {
return Err(Error::UnsupportedKey("SPKI algorithm mismatch"));
}
if let SignatureAlgorithm::EcdsaP256Sha256 | SignatureAlgorithm::EcdsaP384Sha384 = algorithm {
let (curve, _) = read_element(rest).ok_or(Error::UnsupportedKey("curve OID"))?;
if curve.tag != 0x06 {
return Err(Error::UnsupportedKey("curve OID"));
}
let curve = crate::base::id::oid_content_to_dotted(curve.content)?;
let expected_curve = match algorithm {
SignatureAlgorithm::EcdsaP256Sha256 => "1.2.840.10045.3.1.7",
SignatureAlgorithm::EcdsaP384Sha384 => "1.3.132.0.34",
_ => unreachable!(),
};
if curve != expected_curve {
return Err(Error::UnsupportedKey("curve mismatch"));
}
} else if !rest.is_empty() {
return Err(Error::UnsupportedKey("unexpected algorithm parameters"));
}
Ok(&key.content[1..])
}
pub(crate) fn verify_cosignature(
cosigner: &TrustAnchorId,
log: &TrustAnchorId,
algorithm: SignatureAlgorithm,
spki_der: &[u8],
subtree: Subtree,
subtree_hash: &[u8; HASH_SIZE],
signature: &[u8],
) -> Result<()> {
let message = cosigned_subtree_message(cosigner, log, 0, subtree, subtree_hash)?;
let key = spki_key_bytes(algorithm, spki_der)?;
let invalid = || Error::InvalidCosignature(cosigner.clone());
match algorithm {
SignatureAlgorithm::EcdsaP256Sha256 => {
verify_ecdsa_p256(key, &message, signature).map_err(|_| invalid())
}
SignatureAlgorithm::EcdsaP384Sha384 => {
verify_ecdsa_p384(key, &message, signature).map_err(|_| invalid())
}
SignatureAlgorithm::Ed25519 => {
verify_ed25519(key, &message, signature).map_err(|_| invalid())
}
SignatureAlgorithm::MlDsa44 => {
verify_ml_dsa::<ml_dsa::MlDsa44>(key, &message, signature).map_err(|_| invalid())
}
SignatureAlgorithm::MlDsa65 => {
verify_ml_dsa::<ml_dsa::MlDsa65>(key, &message, signature).map_err(|_| invalid())
}
SignatureAlgorithm::MlDsa87 => {
verify_ml_dsa::<ml_dsa::MlDsa87>(key, &message, signature).map_err(|_| invalid())
}
}
}
fn verify_ecdsa_p256(
key: &[u8],
message: &[u8],
signature: &[u8],
) -> core::result::Result<(), signature::Error> {
use signature::Verifier as _;
let key =
p256::ecdsa::VerifyingKey::from_sec1_bytes(key).map_err(|_| signature::Error::new())?;
let signature =
p256::ecdsa::Signature::from_der(signature).map_err(|_| signature::Error::new())?;
key.verify(message, &signature)
}
fn verify_ecdsa_p384(
key: &[u8],
message: &[u8],
signature: &[u8],
) -> core::result::Result<(), signature::Error> {
use signature::Verifier as _;
let key =
p384::ecdsa::VerifyingKey::from_sec1_bytes(key).map_err(|_| signature::Error::new())?;
let signature =
p384::ecdsa::Signature::from_der(signature).map_err(|_| signature::Error::new())?;
key.verify(message, &signature)
}
fn verify_ed25519(
key: &[u8],
message: &[u8],
signature: &[u8],
) -> core::result::Result<(), signature::Error> {
use signature::Verifier as _;
let key_bytes: &[u8; 32] = key.try_into().map_err(|_| signature::Error::new())?;
let key =
ed25519_dalek::VerifyingKey::from_bytes(key_bytes).map_err(|_| signature::Error::new())?;
let signature =
ed25519_dalek::Signature::from_slice(signature).map_err(|_| signature::Error::new())?;
key.verify(message, &signature)
}
fn verify_ml_dsa<P: ml_dsa::MlDsaParams>(
key: &[u8],
message: &[u8],
signature: &[u8],
) -> core::result::Result<(), signature::Error> {
use signature::Verifier as _;
let encoded_key =
ml_dsa::EncodedVerifyingKey::<P>::try_from(key).map_err(|_| signature::Error::new())?;
let key = ml_dsa::VerifyingKey::<P>::decode(&encoded_key);
let encoded_signature =
ml_dsa::EncodedSignature::<P>::try_from(signature).map_err(|_| signature::Error::new())?;
let signature =
ml_dsa::Signature::<P>::decode(&encoded_signature).ok_or_else(signature::Error::new)?;
key.verify(message, &signature)
}