@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.
- package/CHANGELOG.md +10 -0
- package/LICENSE +201 -0
- package/README.md +59 -0
- package/dist/compact-build.json +29 -0
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -0
- package/dist/credentials/bindings.compact +5 -0
- package/dist/credentials/composable.compact +14 -0
- package/dist/credentials/holder-bindings.compact +320 -0
- package/dist/credentials/issue.compact +91 -0
- package/dist/credentials/present.compact +84 -0
- package/dist/credentials/proofs.compact +122 -0
- package/dist/credentials/protocol-support.compact +7 -0
- package/dist/credentials/protocols.compact +125 -0
- package/dist/credentials/relations.compact +36 -0
- package/dist/credentials/status-bindings.compact +60 -0
- package/dist/credentials/types.compact +141 -0
- package/dist/credentials/vc-support.compact +8 -0
- package/dist/credentials/vc.compact +59 -0
- package/dist/credentials/vp.compact +25 -0
- package/dist/credentials.compact +20 -0
- package/dist/holder-binding/same-holder/composable.compact +179 -0
- package/dist/holder-binding/same-holder.compact +180 -0
- package/dist/holder-binding/same-holder.d.ts +2 -0
- package/dist/holder-binding/same-holder.d.ts.map +1 -0
- package/dist/holder-binding/same-holder.js +2 -0
- package/dist/holder-binding/same-holder.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/jubjub.d.ts +3 -0
- package/dist/jubjub.d.ts.map +1 -0
- package/dist/jubjub.js +6 -0
- package/dist/jubjub.js.map +1 -0
- package/dist/managed/credentials/contract/index.d.ts +330 -0
- package/dist/managed/credentials/contract/index.js +2213 -0
- package/dist/managed/credentials/contract/index.js.map +8 -0
- package/dist/managed/same-holder/contract/index.d.ts +400 -0
- package/dist/managed/same-holder/contract/index.js +2688 -0
- package/dist/managed/same-holder/contract/index.js.map +8 -0
- package/package.json +113 -0
- package/src/credentials/bindings.compact +5 -0
- package/src/credentials/composable.compact +14 -0
- package/src/credentials/holder-bindings.compact +320 -0
- package/src/credentials/issue.compact +91 -0
- package/src/credentials/present.compact +84 -0
- package/src/credentials/proofs.compact +122 -0
- package/src/credentials/protocol-support.compact +7 -0
- package/src/credentials/protocols.compact +125 -0
- package/src/credentials/relations.compact +36 -0
- package/src/credentials/status-bindings.compact +60 -0
- package/src/credentials/types.compact +141 -0
- package/src/credentials/vc-support.compact +8 -0
- package/src/credentials/vc.compact +59 -0
- package/src/credentials/vp.compact +25 -0
- package/src/credentials.compact +20 -0
- package/src/holder-binding/same-holder/composable.compact +179 -0
- 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,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
|
+
}
|