Skip to content

feat(EVES-009): new draft about evidence#38

Open
flhps wants to merge 4 commits into
mainfrom
feat/eves-009
Open

feat(EVES-009): new draft about evidence#38
flhps wants to merge 4 commits into
mainfrom
feat/eves-009

Conversation

@flhps
Copy link
Copy Markdown
Contributor

@flhps flhps commented Mar 18, 2026

No description provided.

Signed-off-by: felix hoops <9974641+flhps@users.noreply.github.com>
@flhps flhps requested a review from jdsika March 18, 2026 11:14
@flhps flhps changed the title feat(EVES-009): new draft on evidence feat(EVES-009): new draft about evidence Mar 18, 2026
jdsika added a commit that referenced this pull request Apr 2, 2026
Resolve all review findings against PR #24 (simpulse-id-credentials):

- Replace did:web with did:ethr on Base (ERC-1056) throughout
- Fix all JSON-LD context URLs to w3id.org persistent identifiers
- Fix ontology/schema ID to w3id.org/ascs-ev/simpulse-id/core/v1
- Fix DID verification methods: P-256 JsonWebKey (not Tezos/Etherlink)
- Fix reference implementation URL and repository structure
- Fix does:web typo and all factual mismatches

Add missing specification content:
- Section 3.4: Credential Subject Semantics (member vs memberOf)
- Section 3.5: Revocation (harbour:CRSetEntry)
- Expanded Lifecycle with issuance order and prerequisites
- Schema-first (LinkML) architecture documented
- SHACL validation requirement added

EVES-001 format compliance:
- RFC 2119 keywords (MUST/SHOULD/MAY) throughout normative sections
- Proper linked references with URLs
- Type changed from Process to Standards
- discussions-to points to EVES repo issues
- EVES-009 added as dependency (evidence protocol)

