@midnight-ntwrk/credential-compact 0.1.0-rc2 → 0.2.0-rc1
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 +43 -1
- package/README.md +47 -27
- package/dist/compact-build.json +6 -13
- package/dist/compact-value-codec.d.ts +14 -0
- package/dist/compact-value-codec.d.ts.map +1 -0
- package/dist/compact-value-codec.js +135 -0
- package/dist/compact-value-codec.js.map +1 -0
- package/dist/credentials/composable.compact +1 -3
- package/dist/credentials/holder-bindings.compact +11 -289
- package/dist/credentials/proofs.compact +85 -0
- package/dist/credentials/relations.compact +3 -2
- package/dist/credentials/signer-authorization.compact +246 -0
- package/dist/credentials/status-bindings.compact +0 -19
- package/dist/credentials/types.compact +23 -93
- package/dist/credentials/vc.compact +24 -3
- package/dist/credentials.compact +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/managed/credentials/contract/index.d.ts +102 -196
- package/dist/managed/credentials/contract/index.js +746 -1342
- package/dist/managed/credentials/contract/index.js.map +2 -2
- package/package.json +12 -53
- package/dist/contract.d.ts +0 -2
- package/dist/contract.d.ts.map +0 -1
- package/dist/contract.js +0 -2
- package/dist/contract.js.map +0 -1
- package/dist/credentials/bindings.compact +0 -5
- package/dist/credentials/issue.compact +0 -91
- package/dist/credentials/present.compact +0 -84
- package/dist/credentials/protocol-support.compact +0 -7
- package/dist/credentials/protocols.compact +0 -125
- package/dist/credentials/vc-support.compact +0 -8
- package/dist/holder-binding/same-holder/composable.compact +0 -179
- package/dist/holder-binding/same-holder.compact +0 -180
- package/dist/holder-binding/same-holder.d.ts +0 -2
- package/dist/holder-binding/same-holder.d.ts.map +0 -1
- package/dist/holder-binding/same-holder.js +0 -2
- package/dist/holder-binding/same-holder.js.map +0 -1
- package/dist/jubjub.d.ts +0 -3
- package/dist/jubjub.d.ts.map +0 -1
- package/dist/jubjub.js +0 -6
- package/dist/jubjub.js.map +0 -1
- package/dist/managed/same-holder/contract/index.d.ts +0 -400
- package/dist/managed/same-holder/contract/index.js +0 -2688
- package/dist/managed/same-holder/contract/index.js.map +0 -8
- package/src/credentials/bindings.compact +0 -5
- package/src/credentials/composable.compact +0 -14
- package/src/credentials/holder-bindings.compact +0 -320
- package/src/credentials/issue.compact +0 -91
- package/src/credentials/present.compact +0 -84
- package/src/credentials/proofs.compact +0 -122
- package/src/credentials/protocol-support.compact +0 -7
- package/src/credentials/protocols.compact +0 -125
- package/src/credentials/relations.compact +0 -36
- package/src/credentials/status-bindings.compact +0 -60
- package/src/credentials/types.compact +0 -141
- package/src/credentials/vc-support.compact +0 -8
- package/src/credentials/vc.compact +0 -59
- package/src/credentials/vp.compact +0 -25
- package/src/credentials.compact +0 -20
- package/src/holder-binding/same-holder/composable.compact +0 -179
- package/src/holder-binding/same-holder.compact +0 -180
|
@@ -1,30 +1,20 @@
|
|
|
1
|
-
//
|
|
2
|
-
// This file groups the reusable binding checks for explicit DID, Jubjub,
|
|
3
|
-
// offchain DID, secret-holder, and blinded-secret-holder profiles.
|
|
4
|
-
// =====================
|
|
5
|
-
// Explicit and Jubjub-backed holder bindings
|
|
6
|
-
// =====================
|
|
1
|
+
// Explicit holder-binding primitives.
|
|
7
2
|
export pure circuit assertValidExplicitHolderBinding(
|
|
8
3
|
binding: ExplicitHolderBinding
|
|
9
4
|
): [] {
|
|
10
|
-
|
|
11
|
-
binding.holderVerificationMethodRef.didContractAddress.bytes != pad(32, ""),
|
|
12
|
-
"Explicit holder binding DID contract address must be set"
|
|
13
|
-
);
|
|
14
|
-
assert(
|
|
15
|
-
binding.holderVerificationMethodRef.methodId != pad(32, ""),
|
|
16
|
-
"Explicit holder binding method reference must be set"
|
|
17
|
-
);
|
|
5
|
+
assertValidVerificationMethodRef(binding.holderVerificationMethodRef);
|
|
18
6
|
}
|
|
19
7
|
|
|
20
8
|
export pure circuit assertMatchingExplicitHolderBindings(
|
|
21
9
|
credentialBinding: ExplicitHolderBinding,
|
|
22
10
|
presentationBinding: ExplicitHolderBinding
|
|
23
11
|
): [] {
|
|
12
|
+
assertValidExplicitHolderBinding(credentialBinding);
|
|
13
|
+
assertValidExplicitHolderBinding(presentationBinding);
|
|
24
14
|
assert(
|
|
25
|
-
presentationBinding.holderVerificationMethodRef.
|
|
26
|
-
== credentialBinding.holderVerificationMethodRef.
|
|
27
|
-
"Presentation holder
|
|
15
|
+
presentationBinding.holderVerificationMethodRef.controllerAddress
|
|
16
|
+
== credentialBinding.holderVerificationMethodRef.controllerAddress,
|
|
17
|
+
"Presentation holder controller does not match credential holder binding"
|
|
28
18
|
);
|
|
29
19
|
assert(
|
|
30
20
|
presentationBinding.holderVerificationMethodRef.methodId
|
|
@@ -37,9 +27,11 @@ export pure circuit assertProofMatchesExplicitHolderBinding(
|
|
|
37
27
|
binding: ExplicitHolderBinding,
|
|
38
28
|
presentationProof: Proof
|
|
39
29
|
): [] {
|
|
30
|
+
assertValidExplicitHolderBinding(binding);
|
|
31
|
+
assertValidVerificationMethodRef(presentationProof.signerVerificationMethodRef);
|
|
40
32
|
assert(
|
|
41
|
-
binding.holderVerificationMethodRef.
|
|
42
|
-
== presentationProof.signerVerificationMethodRef.
|
|
33
|
+
binding.holderVerificationMethodRef.controllerAddress
|
|
34
|
+
== presentationProof.signerVerificationMethodRef.controllerAddress,
|
|
43
35
|
"Presentation proof signer must match holder binding"
|
|
44
36
|
);
|
|
45
37
|
assert(
|
|
@@ -48,273 +40,3 @@ export pure circuit assertProofMatchesExplicitHolderBinding(
|
|
|
48
40
|
"Presentation proof signer method reference must match holder binding"
|
|
49
41
|
);
|
|
50
42
|
}
|
|
51
|
-
|
|
52
|
-
export pure circuit assertValidJubjubHolderBinding(
|
|
53
|
-
binding: JubjubHolderBinding
|
|
54
|
-
): [] {
|
|
55
|
-
// This is only a sentinel check that the binding is populated.
|
|
56
|
-
// The helper remains useful for the legacy compatibility profile and as a
|
|
57
|
-
// building block inside richer DID-shaped bindings.
|
|
58
|
-
// Callers still need assertValidPresentationContextProof(...) or an
|
|
59
|
-
// equivalent proof verification path to establish proof-of-possession.
|
|
60
|
-
assert(
|
|
61
|
-
jubjubPointX(binding.holderPublicKey) != 0 as Field ||
|
|
62
|
-
jubjubPointY(binding.holderPublicKey) != 0 as Field,
|
|
63
|
-
"Jubjub holder binding public key must be set"
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export pure circuit assertMatchingJubjubHolderBindings(
|
|
68
|
-
credentialBinding: JubjubHolderBinding,
|
|
69
|
-
presentationBinding: JubjubHolderBinding
|
|
70
|
-
): [] {
|
|
71
|
-
assert(
|
|
72
|
-
jubjubPointX(presentationBinding.holderPublicKey)
|
|
73
|
-
== jubjubPointX(credentialBinding.holderPublicKey) &&
|
|
74
|
-
jubjubPointY(presentationBinding.holderPublicKey)
|
|
75
|
-
== jubjubPointY(credentialBinding.holderPublicKey),
|
|
76
|
-
"Presentation Jubjub holder key does not match the credential holder binding"
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export pure circuit assertProofMatchesJubjubHolderBinding(
|
|
81
|
-
binding: JubjubHolderBinding,
|
|
82
|
-
presentationProof: Proof
|
|
83
|
-
): [] {
|
|
84
|
-
assert(
|
|
85
|
-
jubjubPointX(binding.holderPublicKey)
|
|
86
|
-
== jubjubPointX(presentationProof.publicKey) &&
|
|
87
|
-
jubjubPointY(binding.holderPublicKey)
|
|
88
|
-
== jubjubPointY(presentationProof.publicKey),
|
|
89
|
-
"Presentation proof public key must match the Jubjub holder binding"
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export pure circuit assertValidOffchainMidnightHolderBinding(
|
|
94
|
-
binding: OffchainMidnightHolderBinding
|
|
95
|
-
): [] {
|
|
96
|
-
// This validates presence and consistency only for the preferred lightweight
|
|
97
|
-
// DID-shaped offchain profile.
|
|
98
|
-
// It does not prove that holderPublicKey is the verification method
|
|
99
|
-
// published under holderMethodId in the referenced offchain DID state.
|
|
100
|
-
assert(
|
|
101
|
-
binding.holderDidStateHash != pad(32, ""),
|
|
102
|
-
"Offchain Midnight holder state hash must be set"
|
|
103
|
-
);
|
|
104
|
-
assert(
|
|
105
|
-
binding.holderMethodId != pad(32, ""),
|
|
106
|
-
"Offchain Midnight holder method id must be set"
|
|
107
|
-
);
|
|
108
|
-
const jubjubBinding: JubjubHolderBinding = JubjubHolderBinding {
|
|
109
|
-
holderPublicKey: binding.holderPublicKey
|
|
110
|
-
};
|
|
111
|
-
assertValidJubjubHolderBinding(jubjubBinding);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export pure circuit assertMatchingOffchainMidnightHolderBindings(
|
|
115
|
-
credentialBinding: OffchainMidnightHolderBinding,
|
|
116
|
-
presentationBinding: OffchainMidnightHolderBinding
|
|
117
|
-
): [] {
|
|
118
|
-
assert(
|
|
119
|
-
presentationBinding.holderDidStateHash
|
|
120
|
-
== credentialBinding.holderDidStateHash,
|
|
121
|
-
"Offchain Midnight holder state hash does not match the credential holder binding"
|
|
122
|
-
);
|
|
123
|
-
assert(
|
|
124
|
-
presentationBinding.holderMethodId
|
|
125
|
-
== credentialBinding.holderMethodId,
|
|
126
|
-
"Offchain Midnight holder method id does not match the credential holder binding"
|
|
127
|
-
);
|
|
128
|
-
const credentialJubjubBinding: JubjubHolderBinding = JubjubHolderBinding {
|
|
129
|
-
holderPublicKey: credentialBinding.holderPublicKey
|
|
130
|
-
};
|
|
131
|
-
const presentationJubjubBinding: JubjubHolderBinding = JubjubHolderBinding {
|
|
132
|
-
holderPublicKey: presentationBinding.holderPublicKey
|
|
133
|
-
};
|
|
134
|
-
assertMatchingJubjubHolderBindings(
|
|
135
|
-
credentialJubjubBinding,
|
|
136
|
-
presentationJubjubBinding
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export pure circuit assertProofMatchesOffchainMidnightHolderBinding(
|
|
141
|
-
binding: OffchainMidnightHolderBinding,
|
|
142
|
-
presentationProof: Proof
|
|
143
|
-
): [] {
|
|
144
|
-
const jubjubBinding: JubjubHolderBinding = JubjubHolderBinding {
|
|
145
|
-
holderPublicKey: binding.holderPublicKey
|
|
146
|
-
};
|
|
147
|
-
assertProofMatchesJubjubHolderBinding(jubjubBinding, presentationProof);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// =====================
|
|
151
|
-
// Secret-holder bindings and derived pseudonyms
|
|
152
|
-
// =====================
|
|
153
|
-
export pure circuit noSecretHolderChallengeResponse(): Bytes<32> {
|
|
154
|
-
return pad(32, "midnight:vc:no-holder-response");
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export pure circuit secretHolderBindingCommitment(
|
|
158
|
-
holderSecret: Bytes<32>,
|
|
159
|
-
opening: Bytes<32>
|
|
160
|
-
): Bytes<32> {
|
|
161
|
-
return persistentCommit<Bytes<32>>(holderSecret, opening);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export pure circuit secretHolderBindingChallengeResponse(
|
|
165
|
-
holderSecret: Bytes<32>,
|
|
166
|
-
verifierChallengeHash: Bytes<32>
|
|
167
|
-
): Bytes<32> {
|
|
168
|
-
return persistentHash<Vector<3, Bytes<32>>>([
|
|
169
|
-
pad(32, "midnight:vc:holder-chall"),
|
|
170
|
-
holderSecret,
|
|
171
|
-
verifierChallengeHash
|
|
172
|
-
]);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export pure circuit verifierScopedPseudonym(
|
|
176
|
-
holderSecret: Bytes<32>,
|
|
177
|
-
verifierDomainHash: Bytes<32>
|
|
178
|
-
): Bytes<32> {
|
|
179
|
-
return persistentHash<Vector<3, Bytes<32>>>([
|
|
180
|
-
pad(32, "midnight:vc:holder-pseudonym"),
|
|
181
|
-
holderSecret,
|
|
182
|
-
verifierDomainHash
|
|
183
|
-
]);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export pure circuit assertVerifierScopedPseudonym(
|
|
187
|
-
pseudonym: Bytes<32>,
|
|
188
|
-
holderSecret: Bytes<32>,
|
|
189
|
-
verifierDomainHash: Bytes<32>
|
|
190
|
-
): [] {
|
|
191
|
-
assert(
|
|
192
|
-
pseudonym == verifierScopedPseudonym(holderSecret, verifierDomainHash),
|
|
193
|
-
"Verifier-scoped pseudonym does not match the holder secret and verifier domain"
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export pure circuit blindedSecretHolderCommitment(
|
|
198
|
-
holderSecretCommitment: Bytes<32>,
|
|
199
|
-
issuerNonce: Bytes<32>,
|
|
200
|
-
blindingFactor: Bytes<32>
|
|
201
|
-
): Bytes<32> {
|
|
202
|
-
// NOTE: This remains a hash-based compatibility commitment today. The
|
|
203
|
-
// breaking `persistentCommit` migration is tracked separately and should not
|
|
204
|
-
// be rolled in silently with source-style or purity cleanups.
|
|
205
|
-
return persistentHash<Vector<4, Bytes<32>>>([
|
|
206
|
-
pad(32, "midnight:vc:blind-holder"),
|
|
207
|
-
holderSecretCommitment,
|
|
208
|
-
issuerNonce,
|
|
209
|
-
blindingFactor
|
|
210
|
-
]);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export pure circuit assertValidSecretHolderCredentialBinding(
|
|
214
|
-
binding: SecretHolderBinding
|
|
215
|
-
): [] {
|
|
216
|
-
assert(
|
|
217
|
-
binding.requestChallengeResponse == noSecretHolderChallengeResponse(),
|
|
218
|
-
"Credential secret holder binding must not embed a request challenge response"
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export pure circuit assertValidSecretHolderPresentationBinding(
|
|
223
|
-
binding: SecretHolderBinding
|
|
224
|
-
): [] {
|
|
225
|
-
assert(
|
|
226
|
-
binding.requestChallengeResponse != noSecretHolderChallengeResponse(),
|
|
227
|
-
"Presentation secret holder binding must include a request challenge response"
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export pure circuit assertMatchingSecretHolderBindings(
|
|
232
|
-
credentialBinding: SecretHolderBinding,
|
|
233
|
-
presentationBinding: SecretHolderBinding
|
|
234
|
-
): [] {
|
|
235
|
-
assert(
|
|
236
|
-
credentialBinding.holderSecretCommitment == presentationBinding.holderSecretCommitment,
|
|
237
|
-
"Presentation holder secret commitment does not match the credential holder binding"
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export pure circuit assertValidBlindedSecretHolderCredentialBinding(
|
|
242
|
-
binding: BlindedSecretHolderBinding
|
|
243
|
-
): [] {
|
|
244
|
-
assert(
|
|
245
|
-
binding.requestChallengeResponse == noSecretHolderChallengeResponse(),
|
|
246
|
-
"Credential blinded holder binding must not embed a request challenge response"
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export pure circuit assertValidBlindedSecretHolderPresentationBinding(
|
|
251
|
-
binding: BlindedSecretHolderBinding
|
|
252
|
-
): [] {
|
|
253
|
-
assert(
|
|
254
|
-
binding.requestChallengeResponse != noSecretHolderChallengeResponse(),
|
|
255
|
-
"Presentation blinded holder binding must include a request challenge response"
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export pure circuit assertMatchingBlindedSecretHolderBindings(
|
|
260
|
-
credentialBinding: BlindedSecretHolderBinding,
|
|
261
|
-
presentationBinding: BlindedSecretHolderBinding
|
|
262
|
-
): [] {
|
|
263
|
-
assert(
|
|
264
|
-
credentialBinding.blindedHolderSecretCommitment
|
|
265
|
-
== presentationBinding.blindedHolderSecretCommitment,
|
|
266
|
-
"Presentation blinded holder commitment does not match the credential holder binding"
|
|
267
|
-
);
|
|
268
|
-
assert(
|
|
269
|
-
credentialBinding.issuerNonce == presentationBinding.issuerNonce,
|
|
270
|
-
"Presentation issuer nonce does not match the credential holder binding"
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// =====================
|
|
275
|
-
// Witness validation and recomputation helpers
|
|
276
|
-
// =====================
|
|
277
|
-
// NOTE: these helpers validate caller-supplied witness material passed as
|
|
278
|
-
// ordinary parameters. They do not perform Compact `witness` reads themselves.
|
|
279
|
-
export pure circuit assertSecretHolderBindingWitness(
|
|
280
|
-
binding: SecretHolderBinding,
|
|
281
|
-
verifierChallengeHash: Bytes<32>,
|
|
282
|
-
holderSecret: Bytes<32>,
|
|
283
|
-
opening: Bytes<32>
|
|
284
|
-
): [] {
|
|
285
|
-
assert(
|
|
286
|
-
secretHolderBindingCommitment(holderSecret, opening) == binding.holderSecretCommitment,
|
|
287
|
-
"Holder secret witness does not match the holder-binding commitment"
|
|
288
|
-
);
|
|
289
|
-
assert(
|
|
290
|
-
secretHolderBindingChallengeResponse(holderSecret, verifierChallengeHash)
|
|
291
|
-
== binding.requestChallengeResponse,
|
|
292
|
-
"Holder secret challenge response does not match the verifier challenge"
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export pure circuit assertBlindedSecretHolderBindingWitness(
|
|
297
|
-
binding: BlindedSecretHolderBinding,
|
|
298
|
-
verifierChallengeHash: Bytes<32>,
|
|
299
|
-
holderSecret: Bytes<32>,
|
|
300
|
-
opening: Bytes<32>,
|
|
301
|
-
blindingFactor: Bytes<32>
|
|
302
|
-
): [] {
|
|
303
|
-
const holderCommitment: Bytes<32> = secretHolderBindingCommitment(
|
|
304
|
-
holderSecret,
|
|
305
|
-
opening
|
|
306
|
-
);
|
|
307
|
-
assert(
|
|
308
|
-
blindedSecretHolderCommitment(
|
|
309
|
-
holderCommitment,
|
|
310
|
-
binding.issuerNonce,
|
|
311
|
-
blindingFactor
|
|
312
|
-
) == binding.blindedHolderSecretCommitment,
|
|
313
|
-
"Blinded holder commitment does not match the hidden holder secret witness"
|
|
314
|
-
);
|
|
315
|
-
assert(
|
|
316
|
-
secretHolderBindingChallengeResponse(holderSecret, verifierChallengeHash)
|
|
317
|
-
== binding.requestChallengeResponse,
|
|
318
|
-
"Blinded holder challenge response does not match the verifier challenge"
|
|
319
|
-
);
|
|
320
|
-
}
|
|
@@ -1,11 +1,43 @@
|
|
|
1
1
|
// Context-tagged proof primitives.
|
|
2
2
|
// This file owns signature verification plus the shared proof payload/challenge
|
|
3
3
|
// derivation rules for generic issuance and presentation flows.
|
|
4
|
+
pure circuit assertMatchingJubjubPoints(
|
|
5
|
+
expected: JubjubPoint,
|
|
6
|
+
actual: JubjubPoint
|
|
7
|
+
): [] {
|
|
8
|
+
assert(
|
|
9
|
+
jubjubPointX(expected) == jubjubPointX(actual)
|
|
10
|
+
&& jubjubPointY(expected) == jubjubPointY(actual),
|
|
11
|
+
"Jubjub points do not match"
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pure circuit assertUsableJubjubPoint(point: JubjubPoint): [] {
|
|
16
|
+
assert(
|
|
17
|
+
jubjubPointX(point) != 0 || jubjubPointY(point) != 1,
|
|
18
|
+
"Jubjub point must not be the identity point"
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// (q + 1) / 8 followed by the cofactor projects onto the prime-order
|
|
22
|
+
// subgroup. Equality rejects a torsion component; ecMul constrains the input
|
|
23
|
+
// to the curve.
|
|
24
|
+
const subgroupProjection: JubjubPoint = ecMul(
|
|
25
|
+
ecMul(
|
|
26
|
+
point,
|
|
27
|
+
819310549611346726241370945440405716213240158234039660170669895299022906775 as Field
|
|
28
|
+
),
|
|
29
|
+
8
|
|
30
|
+
);
|
|
31
|
+
assertMatchingJubjubPoints(point, subgroupProjection);
|
|
32
|
+
}
|
|
33
|
+
|
|
4
34
|
export pure circuit verifySignature(
|
|
5
35
|
pk: JubjubPoint,
|
|
6
36
|
signature: Signature,
|
|
7
37
|
challenge: Field
|
|
8
38
|
): Boolean {
|
|
39
|
+
assertUsableJubjubPoint(pk);
|
|
40
|
+
assertUsableJubjubPoint(signature.r);
|
|
9
41
|
const leftSide: JubjubPoint = ecMulGenerator(signature.s);
|
|
10
42
|
const cPk: JubjubPoint = ecMul(pk, challenge);
|
|
11
43
|
const rightSide: JubjubPoint = ecAdd(signature.r, cPk);
|
|
@@ -29,6 +61,14 @@ export pure circuit presentationContextTag(): Bytes<32> {
|
|
|
29
61
|
return pad(32, "midnight:vc:presentation");
|
|
30
62
|
}
|
|
31
63
|
|
|
64
|
+
export pure circuit signerAuthorizationContextTag(): Bytes<32> {
|
|
65
|
+
return pad(32, "midnight:vc:signer-auth:v1");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export pure circuit verifierRequestContextTag(): Bytes<32> {
|
|
69
|
+
return pad(32, "midnight:vc:verifier-req:v1");
|
|
70
|
+
}
|
|
71
|
+
|
|
32
72
|
// =====================
|
|
33
73
|
// Internal payload and challenge derivation
|
|
34
74
|
// =====================
|
|
@@ -63,6 +103,7 @@ pure circuit assertValidProofForContext(
|
|
|
63
103
|
contextTag: Bytes<32>,
|
|
64
104
|
proof: Proof
|
|
65
105
|
): [] {
|
|
106
|
+
assertValidVerificationMethodRef(proof.signerVerificationMethodRef);
|
|
66
107
|
assert(
|
|
67
108
|
verifySignature(
|
|
68
109
|
proof.publicKey,
|
|
@@ -101,6 +142,28 @@ export pure circuit presentationProofChallenge(
|
|
|
101
142
|
return proofChallengeForContext(bodyRoot, presentationContextTag(), proof);
|
|
102
143
|
}
|
|
103
144
|
|
|
145
|
+
export pure circuit signerAuthorizationProofChallenge(
|
|
146
|
+
bodyRoot: Bytes<32>,
|
|
147
|
+
proof: Proof
|
|
148
|
+
): Field {
|
|
149
|
+
return proofChallengeForContext(
|
|
150
|
+
bodyRoot,
|
|
151
|
+
signerAuthorizationContextTag(),
|
|
152
|
+
proof
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export pure circuit verifierRequestProofChallenge(
|
|
157
|
+
requestScopeCommitment: Bytes<32>,
|
|
158
|
+
proof: Proof
|
|
159
|
+
): Field {
|
|
160
|
+
return proofChallengeForContext(
|
|
161
|
+
requestScopeCommitment,
|
|
162
|
+
verifierRequestContextTag(),
|
|
163
|
+
proof
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
104
167
|
// =====================
|
|
105
168
|
// Public context-specific proof entrypoints
|
|
106
169
|
// =====================
|
|
@@ -120,3 +183,25 @@ export pure circuit assertValidPresentationContextProof(
|
|
|
120
183
|
): [] {
|
|
121
184
|
assertValidProofForContext(bodyRoot, presentationContextTag(), proof);
|
|
122
185
|
}
|
|
186
|
+
|
|
187
|
+
export pure circuit assertValidSignerAuthorizationContextProof(
|
|
188
|
+
bodyRoot: Bytes<32>,
|
|
189
|
+
proof: Proof
|
|
190
|
+
): [] {
|
|
191
|
+
assertValidProofForContext(
|
|
192
|
+
bodyRoot,
|
|
193
|
+
signerAuthorizationContextTag(),
|
|
194
|
+
proof
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export pure circuit assertValidVerifierRequestContextProof(
|
|
199
|
+
requestScopeCommitment: Bytes<32>,
|
|
200
|
+
proof: Proof
|
|
201
|
+
): [] {
|
|
202
|
+
assertValidProofForContext(
|
|
203
|
+
requestScopeCommitment,
|
|
204
|
+
verifierRequestContextTag(),
|
|
205
|
+
proof
|
|
206
|
+
);
|
|
207
|
+
}
|
|
@@ -20,13 +20,14 @@ module CredentialPresentationRelations<
|
|
|
20
20
|
): [] {
|
|
21
21
|
// Holder-binding equality is intentionally left to the family layer because
|
|
22
22
|
// different holder-binding profiles require different witness semantics.
|
|
23
|
+
assertMatchingSchemaRefs(credential.schema, presentation.schema);
|
|
23
24
|
assert(
|
|
24
25
|
presentation.credentialClaimRoot == credential.claimRoot,
|
|
25
26
|
"Presentation must reference the credential claim root"
|
|
26
27
|
);
|
|
27
28
|
assert(
|
|
28
|
-
presentation.issuerVerificationMethodRef.
|
|
29
|
-
"Presentation issuer
|
|
29
|
+
presentation.issuerVerificationMethodRef.controllerAddress == credential.issuerVerificationMethodRef.controllerAddress,
|
|
30
|
+
"Presentation issuer controller does not match credential issuer"
|
|
30
31
|
);
|
|
31
32
|
assert(
|
|
32
33
|
presentation.issuerVerificationMethodRef.methodId == credential.issuerVerificationMethodRef.methodId,
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Source-neutral signer authorization primitives.
|
|
2
|
+
// Applications may install descriptors through local governance or verify a
|
|
3
|
+
// domain-separated authority proof before materializing the same descriptor.
|
|
4
|
+
|
|
5
|
+
export enum SignerRole {
|
|
6
|
+
issuer,
|
|
7
|
+
verifier
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export enum AuthorizationState {
|
|
11
|
+
active,
|
|
12
|
+
suspended,
|
|
13
|
+
revoked
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export enum VerificationRelationship {
|
|
17
|
+
assertionMethod,
|
|
18
|
+
authentication,
|
|
19
|
+
capabilityInvocation
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export struct AuthorizedSignerDescriptor {
|
|
23
|
+
version: Uint<16>,
|
|
24
|
+
authorizationId: Bytes<32>,
|
|
25
|
+
decisionSequence: Uint<64>,
|
|
26
|
+
state: AuthorizationState,
|
|
27
|
+
role: SignerRole,
|
|
28
|
+
signerVerificationMethodRef: VerificationMethodRef,
|
|
29
|
+
signerPublicKey: JubjubPoint,
|
|
30
|
+
didStateVersion: Uint<64>,
|
|
31
|
+
verificationRelationship: VerificationRelationship,
|
|
32
|
+
scopeCommitment: Bytes<32>,
|
|
33
|
+
policyCommitment: Bytes<32>,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export struct SignerAuthorizationAuthority {
|
|
37
|
+
domainCommitment: Bytes<32>,
|
|
38
|
+
verificationMethodRef: VerificationMethodRef,
|
|
39
|
+
publicKey: JubjubPoint,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export pure circuit assertValidAuthorizedSignerDescriptor(
|
|
43
|
+
descriptor: AuthorizedSignerDescriptor
|
|
44
|
+
): [] {
|
|
45
|
+
assert(descriptor.version == 1, "Signer authorization version mismatch");
|
|
46
|
+
assert(
|
|
47
|
+
descriptor.authorizationId != pad(32, ""),
|
|
48
|
+
"Signer authorization id must be set"
|
|
49
|
+
);
|
|
50
|
+
assert(
|
|
51
|
+
descriptor.decisionSequence > 0,
|
|
52
|
+
"Signer authorization decision sequence must be positive"
|
|
53
|
+
);
|
|
54
|
+
assertValidVerificationMethodRef(descriptor.signerVerificationMethodRef);
|
|
55
|
+
assertUsableJubjubPoint(descriptor.signerPublicKey);
|
|
56
|
+
assert(
|
|
57
|
+
descriptor.didStateVersion > 0,
|
|
58
|
+
"Signer authorization DID state version must be positive"
|
|
59
|
+
);
|
|
60
|
+
assert(
|
|
61
|
+
descriptor.scopeCommitment != pad(32, ""),
|
|
62
|
+
"Signer authorization scope commitment must be set"
|
|
63
|
+
);
|
|
64
|
+
assert(
|
|
65
|
+
descriptor.policyCommitment != pad(32, ""),
|
|
66
|
+
"Signer authorization policy commitment must be set"
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (descriptor.role == SignerRole.issuer) {
|
|
70
|
+
assert(
|
|
71
|
+
descriptor.verificationRelationship
|
|
72
|
+
== VerificationRelationship.assertionMethod,
|
|
73
|
+
"Issuer authorization requires assertionMethod"
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
assert(
|
|
77
|
+
descriptor.verificationRelationship
|
|
78
|
+
== VerificationRelationship.authentication
|
|
79
|
+
|| descriptor.verificationRelationship
|
|
80
|
+
== VerificationRelationship.capabilityInvocation,
|
|
81
|
+
"Verifier authorization requires authentication or capabilityInvocation"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export pure circuit authorizedSignerDescriptorRoot(
|
|
87
|
+
descriptor: AuthorizedSignerDescriptor
|
|
88
|
+
): Bytes<32> {
|
|
89
|
+
assertValidAuthorizedSignerDescriptor(descriptor);
|
|
90
|
+
return authorizedSignerDescriptorHash(descriptor);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
pure circuit authorizedSignerDescriptorHash(
|
|
94
|
+
descriptor: AuthorizedSignerDescriptor
|
|
95
|
+
): Bytes<32> {
|
|
96
|
+
return persistentHash<AuthorizedSignerDescriptor>(descriptor);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export pure circuit issuerScopeCommitment(schema: SchemaRef): Bytes<32> {
|
|
100
|
+
assertValidSchemaRef(schema);
|
|
101
|
+
return persistentHash<SchemaRef>(schema);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export pure circuit assertProofSignerMatchesAuthorization(
|
|
105
|
+
proof: Proof,
|
|
106
|
+
descriptor: AuthorizedSignerDescriptor
|
|
107
|
+
): [] {
|
|
108
|
+
assertValidAuthorizedSignerDescriptor(descriptor);
|
|
109
|
+
assert(
|
|
110
|
+
descriptor.state == AuthorizationState.active,
|
|
111
|
+
"Signer authorization must be active"
|
|
112
|
+
);
|
|
113
|
+
assert(
|
|
114
|
+
proof.signerVerificationMethodRef.controllerAddress
|
|
115
|
+
== descriptor.signerVerificationMethodRef.controllerAddress,
|
|
116
|
+
"Proof signer controller does not match authorization"
|
|
117
|
+
);
|
|
118
|
+
assert(
|
|
119
|
+
proof.signerVerificationMethodRef.methodId
|
|
120
|
+
== descriptor.signerVerificationMethodRef.methodId,
|
|
121
|
+
"Proof signer method does not match authorization"
|
|
122
|
+
);
|
|
123
|
+
assertMatchingJubjubPoints(
|
|
124
|
+
descriptor.signerPublicKey,
|
|
125
|
+
proof.publicKey
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export pure circuit assertAuthorizedIssuerDescriptor(
|
|
130
|
+
schema: SchemaRef,
|
|
131
|
+
proof: Proof,
|
|
132
|
+
descriptor: AuthorizedSignerDescriptor
|
|
133
|
+
): [] {
|
|
134
|
+
assertProofSignerMatchesAuthorization(proof, descriptor);
|
|
135
|
+
assert(
|
|
136
|
+
descriptor.role == SignerRole.issuer,
|
|
137
|
+
"Signer authorization role must be issuer"
|
|
138
|
+
);
|
|
139
|
+
assert(
|
|
140
|
+
descriptor.scopeCommitment == issuerScopeCommitment(schema),
|
|
141
|
+
"Issuer authorization scope does not match schema"
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export pure circuit assertAuthorizedVerifierProof(
|
|
146
|
+
requestScopeCommitment: Bytes<32>,
|
|
147
|
+
proof: Proof,
|
|
148
|
+
descriptor: AuthorizedSignerDescriptor
|
|
149
|
+
): [] {
|
|
150
|
+
assert(
|
|
151
|
+
requestScopeCommitment != pad(32, ""),
|
|
152
|
+
"Verifier request scope commitment must be set"
|
|
153
|
+
);
|
|
154
|
+
assertProofSignerMatchesAuthorization(proof, descriptor);
|
|
155
|
+
assert(
|
|
156
|
+
descriptor.role == SignerRole.verifier,
|
|
157
|
+
"Signer authorization role must be verifier"
|
|
158
|
+
);
|
|
159
|
+
assert(
|
|
160
|
+
descriptor.scopeCommitment == requestScopeCommitment,
|
|
161
|
+
"Verifier authorization scope does not match request"
|
|
162
|
+
);
|
|
163
|
+
assertValidVerifierRequestContextProof(requestScopeCommitment, proof);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export pure circuit assertValidSignerAuthorizationAuthority(
|
|
167
|
+
authority: SignerAuthorizationAuthority
|
|
168
|
+
): [] {
|
|
169
|
+
assert(
|
|
170
|
+
authority.domainCommitment != pad(32, ""),
|
|
171
|
+
"Signer authorization authority domain must be set"
|
|
172
|
+
);
|
|
173
|
+
assertValidVerificationMethodRef(authority.verificationMethodRef);
|
|
174
|
+
assertUsableJubjubPoint(authority.publicKey);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export pure circuit signerAuthorizationDecisionRoot(
|
|
178
|
+
descriptor: AuthorizedSignerDescriptor,
|
|
179
|
+
domainCommitment: Bytes<32>
|
|
180
|
+
): Bytes<32> {
|
|
181
|
+
assertValidAuthorizedSignerDescriptor(descriptor);
|
|
182
|
+
assert(
|
|
183
|
+
domainCommitment != pad(32, ""),
|
|
184
|
+
"Signer authorization authority domain must be set"
|
|
185
|
+
);
|
|
186
|
+
return persistentHash<Vector<2, Bytes<32>>>([
|
|
187
|
+
domainCommitment,
|
|
188
|
+
authorizedSignerDescriptorHash(descriptor)
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export pure circuit assertValidSignerAuthorizationProof(
|
|
193
|
+
descriptor: AuthorizedSignerDescriptor,
|
|
194
|
+
authorizationProof: Proof,
|
|
195
|
+
authority: SignerAuthorizationAuthority
|
|
196
|
+
): [] {
|
|
197
|
+
assertValidAuthorizedSignerDescriptor(descriptor);
|
|
198
|
+
assertValidSignerAuthorizationAuthority(authority);
|
|
199
|
+
assert(
|
|
200
|
+
authorizationProof.createdAt == descriptor.decisionSequence,
|
|
201
|
+
"Authorization proof sequence does not match descriptor"
|
|
202
|
+
);
|
|
203
|
+
assert(
|
|
204
|
+
authorizationProof.signerVerificationMethodRef.controllerAddress
|
|
205
|
+
== authority.verificationMethodRef.controllerAddress,
|
|
206
|
+
"Authorization proof controller does not match authority"
|
|
207
|
+
);
|
|
208
|
+
assert(
|
|
209
|
+
authorizationProof.signerVerificationMethodRef.methodId
|
|
210
|
+
== authority.verificationMethodRef.methodId,
|
|
211
|
+
"Authorization proof method does not match authority"
|
|
212
|
+
);
|
|
213
|
+
assertMatchingJubjubPoints(
|
|
214
|
+
authority.publicKey,
|
|
215
|
+
authorizationProof.publicKey
|
|
216
|
+
);
|
|
217
|
+
assertValidSignerAuthorizationContextProof(
|
|
218
|
+
signerAuthorizationDecisionRoot(descriptor, authority.domainCommitment),
|
|
219
|
+
authorizationProof
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export pure circuit assertValidSignerAuthorizationUpdate(
|
|
224
|
+
previous: AuthorizedSignerDescriptor,
|
|
225
|
+
next: AuthorizedSignerDescriptor
|
|
226
|
+
): [] {
|
|
227
|
+
assertValidAuthorizedSignerDescriptor(previous);
|
|
228
|
+
assertValidAuthorizedSignerDescriptor(next);
|
|
229
|
+
assert(
|
|
230
|
+
previous.authorizationId == next.authorizationId,
|
|
231
|
+
"Signer authorization update id mismatch"
|
|
232
|
+
);
|
|
233
|
+
assert(
|
|
234
|
+
next.decisionSequence > previous.decisionSequence,
|
|
235
|
+
"Signer authorization update sequence must increase"
|
|
236
|
+
);
|
|
237
|
+
assert(
|
|
238
|
+
next.didStateVersion >= previous.didStateVersion,
|
|
239
|
+
"Signer authorization DID state version must not decrease"
|
|
240
|
+
);
|
|
241
|
+
assert(
|
|
242
|
+
previous.state != AuthorizationState.revoked
|
|
243
|
+
|| next.state == AuthorizationState.revoked,
|
|
244
|
+
"Revoked signer authorization must remain revoked"
|
|
245
|
+
);
|
|
246
|
+
}
|