Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions docs/adr/10-ecdsa-sd-2023-mandatory-selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# ECDSA-SD-2023 Mandatory N-Quad Selection Must Include Type Quads

## Status

Accepted

## Context

During verification of Singapore Academy of Law (SAL) eApostille credentials using the `ecdsa-sd-2023` cryptosuite, we discovered that our implementation failed to verify real-world credentials despite passing all 59 W3C test vectors.

Root cause analysis revealed a hash mismatch in the mandatory N-Quad selection. The W3C VC-DI-ECDSA specification Section 3.4.11 `createInitialSelection` states:

> "The selection MUST include all `type`s in the path of any JSON Pointer, including any root document `type`."

Our initial implementation selected only the N-Quads directly referenced by mandatory pointers (e.g., `/issuer`, `/validFrom`) but omitted the root document's `rdf:type` quad(s). This subtle requirement is not explicitly tested by the W3C test vectors but is essential for interoperability with the Digital Bazaar reference implementation (`di-sd-primitives`).

### Evidence

For mandatory pointers `["/issuer", "/validFrom"]`:
- **Before fix**: 2 quads selected (issuer + validFrom)
- **After fix**: 3 quads selected (type + issuer + validFrom)
- **Mandatory hash before**: `3b3fe231696b24aa21040236152782195736921af2bc49055f39ed78cbdc5ffe`
- **Mandatory hash after**: `aef02d63b87de37d247648f1027f4cc8d6e7a709c76dd9b854689abaeff0d8a9` (matches reference)

## Decision

The `selectMandatoryNQuads` function in `pkg/vc20/crypto/ecdsa/sd_helpers.go` MUST:

1. Track which container paths are touched by mandatory pointers
2. Include the `rdf:type` quad(s) for each container in the path, including the root document
3. For any pointer touching the root level (e.g., `/issuer`), include the root document's type quad(s)

## Consequences

### Positive

- Interoperability with Digital Bazaar reference implementation
- SAL eApostille and other real-world credentials now verify correctly
- Full compliance with W3C VC-DI-ECDSA specification Section 3.4.11
- All existing W3C test vectors continue to pass

### Negative

- More complex mandatory selection logic
- Requires careful reading of W3C spec for future cryptosuite implementations

## References

- W3C VC-DI-ECDSA Specification: https://www.w3.org/TR/vc-di-ecdsa/
- Section 3.4.11 createInitialSelection
- Digital Bazaar di-sd-primitives: https://github.com/digitalbazaar/di-sd-primitives
65 changes: 65 additions & 0 deletions docs/adr/11-real-world-test-vectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Real-World Test Vectors Required for Cryptosuite Validation

## Status

Accepted

## Context

Our ECDSA-SD-2023 implementation passed all 59 W3C conformance test vectors but failed to verify real-world credentials from Singapore Academy of Law (SAL). This exposed a gap in our testing strategy.

W3C test vectors are designed to test specific features in isolation and may not exercise all code paths that real-world implementations encounter. The SAL eApostille credentials:

1. Use BASE proofs (CBOR tag `0xd95d00`) presented directly, not derived proofs
2. Have complex credential structures with nested objects and status lists
3. Use specific mandatory pointer configurations (`/issuer`, `/validFrom`)
4. Were signed by the Digital Bazaar reference implementation

The subtle W3C spec requirement about including type quads in mandatory selection (Section 3.4.11) was not caught by synthetic test vectors because they may have:
- Used credentials where type quads were already implicitly included
- Not tested the exact mandatory pointer combinations that exposed the bug

## Decision

For any cryptographic suite implementation:

1. **W3C test vectors are necessary but not sufficient** - They establish baseline conformance but don't guarantee real-world interoperability

2. **Collect real-world test vectors** from production issuers using the target cryptosuite:
- Singapore SAL eApostille (`ecdsa-sd-2023` BASE proofs)
- Other government/enterprise issuers as available

3. **Store test vectors in `testdata/` directories** organized by source:
- `testdata/w3c-test-vectors/` - Official W3C conformance tests
- `testdata/sg-test-vectors/` - Singapore SAL credentials
- `testdata/<source>-test-vectors/` - Other real-world sources

4. **Create integration tests** that verify against real-world test vectors, with detailed debug output for hash comparisons

5. **Document issuer public keys and DID resolution** for offline testing

## Consequences

### Positive

- Higher confidence in real-world interoperability
- Earlier detection of spec interpretation issues
- Better alignment with reference implementations (Digital Bazaar)
- Comprehensive test coverage across implementation variations

### Negative

- More test data to maintain
- Need to track issuer key rotations
- Potential privacy considerations for test credentials (use synthetic data where possible)

## Implementation

Test files created:
- `pkg/vc20/crypto/ecdsa/sd_eapostille_test.go` - SAL eApostille verification tests
- `testdata/sg-test-vectors/` - Singapore test credentials

Required test assertions:
1. Mandatory hash matches reference implementation
2. Proof hash matches reference implementation
3. Full signature verification succeeds
80 changes: 80 additions & 0 deletions docs/adr/12-ecdsa-sd-2023-base-proof-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Support ECDSA-SD-2023 BASE Proof Verification

## Status

Accepted

