Skip to content

Commit e03d8d7

Browse files
author
adamczykm
committed
Add two claims standards, fix couple of bugs, proceed with happy path flow
1 parent b25b832 commit e03d8d7

File tree

10 files changed

+568
-91
lines changed

10 files changed

+568
-91
lines changed

minauth-plugins/minauth-verified-zkdocument-plugin/src/claim.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CircuitString, Field, Poseidon } from 'o1js';
22
import { z } from 'zod';
33
import { ClaimStruct, IClaimStruct } from './data/claims.js';
4-
import { FieldsSchema } from './data/simple';
4+
import { FieldsSchema } from './data/simple.js';
55

66
export const ClaimStandardSchema = z.object({
77
standardId: z.string().min(2),

minauth-plugins/minauth-verified-zkdocument-plugin/src/credential.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
CredentialStandardSchema,
44
CredentialStandard,
55
CredentialSchema,
6-
CredentialDataSchema,
7-
checkCredentialStandardConformance
6+
checkCredentialStandardConformance,
7+
CredentialDataSchema
88
} from './credential.js';
99

1010
describe('Credential creation and validation tests', () => {
@@ -31,7 +31,7 @@ describe('Credential creation and validation tests', () => {
3131
issuanceDate: { unixTimestamp: number };
3232
expirationDate: { unixTimestamp: number };
3333
subject: { pubkey: string };
34-
credentialSchemaHash: string;
34+
credentialStandardHash: string;
3535
};
3636

3737
let Credential1Raw: {
@@ -58,7 +58,7 @@ describe('Credential creation and validation tests', () => {
5858
subject: {
5959
pubkey: string;
6060
};
61-
credentialSchemaHash: string;
61+
credentialStandardHash: string;
6262
signature: {
6363
signatureBase58: string;
6464
};
@@ -124,7 +124,7 @@ describe('Credential creation and validation tests', () => {
124124
subject: {
125125
pubkey: 'B62qnH2ZHFKinYSMEHCcmu7Z7ijeqRTa6KRHF5KUCXnfDvPkysg5TSL'
126126
},
127-
credentialSchemaHash:
127+
credentialStandardHash:
128128
'17162948422336679126313318026665558372573289127878826905197746632594425837153'
129129
};
130130

@@ -311,7 +311,7 @@ describe('Credential creation and validation tests', () => {
311311
issuanceDate: any; // Use the actual expected type
312312
expirationDate: any; // Use the actual expected type
313313
subject: any; // Use the actual expected type
314-
credentialSchemaHash: any; // Use the actual expected type
314+
credentialStandardHash: any; // Use the actual expected type
315315
}
316316

317317
let nonMatchingCredentialRaw: CredentialData = { ...CredentialDataRaw1 };

minauth-plugins/minauth-verified-zkdocument-plugin/src/credential.ts

Lines changed: 112 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,98 @@
1-
import { CircuitString, Field, Poseidon } from 'o1js';
1+
import { CircuitString, Field, Poseidon, PrivateKey } from 'o1js';
2+
import * as o1js from 'o1js';
23
import { z } from 'zod';
34
import {
45
ClaimSchema,
5-
ClaimStandard,
66
ClaimStandardSchema,
77
IClaim,
88
claimStandardHash
99
} from './claim.js';
1010
import {
11+
CredSubjectId,
1112
CredSubjectIdSchema,
1213
IssuerIdSchema,
1314
VCredIdSchema
1415
} from './data/ids.js';
1516
import {
1617
FieldSchema,
18+
SignatureB58,
1719
SignatureSchema,
1820
UnixTimestampSchema
1921
} from './data/simple.js';
2022
import { arraysAreEqual } from './helpers/utils.js';
23+
import { Issuer } from './issuer.js';
24+
import { VCredStruct, VCredStructUnsigned } from './data/vcred.js';
25+
import { Logger } from 'tslog';
26+
27+
const log = new Logger({ name: 'credential.ts' });
28+
29+
// =============== Credential Trasit Types ==================
30+
31+
// ---------------- Credential Standard ----------------
2132

2233
export const CredentialStandardSchema = z.object({
2334
standardId: z.string().min(2),
2435
description: z.string().min(2),
25-
schema: z.record(z.string().min(2), ClaimStandardSchema).refine(
26-
(schema) => Object.keys(schema).length > 0,
27-
{ message: 'Must define at least one claim.' }
28-
)
36+
schema: z
37+
.record(z.string().min(2), ClaimStandardSchema)
38+
.refine((schema) => Object.keys(schema).length > 0, {
39+
message: 'Must define at least one claim.'
40+
})
2941
});
3042

3143
export type CredentialStandard = z.infer<typeof CredentialStandardSchema>;
3244

33-
export const credentialStandardHash = (standard: CredentialStandard): Field => {
34-
const idHash = Poseidon.hash(
35-
CircuitString.fromString(standard.standardId).toFields()
36-
);
37-
const schemaHash = Poseidon.hash(
38-
Object.values(standard.schema).map(claimStandardHash)
39-
);
40-
return Poseidon.hash([idHash, schemaHash]);
41-
};
45+
// ---------------- Credential Data ----------------
4246

4347
export const CredentialDataSchema = z.object({
44-
claims: z.record(z.string().min(1), ClaimSchema(z.unknown())).refine(
45-
(claims) => Object.keys(claims).length > 0,
46-
{ message: 'Must define at least one claim.' }
47-
),
48+
claims: z
49+
.record(z.string().min(1), ClaimSchema(z.unknown()))
50+
.refine((claims) => Object.keys(claims).length > 0, {
51+
message: 'Must define at least one claim.'
52+
}),
4853
id: VCredIdSchema,
4954
issuer: IssuerIdSchema,
5055
issuanceDate: UnixTimestampSchema,
5156
expirationDate: UnixTimestampSchema,
5257
subject: CredSubjectIdSchema,
53-
credentialSchemaHash: FieldSchema,
58+
credentialStandardHash: FieldSchema
5459
});
5560

61+
export type CredentialData = z.infer<typeof CredentialDataSchema>;
62+
63+
// ---------------- Credential ----------------
64+
5665
export const CredentialSchema = CredentialDataSchema.extend({
5766
signature: SignatureSchema
5867
});
5968

60-
export type CredentialData = z.infer<typeof CredentialDataSchema>;
61-
6269
export type Credential = z.infer<typeof CredentialSchema>;
6370

71+
// =============== To fields-encoded types ===============
72+
73+
export const mkStructCredentialUnsigned = (credential: CredentialData) => {
74+
log.debug('Entering mkStructCredentialUnsigned...', credential);
75+
return VCredStructUnsigned(
76+
Object.values(credential.claims).map((claim) => claim.fieldsValue.length)
77+
).schema.parse({
78+
...credential,
79+
claims: Object.values(credential.claims).map((claim) => ({
80+
claimValue: claim.fieldsValue
81+
}))
82+
});
83+
};
84+
85+
// signed version
86+
export const mkStructCredential = (credential: Credential) => {
87+
return VCredStruct(
88+
Object.values(credential.claims).map((claim) => claim.fieldsValue.length)
89+
).schema.parse({
90+
...credential,
91+
claims: Object.values(credential.claims).map((claim) => ({
92+
claimValue: claim.fieldsValue
93+
}))
94+
});
95+
};
6496

6597
export const checkCredentialStandardConformance = (
6698
credential: Credential,
@@ -85,20 +117,68 @@ export const checkCredentialStandardConformance = (
85117
return true;
86118
};
87119

120+
export const credentialStandardHash = (standard: CredentialStandard): Field => {
121+
const idHash = Poseidon.hash(
122+
CircuitString.fromString(standard.standardId).toFields()
123+
);
124+
const schemaHash = Poseidon.hash(
125+
Object.values(standard.schema).map(claimStandardHash)
126+
);
127+
return Poseidon.hash([idHash, schemaHash]);
128+
};
88129

130+
export const verifyAndIssueCredential =
131+
(
132+
standard: CredentialStandard,
133+
credentialData: CredentialData,
134+
subject: CredSubjectId,
135+
issuer: Issuer
136+
) =>
137+
(privateKey: PrivateKey) => {
138+
// get fields for the signature
139+
log.debug('Entering verifyAndIssueCredential...');
140+
141+
log.debug('Converting the credential data to o1js fields.');
142+
const flds = mkStructCredentialUnsigned(credentialData).toFields();
143+
144+
log.debug('Creating signature for the fields data');
145+
const signature: SignatureB58 = SignatureSchema.parse(
146+
o1js.Signature.create(privateKey, flds).toBase58()
147+
);
148+
149+
// create the credential
150+
151+
log.debug('Parsing the credential with the signature');
152+
const credential = CredentialSchema.parse({
153+
...credentialData,
154+
signature
155+
});
156+
157+
// check if the credential conforms to the standard
158+
log.debug('Checking credential standard conformance...');
159+
const check = checkCredentialStandardConformance(credential, standard);
160+
if (!check) {
161+
throw new Error('Credential does not conform to standard');
162+
}
89163

90-
// ==========================================
91-
// inline tests
164+
// additional assertions
165+
// issuer pubkey matches the private key
166+
if (!issuer.pubkey.equals(privateKey.toPublicKey())) {
167+
throw new Error('Issuer pubkey does not match the private key');
168+
}
92169

93-
let asd = undefined as unknown as ClaimStandard;
170+
// credential subject public key matches the given subject public key
171+
if (!credential.subject.pubkey.equals(subject.pubkey)) {
172+
throw new Error('Subject does not match the credential subject');
173+
}
94174

95-
let cred: CredentialStandard = {
96-
standardId: 'personhood1',
97-
description: 'Proves that subject is a person',
98-
schema: {
99-
name: asd
100-
}
101-
};
175+
return {
176+
credential
177+
};
178+
};
179+
180+
// ==========================================
181+
// inline tests
102182

103183
const TypeCheckerTestSchema = ClaimSchema(z.number());
104184
type TypeCheckerTestType = z.infer<typeof TypeCheckerTestSchema>;

minauth-plugins/minauth-verified-zkdocument-plugin/src/data/simple.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import { Field, PublicKey, Struct, Signature } from 'o1js';
22
import { Logger } from 'tslog';
33
import { z } from 'zod';
44

5+
const hexViaFieldBigInt = (x: number | bigint | string) => {
6+
return '0x' + (new Field(x)).toBigInt().toString(16);
7+
}
8+
59
export const FieldSchemaInt = z.number().int().transform((n) => new Field(BigInt(n)));
610
export const FieldSchemaBigInt = z.bigint().transform(Field);
711
export const FieldSchemaDec = z.string().regex(/^\d+$/).transform(Field);
@@ -22,6 +26,8 @@ export const SignatureSchema = z
2226
})
2327
.transform((o) => Signature.fromBase58(o.signatureBase58));
2428

29+
export type SignatureB58 = z.infer<typeof SignatureSchema>;
30+
2531
export class UnixTimestamp extends Struct({
2632
unixTimestamp: Field
2733
}) {

minauth-plugins/minauth-verified-zkdocument-plugin/src/data/vcred.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('VCredStruct tests', () => {
3939
}),
4040
subject: new CredSubjectId({ pubkey: pk2 }),
4141
claims: mkClaims([claim1, claim2]),
42-
credentialSchemaHash: new Field(789)
42+
credentialStandardHash: new Field(789)
4343
};
4444
};
4545

@@ -61,7 +61,7 @@ describe('VCredStruct tests', () => {
6161
expirationDate: { unixTimestamp: '223123123' },
6262
subject: { pubkey: pk2s },
6363
claims: [claim1s, claim2s],
64-
credentialSchemaHash: '789'
64+
credentialStandardHash: '789'
6565
});
6666

6767
if (!validation.success) {
@@ -85,7 +85,7 @@ describe('VCredStruct tests', () => {
8585
}),
8686
subject: new CredSubjectId({ pubkey: pk2 }),
8787
claims: mkClaims([claim1, claim2]),
88-
credentialSchemaHash: new Field(789),
88+
credentialStandardHash: new Field(789),
8989
signature: Signature.fromBase58('7mX64qh7mUUn5jnWUAAg1o39xwic6vvCq9uwuVSDqTJ7bLD69qtRzn3BzzLu95exgJWxTUUTexdsVv5MFu46RoxrPxF1ZpXE')
9090
};
9191
};
@@ -107,7 +107,7 @@ describe('VCredStruct tests', () => {
107107
expirationDate: { unixTimestamp: '223123123' },
108108
subject: { pubkey: pk2s },
109109
claims: [claim1s, claim2s],
110-
credentialSchemaHash: '789',
110+
credentialStandardHash: '789',
111111
signature: {signatureBase58: '7mX64qh7mUUn5jnWUAAg1o39xwic6vvCq9uwuVSDqTJ7bLD69qtRzn3BzzLu95exgJWxTUUTexdsVv5MFu46RoxrPxF1ZpXE'}
112112
});
113113
if(!validation.success){

minauth-plugins/minauth-verified-zkdocument-plugin/src/data/vcred.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@ export function VCredStructUnsigned(claimSizes: number[]) {
1919
expirationDate: UnixTimestamp,
2020
subject: CredSubjectId,
2121
claims: ClaimsType,
22-
credentialSchemaHash: Field
22+
credentialStandardHash: Field
2323
};
2424

2525
class BaseVCredStruct_ extends Struct(Data) {
2626
static Fields = Data;
2727

28-
public toFields() {
28+
public toFields(): Field[] {
2929
return [
30-
this.id,
30+
...this.id.toFields(),
3131
...this.issuer.toFields(),
32-
this.issuanceDate,
33-
this.expirationDate,
32+
...this.issuanceDate.toFields(),
33+
...this.expirationDate.toFields(),
3434
...this.subject.toFields(),
3535
...this.claims.toFields(),
36-
this.credentialSchemaHash
36+
...this.credentialStandardHash.toFields()
3737
];
3838
}
3939

@@ -45,7 +45,7 @@ export function VCredStructUnsigned(claimSizes: number[]) {
4545
expirationDate: UnixTimestampSchema,
4646
subject: CredSubjectIdSchema,
4747
claims: ClaimsType.schema,
48-
credentialSchemaHash: FieldSchema
48+
credentialStandardHash: FieldSchema
4949
});
5050
}
5151

@@ -85,7 +85,7 @@ export function VCredStruct(claimSizes: number[]) {
8585
return VCredStruct_.dataSchema.transform(
8686
(data) => new VCredStruct_(data)
8787
);
88-
}
88+
}
8989
}
9090

9191
return VCredStruct_;

0 commit comments

Comments
 (0)