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.
- package/CHANGELOG.md +34 -0
- package/dist/core/AbiFunction.d.ts +1 -1
- package/dist/core/AbiFunction.d.ts.map +1 -1
- package/dist/core/AbiFunction.js +6 -2
- package/dist/core/AbiFunction.js.map +1 -1
- package/dist/core/AbiParameters.d.ts +1 -1
- package/dist/core/AbiParameters.d.ts.map +1 -1
- package/dist/core/AbiParameters.js +1 -13
- package/dist/core/AbiParameters.js.map +1 -1
- package/dist/core/AesGcm.d.ts +36 -1
- package/dist/core/AesGcm.d.ts.map +1 -1
- package/dist/core/AesGcm.js +44 -0
- package/dist/core/AesGcm.js.map +1 -1
- package/dist/core/Ed25519.d.ts +46 -1
- package/dist/core/Ed25519.d.ts.map +1 -1
- package/dist/core/Ed25519.js +43 -0
- package/dist/core/Ed25519.js.map +1 -1
- package/dist/core/Engine.d.ts +1 -1
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Hash.d.ts +164 -1
- package/dist/core/Hash.d.ts.map +1 -1
- package/dist/core/Hash.js +172 -0
- package/dist/core/Hash.js.map +1 -1
- package/dist/core/Hex.d.ts.map +1 -1
- package/dist/core/Hex.js +1 -4
- package/dist/core/Hex.js.map +1 -1
- package/dist/core/Rlp.d.ts +32 -0
- package/dist/core/Rlp.d.ts.map +1 -1
- package/dist/core/Rlp.js +146 -0
- package/dist/core/Rlp.js.map +1 -1
- package/dist/core/Secp256k1.d.ts +46 -1
- package/dist/core/Secp256k1.d.ts.map +1 -1
- package/dist/core/Secp256k1.js +48 -0
- package/dist/core/Secp256k1.js.map +1 -1
- package/dist/core/WebAuthn.d.ts +224 -0
- package/dist/core/WebAuthn.d.ts.map +1 -0
- package/dist/core/WebAuthn.js +343 -0
- package/dist/core/WebAuthn.js.map +1 -0
- package/dist/core/WebAuthnP256.d.ts +46 -50
- package/dist/core/WebAuthnP256.d.ts.map +1 -1
- package/dist/core/WebAuthnP256.js +44 -50
- package/dist/core/WebAuthnP256.js.map +1 -1
- package/dist/core/internal/abiParameters.d.ts +7 -87
- package/dist/core/internal/abiParameters.d.ts.map +1 -1
- package/dist/core/internal/abiParameters.js +339 -194
- package/dist/core/internal/abiParameters.js.map +1 -1
- package/dist/core/internal/engine.d.ts +35 -2
- package/dist/core/internal/engine.d.ts.map +1 -1
- package/dist/core/internal/engine.js +12 -1
- package/dist/core/internal/engine.js.map +1 -1
- package/dist/core/internal/hash.d.ts +10 -0
- package/dist/core/internal/hash.d.ts.map +1 -1
- package/dist/core/internal/hash.js +26 -1
- package/dist/core/internal/hash.js.map +1 -1
- package/dist/index.d.ts +23 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -14
- package/dist/index.js.map +1 -1
- package/dist/node/Engine.d.ts +1 -1
- package/dist/node/Engine.d.ts.map +1 -1
- package/dist/node/Engine.js.map +1 -1
- package/dist/node/Hash.d.ts +6 -4
- package/dist/node/Hash.d.ts.map +1 -1
- package/dist/node/Hash.js +35 -3
- package/dist/node/Hash.js.map +1 -1
- package/dist/tempo/AuthorizationTempo.d.ts +6 -6
- package/dist/tempo/AuthorizationTempo.js +6 -6
- package/dist/tempo/KeyAuthorization.d.ts +3 -8
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/KeyAuthorization.js +3 -8
- package/dist/tempo/KeyAuthorization.js.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +8 -8
- package/dist/tempo/SignatureEnvelope.js +8 -8
- package/dist/wasm/Engine.d.ts +1 -1
- package/dist/wasm/Engine.d.ts.map +1 -1
- package/dist/wasm/Engine.js.map +1 -1
- package/dist/wasm/Hash.d.ts +1 -1
- package/dist/wasm/Hash.d.ts.map +1 -1
- package/dist/wasm/Hash.js +5 -0
- package/dist/wasm/Hash.js.map +1 -1
- package/dist/wasm/internal/blake3.d.ts +10 -1
- package/dist/wasm/internal/blake3.d.ts.map +1 -1
- package/dist/wasm/internal/blake3.js +12 -1
- package/dist/wasm/internal/blake3.js.map +1 -1
- package/dist/wasm/internal/blake3.wasm.d.ts +3 -1
- package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -1
- package/dist/wasm/internal/blake3.wasm.js +3 -1
- package/dist/wasm/internal/blake3.wasm.js.map +1 -1
- package/dist/wasm/internal/hash.d.ts +9 -0
- package/dist/wasm/internal/hash.d.ts.map +1 -1
- package/dist/wasm/internal/hash.js +13 -1
- package/dist/wasm/internal/hash.js.map +1 -1
- package/dist/wasm/internal/hashState.d.ts +25 -0
- package/dist/wasm/internal/hashState.d.ts.map +1 -0
- package/dist/wasm/internal/hashState.js +105 -0
- package/dist/wasm/internal/hashState.js.map +1 -0
- package/dist/wasm/internal/hashes.d.ts +12 -1
- package/dist/wasm/internal/hashes.d.ts.map +1 -1
- package/dist/wasm/internal/hashes.js +18 -1
- package/dist/wasm/internal/hashes.js.map +1 -1
- package/dist/wasm/internal/hashes.wasm.d.ts +11 -3
- package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
- package/dist/wasm/internal/hashes.wasm.js +11 -3
- package/dist/wasm/internal/hashes.wasm.js.map +1 -1
- package/dist/webauthn/Authentication.d.ts +2 -0
- package/dist/webauthn/Authentication.d.ts.map +1 -1
- package/dist/webauthn/Authentication.js +2 -1
- package/dist/webauthn/Authentication.js.map +1 -1
- package/dist/webauthn/Registration.d.ts.map +1 -1
- package/dist/webauthn/Registration.js +2 -1
- package/dist/webauthn/Registration.js.map +1 -1
- package/dist/webauthn/Types.d.ts +27 -6
- package/dist/webauthn/Types.d.ts.map +1 -1
- package/dist/webauthn/internal/utils.d.ts.map +1 -1
- package/dist/webauthn/internal/utils.js +35 -9
- package/dist/webauthn/internal/utils.js.map +1 -1
- package/dist/zod/TypedData.d.ts +2 -2
- package/package.json +9 -1
- package/src/benchmarks/Ox.bench.ts +6 -0
- package/src/benchmarks/OxNode.bench.ts +8 -0
- package/src/benchmarks/OxV0.bench.ts +4 -0
- package/src/benchmarks/OxWasm.bench.ts +22 -0
- package/src/core/AbiFunction.ts +5 -5
- package/src/core/AbiParameters.ts +8 -20
- package/src/core/AesGcm.ts +86 -1
- package/src/core/Ed25519.ts +87 -1
- package/src/core/Engine.ts +1 -0
- package/src/core/Hash.ts +262 -1
- package/src/core/Hex.ts +1 -4
- package/src/core/Rlp.ts +172 -5
- package/src/core/Secp256k1.ts +92 -1
- package/src/core/WebAuthn.ts +579 -0
- package/src/core/WebAuthnP256.ts +47 -50
- package/src/core/_test/AbiFunction.test.ts +9 -0
- package/src/core/_test/AbiParameters.differential.fuzz.ts +156 -0
- package/src/core/_test/AbiParameters.encode.test.ts +22 -0
- package/src/core/_test/AbiParameters.fuzz.ts +4 -0
- package/src/core/_test/AesGcm.test.ts +76 -0
- package/src/core/_test/Ed25519.test-d.ts +13 -1
- package/src/core/_test/Ed25519.test.ts +120 -1
- package/src/core/_test/Engine.test-d.ts +9 -0
- package/src/core/_test/Engine.test.ts +1 -1
- package/src/core/_test/Hash.test-d.ts +23 -0
- package/src/core/_test/Hash.test.ts +179 -1
- package/src/core/_test/Hex.test.ts +2 -0
- package/src/core/_test/Rlp.test-d.ts +18 -0
- package/src/core/_test/Rlp.test.ts +109 -0
- package/src/core/_test/Secp256k1.test-d.ts +14 -0
- package/src/core/_test/Secp256k1.test.ts +91 -1
- package/src/core/_test/WebAuthn.test-d.ts +142 -0
- package/src/core/_test/WebAuthn.test.ts +766 -0
- package/src/core/_test/index.test.ts +1 -0
- package/src/core/internal/abiParameters.ts +466 -326
- package/src/core/internal/engine.ts +47 -2
- package/src/core/internal/hash.ts +53 -1
- package/src/index.ts +24 -14
- package/src/node/Engine.ts +1 -0
- package/src/node/Hash.ts +43 -6
- package/src/node/_test/Engine.test-d.ts +1 -0
- package/src/node/_test/Engine.test.ts +2 -0
- package/src/node/_test/Hash.test-d.ts +10 -0
- package/src/node/_test/Hash.test.ts +46 -0
- package/src/tempo/AuthorizationTempo.ts +6 -6
- package/src/tempo/KeyAuthorization.ts +3 -8
- package/src/tempo/SignatureEnvelope.ts +8 -8
- package/src/version.ts +1 -1
- package/src/wasm/Engine.ts +1 -0
- package/src/wasm/Hash.ts +10 -0
- package/src/wasm/_test/Engine.test-d.ts +1 -0
- package/src/wasm/_test/Engine.test.ts +5 -0
- package/src/wasm/_test/Hash.browser.test.ts +28 -0
- package/src/wasm/_test/Hash.test-d.ts +16 -0
- package/src/wasm/_test/Hash.test.ts +250 -1
- package/src/wasm/internal/blake3.ts +23 -2
- package/src/wasm/internal/blake3.wasm.ts +4 -1
- package/src/wasm/internal/hash.ts +27 -1
- package/src/wasm/internal/hashState.ts +154 -0
- package/src/wasm/internal/hashes.ts +56 -15
- package/src/wasm/internal/hashes.wasm.ts +15 -3
- package/src/webauthn/Authentication.ts +4 -0
- package/src/webauthn/Registration.ts +2 -0
- package/src/webauthn/Types.ts +32 -2
- package/src/webauthn/_test/Authentication.test.ts +123 -1
- package/src/webauthn/_test/Registration.test.ts +26 -6
- package/src/webauthn/_test/WebAuthn.browser.test.ts +28 -0
- package/src/webauthn/internal/utils.ts +41 -8
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "ox",
|
|
3
3
|
"description": "Ethereum Standard Library",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.3.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"license": "MIT",
|
|
@@ -417,6 +417,11 @@
|
|
|
417
417
|
"types": "./dist/core/Value.d.ts",
|
|
418
418
|
"default": "./dist/core/Value.js"
|
|
419
419
|
},
|
|
420
|
+
"./WebAuthn": {
|
|
421
|
+
"src": "./src/core/WebAuthn.ts",
|
|
422
|
+
"types": "./dist/core/WebAuthn.d.ts",
|
|
423
|
+
"default": "./dist/core/WebAuthn.js"
|
|
424
|
+
},
|
|
420
425
|
"./WebAuthnP256": {
|
|
421
426
|
"src": "./src/core/WebAuthnP256.ts",
|
|
422
427
|
"types": "./dist/core/WebAuthnP256.d.ts",
|
|
@@ -1057,6 +1062,9 @@
|
|
|
1057
1062
|
"types": "./dist/index.d.ts",
|
|
1058
1063
|
"typesVersions": {
|
|
1059
1064
|
"*": {
|
|
1065
|
+
"_types/*": [
|
|
1066
|
+
"./dist/*.d.ts"
|
|
1067
|
+
],
|
|
1060
1068
|
"*": [
|
|
1061
1069
|
"./dist/*",
|
|
1062
1070
|
"./dist/*/index.d.ts",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Engine } from 'ox'
|
|
2
|
+
import { Engine as NodeEngine } from 'ox/node'
|
|
3
|
+
import { register } from '../../test/benchmarks/comparison.js'
|
|
4
|
+
import { createOperations } from '../../test/benchmarks/comparison.current.js'
|
|
5
|
+
|
|
6
|
+
Engine.reset()
|
|
7
|
+
Engine.set(await NodeEngine.engine())
|
|
8
|
+
register('ox/node', createOperations())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Engine } from 'ox'
|
|
2
|
+
import {
|
|
3
|
+
Engine as WasmEngine,
|
|
4
|
+
Keystore as WasmKeystore,
|
|
5
|
+
Secp256k1 as WasmSecp256k1,
|
|
6
|
+
} from 'ox/wasm'
|
|
7
|
+
import { register } from '../../test/benchmarks/comparison.js'
|
|
8
|
+
import { createOperations } from '../../test/benchmarks/comparison.current.js'
|
|
9
|
+
|
|
10
|
+
const [wasm, keystore, secp256k1] = await Promise.all([
|
|
11
|
+
WasmEngine.engine(),
|
|
12
|
+
WasmKeystore.engine(),
|
|
13
|
+
WasmSecp256k1.engine(),
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
Engine.reset()
|
|
17
|
+
Engine.set({
|
|
18
|
+
...wasm,
|
|
19
|
+
Keystore: keystore,
|
|
20
|
+
Secp256k1: secp256k1,
|
|
21
|
+
})
|
|
22
|
+
register('ox/wasm', createOperations())
|
package/src/core/AbiFunction.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type * as Errors from './Errors.js'
|
|
|
6
6
|
import * as Hex from './Hex.js'
|
|
7
7
|
import type * as internal from './internal/abiFunction.js'
|
|
8
8
|
import type * as AbiItem_internal from './internal/abiItem.js'
|
|
9
|
-
import
|
|
9
|
+
import * as AbiParameters_internal from './internal/abiParameters.js'
|
|
10
10
|
import * as formatAbiItem from './internal/human-readable/formatAbiItem.js'
|
|
11
11
|
import type { IsNarrowable } from './internal/types.js'
|
|
12
12
|
|
|
@@ -670,10 +670,10 @@ export function encodeData(
|
|
|
670
670
|
|
|
671
671
|
const selector = getSelector(item)
|
|
672
672
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
673
|
+
if (args.length === 0) return selector
|
|
674
|
+
return AbiParameters_internal.encodeParameters(item.inputs, args, {
|
|
675
|
+
prefix: selector,
|
|
676
|
+
})
|
|
677
677
|
}
|
|
678
678
|
|
|
679
679
|
export declare namespace encodeData {
|
|
@@ -212,29 +212,17 @@ export function encode<
|
|
|
212
212
|
options?: encode.Options,
|
|
213
213
|
): Hex.Hex {
|
|
214
214
|
const { checksumAddress = false } = options ?? {}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const preparedParameters = internal.prepareParameters({
|
|
223
|
-
checksumAddress,
|
|
224
|
-
parameters: parameters as readonly Parameter[],
|
|
225
|
-
values: values as any,
|
|
226
|
-
})
|
|
227
|
-
const data = internal.encode(preparedParameters)
|
|
228
|
-
if (data.length === 0) return '0x'
|
|
229
|
-
return data
|
|
215
|
+
return internal.encodeParameters(
|
|
216
|
+
parameters as readonly Parameter[],
|
|
217
|
+
values as readonly unknown[],
|
|
218
|
+
{
|
|
219
|
+
checksumAddress,
|
|
220
|
+
},
|
|
221
|
+
)
|
|
230
222
|
}
|
|
231
223
|
|
|
232
224
|
export declare namespace encode {
|
|
233
|
-
type ErrorType =
|
|
234
|
-
| LengthMismatchError
|
|
235
|
-
| internal.encode.ErrorType
|
|
236
|
-
| internal.prepareParameters.ErrorType
|
|
237
|
-
| Errors.GlobalErrorType
|
|
225
|
+
type ErrorType = internal.encodeParameters.ErrorType | Errors.GlobalErrorType
|
|
238
226
|
|
|
239
227
|
type Options = {
|
|
240
228
|
/**
|
package/src/core/AesGcm.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Bytes from './Bytes.js'
|
|
2
|
-
import
|
|
2
|
+
import * as Errors from './Errors.js'
|
|
3
3
|
import * as Hex from './Hex.js'
|
|
4
4
|
|
|
5
5
|
export const ivLength = 16
|
|
@@ -134,6 +134,70 @@ export declare namespace encrypt {
|
|
|
134
134
|
| Errors.GlobalErrorType
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Derives an AES-256-GCM key from a 32-byte WebAuthn PRF output.
|
|
139
|
+
*
|
|
140
|
+
* The permanent derivation contract uses the PRF output as the HMAC-SHA256
|
|
141
|
+
* key. Its message is the UTF-8 bytes of `ox.aesGcm.fromPrf.v1` followed by
|
|
142
|
+
* a 32-bit big-endian counter set to zero.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts twoslash
|
|
146
|
+
* import { AesGcm } from 'ox'
|
|
147
|
+
*
|
|
148
|
+
* const key = await AesGcm.fromPrf(
|
|
149
|
+
* '0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
150
|
+
* )
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @param value - A 32-byte WebAuthn PRF output.
|
|
154
|
+
* @returns A nonextractable AES-256-GCM key for encryption and decryption.
|
|
155
|
+
*/
|
|
156
|
+
export async function fromPrf(
|
|
157
|
+
value: Hex.Hex | Bytes.Bytes,
|
|
158
|
+
): Promise<CryptoKey> {
|
|
159
|
+
const bytes = Bytes.from(value)
|
|
160
|
+
if (bytes.length !== 32) throw new InvalidPrfSizeError({ size: bytes.length })
|
|
161
|
+
|
|
162
|
+
const prfKey = await globalThis.crypto.subtle.importKey(
|
|
163
|
+
'raw',
|
|
164
|
+
bytes,
|
|
165
|
+
{
|
|
166
|
+
name: 'HMAC',
|
|
167
|
+
hash: 'SHA-256',
|
|
168
|
+
},
|
|
169
|
+
false,
|
|
170
|
+
['sign'],
|
|
171
|
+
)
|
|
172
|
+
const key = new Uint8Array(
|
|
173
|
+
await globalThis.crypto.subtle.sign(
|
|
174
|
+
'HMAC',
|
|
175
|
+
prfKey,
|
|
176
|
+
Bytes.concat(fromPrfLabel, Bytes.fromNumber(0, { size: 4 })),
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
try {
|
|
180
|
+
return await globalThis.crypto.subtle.importKey(
|
|
181
|
+
'raw',
|
|
182
|
+
key,
|
|
183
|
+
{ name: 'AES-GCM' },
|
|
184
|
+
false,
|
|
185
|
+
['encrypt', 'decrypt'],
|
|
186
|
+
)
|
|
187
|
+
} finally {
|
|
188
|
+
key.fill(0)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export declare namespace fromPrf {
|
|
193
|
+
type ErrorType =
|
|
194
|
+
| Bytes.concat.ErrorType
|
|
195
|
+
| Bytes.from.ErrorType
|
|
196
|
+
| Bytes.fromNumber.ErrorType
|
|
197
|
+
| InvalidPrfSizeError
|
|
198
|
+
| Errors.GlobalErrorType
|
|
199
|
+
}
|
|
200
|
+
|
|
137
201
|
/**
|
|
138
202
|
* Derives an AES-GCM key from a password using PBKDF2.
|
|
139
203
|
*
|
|
@@ -206,3 +270,24 @@ export function randomSalt(size = 32): Bytes.Bytes {
|
|
|
206
270
|
export declare namespace randomSalt {
|
|
207
271
|
type ErrorType = Bytes.random.ErrorType | Errors.GlobalErrorType
|
|
208
272
|
}
|
|
273
|
+
|
|
274
|
+
/** Thrown when a WebAuthn PRF output is not 32 bytes. */
|
|
275
|
+
export class InvalidPrfSizeError extends Errors.BaseError {
|
|
276
|
+
override readonly name = 'AesGcm.InvalidPrfSizeError'
|
|
277
|
+
|
|
278
|
+
constructor(options: InvalidPrfSizeError.Options) {
|
|
279
|
+
super(
|
|
280
|
+
`PRF output must be exactly 32 bytes. Received ${options.size} bytes.`,
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export declare namespace InvalidPrfSizeError {
|
|
286
|
+
/** Options for {@link ox#AesGcm.InvalidPrfSizeError}. */
|
|
287
|
+
type Options = {
|
|
288
|
+
/** Received PRF output size. */
|
|
289
|
+
size: number
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const fromPrfLabel = Bytes.fromString('ox.aesGcm.fromPrf.v1')
|
package/src/core/Ed25519.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ed25519 } from '@noble/curves/ed25519.js'
|
|
2
2
|
import * as Bytes from './Bytes.js'
|
|
3
|
-
import
|
|
3
|
+
import * as Errors from './Errors.js'
|
|
4
|
+
import * as Hash from './Hash.js'
|
|
4
5
|
import * as Hex from './Hex.js'
|
|
5
6
|
import * as engine from './internal/ed25519.js'
|
|
6
7
|
|
|
@@ -58,6 +59,70 @@ export declare namespace createKeyPair {
|
|
|
58
59
|
| Errors.GlobalErrorType
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Derives an Ed25519 private key from a 32-byte WebAuthn PRF output.
|
|
64
|
+
*
|
|
65
|
+
* The permanent derivation contract uses the PRF output as the HMAC-SHA256
|
|
66
|
+
* key. Its message is the UTF-8 bytes of `ox.ed25519.fromPrf.v1` followed by
|
|
67
|
+
* a 32-bit big-endian counter set to zero.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts twoslash
|
|
71
|
+
* import { Ed25519 } from 'ox'
|
|
72
|
+
*
|
|
73
|
+
* const privateKey = Ed25519.fromPrf(
|
|
74
|
+
* '0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
75
|
+
* )
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @param value - A 32-byte WebAuthn PRF output.
|
|
79
|
+
* @param options - Options.
|
|
80
|
+
* @returns An Ed25519 private key.
|
|
81
|
+
*/
|
|
82
|
+
export function fromPrf<as extends 'Hex' | 'Bytes' = 'Hex'>(
|
|
83
|
+
value: Hex.Hex | Bytes.Bytes,
|
|
84
|
+
options: fromPrf.Options<as> = {},
|
|
85
|
+
): fromPrf.ReturnType<as> {
|
|
86
|
+
const { as = 'Hex' } = options
|
|
87
|
+
const bytes = Bytes.from(value)
|
|
88
|
+
if (bytes.length !== 32) throw new InvalidPrfSizeError({ size: bytes.length })
|
|
89
|
+
|
|
90
|
+
const privateKey = Hash.hmac256(
|
|
91
|
+
bytes,
|
|
92
|
+
Bytes.concat(fromPrfLabel, Bytes.fromNumber(0, { size: 4 })),
|
|
93
|
+
{ as: 'Bytes' },
|
|
94
|
+
)
|
|
95
|
+
if (as === 'Hex') {
|
|
96
|
+
const value = Hex.fromBytes(privateKey)
|
|
97
|
+
privateKey.fill(0)
|
|
98
|
+
return value as never
|
|
99
|
+
}
|
|
100
|
+
return privateKey as never
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export declare namespace fromPrf {
|
|
104
|
+
type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
|
|
105
|
+
/**
|
|
106
|
+
* Format of the returned private key.
|
|
107
|
+
* @default 'Hex'
|
|
108
|
+
*/
|
|
109
|
+
as?: as | 'Hex' | 'Bytes' | undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type ReturnType<as extends 'Hex' | 'Bytes'> =
|
|
113
|
+
| (as extends 'Bytes' ? Bytes.Bytes : never)
|
|
114
|
+
| (as extends 'Hex' ? Hex.Hex : never)
|
|
115
|
+
|
|
116
|
+
type ErrorType =
|
|
117
|
+
| Bytes.concat.ErrorType
|
|
118
|
+
| Bytes.from.ErrorType
|
|
119
|
+
| Bytes.fromNumber.ErrorType
|
|
120
|
+
| Hash.hmac256.ErrorType
|
|
121
|
+
| Hex.fromBytes.ErrorType
|
|
122
|
+
| InvalidPrfSizeError
|
|
123
|
+
| Errors.GlobalErrorType
|
|
124
|
+
}
|
|
125
|
+
|
|
61
126
|
/**
|
|
62
127
|
* Computes the Ed25519 public key from a provided private key.
|
|
63
128
|
*
|
|
@@ -345,3 +410,24 @@ export declare namespace toX25519PrivateKey {
|
|
|
345
410
|
| Hex.fromBytes.ErrorType
|
|
346
411
|
| Errors.GlobalErrorType
|
|
347
412
|
}
|
|
413
|
+
|
|
414
|
+
/** Thrown when a WebAuthn PRF output is not 32 bytes. */
|
|
415
|
+
export class InvalidPrfSizeError extends Errors.BaseError {
|
|
416
|
+
override readonly name = 'Ed25519.InvalidPrfSizeError'
|
|
417
|
+
|
|
418
|
+
constructor(options: InvalidPrfSizeError.Options) {
|
|
419
|
+
super(
|
|
420
|
+
`PRF output must be exactly 32 bytes. Received ${options.size} bytes.`,
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export declare namespace InvalidPrfSizeError {
|
|
426
|
+
/** Options for {@link ox#Ed25519.InvalidPrfSizeError}. */
|
|
427
|
+
type Options = {
|
|
428
|
+
/** Received PRF output size. */
|
|
429
|
+
size: number
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const fromPrfLabel = Bytes.fromString('ox.ed25519.fromPrf.v1')
|
package/src/core/Engine.ts
CHANGED
package/src/core/Hash.ts
CHANGED
|
@@ -1,8 +1,187 @@
|
|
|
1
1
|
import * as Bytes from './Bytes.js'
|
|
2
|
-
import
|
|
2
|
+
import * as Errors from './Errors.js'
|
|
3
3
|
import * as Hex from './Hex.js'
|
|
4
|
+
import type { HashState } from './internal/engine.js'
|
|
4
5
|
import * as engine from './internal/hash.js'
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Incremental hash state.
|
|
9
|
+
*
|
|
10
|
+
* A hasher accepts any number of chunks. Calling `digest` or `digestInto`
|
|
11
|
+
* consumes the state. Call `clone` before finalizing to branch from the same
|
|
12
|
+
* prefix.
|
|
13
|
+
*/
|
|
14
|
+
export type Hasher = {
|
|
15
|
+
/** Creates an independent copy of the current state. */
|
|
16
|
+
clone(): Hasher
|
|
17
|
+
/** Destroys the state. Calling `destroy` more than once has no effect. */
|
|
18
|
+
destroy(): void
|
|
19
|
+
/** Finalizes the digest and consumes the state. */
|
|
20
|
+
digest<as extends 'Hex' | 'Bytes' = 'Hex'>(
|
|
21
|
+
options?: Hasher.DigestOptions<as>,
|
|
22
|
+
): Hasher.DigestReturnType<as>
|
|
23
|
+
/**
|
|
24
|
+
* Finalizes into the start of `output` and consumes the state.
|
|
25
|
+
*
|
|
26
|
+
* `output` may be larger than the digest. Bytes after the digest are left
|
|
27
|
+
* unchanged.
|
|
28
|
+
*/
|
|
29
|
+
digestInto(output: Bytes.Bytes): void
|
|
30
|
+
/** Absorbs a message chunk. */
|
|
31
|
+
update(value: Hex.Hex | Bytes.Bytes): Hasher
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export declare namespace Hasher {
|
|
35
|
+
/** Options for `Hasher.digest`. */
|
|
36
|
+
type DigestOptions<as extends 'Hex' | 'Bytes' = 'Hex'> = {
|
|
37
|
+
/** The return type. @default 'Hex' */
|
|
38
|
+
as?: as | 'Hex' | 'Bytes' | undefined
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Return type for `Hasher.digest`. */
|
|
42
|
+
type DigestReturnType<as extends 'Hex' | 'Bytes' = 'Hex'> =
|
|
43
|
+
| (as extends 'Bytes' ? Bytes.Bytes : never)
|
|
44
|
+
| (as extends 'Hex' ? Hex.Hex : never)
|
|
45
|
+
|
|
46
|
+
/** Errors thrown by an incremental hash state. */
|
|
47
|
+
type ErrorType =
|
|
48
|
+
| HasherDestroyedError
|
|
49
|
+
| InvalidDigestSizeError
|
|
50
|
+
| Bytes.from.ErrorType
|
|
51
|
+
| Hex.fromBytes.ErrorType
|
|
52
|
+
| Errors.GlobalErrorType
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates an incremental BLAKE3 hasher.
|
|
57
|
+
*
|
|
58
|
+
* The installed Engine provider is captured when this function is called.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts twoslash
|
|
62
|
+
* import { Hash } from 'ox'
|
|
63
|
+
*
|
|
64
|
+
* const hash = Hash.createBlake3()
|
|
65
|
+
* hash.update('0xdead')
|
|
66
|
+
* hash.update('0xbeef')
|
|
67
|
+
* hash.digest()
|
|
68
|
+
* // @log: '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd'
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @returns An incremental BLAKE3 hasher.
|
|
72
|
+
*/
|
|
73
|
+
export function createBlake3(): Hasher {
|
|
74
|
+
return fromState(engine.createBlake3(), 32)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export declare namespace createBlake3 {
|
|
78
|
+
type ErrorType = Hasher.ErrorType
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates an incremental HMAC-SHA256 hasher.
|
|
83
|
+
*
|
|
84
|
+
* The installed Engine provider is captured when this function is called.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts twoslash
|
|
88
|
+
* import { Hash, Hex } from 'ox'
|
|
89
|
+
*
|
|
90
|
+
* const hash = Hash.createHmac256(Hex.fromString('key'))
|
|
91
|
+
* hash.update('0xdead')
|
|
92
|
+
* hash.update('0xbeef')
|
|
93
|
+
* hash.digest()
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @param key - HMAC key.
|
|
97
|
+
* @returns An incremental HMAC-SHA256 hasher.
|
|
98
|
+
*/
|
|
99
|
+
export function createHmac256(key: Hex.Hex | Bytes.Bytes): Hasher {
|
|
100
|
+
return fromState(engine.createHmacSha256(Bytes.from(key)), 32)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export declare namespace createHmac256 {
|
|
104
|
+
type ErrorType = Hasher.ErrorType
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Creates an incremental Keccak256 hasher.
|
|
109
|
+
*
|
|
110
|
+
* The installed Engine provider is captured when this function is called.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts twoslash
|
|
114
|
+
* import { Hash } from 'ox'
|
|
115
|
+
*
|
|
116
|
+
* const hash = Hash.createKeccak256()
|
|
117
|
+
* hash.update('0xdead')
|
|
118
|
+
* hash.update('0xbeef')
|
|
119
|
+
* hash.digest()
|
|
120
|
+
* // @log: '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1'
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* @returns An incremental Keccak256 hasher.
|
|
124
|
+
*/
|
|
125
|
+
export function createKeccak256(): Hasher {
|
|
126
|
+
return fromState(engine.createKeccak256(), 32)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export declare namespace createKeccak256 {
|
|
130
|
+
type ErrorType = Hasher.ErrorType
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Creates an incremental RIPEMD-160 hasher.
|
|
135
|
+
*
|
|
136
|
+
* The installed Engine provider is captured when this function is called.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts twoslash
|
|
140
|
+
* import { Hash } from 'ox'
|
|
141
|
+
*
|
|
142
|
+
* const hash = Hash.createRipemd160()
|
|
143
|
+
* hash.update('0xdead')
|
|
144
|
+
* hash.update('0xbeef')
|
|
145
|
+
* hash.digest()
|
|
146
|
+
* // @log: '0x226821c2f5423e11fe9af68bd285c249db2e4b5a'
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* @returns An incremental RIPEMD-160 hasher.
|
|
150
|
+
*/
|
|
151
|
+
export function createRipemd160(): Hasher {
|
|
152
|
+
return fromState(engine.createRipemd160(), 20)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export declare namespace createRipemd160 {
|
|
156
|
+
type ErrorType = Hasher.ErrorType
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Creates an incremental SHA-256 hasher.
|
|
161
|
+
*
|
|
162
|
+
* The installed Engine provider is captured when this function is called.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts twoslash
|
|
166
|
+
* import { Hash } from 'ox'
|
|
167
|
+
*
|
|
168
|
+
* const hash = Hash.createSha256()
|
|
169
|
+
* hash.update('0xdead')
|
|
170
|
+
* hash.update('0xbeef')
|
|
171
|
+
* hash.digest()
|
|
172
|
+
* // @log: '0x5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953'
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* @returns An incremental SHA-256 hasher.
|
|
176
|
+
*/
|
|
177
|
+
export function createSha256(): Hasher {
|
|
178
|
+
return fromState(engine.createSha256(), 32)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export declare namespace createSha256 {
|
|
182
|
+
type ErrorType = Hasher.ErrorType
|
|
183
|
+
}
|
|
184
|
+
|
|
6
185
|
/**
|
|
7
186
|
* Calculates the [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) hash of a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
|
|
8
187
|
*
|
|
@@ -323,3 +502,85 @@ export declare namespace validate {
|
|
|
323
502
|
| Hex.size.ErrorType
|
|
324
503
|
| Errors.GlobalErrorType
|
|
325
504
|
}
|
|
505
|
+
|
|
506
|
+
/** Thrown when an incremental hash state has been consumed or destroyed. */
|
|
507
|
+
export class HasherDestroyedError extends Errors.BaseError {
|
|
508
|
+
override readonly name = 'Hash.HasherDestroyedError'
|
|
509
|
+
|
|
510
|
+
constructor() {
|
|
511
|
+
super('Hasher has been destroyed.')
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** Thrown when a digest output buffer is too small. */
|
|
516
|
+
export class InvalidDigestSizeError extends Errors.BaseError {
|
|
517
|
+
override readonly name = 'Hash.InvalidDigestSizeError'
|
|
518
|
+
|
|
519
|
+
constructor(options: InvalidDigestSizeError.Options) {
|
|
520
|
+
const { minimum, size } = options
|
|
521
|
+
super(
|
|
522
|
+
`Digest output is too small. Expected at least ${minimum} bytes. Received ${size} bytes.`,
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export declare namespace InvalidDigestSizeError {
|
|
528
|
+
type Options = {
|
|
529
|
+
/** Minimum output size. */
|
|
530
|
+
minimum: number
|
|
531
|
+
/** Received output size. */
|
|
532
|
+
size: number
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function fromState(state: HashState, digestSize: number): Hasher {
|
|
537
|
+
let active = true
|
|
538
|
+
|
|
539
|
+
const assertActive = () => {
|
|
540
|
+
if (!active) throw new HasherDestroyedError()
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const consume = <returnType>(fn: () => returnType): returnType => {
|
|
544
|
+
assertActive()
|
|
545
|
+
active = false
|
|
546
|
+
try {
|
|
547
|
+
return fn()
|
|
548
|
+
} finally {
|
|
549
|
+
state.destroy()
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const hasher: Hasher = {
|
|
554
|
+
clone() {
|
|
555
|
+
assertActive()
|
|
556
|
+
return fromState(state.clone(), digestSize)
|
|
557
|
+
},
|
|
558
|
+
destroy() {
|
|
559
|
+
if (!active) return
|
|
560
|
+
active = false
|
|
561
|
+
state.destroy()
|
|
562
|
+
},
|
|
563
|
+
digest(options = {}) {
|
|
564
|
+
const value = new Uint8Array(digestSize)
|
|
565
|
+
consume(() => state.digestInto(value))
|
|
566
|
+
if (options.as === 'Bytes') return value as never
|
|
567
|
+
return Hex.fromBytes(value) as never
|
|
568
|
+
},
|
|
569
|
+
digestInto(output) {
|
|
570
|
+
assertActive()
|
|
571
|
+
if (output.length < digestSize)
|
|
572
|
+
throw new InvalidDigestSizeError({
|
|
573
|
+
minimum: digestSize,
|
|
574
|
+
size: output.length,
|
|
575
|
+
})
|
|
576
|
+
consume(() => state.digestInto(output))
|
|
577
|
+
},
|
|
578
|
+
update(value) {
|
|
579
|
+
assertActive()
|
|
580
|
+
state.update(Bytes.from(value))
|
|
581
|
+
return hasher
|
|
582
|
+
},
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return hasher
|
|
586
|
+
}
|
package/src/core/Hex.ts
CHANGED
|
@@ -74,10 +74,7 @@ export function concat(...values: readonly Hex[]): Hex {
|
|
|
74
74
|
if (values.length === 1) return values[0]!
|
|
75
75
|
if (values.length === 2)
|
|
76
76
|
return `0x${(values[0] as string).slice(2)}${(values[1] as string).slice(2)}` as Hex
|
|
77
|
-
|
|
78
|
-
for (let i = 0; i < values.length; i++)
|
|
79
|
-
parts[i] = (values[i] as string).slice(2)
|
|
80
|
-
return `0x${parts.join('')}` as Hex
|
|
77
|
+
return `0x${values.reduce((hex, value) => hex + value.replace('0x', ''), '')}`
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
export declare namespace concat {
|