use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD as B64URL;
use ed25519_dalek::{Signature, Verifier, VerifyingKey};
use super::bundle::{AssertionProof, DidSignedAssertion, ProducerAssertion};
use super::error::SealedTransferError;
pub const DID_SIGNED_DOMAIN_TAG: &[u8] = b"vta-sealed-transfer/v1\0";
pub fn verify_did_signed_assertion_with_pubkey(
assertion: &DidSignedAssertion,
expected_producer_did: &str,
producer_ed25519_pubkey: &[u8; 32],
client_x25519_pub: &[u8; 32],
bundle_id: &[u8; 16],
) -> Result<(), SealedTransferError> {
if assertion.did != expected_producer_did {
return Err(SealedTransferError::AssertionVerification(format!(
"DidSigned assertion DID '{}' does not match bundle producer_did '{}'",
assertion.did, expected_producer_did,
)));
}
let sig_bytes = B64URL
.decode(assertion.signature_b64.as_bytes())
.map_err(|e| {
SealedTransferError::AssertionVerification(format!("decode signature_b64: {e}"))
})?;
let signature = Signature::from_slice(&sig_bytes).map_err(|e| {
SealedTransferError::AssertionVerification(format!(
"signature shape (expected 64 bytes, got {}): {e}",
sig_bytes.len()
))
})?;
let vk = VerifyingKey::from_bytes(producer_ed25519_pubkey)
.map_err(|e| SealedTransferError::AssertionVerification(format!("pubkey shape: {e}")))?;
let mut msg =
Vec::with_capacity(DID_SIGNED_DOMAIN_TAG.len() + client_x25519_pub.len() + bundle_id.len());
msg.extend_from_slice(DID_SIGNED_DOMAIN_TAG);
msg.extend_from_slice(client_x25519_pub);
msg.extend_from_slice(bundle_id);
vk.verify(&msg, &signature).map_err(|e| {
SealedTransferError::AssertionVerification(format!(
"signature does not verify against producer pubkey: {e}"
))
})
}
#[derive(Debug)]
pub enum VerifiedAssertion<'a> {
DidSignedVerified(&'a ProducerAssertion),
PinnedOnlyAcknowledged(&'a ProducerAssertion),
AttestedNeedsNitroCheck(&'a ProducerAssertion),
}
pub fn verify_producer_assertion_with_pubkey<'a>(
producer: &'a ProducerAssertion,
client_x25519_pub: &[u8; 32],
bundle_id: &[u8; 16],
producer_ed25519_pubkey: Option<&[u8; 32]>,
) -> Result<VerifiedAssertion<'a>, SealedTransferError> {
match &producer.proof {
AssertionProof::PinnedOnly => Ok(VerifiedAssertion::PinnedOnlyAcknowledged(producer)),
AssertionProof::DidSigned(a) => {
let pubkey = producer_ed25519_pubkey.ok_or_else(|| {
SealedTransferError::AssertionVerification(
"DidSigned assertion requires the producer's Ed25519 pubkey; \
resolver returned None"
.into(),
)
})?;
verify_did_signed_assertion_with_pubkey(
a,
&producer.producer_did,
pubkey,
client_x25519_pub,
bundle_id,
)?;
Ok(VerifiedAssertion::DidSignedVerified(producer))
}
AssertionProof::Attested(_) => Ok(VerifiedAssertion::AttestedNeedsNitroCheck(producer)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sealed_transfer::generate_ed25519_keypair;
use ed25519_dalek::{Signer, SigningKey};
fn sign_fixture(
client_x25519_pub: &[u8; 32],
bundle_id: &[u8; 16],
) -> (SigningKey, [u8; 32], DidSignedAssertion) {
let (seed, pub_bytes) = generate_ed25519_keypair();
let signing = SigningKey::from_bytes(&seed);
let mut msg = Vec::new();
msg.extend_from_slice(DID_SIGNED_DOMAIN_TAG);
msg.extend_from_slice(client_x25519_pub);
msg.extend_from_slice(bundle_id);
let sig = signing.sign(&msg);
let assertion = DidSignedAssertion {
did: "did:key:zVtaTestProducer".into(),
signature_b64: B64URL.encode(sig.to_bytes()),
verification_method: "did:key:zVtaTestProducer#z6MkProducer".into(),
};
(signing, pub_bytes, assertion)
}
#[test]
fn verify_passes_for_matching_signature() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, pubkey, assertion) = sign_fixture(&client_x, &bundle_id);
verify_did_signed_assertion_with_pubkey(
&assertion,
"did:key:zVtaTestProducer",
&pubkey,
&client_x,
&bundle_id,
)
.expect("signature verifies under the genuine producer key");
}
#[test]
fn verify_rejects_wrong_producer_did() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, pubkey, assertion) = sign_fixture(&client_x, &bundle_id);
let err = verify_did_signed_assertion_with_pubkey(
&assertion,
"did:key:zSomeoneElse",
&pubkey,
&client_x,
&bundle_id,
)
.expect_err("DID mismatch must be rejected");
assert!(
matches!(err, SealedTransferError::AssertionVerification(_)),
"got: {err:?}",
);
}
#[test]
fn verify_rejects_tampered_bundle_id() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, pubkey, assertion) = sign_fixture(&client_x, &bundle_id);
let tampered_bundle_id = [0x99u8; 16];
let err = verify_did_signed_assertion_with_pubkey(
&assertion,
"did:key:zVtaTestProducer",
&pubkey,
&client_x,
&tampered_bundle_id,
)
.expect_err("bundle_id swap must be rejected");
assert!(
matches!(err, SealedTransferError::AssertionVerification(_)),
"got: {err:?}",
);
}
#[test]
fn verify_rejects_wrong_pubkey() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, _pubkey, assertion) = sign_fixture(&client_x, &bundle_id);
let (_, other_pub) = generate_ed25519_keypair();
let err = verify_did_signed_assertion_with_pubkey(
&assertion,
"did:key:zVtaTestProducer",
&other_pub,
&client_x,
&bundle_id,
)
.expect_err("attacker substituting their own pubkey must be rejected");
assert!(
matches!(err, SealedTransferError::AssertionVerification(_)),
"got: {err:?}",
);
}
#[test]
fn verify_rejects_tampered_signature_bytes() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, pubkey, mut assertion) = sign_fixture(&client_x, &bundle_id);
let mut sig_bytes = B64URL.decode(assertion.signature_b64.as_bytes()).unwrap();
sig_bytes[0] ^= 0x01;
assertion.signature_b64 = B64URL.encode(sig_bytes);
let err = verify_did_signed_assertion_with_pubkey(
&assertion,
"did:key:zVtaTestProducer",
&pubkey,
&client_x,
&bundle_id,
)
.expect_err("bit-flip in signature must be rejected");
assert!(
matches!(err, SealedTransferError::AssertionVerification(_)),
"got: {err:?}",
);
}
#[test]
fn dispatch_pinned_only_yields_acknowledged_variant() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let producer = ProducerAssertion {
producer_did: "did:key:zVtaTestProducer".into(),
proof: AssertionProof::PinnedOnly,
};
let verified =
verify_producer_assertion_with_pubkey(&producer, &client_x, &bundle_id, None)
.expect("PinnedOnly dispatches successfully");
assert!(
matches!(verified, VerifiedAssertion::PinnedOnlyAcknowledged(_)),
"PinnedOnly must yield PinnedOnlyAcknowledged so callers can't mistake it \
for a real signature check — got {verified:?}",
);
}
#[test]
fn dispatch_attested_yields_needs_nitro_check_variant() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let producer = ProducerAssertion {
producer_did: "did:key:zVtaTestProducer".into(),
proof: AssertionProof::Attested(
crate::sealed_transfer::bundle::AttestationQuoteAssertion {
format: "aws-nitro-v1".into(),
quote_b64: "AAAA".into(),
},
),
};
let verified =
verify_producer_assertion_with_pubkey(&producer, &client_x, &bundle_id, None)
.expect("Attested dispatches successfully");
assert!(
matches!(verified, VerifiedAssertion::AttestedNeedsNitroCheck(_)),
"Attested must yield AttestedNeedsNitroCheck so the caller is forced to \
explicitly invoke nitro quote verification — got {verified:?}",
);
}
#[test]
fn dispatch_did_signed_yields_verified_variant() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, pubkey, assertion) = sign_fixture(&client_x, &bundle_id);
let producer = ProducerAssertion {
producer_did: "did:key:zVtaTestProducer".into(),
proof: AssertionProof::DidSigned(assertion),
};
let verified =
verify_producer_assertion_with_pubkey(&producer, &client_x, &bundle_id, Some(&pubkey))
.expect("DidSigned with valid pubkey verifies");
assert!(
matches!(verified, VerifiedAssertion::DidSignedVerified(_)),
"got: {verified:?}",
);
}
#[test]
fn dispatch_did_signed_requires_pubkey() {
let client_x = [0xAAu8; 32];
let bundle_id = [0x55u8; 16];
let (_signing, _pubkey, assertion) = sign_fixture(&client_x, &bundle_id);
let producer = ProducerAssertion {
producer_did: "did:key:zVtaTestProducer".into(),
proof: AssertionProof::DidSigned(assertion),
};
let err = verify_producer_assertion_with_pubkey(&producer, &client_x, &bundle_id, None)
.expect_err("DidSigned without a pubkey is an error");
assert!(
matches!(err, SealedTransferError::AssertionVerification(_)),
"got: {err:?}",
);
}
}