@tinfoilsh/passkey-kit 0.1.0 → 0.2.1

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.
Files changed (50) hide show
  1. package/README.md +138 -81
  2. package/dist/crypto.d.ts +11 -54
  3. package/dist/crypto.d.ts.map +1 -1
  4. package/dist/crypto.js +146 -97
  5. package/dist/crypto.js.map +1 -1
  6. package/dist/errors.d.ts +11 -21
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +10 -27
  9. package/dist/errors.js.map +1 -1
  10. package/dist/index.d.ts +7 -10
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -7
  13. package/dist/index.js.map +1 -1
  14. package/dist/kit.d.ts +2 -62
  15. package/dist/kit.d.ts.map +1 -1
  16. package/dist/kit.js +331 -155
  17. package/dist/kit.js.map +1 -1
  18. package/dist/storage.d.ts +15 -29
  19. package/dist/storage.d.ts.map +1 -1
  20. package/dist/storage.js +113 -62
  21. package/dist/storage.js.map +1 -1
  22. package/dist/support.d.ts +2 -14
  23. package/dist/support.d.ts.map +1 -1
  24. package/dist/support.js +32 -47
  25. package/dist/support.js.map +1 -1
  26. package/dist/types.d.ts +85 -89
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/webauthn.d.ts +10 -33
  29. package/dist/webauthn.d.ts.map +1 -1
  30. package/dist/webauthn.js +89 -177
  31. package/dist/webauthn.js.map +1 -1
  32. package/dist/wrapped-key-record-codec.d.ts +4 -0
  33. package/dist/wrapped-key-record-codec.d.ts.map +1 -0
  34. package/dist/wrapped-key-record-codec.js +89 -0
  35. package/dist/wrapped-key-record-codec.js.map +1 -0
  36. package/package.json +4 -2
  37. package/src/crypto.ts +171 -137
  38. package/src/errors.ts +29 -33
  39. package/src/index.ts +26 -41
  40. package/src/kit.ts +431 -260
  41. package/src/storage.ts +126 -60
  42. package/src/support.ts +36 -50
  43. package/src/types.ts +99 -99
  44. package/src/webauthn.ts +121 -237
  45. package/src/wrapped-key-record-codec.ts +93 -0
  46. package/dist/protocol.d.ts +0 -13
  47. package/dist/protocol.d.ts.map +0 -1
  48. package/dist/protocol.js +0 -13
  49. package/dist/protocol.js.map +0 -1
  50. package/src/protocol.ts +0 -15
package/src/webauthn.ts CHANGED
@@ -1,258 +1,142 @@
1
- /**
2
- * WebAuthn PRF ceremonies: credential creation and assertion with the PRF
3
- * extension. Pure ceremony logic — persistence of the results is handled
4
- * by the kit through the hooks on {@link CeremonyContext}.
5
- */
1
+ import { base64UrlToBytes, bufferSourceToArrayBuffer, bytesToBase64Url } from "./codec.js";
2
+ import { invalidInput, PasskeyKeyError } from "./errors.js";
3
+ import type { PasskeyUser } from "./types.js";
6
4
 
7
- import {
8
- base64UrlToBytes,
9
- bufferSourceToArrayBuffer,
10
- bytesToBase64Url,
11
- } from "./codec.js";
12
- import {
13
- PasskeyKitError,
14
- PasskeyTimeoutError,
15
- PrfNotSupportedError,
16
- } from "./errors.js";
17
- import type {
18
- PasskeyKitErrorMessages,
19
- PasskeyKitLogger,
20
- PasskeyUser,
21
- PrfPasskeyResult,
22
- } from "./types.js";
5
+ const CHALLENGE_BYTES = 32;
6
+ const MAX_USER_HANDLE_BYTES = 64;
7
+ const PRF_OUTPUT_BYTES = 32;
23
8
 
24
9
  export interface CeremonyContext {
25
10
  rpId: string;
26
11
  rpName: string;
27
- /** Salt passed to PRF eval.first — the client internally computes
28
- * SHA-256("WebAuthn PRF" || 0x00 || salt). */
29
- prfSalt: Uint8Array;
30
- webauthnTimeoutMs: number;
31
- stuckTimeoutMs: number;
32
- errorMessages?: PasskeyKitErrorMessages;
33
- logger: PasskeyKitLogger;
34
- /** Invoked after every successful PRF ceremony so the kit can cache state. */
35
- onPrfResult(result: PrfPasskeyResult, credential: PublicKeyCredential): void;
12
+ prfInput: Uint8Array;
13
+ timeoutMs: number;
36
14
  }
