Skip to content

Commit d771216

Browse files
committed
Improve root import tree shaking
1 parent 2ac6860 commit d771216

10 files changed

Lines changed: 240 additions & 196 deletions

src/generic_batched_token.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@
33

44
import * as varint from 'quicvarint';
55

6-
import { publicVerif, privateVerif, type TokenTypeValue } from './index.js';
76
import {
8-
type Token,
97
TOKEN_TYPES,
108
tokenEntryToSerializedLength,
119
tokenRequestToTokenTypeEntry,
12-
} from './index.js';
13-
import { Issuer as Type1Issuer, TokenRequest as Type1TokenRequest } from './priv_verif_token.js';
14-
import { Issuer as Type2Issuer, TokenRequest as Type2TokenRequest } from './pub_verif_token.js';
10+
} from './token_types.js';
11+
import type { Token, TokenTypeValue } from './auth_scheme/private_token.js';
12+
import {
13+
Issuer as Type1Issuer,
14+
TokenRequest as Type1TokenRequest,
15+
TokenResponse as Type1TokenResponse,
16+
} from './priv_verif_token.js';
17+
import {
18+
Issuer as Type2Issuer,
19+
TokenRequest as Type2TokenRequest,
20+
TokenResponse as Type2TokenResponse,
21+
} from './pub_verif_token.js';
1522
import { joinAll } from './util.js';
1623

