Align x-noble with upstream (#1281)

* Align x-noble with upstream

* Remove own version of hashes

* @noble/hashes from master
This commit is contained in:
Jaco
2021-12-04 12:57:36 +02:00
committed by GitHub
parent ee7923c457
commit cd9dc35475
21 changed files with 248 additions and 200 deletions
-2
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { assertNumber, Hash, Input, toBytes, u32 } from './utils';
// prettier-ignore
export const SIGMA = new Uint8Array([
+4 -7
View File
@@ -1,17 +1,14 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
import { Hash, createView, Input, toBytes } from './utils';
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
// Polyfill for Safari 14
function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void {
if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
const wh = Number((value >> _32n) & _u32_max);
const wl = Number(value & _u32_max);
const [h, l] = isLE ? [4, 0] : [0, 4];
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
+2 -6
View File
@@ -1,9 +1,4 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
const U32_MASK64 = BigInt(2 ** 32 - 1);
const _32n = BigInt(32);
export function fromBig(n: bigint, le = false) {
@@ -12,7 +7,8 @@ export function fromBig(n: bigint, le = false) {
}
export function split(lst: bigint[], le = false) {
let [Ah, Al] = [new Uint32Array(lst.length), new Uint32Array(lst.length)];
let Ah = new Uint32Array(lst.length);
let Al = new Uint32Array(lst.length);
for (let i = 0; i < lst.length; i++) {
const { h, l } = fromBig(lst[i], le);
[Ah[i], Al[i]] = [h, l];
-3
View File
@@ -1,6 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
import * as blake2 from './_blake2';
import * as u64 from './_u64';
import { toBytes, u32, wrapConstructorWithOpts } from './utils';
-3
View File
@@ -1,6 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
import * as u64 from './_u64';
import * as blake2 from './_blake2';
import { rotr, toBytes, wrapConstructorWithOpts, u32 } from './utils';
-3
View File
@@ -1,6 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
import * as u64 from './_u64';
import * as blake2 from './_blake2';
import * as blake2s from './blake2s';
+6
View File
@@ -0,0 +1,6 @@
import nodeCrypto from 'crypto';
export const crypto: { node?: any; web?: any } = {
node: nodeCrypto,
web: undefined,
};
@@ -0,0 +1,6 @@
// Global symbol available in browsers only
declare const self: Record<string, any> | undefined;
export const crypto: { node?: any; web?: any } = {
node: undefined,
web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined,
};
-2
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
// prettier-ignore
import {
assertHash, assertNumber, CHash, Input, toBytes
-2
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { assertHash, Hash, CHash, Input, toBytes } from './utils';
// HMAC (RFC 2104)
class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> {
-2
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
throw new Error(
'noble-hashes have no entry-point. Please consult the README.md to learn how to use them'
);
@@ -1,6 +0,0 @@
// Copyright 2017-2021 @polkadot/x-noble-hashes authors & contributors
// SPDX-License-Identifier: Apache-2.0
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/x-noble-hashes', version: '8.0.6-16' };
-2
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { hmac } from './hmac';
// prettier-ignore
import {
+2 -3
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { SHA2 } from './_sha2';
import { wrapConstructor } from './utils';
@@ -9,7 +7,8 @@ import { wrapConstructor } from './utils';
const Rho = new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]);
const Id = Uint8Array.from({ length: 16 }, (_, i) => i);
const Pi = Id.map((i) => (9 * i + 5) % 16);
let [idxL, idxR] = [[Id], [Pi]];
let idxL = [Id];
let idxR = [Pi];
for (let i = 0; i < 4; i++) for (let j of [idxL, idxR]) j.push(j[i].map((k) => Rho[k]));
const shifts = [
+2 -3
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { sha256 } from './sha256';
import { pbkdf2 } from './pbkdf2';
import { assertNumber, asyncLoop, checkOpts, Input, u32 } from './utils';
@@ -63,7 +61,8 @@ function XorAndSalsa(
function BlockMix(input: Uint32Array, ii: number, out: Uint32Array, oi: number, r: number) {
// The block B is r 128-byte chunks (which is equivalent of 2r 64-byte chunks)
let [head, tail] = [oi + 0, oi + 16 * r];
let head = oi + 0;
let tail = oi + 16 * r;
for (let i = 0; i < 16; i++) out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1]
for (let i = 0; i < r; i++, head += 16, ii += 16) {
// We write odd & even Yi at same time. Even: 0bXXXXX0 Odd: 0bXXXXX1
-2
View File
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { SHA2 } from './_sha2';
import { rotr, wrapConstructor } from './utils';
@@ -1,5 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { Input, toBytes, wrapConstructorWithOpts, assertNumber, u32, Hash, HashXOF } from './utils';
import { Keccak, ShakeOpts } from './sha3';
// cSHAKE && KMAC (NIST SP800-185)
+2 -6
View File
@@ -1,6 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
import * as u64 from './_u64';
import {
Hash,
@@ -13,15 +10,14 @@ import {
HashXOF,
} from './utils';
// Various per round constants calculations
const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA]: [number[], number[], bigint[]] = [[], [], []];
const _0n = BigInt(0);
const _1n = BigInt(1);
const _2n = BigInt(2);
const _7n = BigInt(7);
const _256n = BigInt(256);
const _0x71n = BigInt(0x71);
// Various per round constants calculations
const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA]: [number[], number[], bigint[]] = [[], [], []];
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
// Pi
[x, y] = [y, (2 * x + 3 * y) % 5];
+1 -4
View File
@@ -1,6 +1,3 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { BigInt } from '@polkadot/x-bigint';
import { SHA2 } from './_sha2';
import * as u64 from './_u64';
import { wrapConstructor } from './utils';
@@ -28,7 +25,7 @@ const [SHA512_Kh, SHA512_Kl] = u64.split([
'0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',
'0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',
'0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'
].map((n) => BigInt(n)));
].map(n => BigInt(n)));
// Temporary buffer, not used to store anything between runs
const SHA512_W_H = new Uint32Array(80);
+22 -1
View File
@@ -1,5 +1,8 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
// The import here is via the package name. This is to ensure
// that exports mapping/resolution does fall into place.
import { crypto } from '@polkadot/x-noble-hashes/crypto';
// prettier-ignore
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |
@@ -61,6 +64,14 @@ export async function asyncLoop(iters: number, tick: number, cb: (i: number) =>
}
}
// Global symbols in both browsers and Node.js since v11
// https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
// https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
// https://nodejs.org/docs/latest-v12.x/api/util.html#util_class_util_textencoder
// https://nodejs.org/docs/latest-v12.x/api/util.html#util_class_util_textdecoder
// See https://github.com/microsoft/TypeScript/issues/31535
declare const TextEncoder: any;
declare const TextDecoder: any;
export type Input = Uint8Array | string;
export function toBytes(data: Input) {
if (typeof data === 'string') data = new TextEncoder().encode(data);
@@ -152,3 +163,13 @@ export function wrapConstructorWithOpts<H extends Hash<H>, T extends Object>(
hashC.init = hashC.create;
return hashC;
}
export function randomBytes(bytesLength = 32): Uint8Array {
if (crypto.web) {
return crypto.web.getRandomValues(new Uint8Array(bytesLength));
} else if (crypto.node) {
return new Uint8Array(crypto.node.randomBytes(bytesLength).buffer);
} else {
throw new Error("The environment doesn't have randomBytes function");
}
}
+201 -141
View File
@@ -1,7 +1,5 @@
/*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */
import { BigInt } from '@polkadot/x-bigint';
const _0n = BigInt(0);
const _1n = BigInt(1);
const _2n = BigInt(2);
@@ -15,8 +13,8 @@ const _23n = BigInt(23);
const _32n = BigInt(32);
const _44n = BigInt(44);
const _88n = BigInt(88);
const _256n = BigInt(256);
const _128n = BigInt(128);
const _256n = BigInt(256);
const _977n = BigInt(977);
// https://www.secg.org/sec2-v2.pdf
@@ -177,8 +175,8 @@ class JacobianPoint {
// It's faster, but should only be used when you don't care about
// an exposed private key e.g. sig verification, which works over *public* keys.
multiplyUnsafe(scalar: bigint): JacobianPoint {
if (!isValidScalar(scalar)) throw new TypeError('Point#multiply: expected valid scalar');
let n = mod(BigInt(scalar), CURVE.n);
let n = normalizeScalar(scalar);
// The condition is not executed unless you change global var
if (!USE_ENDOMORPHISM) {
let p = JacobianPoint.ZERO;
let d: JacobianPoint = this;
@@ -189,11 +187,10 @@ class JacobianPoint {
}
return p;
}
let [k1neg, k1, k2neg, k2] = splitScalarEndo(n);
let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
let k1p = JacobianPoint.ZERO;
let k2p = JacobianPoint.ZERO;
let d: JacobianPoint = this;
// TODO: see if we need to check for both zeros instead of one
while (k1 > _0n || k2 > _0n) {
if (k1 & _1n) k1p = k1p.add(d);
if (k2 & _1n) k2p = k2p.add(d);
@@ -216,7 +213,7 @@ class JacobianPoint {
const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1;
let points: JacobianPoint[] = [];
let p: JacobianPoint = this;
let base: JacobianPoint;
let base = p;
for (let window = 0; window < windows; window++) {
base = p;
points.push(base);
@@ -231,7 +228,7 @@ class JacobianPoint {
// Implements w-ary non-adjacent form for calculating ec multiplication
// Optional `affinePoint` argument is used to save cached precompute windows on it.
private wNAF(n: bigint, affinePoint?: Point): [JacobianPoint, JacobianPoint] {
private wNAF(n: bigint, affinePoint?: Point): { p: JacobianPoint; f: JacobianPoint } {
if (!affinePoint && this.equals(JacobianPoint.BASE)) affinePoint = Point.BASE;
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
if (256 % W) {
@@ -278,36 +275,40 @@ class JacobianPoint {
// Add random point inside current window to f.
if (wbits === 0) {
// The most important part for const-time getPublicKey
f = f.add(window % 2 ? precomputes[offset].negate() : precomputes[offset]);
let pr = precomputes[offset];
if (window % 2) pr = pr.negate();
f = f.add(pr);
} else {
const cached = precomputes[offset + Math.abs(wbits) - 1];
p = p.add(wbits < 0 ? cached.negate() : cached);
let cached = precomputes[offset + Math.abs(wbits) - 1];
if (wbits < 0) cached = cached.negate();
p = p.add(cached);
}
}
return [p, f];
return { p, f };
}
// Constant time multiplication.
// Uses wNAF method. Windowed method may be 10% faster,
// but takes 2x longer to generate and consumes 2x memory.
multiply(scalar: number | bigint, affinePoint?: Point): JacobianPoint {
if (!isValidScalar(scalar)) throw new TypeError('Point#multiply: expected valid scalar');
let n = mod(BigInt(scalar), CURVE.n);
let n = normalizeScalar(scalar);
// Real point.
let point: JacobianPoint;
// Fake point, we use it to achieve constant-time multiplication.
let fake: JacobianPoint;
if (USE_ENDOMORPHISM) {
const [k1neg, k1, k2neg, k2] = splitScalarEndo(n);
let k1p, k2p, f1p, f2p;
[k1p, f1p] = this.wNAF(k1, affinePoint);
[k2p, f2p] = this.wNAF(k2, affinePoint);
let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
if (k1neg) k1p = k1p.negate();
if (k2neg) k2p = k2p.negate();
k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z);
[point, fake] = [k1p.add(k2p), f1p.add(f2p)];
point = k1p.add(k2p);
fake = f1p.add(f2p);
} else {
[point, fake] = this.wNAF(n, affinePoint);
let { p, f } = this.wNAF(n, affinePoint);
point = p;
fake = f;
}
// Normalize `z` for both points, but return only real one
return JacobianPoint.normalizeZ([point, fake])[0];
@@ -594,17 +595,17 @@ export const SignResult = Signature; // backwards compatibility
// Concatenates two Uint8Arrays into one.
// TODO: check if we're copying data instead of moving it and if that's ok
// function concatBytes(...arrays: Uint8Array[]): Uint8Array {
// if (arrays.length === 1) return arrays[0];
// const length = arrays.reduce((a, arr) => a + arr.length, 0);
// const result = new Uint8Array(length);
// for (let i = 0, pad = 0; i < arrays.length; i++) {
// const arr = arrays[i];
// result.set(arr, pad);
// pad += arr.length;
// }
// return result;
// }
function concatBytes(...arrays: Uint8Array[]): Uint8Array {
if (arrays.length === 1) return arrays[0];
const length = arrays.reduce((a, arr) => a + arr.length, 0);
const result = new Uint8Array(length);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const arr = arrays[i];
result.set(arr, pad);
pad += arr.length;
}
return result;
}
// Convert between types
// ---------------------
@@ -664,10 +665,10 @@ function parseByte(str: string): number {
return Number.parseInt(str, 16) * 2;
}
function isValidScalar(num: number | bigint): boolean {
if (typeof num === 'bigint' && num > _0n) return true;
if (typeof num === 'number' && num > 0 && Number.isSafeInteger(num)) return true;
return false;
function normalizeScalar(num: number | bigint): bigint {
if (typeof num === 'number' && num > 0 && Number.isSafeInteger(num)) return BigInt(num);
if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num;
throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
}
// -------------------------
@@ -720,15 +721,15 @@ function invert(number: bigint, modulo: bigint = CURVE.P): bigint {
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
let a = mod(number, modulo);
let b = modulo;
let [x, y, u, v] = [_0n, _1n, _1n, _0n];
// prettier-ignore
let x = _0n, y = _1n, u = _1n, v = _0n;
while (a !== _0n) {
const q = b / a;
const r = b % a;
const m = x - u * q;
const n = y - v * q;
[b, a] = [a, r];
[x, y] = [u, v];
[u, v] = [m, n];
// prettier-ignore
b = a, a = r, x = u, y = v, u = m, v = n;
}
const gcd = b;
if (gcd !== _1n) throw new Error('invert: does not exist');
@@ -759,7 +760,7 @@ const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
const POW_2_128 = _2n ** _128n;
// Split 256-bit K into 2 128-bit (k1, k2) for which k1 + k2 * lambda = K.
// Used for endomorphism https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
function splitScalarEndo(k: bigint): [boolean, bigint, boolean, bigint] {
function splitScalarEndo(k: bigint) {
const { n } = CURVE;
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
@@ -774,7 +775,7 @@ function splitScalarEndo(k: bigint): [boolean, bigint, boolean, bigint] {
if (k1neg) k1 = n - k1;
if (k2neg) k2 = n - k2;
if (k1 > POW_2_128 || k2 > POW_2_128) throw new Error('splitScalarEndo: Endomorphism failed');
return [k1neg, k1, k2neg, k2];
return { k1neg, k1, k2neg, k2 };
}
function truncateHash(hash: string | Uint8Array): bigint {
@@ -796,7 +797,7 @@ type QRS = [Point, bigint, bigint];
type U8A = Uint8Array;
// Steps A, B and C of RFC6979.
function _abc6979(msgHash: Hex, privateKey: bigint): [U8A, bigint, U8A, U8A, U8A, U8A, U8A] {
function _abc6979(msgHash: Hex, privateKey: bigint) {
if (msgHash == null) throw new Error(`sign: expected valid msgHash, not "${msgHash}"`);
const num = typeof msgHash === 'string' ? hexToNumber(msgHash) : bytesToNumber(msgHash);
// Step A is ignored, since we already provide hash instead of msg
@@ -810,37 +811,37 @@ function _abc6979(msgHash: Hex, privateKey: bigint): [U8A, bigint, U8A, U8A, U8A
let k = new Uint8Array(32).fill(0);
const b0 = Uint8Array.from([0x00]);
const b1 = Uint8Array.from([0x01]);
return [h1, h1n, x, v, k, b0, b1];
return { h1, h1n, x, v, k, b0, b1 };
}
// Deterministic k generation as per RFC6979.
// Generates k, and then calculates Q & Signature {r, s} based on it.
// https://tools.ietf.org/html/rfc6979#section-3.1
// async function getQRSrfc6979(msgHash: Hex, privateKey: PrivKey): Promise<QRS> {
// const privKey = normalizePrivateKey(privateKey);
// let [h1, h1n, x, v, k, b0, b1] = _abc6979(msgHash, privKey);
// const hmac = utils.hmacSha256;
// // Steps D, E, F, G
// k = await hmac(k, v, b0, x, h1);
// v = await hmac(k, v);
// k = await hmac(k, v, b1, x, h1);
// v = await hmac(k, v);
// // Step H3, repeat until 1 < T < n - 1
// for (let i = 0; i < 1000; i++) {
// v = await hmac(k, v);
// let qrs = calcQRSFromK(v, h1n, privKey);
// if (qrs) return qrs;
// k = await hmac(k, v, b0);
// v = await hmac(k, v);
// }
async function getQRSrfc6979(msgHash: Hex, privateKey: PrivKey): Promise<QRS> {
const privKey = normalizePrivateKey(privateKey);
let { h1, h1n, x, v, k, b0, b1 } = _abc6979(msgHash, privKey);
const hmac = utils.hmacSha256;
// Steps D, E, F, G
k = await hmac(k, v, b0, x, h1);
v = await hmac(k, v);
k = await hmac(k, v, b1, x, h1);
v = await hmac(k, v);
// Step H3, repeat until 1 < T < n - 1
for (let i = 0; i < 1000; i++) {
v = await hmac(k, v);
let qrs = calcQRSFromK(v, h1n, privKey);
if (qrs) return qrs;
k = await hmac(k, v, b0);
v = await hmac(k, v);
}
// throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid');
// }
throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid');
}
// Same thing, but for synchronous utils.hmacSha256()
function getQRSrfc6979Sync(msgHash: Hex, privateKey: PrivKey): QRS {
const privKey = normalizePrivateKey(privateKey);
let [h1, h1n, x, v, k, b0, b1] = _abc6979(msgHash, privKey);
let { h1, h1n, x, v, k, b0, b1 } = _abc6979(msgHash, privKey);
const hmac = utils.hmacSha256Sync;
if (!hmac) throw new Error('utils.hmacSha256Sync is undefined, you need to set it');
// Steps D, E, F, G
@@ -987,14 +988,14 @@ function QRSToSig(qrs: QRS, opts: OptsNoRecov | OptsRecov, str = false): SignOut
// https://www.secg.org/sec1-v2.pdf, section 4.1.3
// We are using deterministic signature scheme instead of letting user specify random `k`.
// async function sign(msgHash: U8A, privKey: PrivKey, opts: OptsRecov): Promise<[U8A, number]>;
// async function sign(msgHash: string, privKey: PrivKey, opts: OptsRecov): Promise<[string, number]>;
// async function sign(msgHash: U8A, privKey: PrivKey, opts?: OptsNoRecov): Promise<U8A>;
// async function sign(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): Promise<string>;
// async function sign(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): Promise<string>;
// async function sign(msgHash: Hex, privKey: PrivKey, opts: Opts = {}): Promise<SignOutput> {
// return QRSToSig(await getQRSrfc6979(msgHash, privKey), opts, typeof msgHash === 'string');
// }
async function sign(msgHash: U8A, privKey: PrivKey, opts: OptsRecov): Promise<[U8A, number]>;
async function sign(msgHash: string, privKey: PrivKey, opts: OptsRecov): Promise<[string, number]>;
async function sign(msgHash: U8A, privKey: PrivKey, opts?: OptsNoRecov): Promise<U8A>;
async function sign(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): Promise<string>;
async function sign(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): Promise<string>;
async function sign(msgHash: Hex, privKey: PrivKey, opts: Opts = {}): Promise<SignOutput> {
return QRSToSig(await getQRSrfc6979(msgHash, privKey), opts, typeof msgHash === 'string');
}
function signSync(msgHash: U8A, privKey: PrivKey, opts: OptsRecov): [U8A, number];
function signSync(msgHash: string, privKey: PrivKey, opts: OptsRecov): [string, number];
@@ -1004,7 +1005,7 @@ function signSync(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): string
function signSync(msgHash: Hex, privKey: PrivKey, opts: Opts = {}): SignOutput {
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey), opts, typeof msgHash === 'string');
}
export { /*sign,*/ signSync };
export { sign, signSync };
// https://www.secg.org/sec1-v2.pdf, section 4.1.4
export function verify(signature: Sig, msgHash: Hex, publicKey: PubKey): boolean {
@@ -1032,22 +1033,22 @@ export function verify(signature: Sig, msgHash: Hex, publicKey: PubKey): boolean
// Schnorr-specific code as per BIP0340.
// Strip first byte that signifies whether y is positive or negative, leave only x.
// async function taggedHash(tag: string, ...messages: Uint8Array[]): Promise<bigint> {
// const tagB = new Uint8Array(tag.split('').map((c) => c.charCodeAt(0)));
// const tagH = await utils.sha256(tagB);
// const h = await utils.sha256(concatBytes(tagH, tagH, ...messages));
// return bytesToNumber(h);
// }
async function taggedHash(tag: string, ...messages: Uint8Array[]): Promise<bigint> {
const tagB = new Uint8Array(tag.split('').map((c) => c.charCodeAt(0)));
const tagH = await utils.sha256(tagB);
const h = await utils.sha256(concatBytes(tagH, tagH, ...messages));
return bytesToNumber(h);
}
// async function createChallenge(x: bigint, P: Point, message: Uint8Array) {
// const rx = pad32b(x);
// const t = await taggedHash('BIP0340/challenge', rx, P.toRawX(), message);
// return mod(t, CURVE.n);
// }
async function createChallenge(x: bigint, P: Point, message: Uint8Array) {
const rx = pad32b(x);
const t = await taggedHash('BIP0340/challenge', rx, P.toRawX(), message);
return mod(t, CURVE.n);
}
// function hasEvenY(point: Point) {
// return mod(point.y, _2n) === _0n;
// }
function hasEvenY(point: Point) {
return mod(point.y, _2n) === _0n;
}
class SchnorrSignature {
constructor(readonly r: bigint, readonly s: bigint) {
@@ -1079,72 +1080,72 @@ function schnorrGetPublicKey(privateKey: PrivKey): Hex {
}
// Schnorr signature verifies itself before producing an output, which makes it safer
// async function schnorrSign(msgHash: string, privateKey: string, auxRand?: Hex): Promise<string>;
// async function schnorrSign(
// msgHash: Uint8Array,
// privateKey: Uint8Array,
// auxRand?: Hex
// ): Promise<Uint8Array>;
// async function schnorrSign(
// msgHash: Hex,
// privateKey: PrivKey,
// auxRand: Hex = utils.randomBytes()
// ): Promise<Hex> {
// if (msgHash == null) throw new TypeError(`sign: Expected valid message, not "${msgHash}"`);
// // if (privateKey == null) throw new TypeError('Expected valid private key');
// if (!privateKey) privateKey = _0n;
// const { n } = CURVE;
// const m = ensureBytes(msgHash);
// const d0 = normalizePrivateKey(privateKey); // <== does isWithinCurveOrder check
// const rand = ensureBytes(auxRand);
// if (rand.length !== 32) throw new TypeError('sign: Expected 32 bytes of aux randomness');
async function schnorrSign(msgHash: string, privateKey: string, auxRand?: Hex): Promise<string>;
async function schnorrSign(
msgHash: Uint8Array,
privateKey: Uint8Array,
auxRand?: Hex
): Promise<Uint8Array>;
async function schnorrSign(
msgHash: Hex,
privateKey: PrivKey,
auxRand: Hex = utils.randomBytes()
): Promise<Hex> {
if (msgHash == null) throw new TypeError(`sign: Expected valid message, not "${msgHash}"`);
// if (privateKey == null) throw new TypeError('Expected valid private key');
if (!privateKey) privateKey = _0n;
const { n } = CURVE;
const m = ensureBytes(msgHash);
const d0 = normalizePrivateKey(privateKey); // <== does isWithinCurveOrder check
const rand = ensureBytes(auxRand);
if (rand.length !== 32) throw new TypeError('sign: Expected 32 bytes of aux randomness');
// const P = Point.fromPrivateKey(d0);
// const d = hasEvenY(P) ? d0 : n - d0;
const P = Point.fromPrivateKey(d0);
const d = hasEvenY(P) ? d0 : n - d0;
// const t0h = await taggedHash('BIP0340/aux', rand);
// const t = d ^ t0h;
const t0h = await taggedHash('BIP0340/aux', rand);
const t = d ^ t0h;
// const k0h = await taggedHash('BIP0340/nonce', pad32b(t), P.toRawX(), m);
// const k0 = mod(k0h, n);
// if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero');
const k0h = await taggedHash('BIP0340/nonce', pad32b(t), P.toRawX(), m);
const k0 = mod(k0h, n);
if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero');
// // R = k'⋅G
// const R = Point.fromPrivateKey(k0);
// const k = hasEvenY(R) ? k0 : n - k0;
// const e = await createChallenge(R.x, P, m);
// const sig = new SchnorrSignature(R.x, mod(k + e * d, n));
// const isValid = await schnorrVerify(sig.toRawBytes(), m, P.toRawX());
// R = k'⋅G
const R = Point.fromPrivateKey(k0);
const k = hasEvenY(R) ? k0 : n - k0;
const e = await createChallenge(R.x, P, m);
const sig = new SchnorrSignature(R.x, mod(k + e * d, n));
const isValid = await schnorrVerify(sig.toRawBytes(), m, P.toRawX());
// if (!isValid) throw new Error('sign: Invalid signature produced');
// return typeof msgHash === 'string' ? sig.toHex() : sig.toRawBytes();
// }
if (!isValid) throw new Error('sign: Invalid signature produced');
return typeof msgHash === 'string' ? sig.toHex() : sig.toRawBytes();
}
// no schnorrSignSync() for now
// Also used in sign() function.
// async function schnorrVerify(signature: Hex, msgHash: Hex, publicKey: Hex): Promise<boolean> {
// const sig =
// signature instanceof SchnorrSignature ? signature : SchnorrSignature.fromHex(signature);
// const m = typeof msgHash === 'string' ? hexToBytes(msgHash) : msgHash;
async function schnorrVerify(signature: Hex, msgHash: Hex, publicKey: Hex): Promise<boolean> {
const sig =
signature instanceof SchnorrSignature ? signature : SchnorrSignature.fromHex(signature);
const m = typeof msgHash === 'string' ? hexToBytes(msgHash) : msgHash;
// const P = normalizePublicKey(publicKey);
// const e = await createChallenge(sig.r, P, m);
const P = normalizePublicKey(publicKey);
const e = await createChallenge(sig.r, P, m);
// // R = s⋅G - e⋅P
// const sG = Point.fromPrivateKey(sig.s);
// const eP = P.multiply(e);
// const R = sG.subtract(eP);
// R = s⋅G - e⋅P
const sG = Point.fromPrivateKey(sig.s);
const eP = P.multiply(e);
const R = sG.subtract(eP);
// if (R.equals(Point.BASE) || !hasEvenY(R) || R.x !== sig.r) return false;
// return true;
// }
if (R.equals(Point.BASE) || !hasEvenY(R) || R.x !== sig.r) return false;
return true;
}
export const schnorr = {
Signature: SchnorrSignature,
getPublicKey: schnorrGetPublicKey,
// sign: schnorrSign,
// verify: schnorrVerify,
sign: schnorrSign,
verify: schnorrVerify,
};
// Enable precomputes. Slows down first publicKey computation by 20ms.
@@ -1152,6 +1153,16 @@ Point.BASE._setWindowSize(8);
type Sha256FnSync = undefined | ((...messages: Uint8Array[]) => Uint8Array);
type HmacFnSync = undefined | ((key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array);
// Global symbol available in browsers only
declare const self: Record<string, any> | undefined;
const crypto: { node?: any; web?: any } = (() => {
const webCrypto = typeof self === 'object' && 'crypto' in self ? self.crypto : undefined;
const nodeRequire = typeof module !== 'undefined' && typeof require === 'function';
return {
node: nodeRequire && !webCrypto ? require('crypto') : undefined,
web: webCrypto,
};
})();
export const utils = {
isValidPrivateKey(privateKey: PrivKey) {
@@ -1163,15 +1174,64 @@ export const utils = {
}
},
randomBytes: undefined,
randomBytes: (bytesLength: number = 32): Uint8Array => {
if (crypto.web) {
return crypto.web.getRandomValues(new Uint8Array(bytesLength));
} else if (crypto.node) {
const { randomBytes } = crypto.node;
return new Uint8Array(randomBytes(bytesLength).buffer);
} else {
throw new Error("The environment doesn't have randomBytes function");
}
},
// NIST SP 800-56A rev 3, section 5.6.1.2.2
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
randomPrivateKey: undefined,
randomPrivateKey: (): Uint8Array => {
let i = 8;
while (i--) {
const b32 = utils.randomBytes(32);
const num = bytesToNumber(b32);
if (isWithinCurveOrder(num) && num !== _1n) return b32;
}
throw new Error('Valid private key was not found in 8 iterations. PRNG is broken');
},
sha256: undefined,
sha256: async (message: Uint8Array): Promise<Uint8Array> => {
if (crypto.web) {
const buffer = await crypto.web.subtle.digest('SHA-256', message.buffer);
return new Uint8Array(buffer);
} else if (crypto.node) {
const { createHash } = crypto.node;
return Uint8Array.from(createHash('sha256').update(message).digest());
} else {
throw new Error("The environment doesn't have sha256 function");
}
},
hmacSha256: undefined,
hmacSha256: async (key: Uint8Array, ...messages: Uint8Array[]): Promise<Uint8Array> => {
if (crypto.web) {
const ckey = await crypto.web.subtle.importKey(
'raw',
key,
{ name: 'HMAC', hash: { name: 'SHA-256' } },
false,
['sign']
);
const message = concatBytes(...messages);
const buffer = await crypto.web.subtle.sign('HMAC', ckey, message);
return new Uint8Array(buffer);
} else if (crypto.node) {
const { createHmac } = crypto.node;
const hash = createHmac('sha256', key);
for (let message of messages) {
hash.update(message);
}
return Uint8Array.from(hash.digest());
} else {
throw new Error("The environment doesn't have hmac-sha256 function");
}
},
sha256Sync: undefined as Sha256FnSync,
hmacSha256Sync: undefined as HmacFnSync,