mirror of
https://github.com/pezkuwichain/pezkuwi-common.git
synced 2026-11-04 23:11:06 +00:00
Initial rebrand: @polkadot -> @pezkuwi (14 packages)
- Package namespace: @polkadot/* -> @pezkuwi/* - Repository: polkadot-js/common -> pezkuwichain/pezkuwi-common - Author: Pezkuwi Team <team@pezkuwichain.io> Core packages: - @pezkuwi/util (utilities) - @pezkuwi/util-crypto (crypto primitives) - @pezkuwi/keyring (account management) - @pezkuwi/networks (chain metadata) - @pezkuwi/hw-ledger (Ledger hardware wallet) - @pezkuwi/x-* (10 polyfill packages) Total: 14 packages Upstream: polkadot-js/common v14.0.1
This commit is contained in:
commit
ec06da0ebc
687 files changed
+48096
No files matched your search
@@ -0,0 +1,54 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { mnemonicToLegacySeed } from '@pezkuwi/util-crypto';
|
||||
|
||||
import { hdEthereum } from './index.js';
|
||||
|
||||
describe('hdEthereum', (): void => {
|
||||
const PHRASE = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
const derivationPath = 'm/44\'/60\'/0\'/0/0';
|
||||
const PUBLIC = new Uint8Array([
|
||||
3, 118, 64, 77, 247, 27, 4, 157,
|
||||
236, 206, 251, 221, 230, 244, 154, 147,
|
||||
189, 131, 249, 169, 102, 78, 3, 185,
|
||||
153, 19, 89, 40, 24, 25, 139, 131,
|
||||
93
|
||||
]);
|
||||
const SECRET = new Uint8Array([
|
||||
166, 162, 203, 17, 2, 206, 110, 176,
|
||||
18, 102, 230, 144, 90, 158, 25, 232,
|
||||
43, 180, 176, 49, 189, 149, 3, 71,
|
||||
243, 228, 223, 104, 125, 132, 58, 228
|
||||
]
|
||||
);
|
||||
const PUBLICDERIVED = new Uint8Array([
|
||||
3, 129, 53, 27, 27, 70, 210, 96,
|
||||
43, 9, 146, 187, 93, 85, 49, 249,
|
||||
193, 105, 107, 8, 18, 254, 178, 83,
|
||||
75, 104, 132, 173, 196, 126, 46, 29,
|
||||
139
|
||||
]);
|
||||
const SECRETDERIVED = new Uint8Array([
|
||||
7, 13, 195, 17, 115, 0, 1, 25,
|
||||
24, 226, 107, 2, 23, 105, 69, 204,
|
||||
21, 195, 213, 72, 207, 73, 253, 132,
|
||||
24, 217, 127, 147, 175, 105, 158, 70
|
||||
]);
|
||||
|
||||
it('derives the right key pair from a mnemonic', (): void => {
|
||||
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64));
|
||||
|
||||
expect(key.publicKey).toEqual(PUBLIC);
|
||||
expect(key.secretKey).toEqual(SECRET);
|
||||
});
|
||||
|
||||
it('derives the right key pair from a mnemonic and a derivation path', (): void => {
|
||||
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64), derivationPath);
|
||||
|
||||
expect(key.publicKey).toEqual(PUBLICDERIVED);
|
||||
expect(key.secretKey).toEqual(SECRETDERIVED);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Keypair } from '../../types.js';
|
||||
|
||||
import { bnToU8a, stringToU8a, u8aConcat } from '@pezkuwi/util';
|
||||
|
||||
import { BN_BE_32_OPTS } from '../../bn.js';
|
||||
import { hmacShaAsU8a } from '../../hmac/index.js';
|
||||
import { secp256k1PairFromSeed, secp256k1PrivateKeyTweakAdd } from '../../secp256k1/index.js';
|
||||
import { HARDENED, hdValidatePath } from '../validatePath.js';
|
||||
|
||||
interface CodedKeypair extends Keypair {
|
||||
chainCode: Uint8Array;
|
||||
}
|
||||
|
||||
const MASTER_SECRET = stringToU8a('Bitcoin seed');
|
||||
|
||||
function createCoded (secretKey: Uint8Array, chainCode: Uint8Array): CodedKeypair {
|
||||
return {
|
||||
chainCode,
|
||||
publicKey: secp256k1PairFromSeed(secretKey).publicKey,
|
||||
secretKey
|
||||
};
|
||||
}
|
||||
|
||||
function deriveChild (hd: CodedKeypair, index: number): CodedKeypair {
|
||||
const indexBuffer = bnToU8a(index, BN_BE_32_OPTS);
|
||||
const data = index >= HARDENED
|
||||
? u8aConcat(new Uint8Array(1), hd.secretKey, indexBuffer)
|
||||
: u8aConcat(hd.publicKey, indexBuffer);
|
||||
|
||||
try {
|
||||
const I = hmacShaAsU8a(hd.chainCode, data, 512);
|
||||
|
||||
return createCoded(
|
||||
secp256k1PrivateKeyTweakAdd(hd.secretKey, I.slice(0, 32)),
|
||||
I.slice(32)
|
||||
);
|
||||
} catch {
|
||||
// In case parse256(IL) >= n or ki == 0, proceed with the next value for i
|
||||
return deriveChild(hd, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function hdEthereum (seed: Uint8Array, path = ''): Keypair {
|
||||
const I = hmacShaAsU8a(MASTER_SECRET, seed, 512);
|
||||
let hd = createCoded(I.slice(0, 32), I.slice(32));
|
||||
|
||||
if (!path || path === 'm' || path === 'M' || path === "m'" || path === "M'") {
|
||||
return hd;
|
||||
}
|
||||
|
||||
if (!hdValidatePath(path)) {
|
||||
throw new Error('Invalid derivation path');
|
||||
}
|
||||
|
||||
const parts = path.split('/').slice(1);
|
||||
|
||||
for (const p of parts) {
|
||||
hd = deriveChild(hd, parseInt(p, 10) + (
|
||||
(p.length > 1) && p.endsWith("'")
|
||||
? HARDENED
|
||||
: 0
|
||||
));
|
||||
}
|
||||
|
||||
return hd;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { hdEthereum } from './ethereum/index.js';
|
||||
export { hdLedger } from './ledger/index.js';
|
||||
export { hdValidatePath } from './validatePath.js';
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BN_EIGHT, bnToU8a, u8aConcat, u8aToBn } from '@pezkuwi/util';
|
||||
|
||||
import { BN_LE_32_OPTS, BN_LE_512_OPTS, BN_LE_OPTS } from '../../bn.js';
|
||||
import { hmacShaAsU8a } from '../../hmac/index.js';
|
||||
|
||||
// performs hard-only derivation on the xprv
|
||||
export function ledgerDerivePrivate (xprv: Uint8Array, index: number): Uint8Array {
|
||||
const kl = xprv.subarray(0, 32);
|
||||
const kr = xprv.subarray(32, 64);
|
||||
const cc = xprv.subarray(64, 96);
|
||||
const data = u8aConcat([0], kl, kr, bnToU8a(index, BN_LE_32_OPTS));
|
||||
const z = hmacShaAsU8a(cc, data, 512);
|
||||
|
||||
data[0] = 0x01;
|
||||
|
||||
return u8aConcat(
|
||||
bnToU8a(
|
||||
u8aToBn(kl, BN_LE_OPTS).iadd(
|
||||
u8aToBn(z.subarray(0, 28), BN_LE_OPTS).imul(BN_EIGHT)
|
||||
),
|
||||
BN_LE_512_OPTS
|
||||
).subarray(0, 32),
|
||||
bnToU8a(
|
||||
u8aToBn(kr, BN_LE_OPTS).iadd(
|
||||
u8aToBn(z.subarray(32, 64), BN_LE_OPTS)
|
||||
),
|
||||
BN_LE_512_OPTS
|
||||
).subarray(0, 32),
|
||||
hmacShaAsU8a(cc, data, 512).subarray(32, 64)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { u8aToHex } from '@pezkuwi/util';
|
||||
|
||||
import { hdLedger } from '../index.js';
|
||||
|
||||
const MNE_0 = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
||||
const MNE_1 = 'open jelly jeans corn ketchup supreme brief element armed lens vault weather original scissors rug priority vicious lesson raven spot gossip powder person volcano';
|
||||
const MNE_P = `${MNE_1} testing`;
|
||||
|
||||
const TESTS = {
|
||||
Kusama: {
|
||||
slip44: 0x01b2,
|
||||
tests: [
|
||||
{
|
||||
ed25519: '0x98cb4e14e0e08ea876f88d728545ea7572dc07dbbe69f1731c418fb827e69d41',
|
||||
index: [0, 0],
|
||||
mnemonic: MNE_0
|
||||
},
|
||||
{
|
||||
ed25519: '0x70e9010e84c81095aaa5f63b1c5a6a66a1dcbec017a23c2f3b7a1b08fe5ea65a',
|
||||
index: [0, 0],
|
||||
mnemonic: MNE_1
|
||||
},
|
||||
{
|
||||
ed25519: '0xf06730efb1e6ea59ac752a7c3620fade3909062fb88597856cc3af72045fa65a',
|
||||
index: [5, 7],
|
||||
mnemonic: MNE_1
|
||||
}
|
||||
]
|
||||
},
|
||||
Polkadot: {
|
||||
slip44: 0x0162,
|
||||
tests: [
|
||||
{
|
||||
ed25519: '0xe8c68348586d53e4e8d1a864b0e4e17c75e4eb06e0c63c1432bef2ba29e69d41',
|
||||
index: [0, 0],
|
||||
mnemonic: MNE_0
|
||||
},
|
||||
{
|
||||
ed25519: '0x3890e8db837eba3f8f25215c753e1091062298ce671a51441e7ef89a7adc4f48',
|
||||
index: [0, 0],
|
||||
mnemonic: MNE_P
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
describe('ledgerDerive', (): void => {
|
||||
Object.entries(TESTS).forEach(([network, { slip44, tests }]): void => {
|
||||
tests.forEach(({ ed25519, index: [account, address], mnemonic }, index): void => {
|
||||
it(`derives a known ed25519 seed for ${network} (${index})`, (): void => {
|
||||
expect(u8aToHex(
|
||||
hdLedger(mnemonic, `m/44'/${slip44}'/${account}'/0'/${address}'`)
|
||||
.secretKey
|
||||
.slice(0, 32)
|
||||
)).toEqual(ed25519);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Keypair } from '../../types.js';
|
||||
|
||||
import { ed25519PairFromSeed } from '../../ed25519/index.js';
|
||||
import { mnemonicValidate } from '../../mnemonic/index.js';
|
||||
import { HARDENED, hdValidatePath } from '../validatePath.js';
|
||||
import { ledgerDerivePrivate } from './derivePrivate.js';
|
||||
import { ledgerMaster } from './master.js';
|
||||
|
||||
export function hdLedger (_mnemonic: string, path: string): Keypair {
|
||||
const words = _mnemonic
|
||||
.split(' ')
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s);
|
||||
|
||||
if (![12, 24, 25].includes(words.length)) {
|
||||
throw new Error('Expected a mnemonic with 24 words (or 25 including a password)');
|
||||
}
|
||||
|
||||
const [mnemonic, password] = words.length === 25
|
||||
? [words.slice(0, 24).join(' '), words[24]]
|
||||
: [words.join(' '), ''];
|
||||
|
||||
if (!mnemonicValidate(mnemonic)) {
|
||||
throw new Error('Invalid mnemonic passed to ledger derivation');
|
||||
} else if (!hdValidatePath(path)) {
|
||||
throw new Error('Invalid derivation path');
|
||||
}
|
||||
|
||||
const parts = path.split('/').slice(1);
|
||||
let seed = ledgerMaster(mnemonic, password);
|
||||
|
||||
for (const p of parts) {
|
||||
const n = parseInt(p.replace(/'$/, ''), 10);
|
||||
|
||||
seed = ledgerDerivePrivate(seed, (n < HARDENED) ? (n + HARDENED) : n);
|
||||
}
|
||||
|
||||
return ed25519PairFromSeed(seed.slice(0, 32));
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { u8aToHex } from '@pezkuwi/util';
|
||||
|
||||
import { ledgerMaster } from './master.js';
|
||||
|
||||
const MNEMONIC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
||||
const XPRV = '0x402b03cd9c8bed9ba9f9bd6cd9c315ce9fcc59c7c25d37c85a36096617e69d418e35cb4a3b737afd007f0688618f21a8831643c0e6c77fc33c06026d2a0fc93832596435e70647d7d98ef102a32ea40319ca8fb6c851d7346d3bd8f9d1492658';
|
||||
|
||||
describe('ledgerDerive', (): void => {
|
||||
it('derives a known master xprv', (): void => {
|
||||
expect(u8aToHex(
|
||||
ledgerMaster(MNEMONIC)
|
||||
)).toEqual(XPRV);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aConcat } from '@pezkuwi/util';
|
||||
|
||||
import { hmacShaAsU8a } from '../../hmac/index.js';
|
||||
import { mnemonicToSeedSync } from '../../mnemonic/bip39.js';
|
||||
|
||||
const ED25519_CRYPTO = 'ed25519 seed';
|
||||
|
||||
// gets an xprv from a mnemonic
|
||||
export function ledgerMaster (mnemonic: string, password?: string): Uint8Array {
|
||||
const seed = mnemonicToSeedSync(mnemonic, password);
|
||||
const chainCode = hmacShaAsU8a(ED25519_CRYPTO, new Uint8Array([1, ...seed]), 256);
|
||||
let priv;
|
||||
|
||||
while (!priv || (priv[31] & 0b0010_0000)) {
|
||||
priv = hmacShaAsU8a(ED25519_CRYPTO, priv || seed, 512);
|
||||
}
|
||||
|
||||
priv[0] &= 0b1111_1000;
|
||||
priv[31] &= 0b0111_1111;
|
||||
priv[31] |= 0b0100_0000;
|
||||
|
||||
return u8aConcat(priv, chainCode);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { hdValidatePath } from './index.js';
|
||||
|
||||
const VECTORS: [string, boolean][] = [
|
||||
["m/44'/60'/0'/0/0", true],
|
||||
['m/0', true],
|
||||
["m/123'", true],
|
||||
["n/44'/60'/0'/0/0", false],
|
||||
['m', false],
|
||||
['m/', false],
|
||||
["m/xyz'", false],
|
||||
['m/123x', false],
|
||||
['m/123"', false],
|
||||
["m/123''", false],
|
||||
["m/123'0'", false],
|
||||
[`m/${0x80000000}`, false],
|
||||
['m/-1', false]
|
||||
];
|
||||
|
||||
describe('hdValidatePath', (): void => {
|
||||
VECTORS.forEach(([path, result]): void => {
|
||||
it(`validates ${path} as ${result.toString()}`, (): void => {
|
||||
expect(hdValidatePath(path)).toEqual(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export const HARDENED = 0x80000000;
|
||||
|
||||
export function hdValidatePath (path: string): boolean {
|
||||
if (!path.startsWith('m/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parts = path.split('/').slice(1);
|
||||
|
||||
for (const p of parts) {
|
||||
const n = /^\d+'?$/.test(p)
|
||||
? parseInt(p.replace(/'$/, ''), 10)
|
||||
: Number.NaN;
|
||||
|
||||
if (isNaN(n) || (n >= HARDENED) || (n < 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in new issue
Block a user