@tinfoilsh/passkey-kit 0.1.0 → 0.2.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.
- 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 +305 -159
- 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 +405 -263
- 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/crypto.ts
CHANGED
|
@@ -1,161 +1,195 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import {
|
|
2
|
+
base64UrlToBytes,
|
|
3
|
+
bytesToBase64Url,
|
|
4
|
+
bytesToHex,
|
|
5
|
+
hexToBytes,
|
|
6
|
+
} from "./codec.js";
|
|
7
|
+
import { invalidInput, operationFailed, PasskeyKeyError } from "./errors.js";
|
|
8
|
+
import type { PasskeyKeyProfile, WrappedKey } from "./types.js";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import { TINFOIL_HKDF_INFO_V1, TINFOIL_KEY_ID_INFO_V1 } from "./protocol.js";
|
|
13
|
-
import type { WrappedCek } from "./types.js";
|
|
14
|
-
|
|
15
|
-
export const CEK_BYTES = 32;
|
|
10
|
+
const KEY_BYTES = 32;
|
|
11
|
+
const PRF_OUTPUT_BYTES = 32;
|
|
16
12
|
const AES_GCM_IV_BYTES = 12;
|
|
17
|
-
const
|
|
13
|
+
const AES_GCM_TAG_BYTES = 16;
|
|
14
|
+
export const PROFILE_KEYS = [
|
|
15
|
+
"version",
|
|
16
|
+
"relyingPartyId",
|
|
17
|
+
"prfSalt",
|
|
18
|
+
"hkdfInfo",
|
|
19
|
+
] as const;
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
function assertBytes(value: unknown, name: string, allowEmpty = false): asserts value is Uint8Array {
|
|
22
|
+
if (!(value instanceof Uint8Array) || (!allowEmpty && value.length === 0)) {
|
|
23
|
+
throw invalidInput(`${name} must be ${allowEmpty ? "a" : "a non-empty"} Uint8Array`);
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export function copyAndValidateProfile(profile: PasskeyKeyProfile): PasskeyKeyProfile {
|
|
28
|
+
if (!profile || typeof profile !== "object") throw invalidInput("profile is required");
|
|
29
|
+
const keys = Object.keys(profile).sort();
|
|
30
|
+
const expected = [...PROFILE_KEYS].sort();
|
|
31
|
+
if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
|
|
32
|
+
throw invalidInput(`profile must contain exactly ${PROFILE_KEYS.join(", ")}`);
|
|
33
|
+
}
|
|
34
|
+
if (profile.version !== 1) {
|
|
35
|
+
throw invalidInput("profile.version must be 1");
|
|
36
|
+
}
|
|
37
|
+
if (typeof profile.relyingPartyId !== "string" || profile.relyingPartyId.length === 0) {
|
|
38
|
+
throw invalidInput("profile.relyingPartyId must be a non-empty string");
|
|
39
|
+
}
|
|
40
|
+
assertBytes(profile.prfSalt, "profile.prfSalt");
|
|
41
|
+
assertBytes(profile.hkdfInfo, "profile.hkdfInfo");
|
|
42
|
+
return {
|
|
43
|
+
version: profile.version,
|
|
44
|
+
relyingPartyId: profile.relyingPartyId,
|
|
45
|
+
prfSalt: profile.prfSalt.slice(),
|
|
46
|
+
hkdfInfo: profile.hkdfInfo.slice(),
|
|
47
|
+
};
|
|
31
48
|
}
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
export async function deriveKeyEncryptionKey(
|
|
45
|
-
prfOutput: ArrayBuffer | Uint8Array,
|
|
46
|
-
hkdfInfo: string | Uint8Array = TINFOIL_HKDF_INFO_V1,
|
|
47
|
-
): Promise<CryptoKey> {
|
|
48
|
-
const masterKey = await crypto.subtle.importKey(
|
|
49
|
-
"raw",
|
|
50
|
-
prfOutput as BufferSource,
|
|
51
|
-
"HKDF",
|
|
52
|
-
false, // non-extractable
|
|
53
|
-
["deriveKey"],
|
|
50
|
+
export function profilesEqual(
|
|
51
|
+
left: PasskeyKeyProfile,
|
|
52
|
+
right: PasskeyKeyProfile,
|
|
53
|
+
): boolean {
|
|
54
|
+
return (
|
|
55
|
+
left.version === right.version &&
|
|
56
|
+
left.relyingPartyId === right.relyingPartyId &&
|
|
57
|
+
left.prfSalt.length === right.prfSalt.length &&
|
|
58
|
+
left.prfSalt.every((byte, index) => byte === right.prfSalt[index]) &&
|
|
59
|
+
left.hkdfInfo.length === right.hkdfInfo.length &&
|
|
60
|
+
left.hkdfInfo.every((byte, index) => byte === right.hkdfInfo[index])
|
|
54
61
|
);
|
|
62
|
+
}
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
export function decodeCanonicalBase64Url(value: unknown, field: string): Uint8Array {
|
|
65
|
+
if (
|
|
66
|
+
typeof value !== "string" ||
|
|
67
|
+
value.length === 0 ||
|
|
68
|
+
!/^[A-Za-z0-9_-]+$/.test(value) ||
|
|
69
|
+
value.length % 4 === 1
|
|
70
|
+
) {
|
|
71
|
+
throw invalidInput(`${field} must be unpadded base64url`);
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const bytes = base64UrlToBytes(value);
|
|
75
|
+
if (bytes.length === 0 || bytesToBase64Url(bytes) !== value) {
|
|
76
|
+
throw invalidInput(`${field} must use canonical unpadded base64url`);
|
|
77
|
+
}
|
|
78
|
+
return bytes;
|
|
79
|
+
} catch (cause) {
|
|
80
|
+
if (cause instanceof PasskeyKeyError) throw cause;
|
|
81
|
+
throw invalidInput(`${field} must be unpadded base64url`);
|
|
82
|
+
}
|
|
68
83
|
}
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
kek: CryptoKey;
|
|
78
|
-
cek: Uint8Array;
|
|
79
|
-
}): Promise<WrappedCek> {
|
|
80
|
-
if (opts.cek.length !== CEK_BYTES) {
|
|
81
|
-
throw new PasskeyKitError(
|
|
82
|
-
`passkey-kit: CEK must be ${CEK_BYTES} bytes, got ${opts.cek.length}`,
|
|
83
|
-
);
|
|
85
|
+
export function validateCredentialId(credentialId: string): void {
|
|
86
|
+
decodeCanonicalBase64Url(credentialId, "credentialId");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validateKey(key: Uint8Array, operation?: string): void {
|
|
90
|
+
if (!(key instanceof Uint8Array) || key.length !== KEY_BYTES) {
|
|
91
|
+
throw invalidInput(`key must be exactly ${KEY_BYTES} bytes`, operation);
|
|
84
92
|
}
|
|
85
|
-
const iv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES));
|
|
86
|
-
const ciphertext = await crypto.subtle.encrypt(
|
|
87
|
-
{ name: "AES-GCM", iv: iv as BufferSource },
|
|
88
|
-
opts.kek,
|
|
89
|
-
opts.cek as BufferSource,
|
|
90
|
-
);
|
|
91
|
-
return {
|
|
92
|
-
credentialId: opts.credentialId,
|
|
93
|
-
kekIvHex: bytesToHex(iv),
|
|
94
|
-
wrappedKeyHex: bytesToHex(new Uint8Array(ciphertext)),
|
|
95
|
-
};
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
wrapped: Pick<WrappedCek, "kekIvHex" | "wrappedKeyHex">,
|
|
105
|
-
): Promise<Uint8Array> {
|
|
106
|
-
if (!wrapped.kekIvHex || !wrapped.wrappedKeyHex) {
|
|
107
|
-
throw new PasskeyKitError("passkey-kit: missing iv or wrapped key");
|
|
95
|
+
export function validateWrappedKey(wrapped: WrappedKey, profile: PasskeyKeyProfile): void {
|
|
96
|
+
if (!wrapped || typeof wrapped !== "object") throw invalidInput("wrapped key is required");
|
|
97
|
+
const keys = Object.keys(wrapped).sort();
|
|
98
|
+
const expected = ["profile", "credentialId", "kekIvHex", "wrappedKeyHex"].sort();
|
|
99
|
+
if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
|
|
100
|
+
throw invalidInput("wrapped key has unexpected fields");
|
|
108
101
|
}
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
throw
|
|
102
|
+
const wrappedProfile = copyAndValidateProfile(wrapped.profile);
|
|
103
|
+
if (!profilesEqual(wrappedProfile, profile)) {
|
|
104
|
+
throw invalidInput("wrapped key profile mismatch");
|
|
112
105
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
106
|
+
validateCredentialId(wrapped.credentialId);
|
|
107
|
+
if (!/^[0-9a-f]{24}$/.test(wrapped.kekIvHex)) {
|
|
108
|
+
throw invalidInput("kekIvHex must be a lowercase 12-byte hex value");
|
|
109
|
+
}
|
|
110
|
+
const ciphertextHexLength = (KEY_BYTES + AES_GCM_TAG_BYTES) * 2;
|
|
111
|
+
if (!new RegExp(`^[0-9a-f]{${ciphertextHexLength}}$`).test(wrapped.wrappedKeyHex)) {
|
|
112
|
+
throw invalidInput("wrappedKeyHex has an invalid format or length");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function deriveWrappingKey(
|
|
117
|
+
prfOutput: Uint8Array,
|
|
118
|
+
profile: PasskeyKeyProfile,
|
|
119
|
+
): Promise<CryptoKey> {
|
|
120
|
+
if (!(prfOutput instanceof Uint8Array) || prfOutput.length !== PRF_OUTPUT_BYTES) {
|
|
121
|
+
throw invalidInput(`PRF output must be exactly ${PRF_OUTPUT_BYTES} bytes`);
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const ikm = await crypto.subtle.importKey("raw", prfOutput.slice(), "HKDF", false, [
|
|
125
|
+
"deriveKey",
|
|
126
|
+
]);
|
|
127
|
+
return await crypto.subtle.deriveKey(
|
|
128
|
+
{
|
|
129
|
+
name: "HKDF",
|
|
130
|
+
hash: "SHA-256",
|
|
131
|
+
salt: new Uint8Array() as BufferSource,
|
|
132
|
+
info: profile.hkdfInfo as BufferSource,
|
|
133
|
+
},
|
|
134
|
+
ikm,
|
|
135
|
+
{ name: "AES-GCM", length: 256 },
|
|
136
|
+
false,
|
|
137
|
+
["encrypt", "decrypt"],
|
|
138
|
+
);
|
|
139
|
+
} catch (cause) {
|
|
140
|
+
if (cause instanceof PasskeyKeyError) throw cause;
|
|
141
|
+
throw operationFailed("failed to derive wrapping key", cause);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function wrapKey(
|
|
146
|
+
profile: PasskeyKeyProfile,
|
|
147
|
+
credentialId: string,
|
|
148
|
+
prfOutput: Uint8Array,
|
|
149
|
+
key: Uint8Array,
|
|
150
|
+
operation = "createAndWrapKey",
|
|
151
|
+
): Promise<WrappedKey> {
|
|
152
|
+
validateCredentialId(credentialId);
|
|
153
|
+
validateKey(key, operation);
|
|
154
|
+
try {
|
|
155
|
+
const wrappingKey = await deriveWrappingKey(prfOutput, profile);
|
|
156
|
+
const iv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES));
|
|
157
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
158
|
+
{ name: "AES-GCM", iv: iv as BufferSource },
|
|
159
|
+
wrappingKey,
|
|
160
|
+
key as BufferSource,
|
|
123
161
|
);
|
|
162
|
+
return {
|
|
163
|
+
profile: copyAndValidateProfile(profile),
|
|
164
|
+
credentialId,
|
|
165
|
+
kekIvHex: bytesToHex(iv),
|
|
166
|
+
wrappedKeyHex: bytesToHex(new Uint8Array(ciphertext)),
|
|
167
|
+
};
|
|
168
|
+
} catch (cause) {
|
|
169
|
+
if (cause instanceof PasskeyKeyError) throw cause;
|
|
170
|
+
throw operationFailed("failed to wrap key", cause, operation);
|
|
124
171
|
}
|
|
125
|
-
return cek;
|
|
126
172
|
}
|
|
127
173
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
export async function deriveKeyId(
|
|
134
|
-
cek: Uint8Array,
|
|
135
|
-
opts: { info?: string | Uint8Array; lengthBytes?: number } = {},
|
|
174
|
+
export async function unwrapKey(
|
|
175
|
+
profile: PasskeyKeyProfile,
|
|
176
|
+
prfOutput: Uint8Array,
|
|
177
|
+
wrapped: WrappedKey,
|
|
178
|
+
operation = "recoverKey",
|
|
136
179
|
): Promise<Uint8Array> {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
180
|
+
validateWrappedKey(wrapped, profile);
|
|
181
|
+
try {
|
|
182
|
+
const wrappingKey = await deriveWrappingKey(prfOutput, profile);
|
|
183
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
184
|
+
{ name: "AES-GCM", iv: hexToBytes(wrapped.kekIvHex) as BufferSource },
|
|
185
|
+
wrappingKey,
|
|
186
|
+
hexToBytes(wrapped.wrappedKeyHex) as BufferSource,
|
|
140
187
|
);
|
|
188
|
+
const key = new Uint8Array(plaintext);
|
|
189
|
+
validateKey(key, operation);
|
|
190
|
+
return key;
|
|
191
|
+
} catch (cause) {
|
|
192
|
+
if (cause instanceof PasskeyKeyError) throw cause;
|
|
193
|
+
throw operationFailed("failed to recover key", cause, operation);
|
|
141
194
|
}
|
|
142
|
-
const lengthBytes = opts.lengthBytes ?? DEFAULT_KEY_ID_BYTES;
|
|
143
|
-
const ikm = await crypto.subtle.importKey(
|
|
144
|
-
"raw",
|
|
145
|
-
cek as BufferSource,
|
|
146
|
-
"HKDF",
|
|
147
|
-
false,
|
|
148
|
-
["deriveBits"],
|
|
149
|
-
);
|
|
150
|
-
const bits = await crypto.subtle.deriveBits(
|
|
151
|
-
{
|
|
152
|
-
name: "HKDF",
|
|
153
|
-
hash: "SHA-256",
|
|
154
|
-
salt: new Uint8Array(0) as BufferSource,
|
|
155
|
-
info: toBytes(opts.info ?? TINFOIL_KEY_ID_INFO_V1) as BufferSource,
|
|
156
|
-
},
|
|
157
|
-
ikm,
|
|
158
|
-
lengthBytes * 8,
|
|
159
|
-
);
|
|
160
|
-
return new Uint8Array(bits);
|
|
161
195
|
}
|
package/src/errors.ts
CHANGED
|
@@ -1,41 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export type PasskeyKeyErrorCategory =
|
|
2
|
+
| "unsupported"
|
|
3
|
+
| "cancelled"
|
|
4
|
+
| "timeout"
|
|
5
|
+
| "operation_in_progress"
|
|
6
|
+
| "invalid_input"
|
|
7
|
+
| "operation_failed";
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.name = 'PasskeyKitError'
|
|
11
|
-
}
|
|
12
|
-
}
|
|
9
|
+
export class PasskeyKeyError extends Error {
|
|
10
|
+
readonly category: PasskeyKeyErrorCategory;
|
|
11
|
+
readonly operation?: string;
|
|
12
|
+
readonly cause?: unknown;
|
|
13
13
|
|
|
14
|
-
const PROVIDER_SUGGESTION =
|
|
15
|
-
"Try using iCloud Keychain, Chrome's built-in passkey manager, or the Passwords app in your device settings."
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The authenticator created a credential but does not support the WebAuthn
|
|
19
|
-
* PRF extension, so no key material can be derived from it.
|
|
20
|
-
*/
|
|
21
|
-
export class PrfNotSupportedError extends PasskeyKitError {
|
|
22
14
|
constructor(
|
|
23
|
-
|
|
15
|
+
category: PasskeyKeyErrorCategory,
|
|
16
|
+
message: string,
|
|
17
|
+
options: { cause?: unknown; operation?: string } = {},
|
|
24
18
|
) {
|
|
25
|
-
super(message)
|
|
26
|
-
this.name =
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "PasskeyKeyError";
|
|
21
|
+
this.category = category;
|
|
22
|
+
this.operation = options.operation;
|
|
23
|
+
this.cause = options.cause;
|
|
27
24
|
}
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
27
|
+
export function invalidInput(message: string, operation?: string): PasskeyKeyError {
|
|
28
|
+
return new PasskeyKeyError("invalid_input", message, { operation });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function operationFailed(
|
|
32
|
+
message: string,
|
|
33
|
+
cause: unknown,
|
|
34
|
+
operation?: string,
|
|
35
|
+
): PasskeyKeyError {
|
|
36
|
+
return new PasskeyKeyError("operation_failed", message, { cause, operation });
|
|
41
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,47 +1,32 @@
|
|
|
1
|
+
export { PasskeyKeyError } from "./errors.js";
|
|
2
|
+
export type { PasskeyKeyErrorCategory } from "./errors.js";
|
|
3
|
+
export { createPasskeyKeyManager } from "./kit.js";
|
|
1
4
|
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
bytesToBase64,
|
|
6
|
-
bytesToBase64Url,
|
|
7
|
-
bytesToHex,
|
|
8
|
-
hexToBytes,
|
|
9
|
-
} from "./codec.js";
|
|
5
|
+
decodeWrappedKeyRecord,
|
|
6
|
+
encodeWrappedKeyRecord,
|
|
7
|
+
} from "./wrapped-key-record-codec.js";
|
|
10
8
|
export {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
deriveKeyId,
|
|
14
|
-
generateCek,
|
|
15
|
-
isValidCek,
|
|
16
|
-
unwrapCek,
|
|
17
|
-
wrapCek,
|
|
18
|
-
} from "./crypto.js";
|
|
19
|
-
export {
|
|
20
|
-
PasskeyKitError,
|
|
21
|
-
PasskeyTimeoutError,
|
|
22
|
-
PrfNotSupportedError,
|
|
23
|
-
} from "./errors.js";
|
|
24
|
-
export { createPasskeyKit } from "./kit.js";
|
|
25
|
-
export type { PasskeyKit } from "./kit.js";
|
|
26
|
-
export {
|
|
27
|
-
TINFOIL_HKDF_INFO_V1,
|
|
28
|
-
TINFOIL_KEY_ID_INFO_V1,
|
|
29
|
-
TINFOIL_PRF_SALT_INPUT_V1,
|
|
30
|
-
} from "./protocol.js";
|
|
31
|
-
export {
|
|
32
|
-
browserLocalStorageAdapter,
|
|
33
|
-
createMemoryStorageAdapter,
|
|
9
|
+
createInsecureBrowserLocalStoragePasskeyKeyStorage,
|
|
10
|
+
createMemoryPasskeyKeyStorage,
|
|
34
11
|
} from "./storage.js";
|
|
35
|
-
export type {
|
|
36
|
-
export { detectPrfSupport } from "./support.js";
|
|
12
|
+
export type { CachedPRFResult, PasskeyKeyStorage } from "./storage.js";
|
|
37
13
|
export type {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
14
|
+
CreateAndWrapKeyInput,
|
|
15
|
+
CreatedWrappedKey,
|
|
16
|
+
EvaluatedCredential,
|
|
17
|
+
EvaluateCredentialInput,
|
|
18
|
+
PasskeyCapability,
|
|
19
|
+
PasskeyInteraction,
|
|
20
|
+
PasskeyKeyManager,
|
|
21
|
+
PasskeyKeyManagerConfig,
|
|
22
|
+
PasskeyKeyProfile,
|
|
43
23
|
PasskeyUser,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
PRFResult,
|
|
25
|
+
RecoveredKey,
|
|
26
|
+
RecoverKeyInput,
|
|
27
|
+
RewrapKeyInput,
|
|
28
|
+
UnwrapKeyWithPRFResultInput,
|
|
29
|
+
WrapKeyWithPRFResultInput,
|
|
30
|
+
WrappedKey,
|
|
31
|
+
WrappedKeyRecord,
|
|
47
32
|
} from "./types.js";
|