Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

noble-secp256k1

Package Overview
Dependencies
Maintainers
1
Versions
47
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

noble-secp256k1 - npm Package Compare versions

Comparing version 0.3.2 to 0.4.0

35

index.d.ts

@@ -12,10 +12,8 @@ /*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */

constructor(x: bigint, y: bigint);
static isValidPoint(x: bigint, y: bigint): boolean;
private static fromCompressedHex;
private static isValidPoint;
private static fromUncompressedHex;
static fromHex(hash: Hex): Point;
static fromPrivateKey(privateKey: PrivKey): Point;
static fromSignature(hash: Hex, signature: Signature, recovery: number | bigint): Point | undefined;
private uncompressedHex;
private compressedHex;
static fromSignature(hash: Hex, signature: Signature, recovery: number): Point | undefined;
toRawBytes(isCompressed?: boolean): Uint8Array;

@@ -25,3 +23,3 @@ toHex(isCompressed?: boolean): string;

private double;
multiply(scalar: number | bigint | Uint8Array): Point;
multiply(scalar: number | bigint): Point;
}

@@ -33,23 +31,26 @@ export declare class SignResult {

static fromHex(hex: Hex): SignResult;
private formatLength;
private formatNumberToHex;
toHex(): string;
toHex(compressed?: boolean): string;
}
export declare const BASE_POINT: Point;
export declare function recoverPublicKey(hash: Hex, signature: Signature, recovery: number | bigint): Uint8Array | undefined;
export declare function recoverPublicKey(hash: Hex, signature: Signature, recovery: number): Uint8Array | undefined;
export declare function getPublicKey(privateKey: Uint8Array | bigint | number, isCompressed?: boolean): Uint8Array;
export declare function getPublicKey(privateKey: string, isCompressed?: boolean): string;
export declare function getSharedSecret(privateA: PrivKey, publicB: PubKey): Uint8Array;
declare type Options = {
declare type OptsRecovered = {
recovered: true;
canonical?: true;
k?: number | bigint;
};
declare type OptionsWithK = Partial<Options>;
export declare function sign(hash: string, privateKey: PrivKey, opts: Options): [string, bigint];
export declare function sign(hash: Uint8Array, privateKey: PrivKey, opts: Options): [Uint8Array, bigint];
export declare function sign(hash: Uint8Array, privateKey: PrivKey, opts?: OptionsWithK): Uint8Array;
export declare function sign(hash: string, privateKey: PrivKey, opts?: OptionsWithK): string;
export declare function sign(hash: string, privateKey: PrivKey, opts?: OptionsWithK): string;
declare type OptsNoRecovered = {
recovered?: false;
canonical?: true;
};
export declare function sign(hash: string, privateKey: PrivKey, opts: OptsRecovered): Promise<[string, number]>;
export declare function sign(hash: Uint8Array, privateKey: PrivKey, opts: OptsRecovered): Promise<[Uint8Array, number]>;
export declare function sign(hash: Uint8Array, privateKey: PrivKey, opts?: OptsNoRecovered): Promise<Uint8Array>;
export declare function sign(hash: string, privateKey: PrivKey, opts?: OptsNoRecovered): Promise<string>;
export declare function sign(hash: string, privateKey: PrivKey, opts?: OptsNoRecovered): Promise<string>;
export declare function verify(signature: Signature, hash: Hex, publicKey: PubKey): boolean;
export declare const utils: {
isValidPrivateKey(privateKey: PrivKey): boolean;
};
export {};

@@ -16,14 +16,5 @@ "use strict";

}
static fromCompressedHex(bytes) {
const x = arrayToNumber(bytes.slice(1));
const sqrY = mod(x ** 3n + A * x + B, exports.P);
let y = powMod(sqrY, (exports.P + 1n) / 4n, exports.P);
const isFirstByteOdd = (bytes[0] & 1) === 1;
const isYOdd = (y & 1n) === 1n;
if (isFirstByteOdd !== isYOdd) {
y = mod(-y, exports.P);
}
return new Point(x, y);
}
static isValidPoint(x, y) {
if (x === 0n || y === 0n || x >= exports.P || y >= exports.P)
return false;
const sqrY = y * y;

@@ -40,7 +31,27 @@ const yEquivalence = x ** 3n + A * x + B;

}
static fromCompressedHex(bytes) {
if (bytes.length !== 33) {
throw new TypeError(`Point.fromHex: compressed expects 66 bytes, not ${bytes.length * 2}`);
}
const x = arrayToNumber(bytes.slice(1));
const sqrY = mod(x ** 3n + A * x + B, exports.P);
let y = powMod(sqrY, (exports.P + 1n) / 4n, exports.P);
const isFirstByteOdd = (bytes[0] & 1) === 1;
const isYOdd = (y & 1n) === 1n;
if (isFirstByteOdd !== isYOdd) {
y = mod(-y, exports.P);
}
if (!Point.isValidPoint(x, y)) {
throw new TypeError('Point.fromHex: Point is not on elliptic curve');
}
return new Point(x, y);
}
static fromUncompressedHex(bytes) {
if (bytes.length !== 65) {
throw new TypeError(`Point.fromHex: uncompressed expects 130 bytes, not ${bytes.length * 2}`);
}
const x = arrayToNumber(bytes.slice(1, 33));
const y = arrayToNumber(bytes.slice(33));
if (!this.isValidPoint(x, y)) {
throw new Error("secp256k1: Point is not on elliptic curve");
throw new TypeError('Point.fromHex: Point is not on elliptic curve');
}

@@ -51,5 +62,8 @@ return new Point(x, y);

const bytes = hash instanceof Uint8Array ? hash : hexToArray(hash);
return bytes[0] === 0x4
? this.fromUncompressedHex(bytes)
: this.fromCompressedHex(bytes);
const header = bytes[0];
if (header === 0x02 || header === 0x03)
return this.fromCompressedHex(bytes);
if (header === 0x04)
return this.fromUncompressedHex(bytes);
throw new TypeError('Point.fromHex: received invalid point');
}