37
15
 
38
- const MAX_USER_HANDLE_BYTE_COUNT = 64;
39
-
40
- async function withStuckTimeout<T>(
41
- promise: Promise<T>,
42
- ctx: CeremonyContext,
43
- ): Promise<T> {
44
- let timer: ReturnType<typeof setTimeout> | undefined;
45
- try {
46
- return await Promise.race([
47
- promise,
48
- new Promise<T>((_, reject) => {
49
- timer = setTimeout(
50
- () => reject(new PasskeyTimeoutError(ctx.errorMessages?.timeout)),
51
- ctx.stuckTimeoutMs,
52
- );
53
- }),
54
- ]);
55
- } finally {
56
- if (timer !== undefined) clearTimeout(timer);
57
- }
16
+ export interface InternalPrfResult {
17
+ credentialId: string;
18
+ prfOutput: Uint8Array;
19
+ isPlatformAuthenticator: boolean;
58
20
  }
59
21
 
60
- /**
61
- * Create a new PRF-capable passkey for the given user.
62
- *
63
- * Returns the credential ID and PRF output, or null if the user cancels.
64
- * Throws {@link PrfNotSupportedError} when the authenticator cannot supply
65
- * PRF output and {@link PasskeyTimeoutError} when the provider hangs.
66
- */
67
- export async function createPrfPasskey(
68
- ctx: CeremonyContext,
69
- user: PasskeyUser,
70
- ): Promise<PrfPasskeyResult | null> {
71
- const userIdBytes = new TextEncoder().encode(user.id);
72
- if (userIdBytes.byteLength > MAX_USER_HANDLE_BYTE_COUNT) {
73
- throw new PasskeyKitError(
74
- `passkey-kit: user id must be at most ${MAX_USER_HANDLE_BYTE_COUNT} UTF-8 bytes`,
75
- );
76
- }
77
-
78
- try {
79
- const credential = (await withStuckTimeout(
80
- navigator.credentials.create({
81
- publicKey: {
82
- challenge: crypto.getRandomValues(new Uint8Array(32)),
83
- rp: { id: ctx.rpId, name: ctx.rpName },
84
- user: {
85
- id: userIdBytes,
86
- name: user.name,
87
- displayName: user.displayName || user.name,
88
- },
89
- pubKeyCredParams: [
90
- { type: "public-key", alg: -7 }, // ES256
91
- { type: "public-key", alg: -257 }, // RS256 (broader compat)
92
- ],
93
- authenticatorSelection: {
94
- residentKey: "preferred",
95
- userVerification: "required",
96
- },
97
- timeout: ctx.webauthnTimeoutMs,
98
- extensions: {
99
- prf: { eval: { first: ctx.prfSalt as BufferSource } },
100
- },
101
- },
102
- }),
103
- ctx,
104
- )) as PublicKeyCredential | null;
105
-
106
- if (!credential) {
107
- return null;
108
- }
22
+ interface PrfExtensionResults {
23
+ prf?: {
24
+ enabled?: boolean;
25
+ results?: { first?: BufferSource };
26
+ };
27
+ }
109
28
 
110
- const extensionResults = credential.getClientExtensionResults();
111
- const prfResults = extensionResults.prf;
29
+ function extensionResults(credential: PublicKeyCredential): PrfExtensionResults {
30
+ return credential.getClientExtensionResults() as PrfExtensionResults;
31
+ }
112
32
 
113
- if (!prfResults?.enabled) {
114
- ctx.logger.info?.("Authenticator does not support PRF", {
115
- action: "createPrfPasskey",
116
- });
117
- throw new PrfNotSupportedError(ctx.errorMessages?.prfNotSupported);
118
- }
33
+ function credentialId(credential: PublicKeyCredential): string {
34
+ const rawId = new Uint8Array(credential.rawId);
35
+ if (rawId.length === 0) throw invalidInput("credential ID must not be empty");
36
+ return bytesToBase64Url(rawId);
37
+ }
119
38
 
120
- const credentialId = bytesToBase64Url(new Uint8Array(credential.rawId));
39
+ function resultFromCredential(credential: PublicKeyCredential): InternalPrfResult {
40
+ const first = extensionResults(credential).prf?.results?.first;
41
+ if (!first) {
42
+ throw new PasskeyKeyError("unsupported", "the authenticator returned no PRF output");
43
+ }
44
+ const prfOutput = new Uint8Array(bufferSourceToArrayBuffer(first));
45
+ if (prfOutput.length !== PRF_OUTPUT_BYTES) {
46
+ throw invalidInput(`PRF output must be ${PRF_OUTPUT_BYTES} bytes`);
47
+ }
48
+ return {
49
+ credentialId: credentialId(credential),
50
+ prfOutput,
51
+ isPlatformAuthenticator: credential.authenticatorAttachment === "platform",
52
+ };
53
+ }
121
54
 
122
- // Some authenticators return PRF results during creation, others don't.
123
- // "Not all authenticators support evaluating the PRFs during credential
124
- // creation so outputs may, or may not, be provided."
125
- // — https://w3c.github.io/webauthn/#prf-extension (eval description)
126
- if (prfResults.results?.first) {
127
- const result: PrfPasskeyResult = {
128
- credentialId,
129
- prfOutput: bufferSourceToArrayBuffer(prfResults.results.first),
130
- };
131
- ctx.onPrfResult(result, credential);
132
- return result;
133
- }
55
+ export async function createPrfCredential(
56
+ context: CeremonyContext,
57
+ user: PasskeyUser,
58
+ signal: AbortSignal,
59
+ ): Promise<InternalPrfResult> {
60
+ if (!(user?.id instanceof Uint8Array) || user.id.length === 0) {
61
+ throw invalidInput("user.id must be a non-empty Uint8Array");
62
+ }
63
+ if (user.id.length > MAX_USER_HANDLE_BYTES) {
64
+ throw invalidInput(`user.id must be at most ${MAX_USER_HANDLE_BYTES} bytes`);
65
+ }
66
+ if (typeof user.name !== "string" || user.name.length === 0) {
67
+ throw invalidInput("user.name must be a non-empty string");
68
+ }
69
+ if (user.displayName !== undefined && (typeof user.displayName !== "string" || !user.displayName)) {
70
+ throw invalidInput("user.displayName must be a non-empty string");
71
+ }
134
72
 
135
- // PRF enabled but no results during create — do an immediate get()
136
- ctx.logger.info?.(
137
- "PRF enabled but no results during creation, doing immediate auth",
138
- { action: "createPrfPasskey" },
73
+ const credential = (await navigator.credentials.create({
74
+ signal,
75
+ publicKey: {
76
+ challenge: crypto.getRandomValues(new Uint8Array(CHALLENGE_BYTES)),
77
+ rp: { id: context.rpId, name: context.rpName },
78
+ user: {
79
+ id: user.id.slice(),
80
+ name: user.name,
81
+ displayName: user.displayName ?? user.name,
82
+ },
83
+ pubKeyCredParams: [
84
+ { type: "public-key", alg: -7 },
85
+ { type: "public-key", alg: -257 },
86
+ ],
87
+ authenticatorSelection: {
88
+ authenticatorAttachment: "platform",
89
+ residentKey: "preferred",
90
+ userVerification: "required",
91
+ },
92
+ timeout: context.timeoutMs,
93
+ extensions: {
94
+ prf: { eval: { first: context.prfInput.slice() } },
95
+ } as AuthenticationExtensionsClientInputs,
96
+ },
97
+ })) as PublicKeyCredential | null;
98
+
99
+ if (!credential) {
100
+ throw new PasskeyKeyError(
101
+ "cancelled",
102
+ "credential creation was cancelled or no eligible credential was available",
139
103
  );
140
- // Pass throwOnCancel so a user-cancelled assertion surfaces as a
141
- // DOMException we can handle below — otherwise a `null` return would
142
- // be indistinguishable from "provider returned no PRF output" and we'd
143
- // show the misleading "PRF not supported" error for a plain cancel.
144
- const postCreateAuth = await authenticatePrfPasskey(ctx, [credentialId], {
145
- throwOnCancel: true,
146
- });
147
- if (!postCreateAuth) {
148
- // The provider claimed PRF support during creation but didn't deliver
149
- // a PRF output on the immediately-following assertion. Treat this as
150
- // a lack of real PRF support rather than a silent failure.
151
- throw new PrfNotSupportedError(ctx.errorMessages?.prfNotSupported);
152
- }
153
- return postCreateAuth;
154
- } catch (error) {
155
- if (error instanceof PrfNotSupportedError) throw error;
156
- if (error instanceof PasskeyTimeoutError) throw error;
157
-
158
- // DOMException with name "NotAllowedError" means the user cancelled
159
- if (error instanceof DOMException && error.name === "NotAllowedError") {
160
- ctx.logger.info?.("User cancelled passkey creation", {
161
- action: "createPrfPasskey",
162
- });
163
- return null;
164
- }
165
-
166
- ctx.logger.error?.("Failed to create PRF passkey", error, {
167
- action: "createPrfPasskey",
168
- });
169
- throw error;
170
104
  }
105
+ const extension = extensionResults(credential).prf;
106
+ if (!extension?.enabled) {
107
+ throw new PasskeyKeyError("unsupported", "the authenticator does not support PRF");
108
+ }
109
+ if (extension.results?.first) return resultFromCredential(credential);
110
+
111
+ return evaluatePrfCredential(context, [credentialId(credential)], signal);
171
112
  }
172
113
 
173
- /**
174
- * Authenticate with an existing PRF passkey to derive the PRF output.
175
- *
176
- * @param credentialIds - base64url-encoded credential IDs to allow. Pass all
177
- * known PRF credential IDs so the browser can select the right one.
178
- * @returns The matched credential ID and PRF output, or null on failure/cancel.
179
- */
180
- export async function authenticatePrfPasskey(
181
- ctx: CeremonyContext,
114
+ export async function evaluatePrfCredential(
115
+ context: CeremonyContext,
182
116
  credentialIds: string[],
183
- options: { throwOnCancel?: boolean } = {},
184
- ): Promise<PrfPasskeyResult | null> {
185
- const { throwOnCancel = false } = options;
186
- const allowCredentials: PublicKeyCredentialDescriptor[] = credentialIds.map(
187
- (id) => ({
188
- id: base64UrlToBytes(id) as BufferSource,
189
- type: "public-key",
190
- }),
191
- );
192
-
193
- try {
194
- const assertion = (await withStuckTimeout(
195
- navigator.credentials.get({
196
- publicKey: {
197
- challenge: crypto.getRandomValues(new Uint8Array(32)),
198
- rpId: ctx.rpId,
199
- allowCredentials,
200
- userVerification: "required",
201
- timeout: ctx.webauthnTimeoutMs,
202
- extensions: {
203
- prf: { eval: { first: ctx.prfSalt as BufferSource } },
204
- },
205
- },
206
- }),
207
- ctx,
208
- )) as PublicKeyCredential | null;
209
-
210
- if (!assertion) {
211
- ctx.logger.info?.("passkey assertion returned no credential", {
212
- action: "authenticatePrfPasskey",
213
- allowedCredentials: credentialIds.length,
214
- });
215
- return null;
216
- }
217
-
218
- const extensionResults = assertion.getClientExtensionResults();
219
- const prfOutput = extensionResults.prf?.results?.first;
220
-
221
- if (!prfOutput) {
222
- ctx.logger.error?.("PRF output missing from assertion", undefined, {
223
- action: "authenticatePrfPasskey",
224
- });
225
- throw new PrfNotSupportedError(ctx.errorMessages?.prfNotSupported);
226
- }
227
-
228
- const result: PrfPasskeyResult = {
229
- credentialId: bytesToBase64Url(new Uint8Array(assertion.rawId)),
230
- prfOutput: bufferSourceToArrayBuffer(prfOutput),
231
- };
232
- ctx.onPrfResult(result, assertion);
233
- return result;
234
- } catch (error) {
235
- if (error instanceof PasskeyTimeoutError) throw error;
236
-
237
- if (error instanceof DOMException && error.name === "NotAllowedError") {
238
- // NotAllowedError covers both a user cancel and the case where the
239
- // provider has no usable credential for any of the allowed ids
240
- // (e.g. the passkey was created in a different browser/profile and
241
- // never persisted on this device).
242
- ctx.logger.info?.(
243
- "passkey authentication not allowed (cancelled or no usable credential)",
244
- {
245
- action: "authenticatePrfPasskey",
246
- allowedCredentials: credentialIds.length,
247
- },
248
- );
249
- if (throwOnCancel) throw error;
250
- return null;
251
- }
252
-
253
- ctx.logger.error?.("Failed to authenticate with PRF passkey", error, {
254
- action: "authenticatePrfPasskey",
255
- });
256
- throw error;
117
+ signal: AbortSignal,
118
+ ): Promise<InternalPrfResult> {
119
+ const assertion = (await navigator.credentials.get({
120
+ signal,
121
+ publicKey: {
122
+ challenge: crypto.getRandomValues(new Uint8Array(CHALLENGE_BYTES)),
123
+ rpId: context.rpId,
124
+ allowCredentials: credentialIds.map((credentialId) => ({
125
+ type: "public-key",
126
+ id: base64UrlToBytes(credentialId) as BufferSource,
127
+ })),
128
+ userVerification: "required",
129
+ timeout: context.timeoutMs,
130
+ extensions: {
131
+ prf: { eval: { first: context.prfInput.slice() } },
132
+ } as AuthenticationExtensionsClientInputs,
133
+ },
134
+ })) as PublicKeyCredential | null;
135
+ if (!assertion) {
136
+ throw new PasskeyKeyError(
137
+ "cancelled",
138
+ "credential evaluation was cancelled or no eligible credential was available",
139
+ );
257
140
  }
141
+ return resultFromCredential(assertion);
258
142
  }
@@ -0,0 +1,93 @@
1
+ import { bytesToBase64Url } from "./codec.js";
2
+ import {
3
+ copyAndValidateProfile,
4
+ decodeCanonicalBase64Url,
5
+ PROFILE_KEYS,
6
+ validateWrappedKey,
7
+ } from "./crypto.js";
8
+ import { invalidInput } from "./errors.js";
9
+ import type { WrappedKey, WrappedKeyRecord } from "./types.js";
10
+
11
+ const RECORD_FIELDS = [
12
+ "version",
13
+ "profile",
14
+ "credentialId",
15
+ "kekIvHex",
16
+ "wrappedKeyHex",
17
+ ] as const;
18
+ function hasExactFields(value: unknown, fields: readonly string[]): value is Record<string, unknown> {
19
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
20
+ const keys = Object.keys(value).sort();
21
+ const expected = [...fields].sort();
22
+ return keys.length === expected.length && keys.every((key, index) => key === expected[index]);
23
+ }
24
+
25
+ export function encodeWrappedKeyRecord(wrappedKey: WrappedKey): string {
26
+ if (!wrappedKey || typeof wrappedKey !== "object") {
27
+ throw invalidInput("wrapped key is required");
28
+ }
29
+ const profile = copyAndValidateProfile(wrappedKey.profile);
30
+ validateWrappedKey(wrappedKey, profile);
31
+ const record: WrappedKeyRecord = {
32
+ version: 1,
33
+ profile: {
34
+ version: 1,
35
+ relyingPartyId: profile.relyingPartyId,
36
+ prfSalt: bytesToBase64Url(profile.prfSalt),
37
+ hkdfInfo: bytesToBase64Url(profile.hkdfInfo),
38
+ },
39
+ credentialId: wrappedKey.credentialId,
40
+ kekIvHex: wrappedKey.kekIvHex,
41
+ wrappedKeyHex: wrappedKey.wrappedKeyHex,
42
+ };
43
+ return JSON.stringify(record);
44
+ }
45
+
46
+ export function decodeWrappedKeyRecord(json: string): WrappedKey {
47
+ if (typeof json !== "string") throw invalidInput("wrapped key JSON must be a string");
48
+ let parsed: unknown;
49
+ try {
50
+ parsed = JSON.parse(json);
51
+ } catch {
52
+ throw invalidInput("wrapped key JSON is malformed");
53
+ }
54
+ if (!hasExactFields(parsed, RECORD_FIELDS)) {
55
+ throw invalidInput("wrapped key record has unexpected fields");
56
+ }
57
+ if (parsed.version !== 1) throw invalidInput("wrapped key record version must be 1");
58
+ if (!hasExactFields(parsed.profile, PROFILE_KEYS)) {
59
+ throw invalidInput("wrapped key profile record has unexpected fields");
60
+ }
61
+ if (parsed.profile.version !== 1) {
62
+ throw invalidInput("wrapped key profile version must be 1");
63
+ }
64
+ if (typeof parsed.profile.relyingPartyId !== "string") {
65
+ throw invalidInput("profile.relyingPartyId must be a string");
66
+ }
67
+ if (typeof parsed.credentialId !== "string") {
68
+ throw invalidInput("credentialId must be a string");
69
+ }
70
+ if (typeof parsed.kekIvHex !== "string") {
71
+ throw invalidInput("kekIvHex must be a string");
72
+ }
73
+ if (typeof parsed.wrappedKeyHex !== "string") {
74
+ throw invalidInput("wrappedKeyHex must be a string");
75
+ }
76
+ const wrappedKey: WrappedKey = {
77
+ profile: {
78
+ version: 1,
79
+ relyingPartyId: parsed.profile.relyingPartyId,
80
+ prfSalt: decodeCanonicalBase64Url(parsed.profile.prfSalt, "profile.prfSalt"),
81
+ hkdfInfo: decodeCanonicalBase64Url(parsed.profile.hkdfInfo, "profile.hkdfInfo"),
82
+ },
83
+ credentialId: parsed.credentialId,
84
+ kekIvHex: parsed.kekIvHex,
85
+ wrappedKeyHex: parsed.wrappedKeyHex,
86
+ };
87
+ const profile = copyAndValidateProfile(wrappedKey.profile);
88
+ validateWrappedKey(wrappedKey, profile);
89
+ return {
90
+ ...wrappedKey,
91
+ profile,
92
+ };
93
+ }
@@ -1,13 +0,0 @@
1
- /**
2
- * Tinfoil v1 protocol constants. Every client wrapping the same CEK must
3
- * use identical values: the PRF salt input and HKDF info string both feed
4
- * the KEK derivation, so changing either changes every derived KEK.
5
- * Override both to establish a new protocol domain.
6
- */
7
- /** Input to the WebAuthn PRF `eval.first` salt. */
8
- export declare const TINFOIL_PRF_SALT_INPUT_V1 = "tinfoil-chat-key-encryption";
9
- /** HKDF info string for domain separation when deriving the KEK. */
10
- export declare const TINFOIL_HKDF_INFO_V1 = "tinfoil-chat-kek-v1";
11
- /** HKDF info string for deriving a stable public CEK identifier. */
12
- export declare const TINFOIL_KEY_ID_INFO_V1 = "tinfoil-key-id-v1";
13
- //# sourceMappingURL=protocol.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,gCAAgC,CAAC;AAEvE,oEAAoE;AACpE,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAE1D,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,sBAAsB,CAAC"}
package/dist/protocol.js DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Tinfoil v1 protocol constants. Every client wrapping the same CEK must
3
- * use identical values: the PRF salt input and HKDF info string both feed
4
- * the KEK derivation, so changing either changes every derived KEK.
5
- * Override both to establish a new protocol domain.
6
- */
7
- /** Input to the WebAuthn PRF `eval.first` salt. */
8
- export const TINFOIL_PRF_SALT_INPUT_V1 = "tinfoil-chat-key-encryption";
9
- /** HKDF info string for domain separation when deriving the KEK. */
10
- export const TINFOIL_HKDF_INFO_V1 = "tinfoil-chat-kek-v1";
11
- /** HKDF info string for deriving a stable public CEK identifier. */
12
- export const TINFOIL_KEY_ID_INFO_V1 = "tinfoil-key-id-v1";
13
- //# sourceMappingURL=protocol.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mDAAmD;AACnD,MAAM,CAAC,MAAM,yBAAyB,GAAG,6BAA6B,CAAC;AAEvE,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AAE1D,oEAAoE;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC"}
package/src/protocol.ts DELETED
@@ -1,15 +0,0 @@
1
- /**
2
- * Tinfoil v1 protocol constants. Every client wrapping the same CEK must
3
- * use identical values: the PRF salt input and HKDF info string both feed
4
- * the KEK derivation, so changing either changes every derived KEK.
5
- * Override both to establish a new protocol domain.
6
- */
7
-
8
- /** Input to the WebAuthn PRF `eval.first` salt. */
9
- export const TINFOIL_PRF_SALT_INPUT_V1 = "tinfoil-chat-key-encryption";
10
-
11
- /** HKDF info string for domain separation when deriving the KEK. */
12
- export const TINFOIL_HKDF_INFO_V1 = "tinfoil-chat-kek-v1";
13
-
14
- /** HKDF info string for deriving a stable public CEK identifier. */
15
- export const TINFOIL_KEY_ID_INFO_V1 = "tinfoil-key-id-v1";