Evidence details deferred to EVES-009 (PR #38).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@jdsika jdsika left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation Review: harbour-credentials & simpulse-id-credentials

We audited the harbour-credentials and simpulse-id-credentials implementations against this specification. Overall the implementation is substantially compliant, but the challenge binding mechanism diverges from §2/§4.2 for good reason. Below are detailed findings.

Challenge Binding: OID4VP vs EVES-009

EVES-009 §2 says:

The challenge is the cryptographic hash of the serialized message. The signature object is a VP where the challenge is embedded in the VP's nonce or challenge field.

EVES-009 §4.2 check 4 says:

The VP's nonce MUST equal hash(message).

What harbour implements instead:
harbour uses the OID4VP dual-binding approach with SD-JWT VPs:

  1. VP nonce = random hex string (replay prevention)
  2. KB-JWT transaction_data_hashes = [SHA-256(base64url(transaction_data_param))] per OID4VP Appendix B.3.3

This means the message binding is done via the KB-JWT hash chain, not via the VP nonce directly. The VP nonce serves purely for replay prevention.

Why this divergence is justified:

  1. OID4VP compatibility: The wallet ecosystem (e.g., EUDI, Sphereon, walt.id) implements OID4VP's transaction_data parameter and expects KB-JWT binding — not a custom nonce format. Aligning with EVES-009's nonce = hash(message) would break interoperability with standard OID4VP wallets.

  2. Separation of concerns: OID4VP separates replay prevention (nonce) from message binding (transaction_data_hashes). Overloading nonce with the message hash conflates these two distinct security properties.

  3. Multiple evidence items: harbour supports multiple DelegatedSignatureEvidence items in a single VP. The KB-JWT transaction_data_hashes array supports this naturally. A single nonce field cannot bind to multiple distinct messages.

  4. SD-JWT-VC specifics: For SD-JWT VPs (RFC 9901), the KB-JWT already carries sd_hash binding the VP to the disclosed credential. Adding transaction_data_hashes extends this pattern consistently. Putting the hash in nonce instead would be inconsistent with the SD-JWT verification model.

Suggestion for the spec: Consider relaxing §4.2 check 4 to allow implementation-specific binding mechanisms, e.g.:

The VP MUST be bound to the message. This binding MAY be achieved by setting the VP's nonce equal to hash(message), or by using the OID4VP transaction_data mechanism (Appendix B.3.3) where the KB-JWT transaction_data_hashes array contains hash(transaction_data_param).

This would accommodate both the simple nonce-based approach (useful for non-SD-JWT VPs) and the OID4VP KB-JWT approach (required for SD-JWT VPs and wallet interoperability).

Other Compliance Findings

Requirement Status Notes
Evidence = proof of consent TransactionData + VP with delegation evidence
Message with description, nonce, timestamp TransactionData.create() with SIWE-style display
Challenge = SHA-256(serialize(message)) compute_hash() with canonical JSON
VP signature verified vs Holder DID verify_sd_jwt_vp() step 1
Each VC independently verified Step 2 (issuer JWT signature)
SD-JWT VCs RECOMMENDED Full selective disclosure support
Replay prevention (unique nonce) secrets.token_hex(4) per transaction
Time-bounding validate_transaction_data() with configurable max_age
Human-readable consent display render_transaction_display() (SIWE-inspired)
Credential freshness / revocation ⚠️ Not enforced in VP verification — caller responsibility

Minor Spec Feedback

  1. §3.2 step 1 has a typo: "challenge derived form it" → "from it"
  2. §3.2 step 3 references "credentail request" → "credential request" (in mermaid diagram)
  3. §1 message format: The spec says implementations SHOULD include "a domain or origin identifying the Requester". harbour's TransactionData includes this implicitly via credential_ids (DIDs identifying the credential being acted upon) but does not have an explicit origin field. Consider whether DID-based identification of the Requester satisfies this requirement.

jdsika added a commit that referenced this pull request Apr 4, 2026
* Add first draft for EVES-008
* mardown lint

Resolve all review findings against PR #24 (simpulse-id-credentials):

- Replace did:web with did:ethr on Base (ERC-1056) throughout
- Fix all JSON-LD context URLs to w3id.org persistent identifiers
- Fix ontology/schema ID to w3id.org/ascs-ev/simpulse-id/core/v1
- Fix DID verification methods: P-256 JsonWebKey (not Tezos/Etherlink)
- Fix reference implementation URL and repository structure
- Fix does:web typo and all factual mismatches

Add missing specification content:
- Section 3.4: Credential Subject Semantics (member vs memberOf)
- Section 3.5: Revocation (harbour:CRSetEntry)
- Expanded Lifecycle with issuance order and prerequisites
- Schema-first (LinkML) architecture documented
- SHACL validation requirement added

EVES-001 format compliance:
- RFC 2119 keywords (MUST/SHOULD/MAY) throughout normative sections
- Proper linked references with URLs
- Type changed from Process to Standards
- discussions-to points to EVES repo issues
- EVES-009 added as dependency (evidence protocol)

Evidence details deferred to EVES-009 (PR #38).

* docs(eves-008): clarify context vs schema URL path difference

Review finding: the /v1/ context path vs /core/v1 schema path
was confusing without explanation. Add clarifying note.

* fix(EVES-008): align spec with reference implementation

- Fix subject class URI: simpulseid:Participant → simpulseid:OrganizationParticipant
  (renamed to avoid JSON-LD context collision with gx:Participant)
- Add SimpulseIdLegalForm enum table (21 values across DE/US/UK jurisdictions)
- Add DID document structure requirements: signing vs non-signing DID
  invariants (verificationMethod, authentication, assertionMethod, controller)
- Add concrete @context example for credential JSON-LD
- Add trailing newline
---------

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
ClemensLinnhoff pushed a commit that referenced this pull request Apr 4, 2026
* Add first draft for EVES-008
* mardown lint

Resolve all review findings against PR #24 (simpulse-id-credentials):

- Replace did:web with did:ethr on Base (ERC-1056) throughout
- Fix all JSON-LD context URLs to w3id.org persistent identifiers
- Fix ontology/schema ID to w3id.org/ascs-ev/simpulse-id/core/v1
- Fix DID verification methods: P-256 JsonWebKey (not Tezos/Etherlink)
- Fix reference implementation URL and repository structure
- Fix does:web typo and all factual mismatches

Add missing specification content:
- Section 3.4: Credential Subject Semantics (member vs memberOf)
- Section 3.5: Revocation (harbour:CRSetEntry)
- Expanded Lifecycle with issuance order and prerequisites
- Schema-first (LinkML) architecture documented
- SHACL validation requirement added

EVES-001 format compliance:
- RFC 2119 keywords (MUST/SHOULD/MAY) throughout normative sections
- Proper linked references with URLs
- Type changed from Process to Standards
- discussions-to points to EVES repo issues
- EVES-009 added as dependency (evidence protocol)

Evidence details deferred to EVES-009 (PR #38).

* docs(eves-008): clarify context vs schema URL path difference

Review finding: the /v1/ context path vs /core/v1 schema path
was confusing without explanation. Add clarifying note.

* fix(EVES-008): align spec with reference implementation

- Fix subject class URI: simpulseid:Participant → simpulseid:OrganizationParticipant
  (renamed to avoid JSON-LD context collision with gx:Participant)
- Add SimpulseIdLegalForm enum table (21 values across DE/US/UK jurisdictions)
- Add DID document structure requirements: signing vs non-signing DID
  invariants (verificationMethod, authentication, assertionMethod, controller)
- Add concrete @context example for credential JSON-LD
- Add trailing newline
---------

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
@jdsika
Copy link
Copy Markdown
Contributor

jdsika commented Apr 4, 2026

Follow-up Review: Additional Findings from Cross-Implementation Analysis

After deeper analysis of both harbour-credentials and simpulse-id-credentials against this spec, several additional gaps were identified.

Critical: Serialization Canonicalization (§2)

The spec says `SHA-256(serialize(message))` but does not define a canonical serialization. harbour-credentials uses `json.dumps(sort_keys=True, separators=(",",":"))` — Python's deterministic JSON output. Two implementations using different JSON serializers (different key ordering, whitespace, Unicode escaping) would produce different hashes for the same logical message, making challenges incompatible.

Recommendation: Reference RFC 8785 (JSON Canonicalization Scheme) or explicitly mandate sorted-key, no-whitespace JSON serialization.

Critical: Empty VPs Conflict with §2

§2 line 81 states:

"The VP contains one or more Verifiable Credentials (VCs) proving the Holder's identity and attributes."

But simpulse-id-credentials creates evidence VPs containing zero inner VCs — they prove holder consent (via nonce binding) without presenting any credentials. This is the entire evidence chain pattern: the evidence VP carries only `holder` + `nonce`, no `verifiableCredential` array.

This is semantically valid — sometimes you need proof that a specific DID holder consented, without requiring them to present identity attributes. The spec should allow zero-VC VPs:

"The VP contains zero or more Verifiable Credentials (VCs). When VCs are present, they prove the Holder's identity and attributes."

Medium: SD-JWT VP Verification Steps Missing (§4.2)

harbour-credentials implements an 8-step verification for SD-JWT VPs (`verify_sd_jwt_vp()`) including:

  • `sd_hash` binding (RFC 9901 §4.3.1)
  • KB-JWT signature verification
  • Disclosure hash matching against `_sd` digests
  • Transaction data hash verification in KB-JWT

§4.2 has only 5 checks and doesn't cover any SD-JWT-specific steps. Since SD-JWT VCs are RECOMMENDED (§2), the verification section should at minimum reference the SD-JWT VP verification requirements or defer to RFC 9901.

Medium: Multiple Evidence Items Per VP

harbour supports multiple `DelegatedSignatureEvidence` items in a single VP (validated via `transaction_data_hashes` array in KB-JWT). The spec implicitly assumes 1:1 between message and VP. Should clarify whether batching is permitted.

Style & Convention

  • Gender-neutral language (§3.2 line 110-111): "He also provides" / "his wallet" → "The Requester also provides" / "the Holder's wallet"
  • Redundant phrasing (§1 line 17): "e.g., for example" — pick one
  • Outdated reference (Adding a SHCAL, Ontology and README - file to onboardingAsset #8): `draft-ietf-oauth-sd-jwt-vc-00` is now RFC 9901
  • Implementation section (line 188): "An initial implementation is planned" — both harbour-credentials and simpulse-id-credentials already implement this spec. Should cite them as reference implementations.

Suggested Priority

  1. Challenge binding relaxation (§2, §4.2) — original review finding
  2. Empty VP allowance (§2) — reference implementation conflict
  3. Serialization canonicalization (§2) — interoperability gap
  4. Typo fixes + gender-neutral language (§3.2)
  5. SD-JWT verification reference (§4.2)
  6. Reference updates (RFC 9901, OID4VP appendix)
  7. Implementation section update

Align spec with harbour-credentials and simpulse-id-credentials
reference implementations based on cross-implementation review.

Specification changes:
- §2: Mandate canonical serialization (RFC 8785 or sorted-key
  no-whitespace JSON) for deterministic challenge hashing
- §2: Allow zero-VC VPs (pure consent proofs where Holder DID
  binding alone demonstrates authorization)
- §2: Support both nonce-based and OID4VP KB-JWT transaction_data_hashes
  binding mechanisms for challenge binding
- §2: Allow multiple evidence items per VP via hash arrays
- §4.2: Relax challenge binding check to accommodate OID4VP dual-binding
  (VP nonce for replay prevention + KB-JWT hashes for message binding)
- §4.2: Add SD-JWT-specific verification requirements (sd_hash, disclosure
  hash validation per RFC 9901)
- §4.2: Clarify VC requirement check is skipped for zero-VC VPs
- §5: Add canonical serialization as a security consideration
- §6: Add consent-only evidence privacy guidance for zero-VC VPs

Style and references:
- Fix gendered language: "He also provides" -> "The Requester also provides"
- Fix "his wallet" -> "the Holder's wallet"
- Fix "e.g., for example" -> "e.g.,"
- Fix typos: "derived form" -> "derived from", "credentail" -> "credential"
- Update ref #8 from expired draft to RFC 9901
- Add RFC 8785 (JSON Canonicalization Scheme) as ref #9
- Add OID4VP Appendix B.3.3 as ref #10
- Update Implementation section: cite harbour-credentials and
  simpulse-id-credentials as existing reference implementations

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
jdsika added 2 commits May 18, 2026 11:51
Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>

# Conflicts:
#	EVES/SUMMARY.md
#	README.md
Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
@jdsika
Copy link
Copy Markdown
Contributor

jdsika commented May 18, 2026

Independent Audit: EVES-009 vs Reference Implementations

Scope: Full cross-reference of eves-009.md (current branch head) against harbour-credentials and simpulse-id-credentials, plus reconciliation with existing reviewer comments.


Status of Previous Review Findings

The fix(EVES-009) commit successfully addressed all 7 priority items from @jdsika's review:

Finding Status
Challenge binding relaxation (OID4VP dual-binding) ✅ §2 lines 89-93, §4.2 check 4
Empty VP allowance (zero or more VCs) ✅ §2 line 85
Serialization canonicalization (RFC 8785) ✅ §2 line 81
Typos (form→from, credentail→credential) ✅ Fixed
Gender-neutral language ✅ Fixed
SD-JWT verification (sd_hash, disclosure validation) ✅ §4.2 check 2, check 4
RFC 9901 reference + implementation section ✅ §References #8, §Implementation

New Findings from Implementation Audit

🔴 HIGH: TransactionData Structure Undefined

Problem: The spec says (§1) implementations SHOULD include "description, domain/DID, nonce, timestamp" but provides no normative schema. harbour-credentials defines a TransactionData class with fields {action, nonce, iat, exp, txn}. simpulse-id-credentials extends txn with {credentialId}. Without a normative structure, two independent implementations cannot produce interoperable challenges.

Evidence: harbour's TransactionData.to_json() → canonical JSON → SHA-256. If another implementation structures the same logical message differently, the hashes will differ.

Recommendation: Add a normative JSON schema (or at minimum an informative example) in a new §2.1:

{
  "action": "credential.issue",
  "description": "Issue ASCS membership credential",
  "origin": "did:web:envited-x.net",
  "nonce": "a1b2c3d4e5f6a7b8",
  "iat": 1716024590,
  "exp": 1716024890,
  "txn": { "credentialId": "did:web:example.com:cred:123" }
}

🔴 HIGH: Composite Nonce Format Unspecified

Problem: §2 line 89 mentions <random> <action_type> <hash> as a composite format but doesn't define delimiters, field lengths, or parsing rules. harbour uses <nonce> HARBOUR_DELEGATE <sha256-hex> with space delimiters and expects exactly 3 parts via split(" ").

Risks:

  • If a nonce value contains a space, parsing fails silently
  • Different implementations may use different action type strings (HARBOUR_DELEGATE vs credential.issue vs others)
  • No interoperability between independent implementations

Recommendation: Either:

  1. Define the composite format normatively: <nonce>:<action_type>:<hash> with : delimiter and constraints (nonce = hex, no colons allowed), OR
  2. Remove the composite format from §2 and state that for Simple VPs the nonce MUST equal hash(message) (keeping it simple), with the composite format documented only as an implementation note in harbour

🟡 MEDIUM: Nonce Entropy Minimum Not Specified

Problem: harbour uses secrets.token_hex(4) = 32 bits of entropy. The spec says "unique nonce" (§1, §5) but doesn't specify minimum entropy. 32 bits provides only ~4 billion unique values — insufficient for high-throughput systems and susceptible to birthday attacks for collision-based replay.

Recommendation: Add to §5 Security Considerations:

Message nonces MUST contain at least 128 bits of entropy (for example, 16 random bytes hex-encoded to 32 characters). Implementations using shorter nonces risk replay via birthday collisions.


🟡 MEDIUM: Revocation Check Missing from §4.2 Verification Steps

Problem: §5 says "Verifiers SHOULD check the revocation status of presented VCs" but §4.2 verification steps (the normative checklist) don't include revocation. harbour-credentials confirms this gap: its verify_sd_jwt_vp() does not check revocation — it's left to the caller.

Recommendation: Add step 2b or modify step 2 in §4.2:

Credential freshness (RECOMMENDED): Verifiers SHOULD verify that each presented VC has not been revoked, using the revocation mechanism indicated in the credential (for example, Status List 2021 or OCSP).


🟡 MEDIUM: Evidence Envelope/Storage Format Undefined

Problem: simpulse-id-credentials embeds the evidence VP JWT string inside a VC's evidence array with type: harbour:CredentialEvidence. The spec says evidence is "stored" (§3.2 step 5) but never defines:

  • How evidence should be serialized for storage
  • A type URI for evidence artifacts
  • Whether the evidence VP should be wrapped in another VC or stored standalone

Recommendation: Add an informative §7 "Evidence Packaging" or expand §3.2:

Implementations MAY embed the evidence VP token inside a Verifiable Credential's evidence property (per VC Data Model §5.5) using type EvidenceVerifiablePresentation. Alternatively, the raw VP token MAY be stored standalone with associated metadata (message, timestamp, holder DID).


🟡 MEDIUM: Time-Bounding Guidance Absent

Problem: harbour enforces a 300-second (5 min) default max_age via validate_transaction_data(). The spec says "SHOULD set an expiration" (§5) but provides no guidance on reasonable bounds.

Recommendation: Add informative note to §5:

A typical maximum age for evidence requests is 5–15 minutes. Shorter windows reduce the risk of stale consent but must account for wallet interaction latency.


🟢 LOW: Action Type Registry

Problem: harbour uses HARBOUR_DELEGATE, simpulse uses credential.issue as action types. Without a registry or naming convention, action types may collide or be ambiguous.

Recommendation: Add informative note in §1:

Action types SHOULD use reverse-domain or namespaced identifiers to avoid collisions (for example, envited-x.credential.issue, envited-x.asset.list).


🟢 LOW: Error Model Undefined

Problem: The spec defines verification checks but not how implementations should report failures. harbour raises ValueError with descriptive messages. Lack of normative error conditions makes integration testing across implementations difficult.

Recommendation: Consider adding a table of normative error conditions in §4.2 (for example: INVALID_VP_SIGNATURE, CHALLENGE_MISMATCH, VC_REQUIREMENT_UNMET, HOLDER_MISMATCH).


🟢 LOW: §3.2 Mermaid Diagram Only Shows Simple VP Flow

Problem: The mermaid diagram shows nonce=challenge directly in the OID4VP request. For SD-JWT VPs (the RECOMMENDED format), the flow differs: the nonce is random and the challenge binding happens via transaction_data parameter. This could mislead implementers.

Recommendation: Either add a second diagram for the SD-JWT flow, or add a note below the diagram:

Note: For SD-JWT VPs, the nonce in the OID4VP request carries a random value. The message binding is achieved via the transaction_data request parameter and corresponding KB-JWT transaction_data_hashes.


Summary of Recommended Priority

# Finding Severity Effort
1 TransactionData schema / normative example 🔴 HIGH Medium
2 Composite nonce format specification or removal 🔴 HIGH Low
3 Nonce minimum entropy (128 bits) 🟡 MEDIUM Low
4 Revocation check in §4.2 🟡 MEDIUM Low
5 Evidence envelope/storage format guidance 🟡 MEDIUM Medium
6 Time-bounding guidance (5–15 min) 🟡 MEDIUM Low
7 Action type naming convention 🟢 LOW Low
8 Error model 🟢 LOW Medium
9 SD-JWT flow diagram or note 🟢 LOW Low

Items 1–2 are interoperability-critical. Items 3–6 are security/implementation hardening. Items 7–9 are polish.


Merge Conflict Resolution

The merge conflict (EVES-008 on main vs EVES-009 on this branch in SUMMARY.md and README.md) has been resolved. Both specs are now listed. Line-length lint violations (>300 chars) in §2 and §4.2 have also been fixed.

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.

2 participants