mirror of
https://github.com/pezkuwichain/pezkuwi-wasm.git
synced 2026-11-05 00:41:08 +00:00
7.5.15-7.5.18 shipped a wasm built from a separate sr25519-only module
(4 exports) and replaced every other function in bundle.ts with a stub
that throws "not yet implemented": ed25519 pair/sign/verify, sr25519
agree and hard/soft derivation, VRF, and all of secp256k1. Because the
wasm still initialised, isReady() was true and @pezkuwi/util-crypto
called those stubs instead of falling back to JS, so ed25519 and
ethereum accounts and any // derivation failed at runtime wherever the
package is used. bip39Generate threw as well, and bip39ToSeed hashed with
SHA-256 where BIP39 specifies SHA-512.
The Rust in packages/wasm-crypto/src/rs already implemented all of it,
with sr25519 under the "bizinikiwi" context. This builds that crate and
binds it again:
- bundle.ts: the withWasm bindings for every ext_* export, as in
b61206c; getSigningContext() kept, returning "bizinikiwi".
- wasm-bridge: wasm-bindgen 0.2.90 passes an alignment to
__wbindgen_malloc/__wbindgen_free (without it Rust aborts with
`unreachable`); __wbindgen_memory returns a heap index; the two-argument
getRandomValues import takes the crypto object and a Uint8Array by heap
index, not a pointer and length.
- build-wasm.sh: source rust-version.sh relative to the script; strip the
`import * as wbg` line newer wasm2js emits and call asmFunc({ wbg }),
which is the shape its output expects.
- pack-wasm-base.mjs: also write build/bytes.js. The package exports
./bytes from there for ESM, and it was still the old sr25519-only wasm.
The committed src copies are now empty placeholders, so a build that
skips the wasm step fails instead of shipping a stale binary.
- Tests: the sr25519 known-signature vectors were substrate-context
vectors; they are replaced by vectors signed by 7.5.18, plus a test that
the same seed yields the same public key. A rename had changed the
substrate-bip39 vector's password to "Bizinikiwi"; it is "Substrate".
Compatibility with 7.5.18, measured in separate processes:
mnemonic -> mini secret -> public key is identical for every phrase with
an empty or ASCII password (12/12). It differs when the BIP39 password has
characters NFKD changes: 7.5.18 normalised it, while the Rust crate and
util-crypto's JS path do not, so this returns to the canonical result.
Rust 43/43, JS 55/55, wasm and asm.js runners pass. With this build in
place, pezkuwi-extension's suite goes from 19 failing to 3 (the 3 left
are unrelated sorting tests).
All wasm packages move to 7.5.19 and pin each other at 7.5.19.
214 lines
7.7 KiB
JavaScript
214 lines
7.7 KiB
JavaScript
// Copyright 2019-2026 @pezkuwi/wasm-crypto authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
/* global it */
|
|
|
|
import crypto from 'crypto';
|
|
|
|
import { assert, hexToU8a, stringToU8a, u8aToHex } from '@pezkuwi/util';
|
|
|
|
/**
|
|
* @internal
|
|
* @param {*} pair
|
|
*/
|
|
function extractKeys (pair) {
|
|
return [pair, pair.slice(64), pair.slice(0, 64)];
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* @param {*} wasm
|
|
*/
|
|
function randomPair (wasm) {
|
|
return extractKeys(wasm.sr25519KeypairFromSeed(crypto.randomBytes(32)));
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519PairFromSeed (wasm) {
|
|
it('creates a known pair from a known seed', () => {
|
|
const pair = wasm.sr25519KeypairFromSeed(stringToU8a('12345678901234567890123456789012'));
|
|
|
|
// console.log('\tSEC', u8aToHex(pair.slice(0, 64)));
|
|
// console.log('\tPUB', u8aToHex(pair.slice(64)));
|
|
|
|
assert(u8aToHex(pair) === '0xf0106660c3dda23f16daa9ac5b811b963077f5bc0af89f85804f0de8e424f050f98d66f39442506ff947fd911f18c7a7a5da639a63e8d3b4e233f74143d951c1741c08a06f41c596608f6774259bd9043304adfa5d3eea62760bd9be97634d63', 'ERROR: pairFromSeed() does not match');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519DevFromSeed (wasm) {
|
|
it('creates a known development pair', () => {
|
|
const pair = wasm.sr25519KeypairFromSeed(hexToU8a('0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e'));
|
|
|
|
// console.log('\tSEC', u8aToHex(pair.slice(0, 64)));
|
|
// console.log('\tPUB', u8aToHex(pair.slice(64)));
|
|
|
|
assert(u8aToHex(pair) === '0x28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca3446ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a', 'ERROR: devFromSeed() does not match');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519VerifyExisting (wasm) {
|
|
it('verifies a known signature', () => {
|
|
// Signed by @pezkuwi/wasm-crypto 7.5.18 under the "bizinikiwi" context, the build
|
|
// whose signatures the chain has been accepting. Same vector as src/rs/sr25519.rs.
|
|
const PK = hexToU8a('0x3288fbbb40584c61d8ee04e176fa339a1f1f6273b2a66d2eb12aafd1eb7b5e19');
|
|
const MESSAGE = stringToU8a('I hereby verify that I control this key');
|
|
const SIGNATURE = hexToU8a('0x065565b3c4cd1f2ff097a6372a67a8f7b3153e703378bf0acde013dcda79221485b3e7bf8d45e356a5a0fbf9cc332be54bff10158970daa5a5c55b15fb834181');
|
|
|
|
const isValid = wasm.sr25519Verify(SIGNATURE, MESSAGE, PK);
|
|
|
|
// console.log('\tRES', isValid);
|
|
|
|
assert(isValid, 'ERROR: Unable to verify signature');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519SignDeterministic (wasm) {
|
|
it('creates non-deterministic signatures', () => {
|
|
const [, pk, sk] = randomPair(wasm);
|
|
const sig1 = u8aToHex(wasm.sr25519Sign(pk, sk, stringToU8a('this is a message')));
|
|
const sig2 = u8aToHex(wasm.sr25519Sign(pk, sk, stringToU8a('this is a message')));
|
|
|
|
// console.log('\tSG1', sig1);
|
|
// console.log('\tSG2', sig2);
|
|
|
|
assert(sig1 !== sig2, 'ERROR: Signatures are deterministic');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519SignAndVerify (wasm) {
|
|
it('verifies a created signature', () => {
|
|
const [, pk, sk] = randomPair(wasm);
|
|
const signature = wasm.sr25519Sign(pk, sk, stringToU8a('this is a message'));
|
|
const isValid = wasm.sr25519Verify(signature, stringToU8a('this is a message'), pk);
|
|
|
|
// console.log('\tSIG', u8aToHex(signature));
|
|
// console.log('\tRES', isValid);
|
|
|
|
assert(isValid, 'ERROR: Unable to verify signature');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519DeriveHard (wasm) {
|
|
it('derives using a hard path', () => {
|
|
const [pair] = randomPair(wasm);
|
|
const derived = wasm.sr25519DeriveKeypairHard(pair, hexToU8a('0x0c666f6f00000000000000000000000000000000000000000000000000000000'));
|
|
|
|
// console.log('\tSEC', u8aToHex(derived.slice(0, 64)));
|
|
// console.log('\tPUB', u8aToHex(derived.slice(64)));
|
|
|
|
assert(derived.length === 96, 'Derived key length mismatch');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519DeriveHardKnown (wasm) {
|
|
it('derives a known hard key', () => {
|
|
const derived = wasm.sr25519DeriveKeypairHard(hexToU8a('0x28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca3446ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a'), hexToU8a('0x14416c6963650000000000000000000000000000000000000000000000000000'));
|
|
const publicKey = u8aToHex(derived.slice(64));
|
|
|
|
// console.log('\tSEC', u8aToHex(derived.slice(0, 64)));
|
|
// console.log('\tPUB', publicKey);
|
|
|
|
assert(publicKey === '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', 'Unmatched resulting public keys');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519DeriveSoft (wasm) {
|
|
it('derives using a soft path', () => {
|
|
const [pair] = randomPair(wasm);
|
|
const derived = wasm.sr25519DeriveKeypairSoft(pair, hexToU8a('0x0c666f6f00000000000000000000000000000000000000000000000000000000'));
|
|
|
|
// console.log('\tSEC', u8aToHex(derived.slice(0, 64)));
|
|
// console.log('\tPUB', u8aToHex(derived.slice(64)));
|
|
|
|
assert(derived.length === 96, 'Derived key length mismatch');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519DeriveSoftKnown (wasm) {
|
|
it('derives a known soft key', () => {
|
|
const derived = wasm.sr25519DeriveKeypairSoft(hexToU8a('0x28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca3446ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a'), hexToU8a('0x0c666f6f00000000000000000000000000000000000000000000000000000000'));
|
|
const publicKey = u8aToHex(derived.slice(64));
|
|
|
|
// console.log('\tSEC', u8aToHex(derived.slice(0, 64)));
|
|
// console.log('\tPUB', publicKey);
|
|
|
|
assert(publicKey === '0x40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a', 'Unmatched resulting public keys');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519DeriveSoftPubkey (wasm) {
|
|
it('derives a known soft publicKey', () => {
|
|
const derived = u8aToHex(wasm.sr25519DerivePublicSoft(hexToU8a('0x46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a'), hexToU8a('0x0c666f6f00000000000000000000000000000000000000000000000000000000')));
|
|
|
|
// console.log('\tPUB', derived);
|
|
|
|
assert(derived === '0x40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a', 'Unmatched resulting public keys');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519KeyAgreement (wasm) {
|
|
it('allows for agreements', () => {
|
|
const pair1 = wasm.sr25519KeypairFromSeed(hexToU8a('0x3b44c558f9a8f3dc9690d53088558c1ba2529b677e316c6054d1852595b004af'));
|
|
const pair2 = wasm.sr25519KeypairFromSeed(hexToU8a('0x923b80f79c6981fe756272128ec236eb510ae016dd20bdccbd77a9416b7ab94e'));
|
|
const [, pk1, sk1] = extractKeys(pair1);
|
|
const [, pk2, sk2] = extractKeys(pair2);
|
|
|
|
assert(u8aToHex(wasm.sr25519Agree(pk1, sk2)) === '0xfa7b90001b790fe42ff78b8cd86f6cf7a7c0a70b72f6b4c771b5d67536450222', 'Unmatched agreement keys');
|
|
assert(u8aToHex(wasm.sr25519Agree(pk2, sk1)) === '0xfa7b90001b790fe42ff78b8cd86f6cf7a7c0a70b72f6b4c771b5d67536450222', 'Unmatched agreement keys');
|
|
|
|
for (let i = 0; i < 256; i++) {
|
|
const [, pk1, sk1] = randomPair(wasm);
|
|
const [, pk2, sk2] = randomPair(wasm);
|
|
|
|
assert(u8aToHex(wasm.sr25519Agree(pk1, sk2)) === u8aToHex(wasm.sr25519Agree(pk2, sk1)), 'Unmatched agreement keys');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {*} wasm
|
|
*/
|
|
export function sr25519Benchmark (wasm) {
|
|
it('runs a verification benchmark', () => {
|
|
const MESSAGE = stringToU8a('this is a message');
|
|
|
|
for (let i = 0; i < 256; i++) {
|
|
const [, pk, sk] = randomPair(wasm);
|
|
|
|
assert(wasm.sr25519Verify(wasm.sr25519Sign(pk, sk, MESSAGE), MESSAGE, pk), 'ERROR: Unable to verify signature');
|
|
}
|
|
});
|
|
}
|