@tinfoilsh/passkey-kit 0.1.0

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 (53) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +127 -0
  3. package/dist/codec.d.ts +12 -0
  4. package/dist/codec.d.ts.map +1 -0
  5. package/dist/codec.js +61 -0
  6. package/dist/codec.js.map +1 -0
  7. package/dist/crypto.d.ts +55 -0
  8. package/dist/crypto.d.ts.map +1 -0
  9. package/dist/crypto.js +105 -0
  10. package/dist/crypto.js.map +1 -0
  11. package/dist/errors.d.ts +23 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +33 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +11 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +8 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/kit.d.ts +63 -0
  20. package/dist/kit.d.ts.map +1 -0
  21. package/dist/kit.js +199 -0
  22. package/dist/kit.js.map +1 -0
  23. package/dist/protocol.d.ts +13 -0
  24. package/dist/protocol.d.ts.map +1 -0
  25. package/dist/protocol.js +13 -0
  26. package/dist/protocol.js.map +1 -0
  27. package/dist/storage.d.ts +31 -0
  28. package/dist/storage.d.ts.map +1 -0
  29. package/dist/storage.js +68 -0
  30. package/dist/storage.js.map +1 -0
  31. package/dist/support.d.ts +15 -0
  32. package/dist/support.d.ts.map +1 -0
  33. package/dist/support.js +58 -0
  34. package/dist/support.js.map +1 -0
  35. package/dist/types.d.ts +101 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +2 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/webauthn.d.ts +38 -0
  40. package/dist/webauthn.d.ts.map +1 -0
  41. package/dist/webauthn.js +189 -0
  42. package/dist/webauthn.js.map +1 -0
  43. package/package.json +55 -0
  44. package/src/codec.ts +71 -0
  45. package/src/crypto.ts +161 -0
  46. package/src/errors.ts +41 -0
  47. package/src/index.ts +47 -0
  48. package/src/kit.ts +333 -0
  49. package/src/protocol.ts +15 -0
  50. package/src/storage.ts +69 -0
  51. package/src/support.ts +61 -0
  52. package/src/types.ts +113 -0
  53. package/src/webauthn.ts +258 -0
