ox 1.2.0 → 1.3.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 (187) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/core/AbiFunction.d.ts +1 -1
  3. package/dist/core/AbiFunction.d.ts.map +1 -1
  4. package/dist/core/AbiFunction.js +6 -2
  5. package/dist/core/AbiFunction.js.map +1 -1
  6. package/dist/core/AbiParameters.d.ts +1 -1
  7. package/dist/core/AbiParameters.d.ts.map +1 -1
  8. package/dist/core/AbiParameters.js +1 -13
  9. package/dist/core/AbiParameters.js.map +1 -1
  10. package/dist/core/AesGcm.d.ts +36 -1
  11. package/dist/core/AesGcm.d.ts.map +1 -1
  12. package/dist/core/AesGcm.js +44 -0
  13. package/dist/core/AesGcm.js.map +1 -1
  14. package/dist/core/Ed25519.d.ts +46 -1
  15. package/dist/core/Ed25519.d.ts.map +1 -1
  16. package/dist/core/Ed25519.js +43 -0
  17. package/dist/core/Ed25519.js.map +1 -1
  18. package/dist/core/Engine.d.ts +1 -1
  19. package/dist/core/Engine.d.ts.map +1 -1
  20. package/dist/core/Engine.js.map +1 -1
  21. package/dist/core/Hash.d.ts +164 -1
  22. package/dist/core/Hash.d.ts.map +1 -1
  23. package/dist/core/Hash.js +172 -0
  24. package/dist/core/Hash.js.map +1 -1
  25. package/dist/core/Hex.d.ts.map +1 -1
  26. package/dist/core/Hex.js +1 -4
  27. package/dist/core/Hex.js.map +1 -1
  28. package/dist/core/Rlp.d.ts +32 -0
  29. package/dist/core/Rlp.d.ts.map +1 -1
  30. package/dist/core/Rlp.js +146 -0
  31. package/dist/core/Rlp.js.map +1 -1
  32. package/dist/core/Secp256k1.d.ts +46 -1
  33. package/dist/core/Secp256k1.d.ts.map +1 -1
  34. package/dist/core/Secp256k1.js +48 -0
  35. package/dist/core/Secp256k1.js.map +1 -1
  36. package/dist/core/WebAuthn.d.ts +224 -0
  37. package/dist/core/WebAuthn.d.ts.map +1 -0
  38. package/dist/core/WebAuthn.js +343 -0
  39. package/dist/core/WebAuthn.js.map +1 -0
  40. package/dist/core/WebAuthnP256.d.ts +46 -50
  41. package/dist/core/WebAuthnP256.d.ts.map +1 -1
  42. package/dist/core/WebAuthnP256.js +44 -50
  43. package/dist/core/WebAuthnP256.js.map +1 -1
  44. package/dist/core/internal/abiParameters.d.ts +7 -87
  45. package/dist/core/internal/abiParameters.d.ts.map +1 -1
  46. package/dist/core/internal/abiParameters.js +339 -194
  47. package/dist/core/internal/abiParameters.js.map +1 -1
  48. package/dist/core/internal/engine.d.ts +35 -2
  49. package/dist/core/internal/engine.d.ts.map +1 -1
  50. package/dist/core/internal/engine.js +12 -1
  51. package/dist/core/internal/engine.js.map +1 -1
  52. package/dist/core/internal/hash.d.ts +10 -0
  53. package/dist/core/internal/hash.d.ts.map +1 -1
  54. package/dist/core/internal/hash.js +26 -1
  55. package/dist/core/internal/hash.js.map +1 -1
  56. package/dist/index.d.ts +23 -14
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +23 -14
  59. package/dist/index.js.map +1 -1
  60. package/dist/node/Engine.d.ts +1 -1
  61. package/dist/node/Engine.d.ts.map +1 -1
  62. package/dist/node/Engine.js.map +1 -1
  63. package/dist/node/Hash.d.ts +6 -4
  64. package/dist/node/Hash.d.ts.map +1 -1
  65. package/dist/node/Hash.js +35 -3
  66. package/dist/node/Hash.js.map +1 -1
  67. package/dist/tempo/AuthorizationTempo.d.ts +6 -6
  68. package/dist/tempo/AuthorizationTempo.js +6 -6
  69. package/dist/tempo/KeyAuthorization.d.ts +3 -8
  70. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  71. package/dist/tempo/KeyAuthorization.js +3 -8
  72. package/dist/tempo/KeyAuthorization.js.map +1 -1
  73. package/dist/tempo/SignatureEnvelope.d.ts +8 -8
  74. package/dist/tempo/SignatureEnvelope.js +8 -8
  75. package/dist/wasm/Engine.d.ts +1 -1
  76. package/dist/wasm/Engine.d.ts.map +1 -1
  77. package/dist/wasm/Engine.js.map +1 -1
  78. package/dist/wasm/Hash.d.ts +1 -1
  79. package/dist/wasm/Hash.d.ts.map +1 -1
  80. package/dist/wasm/Hash.js +5 -0
  81. package/dist/wasm/Hash.js.map +1 -1
  82. package/dist/wasm/internal/blake3.d.ts +10 -1
  83. package/dist/wasm/internal/blake3.d.ts.map +1 -1
  84. package/dist/wasm/internal/blake3.js +12 -1
  85. package/dist/wasm/internal/blake3.js.map +1 -1
  86. package/dist/wasm/internal/blake3.wasm.d.ts +3 -1
  87. package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -1
  88. package/dist/wasm/internal/blake3.wasm.js +3 -1
  89. package/dist/wasm/internal/blake3.wasm.js.map +1 -1
  90. package/dist/wasm/internal/hash.d.ts +9 -0
  91. package/dist/wasm/internal/hash.d.ts.map +1 -1
  92. package/dist/wasm/internal/hash.js +13 -1
  93. package/dist/wasm/internal/hash.js.map +1 -1
  94. package/dist/wasm/internal/hashState.d.ts +25 -0
  95. package/dist/wasm/internal/hashState.d.ts.map +1 -0
  96. package/dist/wasm/internal/hashState.js +105 -0
  97. package/dist/wasm/internal/hashState.js.map +1 -0
  98. package/dist/wasm/internal/hashes.d.ts +12 -1
  99. package/dist/wasm/internal/hashes.d.ts.map +1 -1
  100. package/dist/wasm/internal/hashes.js +18 -1
  101. package/dist/wasm/internal/hashes.js.map +1 -1
  102. package/dist/wasm/internal/hashes.wasm.d.ts +11 -3
  103. package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
  104. package/dist/wasm/internal/hashes.wasm.js +11 -3
  105. package/dist/wasm/internal/hashes.wasm.js.map +1 -1
  106. package/dist/webauthn/Authentication.d.ts +2 -0
  107. package/dist/webauthn/Authentication.d.ts.map +1 -1
  108. package/dist/webauthn/Authentication.js +2 -1
  109. package/dist/webauthn/Authentication.js.map +1 -1
  110. package/dist/webauthn/Registration.d.ts.map +1 -1
  111. package/dist/webauthn/Registration.js +2 -1
  112. package/dist/webauthn/Registration.js.map +1 -1
  113. package/dist/webauthn/Types.d.ts +27 -6
  114. package/dist/webauthn/Types.d.ts.map +1 -1
  115. package/dist/webauthn/internal/utils.d.ts.map +1 -1
  116. package/dist/webauthn/internal/utils.js +35 -9
  117. package/dist/webauthn/internal/utils.js.map +1 -1
  118. package/dist/zod/TypedData.d.ts +2 -2
  119. package/package.json +9 -1
  120. package/src/benchmarks/Ox.bench.ts +6 -0
  121. package/src/benchmarks/OxNode.bench.ts +8 -0
  122. package/src/benchmarks/OxV0.bench.ts +4 -0
  123. package/src/benchmarks/OxWasm.bench.ts +22 -0
  124. package/src/core/AbiFunction.ts +5 -5
  125. package/src/core/AbiParameters.ts +8 -20
  126. package/src/core/AesGcm.ts +86 -1
  127. package/src/core/Ed25519.ts +87 -1
  128. package/src/core/Engine.ts +1 -0
  129. package/src/core/Hash.ts +262 -1
  130. package/src/core/Hex.ts +1 -4
  131. package/src/core/Rlp.ts +172 -5
  132. package/src/core/Secp256k1.ts +92 -1
  133. package/src/core/WebAuthn.ts +579 -0
  134. package/src/core/WebAuthnP256.ts +47 -50
  135. package/src/core/_test/AbiFunction.test.ts +9 -0
  136. package/src/core/_test/AbiParameters.differential.fuzz.ts +156 -0
  137. package/src/core/_test/AbiParameters.encode.test.ts +22 -0
  138. package/src/core/_test/AbiParameters.fuzz.ts +4 -0
  139. package/src/core/_test/AesGcm.test.ts +76 -0
  140. package/src/core/_test/Ed25519.test-d.ts +13 -1
  141. package/src/core/_test/Ed25519.test.ts +120 -1
  142. package/src/core/_test/Engine.test-d.ts +9 -0
  143. package/src/core/_test/Engine.test.ts +1 -1
  144. package/src/core/_test/Hash.test-d.ts +23 -0
  145. package/src/core/_test/Hash.test.ts +179 -1
  146. package/src/core/_test/Hex.test.ts +2 -0
  147. package/src/core/_test/Rlp.test-d.ts +18 -0
  148. package/src/core/_test/Rlp.test.ts +109 -0
  149. package/src/core/_test/Secp256k1.test-d.ts +14 -0
  150. package/src/core/_test/Secp256k1.test.ts +91 -1
  151. package/src/core/_test/WebAuthn.test-d.ts +142 -0
  152. package/src/core/_test/WebAuthn.test.ts +766 -0
  153. package/src/core/_test/index.test.ts +1 -0
  154. package/src/core/internal/abiParameters.ts +466 -326
  155. package/src/core/internal/engine.ts +47 -2
  156. package/src/core/internal/hash.ts +53 -1
  157. package/src/index.ts +24 -14
  158. package/src/node/Engine.ts +1 -0
  159. package/src/node/Hash.ts +43 -6
  160. package/src/node/_test/Engine.test-d.ts +1 -0
  161. package/src/node/_test/Engine.test.ts +2 -0
  162. package/src/node/_test/Hash.test-d.ts +10 -0
  163. package/src/node/_test/Hash.test.ts +46 -0
  164. package/src/tempo/AuthorizationTempo.ts +6 -6
  165. package/src/tempo/KeyAuthorization.ts +3 -8
  166. package/src/tempo/SignatureEnvelope.ts +8 -8
  167. package/src/version.ts +1 -1
  168. package/src/wasm/Engine.ts +1 -0
  169. package/src/wasm/Hash.ts +10 -0
  170. package/src/wasm/_test/Engine.test-d.ts +1 -0
  171. package/src/wasm/_test/Engine.test.ts +5 -0
  172. package/src/wasm/_test/Hash.browser.test.ts +28 -0
  173. package/src/wasm/_test/Hash.test-d.ts +16 -0
  174. package/src/wasm/_test/Hash.test.ts +250 -1
  175. package/src/wasm/internal/blake3.ts +23 -2
  176. package/src/wasm/internal/blake3.wasm.ts +4 -1
  177. package/src/wasm/internal/hash.ts +27 -1
  178. package/src/wasm/internal/hashState.ts +154 -0
  179. package/src/wasm/internal/hashes.ts +56 -15
  180. package/src/wasm/internal/hashes.wasm.ts +15 -3
  181. package/src/webauthn/Authentication.ts +4 -0
  182. package/src/webauthn/Registration.ts +2 -0
  183. package/src/webauthn/Types.ts +32 -2
  184. package/src/webauthn/_test/Authentication.test.ts +123 -1
  185. package/src/webauthn/_test/Registration.test.ts +26 -6
  186. package/src/webauthn/_test/WebAuthn.browser.test.ts +28 -0
  187. package/src/webauthn/internal/utils.ts +41 -8
