use crate::envelope::{decode_b64, Envelope};
use crate::error::{Error, Result};
use crate::pae::pae;
use crate::traits::Verifier;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VerifiedPayload {
pub payload_type: String,
pub payload: Vec<u8>,
pub accepted_keys: Vec<String>,
}
pub fn verify(
envelope: &Envelope,
keys: &[&dyn Verifier],
threshold: usize,
) -> Result<VerifiedPayload> {
if threshold == 0 {
return Err(Error::ZeroThreshold);
}
if envelope.payload_type.is_empty() {
return Err(Error::EmptyPayloadType);
}
if envelope.signatures.is_empty() {
return Err(Error::NoSignatures);
}
if envelope.signatures.len() > MAX_SIGNATURES {
return Err(Error::TooManySignatures {
got: envelope.signatures.len(),
cap: MAX_SIGNATURES,
});
}
check_countable(keys, threshold)?;
let payload = decode_b64(&envelope.payload, "payload")?;
let pre_image = pae(&envelope.payload_type, &payload);
let mut entries = Vec::with_capacity(envelope.signatures.len());
for entry in &envelope.signatures {
if let Ok(sig) = decode_b64(&entry.sig, "signatures[].sig") {
entries.push((entry.keyid.as_deref().unwrap_or_default(), sig));
}
}
if entries.is_empty() {
return Err(Error::NonCanonicalBase64 {
field: "signatures[].sig",
});
}
let mut accepted = Vec::new();
for (idx, key) in keys.iter().enumerate() {
let key_id = key.key_id();
if key_accepts(*key, &pre_image, &entries, key_id.as_deref()) {
accepted.push(key_id.unwrap_or_else(|| format!("#{idx}")));
}
}
if accepted.len() < threshold {
return Err(Error::ThresholdNotMet {
accepted: accepted.len(),
threshold,
});
}
Ok(VerifiedPayload {
payload_type: envelope.payload_type.clone(),
payload,
accepted_keys: accepted,
})
}
pub const MAX_SIGNATURES: usize = 1024;
fn check_countable(keys: &[&dyn Verifier], threshold: usize) -> Result<()> {
if threshold < 2 {
return Ok(());
}
let mut ids: Vec<String> = Vec::with_capacity(keys.len());
let mut identities: Vec<Vec<u8>> = Vec::with_capacity(keys.len());
for (index, key) in keys.iter().enumerate() {
match key.key_id().filter(|id| !id.is_empty()) {
None => return Err(Error::UnidentifiedKey { index }),
Some(id) => {
if ids.contains(&id) {
return Err(Error::DuplicateKeyId { key_id: id });
}
ids.push(id);
}
}
let identity = key
.key_identity()
.filter(|identity| !identity.is_empty())
.ok_or(Error::UnidentifiedKeyIdentity { index })?;
if let Some(first) = identities.iter().position(|seen| seen == &identity) {
return Err(Error::DuplicateKeyIdentity {
first,
second: index,
});
}
identities.push(identity);
}
Ok(())
}
fn key_accepts(
key: &dyn Verifier,
pre_image: &[u8],
entries: &[(&str, Vec<u8>)],
key_id: Option<&str>,
) -> bool {
if let Some(id) = key_id.filter(|id| !id.is_empty()) {
if entries
.iter()
.any(|(hint, sig)| *hint == id && key.verify(pre_image, sig))
{
return true;
}
}
entries.iter().any(|(_, sig)| key.verify(pre_image, sig))
}