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,192 @@
|
|
|
1
|
+
import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js'
|
|
3
|
+
import { beforeAll, 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
|
+
import * as hashes from '../internal/hashes.js'
|
|
9
|
+
import { wasmBase64 } from '../internal/hashes.wasm.js'
|
|
10
|
+
import * as internal from '../internal/instantiate.js'
|
|
11
|
+
import * as pbkdf2 from '../internal/pbkdf2.js'
|
|
12
|
+
|
|
13
|
+
let engine: WasmKeystore.engine.ReturnType
|
|
14
|
+
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
engine = await WasmKeystore.engine()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
function input(size: number) {
|
|
20
|
+
return new Uint8Array(size).map((_, index) => (index * 37 + 11) & 0xff)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('engine', () => {
|
|
24
|
+
test('default', async () => {
|
|
25
|
+
expect(Object.keys(await WasmKeystore.engine())).toMatchInlineSnapshot(`
|
|
26
|
+
[
|
|
27
|
+
"pbkdf2Sha256",
|
|
28
|
+
]
|
|
29
|
+
`)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('behavior: shares the compiled hashes instance', async () => {
|
|
33
|
+
const [a, b] = await Promise.all([hashes.load(), hashes.load()])
|
|
34
|
+
expect(a).toBe(b)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('behavior: hands every caller its own engine', async () => {
|
|
38
|
+
const [a, b] = await Promise.all([
|
|
39
|
+
WasmKeystore.engine(),
|
|
40
|
+
WasmKeystore.engine(),
|
|
41
|
+
])
|
|
42
|
+
expect(a).not.toBe(b)
|
|
43
|
+
|
|
44
|
+
a.pbkdf2Sha256 = () => new Uint8Array([9])
|
|
45
|
+
const c = await WasmKeystore.engine()
|
|
46
|
+
expect(c.pbkdf2Sha256(input(8), input(16), { c: 2, dkLen: 33 })).toEqual(
|
|
47
|
+
b.pbkdf2Sha256(input(8), input(16), { c: 2, dkLen: 33 }),
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const cases = [
|
|
53
|
+
{ c: 1, dkLen: 1, password: 0, salt: 0 },
|
|
54
|
+
{ c: 2, dkLen: 31, password: 1, salt: 51 },
|
|
55
|
+
{ c: 3, dkLen: 32, password: 63, salt: 52 },
|
|
56
|
+
{ c: 4, dkLen: 33, password: 64, salt: 55 },
|
|
57
|
+
{ c: 7, dkLen: 64, password: 65, salt: 64 },
|
|
58
|
+
{ c: 2, dkLen: 65, password: 129, salt: 137 },
|
|
59
|
+
] as const
|
|
60
|
+
|
|
61
|
+
describe('pbkdf2Sha256', () => {
|
|
62
|
+
test.each(cases)(
|
|
63
|
+
'matches the default for password=$password salt=$salt c=$c dkLen=$dkLen',
|
|
64
|
+
({ c, dkLen, password: passwordLength, salt: saltLength }) => {
|
|
65
|
+
const password = input(passwordLength)
|
|
66
|
+
const salt = input(saltLength)
|
|
67
|
+
expect(engine.pbkdf2Sha256(password, salt, { c, dkLen })).toEqual(
|
|
68
|
+
pbkdf2_noble(sha256, password, salt, { c, dkLen }),
|
|
69
|
+
)
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
test('behavior: reads non-zero-offset views without mutating their backing buffers', () => {
|
|
74
|
+
const passwordBacking = new Uint8Array(80).fill(0xa5)
|
|
75
|
+
const saltBacking = new Uint8Array(90).fill(0x5a)
|
|
76
|
+
const password = passwordBacking.subarray(7, 72)
|
|
77
|
+
const salt = saltBacking.subarray(11, 63)
|
|
78
|
+
password.set(input(password.length))
|
|
79
|
+
salt.set(input(salt.length))
|
|
80
|
+
const passwordSnapshot = passwordBacking.slice()
|
|
81
|
+
const saltSnapshot = saltBacking.slice()
|
|
82
|
+
|
|
83
|
+
expect(engine.pbkdf2Sha256(password, salt, { c: 3, dkLen: 65 })).toEqual(
|
|
84
|
+
pbkdf2_noble(sha256, password, salt, { c: 3, dkLen: 65 }),
|
|
85
|
+
)
|
|
86
|
+
expect(passwordBacking).toEqual(passwordSnapshot)
|
|
87
|
+
expect(saltBacking).toEqual(saltSnapshot)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('behavior: results are fresh and never alias WASM memory', () => {
|
|
91
|
+
const password = input(65)
|
|
92
|
+
const salt = input(52)
|
|
93
|
+
const first = engine.pbkdf2Sha256(password, salt, {
|
|
94
|
+
c: 2,
|
|
95
|
+
dkLen: 65,
|
|
96
|
+
})
|
|
97
|
+
const snapshot = first.slice()
|
|
98
|
+
const second = engine.pbkdf2Sha256(input(5), input(9), {
|
|
99
|
+
c: 3,
|
|
100
|
+
dkLen: 97,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expect(first).not.toBe(second)
|
|
104
|
+
expect(first).toEqual(snapshot)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('behavior: clears passwords, salts, outputs, and scratch', async () => {
|
|
108
|
+
const module = await internal.instantiate<hashes.Exports>(wasmBase64)
|
|
109
|
+
const password = input(65)
|
|
110
|
+
const salt = input(52)
|
|
111
|
+
const stack = module.view().slice(0, module.heapBase)
|
|
112
|
+
|
|
113
|
+
expect(
|
|
114
|
+
pbkdf2.pbkdf2Sha256(module, password, salt, { c: 3, dkLen: 65 }),
|
|
115
|
+
).toEqual(pbkdf2_noble(sha256, password, salt, { c: 3, dkLen: 65 }))
|
|
116
|
+
expect(module.view().slice(0, module.heapBase)).toEqual(stack)
|
|
117
|
+
expect(module.view().slice(module.heapBase)).toEqual(
|
|
118
|
+
new Uint8Array(module.view().length - module.heapBase),
|
|
119
|
+
)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('behavior: clears the complete region after a late trap', async () => {
|
|
123
|
+
const module = await internal.instantiate<hashes.Exports>(wasmBase64)
|
|
124
|
+
const password = input(65)
|
|
125
|
+
const salt = input(52)
|
|
126
|
+
const exports: pbkdf2.Exports = {
|
|
127
|
+
pbkdf2_sha256(...parameters) {
|
|
128
|
+
module.exports.pbkdf2_sha256(...parameters)
|
|
129
|
+
throw new WebAssembly.RuntimeError('forced late trap')
|
|
130
|
+
},
|
|
131
|
+
zero: (ptr, length) => module.exports.zero(ptr, length),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
expect(() =>
|
|
135
|
+
pbkdf2.pbkdf2Sha256({ ...module, exports }, password, salt, {
|
|
136
|
+
c: 3,
|
|
137
|
+
dkLen: 65,
|
|
138
|
+
}),
|
|
139
|
+
).toThrow(WebAssembly.RuntimeError)
|
|
140
|
+
expect(module.view().slice(module.heapBase)).toEqual(
|
|
141
|
+
new Uint8Array(module.view().length - module.heapBase),
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('behavior: rejects a workspace that overflows wasm32 before growing memory', async () => {
|
|
146
|
+
const module = await internal.instantiate<hashes.Exports>(wasmBase64)
|
|
147
|
+
const memory = module.view().length
|
|
148
|
+
|
|
149
|
+
expect(() =>
|
|
150
|
+
pbkdf2.pbkdf2Sha256(module, new Uint8Array(), new Uint8Array(), {
|
|
151
|
+
c: 1,
|
|
152
|
+
dkLen: 0xffff_ffff,
|
|
153
|
+
}),
|
|
154
|
+
).toThrow(internal.MemoryError)
|
|
155
|
+
expect(module.view()).toHaveLength(memory)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test.each([
|
|
159
|
+
{ name: 'c', options: { c: 0, dkLen: 32 } },
|
|
160
|
+
{ name: 'c', options: { c: 1.5, dkLen: 32 } },
|
|
161
|
+
{ name: 'c', options: { c: 0x1_0000_0000, dkLen: 32 } },
|
|
162
|
+
{ name: 'dkLen', options: { c: 1, dkLen: 0 } },
|
|
163
|
+
{ name: 'dkLen', options: { c: 1, dkLen: 1.5 } },
|
|
164
|
+
{ name: 'dkLen', options: { c: 1, dkLen: 0x1_0000_0000 } },
|
|
165
|
+
])('behavior: rejects invalid $name before entering WASM', ({ options }) => {
|
|
166
|
+
expect(() => engine.pbkdf2Sha256(input(8), input(16), options)).toThrow()
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('Engine.install', () => {
|
|
171
|
+
test('behavior: ox uses synchronous WASM PBKDF2 once installed', async () => {
|
|
172
|
+
await Engine.install({ Keystore: engine })
|
|
173
|
+
try {
|
|
174
|
+
const salt = input(32)
|
|
175
|
+
const [key] = Keystore.pbkdf2({
|
|
176
|
+
iterations: 1_000,
|
|
177
|
+
password: 'testpassword',
|
|
178
|
+
salt,
|
|
179
|
+
})
|
|
180
|
+
expect(key()).toBe(
|
|
181
|
+
Bytes.toHex(
|
|
182
|
+
pbkdf2_noble(sha256, Bytes.fromString('testpassword'), salt, {
|
|
183
|
+
c: 1_000,
|
|
184
|
+
dkLen: 32,
|
|
185
|
+
}),
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
} finally {
|
|
189
|
+
Engine.reset()
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
2
|
+
import * as vectors from '../../../test/vectors/pbkdf2/index.js'
|
|
3
|
+
import * as WasmKeystore from '../Keystore.js'
|
|
4
|
+
|
|
5
|
+
let engine: WasmKeystore.engine.ReturnType
|
|
6
|
+
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
engine = await WasmKeystore.engine()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
describe('pbkdf2Sha256', () => {
|
|
12
|
+
test(`matches ${vectors.vectors.length} RFC 7914 vectors`, () => {
|
|
13
|
+
for (const { iterations, key, password, salt } of vectors.vectors)
|
|
14
|
+
expect(
|
|
15
|
+
engine.pbkdf2Sha256(password, salt, {
|
|
16
|
+
c: iterations,
|
|
17
|
+
dkLen: key.length,
|
|
18
|
+
}),
|
|
19
|
+
).toEqual(key)
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { expectTypeOf, test } from 'vp/test'
|
|
2
|
+
import type * as Engine from '../../core/Engine.js'
|
|
3
|
+
import * as core_Mnemonic from '../../core/Mnemonic.js'
|
|
4
|
+
import * as Mnemonic from '../Mnemonic.js'
|
|
5
|
+
|
|
6
|
+
type Slot = Awaited<ReturnType<typeof Mnemonic.engine>>
|
|
7
|
+
|
|
8
|
+
test('return type exposes Mnemonic.toSeed', () => {
|
|
9
|
+
expectTypeOf<Slot['toSeed']>().toEqualTypeOf<
|
|
10
|
+
NonNullable<Engine.Mnemonic['toSeed']>
|
|
11
|
+
>()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('the WASM namespace exposes the public Mnemonic API', () => {
|
|
15
|
+
expectTypeOf(Mnemonic.toSeed).toEqualTypeOf(core_Mnemonic.toSeed)
|
|
16
|
+
expectTypeOf<typeof Mnemonic>().not.toHaveProperty('create')
|
|
17
|
+
})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { mnemonicToSeedSync } from '@scure/bip39'
|
|
2
|
+
import { describe, expect, test } from 'vp/test'
|
|
3
|
+
import { vectors } from '../../../test/vectors/bip39/index.js'
|
|
4
|
+
import * as Mnemonic from '../Mnemonic.js'
|
|
5
|
+
import * as crypto25519 from '../internal/crypto25519.js'
|
|
6
|
+
import { wasmBase64 } from '../internal/crypto25519.wasm.js'
|
|
7
|
+
import * as internal from '../internal/instantiate.js'
|
|
8
|
+
import * as mnemonic_internal from '../internal/mnemonic.js'
|
|
9
|
+
|
|
10
|
+
describe('engine', () => {
|
|
11
|
+
test('vectors: matches the official English BIP-39 vector', async () => {
|
|
12
|
+
const { toSeed } = await Mnemonic.engine()
|
|
13
|
+
|
|
14
|
+
expect(
|
|
15
|
+
toHex(toSeed(vectors.english.mnemonic, vectors.english.passphrase)),
|
|
16
|
+
).toBe(vectors.english.seed)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('vectors: matches the official Japanese Unicode BIP-39 vector', async () => {
|
|
20
|
+
const { toSeed } = await Mnemonic.engine()
|
|
21
|
+
|
|
22
|
+
expect(
|
|
23
|
+
toHex(toSeed(vectors.japanese.mnemonic, vectors.japanese.passphrase)),
|
|
24
|
+
).toBe(vectors.japanese.seed)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('behavior: matches NFKD and every supported word count', async () => {
|
|
28
|
+
const { toSeed } = await Mnemonic.engine()
|
|
29
|
+
|
|
30
|
+
for (const count of [12, 15, 18, 21, 24]) {
|
|
31
|
+
const composed = Array.from({ length: count }, () => 'café').join(' ')
|
|
32
|
+
const decomposed = Array.from({ length: count }, () => 'cafe\u0301').join(
|
|
33
|
+
' ',
|
|
34
|
+
)
|
|
35
|
+
expect(toSeed(composed, 'pássphrase')).toEqual(
|
|
36
|
+
toSeed(decomposed, 'pa\u0301ssphrase'),
|
|
37
|
+
)
|
|
38
|
+
expect(toSeed(composed, 'pássphrase')).toEqual(
|
|
39
|
+
mnemonicToSeedSync(composed, 'pássphrase'),
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('behavior: rejects the same malformed phrase shapes', async () => {
|
|
45
|
+
const { toSeed } = await Mnemonic.engine()
|
|
46
|
+
|
|
47
|
+
for (const count of [0, 1, 11, 13, 23, 25])
|
|
48
|
+
expect(() =>
|
|
49
|
+
toSeed(Array.from({ length: count }, () => 'word').join(' ')),
|
|
50
|
+
).toThrowError('Invalid mnemonic')
|
|
51
|
+
expect(() => toSeed(1 as never)).toThrowError(
|
|
52
|
+
'invalid mnemonic type: number',
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('behavior: outputs remain owned and staging memory is cleared', async () => {
|
|
57
|
+
const { toSeed } = await Mnemonic.engine()
|
|
58
|
+
const seed = toSeed(vectors.english.mnemonic, vectors.japanese.passphrase)
|
|
59
|
+
const snapshot = seed.slice()
|
|
60
|
+
toSeed(Array.from({ length: 24 }, () => 'word').join(' '), 'other')
|
|
61
|
+
expect(seed).toEqual(snapshot)
|
|
62
|
+
|
|
63
|
+
const passwordSize = new TextEncoder().encode(
|
|
64
|
+
vectors.english.mnemonic.normalize('NFKD'),
|
|
65
|
+
).length
|
|
66
|
+
const saltSize = new TextEncoder().encode(
|
|
67
|
+
`mnemonic${vectors.japanese.passphrase}`.normalize('NFKD'),
|
|
68
|
+
).length
|
|
69
|
+
const module = await crypto25519.load()
|
|
70
|
+
expect(
|
|
71
|
+
module
|
|
72
|
+
.view()
|
|
73
|
+
.subarray(
|
|
74
|
+
module.heapBase,
|
|
75
|
+
module.heapBase + passwordSize + saltSize + 64,
|
|
76
|
+
)
|
|
77
|
+
.every((byte) => byte === 0),
|
|
78
|
+
).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('behavior: clears password, salt, and output after a late trap', async () => {
|
|
82
|
+
const module = await internal.instantiate<crypto25519.Exports>(wasmBase64)
|
|
83
|
+
const vector = vectors.japanese
|
|
84
|
+
const exports: crypto25519.Exports = {
|
|
85
|
+
...module.exports,
|
|
86
|
+
mnemonic_to_seed(...parameters) {
|
|
87
|
+
module.exports.mnemonic_to_seed(...parameters)
|
|
88
|
+
throw new WebAssembly.RuntimeError('forced late trap')
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
expect(() =>
|
|
93
|
+
mnemonic_internal.toSeed(
|
|
94
|
+
{ ...module, exports },
|
|
95
|
+
vector.mnemonic,
|
|
96
|
+
vector.passphrase,
|
|
97
|
+
),
|
|
98
|
+
).toThrow(WebAssembly.RuntimeError)
|
|
99
|
+
expect(
|
|
100
|
+
module
|
|
101
|
+
.view()
|
|
102
|
+
.subarray(module.heapBase)
|
|
103
|
+
.every((byte) => byte === 0),
|
|
104
|
+
).toBe(true)
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
function toHex(value: Uint8Array): string {
|
|
109
|
+
return Buffer.from(value).toString('hex')
|
|
110
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { expect, test } from 'vp/test'
|
|
3
|
+
import { instantiate } from '../internal/instantiate.js'
|
|
4
|
+
import { wasmBase64 } from './runtime.wasm.js'
|
|
5
|
+
|
|
6
|
+
test('freestanding runtime preserves memmove and realloc semantics', async () => {
|
|
7
|
+
const module = await instantiate<{ run(): number }>(wasmBase64)
|
|
8
|
+
expect(module.exports.run()).toBe(0)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('memmove compares flat-memory offsets rather than C object pointers', () => {
|
|
12
|
+
const source = readFileSync(
|
|
13
|
+
new URL('../../../wasm/src/ox_rt.c', import.meta.url),
|
|
14
|
+
'utf8',
|
|
15
|
+
)
|
|
16
|
+
const body = source.match(/void \*memmove\([\s\S]*?\n}\n\nvoid \*memset/)?.[0]
|
|
17
|
+
|
|
18
|
+
expect(body).toBeDefined()
|
|
19
|
+
expect(body).toContain('if ((size_t)d < (size_t)s)')
|
|
20
|
+
expect(body).not.toMatch(/if\s*\(\s*d\s*<\s*s\s*\)/)
|
|
21
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { expectTypeOf, test } from 'vp/test'
|
|
2
|
+
import type * as Engine from '../../core/Engine.js'
|
|
3
|
+
import * as core_X25519 from '../../core/X25519.js'
|
|
4
|
+
import * as X25519 from '../X25519.js'
|
|
5
|
+
|
|
6
|
+
type Slot = Awaited<ReturnType<typeof X25519.engine>>
|
|
7
|
+
|
|
8
|
+
test('return type exposes only implemented X25519 primitives', () => {
|
|
9
|
+
expectTypeOf<Slot['getPublicKey']>().toEqualTypeOf<
|
|
10
|
+
NonNullable<Engine.Ecdh['getPublicKey']>
|
|
11
|
+
>()
|
|
12
|
+
expectTypeOf<Slot['getSharedSecret']>().toEqualTypeOf<
|
|
13
|
+
NonNullable<Engine.Ecdh['getSharedSecret']>
|
|
14
|
+
>()
|
|
15
|
+
expectTypeOf<Slot>().not.toHaveProperty('randomSecretKey')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('the WASM namespace exposes the public X25519 API', () => {
|
|
19
|
+
expectTypeOf(X25519.getPublicKey).toEqualTypeOf(core_X25519.getPublicKey)
|
|
20
|
+
expectTypeOf<typeof X25519>().not.toHaveProperty('create')
|
|
21
|
+
})
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import { x25519 } from '@noble/curves/ed25519.js'
|
|
3
|
+
import { describe, expect, test } from 'vp/test'
|
|
4
|
+
import * as X25519 from '../X25519.js'
|
|
5
|
+
import * as crypto25519 from '../internal/crypto25519.js'
|
|
6
|
+
import { wasmBase64 } from '../internal/crypto25519.wasm.js'
|
|
7
|
+
import * as internal from '../internal/instantiate.js'
|
|
8
|
+
import * as x25519_internal from '../internal/x25519.js'
|
|
9
|
+
import { x25519LowOrder } from './fixtures.js'
|
|
10
|
+
|
|
11
|
+
const alicePrivateKey = fromHex(
|
|
12
|
+
'77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a',
|
|
13
|
+
)
|
|
14
|
+
const bobPrivateKey = fromHex(
|
|
15
|
+
'5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb',
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
describe('engine', () => {
|
|
19
|
+
test('vectors: matches RFC 7748 key agreement', async () => {
|
|
20
|
+
const engine = await X25519.engine()
|
|
21
|
+
const alicePublicKey = engine.getPublicKey(alicePrivateKey)
|
|
22
|
+
const bobPublicKey = engine.getPublicKey(bobPrivateKey)
|
|
23
|
+
|
|
24
|
+
expect(toHex(alicePublicKey)).toMatchInlineSnapshot(
|
|
25
|
+
`"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"`,
|
|
26
|
+
)
|
|
27
|
+
expect(toHex(bobPublicKey)).toMatchInlineSnapshot(
|
|
28
|
+
`"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"`,
|
|
29
|
+
)
|
|
30
|
+
expect(
|
|
31
|
+
toHex(engine.getSharedSecret(alicePrivateKey, bobPublicKey)),
|
|
32
|
+
).toMatchInlineSnapshot(
|
|
33
|
+
`"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"`,
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('vectors: matches RFC 7748 arbitrary-point cases', async () => {
|
|
38
|
+
const engine = await X25519.engine()
|
|
39
|
+
const vectors = [
|
|
40
|
+
[
|
|
41
|
+
'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
|
|
42
|
+
'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
|
|
43
|
+
'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
'4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
|
|
47
|
+
'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
|
|
48
|
+
'95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
|
|
49
|
+
],
|
|
50
|
+
] as const
|
|
51
|
+
|
|
52
|
+
for (const [privateKey, publicKey, expected] of vectors)
|
|
53
|
+
expect(
|
|
54
|
+
toHex(engine.getSharedSecret(fromHex(privateKey), fromHex(publicKey))),
|
|
55
|
+
).toBe(expected)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('vectors: matches RFC 7748 iterated cases', async () => {
|
|
59
|
+
const engine = await X25519.engine()
|
|
60
|
+
let privateKey = Uint8Array.of(9, ...new Uint8Array(31))
|
|
61
|
+
let publicKey = privateKey
|
|
62
|
+
const expected = new Map([
|
|
63
|
+
[1, '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079'],
|
|
64
|
+
[
|
|
65
|
+
1000,
|
|
66
|
+
'684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51',
|
|
67
|
+
],
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
for (let iteration = 1; iteration <= 1000; iteration++) {
|
|
71
|
+
const previousPrivateKey = privateKey
|
|
72
|
+
privateKey = engine.getSharedSecret(privateKey, publicKey)
|
|
73
|
+
publicKey = previousPrivateKey
|
|
74
|
+
if (expected.has(iteration))
|
|
75
|
+
expect(toHex(privateKey), `iteration ${iteration}`).toBe(
|
|
76
|
+
expected.get(iteration),
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('behavior: accepts masked and noncanonical u-coordinates', async () => {
|
|
82
|
+
const engine = await X25519.engine()
|
|
83
|
+
const publicKey = x25519.getPublicKey(bobPrivateKey)
|
|
84
|
+
const masked = publicKey.slice()
|
|
85
|
+
masked[31]! |= 0x80
|
|
86
|
+
const noncanonicalBasePoint = fromHex(`f6${'ff'.repeat(30)}7f`)
|
|
87
|
+
|
|
88
|
+
expect(engine.getSharedSecret(alicePrivateKey, masked)).toEqual(
|
|
89
|
+
engine.getSharedSecret(alicePrivateKey, publicKey),
|
|
90
|
+
)
|
|
91
|
+
expect(
|
|
92
|
+
engine.getSharedSecret(alicePrivateKey, noncanonicalBasePoint),
|
|
93
|
+
).toEqual(engine.getPublicKey(alicePrivateKey))
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('behavior: rejects low-order and malformed public keys', async () => {
|
|
97
|
+
const engine = await X25519.engine()
|
|
98
|
+
|
|
99
|
+
expect(x25519LowOrder).toEqual(
|
|
100
|
+
JSON.parse(
|
|
101
|
+
fs.readFileSync(
|
|
102
|
+
new URL(
|
|
103
|
+
'../../../test/vectors/x25519/low-order.json',
|
|
104
|
+
import.meta.url,
|
|
105
|
+
),
|
|
106
|
+
'utf8',
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
expect(x25519LowOrder).toHaveLength(7)
|
|
111
|
+
for (const vector of x25519LowOrder) {
|
|
112
|
+
const publicKey = fromHex(vector.publicKey)
|
|
113
|
+
const highBitAlias = publicKey.slice()
|
|
114
|
+
highBitAlias[31]! |= 0x80
|
|
115
|
+
for (const value of [publicKey, highBitAlias])
|
|
116
|
+
expect(() =>
|
|
117
|
+
engine.getSharedSecret(alicePrivateKey, value),
|
|
118
|
+
).toThrowError(`invalid private or public key received`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const size of [0, 1, 31, 33, 64]) {
|
|
122
|
+
expect(() => engine.getPublicKey(new Uint8Array(size))).toThrowError(
|
|
123
|
+
`X25519 private key must be 32 bytes, got ${size}`,
|
|
124
|
+
)
|
|
125
|
+
expect(() =>
|
|
126
|
+
engine.getSharedSecret(alicePrivateKey, new Uint8Array(size)),
|
|
127
|
+
).toThrowError(`X25519 public key must be 32 bytes, got ${size}`)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('behavior: respects subviews, ownership, and cleanup', async () => {
|
|
132
|
+
const engine = await X25519.engine()
|
|
133
|
+
const privateKey = offsetView(alicePrivateKey)
|
|
134
|
+
const publicKey = offsetView(x25519.getPublicKey(bobPrivateKey))
|
|
135
|
+
const before = {
|
|
136
|
+
privateKey: privateKey.slice(),
|
|
137
|
+
publicKey: publicKey.slice(),
|
|
138
|
+
}
|
|
139
|
+
const sharedSecret = engine.getSharedSecret(privateKey, publicKey)
|
|
140
|
+
const snapshot = sharedSecret.slice()
|
|
141
|
+
|
|
142
|
+
engine.getPublicKey(new Uint8Array(32).fill(0xa5))
|
|
143
|
+
expect(sharedSecret).toEqual(snapshot)
|
|
144
|
+
expect({ privateKey, publicKey }).toEqual(before)
|
|
145
|
+
|
|
146
|
+
const module = await crypto25519.load()
|
|
147
|
+
expect(
|
|
148
|
+
module
|
|
149
|
+
.view()
|
|
150
|
+
.subarray(module.heapBase, module.heapBase + 96)
|
|
151
|
+
.every((byte) => byte === 0),
|
|
152
|
+
).toBe(true)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('behavior: clears both keys and the shared secret after a late trap', async () => {
|
|
156
|
+
const module = await internal.instantiate<crypto25519.Exports>(wasmBase64)
|
|
157
|
+
const publicKey = x25519.getPublicKey(bobPrivateKey)
|
|
158
|
+
const exports: crypto25519.Exports = {
|
|
159
|
+
...module.exports,
|
|
160
|
+
x25519_get_shared_secret(...parameters) {
|
|
161
|
+
module.exports.x25519_get_shared_secret(...parameters)
|
|
162
|
+
throw new WebAssembly.RuntimeError('forced late trap')
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
expect(() =>
|
|
167
|
+
x25519_internal.getSharedSecret(
|
|
168
|
+
{ ...module, exports },
|
|
169
|
+
alicePrivateKey,
|
|
170
|
+
publicKey,
|
|
171
|
+
),
|
|
172
|
+
).toThrow(WebAssembly.RuntimeError)
|
|
173
|
+
expect(
|
|
174
|
+
module
|
|
175
|
+
.view()
|
|
176
|
+
.subarray(module.heapBase, module.heapBase + 96)
|
|
177
|
+
.every((byte) => byte === 0),
|
|
178
|
+
).toBe(true)
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
function fromHex(value: string): Uint8Array {
|
|
183
|
+
return Uint8Array.from(Buffer.from(value, 'hex'))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function offsetView(value: Uint8Array): Uint8Array {
|
|
187
|
+
const bytes = new Uint8Array(value.length + 4).fill(0xff)
|
|
188
|
+
bytes.set(value, 2)
|
|
189
|
+
return bytes.subarray(2, -2)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function toHex(value: Uint8Array): string {
|
|
193
|
+
return Buffer.from(value).toString('hex')
|
|
194
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Generated by `pnpm wasm:build --target=runtime-test`. Do not edit.
|
|
2
|
+
//
|
|
3
|
+
// Compiled from wasm/test/ox_rt.c, wasm/src/ox_rt.c
|
|
4
|
+
// with wasi-sdk 25.0 + binaryen 123.
|
|
5
|
+
//
|
|
6
|
+
// `pnpm wasm:check` rebuilds this file and fails if the bytes differ, so any
|
|
7
|
+
// change to the C or to `wasm/targets.ts` must be followed by `pnpm wasm:build`.
|
|
8
|
+
|
|
9
|
+
/** Base64-encoded WASM binary. */
|
|
10
|
+
export const wasmBase64 =
|
|
11
|
+
'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'
|