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
@@ -19,13 +19,47 @@
19
19
  */
20
20
 
21
21
  /**
22
- * Hash primitives. One-shot, synchronous, bytes in / bytes out.
22
+ * Mutable hash state.
23
+ *
24
+ * `digestInto` consumes the state. `destroy` is idempotent, and no other method
25
+ * may be called after the state is consumed or destroyed.
26
+ *
27
+ * @internal
28
+ */
29
+ export type HashState = {
30
+ /** Clones the current state. */
31
+ clone(): HashState
32
+ /** Destroys the state. */
33
+ destroy(): void
34
+ /**
35
+ * Finalizes into `output` and consumes the state.
36
+ *
37
+ * A short output throws without consuming the state. A longer output keeps
38
+ * bytes after the digest unchanged.
39
+ */
40
+ digestInto(output: Uint8Array): void
41
+ /** Absorbs a message chunk. */
42
+ update(input: Uint8Array): void
43
+ }
44
+
45
+ /**
46
+ * Hash primitives. Synchronous, bytes in / bytes out.
23
47
  *
24
48
  * @internal
25
49
  */
26
50
  export type Hash = {
27
51
  /** BLAKE3, 32-byte digest. */
28
52
  blake3?: ((input: Uint8Array) => Uint8Array) | undefined
53
+ /** Creates a BLAKE3 state. */
54
+ createBlake3?: (() => HashState) | undefined
55
+ /** Creates an HMAC-SHA256 state. */
56
+ createHmacSha256?: ((key: Uint8Array) => HashState) | undefined
57
+ /** Creates a Keccak256 state. */
58
+ createKeccak256?: (() => HashState) | undefined
59
+ /** Creates a RIPEMD-160 state. */
60
+ createRipemd160?: (() => HashState) | undefined
61
+ /** Creates a SHA-256 state. */
62
+ createSha256?: (() => HashState) | undefined
29
63
  /** HMAC-SHA256. */
30
64
  hmacSha256?:
31
65
  | ((key: Uint8Array, message: Uint8Array) => Uint8Array)
@@ -331,7 +365,18 @@ export const primitives = {
331
365
  'toMontgomerySecret',
332
366
  'verify',
333
367
  ],
334
- Hash: ['blake3', 'hmacSha256', 'keccak256', 'ripemd160', 'sha256'],
368
+ Hash: [
369
+ 'blake3',
370
+ 'createBlake3',
371
+ 'createHmacSha256',
372
+ 'createKeccak256',
373
+ 'createRipemd160',
374
+ 'createSha256',
375
+ 'hmacSha256',
376
+ 'keccak256',
377
+ 'ripemd160',
378
+ 'sha256',
379
+ ],
335
380
  Keystore: [
336
381
  'aesCtrDecrypt',
337
382
  'aesCtrEncrypt',
@@ -3,7 +3,13 @@ import { hmac } from '@noble/hashes/hmac.js'
3
3
  import { ripemd160 as noble_ripemd160 } from '@noble/hashes/legacy.js'
4
4
  import { sha256 as noble_sha256 } from '@noble/hashes/sha2.js'
5
5
  import { keccak_256 as noble_keccak256 } from '@noble/hashes/sha3.js'
6
- import { type Complete, type Hash, overrides } from './engine.js'
6
+ import type { Hash as NobleHash } from '@noble/hashes/utils.js'
7
+ import {
8
+ type Complete,
9
+ type Hash,
10
+ type HashState,
11
+ overrides,
12
+ } from './engine.js'
7
13
 
8
14
  /**
9
15
  * Resolvers for the `Hash` slot, and ox's defaults for it, backed by
@@ -16,6 +22,21 @@ import { type Complete, type Hash, overrides } from './engine.js'
16
22
 
17
23
  const blake3Default: Complete<Hash>['blake3'] = (input) => noble_blake3(input)
18
24
 
25
+ const createBlake3Default: Complete<Hash>['createBlake3'] = () =>
26
+ fromNoble(noble_blake3.create())
27
+
28
+ const createHmacSha256Default: Complete<Hash>['createHmacSha256'] = (key) =>
29
+ fromNoble(hmac.create(noble_sha256, key))
30
+
31
+ const createKeccak256Default: Complete<Hash>['createKeccak256'] = () =>
32
+ fromNoble(noble_keccak256.create())
33
+
34
+ const createRipemd160Default: Complete<Hash>['createRipemd160'] = () =>
35
+ fromNoble(noble_ripemd160.create())
36
+
37
+ const createSha256Default: Complete<Hash>['createSha256'] = () =>
38
+ fromNoble(noble_sha256.create())
39
+
19
40
  const hmacSha256Default: Complete<Hash>['hmacSha256'] = (key, message) =>
20
41
  hmac(noble_sha256, key, message)
21
42
 
@@ -31,6 +52,26 @@ const sha256Default: Complete<Hash>['sha256'] = (input) => noble_sha256(input)
31
52
  export const blake3: Complete<Hash>['blake3'] = (input) =>
32
53
  (overrides.Hash?.blake3 ?? blake3Default)(input)
33
54
 
55
+ /** @internal */
56
+ export const createBlake3: Complete<Hash>['createBlake3'] = () =>
57
+ (overrides.Hash?.createBlake3 ?? createBlake3Default)()
58
+
59
+ /** @internal */
60
+ export const createHmacSha256: Complete<Hash>['createHmacSha256'] = (key) =>
61
+ (overrides.Hash?.createHmacSha256 ?? createHmacSha256Default)(key)
62
+
63
+ /** @internal */
64
+ export const createKeccak256: Complete<Hash>['createKeccak256'] = () =>
65
+ (overrides.Hash?.createKeccak256 ?? createKeccak256Default)()
66
+
67
+ /** @internal */
68
+ export const createRipemd160: Complete<Hash>['createRipemd160'] = () =>
69
+ (overrides.Hash?.createRipemd160 ?? createRipemd160Default)()
70
+
71
+ /** @internal */
72
+ export const createSha256: Complete<Hash>['createSha256'] = () =>
73
+ (overrides.Hash?.createSha256 ?? createSha256Default)()
74
+
34
75
  /** @internal */
35
76
  export const hmacSha256: Complete<Hash>['hmacSha256'] = (key, message) =>
36
77
  (overrides.Hash?.hmacSha256 ?? hmacSha256Default)(key, message)
@@ -46,3 +87,14 @@ export const ripemd160: Complete<Hash>['ripemd160'] = (input) =>
46
87
  /** @internal */
47
88
  export const sha256: Complete<Hash>['sha256'] = (input) =>
48
89
  (overrides.Hash?.sha256 ?? sha256Default)(input)
90
+
91
+ function fromNoble<hash extends NobleHash<hash>>(hash: hash): HashState {
92
+ return {
93
+ clone: () => fromNoble(hash.clone()),
94
+ destroy: () => hash.destroy(),
95
+ digestInto: (output) => hash.digestInto(output),
96
+ update: (input) => {
97
+ hash.update(input)
98
+ },
99
+ }
100
+ }
package/src/index.ts CHANGED
@@ -3708,17 +3708,19 @@ export * as ValidatorData from './core/ValidatorData.js'
3708
3708
  export * as Value from './core/Value.js'
3709
3709
 
3710
3710
  /**
3711
- * Utility functions for [NIST P256](https://csrc.nist.gov/csrc/media/events/workshop-on-elliptic-curve-cryptography-standards/documents/papers/session6-adalier-mehmet.pdf) ECDSA cryptography using the [Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API)
3711
+ * Utilities for WebAuthn credentials, credential-bound PRFs, and
3712
+ * [NIST P256](https://csrc.nist.gov/csrc/media/events/workshop-on-elliptic-curve-cryptography-standards/documents/papers/session6-adalier-mehmet.pdf)
3713
+ * signatures.
3712
3714
  *
3713
3715
  * @example
3714
3716
  * ### Creating Credentials
3715
3717
  *
3716
- * Credentials can be created using {@link ox#WebAuthnP256.(createCredential:function)}:
3718
+ * Credentials can be created using {@link ox#WebAuthn.(createCredential:function)}:
3717
3719
  *
3718
3720
  * ```ts twoslash
3719
- * import { WebAuthnP256 } from 'ox'
3721
+ * import { WebAuthn } from 'ox'
3720
3722
  *
3721
- * const credential = await WebAuthnP256.createCredential({
3723
+ * const credential = await WebAuthn.createCredential({
3722
3724
  * name: 'Example'
3723
3725
  * }) // [!code focus]
3724
3726
  * // @log: {
@@ -3727,7 +3729,7 @@ export * as Value from './core/Value.js'
3727
3729
  * // @log: raw: PublicKeyCredential {},
3728
3730
  * // @log: }
3729
3731
  *
3730
- * const { metadata, signature } = await WebAuthnP256.sign({
3732
+ * const { metadata, signature } = await WebAuthn.sign({
3731
3733
  * credentialId: credential.id,
3732
3734
  * challenge: '0xdeadbeef'
3733
3735
  * })
@@ -3736,16 +3738,16 @@ export * as Value from './core/Value.js'
3736
3738
  * @example
3737
3739
  * ### Signing Payloads
3738
3740
  *
3739
- * Payloads can be signed using {@link ox#WebAuthnP256.(sign:function)}:
3741
+ * Payloads can be signed using {@link ox#WebAuthn.(sign:function)}:
3740
3742
  *
3741
3743
  * ```ts twoslash
3742
- * import { WebAuthnP256 } from 'ox'
3744
+ * import { WebAuthn } from 'ox'
3743
3745
  *
3744
- * const credential = await WebAuthnP256.createCredential({
3746
+ * const credential = await WebAuthn.createCredential({
3745
3747
  * name: 'Example'
3746
3748
  * })
3747
3749
  *
3748
- * const { metadata, signature } = await WebAuthnP256.sign({
3750
+ * const { metadata, signature } = await WebAuthn.sign({
3749
3751
  * // [!code focus]
3750
3752
  * credentialId: credential.id, // [!code focus]
3751
3753
  * challenge: '0xdeadbeef' // [!code focus]
@@ -3765,21 +3767,21 @@ export * as Value from './core/Value.js'
3765
3767
  * @example
3766
3768
  * ### Verifying Signatures
3767
3769
  *
3768
- * Signatures can be verified using {@link ox#WebAuthnP256.(verify:function)}:
3770
+ * Signatures can be verified using {@link ox#WebAuthn.(verify:function)}:
3769
3771
  *
3770
3772
  * ```ts twoslash
3771
- * import { WebAuthnP256 } from 'ox'
3773
+ * import { WebAuthn } from 'ox'
3772
3774
  *
3773
- * const credential = await WebAuthnP256.createCredential({
3775
+ * const credential = await WebAuthn.createCredential({
3774
3776
  * name: 'Example'
3775
3777
  * })
3776
3778
  *
3777
- * const { metadata, signature } = await WebAuthnP256.sign({
3779
+ * const { metadata, signature } = await WebAuthn.sign({
3778
3780
  * credentialId: credential.id,
3779
3781
  * challenge: '0xdeadbeef'
3780
3782
  * })
3781
3783
  *
3782
- * const result = await WebAuthnP256.verify({
3784
+ * const result = await WebAuthn.verify({
3783
3785
  * // [!code focus]
3784
3786
  * metadata, // [!code focus]
3785
3787
  * challenge: '0xdeadbeef', // [!code focus]
@@ -3791,6 +3793,14 @@ export * as Value from './core/Value.js'
3791
3793
  *
3792
3794
  * @category Crypto
3793
3795
  */
3796
+ export * as WebAuthn from './core/WebAuthn.js'
3797
+
3798
+ /**
3799
+ * `WebAuthnP256` is deprecated. Use `WebAuthn` instead.
3800
+ *
3801
+ * @deprecated Use `WebAuthn` instead.
3802
+ * @category Crypto
3803
+ */
3794
3804
  export * as WebAuthnP256 from './core/WebAuthnP256.js'
3795
3805
 
3796
3806
  /**
@@ -24,6 +24,7 @@ export type {
24
24
  Eddsa,
25
25
  Engine,
26
26
  Hash,
27
+ HashState,
27
28
  Keystore,
28
29
  Mnemonic,
29
30
  } from '../core/Engine.js'
package/src/node/Hash.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as crypto from 'node:crypto'
2
2
  import type * as Engine from '../core/Engine.js'
3
3
  import type * as Errors from '../core/Errors.js'
4
+ import * as CoreHash from '../core/Hash.js'
4
5
 
5
6
  export * from '../core/Hash.js'
6
7
 
@@ -12,9 +13,11 @@ export * from '../core/Hash.js'
12
13
  * which installs every implementation this entrypoint provides. Reach for this
13
14
  * to install or hold the `Hash` slot on its own.
14
15
  *
15
- * Node does not provide BLAKE3, and its `sha3-256` is not Ethereum Keccak256, so
16
- * this engine deliberately omits `blake3` and `keccak256`. Any earlier override
17
- * remains installed; otherwise Ox uses its default implementation.
16
+ * Node does not provide BLAKE3, and its `sha3-256` is not Ethereum Keccak256.
17
+ * Node HMAC states cannot be cloned.
18
+ *
19
+ * The engine omits those implementations. Earlier overrides remain installed;
20
+ * Ox uses its defaults when no override exists.
18
21
  *
19
22
  * @example
20
23
  * ```ts twoslash
@@ -31,6 +34,8 @@ export * from '../core/Hash.js'
31
34
  */
32
35
  export function engine(): Promise<engine.ReturnType> {
33
36
  return Promise.resolve({
37
+ createRipemd160: () => fromHash(crypto.createHash('ripemd160'), 20),
38
+ createSha256: () => fromHash(crypto.createHash('sha256'), 32),
34
39
  hmacSha256: (key, message) =>
35
40
  new Uint8Array(crypto.createHmac('sha256', key).update(message).digest()),
36
41
  ripemd160: (input) =>
@@ -42,10 +47,42 @@ export function engine(): Promise<engine.ReturnType> {
42
47
  export declare namespace engine {
43
48
  /** Every `Hash` primitive this module implements. */
44
49
  type ReturnType = {
45
- [key in 'hmacSha256' | 'ripemd160' | 'sha256']-?: NonNullable<
46
- Engine.Hash[key]
47
- >
50
+ [key in
51
+ | 'createRipemd160'
52
+ | 'createSha256'
53
+ | 'hmacSha256'
54
+ | 'ripemd160'
55
+ | 'sha256']-?: NonNullable<Engine.Hash[key]>
48
56
  }
49
57
 
50
58
  type ErrorType = Errors.GlobalErrorType
51
59
  }
60
+
61
+ function fromHash(initial: crypto.Hash, digestSize: number): Engine.HashState {
62
+ let hash: crypto.Hash | undefined = initial
63
+
64
+ const get = () => {
65
+ if (!hash) throw new CoreHash.HasherDestroyedError()
66
+ return hash
67
+ }
68
+
69
+ return {
70
+ clone: () => fromHash(get().copy(), digestSize),
71
+ destroy: () => {
72
+ hash = undefined
73
+ },
74
+ digestInto: (output) => {
75
+ const active = get()
76
+ if (output.length < digestSize)
77
+ throw new CoreHash.InvalidDigestSizeError({
78
+ minimum: digestSize,
79
+ size: output.length,
80
+ })
81
+ hash = undefined
82
+ output.set(active.digest())
83
+ },
84
+ update: (input) => {
85
+ get().update(input)
86
+ },
87
+ }
88
+ }
@@ -13,6 +13,7 @@ test('install and engine expose the same precise engine', () => {
13
13
  })
14
14
 
15
15
  test('the Node Engine namespace exposes synchronous core operations', () => {
16
+ expectTypeOf<Engine.HashState>().toEqualTypeOf<CoreEngine.HashState>()
16
17
  expectTypeOf(Engine.get).toEqualTypeOf(CoreEngine.get)
17
18
  expectTypeOf(Engine.InvalidSlotValueError).toEqualTypeOf(
18
19
  CoreEngine.InvalidSlotValueError,
@@ -51,6 +51,8 @@ describe('install', () => {
51
51
  "toMontgomerySecret",
52
52
  ],
53
53
  "Hash": [
54
+ "createRipemd160",
55
+ "createSha256",
54
56
  "hmacSha256",
55
57
  "ripemd160",
56
58
  "sha256",
@@ -6,6 +6,12 @@ import * as CoreHash from '../../core/Hash.js'
6
6
  type Slot = Awaited<ReturnType<typeof NodeHash.engine>>
7
7
 
8
8
  test('every implemented primitive is present', () => {
9
+ expectTypeOf<Slot['createRipemd160']>().toEqualTypeOf<
10
+ () => CoreEngine.HashState
11
+ >()
12
+ expectTypeOf<Slot['createSha256']>().toEqualTypeOf<
13
+ () => CoreEngine.HashState
14
+ >()
9
15
  expectTypeOf<Slot['hmacSha256']>().toEqualTypeOf<
10
16
  (key: Uint8Array, message: Uint8Array) => Uint8Array
11
17
  >()
@@ -19,6 +25,9 @@ test('every implemented primitive is present', () => {
19
25
 
20
26
  test('unsupported primitives are absent', () => {
21
27
  expectTypeOf<Slot>().not.toHaveProperty('blake3')
28
+ expectTypeOf<Slot>().not.toHaveProperty('createBlake3')
29
+ expectTypeOf<Slot>().not.toHaveProperty('createHmacSha256')
30
+ expectTypeOf<Slot>().not.toHaveProperty('createKeccak256')
22
31
  expectTypeOf<Slot>().not.toHaveProperty('keccak256')
23
32
  })
24
33
 
@@ -28,6 +37,7 @@ test('the result is the raw slot, so `Engine.install` accepts it', () => {
28
37
 
29
38
  test('the Node namespace exposes the public Hash API', () => {
30
39
  expectTypeOf(NodeHash.blake3).toEqualTypeOf(CoreHash.blake3)
40
+ expectTypeOf(NodeHash.createSha256).toEqualTypeOf(CoreHash.createSha256)
31
41
  expectTypeOf(NodeHash.sha256).toEqualTypeOf(CoreHash.sha256)
32
42
  expectTypeOf<typeof NodeHash>().not.toHaveProperty('create')
33
43
  })
@@ -8,6 +8,8 @@ describe('engine', () => {
8
8
 
9
9
  expect(Object.keys(engine).sort()).toMatchInlineSnapshot(`
10
10
  [
11
+ "createRipemd160",
12
+ "createSha256",
11
13
  "hmacSha256",
12
14
  "ripemd160",
13
15
  "sha256",
@@ -98,4 +100,48 @@ describe('engine', () => {
98
100
  `)
99
101
  expect(sha256(input) === sha256(input)).toMatchInlineSnapshot('false')
100
102
  })
103
+
104
+ test('behavior: incremental states support chunking and cloning', async () => {
105
+ const { createRipemd160, createSha256 } = await Hash.engine()
106
+
107
+ for (const [create, digest, size] of [
108
+ [createRipemd160, CoreHash.ripemd160, 20],
109
+ [createSha256, CoreHash.sha256, 32],
110
+ ] as const) {
111
+ const first = create()
112
+ first.update(Uint8Array.of(0xde, 0xad))
113
+ const second = first.clone()
114
+ first.update(Uint8Array.of(0xbe, 0xef))
115
+ second.update(Uint8Array.of(0xca, 0xfe))
116
+
117
+ const firstOutput = new Uint8Array(size)
118
+ const secondOutput = new Uint8Array(size)
119
+ first.digestInto(firstOutput)
120
+ second.digestInto(secondOutput)
121
+
122
+ expect(firstOutput).toEqual(digest('0xdeadbeef', { as: 'Bytes' }))
123
+ expect(secondOutput).toEqual(digest('0xdeadcafe', { as: 'Bytes' }))
124
+ }
125
+ })
126
+
127
+ test('behavior: incremental states enforce lifecycle and output bounds', async () => {
128
+ const { createSha256 } = await Hash.engine()
129
+ const hash = createSha256()
130
+ hash.update(Uint8Array.of(0xde, 0xad))
131
+
132
+ expect(() => hash.digestInto(new Uint8Array(31))).toThrowError(
133
+ Hash.InvalidDigestSizeError,
134
+ )
135
+
136
+ const output = new Uint8Array(34).fill(0xff)
137
+ hash.digestInto(output)
138
+ expect(output.slice(32)).toEqual(Uint8Array.of(0xff, 0xff))
139
+ expect(() => hash.update(new Uint8Array())).toThrowError(
140
+ Hash.HasherDestroyedError,
141
+ )
142
+ expect(() => {
143
+ hash.destroy()
144
+ hash.destroy()
145
+ }).not.toThrow()
146
+ })
101
147
  })
@@ -219,13 +219,13 @@ export type TupleListSigned = TupleList<true>
219
219
  *
220
220
  * ```ts twoslash
221
221
  * // @noErrors
222
- * import { WebAuthnP256 } from 'ox'
222
+ * import { WebAuthn } from 'ox'
223
223
  * import {
224
224
  * AuthorizationTempo,
225
225
  * SignatureEnvelope
226
226
  * } from 'ox/tempo'
227
227
  *
228
- * const credential = await WebAuthnP256.createCredential({
228
+ * const credential = await WebAuthn.createCredential({
229
229
  * name: 'Example'
230
230
  * })
231
231
  *
@@ -235,7 +235,7 @@ export type TupleListSigned = TupleList<true>
235
235
  * nonce: 40n
236
236
  * })
237
237
  *
238
- * const { metadata, signature } = await WebAuthnP256.sign({
238
+ * const { metadata, signature } = await WebAuthn.sign({
239
239
  * challenge:
240
240
  * AuthorizationTempo.getSignPayload(authorization),
241
241
  * credentialId: credential.id
@@ -657,13 +657,13 @@ export declare namespace fromTupleList {
657
657
  *
658
658
  * ```ts twoslash
659
659
  * // @noErrors
660
- * import { WebAuthnP256 } from 'ox'
660
+ * import { WebAuthn } from 'ox'
661
661
  * import {
662
662
  * AuthorizationTempo,
663
663
  * SignatureEnvelope
664
664
  * } from 'ox/tempo'
665
665
  *
666
- * const credential = await WebAuthnP256.createCredential({
666
+ * const credential = await WebAuthn.createCredential({
667
667
  * name: 'Example'
668
668
  * })
669
669
  *
@@ -676,7 +676,7 @@ export declare namespace fromTupleList {
676
676
  * const challenge =
677
677
  * AuthorizationTempo.getSignPayload(authorization) // [!code focus]
678
678
  *
679
- * const { metadata, signature } = await WebAuthnP256.sign({
679
+ * const { metadata, signature } = await WebAuthn.sign({
680
680
  * challenge,
681
681
  * credentialId: credential.id
682
682
  * })
@@ -366,12 +366,7 @@ export type TokenLimit<bigintType = bigint, numberType = number> = {
366
366
  *
367
367
  * ```ts twoslash
368
368
  * // @noErrors
369
- * import {
370
- * Address,
371
- * Value,
372
- * WebCryptoP256,
373
- * WebAuthnP256
374
- * } from 'ox'
369
+ * import { Address, Value, WebCryptoP256, WebAuthn } from 'ox'
375
370
  * import {
376
371
  * KeyAuthorization,
377
372
  * SignatureEnvelope
@@ -393,11 +388,11 @@ export type TokenLimit<bigintType = bigint, numberType = number> = {
393
388
  * ]
394
389
  * })
395
390
  *
396
- * const credential = await WebAuthnP256.createCredential({
391
+ * const credential = await WebAuthn.createCredential({
397
392
  * name: 'Example'
398
393
  * })
399
394
  *
400
- * const { metadata, signature } = await WebAuthnP256.sign({
395
+ * const { metadata, signature } = await WebAuthn.sign({
401
396
  * challenge: KeyAuthorization.getSignPayload(authorization),
402
397
  * credentialId: credential.id
403
398
  * })
@@ -814,7 +814,7 @@ function deserialize_(
814
814
  *
815
815
  * Accepts either a serialized hex string or an existing signature envelope object.
816
816
  * Use this to wrap raw signatures from {@link ox#Secp256k1.(sign:function)}, {@link ox#P256.(sign:function)},
817
- * {@link ox#WebCryptoP256.(sign:function)}, or {@link ox#WebAuthnP256.(sign:function)} into the envelope format
817
+ * {@link ox#WebCryptoP256.(sign:function)}, or {@link ox#WebAuthn.(sign:function)} into the envelope format
818
818
  * required by Tempo transactions.
819
819
  *
820
820
  * [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
@@ -893,14 +893,14 @@ function deserialize_(
893
893
  *
894
894
  * ```ts twoslash
895
895
  * // @noErrors
896
- * import { WebAuthnP256 } from 'ox'
896
+ * import { WebAuthn } from 'ox'
897
897
  * import { SignatureEnvelope } from 'ox/tempo'
898
898
  *
899
- * const credential = await WebAuthnP256.createCredential({
899
+ * const credential = await WebAuthn.createCredential({
900
900
  * name: 'Example'
901
901
  * })
902
902
  *
903
- * const { metadata, signature } = await WebAuthnP256.sign({
903
+ * const { metadata, signature } = await WebAuthn.sign({
904
904
  * challenge: '0xdeadbeef',
905
905
  * credentialId: credential.id
906
906
  * })
@@ -1819,18 +1819,18 @@ export declare namespace validate {
1819
1819
  * ```
1820
1820
  *
1821
1821
  * @example
1822
- * ### WebAuthnP256
1822
+ * ### WebAuthn
1823
1823
  *
1824
1824
  * ```ts twoslash
1825
1825
  * import { SignatureEnvelope } from 'ox/tempo'
1826
- * import { WebAuthnP256 } from 'ox'
1826
+ * import { WebAuthn } from 'ox'
1827
1827
  *
1828
- * const credential = await WebAuthnP256.createCredential({
1828
+ * const credential = await WebAuthn.createCredential({
1829
1829
  * name: 'Example'
1830
1830
  * })
1831
1831
  * const payload = '0xdeadbeef'
1832
1832
  *
1833
- * const { metadata, signature } = await WebAuthnP256.sign({
1833
+ * const { metadata, signature } = await WebAuthn.sign({
1834
1834
  * challenge: payload,
1835
1835
  * credentialId: credential.id
1836
1836
  * })
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = '1.2.0'
2
+ export const version = '1.3.0'
@@ -24,6 +24,7 @@ export type {
24
24
  Eddsa,
25
25
  Engine,
26
26
  Hash,
27
+ HashState,
27
28
  Keystore,
28
29
  Mnemonic,
29
30
  } from '../core/Engine.js'
package/src/wasm/Hash.ts CHANGED
@@ -65,6 +65,11 @@ export async function engine(): Promise<engine.ReturnType> {
65
65
 
66
66
  return {
67
67
  blake3: (input) => blake3.hash(blake3Module, input),
68
+ createBlake3: () => blake3.create(blake3Module),
69
+ createHmacSha256: (key) => hash.createHmacSha256(module, key),
70
+ createKeccak256: () => hashes.create(module, 'keccak256'),
71
+ createRipemd160: () => hashes.create(module, 'ripemd160'),
72
+ createSha256: () => hashes.create(module, 'sha256'),
68
73
  hmacSha256: (key, message) => hash.hmacSha256(module, key, message),
69
74
  keccak256: (input) => call('keccak256', input),
70
75
  ripemd160: (input) => call('ripemd160', input),
@@ -78,6 +83,11 @@ export declare namespace engine {
78
83
  type ReturnType = {
79
84
  [key in
80
85
  | 'blake3'
86
+ | 'createBlake3'
87
+ | 'createHmacSha256'
88
+ | 'createKeccak256'
89
+ | 'createRipemd160'
90
+ | 'createSha256'
81
91
  | 'hmacSha256'
82
92
  | 'keccak256'
83
93
  | 'ripemd160'
@@ -14,6 +14,7 @@ test('install and engine expose the same precise engine', () => {
14
14
  })
15
15
 
16
16
  test('the WASM Engine namespace exposes synchronous core operations', () => {
17
+ expectTypeOf<Engine.HashState>().toEqualTypeOf<CoreEngine.HashState>()
17
18
  expectTypeOf(Engine.get).toEqualTypeOf(CoreEngine.get)
18
19
  expectTypeOf(Engine.InvalidSlotValueError).toEqualTypeOf(
19
20
  CoreEngine.InvalidSlotValueError,
@@ -75,6 +75,11 @@ describe('install', () => {
75
75
  ],
76
76
  "Hash": [
77
77
  "blake3",
78
+ "createBlake3",
79
+ "createHmacSha256",
80
+ "createKeccak256",
81
+ "createRipemd160",
82
+ "createSha256",
78
83
  "hmacSha256",
79
84
  "keccak256",
80
85
  "ripemd160",
@@ -27,6 +27,28 @@ describe('engine', () => {
27
27
  }
28
28
  })
29
29
 
30
+ test('behavior: incremental states clone and cross block boundaries', async () => {
31
+ const engine = await WasmHash.engine()
32
+ const prefix = new Uint8Array(137).fill(1)
33
+ const left = new Uint8Array(1025).fill(2)
34
+ const right = new Uint8Array(65).fill(3)
35
+ const state = engine.createBlake3()
36
+ state.update(prefix.subarray(0, 1))
37
+ state.update(prefix.subarray(1, 136))
38
+ state.update(prefix.subarray(136))
39
+ const clone = state.clone()
40
+ state.update(left)
41
+ clone.update(right)
42
+
43
+ const output = new Uint8Array(32)
44
+ state.digestInto(output)
45
+ expect(output).toEqual(blake3(new Uint8Array([...prefix, ...left])))
46
+
47
+ const clonedOutput = new Uint8Array(32)
48
+ clone.digestInto(clonedOutput)
49
+ expect(clonedOutput).toEqual(blake3(new Uint8Array([...prefix, ...right])))
50
+ })
51
+
30
52
  test('behavior: grows memory in a real browser, then hashes a small input', async () => {
31
53
  const engine = await WasmHash.engine()
32
54
 
@@ -50,6 +72,12 @@ describe('engine', () => {
50
72
  expect(Hash.keccak256('0xdeadbeef')).toBe(
51
73
  '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
52
74
  )
75
+ const state = Hash.createKeccak256()
76
+ state.update('0xdead')
77
+ state.update('0xbeef')
78
+ expect(state.digest()).toBe(
79
+ '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
80
+ )
53
81
  } finally {
54
82
  Engine.reset()
55
83
  }