@@ -0,0 +1,579 @@
1
+ import * as Bytes from './Bytes.js'
2
+ import * as Errors from './Errors.js'
3
+ import * as Hex from './Hex.js'
4
+ import type { UnionOmit } from './internal/types.js'
5
+ import * as WebAuthnP256 from './WebAuthnP256.js'
6
+ import * as Authentication from '../webauthn/Authentication.js'
7
+ import type * as Credential from '../webauthn/Credential.js'
8
+ import * as Registration from '../webauthn/Registration.js'
9
+ import type * as Types from '../webauthn/Types.js'
10
+
11
+ export * from './WebAuthnP256.js'
12
+
13
+ const defaultPrfInput = Bytes.fromString('ox.webauthn.prf.v1')
14
+
15
+ /** Configuration for evaluating a WebAuthn credential-bound PRF. */
16
+ export type Prf =
17
+ | true
18
+ | {
19
+ /**
20
+ * Application-owned PRF input.
21
+ *
22
+ * Use the same input to reproduce an output. Store unpredictable inputs
23
+ * with the credential metadata.
24
+ */
25
+ input: Bytes.Bytes
26
+ }
27
+
28
+ /**
29
+ * Creates a WebAuthn credential and optionally returns its credential-bound
30
+ * PRF output.
31
+ *
32
+ * @example
33
+ * ```ts twoslash
34
+ * import { Secp256k1, WebAuthn } from 'ox'
35
+ *
36
+ * const credential = await WebAuthn.createCredential({
37
+ * name: 'Example',
38
+ * prf: true
39
+ * })
40
+ * const privateKey = Secp256k1.fromPrf(credential.prf)
41
+ * ```
42
+ *
43
+ * `prf: true` uses the stable input `ox.webauthn.prf.v1`. Pass
44
+ * `{ input }` to use an application-owned input instead.
45
+ *
46
+ * When credential creation enables PRF but does not return an output, this
47
+ * function performs a follow-up assertion. The user may be prompted twice.
48
+ *
49
+ * If PRF evaluation fails after registration, the thrown error retains the
50
+ * created credential on its `credential` property.
51
+ *
52
+ * :::warning
53
+ *
54
+ * PRF output is secret application-held key material. Native
55
+ * `credential.raw.toJSON()` output includes extension results, so do not
56
+ * serialize or send the raw credential.
57
+ *
58
+ * PRF-derived keys are software keys. Code on any origin allowed to use the
59
+ * same RP ID can request the same output after user verification.
60
+ *
61
+ * :::
62
+ *
63
+ * @param options - Credential creation and PRF options.
64
+ * @returns The credential, with a copied PRF output when requested.
65
+ */
66
+ export async function createCredential<
67
+ const options extends createCredential.Options | createCredential.PrfOptions,
68
+ >(options: options): Promise<createCredential.ReturnType<options>> {
69
+ if (!('prf' in options) || options.prf === undefined)
70
+ return WebAuthnP256.createCredential(options) as never
71
+ if ('publicKey' in options) throw new InvalidOptionsError()
72
+
73
+ const rpId = options.rp?.id
74
+ const {
75
+ authenticatorSelection = {
76
+ requireResidentKey: true,
77
+ residentKey: 'required',
78
+ },
79
+ challenge = Hex.random(32),
80
+ createFn,
81
+ extensions,
82
+ getFn,
83
+ prf,
84
+ timeout,
85
+ ...rest
86
+ } = options
87
+ assertExtensions(extensions)
88
+
89
+ const input = getPrfInput(prf)
90
+ const credential = await Registration.create({
91
+ ...rest,
92
+ authenticatorSelection: {
93
+ ...authenticatorSelection,
94
+ userVerification: 'required',
95
+ },
96
+ challenge,
97
+ ...(createFn && { createFn }),
98
+ extensions: {
99
+ ...extensions,
100
+ prf: {
101
+ eval: {
102
+ first: input,
103
+ },
104
+ },
105
+ },
106
+ ...(timeout !== undefined && { timeout }),
107
+ } as Registration.create.Options)
108
+
109
+ const result = (() => {
110
+ try {
111
+ return credential.raw.getClientExtensionResults().prf
112
+ } catch (cause) {
113
+ throw new PrfEvaluationFailedError({
114
+ cause: cause as Error,
115
+ credential,
116
+ })
117
+ }
118
+ })()
119
+ const output = result?.results?.first
120
+ if (output !== undefined)
121
+ return {
122
+ ...credential,
123
+ prf: parsePrfOutput(output, {
124
+ credential,
125
+ }),
126
+ } as never
127
+
128
+ if (result?.enabled !== true)
129
+ throw new PrfNotSupportedError({
130
+ credential,
131
+ })
132
+
133
+ try {
134
+ const result = await getCredential({
135
+ credentialId: credential.id,
136
+ ...(getFn && { getFn }),
137
+ prf: {
138
+ input,
139
+ },
140
+ ...(rpId && { rpId }),
141
+ ...(timeout !== undefined && { timeout }),
142
+ })
143
+ if (result.id !== credential.id)
144
+ throw new Error(
145
+ `Expected credential "${credential.id}", but received "${result.id}".`,
146
+ )
147
+ return {
148
+ ...credential,
149
+ prf: result.prf,
150
+ } as never
151
+ } catch (cause) {
152
+ throw new PrfEvaluationFailedError({
153
+ cause: cause as Error,
154
+ credential,
155
+ })
156
+ }
157
+ }
158
+
159
+ export declare namespace createCredential {
160
+ /** Options for ordinary WebAuthn P256 credential creation. */
161
+ type Options = Registration.create.Options & {
162
+ /** Reserved for credential creation with PRF output. */
163
+ prf?: never
164
+ }
165
+
166
+ /** Options for WebAuthn credential creation with required PRF output. */
167
+ type PrfOptions = UnionOmit<
168
+ Registration.getOptions.Options,
169
+ 'authenticatorSelection' | 'challenge' | 'extensions'
170
+ > & {
171
+ /**
172
+ * Criteria used to select an authenticator. User verification is always required.
173
+ */
174
+ authenticatorSelection?:
175
+ | Omit<
176
+ NonNullable<
177
+ Registration.getOptions.Options['authenticatorSelection']
178
+ >,
179
+ 'userVerification'
180
+ >
181
+ | undefined
182
+ /**
183
+ * Cryptographic challenge. Defaults to a random 32-byte value.
184
+ *
185
+ * Supply and verify a server-generated challenge when registration is
186
+ * also used to authenticate a user to a server.
187
+ */
188
+ challenge?: Registration.getOptions.Options['challenge'] | undefined
189
+ /** Function that creates the WebAuthn credential. */
190
+ createFn?:
191
+ | ((
192
+ options?: Types.CredentialCreationOptions,
193
+ ) => Promise<Types.Credential | null>)
194
+ | undefined
195
+ /** Additional WebAuthn extensions. The `prf` extension is managed by this function. */
196
+ extensions?:
197
+ | Omit<Types.AuthenticationExtensionsClientInputs, 'prf'>
198
+ | undefined
199
+ /** Function used for a follow-up PRF assertion when creation does not return an output. */
200
+ getFn?:
201
+ | ((
202
+ options?: Types.CredentialRequestOptions,
203
+ ) => Promise<Types.Credential | null>)
204
+ | undefined
205
+ /** Credential-bound PRF configuration. */
206
+ prf: Prf
207
+ /** Raw credential options cannot be combined with managed PRF evaluation. */
208
+ publicKey?: never
209
+ }
210
+
211
+ /** Created WebAuthn credential with its credential-bound PRF output. */
212
+ type PrfReturnType = Credential.Credential & {
213
+ /** Copied 32-byte credential-bound PRF output. */
214
+ prf: Bytes.Bytes
215
+ }
216
+
217
+ /** Return type for a WebAuthn credential creation request. */
218
+ type ReturnType<options extends Options | PrfOptions> =
219
+ options extends PrfOptions ? PrfReturnType : Credential.Credential
220
+
221
+ type ErrorType =
222
+ | InvalidExtensionError
223
+ | InvalidOptionsError
224
+ | InvalidPrfOutputError
225
+ | PrfEvaluationFailedError
226
+ | PrfNotSupportedError
227
+ | Registration.create.ErrorType
228
+ | Errors.GlobalErrorType
229
+ }
230
+
231
+ /**
232
+ * Requests a WebAuthn credential and returns its credential-bound PRF output.
233
+ *
234
+ * @example
235
+ * ```ts twoslash
236
+ * import { Secp256k1, WebAuthn } from 'ox'
237
+ *
238
+ * const { prf } = await WebAuthn.getCredential({
239
+ * credentialId: 'oZ48...',
240
+ * prf: true
241
+ * })
242
+ * const privateKey = Secp256k1.fromPrf(prf)
243
+ * ```
244
+ *
245
+ * `prf: true` uses the stable input `ox.webauthn.prf.v1`. Pass
246
+ * `{ input }` to use an application-owned input instead.
247
+ *
248
+ * When more than one credential can be selected, check `result.id`
249
+ * before using the PRF output.
250
+ *
251
+ * :::warning
252
+ *
253
+ * PRF output is secret application-held key material. Native
254
+ * `response.raw.toJSON()` output includes extension results, so do not
255
+ * serialize or send the raw response.
256
+ *
257
+ * :::
258
+ *
259
+ * @param options - Credential request and PRF options.
260
+ * @returns The requested credential and a copied 32-byte PRF output.
261
+ */
262
+ export async function getCredential(
263
+ options: getCredential.Options,
264
+ ): Promise<getCredential.ReturnType> {
265
+ if ('publicKey' in options) throw new InvalidOptionsError()
266
+
267
+ const {
268
+ challenge = Hex.random(32),
269
+ extensions,
270
+ getFn = (options: Types.CredentialRequestOptions | undefined) =>
271
+ window.navigator.credentials.get(options as never),
272
+ prf,
273
+ ...rest
274
+ } = options
275
+ assertExtensions(extensions)
276
+
277
+ const input = getPrfInput(prf)
278
+ const request = Authentication.getOptions({
279
+ ...rest,
280
+ challenge,
281
+ extensions: {
282
+ ...extensions,
283
+ prf: {
284
+ eval: {
285
+ first: input,
286
+ },
287
+ },
288
+ },
289
+ userVerification: 'required',
290
+ })
291
+ const raw = await (async () => {
292
+ try {
293
+ return (await getFn(request)) as Types.PublicKeyCredential | null
294
+ } catch (cause) {
295
+ throw new GetCredentialFailedError({
296
+ cause: cause as Error,
297
+ })
298
+ }
299
+ })()
300
+ if (!raw) throw new GetCredentialFailedError()
301
+
302
+ const response = {
303
+ id: raw.id,
304
+ raw,
305
+ }
306
+
307
+ const output = (() => {
308
+ try {
309
+ return response.raw.getClientExtensionResults().prf?.results?.first
310
+ } catch (cause) {
311
+ throw new PrfEvaluationFailedError({
312
+ cause: cause as Error,
313
+ response,
314
+ })
315
+ }
316
+ })()
317
+ if (output === undefined)
318
+ throw new PrfUnavailableError({
319
+ response,
320
+ })
321
+
322
+ return {
323
+ ...response,
324
+ prf: parsePrfOutput(output, {
325
+ response,
326
+ }),
327
+ }
328
+ }
329
+
330
+ export declare namespace getCredential {
331
+ type Options = UnionOmit<
332
+ Authentication.getOptions.Options,
333
+ 'challenge' | 'extensions' | 'userVerification'
334
+ > & {
335
+ /**
336
+ * Challenge to sign. Defaults to a random 32-byte value.
337
+ *
338
+ * Supply and verify a server-generated challenge when the assertion is
339
+ * also used to authenticate a user to a server.
340
+ */
341
+ challenge?: Hex.Hex | undefined
342
+ /** Additional WebAuthn extensions. The `prf` extension is managed by this function. */
343
+ extensions?:
344
+ | Omit<Types.AuthenticationExtensionsClientInputs, 'prf'>
345
+ | undefined
346
+ /** Function that requests the WebAuthn credential. */
347
+ getFn?:
348
+ | ((
349
+ options?: Types.CredentialRequestOptions,
350
+ ) => Promise<Types.Credential | null>)
351
+ | undefined
352
+ /** Credential-bound PRF configuration. */
353
+ prf: Prf
354
+ /** Raw credential options cannot be combined with managed PRF evaluation. */
355
+ publicKey?: never
356
+ }
357
+
358
+ /** Result from a WebAuthn credential request. */
359
+ type Response = {
360
+ /** Credential identifier. */
361
+ id: string
362
+ /** Native WebAuthn credential. */
363
+ raw: Types.PublicKeyCredential
364
+ }
365
+
366
+ type ReturnType = Response & {
367
+ /** Copied 32-byte credential-bound PRF output. */
368
+ prf: Bytes.Bytes
369
+ }
370
+
371
+ type ErrorType =
372
+ | Authentication.getOptions.ErrorType
373
+ | GetCredentialFailedError
374
+ | InvalidExtensionError
375
+ | InvalidOptionsError
376
+ | InvalidPrfOutputError
377
+ | PrfEvaluationFailedError
378
+ | PrfUnavailableError
379
+ | Errors.GlobalErrorType
380
+ }
381
+
382
+ function getPrfInput(prf: Prf): Bytes.Bytes {
383
+ return (prf === true ? defaultPrfInput : prf.input).slice()
384
+ }
385
+
386
+ type PrfResultContext = {
387
+ credential?: Credential.Credential | undefined
388
+ response?: getCredential.Response | undefined
389
+ }
390
+
391
+ function parsePrfOutput(
392
+ value: unknown,
393
+ context: PrfResultContext,
394
+ ): Bytes.Bytes {
395
+ if (ArrayBuffer.isView(value)) {
396
+ if (value.byteLength !== 32)
397
+ throw new InvalidPrfOutputError({
398
+ ...context,
399
+ size: value.byteLength,
400
+ })
401
+ return new Uint8Array(
402
+ value.buffer,
403
+ value.byteOffset,
404
+ value.byteLength,
405
+ ).slice()
406
+ }
407
+
408
+ const arrayBufferSize = getArrayBufferSize(value)
409
+ if (arrayBufferSize !== undefined) {
410
+ if (arrayBufferSize !== 32)
411
+ throw new InvalidPrfOutputError({
412
+ ...context,
413
+ size: arrayBufferSize,
414
+ })
415
+ return new Uint8Array(value as ArrayBuffer).slice()
416
+ }
417
+
418
+ if (
419
+ typeof value === 'object' &&
420
+ value !== null &&
421
+ 'length' in value &&
422
+ typeof value.length === 'number'
423
+ ) {
424
+ if (value.length !== 32)
425
+ throw new InvalidPrfOutputError({
426
+ ...context,
427
+ size: value.length,
428
+ })
429
+
430
+ const output = new Uint8Array(32)
431
+ for (let index = 0; index < output.length; index++) {
432
+ const byte = (value as ArrayLike<unknown>)[index]
433
+ if (
434
+ typeof byte !== 'number' ||
435
+ !Number.isInteger(byte) ||
436
+ byte < 0 ||
437
+ byte > 255
438
+ )
439
+ throw new InvalidPrfOutputError(context)
440
+ output[index] = byte
441
+ }
442
+ return output
443
+ }
444
+
445
+ throw new InvalidPrfOutputError(context)
446
+ }
447
+
448
+ function getArrayBufferSize(value: unknown): number | undefined {
449
+ try {
450
+ return Object.getOwnPropertyDescriptor(
451
+ ArrayBuffer.prototype,
452
+ 'byteLength',
453
+ )?.get?.call(value)
454
+ } catch {
455
+ return undefined
456
+ }
457
+ }
458
+
459
+ function assertExtensions(
460
+ extensions: Types.AuthenticationExtensionsClientInputs | undefined,
461
+ ) {
462
+ if (extensions?.prf) throw new InvalidExtensionError()
463
+ }
464
+
465
+ /** Thrown when a caller supplies the managed `prf` extension. */
466
+ export class InvalidExtensionError extends Errors.BaseError {
467
+ override readonly name = 'WebAuthn.InvalidExtensionError'
468
+
469
+ constructor() {
470
+ super('The `prf` extension is managed by this function.')
471
+ }
472
+ }
473
+
474
+ /** Thrown when raw credential options are combined with managed PRF evaluation. */
475
+ export class InvalidOptionsError extends Errors.BaseError {
476
+ override readonly name = 'WebAuthn.InvalidOptionsError'
477
+
478
+ constructor() {
479
+ super(
480
+ 'The `publicKey` option cannot be combined with managed PRF evaluation.',
481
+ )
482
+ }
483
+ }
484
+
485
+ /** Thrown when a WebAuthn PRF result is not a valid 32-byte output. */
486
+ export class InvalidPrfOutputError extends Errors.BaseError {
487
+ /** Credential that returned the invalid PRF output, when created in this operation. */
488
+ readonly credential?: Credential.Credential | undefined
489
+
490
+ /** Authentication response that returned the invalid PRF output, when available. */
491
+ readonly response?: getCredential.Response | undefined
492
+
493
+ override readonly name = 'WebAuthn.InvalidPrfOutputError'
494
+
495
+ constructor({
496
+ credential,
497
+ response,
498
+ size,
499
+ }: PrfResultContext & {
500
+ size?: number | undefined
501
+ }) {
502
+ super(
503
+ size === undefined
504
+ ? 'Expected a 32-byte PRF output.'
505
+ : `Expected a 32-byte PRF output, but received ${size} bytes.`,
506
+ )
507
+ this.credential = credential
508
+ this.response = response
509
+ }
510
+ }
511
+
512
+ /** Thrown when a created credential does not support PRF evaluation. */
513
+ export class PrfNotSupportedError extends Errors.BaseError {
514
+ /** Credential that was created before PRF support was determined. */
515
+ readonly credential: Credential.Credential
516
+
517
+ override readonly name = 'WebAuthn.PrfNotSupportedError'
518
+
519
+ constructor({ credential }: { credential: Credential.Credential }) {
520
+ super('The created credential does not support PRF evaluation.')
521
+ this.credential = credential
522
+ }
523
+ }
524
+
525
+ /** Thrown when a credential assertion does not return a PRF output. */
526
+ export class PrfUnavailableError extends Errors.BaseError {
527
+ /** Authentication response that did not contain a PRF output. */
528
+ readonly response: getCredential.Response
529
+
530
+ override readonly name = 'WebAuthn.PrfUnavailableError'
531
+
532
+ constructor({ response }: { response: getCredential.Response }) {
533
+ super('The credential did not return a PRF output.')
534
+ this.response = response
535
+ }
536
+ }
537
+
538
+ /** Thrown when WebAuthn PRF evaluation fails after a credential ceremony. */
539
+ export class PrfEvaluationFailedError extends Errors.BaseError<Error> {
540
+ /** Credential that was created before PRF evaluation failed, when available. */
541
+ readonly credential?: Credential.Credential | undefined
542
+
543
+ /** Authentication response produced before PRF evaluation failed, when available. */
544
+ readonly response?: getCredential.Response | undefined
545
+
546
+ override readonly name = 'WebAuthn.PrfEvaluationFailedError'
547
+
548
+ constructor({
549
+ cause,
550
+ credential,
551
+ response,
552
+ }: {
553
+ cause: Error
554
+ credential?: Credential.Credential | undefined
555
+ response?: getCredential.Response | undefined
556
+ }) {
557
+ super(
558
+ credential
559
+ ? 'Created a credential, but failed to evaluate its PRF.'
560
+ : 'Failed to evaluate the credential PRF.',
561
+ {
562
+ cause,
563
+ },
564
+ )
565
+ this.credential = credential
566
+ this.response = response
567
+ }
568
+ }
569
+
570
+ /** Thrown when a WebAuthn credential request fails. */
571
+ export class GetCredentialFailedError extends Errors.BaseError<Error> {
572
+ override readonly name = 'WebAuthn.GetCredentialFailedError'
573
+
574
+ constructor({ cause }: { cause?: Error | undefined } = {}) {
575
+ super('Failed to request credential.', {
576
+ cause,
577
+ })
578
+ }
579
+ }