ox 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/dist/core/Engine.d.ts +43 -11
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +40 -10
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Hash.d.ts +36 -0
- package/dist/core/Hash.d.ts.map +1 -1
- package/dist/core/Hash.js +35 -0
- package/dist/core/Hash.js.map +1 -1
- package/dist/core/internal/engine.d.ts +8 -8
- package/dist/core/internal/engine.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- 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 +29 -16
- package/dist/node/Engine.d.ts.map +1 -1
- package/dist/node/Engine.js +38 -14
- package/dist/node/Engine.js.map +1 -1
- package/dist/node/Hash.d.ts +17 -19
- package/dist/node/Hash.d.ts.map +1 -1
- package/dist/node/Hash.js +17 -19
- package/dist/node/Hash.js.map +1 -1
- 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 +95 -8
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +95 -8
- package/dist/node/index.js.map +1 -1
- package/dist/trusted-setups/Paths.d.ts +1 -0
- package/dist/trusted-setups/Paths.d.ts.map +1 -1
- package/dist/trusted-setups/Paths.js +1 -0
- package/dist/trusted-setups/Paths.js.map +1 -1
- package/dist/trusted-setups/Setups.d.ts +35 -0
- package/dist/trusted-setups/Setups.d.ts.map +1 -0
- package/dist/trusted-setups/Setups.js +29 -0
- package/dist/trusted-setups/Setups.js.map +1 -0
- package/dist/trusted-setups/index.d.ts +20 -0
- package/dist/trusted-setups/index.d.ts.map +1 -1
- package/dist/trusted-setups/index.js +20 -0
- package/dist/trusted-setups/index.js.map +1 -1
- package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
- package/dist/trusted-setups/internal/paths.js +1 -1
- package/dist/trusted-setups/internal/paths.js.map +1 -1
- package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
- package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
- package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
- package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
- 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 +36 -26
- package/dist/wasm/Engine.d.ts.map +1 -1
- package/dist/wasm/Engine.js +44 -25
- package/dist/wasm/Engine.js.map +1 -1
- package/dist/wasm/Hash.d.ts +13 -19
- package/dist/wasm/Hash.d.ts.map +1 -1
- package/dist/wasm/Hash.js +21 -27
- package/dist/wasm/Hash.js.map +1 -1
- package/dist/wasm/Keystore.d.ts +51 -0
- package/dist/wasm/Keystore.d.ts.map +1 -0
- package/dist/wasm/Keystore.js +51 -0
- package/dist/wasm/Keystore.js.map +1 -0
- package/dist/wasm/Kzg.d.ts +112 -0
- package/dist/wasm/Kzg.d.ts.map +1 -0
- package/dist/wasm/Kzg.js +333 -0
- package/dist/wasm/Kzg.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/Secp256k1.d.ts +55 -0
- package/dist/wasm/Secp256k1.d.ts.map +1 -0
- package/dist/wasm/Secp256k1.js +181 -0
- package/dist/wasm/Secp256k1.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 +124 -12
- package/dist/wasm/index.d.ts.map +1 -1
- package/dist/wasm/index.js +124 -12
- package/dist/wasm/index.js.map +1 -1
- 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/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 +4 -2
- package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
- package/dist/wasm/internal/hashes.wasm.js +4 -2
- package/dist/wasm/internal/hashes.wasm.js.map +1 -1
- package/dist/wasm/internal/instantiate.d.ts +12 -0
- package/dist/wasm/internal/instantiate.d.ts.map +1 -1
- package/dist/wasm/internal/instantiate.js +23 -7
- package/dist/wasm/internal/instantiate.js.map +1 -1
- package/dist/wasm/internal/keystore.d.ts +10 -0
- package/dist/wasm/internal/keystore.d.ts.map +1 -0
- package/dist/wasm/internal/keystore.js +10 -0
- package/dist/wasm/internal/keystore.js.map +1 -0
- package/dist/wasm/internal/kzg.d.ts +17 -0
- package/dist/wasm/internal/kzg.d.ts.map +1 -0
- package/dist/wasm/internal/kzg.js +8 -0
- package/dist/wasm/internal/kzg.js.map +1 -0
- package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
- package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/kzg.wasm.js +10 -0
- package/dist/wasm/internal/kzg.wasm.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/scrypt.d.ts +16 -0
- package/dist/wasm/internal/scrypt.d.ts.map +1 -0
- package/dist/wasm/internal/scrypt.js +78 -0
- package/dist/wasm/internal/scrypt.js.map +1 -0
- package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
- package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/scrypt.wasm.js +14 -0
- package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
- package/dist/wasm/internal/secp256k1.d.ts +16 -0
- package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
- package/dist/wasm/internal/secp256k1.js +20 -0
- package/dist/wasm/internal/secp256k1.js.map +1 -0
- package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
- package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/secp256k1.wasm.js +10 -0
- package/dist/wasm/internal/secp256k1.wasm.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/package.json +64 -2
- package/src/core/Engine.ts +84 -11
- package/src/core/Hash.ts +59 -0
- package/src/core/_test/Block.test.ts +9 -2
- package/src/core/_test/Engine.test-d.ts +56 -0
- package/src/core/_test/Engine.test.ts +95 -0
- package/src/core/_test/Hash.test-d.ts +18 -0
- package/src/core/_test/Hash.test.ts +100 -1
- package/src/core/_test/Hash.vectors.test.ts +7 -0
- package/src/core/_test/Transaction.test.ts +11 -2
- package/src/core/internal/engine.ts +8 -8
- package/src/index.ts +5 -2
- package/src/node/Ed25519.ts +87 -0
- package/src/node/Engine.ts +79 -23
- package/src/node/Hash.ts +25 -30
- 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 +62 -22
- package/src/node/_test/Hash.test-d.ts +16 -9
- package/src/node/_test/Hash.test.ts +10 -11
- 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 +100 -8
- package/src/trusted-setups/Paths.ts +1 -0
- package/src/trusted-setups/README.md +39 -0
- package/src/trusted-setups/Setups.ts +46 -0
- package/src/trusted-setups/_test/Paths.test.ts +16 -0
- package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
- package/src/trusted-setups/_test/Setups.test.ts +43 -0
- package/src/trusted-setups/_test/index.test.ts +1 -0
- package/src/trusted-setups/index.ts +21 -0
- package/src/trusted-setups/internal/paths.ts +4 -1
- package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
- package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
- package/src/version.ts +1 -1
- package/src/wasm/Ed25519.ts +114 -0
- package/src/wasm/Engine.ts +78 -31
- package/src/wasm/Hash.bench.ts +2 -2
- package/src/wasm/Hash.ts +29 -50
- package/src/wasm/Keystore.bench.ts +26 -0
- package/src/wasm/Keystore.ts +66 -0
- package/src/wasm/Kzg.bench.ts +63 -0
- package/src/wasm/Kzg.ts +518 -0
- package/src/wasm/Mnemonic.ts +44 -0
- package/src/wasm/Secp256k1.bench.ts +79 -0
- package/src/wasm/Secp256k1.ts +248 -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 +29 -0
- package/src/wasm/_test/Engine.test.ts +59 -25
- package/src/wasm/_test/Hash.browser.test.ts +14 -7
- package/src/wasm/_test/Hash.fuzz.ts +58 -23
- package/src/wasm/_test/Hash.test-d.ts +16 -10
- package/src/wasm/_test/Hash.test.ts +148 -48
- package/src/wasm/_test/Hash.vectors.test.ts +15 -8
- package/src/wasm/_test/Keystore.browser.test.ts +73 -0
- package/src/wasm/_test/Keystore.fuzz.ts +163 -0
- package/src/wasm/_test/Keystore.test-d.ts +42 -0
- package/src/wasm/_test/Keystore.test.ts +477 -0
- package/src/wasm/_test/Keystore.vectors.test.ts +21 -0
- package/src/wasm/_test/Kzg.test-d.ts +19 -0
- package/src/wasm/_test/Kzg.test.ts +220 -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 +9 -0
- package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
- package/src/wasm/_test/Secp256k1.test.ts +354 -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/index.ts +130 -12
- 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/hashes.ts +29 -0
- package/src/wasm/internal/hashes.wasm.ts +5 -2
- package/src/wasm/internal/instantiate.ts +29 -9
- package/src/wasm/internal/keystore.ts +19 -0
- package/src/wasm/internal/kzg.ts +50 -0
- package/src/wasm/internal/kzg.wasm.ts +11 -0
- package/src/wasm/internal/mnemonic.ts +56 -0
- package/src/wasm/internal/pbkdf2.ts +86 -0
- package/src/wasm/internal/scrypt.ts +131 -0
- package/src/wasm/internal/scrypt.wasm.ts +17 -0
- package/src/wasm/internal/secp256k1.ts +62 -0
- package/src/wasm/internal/secp256k1.wasm.ts +11 -0
- package/src/wasm/internal/x25519.ts +29 -0
- package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
- package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
- package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js'
|
|
2
|
+
import { describe, expect, test } from 'vp/test'
|
|
3
|
+
import * as Secp256k1 from '../Secp256k1.js'
|
|
4
|
+
import * as secp256k1_internal from '../internal/secp256k1.js'
|
|
5
|
+
|
|
6
|
+
const privateKey = fromHex(
|
|
7
|
+
'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
|
|
8
|
+
)
|
|
9
|
+
const privateKeyB = fromHex(
|
|
10
|
+
'59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
|
|
11
|
+
)
|
|
12
|
+
const curveOrder = BigInt(
|
|
13
|
+
'0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141',
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
describe('engine', () => {
|
|
17
|
+
test('behavior: exposes only deterministic primitives', async () => {
|
|
18
|
+
const engine = await Secp256k1.engine()
|
|
19
|
+
|
|
20
|
+
expect(Object.keys(engine).sort()).toMatchInlineSnapshot(`
|
|
21
|
+
[
|
|
22
|
+
"getPublicKey",
|
|
23
|
+
"getSharedSecret",
|
|
24
|
+
"recoverPublicKey",
|
|
25
|
+
"sign",
|
|
26
|
+
"verify",
|
|
27
|
+
]
|
|
28
|
+
`)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('behavior: matches public keys and compressed shared points', async () => {
|
|
32
|
+
const engine = await Secp256k1.engine()
|
|
33
|
+
const publicKey = secp256k1.getPublicKey(privateKey, false)
|
|
34
|
+
const publicKeyB = secp256k1.getPublicKey(privateKeyB, false)
|
|
35
|
+
const compressedPublicKeyB = secp256k1.getPublicKey(privateKeyB, true)
|
|
36
|
+
const expected = secp256k1.getSharedSecret(privateKey, publicKeyB, true)
|
|
37
|
+
|
|
38
|
+
expect(engine.getPublicKey(privateKey)).toEqual(publicKey)
|
|
39
|
+
expect(engine.getSharedSecret(privateKey, publicKeyB)).toEqual(expected)
|
|
40
|
+
expect(engine.getSharedSecret(privateKey, compressedPublicKeyB)).toEqual(
|
|
41
|
+
expected,
|
|
42
|
+
)
|
|
43
|
+
expect(engine.getSharedSecret(privateKeyB, publicKey)).toEqual(expected)
|
|
44
|
+
expect(expected).toHaveLength(33)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('behavior: matches signing across payload and entropy boundaries', async () => {
|
|
48
|
+
const engine = await Secp256k1.engine()
|
|
49
|
+
const publicKey = secp256k1.getPublicKey(privateKey, false)
|
|
50
|
+
|
|
51
|
+
for (const size of [0, 1, 31, 32, 33, 63, 64, 65, 127, 1024, 8192])
|
|
52
|
+
for (const prehash of [false, true])
|
|
53
|
+
for (const extraEntropy of [
|
|
54
|
+
false,
|
|
55
|
+
new Uint8Array(),
|
|
56
|
+
Uint8Array.of(1),
|
|
57
|
+
new Uint8Array(32).fill(7),
|
|
58
|
+
new Uint8Array(100).fill(9),
|
|
59
|
+
] as const) {
|
|
60
|
+
const payload = bytes(size)
|
|
61
|
+
const signature = engine.sign(payload, privateKey, {
|
|
62
|
+
extraEntropy,
|
|
63
|
+
prehash,
|
|
64
|
+
})
|
|
65
|
+
const expected = secp256k1.sign(payload, privateKey, {
|
|
66
|
+
extraEntropy,
|
|
67
|
+
format: 'recovered',
|
|
68
|
+
lowS: true,
|
|
69
|
+
prehash,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
expect(signature).toEqual(expected)
|
|
73
|
+
expect(
|
|
74
|
+
engine.verify(signature.slice(1), payload, publicKey, { prehash }),
|
|
75
|
+
).toBe(true)
|
|
76
|
+
expect(signature[0]).toBeOneOf([0, 1])
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('behavior: generates host entropy for hedged signing', async () => {
|
|
81
|
+
const engine = await Secp256k1.engine()
|
|
82
|
+
const payload = bytes(32)
|
|
83
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
84
|
+
const signatureA = engine.sign(payload, privateKey, {
|
|
85
|
+
extraEntropy: true,
|
|
86
|
+
prehash: false,
|
|
87
|
+
})
|
|
88
|
+
const signatureB = engine.sign(payload, privateKey, {
|
|
89
|
+
extraEntropy: true,
|
|
90
|
+
prehash: false,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
expect(signatureA).not.toEqual(signatureB)
|
|
94
|
+
expect(
|
|
95
|
+
engine.verify(signatureA.slice(1), payload, publicKey, {
|
|
96
|
+
prehash: false,
|
|
97
|
+
}),
|
|
98
|
+
).toBe(true)
|
|
99
|
+
expect(
|
|
100
|
+
engine.verify(signatureB.slice(1), payload, publicKey, {
|
|
101
|
+
prehash: false,
|
|
102
|
+
}),
|
|
103
|
+
).toBe(true)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('behavior: recovers the public key with both parities', async () => {
|
|
107
|
+
const engine = await Secp256k1.engine()
|
|
108
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
109
|
+
const signatures = new Map<
|
|
110
|
+
number,
|
|
111
|
+
{ payload: Uint8Array; signature: Uint8Array }
|
|
112
|
+
>()
|
|
113
|
+
|
|
114
|
+
for (let index = 0; signatures.size < 2; index++) {
|
|
115
|
+
const payload = bytes(32, index)
|
|
116
|
+
const signature = engine.sign(payload, privateKey, {
|
|
117
|
+
extraEntropy: false,
|
|
118
|
+
prehash: false,
|
|
119
|
+
})
|
|
120
|
+
signatures.set(signature[0]!, { payload, signature })
|
|
121
|
+
if (index > 100)
|
|
122
|
+
throw new Error('could not produce both recovery parities')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const { payload, signature } of signatures.values())
|
|
126
|
+
expect(engine.recoverPublicKey(signature, payload)).toEqual(publicKey)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('behavior: rejects high-S signatures', async () => {
|
|
130
|
+
const engine = await Secp256k1.engine()
|
|
131
|
+
const payload = bytes(32)
|
|
132
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
133
|
+
const signature = engine
|
|
134
|
+
.sign(payload, privateKey, {
|
|
135
|
+
extraEntropy: false,
|
|
136
|
+
prehash: false,
|
|
137
|
+
})
|
|
138
|
+
.slice(1)
|
|
139
|
+
const highS = signature.slice()
|
|
140
|
+
highS.set(toBytes(curveOrder - toBigInt(signature.slice(32))), 32)
|
|
141
|
+
|
|
142
|
+
expect(
|
|
143
|
+
engine.verify(signature, payload, publicKey, { prehash: false }),
|
|
144
|
+
).toBe(true)
|
|
145
|
+
expect(engine.verify(highS, payload, publicKey, { prehash: false })).toBe(
|
|
146
|
+
false,
|
|
147
|
+
)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('behavior: matches large payload boundaries', async () => {
|
|
151
|
+
const engine = await Secp256k1.engine()
|
|
152
|
+
const payload = bytes(8193)
|
|
153
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
154
|
+
const truncatedSignature = secp256k1.sign(
|
|
155
|
+
payload.slice(0, 32),
|
|
156
|
+
privateKey,
|
|
157
|
+
{
|
|
158
|
+
extraEntropy: false,
|
|
159
|
+
format: 'compact',
|
|
160
|
+
lowS: true,
|
|
161
|
+
prehash: false,
|
|
162
|
+
},
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
expect(() =>
|
|
166
|
+
engine.sign(payload, privateKey, {
|
|
167
|
+
extraEntropy: false,
|
|
168
|
+
prehash: false,
|
|
169
|
+
}),
|
|
170
|
+
).toThrowError(
|
|
171
|
+
'Secp256k1 unhashed payload must not exceed 8192 bytes, got 8193',
|
|
172
|
+
)
|
|
173
|
+
expect(
|
|
174
|
+
engine.verify(new Uint8Array(64), payload, publicKey, {
|
|
175
|
+
prehash: false,
|
|
176
|
+
}),
|
|
177
|
+
).toBe(false)
|
|
178
|
+
expect(
|
|
179
|
+
secp256k1.verify(truncatedSignature, payload, publicKey, {
|
|
180
|
+
lowS: true,
|
|
181
|
+
prehash: false,
|
|
182
|
+
}),
|
|
183
|
+
).toBe(false)
|
|
184
|
+
expect(
|
|
185
|
+
engine.verify(truncatedSignature, payload, publicKey, { prehash: false }),
|
|
186
|
+
).toBe(false)
|
|
187
|
+
expect(() =>
|
|
188
|
+
engine.recoverPublicKey(new Uint8Array(65), payload),
|
|
189
|
+
).toThrowError(
|
|
190
|
+
'Secp256k1 unhashed payload must not exceed 8192 bytes, got 8193',
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
const signature = engine.sign(payload, privateKey, {
|
|
194
|
+
extraEntropy: false,
|
|
195
|
+
prehash: true,
|
|
196
|
+
})
|
|
197
|
+
expect(
|
|
198
|
+
engine.verify(signature.slice(1), payload, publicKey, { prehash: true }),
|
|
199
|
+
).toBe(true)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('behavior: rejects malformed keys and recovered signatures', async () => {
|
|
203
|
+
const engine = await Secp256k1.engine()
|
|
204
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
205
|
+
const payload = bytes(32)
|
|
206
|
+
|
|
207
|
+
for (const size of [0, 1, 31, 33, 64, 65]) {
|
|
208
|
+
if (size !== 32) {
|
|
209
|
+
expect(() => engine.getPublicKey(new Uint8Array(size))).toThrowError(
|
|
210
|
+
`Secp256k1 private key must be 32 bytes, got ${size}`,
|
|
211
|
+
)
|
|
212
|
+
expect(() =>
|
|
213
|
+
engine.sign(payload, new Uint8Array(size), {
|
|
214
|
+
extraEntropy: false,
|
|
215
|
+
prehash: false,
|
|
216
|
+
}),
|
|
217
|
+
).toThrowError(`Secp256k1 private key must be 32 bytes, got ${size}`)
|
|
218
|
+
}
|
|
219
|
+
if (size !== 64)
|
|
220
|
+
expect(() =>
|
|
221
|
+
engine.verify(new Uint8Array(size), payload, publicKey, {
|
|
222
|
+
prehash: false,
|
|
223
|
+
}),
|
|
224
|
+
).toThrowError(`Secp256k1 signature must be 64 bytes, got ${size}`)
|
|
225
|
+
if (size !== 65)
|
|
226
|
+
expect(() =>
|
|
227
|
+
engine.recoverPublicKey(new Uint8Array(size), payload),
|
|
228
|
+
).toThrowError(`Secp256k1 signature must be 65 bytes, got ${size}`)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const size of [0, 1, 32, 34, 64, 66])
|
|
232
|
+
expect(() =>
|
|
233
|
+
engine.getSharedSecret(privateKey, new Uint8Array(size)),
|
|
234
|
+
).toThrowError(`Secp256k1 public key must be 33 or 65 bytes, got ${size}`)
|
|
235
|
+
|
|
236
|
+
const invalidPrivateKeys = [
|
|
237
|
+
new Uint8Array(32),
|
|
238
|
+
toBytes(curveOrder),
|
|
239
|
+
toBytes(curveOrder + 1n),
|
|
240
|
+
]
|
|
241
|
+
for (const value of invalidPrivateKeys) {
|
|
242
|
+
expect(() => engine.getPublicKey(value)).toThrow(
|
|
243
|
+
Secp256k1.InvalidInputError,
|
|
244
|
+
)
|
|
245
|
+
expect(() => engine.getSharedSecret(value, publicKey)).toThrow(
|
|
246
|
+
Secp256k1.InvalidInputError,
|
|
247
|
+
)
|
|
248
|
+
expect(() =>
|
|
249
|
+
engine.sign(payload, value, {
|
|
250
|
+
extraEntropy: false,
|
|
251
|
+
prehash: false,
|
|
252
|
+
}),
|
|
253
|
+
).toThrow(Secp256k1.InvalidInputError)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const publicKeySize of [33, 65]) {
|
|
257
|
+
const invalidPublicKey = new Uint8Array(publicKeySize)
|
|
258
|
+
expect(() =>
|
|
259
|
+
engine.getSharedSecret(privateKey, invalidPublicKey),
|
|
260
|
+
).toThrow(Secp256k1.InvalidInputError)
|
|
261
|
+
expect(
|
|
262
|
+
engine.verify(new Uint8Array(64), payload, invalidPublicKey, {
|
|
263
|
+
prehash: false,
|
|
264
|
+
}),
|
|
265
|
+
).toBe(false)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const invalidRecovery = new Uint8Array(65)
|
|
269
|
+
invalidRecovery[0] = 2
|
|
270
|
+
expect(() => engine.recoverPublicKey(invalidRecovery, payload)).toThrow(
|
|
271
|
+
Secp256k1.InvalidInputError,
|
|
272
|
+
)
|
|
273
|
+
expect(() => engine.recoverPublicKey(new Uint8Array(65), payload)).toThrow(
|
|
274
|
+
Secp256k1.InvalidInputError,
|
|
275
|
+
)
|
|
276
|
+
expect(
|
|
277
|
+
engine.verify(new Uint8Array(64), payload, publicKey, {
|
|
278
|
+
prehash: false,
|
|
279
|
+
}),
|
|
280
|
+
).toBe(false)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('behavior: preserves subviews, input ownership, and output ownership', async () => {
|
|
284
|
+
const engine = await Secp256k1.engine()
|
|
285
|
+
const key = offsetView(privateKey)
|
|
286
|
+
const payload = offsetView(bytes(257))
|
|
287
|
+
const publicKey = offsetView(engine.getPublicKey(key))
|
|
288
|
+
const before = {
|
|
289
|
+
key: key.slice(),
|
|
290
|
+
payload: payload.slice(),
|
|
291
|
+
publicKey: publicKey.slice(),
|
|
292
|
+
}
|
|
293
|
+
const signature = engine.sign(payload, key, {
|
|
294
|
+
extraEntropy: false,
|
|
295
|
+
prehash: false,
|
|
296
|
+
})
|
|
297
|
+
const outputs = [
|
|
298
|
+
engine.getPublicKey(key),
|
|
299
|
+
engine.getSharedSecret(key, publicKey),
|
|
300
|
+
signature,
|
|
301
|
+
engine.recoverPublicKey(signature, payload),
|
|
302
|
+
]
|
|
303
|
+
const snapshots = outputs.map((output) => output.slice())
|
|
304
|
+
|
|
305
|
+
engine.sign(new Uint8Array(1024 * 1024), key, {
|
|
306
|
+
extraEntropy: false,
|
|
307
|
+
prehash: true,
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
expect(outputs).toEqual(snapshots)
|
|
311
|
+
expect({ key, payload, publicKey }).toEqual(before)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('behavior: clears the shared staging region', async () => {
|
|
315
|
+
const engine = await Secp256k1.engine()
|
|
316
|
+
const payload = new Uint8Array(1024).fill(0xa5)
|
|
317
|
+
const entropy = new Uint8Array(17).fill(0x5a)
|
|
318
|
+
engine.sign(payload, privateKey, {
|
|
319
|
+
extraEntropy: entropy,
|
|
320
|
+
prehash: false,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
const module = await secp256k1_internal.load()
|
|
324
|
+
const size = payload.length + 32 + entropy.length + 65
|
|
325
|
+
expect(
|
|
326
|
+
module
|
|
327
|
+
.view()
|
|
328
|
+
.subarray(module.heapBase, module.heapBase + size)
|
|
329
|
+
.every((byte) => byte === 0),
|
|
330
|
+
).toBe(true)
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
function bytes(length: number, offset = length): Uint8Array {
|
|
335
|
+
return Uint8Array.from({ length }, (_, index) => (index * 29 + offset) % 251)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function fromHex(value: string): Uint8Array {
|
|
339
|
+
return Uint8Array.from(Buffer.from(value, 'hex'))
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function offsetView(value: Uint8Array): Uint8Array {
|
|
343
|
+
const bytes = new Uint8Array(value.length + 4).fill(0xff)
|
|
344
|
+
bytes.set(value, 2)
|
|
345
|
+
return bytes.subarray(2, -2)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function toBigInt(value: Uint8Array): bigint {
|
|
349
|
+
return BigInt(`0x${Buffer.from(value).toString('hex')}`)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function toBytes(value: bigint): Uint8Array {
|
|
353
|
+
return fromHex(value.toString(16).padStart(64, '0'))
|
|
354
|
+
}
|
|
@@ -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
|
+
}
|