1724
const TokenStatus = {
@@ -142,12 +149,7 @@ export class OptionalTokenResponse {
142149
// GenericTokenResponse generic_token_response; /* Defined by token_type */
143150
// }
144151
// } OptionalTokenResponse;
145-
constructor(
146-
public readonly tokenResponse:
147-
| null
148-
| publicVerif.TokenResponse
149-
| privateVerif.TokenResponse,
150-
) {}
152+
constructor(public readonly tokenResponse: null | Type1TokenResponse | Type2TokenResponse) {}
151153

152154
static deserialize(bytes: Uint8Array): OptionalTokenResponse {
153155
if (bytes.length === 0) {
@@ -173,8 +175,8 @@ export class OptionalTokenResponse {
173175
const responseBytes = bytes.slice(3);
174176
const response =
175177
tokenType === TOKEN_TYPES.VOPRF.value
176-
? privateVerif.TokenResponse.deserialize(responseBytes)
177-
: publicVerif.TokenResponse.deserialize(responseBytes);
178+
? Type1TokenResponse.deserialize(responseBytes)
179+
: Type2TokenResponse.deserialize(responseBytes);
178180
return new OptionalTokenResponse(response);
179181
}
180182
default:
@@ -288,10 +290,13 @@ export class Issuer {
288290
tokenType: TokenTypeValue,
289291
truncatedTokenKeyId: number,
290292
): Promise<Type1Issuer | Type2Issuer> {
291-
if (![TOKEN_TYPES.VOPRF.value, TOKEN_TYPES.BLIND_RSA.value].includes(tokenType)) {
293+
if (tokenType !== TOKEN_TYPES.VOPRF.value && tokenType !== TOKEN_TYPES.BLIND_RSA.value) {
292294
throw new Error('unsupported token type');
293295
}
294-
const issuers = this.issuers[tokenType as 1 | 2];
296+
const issuers =
297+
tokenType === TOKEN_TYPES.VOPRF.value
298+
? this.issuers[TOKEN_TYPES.VOPRF.value]
299+
: this.issuers[TOKEN_TYPES.BLIND_RSA.value];
295300
for (const issuer of issuers) {
296301
// "truncated_token_key_id" is the least significant byte of the
297302
// token_key_id in network byte order (in other words, the

src/index.ts

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the Apache-2.0 license found in the LICENSE file or at https://opensource.org/licenses/Apache-2.0
33

44
import { base64url } from 'rfc4648';
5-
import { WWWAuthenticateHeader, type TokenTypeEntry } from './auth_scheme/private_token.js';
5+
import { WWWAuthenticateHeader } from './auth_scheme/private_token.js';
66
import {
77
Client as PublicVerifClient,
88
BLIND_RSA,
@@ -11,9 +11,9 @@ import {
1111
} from './pub_verif_token.js';
1212
import { Client as PrivateVerifClient, VOPRF } from './priv_verif_token.js';
1313
import { fetchToken, type PrivacyPassClient } from './issuance.js';
14-
import { convertEncToRSASSAPSS, convertRSASSAPSSToEnc } from './util.js';
14+
export { tokenEntryToSerializedLength } from './token_types.js';
15+
import { tokenRequestToTokenTypeEntry as tokenRequestToTokenTypeEntryBase } from './token_types.js';
1516

16-
export const util = { convertEncToRSASSAPSS, convertRSASSAPSSToEnc };
1717
export * from './auth_scheme/private_token.js';
1818
export * from './issuance.js';
1919
export * as genericBatched from './generic_batched_token.js';
@@ -35,6 +35,10 @@ export const TOKEN_TYPES = {
3535
VOPRF,
3636
} as const;
3737

38+
export function tokenRequestToTokenTypeEntry(bytes: Uint8Array) {
39+
return tokenRequestToTokenTypeEntryBase(bytes, TOKEN_TYPES);
40+
}
41+
3842
// The Privacy Pass HTTP Authentication Scheme
3943
//
4044
// Ref. https://datatracker.ietf.org/doc/draft-ietf-privacypass-auth-scheme/
@@ -83,32 +87,3 @@ export async function header_to_token(header: string): Promise<string | null> {
8387
const encodedToken = base64url.stringify(te.encode(authHeader.toString()));
8488
return encodedToken;
8589
}
86-
87-
export function tokenEntryToSerializedLength(tokenType: TokenTypeEntry): number {
88-
// TokenRequest structure: 2-byte token_type + 1-byte truncated_token_key_id + blinded_msg
89-
const headerLen = 3; // token_type (2) + truncated_token_key_id (1)
90-
switch (tokenType.value) {
91-
case TOKEN_TYPES.VOPRF.value:
92-
return headerLen + VOPRF.Ne;
93-
case TOKEN_TYPES.BLIND_RSA.value:
94-
return headerLen + BLIND_RSA.Nk;
95-
case TOKEN_TYPES.PARTIALLY_BLIND_RSA.value:
96-
return headerLen + PARTIALLY_BLIND_RSA.Nk;
97-
default:
98-
throw new Error(`unrecognized or non-supported token type: ${tokenType.value}`);
99-
}
100-
}
101-
102-
export function tokenRequestToTokenTypeEntry(bytes: Uint8Array): TokenTypeEntry {
103-
// All token requests have a 2-byte value at the beginning of the token describing TokenTypeEntry.
104-
const input = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
105-
106-
const type = input.getUint16(0);
107-
const tokenType = Object.values(TOKEN_TYPES).find((t) => t.value === type);
108-
109-
if (tokenType === undefined) {
110-
throw new Error(`unrecognized or non-supported token type: ${type}`);
111-
}
112-
113-
return tokenType;
114-
}

src/priv_verif_token.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
type TokenTypeValue,
2424
} from './auth_scheme/private_token.js';
2525
import { joinAll } from './util.js';
26-
import { TOKEN_TYPES } from './index.js';
26+
import { VOPRF_TOKEN_TYPE } from './token_types.js';
2727

2828
export interface VOPRFExtraParams {
2929
suite: SuiteID;
@@ -57,12 +57,7 @@ const VOPRF_EXTRA_PARAMS: VOPRFExtraParams = {
5757
//
5858
// https://datatracker.ietf.org/doc/html/draft-ietf-privacypass-protocol-16#name-token-type-voprf-p-384-sha-
5959
export const VOPRF: Readonly<TokenTypeEntry> & VOPRFExtraParams = {
60-
value: 0x0001,
61-
name: 'VOPRF (P-384, SHA-384)',
62-
Nid: 32,
63-
publicVerifiable: false,
64-
publicMetadata: false,
65-
privateMetadata: false,
60+
...VOPRF_TOKEN_TYPE,
6661
...VOPRF_EXTRA_PARAMS,
6762
} as const;
6863

@@ -139,7 +134,7 @@ export class TokenResponse {
139134
// uint8_t evaluate_proof[Ns+Ns];
140135
// } TokenResponse;
141136

142-
public readonly tokenType: number = TOKEN_TYPES.VOPRF.value;
137+
public readonly tokenType: number = VOPRF.value;
143138

144139
constructor(
145140
public readonly evaluateMsg: Uint8Array,

src/pub_verif_token.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
RSAPBSSA,
1010
} from '@cloudflare/blindrsa-ts';
1111

12-
import { convertEncToRSASSAPSS, convertRSASSAPSSToEnc, joinAll } from './util.js';
12+
import { convertEncToRSASSAPSS, convertRSASSAPSSToEnc } from './rsa_util.js';
13+
import { joinAll } from './util.js';
1314
import {
1415
AuthenticatorInput,
1516
Extensions,
@@ -18,14 +19,15 @@ import {
1819
type TokenTypeEntry,
1920
type TokenTypeValue,
2021
} from './auth_scheme/private_token.js';
22+
import { BLIND_RSA_TOKEN_TYPE, PARTIALLY_BLIND_RSA_TOKEN_TYPE } from './token_types.js';
2123

2224
export enum BlindRSAMode {
2325
PSSZero = 0, // Corresponds to RSASSA.SHA384.PSSZero.Deterministic
2426
PSS = 48, // Corresponds to RSASSA.SHA384.PSS.Deterministic
2527
}
2628

2729
export import PartiallyBlindRSAMode = BlindRSAMode;
28-
import { TOKEN_TYPES } from './index.js';
30+
export { convertEncToRSASSAPSS, convertRSASSAPSSToEnc } from './rsa_util.js';
2931

3032
export interface BlindRSAExtraParams {
3133
suite: Record<BlindRSAMode, (params?: BlindRSAPlatformParams) => BlindRSA>;
@@ -66,25 +68,13 @@ const PARTIALLY_BLINDRSA_EXTRA_PARAMS: PartiallyBlindRSAExtraParams = {
6668
// https://datatracker.ietf.org/doc/html/draft-ietf-privacypass-protocol-16#name-token-type-blind-rsa-2048-b',
6769
// https://datatracker.ietf.org/doc/html/draft-hendrickson-privacypass-public-metadata-03#section-8.2
6870
export const BLIND_RSA: Readonly<TokenTypeEntry> & BlindRSAExtraParams = {
69-
value: 0x0002,
70-
name: 'Blind RSA (2048)',
71-
Nk: 256,
72-
Nid: 32,
73-
publicVerifiable: true,
74-
publicMetadata: false,
75-
privateMetadata: false,
71+
...BLIND_RSA_TOKEN_TYPE,
7672
...BLINDRSA_EXTRA_PARAMS,
7773
} as const;
7874
type BlindRSAType = typeof BLIND_RSA;
7975

8076
export const PARTIALLY_BLIND_RSA: Readonly<TokenTypeEntry> & PartiallyBlindRSAExtraParams = {
81-
value: 0xda7a,
82-
name: 'Partially Blind RSA (2048-bit)',
83-
Nk: 256,
84-
Nid: 32,
85-
publicVerifiable: true,
86-
publicMetadata: true,
87-
privateMetadata: false,
77+
...PARTIALLY_BLIND_RSA_TOKEN_TYPE,
8878
...PARTIALLY_BLINDRSA_EXTRA_PARAMS,
8979
} as const;
9080
type PartiallyBlindRSAType = typeof PARTIALLY_BLIND_RSA;
@@ -213,7 +203,7 @@ export class TokenResponse {
213203
// uint8_t blind_sig[Nk];
214204
// } TokenResponse;
215205

216-
public readonly tokenType: number = TOKEN_TYPES.BLIND_RSA.value;
206+
public readonly tokenType: number = BLIND_RSA.value;
217207

218208
constructor(public readonly blindSig: Uint8Array) {
219209
if (blindSig.length !== BLIND_RSA.Nk) {

src/rsa_util.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) 2023 Cloudflare, Inc.
2+
// Licensed under the Apache-2.0 license found in the LICENSE file or at https://opensource.org/licenses/Apache-2.0
3+
4+
import * as asn1js from 'asn1js';
5+
6+
// Converts a RSA-PSS key into a RSA Encryption key.
7+
// Required because WebCrypto do not support importing keys with `RSASSA-PSS` OID,
8+
//
9+
// Chromium: https://www.chromium.org/blink/webcrypto/#supported-key-formats
10+
// Firefox: https://github.com/mozilla/pkipolicy/blob/master/rootstore/policy.md#511-rsa
11+
// WebCrypto: https://github.com/w3c/webcrypto/pull/325
12+
//
13+
// Documentation: https://www.rfc-editor.org/rfc/rfc4055#section-6
14+
export function convertRSASSAPSSToEnc(keyRSAPSSEncSpki: Uint8Array): Uint8Array {
15+
const RSAEncryptionAlgID = '1.2.840.113549.1.1.1';
16+
const schema = new asn1js.Sequence({
17+
value: [
18+
new asn1js.Sequence({ name: 'algorithm' }),
19+
new asn1js.BitString({ name: 'subjectPublicKey' }),
20+
],
21+
});
22+
const cmp = asn1js.verifySchema(keyRSAPSSEncSpki, schema);
23+
if (!cmp.verified) {
24+
throw new Error('bad parsing RSA-PSS key');
25+
}
26+
27+
const keyASN = new asn1js.Sequence({
28+
value: [
29+
new asn1js.Sequence({
30+
value: [
31+
new asn1js.ObjectIdentifier({ value: RSAEncryptionAlgID }),
32+
new asn1js.Null(),
33+
],
34+
}),
35+
cmp.result.subjectPublicKey,
36+
],
37+
});
38+
39+
return new Uint8Array(keyASN.toBER());
40+
}
41+
42+
function algorithm_RSASSA_PSS() {
43+
const RSAPSSAlgID = '1.2.840.113549.1.1.10';
44+
const publicKeyParams = new asn1js.Sequence({
45+
value: [
46+
new asn1js.Constructed({
47+
idBlock: {
48+
tagClass: 3, // CONTEXT-SPECIFIC
49+
tagNumber: 0, // [0]
50+
},
51+
value: [
52+
new asn1js.Sequence({
53+
value: [
54+
new asn1js.ObjectIdentifier({ value: '2.16.840.1.101.3.4.2.2' }), // sha-384
55+
],
56+
}),
57+
],
58+
}),
59+
new asn1js.Constructed({
60+
idBlock: {
61+
tagClass: 3, // CONTEXT-SPECIFIC
62+
tagNumber: 1, // [1]
63+
},
64+
value: [
65+
new asn1js.Sequence({
66+
value: [
67+
new asn1js.ObjectIdentifier({ value: '1.2.840.113549.1.1.8' }), // pkcs1-MGF
68+
new asn1js.Sequence({
69+
value: [
70+
new asn1js.ObjectIdentifier({
71+
value: '2.16.840.1.101.3.4.2.2',
72+
}), // sha-384
73+
],
74+
}),
75+
],
76+
}),
77+
],
78+
}),
79+
new asn1js.Constructed({
80+
idBlock: {
81+
tagClass: 3, // CONTEXT-SPECIFIC
82+
tagNumber: 2, // [2]
83+
},
84+
value: [
85+
new asn1js.Integer({ value: 48 }), // sLen = 48
86+
],
87+
}),
88+
],
89+
});
90+
91+
return new asn1js.Sequence({
92+
value: [new asn1js.ObjectIdentifier({ value: RSAPSSAlgID }), publicKeyParams],
93+
});
94+
}
95+
96+
// Exports a RSA-PSS key as RSASSA-PSS.
97+
// This is required because browsers do not support exporting RSA-PSS keys.
98+
//
99+
// Chromium: https://www.chromium.org/blink/webcrypto/#supported-key-formats
100+
// Firefox: https://github.com/mozilla/pkipolicy/blob/master/rootstore/policy.md#511-rsa
101+
//
102+
// Documentation: https://www.rfc-editor.org/rfc/rfc4055#section-6
103+
export function convertEncToRSASSAPSS(keyEncRSAPSSSpki: Uint8Array): Uint8Array {
104+
const schema = new asn1js.Sequence({
105+
value: [
106+
new asn1js.Sequence({ name: 'algorithm' }),
107+
new asn1js.BitString({ name: 'subjectPublicKey' }),
108+
],
109+
});
110+
111+
const cmp = asn1js.verifySchema(keyEncRSAPSSSpki, schema);
112+
if (!cmp.verified) {
113+
throw new Error('bad parsing EncRSA key');
114+
}
115+
116+
const algorithmID = algorithm_RSASSA_PSS();
117+
const asn = new asn1js.Sequence({
118+
value: [algorithmID, cmp.result.subjectPublicKey],
119+
});
120+
121+
return new Uint8Array(asn.toBER());
122+
}

0 commit comments

Comments
 (0)