Skip to content

Commit 4add513

Browse files
committed
WIP
1 parent b3e1a80 commit 4add513

10 files changed

Lines changed: 229 additions & 97 deletions

examples/generic_batched.example.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ async function rsaVariant(): Promise<boolean> {
8282
if (res.tokenResponse === null) {
8383
continue;
8484
}
85-
const r = publicVerif.TokenResponse.deserialize(res.tokenResponse);
86-
tokens[i] = await clients[i].finalize(r);
85+
const r = res.tokenResponse;
86+
tokens[i] = await clients[i].finalize(r as publicVerif.TokenResponse);
8787

8888
i += 1;
8989
}

package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"devDependencies": {
4848
"@eslint/js": "9.9.1",
4949
"@types/benchmark": "2.1.5",
50+
"@types/node": "^25.2.3",
5051
"@vitest/coverage-v8": "3.0.7",
5152
"benchmark": "2.1.4",
5253
"eslint": "8.57.1",

src/generic_batched_token.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33

44
import * as varint from 'quicvarint';
55

6+
import type { privateVerif, publicVerif } from './index.js';
67
import {
78
type Token,
89
TOKEN_TYPES,
910
tokenEntryToSerializedLength,
1011
tokenRequestToTokenTypeEntry,
1112
} from './index.js';
1213
import { Issuer as Type1Issuer, TokenRequest as Type1TokenRequest } from './priv_verif_token.js';
13-
import { Issuer as Type2Issuer, TokenRequest as Type2TokenRequest } from './pub_verif_token.js';
14+
import {
15+
TokenResponse,
16+
Issuer as Type2Issuer,
17+
TokenRequest as Type2TokenRequest,
18+
} from './pub_verif_token.js';
1419
import { joinAll } from './util.js';
1520

1621
const TokenStatus = {
@@ -130,17 +135,29 @@ export class OptionalTokenResponse {
130135
// struct {
131136
// optional<GenericTokenResponse> generic_token_response; /* Defined by token_type */
132137
// } OptionalTokenResponse;
133-
constructor(public readonly tokenResponse: null | Uint8Array) {}
134-
135-
static deserialize(bytes: Uint8Array): OptionalTokenResponse {
138+
constructor(
139+
public readonly tokenResponse:
140+
| null
141+
| publicVerif.TokenResponse
142+
| privateVerif.TokenResponse,
143+
) {}
144+
145+
static deserialize(bytes: Uint8Array, type: 1 | 2): OptionalTokenResponse {
136146
if (bytes.length === 0) {
137147
throw new Error('OptionalTokenResponse MUST be of length strictly greater than 0');
138148
}
139149
switch (bytes[0]) {
140150
case TokenStatus.ABSENT:
141151
return new OptionalTokenResponse(null);
142152
case TokenStatus.PRESENT:
143-
return new OptionalTokenResponse(bytes.slice(1));
153+
switch (type) {
154+
case TOKEN_TYPES.VOPRF.value:
155+
return new OptionalTokenResponse(TokenResponse.deserialize(bytes.slice(1)));
156+
case TOKEN_TYPES.BLIND_RSA.value:
157+
return new OptionalTokenResponse(TokenResponse.deserialize(bytes.slice(1)));
158+
default:
159+
throw new Error('unsupported token type');
160+
}
144161
default:
145162
throw new Error('OptionalTokenResponse MUST start with either 0x00 or 0x01');
146163
}
@@ -150,7 +167,8 @@ export class OptionalTokenResponse {
150167
if (this.tokenResponse === null) {
151168
return new Uint8Array([TokenStatus.ABSENT]);
152169
}
153-
return new Uint8Array([TokenStatus.PRESENT, ...this.tokenResponse]);
170+
const serialized = this.tokenResponse.serialize();
171+
return new Uint8Array([TokenStatus.PRESENT, ...serialized]);
154172
}
155173
}
156174

@@ -160,7 +178,7 @@ export class GenericBatchTokenResponse {
160178
// } GenericBatchTokenResponse
161179
constructor(public readonly tokenResponses: OptionalTokenResponse[]) {}
162180

163-
static deserialize(bytes: Uint8Array): GenericBatchTokenResponse {
181+
static deserialize(bytes: Uint8Array, types: (1 | 2)[]): GenericBatchTokenResponse {
164182
let offset = 0;
165183
const input = new DataView(bytes.buffer);
166184

@@ -173,13 +191,16 @@ export class GenericBatchTokenResponse {
173191

174192
const batchedTokenResponses: OptionalTokenResponse[] = [];
175193

194+
let i = 0;
176195
while (offset < bytes.length) {
177-
const len = input.getUint16(offset);
178-
offset += 2;
179-
const b = new Uint8Array(input.buffer.slice(offset, offset + len));
180-
offset += len;
181-
182-
batchedTokenResponses.push(OptionalTokenResponse.deserialize(b));
196+
const type = types[i++];
197+
const otr = OptionalTokenResponse.deserialize(bytes.slice(offset), type);
198+
if (otr.tokenResponse === null) {
199+
offset += 1;
200+
} else {
201+
offset += otr.tokenResponse.length() + 1;
202+
}
203+
batchedTokenResponses.push(otr);
183204
}
184205

185206
return new GenericBatchTokenResponse(batchedTokenResponses);
@@ -261,9 +282,10 @@ export class Issuer {
261282
tokenRequest.truncatedTokenKeyId,
262283
);
263284
const response = (await issuer.issue(tokenRequest.tokenRequest)).serialize();
264-
tokenResponses.push(new OptionalTokenResponse(response));
285+
tokenResponses.push(new OptionalTokenResponse(TokenResponse.deserialize(response)));
265286
// eslint-disable-next-line @typescript-eslint/no-unused-vars
266-
} catch (_) {
287+
} catch (e) {
288+
console.log(e);
267289
tokenResponses.push(new OptionalTokenResponse(null));
268290
}
269291
}
@@ -304,7 +326,7 @@ export class Client {
304326
return new BatchedTokenRequest(tokenRequests);
305327
}
306328

307-
deserializeTokenResponse(bytes: Uint8Array): GenericBatchTokenResponse {
308-
return GenericBatchTokenResponse.deserialize(bytes);
329+
deserializeTokenResponse(bytes: Uint8Array, types: (1 | 2)[]): GenericBatchTokenResponse {
330+
return GenericBatchTokenResponse.deserialize(bytes, types);
309331
}
310332
}

src/priv_verif_token.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ export class TokenResponse {
163163
serialize(): Uint8Array {
164164
return new Uint8Array(joinAll([this.evaluateMsg, this.evaluateProof]));
165165
}
166+
167+
length(): number {
168+
return this.evaluateMsg.length + this.evaluateProof.length;
169+
}
166170
}
167171

168172
export function verifyToken(token: Token, privateKeyIssuer: Uint8Array): Promise<boolean> {

src/pub_verif_token.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ export class TokenResponse {
200200

201201
constructor(public readonly blindSig: Uint8Array) {
202202
if (blindSig.length !== BLIND_RSA.Nk) {
203-
throw new Error('blind signature has invalid size');
203+
throw new Error(`blind signature has invalid size: ${blindSig.length}`);
204204
}
205205
}
206206

@@ -211,6 +211,10 @@ export class TokenResponse {
211211
serialize(): Uint8Array {
212212
return new Uint8Array(this.blindSig);
213213
}
214+
215+
length(): number {
216+
return this.blindSig.length;
217+
}
214218
}
215219

216220
abstract class PubliclyVerifiableIssuer {

test/generic_batched_token.test.ts

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,49 @@ import {
1919
privateVerif,
2020
publicVerif,
2121
} from '../src/index.js';
22-
const { Client: Type1Client } = privateVerif;
23-
const { BlindRSAMode, Client: Type2Client } = publicVerif;
2422

2523
import { keysFromVector as type2KeysFromVector } from './pub_verif_token.js';
26-
import { hexToUint8, testSerialize, uint8ToHex } from './util.js';
24+
import { hexToUint8, uint8ToHex } from './util.js';
2725

2826
// https://github.com/cloudflare/pat-go/blob/main/tokens/batched/batched-issuance-test-vectors.json
2927
import vectorsGo from './test_data/generic_batched_tokens_v6_go.json';
3028
// https://raw.githubusercontent.com/raphaelrobert/privacypass/0600835c039c4b89f2137be3f5b1ecbeffe05417/tests/kat_vectors/generic_rs.json
3129
import vectorsRust from './test_data/generic_batched_tokens_v6_rs.json';
3230

3331
const vectors = [...vectorsGo, ...vectorsRust];
32+
console.log('NUMBEROFVECTORS', vectors.length);
3433
type Vectors = (typeof vectors)[number];
3534

3635
const SUPPORTED_TYPES = [TOKEN_TYPES.VOPRF.value, TOKEN_TYPES.BLIND_RSA.value].map((t) =>
3736
t.toString().padStart(4, '0'),
3837
);
3938

39+
const token_type = (i: unknown): string => {
40+
if (typeof i !== 'object' || i === null) {
41+
throw new Error('unsupported');
42+
}
43+
if ('type' in i && typeof i.type === 'string') {
44+
return i.type;
45+
} else if ('token_type' in i && typeof i.token_type === 'string') {
46+
return i.token_type;
47+
} else {
48+
throw new Error('unsupported');
49+
}
50+
};
51+
4052
describe.each(vectors)('GenericBatched-Vector-%#', (v: Vectors) => {
4153
const params = [[], [{ supportsRSARAW: true }]];
4254

4355
test.each(params)('GenericBatched-Vector-%#-Issuer-Params-%#', async (...params) => {
4456
// if the test vector contains an unsupported type, skip the test
45-
if (v.issuance.find((i) => !SUPPORTED_TYPES.includes(i.type)) !== undefined) {
46-
expect(true).toBe(true);
47-
return;
48-
}
57+
console.log(v.issuance.map(token_type));
58+
expect(v.issuance.every((i) => SUPPORTED_TYPES.includes(token_type(i)))).toBe(true);
4959
const tokenRequests = new Array<TokenRequest>(v.issuance.length);
5060
const issuers = new Array<privateVerif.Issuer | publicVerif.Issuer>(v.issuance.length);
5161
const clients = new Array<privateVerif.Client | publicVerif.Client>(v.issuance.length);
5262
for (let i = 0; i < v.issuance.length; i += 1) {
5363
const issuance = v.issuance[i];
54-
const type = Number.parseInt(issuance.type);
64+
const type = Number.parseInt(token_type(issuance));
5565

5666
const nonce = hexToUint8(issuance.nonce);
5767
const blind = hexToUint8(issuance.blind);
@@ -68,7 +78,7 @@ describe.each(vectors)('GenericBatched-Vector-%#', (v: Vectors) => {
6878
Promise.resolve(TOKEN_TYPES.VOPRF.group.desScalar(blind)),
6979
);
7080

71-
const client = new Type1Client();
81+
const client = new privateVerif.Client();
7282
clients[i] = client;
7383
const tokReq = await client.createTokenRequest(tokChl, publicKey);
7484
tokenRequests[i] = new TokenRequest(tokReq);
@@ -82,21 +92,21 @@ describe.each(vectors)('GenericBatched-Vector-%#', (v: Vectors) => {
8292
break;
8393
}
8494
case TOKEN_TYPES.BLIND_RSA.value: {
85-
if (issuance.salt === undefined) {
95+
if (issuance.salt === undefined || issuance.salt === null) {
8696
throw new Error('invalid test vector');
8797
}
8898
const salt = hexToUint8(issuance.salt);
8999
const mode =
90-
salt.length == (BlindRSAMode.PSS as number)
91-
? BlindRSAMode.PSS
92-
: BlindRSAMode.PSSZero;
100+
salt.length == (publicVerif.BlindRSAMode.PSS as number)
101+
? publicVerif.BlindRSAMode.PSS
102+
: publicVerif.BlindRSAMode.PSSZero;
93103

94104
vi.spyOn(crypto, 'getRandomValues')
95105
.mockReturnValueOnce(nonce)
96106
.mockReturnValueOnce(salt)
97107
.mockReturnValueOnce(blind);
98108

99-
const client = new Type2Client(mode);
109+
const client = new publicVerif.Client(mode);
100110
clients[i] = client;
101111
const [{ privateKey, publicKey }, publicKeyEnc] =
102112
await type2KeysFromVector(issuance);
@@ -124,34 +134,40 @@ describe.each(vectors)('GenericBatched-Vector-%#', (v: Vectors) => {
124134

125135
const issuer = new Issuer(...issuers);
126136

137+
console.log(tokReq);
127138
const tokRes = await issuer.issue(tokReq);
128-
testSerialize(GenericBatchTokenResponse, tokRes);
139+
const bytes = tokRes.serialize();
140+
const got = GenericBatchTokenResponse.deserialize(
141+
bytes,
142+
tokReq.tokenRequests.map((r) => r.tokenType) as (1 | 2)[],
143+
);
144+
expect(got).toStrictEqual(tokRes);
145+
console.log(tokRes);
129146

130147
for (let i = 0; i < v.issuance.length; i += 1) {
131148
const issuance = v.issuance[i];
132149
const res = tokRes.tokenResponses[i];
133-
const type = Number.parseInt(issuance.type);
150+
const type = Number.parseInt(token_type(issuance));
134151

135152
let token: Token;
136153
switch (type) {
137154
case TOKEN_TYPES.VOPRF.value: {
138155
const client = clients[i] as privateVerif.Client;
139-
const rawTokenResponse = res.tokenResponse;
140-
if (rawTokenResponse === null) {
156+
const tokenResponse = res.tokenResponse as privateVerif.TokenResponse | null;
157+
if (tokenResponse === null) {
141158
throw new Error('should not be null');
142159
}
143-
const tokenResponse = privateVerif.TokenResponse.deserialize(rawTokenResponse);
144160
token = await client.finalize(tokenResponse);
145161
break;
146162
}
147163
case TOKEN_TYPES.BLIND_RSA.value: {
148164
const client = clients[i] as publicVerif.Client;
149-
const rawTokenResponse = res.tokenResponse;
150-
if (rawTokenResponse === null) {
165+
const tokenResponse = res.tokenResponse;
166+
if (tokenResponse === null) {
151167
throw new Error('should not be null');
152168
}
153-
const tokenResponse = publicVerif.TokenResponse.deserialize(rawTokenResponse);
154-
token = await client.finalize(tokenResponse);
169+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
170+
token = await client.finalize(tokenResponse as publicVerif.TokenResponse);
155171
break;
156172
}
157173
default:

test/test_data/generic_batched_tokens_v6_go.json

Lines changed: 108 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)