@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.
- package/README.md +138 -81
- package/dist/crypto.d.ts +11 -54
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +146 -97
- package/dist/crypto.js.map +1 -1
- package/dist/errors.d.ts +11 -21
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -27
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +7 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -7
- package/dist/index.js.map +1 -1
- package/dist/kit.d.ts +2 -62
- package/dist/kit.d.ts.map +1 -1
- package/dist/kit.js +331 -155
- package/dist/kit.js.map +1 -1
- package/dist/storage.d.ts +15 -29
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +113 -62
- package/dist/storage.js.map +1 -1
- package/dist/support.d.ts +2 -14
- package/dist/support.d.ts.map +1 -1
- package/dist/support.js +32 -47
- package/dist/support.js.map +1 -1
- package/dist/types.d.ts +85 -89
- package/dist/types.d.ts.map +1 -1
- package/dist/webauthn.d.ts +10 -33
- package/dist/webauthn.d.ts.map +1 -1
- package/dist/webauthn.js +89 -177
- package/dist/webauthn.js.map +1 -1
- package/dist/wrapped-key-record-codec.d.ts +4 -0
- package/dist/wrapped-key-record-codec.d.ts.map +1 -0
- package/dist/wrapped-key-record-codec.js +89 -0
- package/dist/wrapped-key-record-codec.js.map +1 -0
- package/package.json +4 -2
- package/src/crypto.ts +171 -137
- package/src/errors.ts +29 -33
- package/src/index.ts +26 -41
- package/src/kit.ts +431 -260
- package/src/storage.ts +126 -60
- package/src/support.ts +36 -50
- package/src/types.ts +99 -99
- package/src/webauthn.ts +121 -237
- package/src/wrapped-key-record-codec.ts +93 -0
- package/dist/protocol.d.ts +0 -13
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -13
- package/dist/protocol.js.map +0 -1
- package/src/protocol.ts +0 -15
package/src/storage.ts
CHANGED
|
@@ -1,69 +1,135 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
*/
|
|
1
|
+
import { base64ToBytes, bytesToBase64 } from "./codec.js";
|
|
2
|
+
import type { PasskeyKeyProfile } from "./types.js";
|
|
16
3
|
|
|
17
|
-
export interface
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
export interface CachedPRFResult {
|
|
5
|
+
profile: PasskeyKeyProfile;
|
|
6
|
+
credentialId: string;
|
|
7
|
+
prfOutput: Uint8Array;
|
|
21
8
|
}
|
|
22
9
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*/
|
|
30
|
-
export const browserLocalStorageAdapter: StorageAdapter = {
|
|
31
|
-
getItem(key: string): string | null {
|
|
32
|
-
try {
|
|
33
|
-
if (typeof localStorage === 'undefined') return null
|
|
34
|
-
return localStorage.getItem(key)
|
|
35
|
-
} catch {
|
|
36
|
-
return null
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
setItem(key: string, value: string): void {
|
|
40
|
-
try {
|
|
41
|
-
if (typeof localStorage === 'undefined') return
|
|
42
|
-
localStorage.setItem(key, value)
|
|
43
|
-
} catch {
|
|
44
|
-
// best-effort
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
removeItem(key: string): void {
|
|
48
|
-
try {
|
|
49
|
-
if (typeof localStorage === 'undefined') return
|
|
50
|
-
localStorage.removeItem(key)
|
|
51
|
-
} catch {
|
|
52
|
-
// best-effort
|
|
53
|
-
}
|
|
54
|
-
},
|
|
10
|
+
export interface PasskeyKeyStorage {
|
|
11
|
+
loadCachedPRFResult(): CachedPRFResult | null;
|
|
12
|
+
saveCachedPRFResult(result: CachedPRFResult): void;
|
|
13
|
+
loadLocalCredentialId(): string | null;
|
|
14
|
+
saveLocalCredentialId(credentialId: string): void;
|
|
15
|
+
clear(): void;
|
|
55
16
|
}
|
|
56
17
|
|
|
57
|
-
|
|
58
|
-
export function createMemoryStorageAdapter(): StorageAdapter {
|
|
59
|
-
const store = new Map<string, string>()
|
|
18
|
+
function copyProfile(profile: PasskeyKeyProfile): PasskeyKeyProfile {
|
|
60
19
|
return {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
20
|
+
...profile,
|
|
21
|
+
prfSalt: profile.prfSalt.slice(),
|
|
22
|
+
hkdfInfo: profile.hkdfInfo.slice(),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function copyCachedResult(result: CachedPRFResult): CachedPRFResult {
|
|
27
|
+
return {
|
|
28
|
+
profile: copyProfile(result.profile),
|
|
29
|
+
credentialId: result.credentialId,
|
|
30
|
+
prfOutput: result.prfOutput.slice(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createMemoryPasskeyKeyStorage(): PasskeyKeyStorage {
|
|
35
|
+
let cached: CachedPRFResult | null = null;
|
|
36
|
+
let localCredentialId: string | null = null;
|
|
37
|
+
return {
|
|
38
|
+
loadCachedPRFResult() {
|
|
39
|
+
return cached ? copyCachedResult(cached) : null;
|
|
40
|
+
},
|
|
41
|
+
saveCachedPRFResult(result) {
|
|
42
|
+
cached = copyCachedResult(result);
|
|
43
|
+
},
|
|
44
|
+
loadLocalCredentialId() {
|
|
45
|
+
return localCredentialId;
|
|
46
|
+
},
|
|
47
|
+
saveLocalCredentialId(credentialId) {
|
|
48
|
+
localCredentialId = credentialId;
|
|
49
|
+
},
|
|
50
|
+
clear() {
|
|
51
|
+
cached = null;
|
|
52
|
+
localCredentialId = null;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface SerializedCachedPRFResult {
|
|
58
|
+
profile: Omit<PasskeyKeyProfile, "prfSalt" | "hkdfInfo"> & {
|
|
59
|
+
prfSaltBase64: string;
|
|
60
|
+
hkdfInfoBase64: string;
|
|
61
|
+
};
|
|
62
|
+
credentialId: string;
|
|
63
|
+
prfOutputBase64: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Stores raw PRF output unencrypted in localStorage under an explicit namespace. */
|
|
67
|
+
export function createInsecureBrowserLocalStoragePasskeyKeyStorage(
|
|
68
|
+
namespace: string,
|
|
69
|
+
): PasskeyKeyStorage {
|
|
70
|
+
const prefix = `passkey-key/${encodeURIComponent(namespace)}`;
|
|
71
|
+
const cachedKey = `${prefix}/cached-prf`;
|
|
72
|
+
const localCredentialKey = `${prefix}/local-credential`;
|
|
73
|
+
return {
|
|
74
|
+
loadCachedPRFResult() {
|
|
75
|
+
try {
|
|
76
|
+
if (typeof localStorage === "undefined") return null;
|
|
77
|
+
const value = localStorage.getItem(cachedKey);
|
|
78
|
+
if (!value) return null;
|
|
79
|
+
const stored = JSON.parse(value) as SerializedCachedPRFResult;
|
|
80
|
+
return {
|
|
81
|
+
profile: {
|
|
82
|
+
version: stored.profile.version,
|
|
83
|
+
relyingPartyId: stored.profile.relyingPartyId,
|
|
84
|
+
prfSalt: base64ToBytes(stored.profile.prfSaltBase64),
|
|
85
|
+
hkdfInfo: base64ToBytes(stored.profile.hkdfInfoBase64),
|
|
86
|
+
},
|
|
87
|
+
credentialId: stored.credentialId,
|
|
88
|
+
prfOutput: base64ToBytes(stored.prfOutputBase64),
|
|
89
|
+
};
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
saveCachedPRFResult(result) {
|
|
95
|
+
try {
|
|
96
|
+
if (typeof localStorage === "undefined") return;
|
|
97
|
+
const serialized: SerializedCachedPRFResult = {
|
|
98
|
+
profile: {
|
|
99
|
+
version: result.profile.version,
|
|
100
|
+
relyingPartyId: result.profile.relyingPartyId,
|
|
101
|
+
prfSaltBase64: bytesToBase64(result.profile.prfSalt),
|
|
102
|
+
hkdfInfoBase64: bytesToBase64(result.profile.hkdfInfo),
|
|
103
|
+
},
|
|
104
|
+
credentialId: result.credentialId,
|
|
105
|
+
prfOutputBase64: bytesToBase64(result.prfOutput),
|
|
106
|
+
};
|
|
107
|
+
localStorage.setItem(cachedKey, JSON.stringify(serialized));
|
|
108
|
+
} catch {}
|
|
109
|
+
},
|
|
110
|
+
loadLocalCredentialId() {
|
|
111
|
+
try {
|
|
112
|
+
if (typeof localStorage === "undefined") return null;
|
|
113
|
+
return localStorage.getItem(localCredentialKey);
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
saveLocalCredentialId(credentialId) {
|
|
119
|
+
try {
|
|
120
|
+
if (typeof localStorage === "undefined") return;
|
|
121
|
+
localStorage.setItem(localCredentialKey, credentialId);
|
|
122
|
+
} catch {}
|
|
64
123
|
},
|
|
65
|
-
|
|
66
|
-
|
|
124
|
+
clear() {
|
|
125
|
+
try {
|
|
126
|
+
if (typeof localStorage === "undefined") return;
|
|
127
|
+
localStorage.removeItem(cachedKey);
|
|
128
|
+
} catch {}
|
|
129
|
+
try {
|
|
130
|
+
if (typeof localStorage === "undefined") return;
|
|
131
|
+
localStorage.removeItem(localCredentialKey);
|
|
132
|
+
} catch {}
|
|
67
133
|
},
|
|
68
|
-
}
|
|
134
|
+
};
|
|
69
135
|
}
|
package/src/support.ts
CHANGED
|
@@ -1,61 +1,47 @@
|
|
|
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(): Promise<boolean> {
|
|
15
|
-
if (typeof window === 'undefined') {
|
|
16
|
-
return false
|
|
17
|
-
}
|
|
1
|
+
import type { PasskeyCapability } from "./types.js";
|
|
18
2
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
function hasWebAuthn(operation: "enroll" | "recover"): boolean {
|
|
4
|
+
return (
|
|
5
|
+
typeof navigator !== "undefined" &&
|
|
6
|
+
!!navigator.credentials &&
|
|
7
|
+
typeof navigator.credentials[operation === "enroll" ? "create" : "get"] ===
|
|
8
|
+
"function" &&
|
|
9
|
+
typeof PublicKeyCredential !== "undefined"
|
|
10
|
+
);
|
|
11
|
+
}
|
|
22
12
|
|
|
23
|
-
|
|
13
|
+
async function prfCapability(): Promise<PasskeyCapability> {
|
|
14
|
+
const credentialClass = PublicKeyCredential as typeof PublicKeyCredential & {
|
|
15
|
+
getClientCapabilities?: () => Promise<Record<string, boolean>>;
|
|
16
|
+
};
|
|
17
|
+
if (typeof credentialClass.getClientCapabilities !== "function") return "unknown";
|
|
24
18
|
try {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
}
|
|
19
|
+
const capabilities = await credentialClass.getClientCapabilities();
|
|
20
|
+
const reported = capabilities["extension:prf"];
|
|
21
|
+
if (reported === true) return "supported";
|
|
22
|
+
if (reported === false) return "unsupported";
|
|
30
23
|
} catch {
|
|
31
|
-
return
|
|
24
|
+
return "unknown";
|
|
32
25
|
}
|
|
26
|
+
return "unknown";
|
|
27
|
+
}
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
export async function capability(
|
|
30
|
+
operation: "enroll" | "recover",
|
|
31
|
+
): Promise<PasskeyCapability> {
|
|
32
|
+
if (!hasWebAuthn(operation)) return "unsupported";
|
|
33
|
+
if (operation === "recover") return prfCapability();
|
|
34
|
+
if (
|
|
35
|
+
typeof PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable !== "function"
|
|
36
|
+
) {
|
|
37
|
+
return "unknown";
|
|
38
|
+
}
|
|
36
39
|
try {
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
if (caps && typeof caps === 'object') {
|
|
40
|
-
// The shape of this API varies across browser versions.
|
|
41
|
-
// Chrome returns a map-like object; check for extension-prf or prf key.
|
|
42
|
-
const hasPrf =
|
|
43
|
-
(caps as Record<string, boolean>)['extension-prf'] === true ||
|
|
44
|
-
(caps as Record<string, boolean>)['prf'] === true
|
|
45
|
-
if (hasPrf) {
|
|
46
|
-
return true
|
|
47
|
-
}
|
|
48
|
-
// If getClientCapabilities is available but doesn't report PRF,
|
|
49
|
-
// that's a strong negative signal on platforms that implement it.
|
|
50
|
-
// However, since this API is still evolving, we don't treat absence
|
|
51
|
-
// as definitive — fall through to the optimistic path.
|
|
52
|
-
}
|
|
40
|
+
if (!(await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable())) {
|
|
41
|
+
return "unsupported";
|
|
53
42
|
}
|
|
54
43
|
} catch {
|
|
55
|
-
|
|
44
|
+
return "unknown";
|
|
56
45
|
}
|
|
57
|
-
|
|
58
|
-
// Optimistic: platform authenticator is available, WebAuthn is supported.
|
|
59
|
-
// Actual PRF support will be confirmed during credential creation.
|
|
60
|
-
return true
|
|
46
|
+
return prfCapability();
|
|
61
47
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
localCredentialId: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Overrides for the messages on errors the SDK throws. Useful for branding
|
|
22
|
-
* or localization; the error classes themselves stay the same, so
|
|
23
|
-
* `instanceof` checks are unaffected.
|
|
24
|
-
*/
|
|
25
|
-
export interface PasskeyKitErrorMessages {
|
|
26
|
-
/** Message used for `PrfNotSupportedError`. */
|
|
27
|
-
prfNotSupported?: string;
|
|
28
|
-
/** Message used for `PasskeyTimeoutError`. */
|
|
29
|
-
timeout?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface PasskeyKitConfig {
|
|
33
|
-
/** WebAuthn relying party id (e.g. `example.com`, or `localhost` in dev). */
|
|
34
|
-
rpId: string;
|
|
35
|
-
/** Human-readable relying party name shown in passkey prompts. */
|
|
36
|
-
rpName: string;
|
|
37
|
-
/**
|
|
38
|
-
* Input to the PRF `eval.first` salt. Must stay stable across all clients
|
|
39
|
-
* of the same protocol: changing it changes every derived KEK.
|
|
40
|
-
* Defaults to the Tinfoil v1 protocol constant.
|
|
41
|
-
*/
|
|
42
|
-
prfSaltInput?: string | Uint8Array;
|
|
43
|
-
/**
|
|
44
|
-
* HKDF info string used for domain separation when deriving the KEK from
|
|
45
|
-
* the PRF output. Defaults to the Tinfoil v1 protocol constant.
|
|
46
|
-
*/
|
|
47
|
-
hkdfInfo?: string | Uint8Array;
|
|
48
|
-
/**
|
|
49
|
-
* Local persistence for the PRF cache and this device's credential id.
|
|
50
|
-
* Defaults to a best-effort `localStorage` adapter; pass `null` to
|
|
51
|
-
* disable local persistence entirely.
|
|
52
|
-
*/
|
|
53
|
-
storage?: StorageAdapter | null;
|
|
54
|
-
storageKeys?: Partial<PasskeyKitStorageKeys>;
|
|
55
|
-
/** Timeout passed to the WebAuthn API (some browsers ignore this). */
|
|
56
|
-
webauthnTimeoutMs?: number;
|
|
57
|
-
/**
|
|
58
|
-
* Hard client-side timeout guarding against providers that never resolve
|
|
59
|
-
* the WebAuthn promise. When exceeded, `PasskeyTimeoutError` is thrown.
|
|
60
|
-
*/
|
|
61
|
-
stuckTimeoutMs?: number;
|
|
62
|
-
/** Custom messages for the errors the SDK throws. */
|
|
63
|
-
errorMessages?: PasskeyKitErrorMessages;
|
|
64
|
-
logger?: PasskeyKitLogger;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Identity attached to a newly created passkey. */
|
|
1
|
+
import type { PasskeyKeyStorage } from "./storage.js";
|
|
2
|
+
|
|
3
|
+
export interface PasskeyKeyProfile {
|
|
4
|
+
version: 1;
|
|
5
|
+
relyingPartyId: string;
|
|
6
|
+
prfSalt: Uint8Array;
|
|
7
|
+
hkdfInfo: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface WrappedKey {
|
|
11
|
+
profile: PasskeyKeyProfile;
|
|
12
|
+
credentialId: string;
|
|
13
|
+
kekIvHex: string;
|
|
14
|
+
wrappedKeyHex: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
68
17
|
export interface PasskeyUser {
|
|
69
|
-
|
|
70
|
-
id: string;
|
|
71
|
-
/** Account identifier shown in passkey pickers (usually an email). */
|
|
18
|
+
id: Uint8Array;
|
|
72
19
|
name: string;
|
|
73
|
-
/** Friendly display name; falls back to `name` when omitted. */
|
|
74
20
|
displayName?: string;
|
|
75
21
|
}
|
|
76
22
|
|
|
77
|
-
|
|
78
|
-
export
|
|
79
|
-
|
|
23
|
+
export type PasskeyCapability = "supported" | "unsupported" | "unknown";
|
|
24
|
+
export type PasskeyInteraction = "interactive" | "immediatelyAvailable";
|
|
25
|
+
|
|
26
|
+
export interface CreateAndWrapKeyInput {
|
|
27
|
+
user: PasskeyUser;
|
|
28
|
+
key: Uint8Array;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CreatedWrappedKey {
|
|
33
|
+
credentialId: string;
|
|
34
|
+
wrappedKey: WrappedKey;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RecoverKeyInput {
|
|
38
|
+
wrappedKeys: WrappedKey[];
|
|
39
|
+
preferredCredentialId?: string;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
interaction?: PasskeyInteraction;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface EvaluateCredentialInput {
|
|
45
|
+
credentialIds: string[];
|
|
46
|
+
preferredCredentialId?: string;
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
interaction?: PasskeyInteraction;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface PRFResult {
|
|
52
|
+
output: Uint8Array;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface EvaluatedCredential {
|
|
80
56
|
credentialId: string;
|
|
81
|
-
|
|
82
|
-
prfOutput: ArrayBuffer;
|
|
57
|
+
prfResult: PRFResult;
|
|
83
58
|
}
|
|
84
59
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
/** base64url-encoded credential id whose PRF output wraps this CEK. */
|
|
60
|
+
export interface WrapKeyWithPRFResultInput {
|
|
61
|
+
keyMaterial: Uint8Array;
|
|
88
62
|
credentialId: string;
|
|
89
|
-
|
|
90
|
-
kekIvHex: string;
|
|
91
|
-
/** Wrapped CEK ciphertext (including GCM tag), hex-encoded. */
|
|
92
|
-
wrappedKeyHex: string;
|
|
63
|
+
prfResult: PRFResult;
|
|
93
64
|
}
|
|
94
65
|
|
|
95
|
-
|
|
96
|
-
|
|
66
|
+
export interface UnwrapKeyWithPRFResultInput {
|
|
67
|
+
wrappedKey: WrappedKey;
|
|
68
|
+
prfResult: PRFResult;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface RecoveredKey {
|
|
97
72
|
credentialId: string;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
73
|
+
key: Uint8Array;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface RewrapKeyInput {
|
|
77
|
+
key: Uint8Array;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface PasskeyKeyManagerConfig {
|
|
81
|
+
profile: PasskeyKeyProfile;
|
|
82
|
+
relyingPartyName: string;
|
|
83
|
+
timeoutMs?: number;
|
|
84
|
+
storage?: PasskeyKeyStorage;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface PasskeyKeyManager {
|
|
88
|
+
capability(input: {
|
|
89
|
+
operation: "enroll" | "recover";
|
|
90
|
+
}): Promise<PasskeyCapability>;
|
|
91
|
+
createAndWrapKey(input: CreateAndWrapKeyInput): Promise<CreatedWrappedKey>;
|
|
92
|
+
recoverKey(input: RecoverKeyInput): Promise<RecoveredKey>;
|
|
93
|
+
evaluateCredential(input: EvaluateCredentialInput): Promise<EvaluatedCredential>;
|
|
94
|
+
wrapKeyWithPRFResult(input: WrapKeyWithPRFResultInput): Promise<WrappedKey>;
|
|
95
|
+
unwrapKeyWithPRFResult(input: UnwrapKeyWithPRFResultInput): Promise<Uint8Array>;
|
|
96
|
+
recoverKeyFromCache(input: RecoverKeyInput): Promise<RecoveredKey | null>;
|
|
97
|
+
rewrapKeyFromCache(input: RewrapKeyInput): Promise<WrappedKey | null>;
|
|
98
|
+
clearLocalState(): void;
|
|
99
|
+
cancelActiveCeremony(): void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface WrappedKeyRecord {
|
|
103
|
+
version: 1;
|
|
104
|
+
profile: {
|
|
105
|
+
version: 1;
|
|
106
|
+
relyingPartyId: string;
|
|
107
|
+
prfSalt: string;
|
|
108
|
+
hkdfInfo: string;
|
|
109
|
+
};
|
|
110
110
|
credentialId: string;
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
kekIvHex: string;
|
|
112
|
+
wrappedKeyHex: string;
|
|
113
113
|
}
|