@@ -0,0 +1,11 @@
1
+ export { base64ToBytes, base64UrlToBytes, bufferSourceToArrayBuffer, bytesToBase64, bytesToBase64Url, bytesToHex, hexToBytes, } from "./codec.js";
2
+ export { CEK_BYTES, deriveKeyEncryptionKey, deriveKeyId, generateCek, isValidCek, unwrapCek, wrapCek, } from "./crypto.js";
3
+ export { PasskeyKitError, PasskeyTimeoutError, PrfNotSupportedError, } from "./errors.js";
4
+ export { createPasskeyKit } from "./kit.js";
5
+ export type { PasskeyKit } from "./kit.js";
6
+ export { TINFOIL_HKDF_INFO_V1, TINFOIL_KEY_ID_INFO_V1, TINFOIL_PRF_SALT_INPUT_V1, } from "./protocol.js";
7
+ export { browserLocalStorageAdapter, createMemoryStorageAdapter, } from "./storage.js";
8
+ export type { StorageAdapter } from "./storage.js";
9
+ export { detectPrfSupport } from "./support.js";
10
+ export type { EnrollResult, PasskeyKitConfig, PasskeyKitErrorMessages, PasskeyKitLogger, PasskeyKitStorageKeys, PasskeyUser, PrfPasskeyResult, UnlockResult, WrappedCek, } from "./types.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,yBAAyB,EACzB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,qBAAqB,EACrB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { base64ToBytes, base64UrlToBytes, bufferSourceToArrayBuffer, bytesToBase64, bytesToBase64Url, bytesToHex, hexToBytes, } from "./codec.js";
2
+ export { CEK_BYTES, deriveKeyEncryptionKey, deriveKeyId, generateCek, isValidCek, unwrapCek, wrapCek, } from "./crypto.js";
3
+ export { PasskeyKitError, PasskeyTimeoutError, PrfNotSupportedError, } from "./errors.js";
4
+ export { createPasskeyKit } from "./kit.js";
5
+ export { TINFOIL_HKDF_INFO_V1, TINFOIL_KEY_ID_INFO_V1, TINFOIL_PRF_SALT_INPUT_V1, } from "./protocol.js";
6
+ export { browserLocalStorageAdapter, createMemoryStorageAdapter, } from "./storage.js";
7
+ export { detectPrfSupport } from "./support.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,yBAAyB,EACzB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
package/dist/kit.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The main SDK entry point. `createPasskeyKit(config)` binds relying-party
3
+ * configuration, protocol constants, and local persistence into a single
4
+ * object exposing both high-level flows (enroll / unlock / rewrap) and the
5
+ * lower-level ceremony + crypto building blocks.
6
+ */
7
+ import type { EnrollResult, PasskeyKitConfig, PasskeyUser, PrfPasskeyResult, UnlockResult, WrappedCek } from "./types.js";
8
+ export interface PasskeyKit {
9
+ /** Optimistic device/browser PRF support check (cached per kit). */
10
+ isPrfSupported(): Promise<boolean>;
11
+ resetPrfSupportCache(): void;
12
+ /**
13
+ * Create a new PRF-capable passkey. Returns null when the user cancels;
14
+ * throws PrfNotSupportedError / PasskeyTimeoutError otherwise on failure.
15
+ */
16
+ createPasskey(user: PasskeyUser): Promise<PrfPasskeyResult | null>;
17
+ /**
18
+ * Prompt for an assertion against any of the given credential ids and
19
+ * return the matched credential's PRF output. Null on cancel/failure.
20
+ */
21
+ authenticate(credentialIds: string[], options?: {
22
+ throwOnCancel?: boolean;
23
+ }): Promise<PrfPasskeyResult | null>;
24
+ /** Derive the AES-256-GCM KEK from a PRF output (HKDF-SHA-256). */
25
+ deriveKek(prfOutput: ArrayBuffer | Uint8Array): Promise<CryptoKey>;
26
+ /** Create a passkey and wrap the given CEK under it in one flow. */
27
+ enroll(opts: {
28
+ user: PasskeyUser;
29
+ cek: Uint8Array;
30
+ }): Promise<EnrollResult | null>;
31
+ /** Authenticate against the given wrapped CEKs and unwrap the matching one. */
32
+ unlock(wrappedCeks: WrappedCek[]): Promise<UnlockResult | null>;
33
+ /**
34
+ * Unwrap the wrapped CEK matching the cached PRF output without a
35
+ * biometric prompt. Returns null when nothing is cached, no wrapped CEK
36
+ * matches the cached credential, or the cached output fails to unwrap;
37
+ * fall back to `unlock()` in that case.
38
+ */
39
+ unlockWithCachedPrf(wrappedCeks: WrappedCek[]): Promise<UnlockResult | null>;
40
+ /**
41
+ * Re-wrap a CEK using the cached PRF output (no biometric prompt).
42
+ * Returns null when nothing is cached.
43
+ */
44
+ rewrapWithCachedPrf(cek: Uint8Array): Promise<WrappedCek | null>;
45
+ /** Wrap a CEK under the KEK of an explicit PRF result. */
46
+ wrapWithPrfResult(prfResult: PrfPasskeyResult, cek: Uint8Array): Promise<WrappedCek>;
47
+ /** Unwrap a CEK with the KEK of an explicit PRF result. */
48
+ unwrapWithPrfResult(prfResult: PrfPasskeyResult, wrapped: Pick<WrappedCek, "kekIvHex" | "wrappedKeyHex">): Promise<Uint8Array>;
49
+ /**
50
+ * Cached PRF result from local storage, if any. The PRF output is
51
+ * deterministic for a given passkey, so it can be reused to avoid
52
+ * re-prompting biometrics on key updates.
53
+ */
54
+ getCachedPrfResult(): PrfPasskeyResult | null;
55
+ clearCachedPrfResult(): void;
56
+ /** Credential id owned by this device (platform attachment), if known. */
57
+ getLocalCredentialId(): string | null;
58
+ setLocalCredentialId(credentialId: string): void;
59
+ /** Clear all device-local state (e.g. on sign-out). */
60
+ clearLocalState(): void;
61
+ }
62
+ export declare function createPasskeyKit(config: PasskeyKitConfig): PasskeyKit;
63
+ //# sourceMappingURL=kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../src/kit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAEhB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACX,MAAM,YAAY,CAAC;AA0BpB,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,oBAAoB,IAAI,IAAI,CAAC;IAE7B;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnE;;;OAGG;IACH,YAAY,CACV,aAAa,EAAE,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GACpC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpC,mEAAmE;IACnE,SAAS,CAAC,SAAS,EAAE,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAEnE,oEAAoE;IACpE,MAAM,CAAC,IAAI,EAAE;QACX,IAAI,EAAE,WAAW,CAAC;QAClB,GAAG,EAAE,UAAU,CAAC;KACjB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACjC,+EAA+E;IAC/E,MAAM,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE;;;;;OAKG;IACH,mBAAmB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC7E;;;OAGG;IACH,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACjE,0DAA0D;IAC1D,iBAAiB,CACf,SAAS,EAAE,gBAAgB,EAC3B,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,2DAA2D;IAC3D,mBAAmB,CACjB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,eAAe,CAAC,GACtD,OAAO,CAAC,UAAU,CAAC,CAAC;IAEvB;;;;OAIG;IACH,kBAAkB,IAAI,gBAAgB,GAAG,IAAI,CAAC;IAC9C,oBAAoB,IAAI,IAAI,CAAC;IAC7B,0EAA0E;IAC1E,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAAC;IACtC,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,uDAAuD;IACvD,eAAe,IAAI,IAAI,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAyNrE"}
package/dist/kit.js ADDED
@@ -0,0 +1,199 @@
1
+ /**
2
+ * The main SDK entry point. `createPasskeyKit(config)` binds relying-party
3
+ * configuration, protocol constants, and local persistence into a single
4
+ * object exposing both high-level flows (enroll / unlock / rewrap) and the
5
+ * lower-level ceremony + crypto building blocks.
6
+ */
7
+ import { base64ToBytes, bytesToBase64 } from "./codec.js";
8
+ import { deriveKeyEncryptionKey as deriveKekFromPrf, unwrapCek, wrapCek, } from "./crypto.js";
9
+ import { TINFOIL_HKDF_INFO_V1, TINFOIL_PRF_SALT_INPUT_V1 } from "./protocol.js";
10
+ import { browserLocalStorageAdapter } from "./storage.js";
11
+ import { detectPrfSupport } from "./support.js";
12
+ import { authenticatePrfPasskey, createPrfPasskey, } from "./webauthn.js";
13
+ const DEFAULT_STORAGE_KEYS = {
14
+ prfResult: "tinfoil-secret-passkey-prf-output",
15
+ localCredentialId: "tinfoil-local-passkey-credential-id",
16
+ };
17
+ const DEFAULT_WEBAUTHN_TIMEOUT_MS = 30000;
18
+ // Internal hard timeout to guard against providers (e.g. some
19
+ // password-manager browser extensions) that never resolve the
20
+ // credentials.create/get promise. Kept tight so users aren't left staring
21
+ // at an indefinite spinner; the WebAuthn flow itself should complete well
22
+ // within this window once the provider actually prompts.
23
+ const DEFAULT_STUCK_TIMEOUT_MS = 60000;
24
+ export function createPasskeyKit(config) {
25
+ const logger = config.logger ?? {};
26
+ const storage = config.storage === undefined ? browserLocalStorageAdapter : config.storage;
27
+ const storageKeys = {
28
+ ...DEFAULT_STORAGE_KEYS,
29
+ ...config.storageKeys,
30
+ };
31
+ // Byte inputs are snapshotted so later caller-side buffer reuse cannot
32
+ // silently change the PRF domain or KEK derivation between ceremonies.
33
+ const prfSalt = typeof config.prfSaltInput === "string" || config.prfSaltInput === undefined
34
+ ? new TextEncoder().encode(config.prfSaltInput ?? TINFOIL_PRF_SALT_INPUT_V1)
35
+ : config.prfSaltInput.slice();
36
+ const hkdfInfo = config.hkdfInfo === undefined
37
+ ? TINFOIL_HKDF_INFO_V1
38
+ : typeof config.hkdfInfo === "string"
39
+ ? config.hkdfInfo
40
+ : config.hkdfInfo.slice();
41
+ let prfSupportCache = null;
42
+ function cachePrfResult(result) {
43
+ if (!storage)
44
+ return;
45
+ const entry = {
46
+ credentialId: result.credentialId,
47
+ prfOutput: bytesToBase64(new Uint8Array(result.prfOutput)),
48
+ };
49
+ try {
50
+ storage.setItem(storageKeys.prfResult, JSON.stringify(entry));
51
+ }
52
+ catch {
53
+ // best-effort
54
+ }
55
+ }
56
+ function getCachedPrfResult() {
57
+ if (!storage)
58
+ return null;
59
+ try {
60
+ const raw = storage.getItem(storageKeys.prfResult);
61
+ if (!raw)
62
+ return null;
63
+ const entry = JSON.parse(raw);
64
+ return {
65
+ credentialId: entry.credentialId,
66
+ prfOutput: base64ToBytes(entry.prfOutput).buffer,
67
+ };
68
+ }
69
+ catch {
70
+ return null;
71
+ }
72
+ }
73
+ function setLocalCredentialId(credentialId) {
74
+ storage?.setItem(storageKeys.localCredentialId, credentialId);
75
+ }
76
+ // Cross-device hybrid (QR-paired phone, etc.) reports
77
+ // `authenticatorAttachment === 'cross-platform'` on the resulting
78
+ // credential. Caching the cred id in that case would make this
79
+ // device look like it has its own passkey when in reality the user
80
+ // just borrowed another device's passkey for a one-shot unlock.
81
+ // Per WebAuthn L3 §5.2.1, we only treat `authenticatorAttachment`
82
+ // of `'platform'` as "this device truly owns this credential".
83
+ function onPrfResult(result, credential) {
84
+ cachePrfResult(result);
85
+ if (credential.authenticatorAttachment === "platform") {
86
+ try {
87
+ setLocalCredentialId(result.credentialId);
88
+ }
89
+ catch {
90
+ // best-effort: a throwing custom adapter must not discard the
91
+ // ceremony result
92
+ }
93
+ }
94
+ }
95
+ const ceremonyContext = {
96
+ rpId: config.rpId,
97
+ rpName: config.rpName,
98
+ prfSalt,
99
+ webauthnTimeoutMs: config.webauthnTimeoutMs ?? DEFAULT_WEBAUTHN_TIMEOUT_MS,
100
+ stuckTimeoutMs: config.stuckTimeoutMs ?? DEFAULT_STUCK_TIMEOUT_MS,
101
+ errorMessages: config.errorMessages,
102
+ logger,
103
+ onPrfResult,
104
+ };
105
+ const deriveKek = (prfOutput) => deriveKekFromPrf(prfOutput, hkdfInfo);
106
+ async function wrapWithPrfResult(prfResult, cek) {
107
+ const kek = await deriveKek(prfResult.prfOutput);
108
+ return wrapCek({ credentialId: prfResult.credentialId, kek, cek });
109
+ }
110
+ return {
111
+ async isPrfSupported() {
112
+ if (prfSupportCache !== null)
113
+ return prfSupportCache;
114
+ prfSupportCache = await detectPrfSupport();
115
+ return prfSupportCache;
116
+ },
117
+ resetPrfSupportCache() {
118
+ prfSupportCache = null;
119
+ },
120
+ createPasskey(user) {
121
+ return createPrfPasskey(ceremonyContext, user);
122
+ },
123
+ async authenticate(credentialIds, options = {}) {
124
+ // An empty allowCredentials list would start a discoverable-passkey
125
+ // ceremony against ANY credential, breaking the "only the supplied
126
+ // ids" contract.
127
+ if (credentialIds.length === 0)
128
+ return null;
129
+ return authenticatePrfPasskey(ceremonyContext, credentialIds, options);
130
+ },
131
+ deriveKek,
132
+ async enroll(opts) {
133
+ const prfResult = await createPrfPasskey(ceremonyContext, opts.user);
134
+ if (!prfResult)
135
+ return null;
136
+ const wrappedCek = await wrapWithPrfResult(prfResult, opts.cek);
137
+ return { credentialId: prfResult.credentialId, wrappedCek, prfResult };
138
+ },
139
+ async unlock(wrappedCeks) {
140
+ if (wrappedCeks.length === 0)
141
+ return null;
142
+ const prfResult = await authenticatePrfPasskey(ceremonyContext, wrappedCeks.map((w) => w.credentialId));
143
+ if (!prfResult)
144
+ return null;
145
+ const match = wrappedCeks.find((w) => w.credentialId === prfResult.credentialId);
146
+ if (!match) {
147
+ logger.error?.("assertion matched a credential with no wrapped CEK", undefined, { action: "unlock", credentialId: prfResult.credentialId });
148
+ return null;
149
+ }
150
+ const kek = await deriveKek(prfResult.prfOutput);
151
+ const cek = await unwrapCek(kek, match);
152
+ return { credentialId: prfResult.credentialId, cek };
153
+ },
154
+ async unlockWithCachedPrf(wrappedCeks) {
155
+ const cached = getCachedPrfResult();
156
+ if (!cached)
157
+ return null;
158
+ const match = wrappedCeks.find((w) => w.credentialId === cached.credentialId);
159
+ if (!match)
160
+ return null;
161
+ try {
162
+ const kek = await deriveKek(cached.prfOutput);
163
+ const cek = await unwrapCek(kek, match);
164
+ return { credentialId: cached.credentialId, cek };
165
+ }
166
+ catch (error) {
167
+ logger.error?.("failed to unwrap CEK with cached PRF output", error, {
168
+ action: "unlockWithCachedPrf",
169
+ credentialId: cached.credentialId,
170
+ });
171
+ return null;
172
+ }
173
+ },
174
+ async rewrapWithCachedPrf(cek) {
175
+ const cached = getCachedPrfResult();
176
+ if (!cached)
177
+ return null;
178
+ return wrapWithPrfResult(cached, cek);
179
+ },
180
+ wrapWithPrfResult,
181
+ async unwrapWithPrfResult(prfResult, wrapped) {
182
+ const kek = await deriveKek(prfResult.prfOutput);
183
+ return unwrapCek(kek, wrapped);
184
+ },
185
+ getCachedPrfResult,
186
+ clearCachedPrfResult() {
187
+ storage?.removeItem(storageKeys.prfResult);
188
+ },
189
+ getLocalCredentialId() {
190
+ return storage?.getItem(storageKeys.localCredentialId) ?? null;
191
+ },
192
+ setLocalCredentialId,
193
+ clearLocalState() {
194
+ storage?.removeItem(storageKeys.prfResult);
195
+ storage?.removeItem(storageKeys.localCredentialId);
196
+ },
197
+ };
198
+ }
199
+ //# sourceMappingURL=kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kit.js","sourceRoot":"","sources":["../src/kit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EACL,sBAAsB,IAAI,gBAAgB,EAC1C,SAAS,EACT,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,0BAA0B,EAAuB,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUhD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,GAEjB,MAAM,eAAe,CAAC;AAEvB,MAAM,oBAAoB,GAA0B;IAClD,SAAS,EAAE,mCAAmC;IAC9C,iBAAiB,EAAE,qCAAqC;CACzD,CAAC;AAEF,MAAM,2BAA2B,GAAG,KAAM,CAAC;AAE3C,8DAA8D;AAC9D,8DAA8D;AAC9D,0EAA0E;AAC1E,0EAA0E;AAC1E,yDAAyD;AACzD,MAAM,wBAAwB,GAAG,KAAM,CAAC;AAwExC,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;IACnC,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC7E,MAAM,WAAW,GAA0B;QACzC,GAAG,oBAAoB;QACvB,GAAG,MAAM,CAAC,WAAW;KACtB,CAAC;IACF,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,OAAO,GACX,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS;QAC1E,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CACtB,MAAM,CAAC,YAAY,IAAI,yBAAyB,CACjD;QACH,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;IAClC,MAAM,QAAQ,GACZ,MAAM,CAAC,QAAQ,KAAK,SAAS;QAC3B,CAAC,CAAC,oBAAoB;QACtB,CAAC,CAAC,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YACnC,CAAC,CAAC,MAAM,CAAC,QAAQ;YACjB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAEhC,IAAI,eAAe,GAAmB,IAAI,CAAC;IAE3C,SAAS,cAAc,CAAC,MAAwB;QAC9C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,KAAK,GAAkB;YAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,SAAS,EAAE,aAAa,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;SAC3D,CAAC;QACF,IAAI,CAAC;YACH,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IAED,SAAS,kBAAkB;QACzB,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;YAC/C,OAAO;gBACL,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAqB;aAChE,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,SAAS,oBAAoB,CAAC,YAAoB;QAChD,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;IAChE,CAAC;IAED,sDAAsD;IACtD,kEAAkE;IAClE,+DAA+D;IAC/D,mEAAmE;IACnE,gEAAgE;IAChE,kEAAkE;IAClE,+DAA+D;IAC/D,SAAS,WAAW,CAClB,MAAwB,EACxB,UAA+B;QAE/B,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,UAAU,CAAC,uBAAuB,KAAK,UAAU,EAAE,CAAC;YACtD,IAAI,CAAC;gBACH,oBAAoB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAC5C,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;gBAC9D,kBAAkB;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GAAoB;QACvC,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO;QACP,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,2BAA2B;QAC1E,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,wBAAwB;QACjE,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,MAAM;QACN,WAAW;KACZ,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,SAAmC,EAAE,EAAE,CACxD,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAExC,KAAK,UAAU,iBAAiB,CAC9B,SAA2B,EAC3B,GAAe;QAEf,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjD,OAAO,OAAO,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,OAAO;QACL,KAAK,CAAC,cAAc;YAClB,IAAI,eAAe,KAAK,IAAI;gBAAE,OAAO,eAAe,CAAC;YACrD,eAAe,GAAG,MAAM,gBAAgB,EAAE,CAAC;YAC3C,OAAO,eAAe,CAAC;QACzB,CAAC;QAED,oBAAoB;YAClB,eAAe,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,aAAa,CAAC,IAAiB;YAC7B,OAAO,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QACjD,CAAC;QAED,KAAK,CAAC,YAAY,CAChB,aAAuB,EACvB,UAAuC,EAAE;YAEzC,oEAAoE;YACpE,mEAAmE;YACnE,iBAAiB;YACjB,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC5C,OAAO,sBAAsB,CAAC,eAAe,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QACzE,CAAC;QAED,SAAS;QAET,KAAK,CAAC,MAAM,CAAC,IAGZ;YACC,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACrE,IAAI,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC5B,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;QACzE,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,WAAyB;YACpC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAC5C,eAAe,EACf,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CACvC,CAAC;YACF,IAAI,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC5B,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS,CAAC,YAAY,CACjD,CAAC;YACF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,KAAK,EAAE,CACZ,oDAAoD,EACpD,SAAS,EACT,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,CAC3D,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACxC,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC;QACvD,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,WAAyB;YAEzB,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACzB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,MAAM,CAAC,YAAY,CAC9C,CAAC;YACF,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC9C,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACxC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,EAAE,CAAC,6CAA6C,EAAE,KAAK,EAAE;oBACnE,MAAM,EAAE,qBAAqB;oBAC7B,YAAY,EAAE,MAAM,CAAC,YAAY;iBAClC,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,KAAK,CAAC,mBAAmB,CAAC,GAAe;YACvC,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACzB,OAAO,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;QAED,iBAAiB;QAEjB,KAAK,CAAC,mBAAmB,CACvB,SAA2B,EAC3B,OAAuD;YAEvD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACjD,OAAO,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;QAED,kBAAkB;QAElB,oBAAoB;YAClB,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QAED,oBAAoB;YAClB,OAAO,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC;QACjE,CAAC;QAED,oBAAoB;QAEpB,eAAe;YACb,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YAC3C,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QACrD,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,13 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Pluggable local persistence for the SDK's device-side state: the cached
3
+ * PRF output and the credential id this device owns. Adapters are
4
+ * synchronous by design (mirroring the Web Storage API) so callers can
5
+ * read cached state without awaiting.
6
+ *
7
+ * SECURITY: the PRF output cached through an adapter is raw key material —
8
+ * anyone who can read it can re-derive the KEK and unwrap the CEK. The
9
+ * default `localStorage` adapter stores it in plaintext, which is only as
10
+ * strong as the origin's script-injection defenses (an XSS attacker could
11
+ * equally just run the ceremony or exfiltrate decrypted data). Hosts with
12
+ * stricter requirements should supply their own adapter with at-rest
13
+ * protection, or pass `storage: null` to disable caching and re-prompt
14
+ * biometrics instead.
15
+ */
16
+ export interface StorageAdapter {
17
+ getItem(key: string): string | null;
18
+ setItem(key: string, value: string): void;
19
+ removeItem(key: string): void;
20
+ }
21
+ /**
22
+ * Default adapter backed by `window.localStorage`. Every operation is
23
+ * best-effort: quota errors, privacy-mode failures, blocked-storage
24
+ * contexts (e.g. sandboxed frames, where even touching `localStorage`
25
+ * throws), and SSR (no window) all degrade to no-ops so storage problems
26
+ * never interrupt a passkey ceremony.
27
+ */
28
+ export declare const browserLocalStorageAdapter: StorageAdapter;
29
+ /** In-memory adapter for tests and non-browser environments. */
30
+ export declare function createMemoryStorageAdapter(): StorageAdapter;
31
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACnC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,cAyBxC,CAAA;AAED,gEAAgE;AAChE,wBAAgB,0BAA0B,IAAI,cAAc,CAW3D"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Pluggable local persistence for the SDK's device-side state: the cached
3
+ * PRF output and the credential id this device owns. Adapters are
4
+ * synchronous by design (mirroring the Web Storage API) so callers can
5
+ * read cached state without awaiting.
6
+ *
7
+ * SECURITY: the PRF output cached through an adapter is raw key material —
8
+ * anyone who can read it can re-derive the KEK and unwrap the CEK. The
9
+ * default `localStorage` adapter stores it in plaintext, which is only as
10
+ * strong as the origin's script-injection defenses (an XSS attacker could
11
+ * equally just run the ceremony or exfiltrate decrypted data). Hosts with
12
+ * stricter requirements should supply their own adapter with at-rest
13
+ * protection, or pass `storage: null` to disable caching and re-prompt
14
+ * biometrics instead.
15
+ */
16
+ /**
17
+ * Default adapter backed by `window.localStorage`. Every operation is
18
+ * best-effort: quota errors, privacy-mode failures, blocked-storage
19
+ * contexts (e.g. sandboxed frames, where even touching `localStorage`
20
+ * throws), and SSR (no window) all degrade to no-ops so storage problems
21
+ * never interrupt a passkey ceremony.
22
+ */
23
+ export const browserLocalStorageAdapter = {
24
+ getItem(key) {
25
+ try {
26
+ if (typeof localStorage === 'undefined')
27
+ return null;
28
+ return localStorage.getItem(key);
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ },
34
+ setItem(key, value) {
35
+ try {
36
+ if (typeof localStorage === 'undefined')
37
+ return;
38
+ localStorage.setItem(key, value);
39
+ }
40
+ catch {
41
+ // best-effort
42
+ }
43
+ },
44
+ removeItem(key) {
45
+ try {
46
+ if (typeof localStorage === 'undefined')
47
+ return;
48
+ localStorage.removeItem(key);
49
+ }
50
+ catch {
51
+ // best-effort
52
+ }
53
+ },
54
+ };
55
+ /** In-memory adapter for tests and non-browser environments. */
56
+ export function createMemoryStorageAdapter() {
57
+ const store = new Map();
58
+ return {
59
+ getItem: (key) => store.get(key) ?? null,
60
+ setItem: (key, value) => {
61
+ store.set(key, value);
62
+ },
63
+ removeItem: (key) => {
64
+ store.delete(key);
65
+ },
66
+ };
67
+ }
68
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAmB;IACxD,OAAO,CAAC,GAAW;QACjB,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW;gBAAE,OAAO,IAAI,CAAA;YACpD,OAAO,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAW,EAAE,KAAa;QAChC,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW;gBAAE,OAAM;YAC/C,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IACD,UAAU,CAAC,GAAW;QACpB,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW;gBAAE,OAAM;YAC/C,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;CACF,CAAA;AAED,gEAAgE;AAChE,MAAM,UAAU,0BAA0B;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,OAAO;QACL,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI;QACxC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACtB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACvB,CAAC;QACD,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE;YAClB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACnB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * PRF support detection.
3
+ *
4
+ * Checks whether the current browser/platform supports the WebAuthn PRF
5
+ * extension. This is an optimistic check — actual PRF support is only
6
+ * confirmed when a credential is created with prf.enabled: true in the
7
+ * response. If creation fails, callers should fall back to a manual flow.
8
+ *
9
+ * Detection strategy:
10
+ * 1. Check window.PublicKeyCredential exists (basic WebAuthn support)
11
+ * 2. Check isUserVerifyingPlatformAuthenticatorAvailable() (biometric/PIN authenticator present)
12
+ * 3. Optionally check getClientCapabilities() for explicit PRF support signal (new API, not universal)
13
+ */
14
+ export declare function detectPrfSupport(): Promise<boolean>;
15
+ //# sourceMappingURL=support.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support.d.ts","sourceRoot":"","sources":["../src/support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CA+CzD"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * PRF support detection.
3
+ *
4
+ * Checks whether the current browser/platform supports the WebAuthn PRF
5
+ * extension. This is an optimistic check — actual PRF support is only
6
+ * confirmed when a credential is created with prf.enabled: true in the
7
+ * response. If creation fails, callers should fall back to a manual flow.
8
+ *
9
+ * Detection strategy:
10
+ * 1. Check window.PublicKeyCredential exists (basic WebAuthn support)
11
+ * 2. Check isUserVerifyingPlatformAuthenticatorAvailable() (biometric/PIN authenticator present)
12
+ * 3. Optionally check getClientCapabilities() for explicit PRF support signal (new API, not universal)
13
+ */
14
+ export async function detectPrfSupport() {
15
+ if (typeof window === 'undefined') {
16
+ return false;
17
+ }
18
+ if (!window.PublicKeyCredential) {
19
+ return false;
20
+ }
21
+ // Check that a platform authenticator is available (Face ID, Touch ID, Windows Hello, etc.)
22
+ try {
23
+ const available = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
24
+ if (!available) {
25
+ return false;
26
+ }
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ // If getClientCapabilities is available, use it for a more precise check.
32
+ // This API is newer and may not be present in all browsers.
33
+ try {
34
+ if (typeof PublicKeyCredential.getClientCapabilities === 'function') {
35
+ const caps = await PublicKeyCredential.getClientCapabilities();
36
+ if (caps && typeof caps === 'object') {
37
+ // The shape of this API varies across browser versions.
38
+ // Chrome returns a map-like object; check for extension-prf or prf key.
39
+ const hasPrf = caps['extension-prf'] === true ||
40
+ caps['prf'] === true;
41
+ if (hasPrf) {
42
+ return true;
43
+ }
44
+ // If getClientCapabilities is available but doesn't report PRF,
45
+ // that's a strong negative signal on platforms that implement it.
46
+ // However, since this API is still evolving, we don't treat absence
47
+ // as definitive — fall through to the optimistic path.
48
+ }
49
+ }
50
+ }
51
+ catch {
52
+ // getClientCapabilities not available or threw — fall through
53
+ }
54
+ // Optimistic: platform authenticator is available, WebAuthn is supported.
55
+ // Actual PRF support will be confirmed during credential creation.
56
+ return true;
57
+ }
58
+ //# sourceMappingURL=support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support.js","sourceRoot":"","sources":["../src/support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAChC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,4FAA4F;IAC5F,IAAI,CAAC;QACH,MAAM,SAAS,GACb,MAAM,mBAAmB,CAAC,6CAA6C,EAAE,CAAA;QAC3E,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IAED,0EAA0E;IAC1E,4DAA4D;IAC5D,IAAI,CAAC;QACH,IAAI,OAAO,mBAAmB,CAAC,qBAAqB,KAAK,UAAU,EAAE,CAAC;YACpE,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,qBAAqB,EAAE,CAAA;YAC9D,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrC,wDAAwD;gBACxD,wEAAwE;gBACxE,MAAM,MAAM,GACT,IAAgC,CAAC,eAAe,CAAC,KAAK,IAAI;oBAC1D,IAAgC,CAAC,KAAK,CAAC,KAAK,IAAI,CAAA;gBACnD,IAAI,MAAM,EAAE,CAAC;oBACX,OAAO,IAAI,CAAA;gBACb,CAAC;gBACD,gEAAgE;gBAChE,kEAAkE;gBAClE,oEAAoE;gBACpE,uDAAuD;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;IAED,0EAA0E;IAC1E,mEAAmE;IACnE,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,101 @@
1
+ import type { StorageAdapter } from "./storage.js";
2
+ /** Structured logging hooks; the SDK never writes to the console itself. */
3
+ export interface PasskeyKitLogger {
4
+ info?(message: string, metadata?: Record<string, unknown>): void;
5
+ error?(message: string, error?: unknown, metadata?: Record<string, unknown>): void;
6
+ }
7
+ export interface PasskeyKitStorageKeys {
8
+ /** Key under which the cached PRF result is persisted. */
9
+ prfResult: string;
10
+ /** Key under which this device's own credential id is persisted. */
11
+ localCredentialId: string;
12
+ }
13
+ /**
14
+ * Overrides for the messages on errors the SDK throws. Useful for branding
15
+ * or localization; the error classes themselves stay the same, so
16
+ * `instanceof` checks are unaffected.
17
+ */
18
+ export interface PasskeyKitErrorMessages {
19
+ /** Message used for `PrfNotSupportedError`. */
20
+ prfNotSupported?: string;
21
+ /** Message used for `PasskeyTimeoutError`. */
22
+ timeout?: string;
23
+ }
24
+ export interface PasskeyKitConfig {
25
+ /** WebAuthn relying party id (e.g. `example.com`, or `localhost` in dev). */
26
+ rpId: string;
27
+ /** Human-readable relying party name shown in passkey prompts. */
28
+ rpName: string;
29
+ /**
30
+ * Input to the PRF `eval.first` salt. Must stay stable across all clients
31
+ * of the same protocol: changing it changes every derived KEK.
32
+ * Defaults to the Tinfoil v1 protocol constant.
33
+ */
34
+ prfSaltInput?: string | Uint8Array;
35
+ /**
36
+ * HKDF info string used for domain separation when deriving the KEK from
37
+ * the PRF output. Defaults to the Tinfoil v1 protocol constant.
38
+ */
39
+ hkdfInfo?: string | Uint8Array;
40
+ /**
41
+ * Local persistence for the PRF cache and this device's credential id.
42
+ * Defaults to a best-effort `localStorage` adapter; pass `null` to
43
+ * disable local persistence entirely.
44
+ */
45
+ storage?: StorageAdapter | null;
46
+ storageKeys?: Partial<PasskeyKitStorageKeys>;
47
+ /** Timeout passed to the WebAuthn API (some browsers ignore this). */
48
+ webauthnTimeoutMs?: number;
49
+ /**
50
+ * Hard client-side timeout guarding against providers that never resolve
51
+ * the WebAuthn promise. When exceeded, `PasskeyTimeoutError` is thrown.
52
+ */
53
+ stuckTimeoutMs?: number;
54
+ /** Custom messages for the errors the SDK throws. */
55
+ errorMessages?: PasskeyKitErrorMessages;
56
+ logger?: PasskeyKitLogger;
57
+ }
58
+ /** Identity attached to a newly created passkey. */
59
+ export interface PasskeyUser {
60
+ /** Stable opaque user id (becomes the WebAuthn user handle). */
61
+ id: string;
62
+ /** Account identifier shown in passkey pickers (usually an email). */
63
+ name: string;
64
+ /** Friendly display name; falls back to `name` when omitted. */
65
+ displayName?: string;
66
+ }
67
+ /** Result of a successful PRF ceremony (create or authenticate). */
68
+ export interface PrfPasskeyResult {
69
+ /** base64url-encoded credential id. */
70
+ credentialId: string;
71
+ /** Raw 32-byte PRF output; treat as secret key material. */
72
+ prfOutput: ArrayBuffer;
73
+ }
74
+ /** A CEK wrapped under a passkey-derived KEK with AES-256-GCM. */
75
+ export interface WrappedCek {
76
+ /** base64url-encoded credential id whose PRF output wraps this CEK. */
77
+ credentialId: string;
78
+ /** 12-byte AES-GCM IV, hex-encoded. */
79
+ kekIvHex: string;
80
+ /** Wrapped CEK ciphertext (including GCM tag), hex-encoded. */
81
+ wrappedKeyHex: string;
82
+ }
83
+ /** Result of the high-level enroll flow: create passkey + wrap CEK. */
84
+ export interface EnrollResult {
85
+ credentialId: string;
86
+ /** Ciphertext safe to persist server-side. */
87
+ wrappedCek: WrappedCek;
88
+ /**
89
+ * Device-local secret state (PRF output). Already persisted through the
90
+ * storage adapter when one is configured; returned so hosts with custom
91
+ * persistence can store it themselves.
92
+ */
93
+ prfResult: PrfPasskeyResult;
94
+ }
95
+ /** Result of the high-level unlock flow: authenticate + unwrap CEK. */
96
+ export interface UnlockResult {
97
+ credentialId: string;
98
+ /** The recovered raw 32-byte CEK. */
99
+ cek: Uint8Array;
100
+ }
101
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjE,KAAK,CAAC,CACJ,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,IAAI,CAAC;CACT;AAED,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC7C,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,SAAS,EAAE,WAAW,CAAC;CACxB;AAED,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,UAAU,EAAE,UAAU,CAAC;IACvB;;;;OAIG;IACH,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,GAAG,EAAE,UAAU,CAAC;CACjB"}