ox 1.0.5 → 1.1.2
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 +77 -0
- package/dist/core/Base64.d.ts +10 -21
- package/dist/core/Base64.d.ts.map +1 -1
- package/dist/core/Base64.js +8 -127
- package/dist/core/Base64.js.map +1 -1
- package/dist/core/BinaryStateTree.d.ts.map +1 -1
- package/dist/core/BinaryStateTree.js +2 -2
- package/dist/core/BinaryStateTree.js.map +1 -1
- package/dist/core/Bls.d.ts.map +1 -1
- package/dist/core/Bls.js +19 -44
- package/dist/core/Bls.js.map +1 -1
- package/dist/core/Ed25519.d.ts.map +1 -1
- package/dist/core/Ed25519.js +7 -6
- package/dist/core/Ed25519.js.map +1 -1
- package/dist/core/Engine.d.ts +156 -0
- package/dist/core/Engine.d.ts.map +1 -0
- package/dist/core/Engine.js +210 -0
- package/dist/core/Engine.js.map +1 -0
- package/dist/core/Hash.d.ts +4 -4
- package/dist/core/Hash.d.ts.map +1 -1
- package/dist/core/Hash.js +9 -12
- package/dist/core/Hash.js.map +1 -1
- package/dist/core/Keystore.d.ts.map +1 -1
- package/dist/core/Keystore.js +15 -10
- package/dist/core/Keystore.js.map +1 -1
- package/dist/core/Mnemonic.d.ts.map +1 -1
- package/dist/core/Mnemonic.js +3 -2
- package/dist/core/Mnemonic.js.map +1 -1
- package/dist/core/P256.d.ts.map +1 -1
- package/dist/core/P256.js +8 -9
- package/dist/core/P256.js.map +1 -1
- package/dist/core/Secp256k1.d.ts.map +1 -1
- package/dist/core/Secp256k1.js +8 -9
- package/dist/core/Secp256k1.js.map +1 -1
- package/dist/core/WebCryptoP256.d.ts.map +1 -1
- package/dist/core/WebCryptoP256.js +2 -2
- package/dist/core/WebCryptoP256.js.map +1 -1
- package/dist/core/X25519.d.ts.map +1 -1
- package/dist/core/X25519.js +4 -3
- package/dist/core/X25519.js.map +1 -1
- package/dist/core/internal/bls.d.ts +12 -0
- package/dist/core/internal/bls.d.ts.map +1 -0
- package/dist/core/internal/bls.js +79 -0
- package/dist/core/internal/bls.js.map +1 -0
- package/dist/core/internal/codec/base64.d.ts +84 -0
- package/dist/core/internal/codec/base64.d.ts.map +1 -0
- package/dist/core/internal/codec/base64.js +177 -0
- package/dist/core/internal/codec/base64.js.map +1 -0
- package/dist/core/internal/codec/hex.d.ts +49 -4
- package/dist/core/internal/codec/hex.d.ts.map +1 -1
- package/dist/core/internal/codec/hex.js +109 -35
- package/dist/core/internal/codec/hex.js.map +1 -1
- package/dist/core/internal/curves.d.ts +17 -0
- package/dist/core/internal/curves.d.ts.map +1 -0
- package/dist/core/internal/curves.js +17 -0
- package/dist/core/internal/curves.js.map +1 -0
- package/dist/core/internal/ed25519.d.ts +14 -0
- package/dist/core/internal/ed25519.d.ts.map +1 -0
- package/dist/core/internal/ed25519.js +29 -0
- package/dist/core/internal/ed25519.js.map +1 -0
- package/dist/core/internal/engine.d.ts +278 -0
- package/dist/core/internal/engine.d.ts.map +1 -0
- package/dist/core/internal/engine.js +131 -0
- package/dist/core/internal/engine.js.map +1 -0
- package/dist/core/internal/hash.d.ts +12 -0
- package/dist/core/internal/hash.d.ts.map +1 -0
- package/dist/core/internal/hash.js +30 -0
- package/dist/core/internal/hash.js.map +1 -0
- package/dist/core/internal/keystore.d.ts +14 -0
- package/dist/core/internal/keystore.d.ts.map +1 -0
- package/dist/core/internal/keystore.js +32 -0
- package/dist/core/internal/keystore.js.map +1 -0
- package/dist/core/internal/mnemonic.d.ts +4 -0
- package/dist/core/internal/mnemonic.d.ts.map +1 -0
- package/dist/core/internal/mnemonic.js +14 -0
- package/dist/core/internal/mnemonic.js.map +1 -0
- package/dist/core/internal/p256.d.ts +14 -0
- package/dist/core/internal/p256.d.ts.map +1 -0
- package/dist/core/internal/p256.js +36 -0
- package/dist/core/internal/p256.js.map +1 -0
- package/dist/core/internal/secp256k1.d.ts +14 -0
- package/dist/core/internal/secp256k1.d.ts.map +1 -0
- package/dist/core/internal/secp256k1.js +36 -0
- package/dist/core/internal/secp256k1.js.map +1 -0
- package/dist/core/internal/x25519.d.ts +8 -0
- package/dist/core/internal/x25519.d.ts.map +1 -0
- package/dist/core/internal/x25519.js +20 -0
- package/dist/core/internal/x25519.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.docs.d.ts +2 -0
- package/dist/index.docs.d.ts.map +1 -1
- package/dist/index.docs.js +2 -0
- package/dist/index.docs.js.map +1 -1
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -1
- package/dist/node/Ed25519.d.ts +33 -0
- package/dist/node/Ed25519.d.ts.map +1 -0
- package/dist/node/Ed25519.js +67 -0
- package/dist/node/Ed25519.js.map +1 -0
- package/dist/node/Engine.d.ts +68 -0
- package/dist/node/Engine.d.ts.map +1 -0
- package/dist/node/Engine.js +75 -0
- package/dist/node/Engine.js.map +1 -0
- package/dist/node/Hash.d.ts +37 -0
- package/dist/node/Hash.d.ts.map +1 -0
- package/dist/node/Hash.js +35 -0
- package/dist/node/Hash.js.map +1 -0
- package/dist/node/Keystore.d.ts +36 -0
- package/dist/node/Keystore.d.ts.map +1 -0
- package/dist/node/Keystore.js +72 -0
- package/dist/node/Keystore.js.map +1 -0
- package/dist/node/Mnemonic.d.ts +35 -0
- package/dist/node/Mnemonic.d.ts.map +1 -0
- package/dist/node/Mnemonic.js +46 -0
- package/dist/node/Mnemonic.js.map +1 -0
- package/dist/node/P256.d.ts +32 -0
- package/dist/node/P256.d.ts.map +1 -0
- package/dist/node/P256.js +34 -0
- package/dist/node/P256.js.map +1 -0
- package/dist/node/X25519.d.ts +31 -0
- package/dist/node/X25519.d.ts.map +1 -0
- package/dist/node/X25519.js +71 -0
- package/dist/node/X25519.js.map +1 -0
- package/dist/node/index.d.ts +126 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +126 -0
- package/dist/node/index.js.map +1 -0
- package/dist/tempo/VirtualMaster.d.ts +2 -0
- package/dist/tempo/VirtualMaster.d.ts.map +1 -1
- package/dist/tempo/VirtualMaster.js +19 -5
- package/dist/tempo/VirtualMaster.js.map +1 -1
- package/dist/tempo/internal/mine.wasm.d.ts +2 -4
- package/dist/tempo/internal/mine.wasm.d.ts.map +1 -1
- package/dist/tempo/internal/mine.wasm.js +7 -12
- package/dist/tempo/internal/mine.wasm.js.map +1 -1
- package/dist/tempo/internal/virtualMasterPool.js +4 -1
- package/dist/tempo/internal/virtualMasterPool.js.map +1 -1
- package/dist/wasm/Ed25519.d.ts +35 -0
- package/dist/wasm/Ed25519.d.ts.map +1 -0
- package/dist/wasm/Ed25519.js +88 -0
- package/dist/wasm/Ed25519.js.map +1 -0
- package/dist/wasm/Engine.d.ts +71 -0
- package/dist/wasm/Engine.d.ts.map +1 -0
- package/dist/wasm/Engine.js +76 -0
- package/dist/wasm/Engine.js.map +1 -0
- package/dist/wasm/Hash.d.ts +43 -0
- package/dist/wasm/Hash.d.ts.map +1 -0
- package/dist/wasm/Hash.js +64 -0
- package/dist/wasm/Hash.js.map +1 -0
- package/dist/wasm/Keystore.d.ts +37 -0
- package/dist/wasm/Keystore.d.ts.map +1 -0
- package/dist/wasm/Keystore.js +34 -0
- package/dist/wasm/Keystore.js.map +1 -0
- package/dist/wasm/Mnemonic.d.ts +33 -0
- package/dist/wasm/Mnemonic.d.ts.map +1 -0
- package/dist/wasm/Mnemonic.js +30 -0
- package/dist/wasm/Mnemonic.js.map +1 -0
- package/dist/wasm/X25519.d.ts +33 -0
- package/dist/wasm/X25519.d.ts.map +1 -0
- package/dist/wasm/X25519.js +53 -0
- package/dist/wasm/X25519.js.map +1 -0
- package/dist/wasm/index.d.ts +115 -0
- package/dist/wasm/index.d.ts.map +1 -0
- package/dist/wasm/index.js +115 -0
- package/dist/wasm/index.js.map +1 -0
- package/dist/wasm/internal/blake3.d.ts +11 -0
- package/dist/wasm/internal/blake3.d.ts.map +1 -0
- package/dist/wasm/internal/blake3.js +31 -0
- package/dist/wasm/internal/blake3.js.map +1 -0
- package/dist/wasm/internal/blake3.wasm.d.ts +3 -0
- package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/blake3.wasm.js +10 -0
- package/dist/wasm/internal/blake3.wasm.js.map +1 -0
- package/dist/wasm/internal/crypto25519.d.ts +16 -0
- package/dist/wasm/internal/crypto25519.d.ts.map +1 -0
- package/dist/wasm/internal/crypto25519.js +5 -0
- package/dist/wasm/internal/crypto25519.js.map +1 -0
- package/dist/wasm/internal/crypto25519.wasm.d.ts +3 -0
- package/dist/wasm/internal/crypto25519.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/crypto25519.wasm.js +10 -0
- package/dist/wasm/internal/crypto25519.wasm.js.map +1 -0
- package/dist/wasm/internal/ed25519.d.ts +4 -0
- package/dist/wasm/internal/ed25519.d.ts.map +1 -0
- package/dist/wasm/internal/ed25519.js +21 -0
- package/dist/wasm/internal/ed25519.js.map +1 -0
- package/dist/wasm/internal/hash.d.ts +9 -0
- package/dist/wasm/internal/hash.d.ts.map +1 -0
- package/dist/wasm/internal/hash.js +26 -0
- package/dist/wasm/internal/hash.js.map +1 -0
- package/dist/wasm/internal/hashes.d.ts +12 -0
- package/dist/wasm/internal/hashes.d.ts.map +1 -0
- package/dist/wasm/internal/hashes.js +8 -0
- package/dist/wasm/internal/hashes.js.map +1 -0
- package/dist/wasm/internal/hashes.wasm.d.ts +7 -0
- package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/hashes.wasm.js +14 -0
- package/dist/wasm/internal/hashes.wasm.js.map +1 -0
- package/dist/wasm/internal/instantiate.d.ts +56 -0
- package/dist/wasm/internal/instantiate.d.ts.map +1 -0
- package/dist/wasm/internal/instantiate.js +92 -0
- package/dist/wasm/internal/instantiate.js.map +1 -0
- package/dist/wasm/internal/mnemonic.d.ts +4 -0
- package/dist/wasm/internal/mnemonic.d.ts.map +1 -0
- package/dist/wasm/internal/mnemonic.js +45 -0
- package/dist/wasm/internal/mnemonic.js.map +1 -0
- package/dist/wasm/internal/pbkdf2.d.ts +12 -0
- package/dist/wasm/internal/pbkdf2.d.ts.map +1 -0
- package/dist/wasm/internal/pbkdf2.js +51 -0
- package/dist/wasm/internal/pbkdf2.js.map +1 -0
- package/dist/wasm/internal/x25519.d.ts +4 -0
- package/dist/wasm/internal/x25519.d.ts.map +1 -0
- package/dist/wasm/internal/x25519.js +21 -0
- package/dist/wasm/internal/x25519.js.map +1 -0
- package/dist/zod/TypedData.d.ts +12 -12
- package/package.json +81 -1
- package/src/core/Base64.ts +19 -166
- package/src/core/BinaryStateTree.ts +2 -3
- package/src/core/Bls.ts +27 -77
- package/src/core/Ed25519.ts +7 -7
- package/src/core/Engine.ts +308 -0
- package/src/core/Hash.ts +9 -12
- package/src/core/Keystore.ts +17 -17
- package/src/core/Mnemonic.ts +3 -6
- package/src/core/P256.ts +15 -24
- package/src/core/Rlp_tx.bench.ts +2 -4
- package/src/core/Secp256k1.ts +9 -14
- package/src/core/TxEnvelopeEip1559.bench.ts +0 -5
- package/src/core/WebCryptoP256.ts +2 -2
- package/src/core/X25519.ts +4 -3
- package/src/core/_test/AbiParameters.adversarial.fuzz.ts +1 -2
- package/src/core/_test/AbiParameters.fuzz.ts +1 -2
- package/src/core/_test/Base.fuzz.ts +1 -2
- package/src/core/_test/Cbor.fuzz.ts +1 -2
- package/src/core/_test/Engine.fuzz.ts +293 -0
- package/src/core/_test/Engine.test-d.ts +140 -0
- package/src/core/_test/Engine.test.ts +618 -0
- package/src/core/_test/Hash.vectors.test.ts +64 -0
- package/src/core/_test/Hex.fuzz.ts +1 -2
- package/src/core/_test/Rlp.fuzz.ts +1 -2
- package/src/core/_test/Signature.fuzz.ts +1 -2
- package/src/core/_test/curves.test.ts +7 -0
- package/src/core/_test/index.test.ts +1 -0
- package/src/core/internal/bls.ts +121 -0
- package/src/core/internal/codec/_test/base64.conformance.ts +156 -0
- package/src/core/internal/codec/_test/hex.conformance.ts +204 -0
- package/src/core/internal/codec/_test/hex.fuzz.ts +234 -0
- package/src/core/internal/codec/_test/hex.test.ts +124 -0
- package/src/core/internal/codec/base64.ts +222 -0
- package/src/core/internal/codec/hex.ts +115 -34
- package/src/core/internal/curves.ts +18 -0
- package/src/core/internal/ed25519.ts +64 -0
- package/src/core/internal/engine.ts +410 -0
- package/src/core/internal/hash.ts +48 -0
- package/src/core/internal/keystore.ts +107 -0
- package/src/core/internal/mnemonic.ts +18 -0
- package/src/core/internal/p256.ts +91 -0
- package/src/core/internal/secp256k1.ts +91 -0
- package/src/core/internal/x25519.ts +40 -0
- package/src/index.docs.ts +2 -0
- package/src/index.ts +39 -0
- package/src/node/Ed25519.ts +87 -0
- package/src/node/Engine.ts +125 -0
- package/src/node/Hash.ts +51 -0
- package/src/node/Keystore.ts +103 -0
- package/src/node/Mnemonic.ts +68 -0
- package/src/node/P256.ts +48 -0
- package/src/node/X25519.ts +93 -0
- package/src/node/_test/Curves.fuzz.ts +76 -0
- package/src/node/_test/Ed25519.test-d.ts +35 -0
- package/src/node/_test/Ed25519.test.ts +220 -0
- package/src/node/_test/Engine.test-d.ts +28 -0
- package/src/node/_test/Engine.test.ts +102 -0
- package/src/node/_test/Hash.test-d.ts +32 -0
- package/src/node/_test/Hash.test.ts +101 -0
- package/src/node/_test/Keystore.fuzz.ts +58 -0
- package/src/node/_test/Keystore.test-d.ts +43 -0
- package/src/node/_test/Keystore.test.ts +198 -0
- package/src/node/_test/Mnemonic.fuzz.ts +31 -0
- package/src/node/_test/Mnemonic.test-d.ts +21 -0
- package/src/node/_test/Mnemonic.test.ts +98 -0
- package/src/node/_test/P256.test-d.ts +29 -0
- package/src/node/_test/P256.test.ts +102 -0
- package/src/node/_test/X25519.test-d.ts +28 -0
- package/src/node/_test/X25519.test.ts +239 -0
- package/src/node/index.ts +132 -0
- package/src/tempo/VirtualMaster.test.ts +75 -0
- package/src/tempo/VirtualMaster.ts +28 -6
- package/src/tempo/internal/mine.wasm.ts +7 -13
- package/src/tempo/internal/virtualMasterPool.ts +4 -1
- package/src/tsconfig.json +2 -1
- package/src/version.ts +1 -1
- package/src/wasm/Ed25519.ts +114 -0
- package/src/wasm/Engine.ts +121 -0
- package/src/wasm/Hash.bench.ts +60 -0
- package/src/wasm/Hash.ts +88 -0
- package/src/wasm/Keystore.ts +48 -0
- package/src/wasm/Mnemonic.ts +44 -0
- package/src/wasm/X25519.ts +70 -0
- package/src/wasm/_test/Crypto25519.conformance.ts +57 -0
- package/src/wasm/_test/Crypto25519.fuzz.ts +187 -0
- package/src/wasm/_test/Ed25519.test-d.ts +28 -0
- package/src/wasm/_test/Ed25519.test.ts +168 -0
- package/src/wasm/_test/Ed25519.vectors.test.ts +73 -0
- package/src/wasm/_test/Engine.test-d.ts +28 -0
- package/src/wasm/_test/Engine.test.ts +96 -0
- package/src/wasm/_test/Hash.browser.test.ts +54 -0
- package/src/wasm/_test/Hash.fuzz.ts +223 -0
- package/src/wasm/_test/Hash.test-d.ts +33 -0
- package/src/wasm/_test/Hash.test.ts +311 -0
- package/src/wasm/_test/Hash.vectors.test.ts +124 -0
- package/src/wasm/_test/Keystore.browser.test.ts +42 -0
- package/src/wasm/_test/Keystore.fuzz.ts +113 -0
- package/src/wasm/_test/Keystore.test-d.ts +33 -0
- package/src/wasm/_test/Keystore.test.ts +192 -0
- package/src/wasm/_test/Keystore.vectors.test.ts +21 -0
- package/src/wasm/_test/Mnemonic.test-d.ts +17 -0
- package/src/wasm/_test/Mnemonic.test.ts +110 -0
- package/src/wasm/_test/Runtime.test.ts +21 -0
- package/src/wasm/_test/X25519.test-d.ts +21 -0
- package/src/wasm/_test/X25519.test.ts +194 -0
- package/src/wasm/_test/fixtures.ts +4 -0
- package/src/wasm/_test/runtime.wasm.ts +11 -0
- package/src/wasm/index.ts +120 -0
- package/src/wasm/internal/blake3.ts +45 -0
- package/src/wasm/internal/blake3.wasm.ts +11 -0
- package/src/wasm/internal/crypto25519.ts +43 -0
- package/src/wasm/internal/crypto25519.wasm.ts +11 -0
- package/src/wasm/internal/ed25519.ts +31 -0
- package/src/wasm/internal/hash.ts +50 -0
- package/src/wasm/internal/hashes.ts +29 -0
- package/src/wasm/internal/hashes.wasm.ts +17 -0
- package/src/wasm/internal/instantiate.ts +135 -0
- package/src/wasm/internal/mnemonic.ts +56 -0
- package/src/wasm/internal/pbkdf2.ts +86 -0
- package/src/wasm/internal/x25519.ts +29 -0
- package/src/tempo/internal/mine.c +0 -182
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { blake3 } from '@noble/hashes/blake3.js'
|
|
2
|
+
import { hmac } from '@noble/hashes/hmac.js'
|
|
3
|
+
import { ripemd160 } from '@noble/hashes/legacy.js'
|
|
4
|
+
import { sha256 } from '@noble/hashes/sha2.js'
|
|
5
|
+
import { keccak_256 } from '@noble/hashes/sha3.js'
|
|
6
|
+
import { Bytes, Engine, Hash } from 'ox'
|
|
7
|
+
import { Hash as WasmHash } from 'ox/wasm'
|
|
8
|
+
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
9
|
+
import { wasmBase64 as blake3WasmBase64 } from '../internal/blake3.wasm.js'
|
|
10
|
+
import * as blake3_internal from '../internal/blake3.js'
|
|
11
|
+
import { hmacSha256ScratchSize, wasmBase64 } from '../internal/hashes.wasm.js'
|
|
12
|
+
import * as hash from '../internal/hash.js'
|
|
13
|
+
import * as internal from '../internal/instantiate.js'
|
|
14
|
+
|
|
15
|
+
let engine: WasmHash.engine.ReturnType
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
engine = await WasmHash.engine()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('engine', () => {
|
|
22
|
+
test('default', async () => {
|
|
23
|
+
expect(Object.keys(await WasmHash.engine())).toMatchInlineSnapshot(`
|
|
24
|
+
[
|
|
25
|
+
"blake3",
|
|
26
|
+
"hmacSha256",
|
|
27
|
+
"keccak256",
|
|
28
|
+
"ripemd160",
|
|
29
|
+
"sha256",
|
|
30
|
+
]
|
|
31
|
+
`)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('behavior: hands every caller its own engine', async () => {
|
|
35
|
+
const [a, b] = await Promise.all([WasmHash.engine(), WasmHash.engine()])
|
|
36
|
+
|
|
37
|
+
// Deliberately not the same object. Sharing the slot object would let one
|
|
38
|
+
// caller's customisation leak into a later `engine` or `Engine.install`.
|
|
39
|
+
expect(a).not.toBe(b)
|
|
40
|
+
|
|
41
|
+
a.keccak256 = () => new Uint8Array(32).fill(9)
|
|
42
|
+
const c = await WasmHash.engine()
|
|
43
|
+
expect(c.keccak256(Bytes.from('0xdeadbeef'))).toEqual(
|
|
44
|
+
b.keccak256(Bytes.from('0xdeadbeef')),
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Sizes chosen around the block boundaries where padding bugs live: keccak256's
|
|
50
|
+
// rate is 136 bytes, sha256 and ripemd160 use 64-byte blocks and need a second
|
|
51
|
+
// block once the remainder reaches 56, and BLAKE3 uses 1024-byte chunks.
|
|
52
|
+
const sizes = [
|
|
53
|
+
0, 1, 2, 31, 32, 55, 56, 63, 64, 65, 71, 72, 111, 135, 136, 137, 199, 200,
|
|
54
|
+
271, 272, 1000, 1023, 1024, 1025, 2048, 2049,
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
function input(size: number) {
|
|
58
|
+
return new Uint8Array(size).map((_, index) => (index * 37 + 11) & 0xff)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe('Hash', () => {
|
|
62
|
+
test.each(sizes)('blake3 matches the default at %i bytes', (size) => {
|
|
63
|
+
expect(engine.blake3(input(size))).toEqual(blake3(input(size)))
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test.each(sizes)('keccak256 matches the default at %i bytes', (size) => {
|
|
67
|
+
expect(engine.keccak256(input(size))).toEqual(keccak_256(input(size)))
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test.each(sizes)('sha256 matches the default at %i bytes', (size) => {
|
|
71
|
+
expect(engine.sha256(input(size))).toEqual(sha256(input(size)))
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test.each(sizes)('ripemd160 matches the default at %i bytes', (size) => {
|
|
75
|
+
expect(engine.ripemd160(input(size))).toEqual(ripemd160(input(size)))
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test.each([0, 1, 32, 63, 64, 65, 200])(
|
|
79
|
+
'hmacSha256 matches the default with a %i byte key',
|
|
80
|
+
(size) => {
|
|
81
|
+
const key = new Uint8Array(size).fill(0xa5)
|
|
82
|
+
const message = input(100)
|
|
83
|
+
expect(engine.hmacSha256(key, message)).toEqual(
|
|
84
|
+
hmac(sha256, key, message),
|
|
85
|
+
)
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
test('behavior: known vectors', () => {
|
|
90
|
+
expect(Bytes.toHex(engine.blake3(new Uint8Array(0)))).toBe(
|
|
91
|
+
'0xaf1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262',
|
|
92
|
+
)
|
|
93
|
+
expect(Bytes.toHex(engine.keccak256(new Uint8Array(0)))).toBe(
|
|
94
|
+
'0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
|
|
95
|
+
)
|
|
96
|
+
expect(Bytes.toHex(engine.sha256(new Uint8Array(0)))).toBe(
|
|
97
|
+
'0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
|
98
|
+
)
|
|
99
|
+
expect(Bytes.toHex(engine.ripemd160(new Uint8Array(0)))).toBe(
|
|
100
|
+
'0x9c1185a5c5e9fc54612808977ee8f548b2258d31',
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('behavior: grows memory past 64 MiB, then still hashes small inputs', () => {
|
|
105
|
+
// The input itself is exactly 64 MiB, so the heap base and digest force
|
|
106
|
+
// memory past that boundary. Growing detaches the previous `ArrayBuffer`;
|
|
107
|
+
// a retained view would silently read zero bytes in the assertion below.
|
|
108
|
+
const large = new Uint8Array(64 * 1024 * 1024).fill(7)
|
|
109
|
+
expect(engine.sha256(large)).toEqual(sha256(large))
|
|
110
|
+
expect(engine.blake3(large)).toEqual(blake3(large))
|
|
111
|
+
|
|
112
|
+
const small = input(32)
|
|
113
|
+
expect(engine.sha256(small)).toEqual(sha256(small))
|
|
114
|
+
expect(engine.blake3(small)).toEqual(blake3(small))
|
|
115
|
+
}, 60_000)
|
|
116
|
+
|
|
117
|
+
test('behavior: accepts Uint8Array subviews without mutation', () => {
|
|
118
|
+
const backing = input(1027)
|
|
119
|
+
const subview = backing.subarray(1, 1026)
|
|
120
|
+
const snapshot = backing.slice()
|
|
121
|
+
expect(engine.blake3(subview)).toEqual(blake3(subview))
|
|
122
|
+
expect(backing).toEqual(snapshot)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('behavior: blake3 clears its complete memory region', async () => {
|
|
126
|
+
const module =
|
|
127
|
+
await internal.instantiate<blake3_internal.Exports>(blake3WasmBase64)
|
|
128
|
+
const message = input(1025)
|
|
129
|
+
const end = module.heapBase + message.length + 32
|
|
130
|
+
|
|
131
|
+
expect(blake3_internal.hash(module, message)).toEqual(blake3(message))
|
|
132
|
+
expect(module.view().slice(module.heapBase, end)).toEqual(
|
|
133
|
+
new Uint8Array(end - module.heapBase),
|
|
134
|
+
)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('behavior: blake3 clears its complete region after a late trap', async () => {
|
|
138
|
+
const module =
|
|
139
|
+
await internal.instantiate<blake3_internal.Exports>(blake3WasmBase64)
|
|
140
|
+
const message = input(1025)
|
|
141
|
+
const end = module.heapBase + message.length + 32
|
|
142
|
+
const exports = {
|
|
143
|
+
blake3_hash(inputPtr: number, length: number, outPtr: number) {
|
|
144
|
+
module.exports.blake3_hash(inputPtr, length, outPtr)
|
|
145
|
+
throw new WebAssembly.RuntimeError('forced late trap')
|
|
146
|
+
},
|
|
147
|
+
zero: (ptr: number, length: number) => module.exports.zero(ptr, length),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
expect(() =>
|
|
151
|
+
blake3_internal.hash({ ...module, exports }, message),
|
|
152
|
+
).toThrowError('forced late trap')
|
|
153
|
+
expect(module.view().slice(module.heapBase, end)).toEqual(
|
|
154
|
+
new Uint8Array(end - module.heapBase),
|
|
155
|
+
)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('behavior: rejects wasm32 pointer overflow before reserving', () => {
|
|
159
|
+
let reserved = false
|
|
160
|
+
const module: internal.Module<blake3_internal.Exports> = {
|
|
161
|
+
exports: {
|
|
162
|
+
blake3_hash() {},
|
|
163
|
+
zero() {},
|
|
164
|
+
},
|
|
165
|
+
heapBase: 0xffff_fff0,
|
|
166
|
+
reserve() {
|
|
167
|
+
reserved = true
|
|
168
|
+
},
|
|
169
|
+
view: () => new Uint8Array(0),
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
expect(() => blake3_internal.hash(module, input(1))).toThrow(
|
|
173
|
+
internal.MemoryError,
|
|
174
|
+
)
|
|
175
|
+
expect(reserved).toBe(false)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('behavior: accepts a workspace ending exactly at the wasm32 boundary', () => {
|
|
179
|
+
const marker = new Error('reserve reached')
|
|
180
|
+
const module: internal.Module<blake3_internal.Exports> = {
|
|
181
|
+
exports: {
|
|
182
|
+
blake3_hash() {},
|
|
183
|
+
zero() {},
|
|
184
|
+
},
|
|
185
|
+
heapBase: 0x1_0000_0000 - 33,
|
|
186
|
+
reserve(bytes) {
|
|
187
|
+
expect(bytes).toBe(33)
|
|
188
|
+
throw marker
|
|
189
|
+
},
|
|
190
|
+
view: () => new Uint8Array(0),
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
expect(() => blake3_internal.hash(module, input(1))).toThrow(marker)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
test('behavior: hmacSha256 clears its complete memory region', async () => {
|
|
197
|
+
const module = await internal.instantiate<hash.Exports>(wasmBase64)
|
|
198
|
+
const key = input(120)
|
|
199
|
+
const message = input(17)
|
|
200
|
+
const out = 32
|
|
201
|
+
const messagePtr = module.heapBase
|
|
202
|
+
const keyPtr = messagePtr + message.length
|
|
203
|
+
const outPtr = keyPtr + key.length
|
|
204
|
+
const scratchPtr = Math.ceil((outPtr + out) / 4) * 4
|
|
205
|
+
const end = scratchPtr + hmacSha256ScratchSize
|
|
206
|
+
const stack = module.view().slice(0, module.heapBase)
|
|
207
|
+
|
|
208
|
+
expect(hash.hmacSha256(module, key, message)).toEqual(
|
|
209
|
+
hmac(sha256, key, message),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
expect(module.view().slice(0, module.heapBase)).toEqual(stack)
|
|
213
|
+
expect(module.view().slice(messagePtr, end)).toEqual(
|
|
214
|
+
new Uint8Array(end - messagePtr),
|
|
215
|
+
)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test('behavior: hmacSha256 clears its complete region after a trap', async () => {
|
|
219
|
+
const module = await internal.instantiate<hash.Exports>(wasmBase64)
|
|
220
|
+
const key = input(120)
|
|
221
|
+
const message = input(17)
|
|
222
|
+
const out = 32
|
|
223
|
+
const messagePtr = module.heapBase
|
|
224
|
+
const keyPtr = messagePtr + message.length
|
|
225
|
+
const validOutPtr = keyPtr + key.length
|
|
226
|
+
const scratchPtr = Math.ceil((validOutPtr + out) / 4) * 4
|
|
227
|
+
const end = scratchPtr + hmacSha256ScratchSize
|
|
228
|
+
const stack = module.view().slice(0, module.heapBase)
|
|
229
|
+
// Preserve the production boundary while making the real export trap only
|
|
230
|
+
// after it has populated the scratch buffer.
|
|
231
|
+
const exports = {
|
|
232
|
+
hmac_sha256(
|
|
233
|
+
keyPtr: number,
|
|
234
|
+
keyLength: number,
|
|
235
|
+
messagePtr: number,
|
|
236
|
+
messageLength: number,
|
|
237
|
+
_outPtr: number,
|
|
238
|
+
scratchPtr: number,
|
|
239
|
+
) {
|
|
240
|
+
module.exports.hmac_sha256(
|
|
241
|
+
keyPtr,
|
|
242
|
+
keyLength,
|
|
243
|
+
messagePtr,
|
|
244
|
+
messageLength,
|
|
245
|
+
module.view().length - 16,
|
|
246
|
+
scratchPtr,
|
|
247
|
+
)
|
|
248
|
+
},
|
|
249
|
+
zero: (ptr: number, length: number) => module.exports.zero(ptr, length),
|
|
250
|
+
}
|
|
251
|
+
const trappingModule = { ...module, exports }
|
|
252
|
+
|
|
253
|
+
expect(() => hash.hmacSha256(trappingModule, key, message)).toThrow(
|
|
254
|
+
WebAssembly.RuntimeError,
|
|
255
|
+
)
|
|
256
|
+
expect(module.view().slice(0, module.heapBase)).toEqual(stack)
|
|
257
|
+
expect(module.view().slice(messagePtr, end)).toEqual(
|
|
258
|
+
new Uint8Array(end - messagePtr),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
module.exports.zero(module.view().length - 16, 16)
|
|
262
|
+
expect(hash.hmacSha256(module, key, message)).toEqual(
|
|
263
|
+
hmac(sha256, key, message),
|
|
264
|
+
)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
test('behavior: consecutive calls do not corrupt each other', () => {
|
|
268
|
+
const a = input(200)
|
|
269
|
+
const b = input(7)
|
|
270
|
+
expect(engine.keccak256(a)).toEqual(keccak_256(a))
|
|
271
|
+
expect(engine.blake3(a)).toEqual(blake3(a))
|
|
272
|
+
expect(engine.blake3(b)).toEqual(blake3(b))
|
|
273
|
+
expect(engine.blake3(a)).toEqual(blake3(a))
|
|
274
|
+
expect(engine.keccak256(b)).toEqual(keccak_256(b))
|
|
275
|
+
expect(engine.keccak256(a)).toEqual(keccak_256(a))
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('behavior: results do not alias WASM memory', () => {
|
|
279
|
+
const first = engine.keccak256(input(32))
|
|
280
|
+
const snapshot = first.slice()
|
|
281
|
+
engine.keccak256(input(64))
|
|
282
|
+
expect(first).toEqual(snapshot)
|
|
283
|
+
|
|
284
|
+
const blake3First = engine.blake3(input(32))
|
|
285
|
+
const blake3Snapshot = blake3First.slice()
|
|
286
|
+
engine.blake3(input(64))
|
|
287
|
+
expect(blake3First).toEqual(blake3Snapshot)
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
describe('Engine.install', () => {
|
|
292
|
+
test('behavior: ox uses the WASM implementation once installed', async () => {
|
|
293
|
+
await Engine.install({ Hash: engine })
|
|
294
|
+
try {
|
|
295
|
+
expect(Engine.get().Hash?.blake3?.(Bytes.from('0xdeadbeef'))).toEqual(
|
|
296
|
+
blake3(Bytes.from('0xdeadbeef')),
|
|
297
|
+
)
|
|
298
|
+
expect(Hash.keccak256('0xdeadbeef')).toBe(
|
|
299
|
+
'0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
|
|
300
|
+
)
|
|
301
|
+
expect(Hash.sha256('0xdeadbeef')).toBe(
|
|
302
|
+
'0x5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953',
|
|
303
|
+
)
|
|
304
|
+
expect(Hash.ripemd160('0xdeadbeef')).toBe(
|
|
305
|
+
'0x226821c2f5423e11fe9af68bd285c249db2e4b5a',
|
|
306
|
+
)
|
|
307
|
+
} finally {
|
|
308
|
+
Engine.reset()
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
})
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Hash as WasmHash } from 'ox/wasm'
|
|
2
|
+
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
3
|
+
import * as vectors from '../../../test/vectors/hashes/index.js'
|
|
4
|
+
import { wasmBase64 } from '../internal/hashes.wasm.js'
|
|
5
|
+
import { instantiate } from '../internal/instantiate.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Published vectors against the WASM implementations.
|
|
9
|
+
*
|
|
10
|
+
* The differential tests in `Hash.test.ts` and `Hash.fuzz.ts` prove the WASM
|
|
11
|
+
* agrees with `@noble/hashes`. These prove it agrees with the specifications, so
|
|
12
|
+
* a fault the two implementations happened to share would still be caught.
|
|
13
|
+
* See `test/vectors/hashes/README.md`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
let engine: WasmHash.engine.ReturnType
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
engine = await WasmHash.engine()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('blake3', () => {
|
|
23
|
+
test(`matches ${vectors.blake3.length} official BLAKE3 vectors`, () => {
|
|
24
|
+
for (const { digest, message } of vectors.blake3)
|
|
25
|
+
expect(engine.blake3(message)).toEqual(digest)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('sha256', () => {
|
|
30
|
+
test(`matches ${vectors.sha256.length} NIST CAVP vectors`, () => {
|
|
31
|
+
for (const { digest, message } of vectors.sha256)
|
|
32
|
+
expect(engine.sha256(message)).toEqual(digest)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('hmacSha256', () => {
|
|
37
|
+
test(`matches ${vectors.hmacSha256.length} RFC 4231 vectors`, () => {
|
|
38
|
+
// Two of these use 131-byte keys, which is the only coverage of the
|
|
39
|
+
// longer-than-a-block path where the key is hashed before padding.
|
|
40
|
+
for (const { digest, key, message } of vectors.hmacSha256)
|
|
41
|
+
expect(engine.hmacSha256(key, message)).toEqual(digest)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('ripemd160', () => {
|
|
46
|
+
test(`matches ${vectors.ripemd160.length} reference vectors`, () => {
|
|
47
|
+
for (const { digest, message } of vectors.ripemd160)
|
|
48
|
+
expect(engine.ripemd160(message)).toEqual(digest)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('matches the million-`a` reference vector', () => {
|
|
52
|
+
const { digest, message } = vectors.ripemd160MillionA
|
|
53
|
+
expect(engine.ripemd160(message)).toEqual(digest)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe('keccak256', () => {
|
|
58
|
+
test(`matches ${vectors.keccak256.length} OpenSSL vectors`, () => {
|
|
59
|
+
for (const { digest, message } of vectors.keccak256)
|
|
60
|
+
expect(engine.keccak256(message)).toEqual(digest)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('keccak_f1600', () => {
|
|
65
|
+
type Exports = { keccak_f1600(state: number): void }
|
|
66
|
+
|
|
67
|
+
/** Writes 25 lanes as little-endian 64-bit words, permutes, and reads back. */
|
|
68
|
+
async function permute(lanes: readonly bigint[]) {
|
|
69
|
+
// A separate instance, because this export is not part of the engine and so
|
|
70
|
+
// is deliberately not reachable through `WasmHash.engine`.
|
|
71
|
+
const module = await instantiate<Exports>(wasmBase64)
|
|
72
|
+
module.reserve(200)
|
|
73
|
+
const view = module.view()
|
|
74
|
+
for (let lane = 0; lane < 25; lane++) {
|
|
75
|
+
let value = lanes[lane]!
|
|
76
|
+
for (let byte = 0; byte < 8; byte++) {
|
|
77
|
+
view[module.heapBase + lane * 8 + byte] = Number(value & 0xffn)
|
|
78
|
+
value >>= 8n
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports.keccak_f1600(module.heapBase)
|
|
83
|
+
|
|
84
|
+
const after = module.view()
|
|
85
|
+
const result: bigint[] = []
|
|
86
|
+
for (let lane = 0; lane < 25; lane++) {
|
|
87
|
+
let value = 0n
|
|
88
|
+
for (let byte = 7; byte >= 0; byte--)
|
|
89
|
+
value =
|
|
90
|
+
(value << 8n) | BigInt(after[module.heapBase + lane * 8 + byte]!)
|
|
91
|
+
result.push(value)
|
|
92
|
+
}
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
test('matches the Keccak team‘s reference examples', async () => {
|
|
97
|
+
for (const example of vectors.keccakPermutation.examples)
|
|
98
|
+
expect(await permute(example.input)).toEqual(example.output)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('reference examples chain, so the second is not a restatement', () => {
|
|
102
|
+
// XKCP's second example feeds the first one's output back in. Asserting the
|
|
103
|
+
// link means a parser that silently read the same block twice would fail
|
|
104
|
+
// here rather than quietly halving the coverage above.
|
|
105
|
+
const [first, second] = vectors.keccakPermutation.examples
|
|
106
|
+
expect(second!.input).toEqual(first!.output)
|
|
107
|
+
expect(second!.output).not.toEqual(first!.output)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('the parsed fixture is internally consistent', () => {
|
|
111
|
+
// Checks the fixture, not the implementation. Each `rounds[n]` is the state
|
|
112
|
+
// after round `n`'s iota step, and nothing exported reaches a single round,
|
|
113
|
+
// so per-round agreement is not observable from here -- the permutation
|
|
114
|
+
// itself is covered above, end to end against `input`/`output`. What this
|
|
115
|
+
// catches is a parser that mis-read the file and quietly weakened that.
|
|
116
|
+
const { examples, rhoOffsets, roundConstants } = vectors.keccakPermutation
|
|
117
|
+
expect(roundConstants).toHaveLength(24)
|
|
118
|
+
expect(rhoOffsets).toHaveLength(25)
|
|
119
|
+
for (const example of examples) {
|
|
120
|
+
expect(example.rounds).toHaveLength(24)
|
|
121
|
+
expect(example.rounds.at(-1)).toEqual(example.output)
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js'
|
|
3
|
+
import { describe, expect, test } from 'vp/test'
|
|
4
|
+
import * as Bytes from '../../core/Bytes.js'
|
|
5
|
+
import * as Engine from '../../core/Engine.js'
|
|
6
|
+
import * as Keystore from '../../core/Keystore.js'
|
|
7
|
+
import * as WasmKeystore from '../Keystore.js'
|
|
8
|
+
|
|
9
|
+
describe('engine', () => {
|
|
10
|
+
test('behavior: derives arbitrary-length keys in a real browser', async () => {
|
|
11
|
+
const engine = await WasmKeystore.engine()
|
|
12
|
+
const password = Bytes.fromString('testpassword')
|
|
13
|
+
const salt = new Uint8Array(52).fill(0xa5)
|
|
14
|
+
|
|
15
|
+
expect(engine.pbkdf2Sha256(password, salt, { c: 3, dkLen: 65 })).toEqual(
|
|
16
|
+
pbkdf2_noble(sha256, password, salt, { c: 3, dkLen: 65 }),
|
|
17
|
+
)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('behavior: ox uses synchronous WASM PBKDF2 once installed', async () => {
|
|
21
|
+
const engine = await WasmKeystore.engine()
|
|
22
|
+
await Engine.install({ Keystore: engine })
|
|
23
|
+
try {
|
|
24
|
+
const salt = new Uint8Array(32).fill(7)
|
|
25
|
+
const [key] = Keystore.pbkdf2({
|
|
26
|
+
iterations: 1_000,
|
|
27
|
+
password: 'testpassword',
|
|
28
|
+
salt,
|
|
29
|
+
})
|
|
30
|
+
expect(key()).toBe(
|
|
31
|
+
Bytes.toHex(
|
|
32
|
+
pbkdf2_noble(sha256, Bytes.fromString('testpassword'), salt, {
|
|
33
|
+
c: 1_000,
|
|
34
|
+
dkLen: 32,
|
|
35
|
+
}),
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
} finally {
|
|
39
|
+
Engine.reset()
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { fc, test } from '@fast-check/vitest'
|
|
2
|
+
import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
|
|
3
|
+
import { sha256 } from '@noble/hashes/sha2.js'
|
|
4
|
+
import { beforeAll, describe, expect } from 'vp/test'
|
|
5
|
+
import { numRuns } from '../../../test/fuzz/numRuns.js'
|
|
6
|
+
import * as WasmHash from '../Hash.js'
|
|
7
|
+
import * as WasmKeystore from '../Keystore.js'
|
|
8
|
+
|
|
9
|
+
let hash: WasmHash.engine.ReturnType
|
|
10
|
+
let keystore: WasmKeystore.engine.ReturnType
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
;[hash, keystore] = await Promise.all([
|
|
14
|
+
WasmHash.engine(),
|
|
15
|
+
WasmKeystore.engine(),
|
|
16
|
+
])
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const arbitraryPassword = fc.oneof(
|
|
20
|
+
{
|
|
21
|
+
arbitrary: fc
|
|
22
|
+
.constantFrom(0, 1, 32, 63, 64, 65, 128, 129)
|
|
23
|
+
.chain((size) => fc.uint8Array({ maxLength: size, minLength: size })),
|
|
24
|
+
weight: 3,
|
|
25
|
+
},
|
|
26
|
+
{ arbitrary: fc.uint8Array({ maxLength: 200 }), weight: 2 },
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const arbitrarySalt = fc.oneof(
|
|
30
|
+
{
|
|
31
|
+
arbitrary: fc
|
|
32
|
+
.constantFrom(0, 1, 31, 32, 51, 52, 55, 56, 63, 64, 65, 136)
|
|
33
|
+
.chain((size) => fc.uint8Array({ maxLength: size, minLength: size })),
|
|
34
|
+
weight: 3,
|
|
35
|
+
},
|
|
36
|
+
{ arbitrary: fc.uint8Array({ maxLength: 200 }), weight: 2 },
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
const arbitraryDkLen = fc.oneof(
|
|
40
|
+
{
|
|
41
|
+
arbitrary: fc.constantFrom(1, 2, 31, 32, 33, 63, 64, 65, 96, 97),
|
|
42
|
+
weight: 3,
|
|
43
|
+
},
|
|
44
|
+
{ arbitrary: fc.integer({ max: 256, min: 1 }), weight: 2 },
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
function asSubview(input: Uint8Array) {
|
|
48
|
+
const backing = new Uint8Array(input.length + 11).fill(0xa5)
|
|
49
|
+
backing.set(input, 7)
|
|
50
|
+
return { backing, view: backing.subarray(7, 7 + input.length) }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe('pbkdf2Sha256', () => {
|
|
54
|
+
test.prop(
|
|
55
|
+
{
|
|
56
|
+
c: fc.integer({ max: 20, min: 1 }),
|
|
57
|
+
dkLen: arbitraryDkLen,
|
|
58
|
+
password: arbitraryPassword,
|
|
59
|
+
salt: arbitrarySalt,
|
|
60
|
+
},
|
|
61
|
+
{ numRuns },
|
|
62
|
+
)(
|
|
63
|
+
'matches @noble/hashes for arbitrary inputs and output lengths',
|
|
64
|
+
({ c, dkLen, password, salt }) => {
|
|
65
|
+
expect(keystore.pbkdf2Sha256(password, salt, { c, dkLen })).toEqual(
|
|
66
|
+
pbkdf2_noble(sha256, password, salt, { c, dkLen }),
|
|
67
|
+
)
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
test.prop(
|
|
72
|
+
{
|
|
73
|
+
c: fc.integer({ max: 10, min: 1 }),
|
|
74
|
+
dkLen: arbitraryDkLen,
|
|
75
|
+
password: arbitraryPassword,
|
|
76
|
+
salt: arbitrarySalt,
|
|
77
|
+
},
|
|
78
|
+
{ numRuns },
|
|
79
|
+
)(
|
|
80
|
+
'handles subviews without mutating either backing buffer',
|
|
81
|
+
({ c, dkLen, password, salt }) => {
|
|
82
|
+
const password_ = asSubview(password)
|
|
83
|
+
const salt_ = asSubview(salt)
|
|
84
|
+
const passwordSnapshot = password_.backing.slice()
|
|
85
|
+
const saltSnapshot = salt_.backing.slice()
|
|
86
|
+
|
|
87
|
+
expect(
|
|
88
|
+
keystore.pbkdf2Sha256(password_.view, salt_.view, {
|
|
89
|
+
c,
|
|
90
|
+
dkLen,
|
|
91
|
+
}),
|
|
92
|
+
).toEqual(pbkdf2_noble(sha256, password_.view, salt_.view, { c, dkLen }))
|
|
93
|
+
expect(password_.backing).toEqual(passwordSnapshot)
|
|
94
|
+
expect(salt_.backing).toEqual(saltSnapshot)
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
test.prop(
|
|
99
|
+
{
|
|
100
|
+
dkLen: arbitraryDkLen,
|
|
101
|
+
input: fc.uint8Array({ maxLength: 256 }),
|
|
102
|
+
},
|
|
103
|
+
{ numRuns },
|
|
104
|
+
)('shares memory safely with hash calls', ({ dkLen, input }) => {
|
|
105
|
+
const derived = keystore.pbkdf2Sha256(input, input, {
|
|
106
|
+
c: 2,
|
|
107
|
+
dkLen,
|
|
108
|
+
})
|
|
109
|
+
const snapshot = derived.slice()
|
|
110
|
+
expect(hash.sha256(input)).toEqual(sha256(input))
|
|
111
|
+
expect(derived).toEqual(snapshot)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Engine } from 'ox'
|
|
2
|
+
import { expectTypeOf, test } from 'vp/test'
|
|
3
|
+
import * as core_Keystore from '../../core/Keystore.js'
|
|
4
|
+
import * as WasmKeystore from '../Keystore.js'
|
|
5
|
+
|
|
6
|
+
type Slot = Awaited<ReturnType<typeof WasmKeystore.engine>>
|
|
7
|
+
|
|
8
|
+
test('synchronous PBKDF2 is present', () => {
|
|
9
|
+
expectTypeOf<Slot['pbkdf2Sha256']>().toEqualTypeOf<
|
|
10
|
+
(
|
|
11
|
+
password: Uint8Array,
|
|
12
|
+
salt: Uint8Array,
|
|
13
|
+
options: { c: number; dkLen: number },
|
|
14
|
+
) => Uint8Array
|
|
15
|
+
>()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('unsupported primitives are absent', () => {
|
|
19
|
+
expectTypeOf<Slot>().not.toHaveProperty('aesCtrDecrypt')
|
|
20
|
+
expectTypeOf<Slot>().not.toHaveProperty('aesCtrEncrypt')
|
|
21
|
+
expectTypeOf<Slot>().not.toHaveProperty('pbkdf2Sha256Async')
|
|
22
|
+
expectTypeOf<Slot>().not.toHaveProperty('scrypt')
|
|
23
|
+
expectTypeOf<Slot>().not.toHaveProperty('scryptAsync')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('the result is the raw slot', () => {
|
|
27
|
+
expectTypeOf<{ Keystore: Slot }>().toExtend<Engine.Engine>()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('the WASM namespace exposes the public Keystore API', () => {
|
|
31
|
+
expectTypeOf(WasmKeystore.pbkdf2).toEqualTypeOf(core_Keystore.pbkdf2)
|
|
32
|
+
expectTypeOf<typeof WasmKeystore>().not.toHaveProperty('create')
|
|
33
|
+
})
|