mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-19 19:55:51 +00:00
Switch to using `@noble/hashes`, `@noble/curves`, `@scure/base`, `@scure/bip32`, and `@scure/bip39`. This replaces `crypto` polyfills (such as `crypto-browserify`), `create-hash`, `elliptic`, `hash.js`, `bn.js` (both versions), and their many dependencies. This also means there are 33 less dependencies downloaded when running a fresh `npm install` and will make the project much easier to maintain. This reduces the bundle size by 44% (82kb minified and gzipped) over the current 3.0 branch as well as reducing the amount of configuration required to bundle. Closes #1814, #1817, #2272, and #2306 Co-authored-by: Caleb Kniffen <ckniffen@ripple.com>
91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import { randomBytes } from "@xrplf/isomorphic/utils";
|
|
|
|
function randomEntropy(): Buffer {
|
|
return Buffer.from(randomBytes(16));
|
|
}
|
|
|
|
function calculateChecksum(position: number, value: number): number {
|
|
return (value * (position * 2 + 1)) % 9;
|
|
}
|
|
|
|
function checkChecksum(
|
|
position: number,
|
|
value: number | string,
|
|
checksum?: number
|
|
): boolean {
|
|
let normalizedChecksum: number;
|
|
let normalizedValue: number;
|
|
|
|
if (typeof value === "string") {
|
|
if (value.length !== 6) {
|
|
throw new Error("value must have a length of 6");
|
|
}
|
|
normalizedChecksum = parseInt(value.slice(5), 10);
|
|
normalizedValue = parseInt(value.slice(0, 5), 10);
|
|
} else {
|
|
if (typeof checksum !== "number") {
|
|
throw new Error("checksum must be a number when value is a number");
|
|
}
|
|
normalizedChecksum = checksum;
|
|
normalizedValue = value;
|
|
}
|
|
return (normalizedValue * (position * 2 + 1)) % 9 === normalizedChecksum;
|
|
}
|
|
|
|
function entropyToSecret(entropy: Buffer): string[] {
|
|
const len = new Array(Math.ceil(entropy.length / 2));
|
|
const chunks = Array.from(len, (_a, chunk) => {
|
|
const buffChunk = entropy.slice(chunk * 2, (chunk + 1) * 2);
|
|
const no = parseInt(buffChunk.toString("hex"), 16);
|
|
const fill = "0".repeat(5 - String(no).length);
|
|
return fill + String(no) + String(calculateChecksum(chunk, no));
|
|
});
|
|
if (chunks.length !== 8) {
|
|
throw new Error("Chucks must have 8 digits");
|
|
}
|
|
return chunks;
|
|
}
|
|
|
|
function randomSecret(): string[] {
|
|
return entropyToSecret(randomEntropy());
|
|
}
|
|
|
|
function secretToEntropy(secret: string[]): Buffer {
|
|
return Buffer.concat(
|
|
secret.map((chunk, i) => {
|
|
const no = Number(chunk.slice(0, 5));
|
|
const checksum = Number(chunk.slice(5));
|
|
if (chunk.length !== 6) {
|
|
throw new Error("Invalid secret: number invalid");
|
|
}
|
|
if (!checkChecksum(i, no, checksum)) {
|
|
throw new Error("Invalid secret part: checksum invalid");
|
|
}
|
|
const hex = `0000${no.toString(16)}`.slice(-4);
|
|
return Buffer.from(hex, "hex");
|
|
})
|
|
);
|
|
}
|
|
|
|
function parseSecretString(secret: string): string[] {
|
|
const normalizedSecret = secret.replace(/[^0-9]/gu, "");
|
|
if (normalizedSecret.length !== 48) {
|
|
throw new Error(
|
|
"Invalid secret string (should contain 8 blocks of 6 digits"
|
|
);
|
|
}
|
|
return Array.from(new Array(8), (_a, index) => {
|
|
return normalizedSecret.slice(index * 6, (index + 1) * 6);
|
|
});
|
|
}
|
|
|
|
export {
|
|
randomEntropy,
|
|
randomSecret,
|
|
entropyToSecret,
|
|
secretToEntropy,
|
|
calculateChecksum,
|
|
checkChecksum,
|
|
parseSecretString,
|
|
};
|