## Context

The ECDSA-SD-2023 specification defines two proof types:

1. **BASE proofs** (CBOR tag `0xd95d00` / 23808) - Created by the issuer, contain:
- Base signature (64 bytes for P-256)
- Ephemeral public key (35 bytes multicodec-compressed)
- HMAC key (32 bytes)
- Per-message signatures for non-mandatory quads
- Mandatory JSON pointers

2. **DERIVED proofs** (CBOR tag `0xd95d01` / 23809) - Created by the holder for selective disclosure

The typical flow is: Issuer creates BASE → Holder creates DERIVED → Verifier verifies DERIVED.

However, real-world usage (e.g., Singapore SAL eApostille) shows that **BASE proofs may be presented directly** without derivation. This occurs when:
- Full credential disclosure is acceptable
- The holder system doesn't implement derivation
- The credential is verified at the point of issuance

Our initial implementation only supported DERIVED proof verification, causing BASE proofs to fail.

## Decision

The verifier MUST support verification of both BASE and DERIVED proofs:

### BASE Proof Verification (Section 3.6.2)

1. Decode CBOR with tag `0xd95d00`
2. Extract: baseSignature, ephemeralPublicKey, hmacKey, signatures, mandatoryPointers
3. Compute proofHash from canonicalized proof options
4. Select mandatory N-Quads using pointers (including type quads per ADR-10)
5. Compute mandatoryHash from selected quads
6. Build combined data: `proofHash || ephemeralPublicKey || mandatoryHash`
7. Verify baseSignature against SHA-256(combined) using issuer's public key

### DERIVED Proof Verification (Section 3.6.4)

1. Decode CBOR with tag `0xd95d01`
2. Follow the selective disclosure verification algorithm
3. Verify disclosed claims against holder's presentation

### Detection Logic

```go
if len(proofBytes) >= 3 && proofBytes[0] == 0xd9 && proofBytes[1] == 0x5d {
switch proofBytes[2] {
case 0x00:
return verifyBaseProof(...)
case 0x01:
return verifyDerivedProof(...)
}
}
```

## Consequences

### Positive

- Full interoperability with issuers presenting BASE proofs directly
- SAL eApostille credentials verify correctly
- Flexible deployment options for holders

### Negative

- More complex verification logic
- Need to maintain two verification code paths
- BASE proofs reveal full credential (no selective disclosure)

## References

- W3C VC-DI-ECDSA Section 3.5.2: serializeBaseProofValue
- W3C VC-DI-ECDSA Section 3.5.7: serializeDerivedProofValue
- W3C VC-DI-ECDSA Section 3.6.2: Base Proof Verification
73 changes: 73 additions & 0 deletions docs/adr/13-reference-implementation-alignment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Align with Digital Bazaar Reference Implementation for Cryptosuites

## Status

Accepted

## Context

The W3C Verifiable Credentials Data Integrity specifications are complex and contain subtle requirements that can be interpreted differently. During ECDSA-SD-2023 implementation, we encountered:

1. **Ambiguous spec language** around mandatory N-Quad selection
2. **Edge cases not covered by test vectors** (e.g., type quad inclusion)
3. **Implementation-specific choices** (e.g., HMAC blank node label format)

Digital Bazaar maintains the reference implementations for VC Data Integrity:
- `@digitalbazaar/di-sd-primitives` - Core selective disclosure primitives
- `@digitalbazaar/ecdsa-sd-2023-cryptosuite` - ECDSA-SD-2023 implementation
- `@digitalbazaar/data-integrity` - General Data Integrity proof handling

These libraries are used by many production issuers and are the de facto standard for interoperability.

## Decision

When implementing cryptographic suites, we MUST:

1. **Use Digital Bazaar implementations as reference** for spec interpretation
2. **Create JavaScript debug scripts** to extract intermediate values from reference implementation:
- Proof hash
- Mandatory hash
- HMAC-transformed blank node labels
- Combined signing data
3. **Compare Go implementation outputs** against reference at each step
4. **Document any intentional deviations** with rationale

### Debug Script Pattern

```javascript
// Example: verify-debug.mjs
import * as diSdPrimitives from '@digitalbazaar/di-sd-primitives';
// ... extract and log intermediate values for comparison
```

### Go Test Pattern

```go
// Log intermediate values for comparison with reference
t.Logf("Mandatory Hash: %s", hex.EncodeToString(mandatoryHash[:]))
t.Logf("Expected (from JS): %s", expectedMandatoryHash)
```

## Consequences

### Positive

- Guaranteed interoperability with production issuers
- Clear disambiguation of spec ambiguities
- Faster debugging of verification failures
- Confidence in spec compliance

### Negative

- Dependency on external JavaScript tooling for debugging
- Need to track Digital Bazaar library updates
- May inherit any bugs from reference (rare, well-tested)

## Implementation Notes

Key packages for reference:
- `di-sd-primitives@3.0.0` - `createInitialSelection`, `canonicalizeAndGroup`
- `ecdsa-sd-2023-cryptosuite@3.4.0` - Proof creation/verification
- `jsonld@8.x` - JSON-LD processing

Debug scripts location: `testdata/` or `debug-*/` directories (not committed if containing credentials)
Loading