forked from tornado-packages/noble-curves
utils: harmonize with noble-hashes
This commit is contained in:
parent
aade023e48
commit
c6b4aadafb
@ -1,13 +1,14 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
// 100 lines of code in the file are duplicated from noble-hashes (utils).
|
||||||
|
// This is OK: `abstract` directory does not use noble-hashes.
|
||||||
|
// User may opt-in into using different hashing library. This way, noble-hashes
|
||||||
|
// won't be included into their bundle.
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
const _2n = BigInt(2);
|
const _2n = BigInt(2);
|
||||||
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||||
|
export type Hex = Uint8Array | string; // hex strings are accepted for simplicity
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
export type PrivKey = Hex | bigint; // bigints are accepted to ease learning curve
|
||||||
export type Hex = Uint8Array | string;
|
|
||||||
// Very few implementations accept numbers, we do it to ease learning curve
|
|
||||||
export type PrivKey = Hex | bigint;
|
|
||||||
export type CHash = {
|
export type CHash = {
|
||||||
(message: Uint8Array | string): Uint8Array;
|
(message: Uint8Array | string): Uint8Array;
|
||||||
blockLen: number;
|
blockLen: number;
|
||||||
@ -17,6 +18,9 @@ export type CHash = {
|
|||||||
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||||
|
|
||||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||||
|
/**
|
||||||
|
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
||||||
|
*/
|
||||||
export function bytesToHex(bytes: Uint8Array): string {
|
export function bytesToHex(bytes: Uint8Array): string {
|
||||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||||
// pre-caching improves the speed 6x
|
// pre-caching improves the speed 6x
|
||||||
@ -38,22 +42,25 @@ export function hexToNumber(hex: string): bigint {
|
|||||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caching slows it down 2-3x
|
/**
|
||||||
|
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
||||||
|
*/
|
||||||
export function hexToBytes(hex: string): Uint8Array {
|
export function hexToBytes(hex: string): Uint8Array {
|
||||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
|
const len = hex.length;
|
||||||
const array = new Uint8Array(hex.length / 2);
|
if (len % 2) throw new Error('padded hex string expected, got unpadded hex of length ' + len);
|
||||||
|
const array = new Uint8Array(len / 2);
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
const j = i * 2;
|
const j = i * 2;
|
||||||
const hexByte = hex.slice(j, j + 2);
|
const hexByte = hex.slice(j, j + 2);
|
||||||
const byte = Number.parseInt(hexByte, 16);
|
const byte = Number.parseInt(hexByte, 16);
|
||||||
if (Number.isNaN(byte) || byte < 0) throw new Error('invalid byte sequence');
|
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
||||||
array[i] = byte;
|
array[i] = byte;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big Endian
|
// BE: Big Endian, LE: Little Endian
|
||||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||||
return hexToNumber(bytesToHex(bytes));
|
return hexToNumber(bytesToHex(bytes));
|
||||||
}
|
}
|
||||||
@ -62,12 +69,26 @@ export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
|||||||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const numberToBytesBE = (n: bigint, len: number) =>
|
export function numberToBytesBE(n: number | bigint, len: number): Uint8Array {
|
||||||
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||||
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
}
|
||||||
// Returns variable number bytes (minimal bigint encoding?)
|
export function numberToBytesLE(n: number | bigint, len: number): Uint8Array {
|
||||||
export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n));
|
return numberToBytesBE(n, len).reverse();
|
||||||
|
}
|
||||||
|
// Unpadded, rarely used
|
||||||
|
export function numberToVarBytesBE(n: number | bigint): Uint8Array {
|
||||||
|
return hexToBytes(numberToHexUnpadded(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes hex string or Uint8Array, converts to Uint8Array.
|
||||||
|
* Validates output length.
|
||||||
|
* Will throw error for other types.
|
||||||
|
* @param title descriptive title for an error e.g. 'private key'
|
||||||
|
* @param hex hex string or Uint8Array
|
||||||
|
* @param expectedLength optional, will compare to result array's length
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array {
|
export function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array {
|
||||||
let res: Uint8Array;
|
let res: Uint8Array;
|
||||||
if (typeof hex === 'string') {
|
if (typeof hex === 'string') {
|
||||||
@ -89,11 +110,13 @@ export function ensureBytes(title: string, hex: Hex, expectedLength?: number): U
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies several Uint8Arrays into one.
|
/**
|
||||||
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
|
* Copies several Uint8Arrays into one.
|
||||||
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
*/
|
||||||
|
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||||
|
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
|
||||||
let pad = 0; // walk through each item, ensure they have proper type
|
let pad = 0; // walk through each item, ensure they have proper type
|
||||||
arrs.forEach((a) => {
|
arrays.forEach((a) => {
|
||||||
if (!u8a(a)) throw new Error('Uint8Array expected');
|
if (!u8a(a)) throw new Error('Uint8Array expected');
|
||||||
r.set(a, pad);
|
r.set(a, pad);
|
||||||
pad += a.length;
|
pad += a.length;
|
||||||
@ -111,29 +134,47 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
|||||||
// Global symbols in both browsers and Node.js since v11
|
// Global symbols in both browsers and Node.js since v11
|
||||||
// See https://github.com/microsoft/TypeScript/issues/31535
|
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||||
declare const TextEncoder: any;
|
declare const TextEncoder: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
||||||
|
*/
|
||||||
export function utf8ToBytes(str: string): Uint8Array {
|
export function utf8ToBytes(str: string): Uint8Array {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
|
||||||
}
|
|
||||||
return new TextEncoder().encode(str);
|
return new TextEncoder().encode(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bit operations
|
// Bit operations
|
||||||
|
|
||||||
// Amount of bits inside bigint (Same as n.toString(2).length)
|
/**
|
||||||
|
* Calculates amount of bits in a bigint.
|
||||||
|
* Same as `n.toString(2).length`
|
||||||
|
*/
|
||||||
export function bitLen(n: bigint) {
|
export function bitLen(n: bigint) {
|
||||||
let len;
|
let len;
|
||||||
for (len = 0; n > _0n; n >>= _1n, len += 1);
|
for (len = 0; n > _0n; n >>= _1n, len += 1);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
|
|
||||||
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
|
/**
|
||||||
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & _1n;
|
* Gets single bit at position.
|
||||||
// Sets single bit at position
|
* NOTE: first bit position is 0 (same as arrays)
|
||||||
export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
* Same as `!!+Array.from(n.toString(2)).reverse()[pos]`
|
||||||
n | ((value ? _1n : _0n) << BigInt(pos));
|
*/
|
||||||
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
export function bitGet(n: bigint, pos: number) {
|
||||||
// Not using ** operator with bigints for old engines.
|
return (n >> BigInt(pos)) & _1n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets single bit at position.
|
||||||
|
*/
|
||||||
|
export const bitSet = (n: bigint, pos: number, value: boolean) => {
|
||||||
|
return n | ((value ? _1n : _0n) << BigInt(pos));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate mask for N bits. Not using ** operator with bigints because of old engines.
|
||||||
|
* Same as BigInt(`0b${Array(i).fill('1').join('')}`)
|
||||||
|
*/
|
||||||
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
||||||
|
|
||||||
// DRBG
|
// DRBG
|
||||||
|
Loading…
Reference in New Issue
Block a user