@@ -60,5 +74,5 @@ static fromPrivateKey(privateKey) {

static fromSignature(hash, signature, recovery) {
recovery = BigInt(recovery);
recovery = Number(recovery);
const sign = normalizeSignature(signature);
const message = truncateHash(typeof hash === "string" ? hexToArray(hash) : hash);
const message = truncateHash(typeof hash === 'string' ? hexToArray(hash) : hash);
if (sign.r === 0n || sign.s === 0n) {

@@ -68,3 +82,3 @@ return;

let publicKeyX = sign.r;
if (recovery >> 1n) {
if (recovery >> 1) {
if (publicKeyX >= SUBPN) {

@@ -75,3 +89,3 @@ return;

}
const compresedHex = `$0{2n + (recovery & 1n)}${publicKeyX.toString(16)}`;
const compresedHex = `0${2 + (recovery & 1)}${pad64(publicKeyX)}`;
const publicKey = Point.fromHex(compresedHex);

@@ -85,20 +99,18 @@ const rInv = modInverse(sign.r, exports.PRIME_ORDER);

}
uncompressedHex() {
const yHex = this.y.toString(16).padStart(64, "0");
const xHex = this.x.toString(16).padStart(64, "0");
return `04${xHex}${yHex}`;
}
compressedHex() {
let hex = this.x.toString(16).padStart(64, "0");
const head = this.y & 1n ? "03" : "02";
return `${head}${hex}`;
}
toRawBytes(isCompressed = false) {
const hex = this.toHex(isCompressed);
return hexToArray(hex);
return hexToArray(this.toHex(isCompressed));
}
toHex(isCompressed = false) {
return isCompressed ? this.compressedHex() : this.uncompressedHex();
const x = pad64(this.x);
if (isCompressed) {
return `${this.y & 1n ? '03' : '02'}${x}`;
}
else {
return `04${x}${pad64(this.y)}`;
}
}
add(other) {
if (!(other instanceof Point)) {
throw new TypeError('Point#add: expected Point');
}
const a = this;

@@ -112,5 +124,13 @@ const b = other;

}
if (a.x === b.y && a.y == -b.y) {
if (a.x === b.y && a.y === -b.y) {
return new Point(0n, 0n);
}
if (a.x === b.x) {
if (a.y === b.y) {
return this.double();
}
else {
throw new TypeError('Point#add: cannot add points (a.x == b.x, a.y != b.y)');
}
}
const lamAdd = mod((b.y - a.y) * modInverse(b.x - a.x, exports.P), exports.P);

@@ -129,4 +149,9 @@ const x = mod(lamAdd * lamAdd - a.x - b.x, exports.P);

multiply(scalar) {
let n = scalar instanceof Uint8Array ?
arrayToNumber(scalar) : BigInt(scalar);
if (typeof scalar !== 'number' && typeof scalar !== 'bigint') {
throw new TypeError('Point#multiply: expected number or bigint');
}
let n = BigInt(scalar);
if (!isValidPrivateKey(n)) {
throw new Error('Private key is invalid. Expected 0 < key < PRIME_ORDER');
}
let Q = new Point(0n, 0n);

@@ -153,2 +178,5 @@ let F = new Point(this.x, this.y);

exports.Point = Point;
function parseByte(str) {
return Number.parseInt(str, 16) * 2;
}
class SignResult {

@@ -160,21 +188,31 @@ constructor(r, s) {

static fromHex(hex) {
const hash = hex instanceof Uint8Array ? arrayToHex(hex) : hex;
const rLength = parseInt(`${hash[6]}${hash[7]}`, 16) * 2;
const r = BigInt(`0x${hash.substr(8, rLength)}`);
const s = BigInt(`0x${hash.slice(12 + rLength)}`);
const str = hex instanceof Uint8Array ? arrayToHex(hex) : hex;
if (typeof str !== 'string')
throw new TypeError({}.toString.call(hex));
const check1 = str.slice(0, 2);
const length = parseByte(str.slice(2, 4));
const check2 = str.slice(4, 6);
if (check1 !== '30' || length !== str.length - 4 || check2 !== '02') {
throw new Error('SignResult.fromHex: Invalid signature');
}
const rLen = parseByte(str.slice(6, 8));
const rEnd = 8 + rLen;
const r = hexToNumber(str.slice(8, rEnd));
const check3 = str.slice(rEnd, rEnd + 2);
if (check3 !== '02') {
throw new Error('SignResult.fromHex: Invalid signature');
}
const sLen = parseByte(str.slice(rEnd + 2, rEnd + 4));
const sStart = rEnd + 4;
const s = hexToNumber(str.slice(sStart, sStart + sLen));
return new SignResult(r, s);
}
formatLength(hex) {
return (hex.length / 2).toString(16).padStart(2, "0");
}
formatNumberToHex(num) {
const res = num.toString(16);
return res.length & 1 ? `0${res}` : res;
}
toHex() {
const rHex = `00${this.formatNumberToHex(this.r)}`;
const sHex = this.formatNumberToHex(this.s);
const rLen = this.formatLength(rHex);
const sLen = this.formatLength(sHex);
const length = this.formatNumberToHex(rHex.length / 2 + sHex.length / 2 + 4);
toHex(compressed = false) {
const rHex = numberToHex(this.r);
const sHex = numberToHex(this.s);
if (compressed)
return sHex;
const rLen = numberToHex(rHex.length / 2);
const sLen = numberToHex(sHex.length / 2);
const length = numberToHex(rHex.length / 2 + sHex.length / 2 + 4);
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;

@@ -185,24 +223,22 @@ }

exports.BASE_POINT = new Point(55066263022277343669578718895168534326250603453777594175500187360389116729240n, 32670510020758816978083085130507043184471273380659243275938904335757337482424n);
let secureRandom = (bytesLength) => new Uint8Array(bytesLength);
if (typeof window == "object" && "crypto" in window) {
secureRandom = (bytesLength) => {
const array = new Uint8Array(bytesLength);
window.crypto.getRandomValues(array);
return array;
let hmac;
if (typeof window == 'object' && 'crypto' in window) {
hmac = async (key, message) => {
const ckey = await window.crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign', 'verify']);
const buffer = await window.crypto.subtle.sign('HMAC', ckey, message);
return new Uint8Array(buffer);
};
}
else if (typeof process === "object" && "node" in process.versions) {
else if (typeof process === 'object' && 'node' in process.versions) {
const req = require;
const { randomBytes } = req("crypto");
secureRandom = (bytesLength) => {
const b = randomBytes(bytesLength);
return new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
const { createHmac } = req('crypto');
hmac = async (key, message) => {
const hash = createHmac('sha256', key);
hash.update(message);
return Uint8Array.from(hash.digest());
};
}
else {
throw new Error("The environment doesn't have cryptographically secure random function");
throw new Error("The environment doesn't have hmac-sha256 function");
}
function getRandomValue(bytesLength) {
return arrayLEToNumber(secureRandom(bytesLength));
}
function powMod(x, power, order) {

@@ -220,32 +256,33 @@ let res = 1n;

function arrayToHex(uint8a) {
return Array.from(uint8a)
.map(c => c.toString(16).padStart(2, "0"))
.join("");
}
function hexToArray(hash) {
hash = hash.length & 1 ? `0${hash}` : hash;
const len = hash.length;
const result = new Uint8Array(len / 2);
for (let i = 0, j = 0; i < len - 1; i += 2, j++) {
result[j] = parseInt(hash[i] + hash[i + 1], 16);
let hex = '';
for (let i = 0; i < uint8a.length; i++) {
hex += uint8a[i].toString(16).padStart(2, '0');
}
return result;
return hex;
}
function numberToHex(num) {
const hex = num.toString(16);
return hex.length & 1 ? `0${hex}` : hex;
}
function hexToNumber(hex) {
if (typeof hex !== 'string') {
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
}
return BigInt(`0x${hex}`);
}
function arrayToNumber(bytes) {
let value = 0n;
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++) {
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j));
function hexToArray(hex) {
hex = hex.length & 1 ? `0${hex}` : hex;
const array = new Uint8Array(hex.length / 2);
for (let i = 0; i < array.length; i++) {
let j = i * 2;
array[i] = Number.parseInt(hex.slice(j, j + 2), 16);
}
return value;
return array;
}
function arrayLEToNumber(bytes) {
let value = 0n;
for (let i = 0; i < bytes.length; i++) {
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(i));
}
return value;
function arrayToNumber(bytes) {
return hexToNumber(arrayToHex(bytes));
}
function pad64(num) {
return num.toString(16).padStart(64, '0');
}
function mod(a, b) {

@@ -275,4 +312,4 @@ const result = a % b;

function truncateHash(hash) {
hash = typeof hash === "string" ? hash : arrayToHex(hash);
let msg = BigInt(`0x${hash || "0"}`);
hash = typeof hash === 'string' ? hash : arrayToHex(hash);
let msg = hexToNumber(hash || '0');
const delta = (hash.length / 2) * 8 - PRIME_SIZE;

@@ -287,22 +324,60 @@ if (delta > 0) {

}
function isValidPrivateKey(privateKey) {
if (privateKey instanceof Uint8Array) {
return privateKey.length <= 32;
function concatTypedArrays(...args) {
const result = new Uint8Array(args.reduce((a, arr) => a + arr.length, 0));
for (let i = 0, pad = 0; i < args.length; i++) {
const arr = args[i];
result.set(arr, pad);
pad += arr.length;
}
if (typeof privateKey === "string") {
return /^[0-9a-f]{0,64}$/i.test(privateKey);
return result;
}
async function getQRSrfc6979(hash, privateKey) {
const num = typeof hash === 'string' ? hexToNumber(hash) : arrayToNumber(hash);
const h1 = hexToArray(pad64(num));
const x = hexToArray(pad64(privateKey));
const h1n = arrayToNumber(h1);
let v = new Uint8Array(32).fill(1);
let k = new Uint8Array(32).fill(0);
const b0 = Uint8Array.from([0x00]);
const b1 = Uint8Array.from([0x01]);
const concat = concatTypedArrays;
k = await hmac(k, concat(v, b0, x, h1));
v = await hmac(k, v);
k = await hmac(k, concat(v, b1, x, h1));
v = await hmac(k, v);
for (let i = 0; i < 1000; i++) {
v = await hmac(k, v);
const T = arrayToNumber(v);
let qrs;
if (isValidPrivateKey(T) && (qrs = calcQRSFromK(T, h1n, privateKey))) {
return qrs;
}
k = await hmac(k, concat(v, b0));
v = await hmac(k, v);
}
return privateKey.toString(16).length <= 64;
throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid');
}
function isValidPrivateKey(privateKey) {
return 0 < privateKey && privateKey < exports.PRIME_ORDER;
}
function calcQRSFromK(k, msg, priv) {
const q = exports.BASE_POINT.multiply(k);
const r = mod(q.x, exports.PRIME_ORDER);
const s = mod(modInverse(k, exports.PRIME_ORDER) * (msg + r * priv), exports.PRIME_ORDER);
if (r === 0n || s === 0n)
return;
return [q, r, s];
}
function normalizePrivateKey(privateKey) {
if (!isValidPrivateKey(privateKey)) {
throw new Error("Private key is invalid. It should be less than 257 bit or contain valid hex string");
}
let key;
if (privateKey instanceof Uint8Array) {
return arrayToNumber(privateKey);
key = arrayToNumber(privateKey);
}
if (typeof privateKey === "string") {
return hexToNumber(privateKey);
else if (typeof privateKey === 'string') {
key = hexToNumber(privateKey);
}
return BigInt(privateKey);
else {
key = BigInt(privateKey);
}
return key;
}

@@ -313,5 +388,3 @@ function normalizePublicKey(publicKey) {

function normalizeSignature(signature) {
return signature instanceof SignResult
? signature
: SignResult.fromHex(signature);
return signature instanceof SignResult ? signature : SignResult.fromHex(signature);
}

@@ -325,3 +398,3 @@ function recoverPublicKey(hash, signature, recovery) {

const point = Point.fromPrivateKey(privateKey);
if (typeof privateKey === "string") {
if (typeof privateKey === 'string') {
return point.toHex(isCompressed);

@@ -337,15 +410,15 @@ }

exports.getSharedSecret = getSharedSecret;
function sign(hash, privateKey, { k = getRandomValue(5), recovered, canonical } = {}) {
const number = normalizePrivateKey(privateKey);
k = BigInt(k);
const message = truncateHash(hash);
const q = exports.BASE_POINT.multiply(k);
const r = mod(q.x, exports.PRIME_ORDER);
let s = mod(modInverse(k, exports.PRIME_ORDER) * (message + r * number), exports.PRIME_ORDER);
let recovery = (q.x === r ? 0n : 2n) | (q.y & 1n);
async function sign(hash, privateKey, { recovered, canonical } = {}) {
const priv = normalizePrivateKey(privateKey);
if (!isValidPrivateKey(priv)) {
throw new Error('Private key is invalid. Expected 0 < key < PRIME_ORDER');
}
const [q, r, s] = await getQRSrfc6979(hash, priv);
let recovery = (q.x === r ? 0 : 2) | Number(q.y & 1n);
let adjustedS = s;
if (s > HIGH_NUMBER && canonical) {
s = exports.PRIME_ORDER - s;
recovery ^= 1n;
adjustedS = exports.PRIME_ORDER - s;
recovery ^= 1;
}
const res = new SignResult(r, s).toHex();
const res = new SignResult(r, adjustedS).toHex();
const hashed = hash instanceof Uint8Array ? hexToArray(res) : res;

@@ -356,11 +429,16 @@ return recovered ? [hashed, recovery] : hashed;

function verify(signature, hash, publicKey) {
const message = truncateHash(hash);
const msg = truncateHash(hash);
const sign = normalizeSignature(signature);
const point = normalizePublicKey(publicKey);
const sign = normalizeSignature(signature);
const w = modInverse(sign.s, exports.PRIME_ORDER);
const point1 = exports.BASE_POINT.multiply(mod(message * w, exports.PRIME_ORDER));
const point1 = exports.BASE_POINT.multiply(mod(msg * w, exports.PRIME_ORDER));
const point2 = point.multiply(mod(sign.r * w, exports.PRIME_ORDER));
const { x } = point1.add(point2);
return x === sign.r;
const point3 = point1.add(point2);
return point3.x === sign.r;
}
exports.verify = verify;
exports.utils = {
isValidPrivateKey(privateKey) {
return isValidPrivateKey(normalizePrivateKey(privateKey));
}
};
{
"name": "noble-secp256k1",
"version": "0.3.2",
"version": "0.4.0",
"description": "Noble secp256k1. High-security, easily auditable, 0-dep, 1-file pubkey & ECDSA.",

@@ -15,3 +15,3 @@ "main": "index.js",

"jest": {
"testRegex": "/.*?\\.test.ts",
"testRegex": "/test/.*?\\.ts",
"transform": {

@@ -34,3 +34,3 @@ "^.+\\.ts$": "ts-jest"

"ts-jest": "^25.2.0",
"typescript": "^3.7.5"
"typescript": "3.8.3"
},

@@ -37,0 +37,0 @@ "keywords": [

@@ -5,3 +5,3 @@ # noble-secp256k1

Algorithmically resistant to timing attacks.
Algorithmically resistant to timing attacks. With tens of thousands test vectors.

@@ -76,11 +76,15 @@ ### This library belongs to *noble* crypto

```typescript
function sign(hash: Uint8Array, privateKey: Uint8Array | bigint, opts?: Options): Uint8Array;
function sign(hash: string, privateKey: string | bigint, opts?: Options): string;
function sign(hash: Uint8Array, privateKey: Uint8Array | bigint, opts?: Options): Promise<Uint8Array>;
function sign(hash: string, privateKey: string | bigint, opts?: Options): Promise<string>;
function sign(hash: Uint8Array, privateKey: Uint8Array | bigint, opts?: Options): Promise<[Uint8Array | string, number]>;
```
Generates deterministic ECDSA signature as per RFC 6979. Asynchronous, so use `await`.
- `hash: Uint8Array | string` - message hash which would be signed
- `privateKey: Uint8Array | string | bigint` - private key which will sign the hash
- `options?: Options` - *optional* object related to signature value and format
- `options?.k: number | bigint` - random seed. Default is one from `crypto.getRandomValues()`. **Must be cryptographically secure**, which means `Math.random()` won't work.
- `options?.recovered: boolean` - determines whether the recovered bit should be included in the result. In this case, the result would be an array of two items.
- `options?.canonical: boolean` - determines whether a signature `s` should be sorted by half prime order
- `options?.recovered: boolean = false` - determines whether the recovered bit should be included in the result. In this case, the result would be an array of two items.
- `options?.canonical: boolean = false` - determines whether a signature `s` should be no more than 1/2 prime order
- Returns DER encoded ECDSA signature, as hex uint8a / string and recovered bit if `options.recovered == true`.

@@ -144,8 +148,13 @@

static fromHex(hex: Uint8Array | string);
toHex()
toHex(): string;
}
```
There is an option to have 2x speed-up by precomputing powers of two. Use `generate-precomputes` script & include the result in index.ts.
## Contributing
1. Clone the repository.
2. `npm install` to install build dependencies like TypeScript
3. `npm run compile` to compile TypeScript code
4. `npm run test` to run jest on `test/index.ts`
## Security

@@ -152,0 +161,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc