@midnight-ntwrk/credential-compact 0.1.0-rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +201 -0
  3. package/README.md +59 -0
  4. package/dist/compact-build.json +29 -0
  5. package/dist/contract.d.ts +2 -0
  6. package/dist/contract.d.ts.map +1 -0
  7. package/dist/contract.js +2 -0
  8. package/dist/contract.js.map +1 -0
  9. package/dist/credentials/bindings.compact +5 -0
  10. package/dist/credentials/composable.compact +14 -0
  11. package/dist/credentials/holder-bindings.compact +320 -0
  12. package/dist/credentials/issue.compact +91 -0
  13. package/dist/credentials/present.compact +84 -0
  14. package/dist/credentials/proofs.compact +122 -0
  15. package/dist/credentials/protocol-support.compact +7 -0
  16. package/dist/credentials/protocols.compact +125 -0
  17. package/dist/credentials/relations.compact +36 -0
  18. package/dist/credentials/status-bindings.compact +60 -0
  19. package/dist/credentials/types.compact +141 -0
  20. package/dist/credentials/vc-support.compact +8 -0
  21. package/dist/credentials/vc.compact +59 -0
  22. package/dist/credentials/vp.compact +25 -0
  23. package/dist/credentials.compact +20 -0
  24. package/dist/holder-binding/same-holder/composable.compact +179 -0
  25. package/dist/holder-binding/same-holder.compact +180 -0
  26. package/dist/holder-binding/same-holder.d.ts +2 -0
  27. package/dist/holder-binding/same-holder.d.ts.map +1 -0
  28. package/dist/holder-binding/same-holder.js +2 -0
  29. package/dist/holder-binding/same-holder.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +3 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/jubjub.d.ts +3 -0
  35. package/dist/jubjub.d.ts.map +1 -0
  36. package/dist/jubjub.js +6 -0
  37. package/dist/jubjub.js.map +1 -0
  38. package/dist/managed/credentials/contract/index.d.ts +330 -0
  39. package/dist/managed/credentials/contract/index.js +2213 -0
  40. package/dist/managed/credentials/contract/index.js.map +8 -0
  41. package/dist/managed/same-holder/contract/index.d.ts +400 -0
  42. package/dist/managed/same-holder/contract/index.js +2688 -0
  43. package/dist/managed/same-holder/contract/index.js.map +8 -0
  44. package/package.json +113 -0
  45. package/src/credentials/bindings.compact +5 -0
  46. package/src/credentials/composable.compact +14 -0
  47. package/src/credentials/holder-bindings.compact +320 -0
  48. package/src/credentials/issue.compact +91 -0
  49. package/src/credentials/present.compact +84 -0
  50. package/src/credentials/proofs.compact +122 -0
  51. package/src/credentials/protocol-support.compact +7 -0
  52. package/src/credentials/protocols.compact +125 -0
  53. package/src/credentials/relations.compact +36 -0
  54. package/src/credentials/status-bindings.compact +60 -0
  55. package/src/credentials/types.compact +141 -0
  56. package/src/credentials/vc-support.compact +8 -0
  57. package/src/credentials/vc.compact +59 -0
  58. package/src/credentials/vp.compact +25 -0
  59. package/src/credentials.compact +20 -0
  60. package/src/holder-binding/same-holder/composable.compact +179 -0
  61. package/src/holder-binding/same-holder.compact +180 -0
@@ -0,0 +1,91 @@
1
+ // Generic issuance message choreography.
2
+ // Owns offer/request/result envelope alignment only; issuer- and holder-side
3
+ // claim semantics stay in the family layer.
4
+ module Issue<TOfferBody, TRequestBody, TResultBody> {
5
+ export struct OfferMessage {
6
+ envelope: ProtocolMessageEnvelope,
7
+ schema: SchemaRef,
8
+ issuerVerificationMethodRef: VerificationMethodRef,
9
+ holderBindingProfile: HolderBindingProfile,
10
+ features: CredentialProtocolFeatures,
11
+ body: TOfferBody,
12
+ }
13
+
14
+ export struct RequestMessage {
15
+ envelope: ProtocolMessageEnvelope,
16
+ schema: SchemaRef,
17
+ issuerVerificationMethodRef: VerificationMethodRef,
18
+ holderBindingProfile: HolderBindingProfile,
19
+ body: TRequestBody,
20
+ }
21
+
22
+ export struct ResultMessage {
23
+ envelope: ProtocolMessageEnvelope,
24
+ schema: SchemaRef,
25
+ issuerVerificationMethodRef: VerificationMethodRef,
26
+ holderBindingProfile: HolderBindingProfile,
27
+ body: TResultBody,
28
+ }
29
+
30
+ export pure circuit assertValidOfferMessage(
31
+ offer: OfferMessage
32
+ ): [] {
33
+ assertValidProtocolMessageEnvelope(offer.envelope);
34
+ assert(offer.envelope.initialMessage, "Issuance offer must be the initial message");
35
+ assertValidVerificationMethodRef(offer.issuerVerificationMethodRef);
36
+ }
37
+
38
+ export pure circuit assertValidRequestMessage(
39
+ request: RequestMessage
40
+ ): [] {
41
+ assertValidProtocolMessageEnvelope(request.envelope);
42
+ assert(!request.envelope.initialMessage, "Issuance request must be a response message");
43
+ assertValidVerificationMethodRef(request.issuerVerificationMethodRef);
44
+ }
45
+
46
+ export pure circuit assertOfferRequestAlignment(
47
+ offer: OfferMessage,
48
+ request: RequestMessage
49
+ ): [] {
50
+ assertValidOfferMessage(offer);
51
+ assertValidRequestMessage(request);
52
+ assertProtocolResponseEnvelope(offer.envelope, request.envelope);
53
+ assertMatchingSchemaRefs(offer.schema, request.schema);
54
+ assert(
55
+ offer.issuerVerificationMethodRef.didContractAddress == request.issuerVerificationMethodRef.didContractAddress
56
+ && offer.issuerVerificationMethodRef.methodId == request.issuerVerificationMethodRef.methodId,
57
+ "Issuance request issuer verification method does not match the offer"
58
+ );
59
+ assert(
60
+ offer.holderBindingProfile == request.holderBindingProfile,
61
+ "Issuance request holder binding profile does not match the offer"
62
+ );
63
+ }
64
+
65
+ export pure circuit assertValidResultMessage(
66
+ result: ResultMessage
67
+ ): [] {
68
+ assertValidProtocolMessageEnvelope(result.envelope);
69
+ assert(!result.envelope.initialMessage, "Issuance result must be a response message");
70
+ assertValidVerificationMethodRef(result.issuerVerificationMethodRef);
71
+ }
72
+
73
+ export pure circuit assertRequestResultAlignment(
74
+ request: RequestMessage,
75
+ result: ResultMessage
76
+ ): [] {
77
+ assertValidRequestMessage(request);
78
+ assertValidResultMessage(result);
79
+ assertProtocolResponseEnvelope(request.envelope, result.envelope);
80
+ assertMatchingSchemaRefs(request.schema, result.schema);
81
+ assert(
82
+ request.issuerVerificationMethodRef.didContractAddress == result.issuerVerificationMethodRef.didContractAddress
83
+ && request.issuerVerificationMethodRef.methodId == result.issuerVerificationMethodRef.methodId,
84
+ "Issuance result issuer verification method does not match the request"
85
+ );
86
+ assert(
87
+ request.holderBindingProfile == result.holderBindingProfile,
88
+ "Issuance result holder binding profile does not match the request"
89
+ );
90
+ }
91
+ }
@@ -0,0 +1,84 @@
1
+ // Generic presentation message choreography.
2
+ // Owns request/submission/result envelope alignment; the family layer decides
3
+ // what a verifier asked for and what a holder actually proved or disclosed.
4
+ module Present<TRequestBody, TSubmissionBody, TResultBody> {
5
+ export struct RequestMessage {
6
+ envelope: ProtocolMessageEnvelope,
7
+ schema: SchemaRef,
8
+ issuerVerificationMethodRef: VerificationMethodRef,
9
+ holderBindingProfile: HolderBindingProfile,
10
+ features: CredentialProtocolFeatures,
11
+ verifierChallengeHash: Bytes<32>,
12
+ body: TRequestBody,
13
+ }
14
+
15
+ export struct SubmissionMessage {
16
+ envelope: ProtocolMessageEnvelope,
17
+ schema: SchemaRef,
18
+ issuerVerificationMethodRef: VerificationMethodRef,
19
+ holderBindingProfile: HolderBindingProfile,
20
+ challengeHash: Bytes<32>,
21
+ body: TSubmissionBody,
22
+ }
23
+
24
+ export struct ResultMessage {
25
+ envelope: ProtocolMessageEnvelope,
26
+ approved: Boolean,
27
+ body: TResultBody,
28
+ }
29
+
30
+ export pure circuit assertValidRequestMessage(
31
+ request: RequestMessage
32
+ ): [] {
33
+ assertValidProtocolMessageEnvelope(request.envelope);
34
+ assert(request.envelope.initialMessage, "Presentation request must be the initial message");
35
+ assertValidVerificationMethodRef(request.issuerVerificationMethodRef);
36
+ }
37
+
38
+ export pure circuit assertValidSubmissionMessage(
39
+ submission: SubmissionMessage
40
+ ): [] {
41
+ assertValidProtocolMessageEnvelope(submission.envelope);
42
+ assert(!submission.envelope.initialMessage, "Presentation submission must be a response message");
43
+ assertValidVerificationMethodRef(submission.issuerVerificationMethodRef);
44
+ }
45
+
46
+ export pure circuit assertRequestSubmissionAlignment(
47
+ request: RequestMessage,
48
+ submission: SubmissionMessage
49
+ ): [] {
50
+ assertValidRequestMessage(request);
51
+ assertValidSubmissionMessage(submission);
52
+ assertProtocolResponseEnvelope(request.envelope, submission.envelope);
53
+ assertMatchingSchemaRefs(request.schema, submission.schema);
54
+ assert(
55
+ request.issuerVerificationMethodRef.didContractAddress == submission.issuerVerificationMethodRef.didContractAddress
56
+ && request.issuerVerificationMethodRef.methodId == submission.issuerVerificationMethodRef.methodId,
57
+ "Presentation submission issuer verification method does not match the request"
58
+ );
59
+ assert(
60
+ request.holderBindingProfile == submission.holderBindingProfile,
61
+ "Presentation submission holder binding profile does not match the request"
62
+ );
63
+ assert(
64
+ request.verifierChallengeHash == submission.challengeHash,
65
+ "Presentation submission challenge does not match the request challenge"
66
+ );
67
+ }
68
+
69
+ export pure circuit assertValidResultMessage(
70
+ result: ResultMessage
71
+ ): [] {
72
+ assertValidProtocolMessageEnvelope(result.envelope);
73
+ assert(!result.envelope.initialMessage, "Presentation result must be a response message");
74
+ }
75
+
76
+ export pure circuit assertSubmissionResultAlignment(
77
+ submission: SubmissionMessage,
78
+ result: ResultMessage
79
+ ): [] {
80
+ assertValidSubmissionMessage(submission);
81
+ assertValidResultMessage(result);
82
+ assertProtocolResponseEnvelope(submission.envelope, result.envelope);
83
+ }
84
+ }
@@ -0,0 +1,122 @@
1
+ // Context-tagged proof primitives.
2
+ // This file owns signature verification plus the shared proof payload/challenge
3
+ // derivation rules for generic issuance and presentation flows.
4
+ export pure circuit verifySignature(
5
+ pk: JubjubPoint,
6
+ signature: Signature,
7
+ challenge: Field
8
+ ): Boolean {
9
+ const leftSide: JubjubPoint = ecMulGenerator(signature.s);
10
+ const cPk: JubjubPoint = ecMul(pk, challenge);
11
+ const rightSide: JubjubPoint = ecAdd(signature.r, cPk);
12
+ const xMatches: Boolean = jubjubPointX(leftSide) == jubjubPointX(rightSide);
13
+ const yMatches: Boolean = jubjubPointY(leftSide) == jubjubPointY(rightSide);
14
+ assert(xMatches && yMatches, "Signature verification failed");
15
+ return xMatches && yMatches;
16
+ }
17
+
18
+ // NOTE: The proof object does not carry a purpose field. Instead, the verifier
19
+ // chooses the context-specific challenge derivation helper, which gives us
20
+ // explicit domain separation without asking callers to populate a mutable enum.
21
+ // =====================
22
+ // Context tags
23
+ // =====================
24
+ export pure circuit issuanceContextTag(): Bytes<32> {
25
+ return pad(32, "midnight:vc:issuance");
26
+ }
27
+
28
+ export pure circuit presentationContextTag(): Bytes<32> {
29
+ return pad(32, "midnight:vc:presentation");
30
+ }
31
+
32
+ // =====================
33
+ // Internal payload and challenge derivation
34
+ // =====================
35
+ pure circuit proofPayloadRootForContext(
36
+ bodyRoot: Bytes<32>,
37
+ contextTag: Bytes<32>,
38
+ proof: Proof
39
+ ): Bytes<32> {
40
+ return persistentHash<Vector<5, Bytes<32>>>([
41
+ bodyRoot,
42
+ contextTag,
43
+ persistentHash<VerificationMethodRef>(proof.signerVerificationMethodRef),
44
+ upgradeFromTransient(transientHash<Uint<64>>(proof.createdAt)),
45
+ proof.challengeHash
46
+ ]);
47
+ }
48
+
49
+ pure circuit proofChallengeForContext(
50
+ bodyRoot: Bytes<32>,
51
+ contextTag: Bytes<32>,
52
+ proof: Proof
53
+ ): Field {
54
+ return degradeToTransient(persistentHash<Vector<3, Bytes<32>>>([
55
+ proofPayloadRootForContext(bodyRoot, contextTag, proof),
56
+ upgradeFromTransient(transientHash<JubjubPoint>(proof.publicKey)),
57
+ upgradeFromTransient(transientHash<JubjubPoint>(proof.signature.r))
58
+ ]));
59
+ }
60
+
61
+ pure circuit assertValidProofForContext(
62
+ bodyRoot: Bytes<32>,
63
+ contextTag: Bytes<32>,
64
+ proof: Proof
65
+ ): [] {
66
+ assert(
67
+ verifySignature(
68
+ proof.publicKey,
69
+ proof.signature,
70
+ proofChallengeForContext(bodyRoot, contextTag, proof)
71
+ ),
72
+ "Proof verification failed"
73
+ );
74
+ }
75
+
76
+ export pure circuit issuanceProofPayloadRoot(
77
+ bodyRoot: Bytes<32>,
78
+ proof: Proof
79
+ ): Bytes<32> {
80
+ return proofPayloadRootForContext(bodyRoot, issuanceContextTag(), proof);
81
+ }
82
+
83
+ export pure circuit presentationProofPayloadRoot(
84
+ bodyRoot: Bytes<32>,
85
+ proof: Proof
86
+ ): Bytes<32> {
87
+ return proofPayloadRootForContext(bodyRoot, presentationContextTag(), proof);
88
+ }
89
+
90
+ export pure circuit issuanceProofChallenge(
91
+ bodyRoot: Bytes<32>,
92
+ proof: Proof
93
+ ): Field {
94
+ return proofChallengeForContext(bodyRoot, issuanceContextTag(), proof);
95
+ }
96
+
97
+ export pure circuit presentationProofChallenge(
98
+ bodyRoot: Bytes<32>,
99
+ proof: Proof
100
+ ): Field {
101
+ return proofChallengeForContext(bodyRoot, presentationContextTag(), proof);
102
+ }
103
+
104
+ // =====================
105
+ // Public context-specific proof entrypoints
106
+ // =====================
107
+ // NOTE: The proof container is generic, but the verification context is
108
+ // explicit. Callers choose the context helper rather than mutating the proof
109
+ // payload itself.
110
+ export pure circuit assertValidIssuanceContextProof(
111
+ bodyRoot: Bytes<32>,
112
+ proof: Proof
113
+ ): [] {
114
+ assertValidProofForContext(bodyRoot, issuanceContextTag(), proof);
115
+ }
116
+
117
+ export pure circuit assertValidPresentationContextProof(
118
+ bodyRoot: Bytes<32>,
119
+ proof: Proof
120
+ ): [] {
121
+ assertValidProofForContext(bodyRoot, presentationContextTag(), proof);
122
+ }
@@ -0,0 +1,7 @@
1
+ // Shared protocol surface for credential families that model issuance and
2
+ // presentation exchanges over the generic protocol modules.
3
+
4
+ include "./types";
5
+ include "./protocols";
6
+ include "./issue";
7
+ include "./present";
@@ -0,0 +1,125 @@
1
+ // Shared protocol-envelope primitives.
2
+ // These helpers are intentionally generic over issuance and presentation
3
+ // choreography and remain reusable across family- and transport-specific flows.
4
+ export enum HolderBindingProfile {
5
+ explicitDid,
6
+ secretHolder,
7
+ blindedSecretHolder
8
+ }
9
+
10
+ // Compatibility protocol hint. Authoritative family capabilities belong on the
11
+ // schema descriptor; protocol messages may carry this shape only as a transport
12
+ // hint while older adapters migrate.
13
+ export struct CredentialProtocolFeatures {
14
+ supportsSelectiveDisclosure: Boolean,
15
+ supportsPredicateProofs: Boolean,
16
+ supportsVerifierScopedPseudonym: Boolean,
17
+ supportsSameHolderProof: Boolean,
18
+ }
19
+
20
+ export pure circuit protocolFeaturesAsSchemaCapabilities(
21
+ features: CredentialProtocolFeatures
22
+ ): SchemaCapabilities {
23
+ return SchemaCapabilities {
24
+ supportsSelectiveDisclosure: features.supportsSelectiveDisclosure,
25
+ supportsPredicateProofs: features.supportsPredicateProofs,
26
+ supportsVerifierScopedPseudonym: features.supportsVerifierScopedPseudonym,
27
+ supportsSameHolderProof: features.supportsSameHolderProof,
28
+ };
29
+ }
30
+
31
+ export pure circuit assertProtocolFeaturesMatchSchemaCapabilities(
32
+ features: CredentialProtocolFeatures,
33
+ capabilities: SchemaCapabilities
34
+ ): [] {
35
+ assertMatchingSchemaCapabilities(
36
+ protocolFeaturesAsSchemaCapabilities(features),
37
+ capabilities
38
+ );
39
+ }
40
+
41
+ export struct ProtocolMessageEnvelope {
42
+ version: Uint<16>,
43
+ messageId: Bytes<32>,
44
+ threadId: Bytes<32>,
45
+ initialMessage: Boolean,
46
+ respondsToMessageId: Bytes<32>,
47
+ createdAt: Uint<64>,
48
+ hasExpiresAt: Boolean,
49
+ expiresAt: Uint<64>,
50
+ }
51
+
52
+ export pure circuit noProtocolResponseReference(): Bytes<32> {
53
+ return pad(32, "midnight:vc:protocol:none");
54
+ }
55
+
56
+ export pure circuit assertValidVerificationMethodRef(
57
+ verificationMethodRef: VerificationMethodRef
58
+ ): [] {
59
+ assert(
60
+ verificationMethodRef.methodId != pad(32, ""),
61
+ "Verification method reference must be set"
62
+ );
63
+ }
64
+
65
+ export pure circuit assertMatchingSchemaRefs(
66
+ expected: SchemaRef,
67
+ actual: SchemaRef
68
+ ): [] {
69
+ assertValidSchemaRef(expected);
70
+ assertValidSchemaRef(actual);
71
+ assert(
72
+ expected.packageId == actual.packageId
73
+ && expected.schemaId == actual.schemaId
74
+ && expected.majorVersion == actual.majorVersion
75
+ && expected.minorVersion == actual.minorVersion,
76
+ "Schema reference mismatch"
77
+ );
78
+ }
79
+
80
+ export pure circuit assertValidProtocolMessageEnvelope(
81
+ envelope: ProtocolMessageEnvelope
82
+ ): [] {
83
+ const noResponse = noProtocolResponseReference();
84
+ assert(envelope.version == 1, "Protocol message version mismatch");
85
+ assert(envelope.messageId != noResponse, "Protocol message id must be set");
86
+ assert(envelope.threadId != noResponse, "Protocol thread id must be set");
87
+ if (envelope.initialMessage) {
88
+ assert(
89
+ envelope.respondsToMessageId == noResponse,
90
+ "Initial protocol message must not reference a previous message"
91
+ );
92
+ } else {
93
+ assert(
94
+ envelope.respondsToMessageId != noResponse,
95
+ "Protocol response message must reference a previous message"
96
+ );
97
+ }
98
+ if (envelope.hasExpiresAt) {
99
+ assert(
100
+ envelope.expiresAt >= envelope.createdAt,
101
+ "Protocol message expiration must not precede creation"
102
+ );
103
+ }
104
+ }
105
+
106
+ export pure circuit assertProtocolResponseEnvelope(
107
+ requestEnvelope: ProtocolMessageEnvelope,
108
+ responseEnvelope: ProtocolMessageEnvelope
109
+ ): [] {
110
+ assertValidProtocolMessageEnvelope(requestEnvelope);
111
+ assertValidProtocolMessageEnvelope(responseEnvelope);
112
+ assert(!responseEnvelope.initialMessage, "Protocol response must not be initial");
113
+ assert(
114
+ responseEnvelope.threadId == requestEnvelope.threadId,
115
+ "Protocol response thread id does not match the request thread id"
116
+ );
117
+ assert(
118
+ responseEnvelope.respondsToMessageId == requestEnvelope.messageId,
119
+ "Protocol response does not reference the request message id"
120
+ );
121
+ assert(
122
+ responseEnvelope.createdAt >= requestEnvelope.createdAt,
123
+ "Protocol response creation time must not precede the request"
124
+ );
125
+ }
@@ -0,0 +1,36 @@
1
+ // Generic VC↔VP linkage helpers.
2
+ // This module intentionally validates only shared envelope relations; family
3
+ // semantics and holder-binding witness checks stay in higher layers.
4
+ module CredentialPresentationRelations<
5
+ TPublicClaims,
6
+ TClaimCommitments,
7
+ TDisclosures,
8
+ THolderBinding,
9
+ TStatusBinding
10
+ > {
11
+ // The generic parameters are intentionally threaded through the imported
12
+ // `VC<>` and `VP<>` instantiations so the relation helper stays family-typed
13
+ // even though the linkage checks only read shared envelope fields.
14
+ import VC<TPublicClaims, TClaimCommitments, THolderBinding, TStatusBinding> prefix VC_;
15
+ import VP<TDisclosures, THolderBinding> prefix VP_;
16
+
17
+ export pure circuit assertMatchingCredentialPresentation(
18
+ credential: VC_Credential,
19
+ presentation: VP_Presentation
20
+ ): [] {
21
+ // Holder-binding equality is intentionally left to the family layer because
22
+ // different holder-binding profiles require different witness semantics.
23
+ assert(
24
+ presentation.credentialClaimRoot == credential.claimRoot,
25
+ "Presentation must reference the credential claim root"
26
+ );
27
+ assert(
28
+ presentation.issuerVerificationMethodRef.didContractAddress == credential.issuerVerificationMethodRef.didContractAddress,
29
+ "Presentation issuer contract does not match credential issuer"
30
+ );
31
+ assert(
32
+ presentation.issuerVerificationMethodRef.methodId == credential.issuerVerificationMethodRef.methodId,
33
+ "Presentation issuer method reference does not match credential issuer"
34
+ );
35
+ }
36
+ }
@@ -0,0 +1,60 @@
1
+ // VC-side status-binding primitives.
2
+ // Owns only the credential-embedded binding layer; proof protocols and live
3
+ // registry evaluation stay in the status-registry package.
4
+ export struct StatusRegistryRef {
5
+ registryId: Bytes<32>,
6
+ authorityVerificationMethodRef: VerificationMethodRef,
7
+ }
8
+
9
+ export struct NoStatusBinding {}
10
+
11
+ export enum StatusType {
12
+ revocationRegistry
13
+ }
14
+
15
+ export struct RegistryBoundStatusBinding {
16
+ statusType: StatusType,
17
+ registryRef: StatusRegistryRef,
18
+ statusHandleCommitment: Bytes<32>,
19
+ }
20
+
21
+ // NOTE: `StatusType` is explicit even though the current repo only supports the
22
+ // revocation-registry binding mode. Keeping it in the binding preserves an
23
+ // extension seam without overloading registry ids or handle commitment shapes.
24
+ export pure circuit assertValidStatusRegistryRef(
25
+ registryRef: StatusRegistryRef
26
+ ): [] {
27
+ assert(
28
+ registryRef.registryId != pad(32, ""),
29
+ "Status registry id must be set"
30
+ );
31
+ assertValidVerificationMethodRef(registryRef.authorityVerificationMethodRef);
32
+ }
33
+
34
+ export pure circuit assertValidNoStatusBinding(
35
+ binding: NoStatusBinding
36
+ ): [] {
37
+ // NOTE: NoStatusBinding is intentionally empty; this validator stays as a
38
+ // symmetry point with the real binding variants.
39
+ }
40
+
41
+ export pure circuit assertValidRegistryBoundStatusBinding(
42
+ binding: RegistryBoundStatusBinding
43
+ ): [] {
44
+ assert(
45
+ binding.statusType == StatusType.revocationRegistry,
46
+ "Registry-bound status type must be revocationRegistry"
47
+ );
48
+ assertValidStatusRegistryRef(binding.registryRef);
49
+ assert(
50
+ binding.statusHandleCommitment != pad(32, ""),
51
+ "Status handle commitment must be set"
52
+ );
53
+ }
54
+
55
+ export pure circuit registryBoundStatusBindingRoot(
56
+ binding: RegistryBoundStatusBinding
57
+ ): Bytes<32> {
58
+ assertValidRegistryBoundStatusBinding(binding);
59
+ return persistentHash<RegistryBoundStatusBinding>(binding);
60
+ }
@@ -0,0 +1,141 @@
1
+ export struct SchemaRef {
2
+ packageId: Bytes<32>,
3
+ schemaId: Bytes<32>,
4
+ majorVersion: Uint<16>,
5
+ minorVersion: Uint<16>,
6
+ }
7
+
8
+ export struct SchemaCapabilities {
9
+ supportsSelectiveDisclosure: Boolean,
10
+ supportsPredicateProofs: Boolean,
11
+ supportsVerifierScopedPseudonym: Boolean,
12
+ supportsSameHolderProof: Boolean,
13
+ }
14
+
15
+ export struct SchemaFamilyResolutionHint {
16
+ hasResolverHint: Boolean,
17
+ resolverHint: Bytes<32>,
18
+ }
19
+
20
+ export struct SchemaDescriptor {
21
+ schema: SchemaRef,
22
+ capabilities: SchemaCapabilities,
23
+ familyResolutionHint: SchemaFamilyResolutionHint,
24
+ }
25
+
26
+ export pure circuit noSchemaFamilyResolverHint(): Bytes<32> {
27
+ return pad(32, "midnight:vc:schema:no-hint");
28
+ }
29
+
30
+ export pure circuit assertValidSchemaRef(schema: SchemaRef): [] {
31
+ assert(schema.packageId != pad(32, ""), "Schema package id must be set");
32
+ assert(schema.schemaId != pad(32, ""), "Schema id must be set");
33
+ assert(schema.majorVersion > 0, "Schema major version must be positive");
34
+ }
35
+
36
+ export pure circuit assertValidSchemaCapabilities(
37
+ capabilities: SchemaCapabilities
38
+ ): [] {
39
+ // Boolean-only capabilities have no invalid values today. Keep this helper so
40
+ // future non-boolean capability fields get one validation entry point.
41
+ }
42
+
43
+ export pure circuit assertValidSchemaFamilyResolutionHint(
44
+ hint: SchemaFamilyResolutionHint
45
+ ): [] {
46
+ const noHint = noSchemaFamilyResolverHint();
47
+ if (hint.hasResolverHint) {
48
+ assert(hint.resolverHint != noHint, "Schema resolver hint must be set");
49
+ assert(
50
+ hint.resolverHint != pad(32, ""),
51
+ "Schema resolver hint must not be empty"
52
+ );
53
+ } else {
54
+ assert(
55
+ hint.resolverHint == noHint,
56
+ "Absent schema resolver hint must use the no-hint sentinel"
57
+ );
58
+ }
59
+ }
60
+
61
+ export pure circuit assertValidSchemaDescriptor(
62
+ descriptor: SchemaDescriptor
63
+ ): [] {
64
+ assertValidSchemaRef(descriptor.schema);
65
+ assertValidSchemaCapabilities(descriptor.capabilities);
66
+ assertValidSchemaFamilyResolutionHint(descriptor.familyResolutionHint);
67
+ }
68
+
69
+ export pure circuit assertMatchingSchemaCapabilities(
70
+ expected: SchemaCapabilities,
71
+ actual: SchemaCapabilities
72
+ ): [] {
73
+ assert(
74
+ expected.supportsSelectiveDisclosure == actual.supportsSelectiveDisclosure
75
+ && expected.supportsPredicateProofs == actual.supportsPredicateProofs
76
+ && expected.supportsVerifierScopedPseudonym == actual.supportsVerifierScopedPseudonym
77
+ && expected.supportsSameHolderProof == actual.supportsSameHolderProof,
78
+ "Schema capabilities mismatch"
79
+ );
80
+ }
81
+
82
+ export struct VerificationMethodRef {
83
+ didContractAddress: ContractAddress,
84
+ methodId: Bytes<32>,
85
+ }
86
+
87
+ export struct NoPublicClaims {
88
+ }
89
+
90
+ export struct NoClaimCommitments {
91
+ }
92
+
93
+ export struct Signature {
94
+ r: JubjubPoint,
95
+ s: Field,
96
+ }
97
+
98
+ export struct ExplicitHolderBinding {
99
+ holderVerificationMethodRef: VerificationMethodRef,
100
+ }
101
+
102
+ export struct JubjubHolderBinding {
103
+ // Legacy compatibility/minimal holder binding.
104
+ // This binds the VC/VP to a specific Jubjub public key, but it is not the
105
+ // recommended public profile for new DID-shaped flows.
106
+ // Proof-of-possession still depends on validating the presentation Proof
107
+ // alongside this binding.
108
+ holderPublicKey: JubjubPoint,
109
+ }
110
+
111
+ export struct OffchainMidnightHolderBinding {
112
+ // Preferred lightweight DID-shaped binding for offchain/prototype flows.
113
+ // This adds DID-shaped metadata for prototype offchain Midnight DID flows.
114
+ // It does not by itself prove that holderPublicKey is the key published in
115
+ // the offchain DID state; callers still need the surrounding DID semantics.
116
+ holderDidStateHash: Bytes<32>,
117
+ holderMethodId: Bytes<32>,
118
+ holderPublicKey: JubjubPoint,
119
+ }
120
+
121
+ export struct SecretHolderBinding {
122
+ holderSecretCommitment: Bytes<32>,
123
+ requestChallengeResponse: Bytes<32>,
124
+ }
125
+
126
+ export struct BlindedSecretHolderBinding {
127
+ blindedHolderSecretCommitment: Bytes<32>,
128
+ issuerNonce: Bytes<32>,
129
+ requestChallengeResponse: Bytes<32>,
130
+ }
131
+
132
+ // Proof is kept separate from the VC/VP body on purpose.
133
+ // The body is the semantic payload; the proof is the cryptographic artifact
134
+ // over that payload plus interaction context.
135
+ export struct Proof {
136
+ signerVerificationMethodRef: VerificationMethodRef,
137
+ createdAt: Uint<64>,
138
+ challengeHash: Bytes<32>,
139
+ publicKey: JubjubPoint,
140
+ signature: Signature,
141
+ }
@@ -0,0 +1,8 @@
1
+ // Shared VC surface for credential families that need envelope types,
2
+ // proof helpers, and proof-bound validation rules.
3
+
4
+ include "./types";
5
+ include "./proofs";
6
+ include "./vc";
7
+ include "./vp";
8
+ include "./relations";