-
Notifications
You must be signed in to change notification settings - Fork 9
/
social-kyc.spec.ts
152 lines (130 loc) · 5.76 KB
/
social-kyc.spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { generateRandomFieldElement, generateRandomG1Element, pedersenCommitmentG1 } from 'crypto-wasm-new';
import {
CompositeProof,
initializeWasm,
MetaStatements,
ProofSpec,
Statement,
Statements,
Witness,
Witnesses
} from '../../src';
import {
BlindSignature,
encodeMessageForSigningIfNotPS,
encodeMessageForSigningIfPS,
getStatementForBlindSigRequest,
getWitnessForBlindSigRequest,
isBBS,
isKvac,
isPS,
PublicKey,
Scheme,
SecretKey,
Signature,
SignatureParams
} from '../scheme';
import { checkResult, getParamsAndKeys, stringToBytes } from '../utils';
describe(`Social KYC (Know Your Customer) with ${Scheme} credentials`, () => {
// A social KYC (Know Your Customer) credential claims that the subject owns certain social media profile like a twitter
// profile credential claims that a user owns the twitter profile with certain handle. User posts a commitment to some
// random value on his profile and then requests a credential from the issuer by supplying a proof of knowledge of the
// opening (committed random value) of the commitment. This test shows an example of user getting a twitter profile
// credential from an issuer and the credential contains the profile handle, name, description and no. of followers. The
// credential will contain the user's secret id which he will hide from the issuer, thus gets a blinded credential.
// Issuer's parameters
let sigParams: SignatureParams;
// Issuers secret key and public keys
let sk: SecretKey, pk: PublicKey;
// Commitment key for commitment posted on social profile.
let g: Uint8Array;
let h: Uint8Array;
// No of attributes in the KYC credential
const attributeCount = 5;
beforeAll(async () => {
// Load the WASM module
await initializeWasm();
// Generate keys
[sigParams, sk, pk] = getParamsAndKeys(attributeCount);
h = generateRandomG1Element();
g = generateRandomG1Element();
});
it('Requesting credential', async () => {
// Holder creates random value posts a commitment to it on his twitter profile as a tweet.
const randomValueTweet = generateRandomFieldElement();
// This commitment will be posted in the tweet
const commitmentTweet = pedersenCommitmentG1([g], [randomValueTweet]);
// Prepare messages that will be blinded (hidden) and known to signer
const blindedAttributes = new Map();
blindedAttributes.set(0, encodeMessageForSigningIfPS(stringToBytes('my-secret-id')));
// Issuer will know these attributes
const knownAttributes = new Map();
knownAttributes.set(1, encodeMessageForSigningIfPS(stringToBytes('@johnsmith')));
knownAttributes.set(2, encodeMessageForSigningIfPS(stringToBytes('John Smith')));
knownAttributes.set(3, encodeMessageForSigningIfPS(stringToBytes('Some guy on twitter')));
knownAttributes.set(4, encodeMessageForSigningIfPS(stringToBytes('5000')));
// Generate a blind signature request
let blindings = new Map(),
blinding,
request;
if (isPS()) {
[blinding, request] = BlindSignature.generateRequest(
blindedAttributes,
sigParams,
h,
blindings,
void 0,
knownAttributes
);
} else if (isBBS()) {
request = BlindSignature.generateRequest(blindedAttributes, sigParams, true, knownAttributes);
} else {
[blinding, request] = BlindSignature.generateRequest(blindedAttributes, sigParams, true, void 0, knownAttributes);
}
// The proof is created for 2 statements.
// The 1st statement to prove is knowledge of opening of the commitment in the tweet
const statement1 = Statement.pedersenCommitmentG1([g], commitmentTweet);
const statements = new Statements(getStatementForBlindSigRequest(request, sigParams, h));
statements.add(statement1);
// Some context to the proof to prevent replayability, for stronger protection this should contain today's date etc as well
const context = stringToBytes('Verifying twitter profile with issuer 1');
const meta = new MetaStatements();
const proofSpec = new ProofSpec(statements, meta, [], context);
// This is the opening of the commitment posted in tweet
const witness1 = Witness.pedersenCommitment([randomValueTweet]);
// The witness to the Pedersen commitment contains the blinding at index 0 by convention and then the hidden attributes
const witnesses = new Witnesses(
getWitnessForBlindSigRequest(
new Map([...blindedAttributes].map(([idx, attr]) => [idx, encodeMessageForSigningIfNotPS(attr)])),
blinding,
blindings
)
);
witnesses.add(witness1);
// User creates this proof and sends to the issuer.
const proof = CompositeProof.generate(proofSpec, witnesses);
// Issuer checks that the commitment `commitmentTweet` is present in the tweet and then verifies the following
// proof to check user's knowledge of its opening.
checkResult(proof.verify(proofSpec));
// Issuer is convinced that user knows the opening to the both commitments
const blindSig = isPS()
? BlindSignature.fromRequest(request, sk, h)
: BlindSignature.fromRequest(request, sk, sigParams);
// // User unblinds the signature and now has valid credential
const sig: Signature = isBBS()
? blindSig
: isPS()
? blindSig.unblind(blindings!, pk, h)
: blindSig.unblind(blinding!);
// Combine blinded and known attributes in an array
const attributes = Array(blindedAttributes.size + knownAttributes.size);
for (const [i, m] of blindedAttributes.entries()) {
attributes[i] = m;
}
for (const [i, m] of knownAttributes.entries()) {
attributes[i] = m;
}
const result = isKvac() ? sig.verify(attributes, sk, sigParams, true) : sig.verify(attributes, pk, sigParams, true);
checkResult(result);
});
});