ox 0.12.4 → 0.13.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/CHANGELOG.md +21 -0
- package/_cjs/core/P256.js +1 -1
- package/_cjs/core/P256.js.map +1 -1
- package/_cjs/core/WebAuthnP256.js +15 -256
- package/_cjs/core/WebAuthnP256.js.map +1 -1
- package/_cjs/core/WebCryptoP256.js +3 -1
- package/_cjs/core/WebCryptoP256.js.map +1 -1
- package/_cjs/core/internal/webauthn.js +5 -13
- package/_cjs/core/internal/webauthn.js.map +1 -1
- package/_cjs/index.docs.js +1 -0
- package/_cjs/index.docs.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +18 -3
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +26 -0
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TxEnvelopeTempo.js +5 -10
- package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_cjs/webauthn/Authentication.js +246 -0
- package/_cjs/webauthn/Authentication.js.map +1 -0
- package/_cjs/webauthn/Authenticator.js +55 -0
- package/_cjs/webauthn/Authenticator.js.map +1 -0
- package/_cjs/webauthn/Credential.js +53 -0
- package/_cjs/webauthn/Credential.js.map +1 -0
- package/_cjs/webauthn/Registration.js +349 -0
- package/_cjs/webauthn/Registration.js.map +1 -0
- package/_cjs/webauthn/Types.js +3 -0
- package/_cjs/webauthn/Types.js.map +1 -0
- package/_cjs/webauthn/index.js +9 -0
- package/_cjs/webauthn/index.js.map +1 -0
- package/_cjs/webauthn/internal/utils.js +53 -0
- package/_cjs/webauthn/internal/utils.js.map +1 -0
- package/_esm/core/P256.js +1 -1
- package/_esm/core/P256.js.map +1 -1
- package/_esm/core/WebAuthnP256.js +13 -261
- package/_esm/core/WebAuthnP256.js.map +1 -1
- package/_esm/core/WebCryptoP256.js +4 -1
- package/_esm/core/WebCryptoP256.js.map +1 -1
- package/_esm/core/internal/webauthn.js +5 -13
- package/_esm/core/internal/webauthn.js.map +1 -1
- package/_esm/erc8021/index.js +2 -2
- package/_esm/index.docs.js +1 -0
- package/_esm/index.docs.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +66 -3
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +74 -0
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TransactionReceipt.js +1 -1
- package/_esm/tempo/TransactionRequest.js +1 -1
- package/_esm/tempo/TxEnvelopeTempo.js +5 -10
- package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_esm/webauthn/Authentication.js +453 -0
- package/_esm/webauthn/Authentication.js.map +1 -0
- package/_esm/webauthn/Authenticator.js +176 -0
- package/_esm/webauthn/Authenticator.js.map +1 -0
- package/_esm/webauthn/Credential.js +95 -0
- package/_esm/webauthn/Credential.js.map +1 -0
- package/_esm/webauthn/Registration.js +512 -0
- package/_esm/webauthn/Registration.js.map +1 -0
- package/_esm/webauthn/Types.js +2 -0
- package/_esm/webauthn/Types.js.map +1 -0
- package/_esm/webauthn/index.js +31 -0
- package/_esm/webauthn/index.js.map +1 -0
- package/_esm/webauthn/internal/utils.js +52 -0
- package/_esm/webauthn/internal/utils.js.map +1 -0
- package/_types/core/WebAuthnP256.d.ts +33 -208
- package/_types/core/WebAuthnP256.d.ts.map +1 -1
- package/_types/core/WebCryptoP256.d.ts +2 -0
- package/_types/core/WebCryptoP256.d.ts.map +1 -1
- package/_types/core/internal/webauthn.d.ts +2 -110
- package/_types/core/internal/webauthn.d.ts.map +1 -1
- package/_types/erc8021/index.d.ts +2 -2
- package/_types/index.docs.d.ts +1 -0
- package/_types/index.docs.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +57 -0
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +75 -0
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/Transaction.d.ts +2 -2
- package/_types/tempo/TransactionReceipt.d.ts +2 -2
- package/_types/tempo/TransactionRequest.d.ts +2 -2
- package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/_types/webauthn/Authentication.d.ts +324 -0
- package/_types/webauthn/Authentication.d.ts.map +1 -0
- package/_types/webauthn/Authenticator.d.ts +182 -0
- package/_types/webauthn/Authenticator.d.ts.map +1 -0
- package/_types/webauthn/Credential.d.ts +77 -0
- package/_types/webauthn/Credential.d.ts.map +1 -0
- package/_types/webauthn/Registration.d.ts +308 -0
- package/_types/webauthn/Registration.d.ts.map +1 -0
- package/_types/webauthn/Types.d.ts +106 -0
- package/_types/webauthn/Types.d.ts.map +1 -0
- package/_types/webauthn/index.d.ts +33 -0
- package/_types/webauthn/index.d.ts.map +1 -0
- package/_types/webauthn/internal/utils.d.ts +17 -0
- package/_types/webauthn/internal/utils.d.ts.map +1 -0
- package/core/P256.ts +1 -1
- package/core/WebAuthnP256.ts +37 -582
- package/core/WebCryptoP256.ts +6 -1
- package/core/internal/webauthn.ts +6 -165
- package/erc8021/index.ts +2 -2
- package/index.docs.ts +1 -0
- package/package.json +31 -1
- package/tempo/KeyAuthorization.test.ts +139 -0
- package/tempo/KeyAuthorization.ts +82 -3
- package/tempo/SignatureEnvelope.test.ts +147 -0
- package/tempo/SignatureEnvelope.ts +113 -0
- package/tempo/Transaction.ts +2 -2
- package/tempo/TransactionReceipt.ts +2 -2
- package/tempo/TransactionRequest.ts +2 -2
- package/tempo/TxEnvelopeTempo.ts +5 -12
- package/tempo/e2e.test.ts +265 -0
- package/version.ts +1 -1
- package/webauthn/Authentication/package.json +6 -0
- package/webauthn/Authentication.ts +673 -0
- package/webauthn/Authenticator/package.json +6 -0
- package/webauthn/Authenticator.ts +259 -0
- package/webauthn/Credential/package.json +6 -0
- package/webauthn/Credential.ts +146 -0
- package/webauthn/Registration/package.json +6 -0
- package/webauthn/Registration.ts +805 -0
- package/webauthn/Types/package.json +6 -0
- package/webauthn/Types.ts +158 -0
- package/webauthn/index.ts +38 -0
- package/webauthn/internal/utils.ts +63 -0
- package/webauthn/package.json +6 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import * as Base64 from '../core/Base64.js';
|
|
2
|
+
import * as Bytes from '../core/Bytes.js';
|
|
3
|
+
import * as Errors from '../core/Errors.js';
|
|
4
|
+
import * as Hash from '../core/Hash.js';
|
|
5
|
+
import * as Hex from '../core/Hex.js';
|
|
6
|
+
import * as internal from '../core/internal/webauthn.js';
|
|
7
|
+
import * as P256 from '../core/P256.js';
|
|
8
|
+
import * as Signature from '../core/Signature.js';
|
|
9
|
+
import { getAuthenticatorData, getClientDataJSON } from './Authenticator.js';
|
|
10
|
+
import { base64UrlOptions, bufferSourceToBytes, bytesToArrayBuffer, deserializeExtensions, responseKeys, serializeExtensions, } from './internal/utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* Deserializes credential request options that can be passed to
|
|
13
|
+
* `navigator.credentials.get()`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts twoslash
|
|
17
|
+
* import { Authentication } from 'ox/webauthn'
|
|
18
|
+
*
|
|
19
|
+
* const options = Authentication.getOptions({
|
|
20
|
+
* challenge: '0xdeadbeef',
|
|
21
|
+
* })
|
|
22
|
+
* const serialized = Authentication.serializeOptions(options)
|
|
23
|
+
*
|
|
24
|
+
* // ... send to server and back ...
|
|
25
|
+
*
|
|
26
|
+
* const deserialized = Authentication.deserializeOptions(serialized) // [!code focus]
|
|
27
|
+
* const credential = await window.navigator.credentials.get(deserialized)
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @param options - The serialized credential request options.
|
|
31
|
+
* @returns The deserialized credential request options.
|
|
32
|
+
*/
|
|
33
|
+
export function deserializeOptions(options) {
|
|
34
|
+
const { publicKey, ...rest } = options;
|
|
35
|
+
if (!publicKey)
|
|
36
|
+
return { ...rest };
|
|
37
|
+
const { allowCredentials, challenge, extensions, ...publicKeyRest } = publicKey;
|
|
38
|
+
return {
|
|
39
|
+
...rest,
|
|
40
|
+
publicKey: {
|
|
41
|
+
...publicKeyRest,
|
|
42
|
+
challenge: Bytes.fromHex(challenge),
|
|
43
|
+
...(allowCredentials && {
|
|
44
|
+
allowCredentials: allowCredentials.map(({ id, ...rest }) => ({
|
|
45
|
+
...rest,
|
|
46
|
+
id: Base64.toBytes(id),
|
|
47
|
+
})),
|
|
48
|
+
}),
|
|
49
|
+
...(extensions && {
|
|
50
|
+
extensions: deserializeExtensions(extensions),
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Deserializes a serialized authentication response.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts twoslash
|
|
60
|
+
* import { Authentication } from 'ox/webauthn'
|
|
61
|
+
*
|
|
62
|
+
* const response = Authentication.deserializeResponse({ // [!code focus]
|
|
63
|
+
* id: 'm1-bMPuAqpWhCxHZQZTT6e-lSPntQbh3opIoGe7g4Qs', // [!code focus]
|
|
64
|
+
* metadata: { // [!code focus]
|
|
65
|
+
* authenticatorData: '0x49960de5...', // [!code focus]
|
|
66
|
+
* clientDataJSON: '{"type":"webauthn.get",...}', // [!code focus]
|
|
67
|
+
* challengeIndex: 23, // [!code focus]
|
|
68
|
+
* typeIndex: 1, // [!code focus]
|
|
69
|
+
* userVerificationRequired: true, // [!code focus]
|
|
70
|
+
* }, // [!code focus]
|
|
71
|
+
* raw: { // [!code focus]
|
|
72
|
+
* id: 'm1-bMPuAqpWhCxHZQZTT6e-lSPntQbh3opIoGe7g4Qs', // [!code focus]
|
|
73
|
+
* type: 'public-key', // [!code focus]
|
|
74
|
+
* authenticatorAttachment: 'platform', // [!code focus]
|
|
75
|
+
* rawId: 'm1-bMPuAqpWhCxHZQZTT6e-lSPntQbh3opIoGe7g4Qs', // [!code focus]
|
|
76
|
+
* response: { clientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0' }, // [!code focus]
|
|
77
|
+
* }, // [!code focus]
|
|
78
|
+
* signature: '0x...', // [!code focus]
|
|
79
|
+
* }) // [!code focus]
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @param response - The serialized authentication response.
|
|
83
|
+
* @returns The deserialized authentication response.
|
|
84
|
+
*/
|
|
85
|
+
export function deserializeResponse(response) {
|
|
86
|
+
const { id, metadata, raw, signature } = response;
|
|
87
|
+
const rawResponse = {};
|
|
88
|
+
for (const [key, value] of Object.entries(raw.response))
|
|
89
|
+
rawResponse[key] = bytesToArrayBuffer(Base64.toBytes(value));
|
|
90
|
+
return {
|
|
91
|
+
id,
|
|
92
|
+
metadata,
|
|
93
|
+
raw: {
|
|
94
|
+
id: raw.id,
|
|
95
|
+
type: raw.type,
|
|
96
|
+
authenticatorAttachment: raw.authenticatorAttachment,
|
|
97
|
+
rawId: bytesToArrayBuffer(Base64.toBytes(raw.rawId)),
|
|
98
|
+
response: rawResponse,
|
|
99
|
+
getClientExtensionResults: () => ({}),
|
|
100
|
+
},
|
|
101
|
+
signature: Signature.from(signature),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Returns the request options to sign a challenge with the Web Authentication API.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts twoslash
|
|
109
|
+
* import { Authentication } from 'ox/webauthn'
|
|
110
|
+
*
|
|
111
|
+
* const options = Authentication.getOptions({
|
|
112
|
+
* challenge: '0xdeadbeef',
|
|
113
|
+
* })
|
|
114
|
+
*
|
|
115
|
+
* const credential = await window.navigator.credentials.get(options)
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @param options - Options.
|
|
119
|
+
* @returns The credential request options.
|
|
120
|
+
*/
|
|
121
|
+
export function getOptions(options) {
|
|
122
|
+
const { credentialId, challenge, extensions, rpId = window.location.hostname, userVerification = 'required', } = options;
|
|
123
|
+
return {
|
|
124
|
+
publicKey: {
|
|
125
|
+
...(credentialId
|
|
126
|
+
? {
|
|
127
|
+
allowCredentials: Array.isArray(credentialId)
|
|
128
|
+
? credentialId.map((id) => ({
|
|
129
|
+
id: Base64.toBytes(id),
|
|
130
|
+
type: 'public-key',
|
|
131
|
+
}))
|
|
132
|
+
: [
|
|
133
|
+
{
|
|
134
|
+
id: Base64.toBytes(credentialId),
|
|
135
|
+
type: 'public-key',
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
140
|
+
challenge: Bytes.fromHex(challenge),
|
|
141
|
+
...(extensions && { extensions }),
|
|
142
|
+
rpId,
|
|
143
|
+
userVerification,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Constructs the final digest that was signed and computed by the authenticator. This payload includes
|
|
149
|
+
* the cryptographic `challenge`, as well as authenticator metadata (`authenticatorData` + `clientDataJSON`).
|
|
150
|
+
* This value can be also used with raw P256 verification (such as `P256.verify` or
|
|
151
|
+
* `WebCryptoP256.verify`).
|
|
152
|
+
*
|
|
153
|
+
* :::warning
|
|
154
|
+
*
|
|
155
|
+
* This function is mainly for testing purposes or for manually constructing
|
|
156
|
+
* signing payloads. In most cases you will not need this function and
|
|
157
|
+
* instead use `Authentication.sign`.
|
|
158
|
+
*
|
|
159
|
+
* :::
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts twoslash
|
|
163
|
+
* import { Authentication } from 'ox/webauthn'
|
|
164
|
+
* import { WebCryptoP256 } from 'ox'
|
|
165
|
+
*
|
|
166
|
+
* const { metadata, payload } = Authentication.getSignPayload({ // [!code focus]
|
|
167
|
+
* challenge: '0xdeadbeef', // [!code focus]
|
|
168
|
+
* }) // [!code focus]
|
|
169
|
+
*
|
|
170
|
+
* const { publicKey, privateKey } = await WebCryptoP256.createKeyPair()
|
|
171
|
+
*
|
|
172
|
+
* const signature = await WebCryptoP256.sign({
|
|
173
|
+
* payload,
|
|
174
|
+
* privateKey,
|
|
175
|
+
* })
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* @param options - Options to construct the signing payload.
|
|
179
|
+
* @returns The signing payload.
|
|
180
|
+
*/
|
|
181
|
+
export function getSignPayload(options) {
|
|
182
|
+
const { challenge, crossOrigin, extraClientData, flag, origin, rpId, signCount, userVerification = 'required', } = options;
|
|
183
|
+
const authenticatorData = getAuthenticatorData({
|
|
184
|
+
flag,
|
|
185
|
+
rpId,
|
|
186
|
+
signCount,
|
|
187
|
+
});
|
|
188
|
+
const clientDataJSON = getClientDataJSON({
|
|
189
|
+
challenge,
|
|
190
|
+
crossOrigin,
|
|
191
|
+
extraClientData,
|
|
192
|
+
origin,
|
|
193
|
+
});
|
|
194
|
+
const clientDataJSONHash = Hash.sha256(Hex.fromString(clientDataJSON));
|
|
195
|
+
const challengeIndex = clientDataJSON.indexOf('"challenge"');
|
|
196
|
+
const typeIndex = clientDataJSON.indexOf('"type"');
|
|
197
|
+
const metadata = {
|
|
198
|
+
authenticatorData,
|
|
199
|
+
clientDataJSON,
|
|
200
|
+
challengeIndex,
|
|
201
|
+
typeIndex,
|
|
202
|
+
userVerificationRequired: userVerification === 'required',
|
|
203
|
+
};
|
|
204
|
+
const payload = Hex.concat(authenticatorData, clientDataJSONHash);
|
|
205
|
+
return { metadata, payload };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Serializes credential request options into a JSON-serializable
|
|
209
|
+
* format, converting `BufferSource` fields to base64url strings.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts twoslash
|
|
213
|
+
* import { Authentication } from 'ox/webauthn'
|
|
214
|
+
*
|
|
215
|
+
* const options = Authentication.getOptions({
|
|
216
|
+
* challenge: '0xdeadbeef',
|
|
217
|
+
* })
|
|
218
|
+
*
|
|
219
|
+
* const serialized = Authentication.serializeOptions(options) // [!code focus]
|
|
220
|
+
*
|
|
221
|
+
* // `serialized` is JSON-serializable — send it to a server, store it, etc.
|
|
222
|
+
* const json = JSON.stringify(serialized)
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @param options - The credential request options to serialize.
|
|
226
|
+
* @returns The serialized credential request options.
|
|
227
|
+
*/
|
|
228
|
+
export function serializeOptions(options) {
|
|
229
|
+
const { publicKey, signal: _, ...rest } = options;
|
|
230
|
+
if (!publicKey)
|
|
231
|
+
return { ...rest };
|
|
232
|
+
const { allowCredentials, challenge, extensions, ...publicKeyRest } = publicKey;
|
|
233
|
+
return {
|
|
234
|
+
...rest,
|
|
235
|
+
publicKey: {
|
|
236
|
+
...publicKeyRest,
|
|
237
|
+
challenge: Hex.fromBytes(bufferSourceToBytes(challenge)),
|
|
238
|
+
...(allowCredentials && {
|
|
239
|
+
allowCredentials: allowCredentials.map(({ id, ...rest }) => ({
|
|
240
|
+
...rest,
|
|
241
|
+
id: Base64.fromBytes(bufferSourceToBytes(id), base64UrlOptions),
|
|
242
|
+
})),
|
|
243
|
+
}),
|
|
244
|
+
...(extensions && {
|
|
245
|
+
extensions: serializeExtensions(extensions),
|
|
246
|
+
}),
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Serializes an authentication response into a JSON-serializable
|
|
252
|
+
* format, converting `BufferSource` fields to base64url strings
|
|
253
|
+
* and the signature to a hex string.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```ts twoslash
|
|
257
|
+
* import { Authentication } from 'ox/webauthn'
|
|
258
|
+
*
|
|
259
|
+
* const response = await Authentication.sign({
|
|
260
|
+
* challenge: '0xdeadbeef',
|
|
261
|
+
* })
|
|
262
|
+
*
|
|
263
|
+
* const serialized = Authentication.serializeResponse(response) // [!code focus]
|
|
264
|
+
*
|
|
265
|
+
* // `serialized` is JSON-serializable — send it to a server, store it, etc.
|
|
266
|
+
* const json = JSON.stringify(serialized)
|
|
267
|
+
* ```
|
|
268
|
+
*
|
|
269
|
+
* @param response - The authentication response to serialize.
|
|
270
|
+
* @returns The serialized authentication response.
|
|
271
|
+
*/
|
|
272
|
+
export function serializeResponse(response) {
|
|
273
|
+
const { id, metadata, raw, signature } = response;
|
|
274
|
+
const rawResponse = {};
|
|
275
|
+
for (const key of responseKeys) {
|
|
276
|
+
const value = raw.response[key];
|
|
277
|
+
if (value instanceof ArrayBuffer)
|
|
278
|
+
rawResponse[key] = Base64.fromBytes(new Uint8Array(value), base64UrlOptions);
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
id,
|
|
282
|
+
metadata,
|
|
283
|
+
raw: {
|
|
284
|
+
id: raw.id,
|
|
285
|
+
type: raw.type,
|
|
286
|
+
authenticatorAttachment: raw.authenticatorAttachment,
|
|
287
|
+
rawId: Base64.fromBytes(bufferSourceToBytes(raw.rawId), base64UrlOptions),
|
|
288
|
+
response: rawResponse,
|
|
289
|
+
},
|
|
290
|
+
signature: Signature.toHex(signature),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Signs a challenge using a stored WebAuthn P256 Credential. If no Credential is provided,
|
|
295
|
+
* a prompt will be displayed for the user to select an existing Credential
|
|
296
|
+
* that was previously registered.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```ts twoslash
|
|
300
|
+
* import { Registration, Authentication } from 'ox/webauthn'
|
|
301
|
+
*
|
|
302
|
+
* const credential = await Registration.create({
|
|
303
|
+
* name: 'Example',
|
|
304
|
+
* })
|
|
305
|
+
*
|
|
306
|
+
* const { metadata, signature } = await Authentication.sign({ // [!code focus]
|
|
307
|
+
* credentialId: credential.id, // [!code focus]
|
|
308
|
+
* challenge: '0xdeadbeef', // [!code focus]
|
|
309
|
+
* }) // [!code focus]
|
|
310
|
+
* // @log: {
|
|
311
|
+
* // @log: metadata: {
|
|
312
|
+
* // @log: authenticatorData: '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000',
|
|
313
|
+
* // @log: clientDataJSON: '{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"http://localhost:5173","crossOrigin":false}',
|
|
314
|
+
* // @log: challengeIndex: 23,
|
|
315
|
+
* // @log: typeIndex: 1,
|
|
316
|
+
* // @log: userVerificationRequired: true,
|
|
317
|
+
* // @log: },
|
|
318
|
+
* // @log: signature: { r: 51231...4215n, s: 12345...6789n },
|
|
319
|
+
* // @log: }
|
|
320
|
+
* ```
|
|
321
|
+
*
|
|
322
|
+
* @param options - Options.
|
|
323
|
+
* @returns The signature.
|
|
324
|
+
*/
|
|
325
|
+
export async function sign(options) {
|
|
326
|
+
const { getFn = window.navigator.credentials.get.bind(window.navigator.credentials), ...rest } = options;
|
|
327
|
+
const requestOptions = 'publicKey' in rest
|
|
328
|
+
? rest
|
|
329
|
+
: getOptions(rest);
|
|
330
|
+
try {
|
|
331
|
+
const credential = (await getFn(requestOptions));
|
|
332
|
+
if (!credential)
|
|
333
|
+
throw new SignFailedError();
|
|
334
|
+
const response = credential.response;
|
|
335
|
+
const clientDataJSON = String.fromCharCode(...new Uint8Array(response.clientDataJSON));
|
|
336
|
+
const challengeIndex = clientDataJSON.indexOf('"challenge"');
|
|
337
|
+
const typeIndex = clientDataJSON.indexOf('"type"');
|
|
338
|
+
const signature = internal.parseAsn1Signature(new Uint8Array(response.signature));
|
|
339
|
+
return {
|
|
340
|
+
id: credential.id,
|
|
341
|
+
metadata: {
|
|
342
|
+
authenticatorData: Hex.fromBytes(new Uint8Array(response.authenticatorData)),
|
|
343
|
+
clientDataJSON,
|
|
344
|
+
challengeIndex,
|
|
345
|
+
typeIndex,
|
|
346
|
+
userVerificationRequired: requestOptions.publicKey.userVerification === 'required',
|
|
347
|
+
},
|
|
348
|
+
signature,
|
|
349
|
+
raw: credential,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
throw new SignFailedError({
|
|
354
|
+
cause: error,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/** Thrown when a WebAuthn P256 credential request fails. */
|
|
359
|
+
export class SignFailedError extends Errors.BaseError {
|
|
360
|
+
constructor({ cause } = {}) {
|
|
361
|
+
super('Failed to request credential.', {
|
|
362
|
+
cause,
|
|
363
|
+
});
|
|
364
|
+
Object.defineProperty(this, "name", {
|
|
365
|
+
enumerable: true,
|
|
366
|
+
configurable: true,
|
|
367
|
+
writable: true,
|
|
368
|
+
value: 'Authentication.SignFailedError'
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Verifies a signature using the Credential's public key and the challenge which was signed.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts twoslash
|
|
377
|
+
* import { Registration, Authentication } from 'ox/webauthn'
|
|
378
|
+
*
|
|
379
|
+
* const credential = await Registration.create({
|
|
380
|
+
* name: 'Example',
|
|
381
|
+
* })
|
|
382
|
+
*
|
|
383
|
+
* const { metadata, signature } = await Authentication.sign({
|
|
384
|
+
* credentialId: credential.id,
|
|
385
|
+
* challenge: '0xdeadbeef',
|
|
386
|
+
* })
|
|
387
|
+
*
|
|
388
|
+
* const result = Authentication.verify({ // [!code focus]
|
|
389
|
+
* metadata, // [!code focus]
|
|
390
|
+
* challenge: '0xdeadbeef', // [!code focus]
|
|
391
|
+
* publicKey: credential.publicKey, // [!code focus]
|
|
392
|
+
* signature, // [!code focus]
|
|
393
|
+
* }) // [!code focus]
|
|
394
|
+
* // @log: true
|
|
395
|
+
* ```
|
|
396
|
+
*
|
|
397
|
+
* @param options - Options.
|
|
398
|
+
* @returns Whether the signature is valid.
|
|
399
|
+
*/
|
|
400
|
+
export function verify(options) {
|
|
401
|
+
const { challenge, metadata, origin, publicKey, rpId, signature } = options;
|
|
402
|
+
const { authenticatorData, clientDataJSON, userVerificationRequired } = metadata;
|
|
403
|
+
const authenticatorDataBytes = Bytes.fromHex(authenticatorData);
|
|
404
|
+
// Check length of `authenticatorData`.
|
|
405
|
+
if (authenticatorDataBytes.length < 37)
|
|
406
|
+
return false;
|
|
407
|
+
// If rpId is provided, validate the rpIdHash in authenticatorData.
|
|
408
|
+
if (rpId !== undefined) {
|
|
409
|
+
const rpIdHash = authenticatorDataBytes.slice(0, 32);
|
|
410
|
+
const expectedRpIdHash = Hash.sha256(Hex.fromString(rpId), { as: 'Bytes' });
|
|
411
|
+
if (!Bytes.isEqual(rpIdHash, expectedRpIdHash))
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
const flag = authenticatorDataBytes[32];
|
|
415
|
+
// Verify that the UP bit of the flags in authData is set.
|
|
416
|
+
if ((flag & 0x01) !== 0x01)
|
|
417
|
+
return false;
|
|
418
|
+
// If user verification was determined to be required, verify that
|
|
419
|
+
// the UV bit of the flags in authData is set. Otherwise, ignore the
|
|
420
|
+
// value of the UV flag.
|
|
421
|
+
if (userVerificationRequired && (flag & 0x04) !== 0x04)
|
|
422
|
+
return false;
|
|
423
|
+
// If the BE bit of the flags in authData is not set, verify that
|
|
424
|
+
// the BS bit is not set.
|
|
425
|
+
if ((flag & 0x08) !== 0x08 && (flag & 0x10) === 0x10)
|
|
426
|
+
return false;
|
|
427
|
+
// Parse clientDataJSON for validation.
|
|
428
|
+
const clientData = JSON.parse(clientDataJSON);
|
|
429
|
+
// Verify that response is for an authentication assertion.
|
|
430
|
+
if (clientData.type !== 'webauthn.get')
|
|
431
|
+
return false;
|
|
432
|
+
// Validate the challenge in the clientDataJSON.
|
|
433
|
+
if (!clientData.challenge ||
|
|
434
|
+
Hex.fromBytes(Base64.toBytes(clientData.challenge)) !== challenge)
|
|
435
|
+
return false;
|
|
436
|
+
// If origin is provided, validate origin.
|
|
437
|
+
if (origin !== undefined) {
|
|
438
|
+
const origins = Array.isArray(origin) ? origin : [origin];
|
|
439
|
+
if (!origins.includes(clientData.origin))
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
const clientDataJSONHash = Hash.sha256(Bytes.fromString(clientDataJSON), {
|
|
443
|
+
as: 'Bytes',
|
|
444
|
+
});
|
|
445
|
+
const payload = Bytes.concat(authenticatorDataBytes, clientDataJSONHash);
|
|
446
|
+
return P256.verify({
|
|
447
|
+
hash: true,
|
|
448
|
+
payload,
|
|
449
|
+
publicKey,
|
|
450
|
+
signature,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=Authentication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Authentication.js","sourceRoot":"","sources":["../../webauthn/Authentication.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import * as Base64 from '../core/Base64.js';
|
|
2
|
+
import * as Bytes from '../core/Bytes.js';
|
|
3
|
+
import * as Cbor from '../core/Cbor.js';
|
|
4
|
+
import * as CoseKey from '../core/CoseKey.js';
|
|
5
|
+
import * as Hash from '../core/Hash.js';
|
|
6
|
+
import * as Hex from '../core/Hex.js';
|
|
7
|
+
/**
|
|
8
|
+
* Gets the authenticator data which contains information about the
|
|
9
|
+
* processing of an authenticator request (ie. from `Authentication.sign`).
|
|
10
|
+
*
|
|
11
|
+
* :::warning
|
|
12
|
+
*
|
|
13
|
+
* This function is mainly for testing purposes or for manually constructing
|
|
14
|
+
* autenticator data. In most cases you will not need this function.
|
|
15
|
+
* `authenticatorData` is typically returned as part of the
|
|
16
|
+
* authenticator response.
|
|
17
|
+
*
|
|
18
|
+
* :::
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts twoslash
|
|
22
|
+
* import { Authenticator } from 'ox/webauthn'
|
|
23
|
+
*
|
|
24
|
+
* const authenticatorData = Authenticator.getAuthenticatorData({
|
|
25
|
+
* rpId: 'example.com',
|
|
26
|
+
* signCount: 420,
|
|
27
|
+
* })
|
|
28
|
+
* // @log: "0xa379a6f6eeafb9a55e378c118034e2751e682fab9f2d30ab13d2125586ce194705000001a4"
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ### With Attested Credential Data
|
|
33
|
+
*
|
|
34
|
+
* Include a credential ID and public key in the authenticator data (for registration responses):
|
|
35
|
+
*
|
|
36
|
+
* ```ts twoslash
|
|
37
|
+
* import { P256 } from 'ox'
|
|
38
|
+
* import { Authenticator } from 'ox/webauthn'
|
|
39
|
+
*
|
|
40
|
+
* const { publicKey } = P256.createKeyPair()
|
|
41
|
+
*
|
|
42
|
+
* const authenticatorData = Authenticator.getAuthenticatorData({
|
|
43
|
+
* rpId: 'example.com',
|
|
44
|
+
* flag: 0x41, // UP + AT
|
|
45
|
+
* credential: {
|
|
46
|
+
* id: new Uint8Array(32),
|
|
47
|
+
* publicKey,
|
|
48
|
+
* },
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @param options - Options to construct the authenticator data.
|
|
53
|
+
* @returns The authenticator data.
|
|
54
|
+
*/
|
|
55
|
+
export function getAuthenticatorData(options = {}) {
|
|
56
|
+
const { credential, flag = 5, rpId = window.location.hostname, signCount = 0, } = options;
|
|
57
|
+
const rpIdHash = Hash.sha256(Hex.fromString(rpId));
|
|
58
|
+
const flag_bytes = Hex.fromNumber(flag, { size: 1 });
|
|
59
|
+
const signCount_bytes = Hex.fromNumber(signCount, { size: 4 });
|
|
60
|
+
const base = Hex.concat(rpIdHash, flag_bytes, signCount_bytes);
|
|
61
|
+
if (!credential)
|
|
62
|
+
return base;
|
|
63
|
+
// AAGUID (16 bytes of zeros)
|
|
64
|
+
const aaguid = Hex.fromBytes(new Uint8Array(16));
|
|
65
|
+
// Credential ID
|
|
66
|
+
const credentialId = Hex.fromBytes(credential.id);
|
|
67
|
+
const credIdLen = Hex.fromNumber(credential.id.length, { size: 2 });
|
|
68
|
+
// COSE public key
|
|
69
|
+
const coseKey = CoseKey.fromPublicKey(credential.publicKey);
|
|
70
|
+
return Hex.concat(base, aaguid, credIdLen, credentialId, coseKey);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extracts the signature counter from the authenticator data.
|
|
74
|
+
* The counter is a 4-byte big-endian unsigned integer at bytes 33–36.
|
|
75
|
+
*
|
|
76
|
+
* Useful for detecting cloned authenticators: if the counter is non-zero and
|
|
77
|
+
* does not monotonically increase between assertions, it may indicate a cloned key.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts twoslash
|
|
81
|
+
* import { Authenticator } from 'ox/webauthn'
|
|
82
|
+
*
|
|
83
|
+
* const signCount = Authenticator.getSignCount(
|
|
84
|
+
* '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000001',
|
|
85
|
+
* )
|
|
86
|
+
* // @log: 1
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @param authenticatorData - The authenticator data hex string.
|
|
90
|
+
* @returns The signature counter.
|
|
91
|
+
*/
|
|
92
|
+
export function getSignCount(authenticatorData) {
|
|
93
|
+
const bytes = Bytes.fromHex(authenticatorData);
|
|
94
|
+
if (bytes.length < 37)
|
|
95
|
+
return 0;
|
|
96
|
+
return (((bytes[33] << 24) |
|
|
97
|
+
(bytes[34] << 16) |
|
|
98
|
+
(bytes[35] << 8) |
|
|
99
|
+
bytes[36]) >>>
|
|
100
|
+
0);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Constructs the Client Data in stringified JSON format which represents client data that
|
|
104
|
+
* was passed to `credentials.get()` or `credentials.create()`.
|
|
105
|
+
*
|
|
106
|
+
* :::warning
|
|
107
|
+
*
|
|
108
|
+
* This function is mainly for testing purposes or for manually constructing
|
|
109
|
+
* client data. In most cases you will not need this function.
|
|
110
|
+
* `clientDataJSON` is typically returned as part of the authenticator response.
|
|
111
|
+
*
|
|
112
|
+
* :::
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts twoslash
|
|
116
|
+
* import { Authenticator } from 'ox/webauthn'
|
|
117
|
+
*
|
|
118
|
+
* const clientDataJSON = Authenticator.getClientDataJSON({
|
|
119
|
+
* challenge: '0xdeadbeef',
|
|
120
|
+
* origin: 'https://example.com',
|
|
121
|
+
* })
|
|
122
|
+
* // @log: "{"type":"webauthn.get","challenge":"3q2-7w","origin":"https://example.com","crossOrigin":false}"
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @param options - Options to construct the client data.
|
|
126
|
+
* @returns The client data.
|
|
127
|
+
*/
|
|
128
|
+
export function getClientDataJSON(options) {
|
|
129
|
+
const { challenge, crossOrigin = false, extraClientData, origin = window.location.origin, type = 'webauthn.get', } = options;
|
|
130
|
+
return JSON.stringify({
|
|
131
|
+
type,
|
|
132
|
+
challenge: Base64.fromHex(challenge, { url: true, pad: false }),
|
|
133
|
+
origin,
|
|
134
|
+
crossOrigin,
|
|
135
|
+
...extraClientData,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Constructs a CBOR-encoded attestation object for testing WebAuthn registration
|
|
140
|
+
* verification. Combines the authenticator data with an attestation statement.
|
|
141
|
+
*
|
|
142
|
+
* :::warning
|
|
143
|
+
*
|
|
144
|
+
* This function is mainly for testing purposes. In production, the attestation
|
|
145
|
+
* object is returned by the authenticator during `navigator.credentials.create()`.
|
|
146
|
+
*
|
|
147
|
+
* :::
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts twoslash
|
|
151
|
+
* import { P256 } from 'ox'
|
|
152
|
+
* import { Authenticator } from 'ox/webauthn'
|
|
153
|
+
*
|
|
154
|
+
* const { publicKey } = P256.createKeyPair()
|
|
155
|
+
*
|
|
156
|
+
* const attestationObject = Authenticator.getAttestationObject({
|
|
157
|
+
* authData: Authenticator.getAuthenticatorData({
|
|
158
|
+
* rpId: 'example.com',
|
|
159
|
+
* flag: 0x41,
|
|
160
|
+
* credential: { id: new Uint8Array(32), publicKey },
|
|
161
|
+
* }),
|
|
162
|
+
* })
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* @param options - Options to construct the attestation object.
|
|
166
|
+
* @returns The CBOR-encoded attestation object as a Hex string.
|
|
167
|
+
*/
|
|
168
|
+
export function getAttestationObject(options) {
|
|
169
|
+
const { attStmt = {}, authData, fmt = 'none' } = options;
|
|
170
|
+
return Cbor.encode({
|
|
171
|
+
fmt,
|
|
172
|
+
attStmt,
|
|
173
|
+
authData: Hex.toBytes(authData),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=Authenticator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Authenticator.js","sourceRoot":"","sources":["../../webauthn/Authenticator.ts"],"names":[],"mappings":"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"}
|