Skip to content

fix: use proper COSE_Sign1 in identity assertion test + add validation checking#52

Open
marmarko wants to merge 1 commit intocontentauth:mainfrom
TrueTake:fix/identity-assertion-cose-and-validation
Open

fix: use proper COSE_Sign1 in identity assertion test + add validation checking#52
marmarko wants to merge 1 commit intocontentauth:mainfrom
TrueTake:fix/identity-assertion-cose-and-validation

Conversation

@marmarko
Copy link

Summary

The IdentityAssertion.spec.ts test had two issues that masked a broken identity assertion signing protocol.

Issue 1: Raw signature instead of COSE_Sign1

The TestCawgSigner credential holder callback returned a raw DER signature:

// Before (broken)
sign = async (payload) => {
  const cborBytes = Buffer.from(encode(payload));
  return this.manifestSigner.sign(cborBytes); // raw DER
};

But sig_type: "cawg.x509.cose" requires the signature field in the identity assertion to be a complete COSE_Sign1 envelope (RFC 9052). The verifier calls CoseSign1::from_tagged_slice() on this field and fails with "extraneous data in CBOR input" on raw DER bytes.

Fix

New CoseCawgSigner class that builds a proper COSE_Sign1:

  1. CBOR-encode SignerPayload → detached payload
  2. Protected header { 1: -7 } (ES256) as CBOR bstr
  3. Build Sig_structure = ["Signature1", protected, b"", payload] per RFC 9052 §4.4
  4. Sign CBOR(Sig_structure) with IEEE P1363 format
  5. Return CBOR(Tag(18, [protected, {}, nil, signature]))

Important: byte strings must be Uint8Array, not Buffer. The cbor2 library unwraps Buffer content (treating it as pre-encoded CBOR) instead of encoding it as a raw bstr, which causes "got map, expected bstr" parse errors.

Issue 2: No validation checking

The test only verified that Reader.fromAsset() didn't throw — it never inspected validation_status. This masked the claimSignature.mismatch error that occurred on every run.

Fix

Added assertions for:

  • cawg.identity assertion is present in the signed manifest
  • Zero integrity errors in validation_status (trust warnings from self-signed certs are expected and filtered)

Dependencies

This test also requires the save_to_stream JUMBF regeneration fix in c2pa-rs (see contentauth/c2pa-rs#1944) to pass. Without that fix, dynamic assertions produce stale JUMBF → claimSignature.mismatch.

References

…n checking

The IdentityAssertion.spec.ts test had two issues:

1. The credential holder callback returned a raw DER signature, but
   sig_type "cawg.x509.cose" requires a complete COSE_Sign1 envelope
   (RFC 9052). The verifier parses the signature field as COSE_Sign1
   and fails with "extraneous data in CBOR input" on raw DER bytes.

   Fix: implement CoseCawgSigner that builds a proper COSE_Sign1:
   - Protected header { 1: -7 } (ES256) encoded as CBOR bstr
   - Sig_structure = ["Signature1", protected, external_aad, payload]
   - Sign CBOR(Sig_structure) with IEEE P1363 format
   - Return Tag(18, [protected, {}, nil, signature])

   IMPORTANT: Use Uint8Array (not Buffer) for byte strings in cbor2
   to prevent CBOR content unwrapping.

2. The test only checked that Reader.fromAsset() didn't throw — it
   never inspected validation_status. This masked the
   claimSignature.mismatch error that occurred on every run.

   Fix: add assertions for cawg.identity presence and zero integrity
   errors in validation_status.

Note: this test also requires the save_to_stream JUMBF regeneration
fix in c2pa-rs (see contentauth/c2pa-rs#1944) to pass without
claimSignature.mismatch.

Refs: contentauth#51

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant