Socket
Socket
Sign inDemoInstall

@noble/secp256k1

Package Overview
Dependencies
Maintainers
1
Versions
20
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 1.5.2 to 1.5.3

143

lib/esm/index.js

@@ -47,9 +47,13 @@ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */

equals(other) {
const a = this;
const b = other;
const az2 = mod(a.z * a.z);
const az3 = mod(a.z * az2);
const bz2 = mod(b.z * b.z);
const bz3 = mod(b.z * bz2);
return mod(a.x * bz2) === mod(az2 * b.x) && mod(a.y * bz3) === mod(az3 * b.y);
if (!(other instanceof JacobianPoint))
throw new TypeError('JacobianPoint expected');
const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other;
const Z1Z1 = mod(Z1 ** _2n);
const Z2Z2 = mod(Z2 ** _2n);
const U1 = mod(X1 * Z2Z2);
const U2 = mod(X2 * Z1Z1);
const S1 = mod(mod(Y1 * Z2) * Z2Z2);
const S2 = mod(mod(Y2 * Z1) * Z1Z1);
return U1 === U2 && S1 === S2;
}

@@ -60,9 +64,7 @@ negate() {

double() {
const X1 = this.x;
const Y1 = this.y;
const Z1 = this.z;
const { x: X1, y: Y1, z: Z1 } = this;
const A = mod(X1 ** _2n);
const B = mod(Y1 ** _2n);
const C = mod(B ** _2n);
const D = mod(_2n * (mod(mod((X1 + B) ** _2n)) - A - C));
const D = mod(_2n * (mod((X1 + B) ** _2n) - A - C));
const E = mod(_3n * A);

@@ -76,11 +78,6 @@ const F = mod(E ** _2n);

add(other) {
if (!(other instanceof JacobianPoint)) {
throw new TypeError('JacobianPoint#add: expected JacobianPoint');
}
const X1 = this.x;
const Y1 = this.y;
const Z1 = this.z;
const X2 = other.x;
const Y2 = other.y;
const Z2 = other.z;
if (!(other instanceof JacobianPoint))
throw new TypeError('JacobianPoint expected');
const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other;
if (X2 === _0n || Y2 === _0n)

@@ -94,3 +91,3 @@ return this;

const U2 = mod(X2 * Z1Z1);
const S1 = mod(Y1 * Z2 * Z2Z2);
const S1 = mod(mod(Y1 * Z2) * Z2Z2);
const S2 = mod(mod(Y2 * Z1) * Z1Z1);

@@ -120,4 +117,12 @@ const H = mod(U2 - U1);

let n = normalizeScalar(scalar);
const G = JacobianPoint.BASE;
const P0 = JacobianPoint.ZERO;
if (n === _0n)
return P0;
if (this.equals(P0) || n === _1n)
return this;
if (this.equals(G))
return this.wNAF(n).p;
if (!USE_ENDOMORPHISM) {
let p = JacobianPoint.ZERO;
let p = P0;
let d = this;

@@ -133,4 +138,4 @@ while (n > _0n) {

let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
let k1p = JacobianPoint.ZERO;
let k2p = JacobianPoint.ZERO;
let k1p = P0;
let k2p = P0;
let d = this;

@@ -186,3 +191,3 @@ while (k1 > _0n || k2 > _0n) {

let f = JacobianPoint.ZERO;
const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1;
const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W);
const windowSize = 2 ** (W - 1);

@@ -220,3 +225,3 @@ const mask = BigInt(2 ** W - 1);

if (USE_ENDOMORPHISM) {
let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
const { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);

@@ -233,3 +238,3 @@ let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);

else {
let { p, f } = this.wNAF(n, affinePoint);
const { p, f } = this.wNAF(n, affinePoint);
point = p;

@@ -241,6 +246,12 @@ fake = f;

toAffine(invZ = invert(this.z)) {
const invZ2 = invZ ** _2n;
const x = mod(this.x * invZ2);
const y = mod(this.y * invZ2 * invZ);
return new Point(x, y);
const { x, y, z } = this;
const iz1 = invZ;
const iz2 = mod(iz1 * iz1);
const iz3 = mod(iz2 * iz1);
const ax = mod(x * iz2);
const ay = mod(y * iz3);
const zz = mod(z * iz1);
if (zz !== _1n)
throw new Error('invZ was invalid');
return new Point(ax, ay);
}

@@ -444,9 +455,9 @@ }

toDERHex(isCompressed = false) {
const sHex = sliceDER(numberToHex(this.s));
const sHex = sliceDER(numberToHexUnpadded(this.s));
if (isCompressed)
return sHex;
const rHex = sliceDER(numberToHex(this.r));
const rLen = numberToHex(rHex.length / 2);
const sLen = numberToHex(sHex.length / 2);
const length = numberToHex(rHex.length / 2 + sHex.length / 2 + 4);
const rHex = sliceDER(numberToHexUnpadded(this.r));
const rLen = numberToHexUnpadded(rHex.length / 2);
const sLen = numberToHexUnpadded(sHex.length / 2);
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4);
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;

@@ -502,3 +513,3 @@ }

}
function numberToHex(num) {
function numberToHexUnpadded(num) {
const hex = num.toString(16);

@@ -545,3 +556,3 @@ return hex.length & 1 ? `0${hex}` : hex;

const result = a % b;
return result >= 0 ? result : b + result;
return result >= _0n ? result : b + result;
}

@@ -599,21 +610,18 @@ function pow2(x, power) {

}
function invertBatch(nums, n = CURVE.P) {
const len = nums.length;
const scratch = new Array(len);
let acc = _1n;
for (let i = 0; i < len; i++) {
if (nums[i] === _0n)
continue;
function invertBatch(nums, p = CURVE.P) {
const scratch = new Array(nums.length);
const lastMultiplied = nums.reduce((acc, num, i) => {
if (num === _0n)
return acc;
scratch[i] = acc;
acc = mod(acc * nums[i], n);
}
acc = invert(acc, n);
for (let i = len - 1; i >= 0; i--) {
if (nums[i] === _0n)
continue;
const tmp = mod(acc * nums[i], n);
nums[i] = mod(acc * scratch[i], n);
acc = tmp;
}
return nums;
return mod(acc * num, p);
}, _1n);
const inverted = invert(lastMultiplied, p);
nums.reduceRight((acc, num, i) => {
if (num === _0n)
return acc;
scratch[i] = mod(acc * scratch[i], p);
return mod(acc * num, p);
}, inverted);
return scratch;
}

@@ -638,4 +646,5 @@ const divNearest = (a, b) => (a + b / _2n) / b;

k2 = n - k2;
if (k1 > POW_2_128 || k2 > POW_2_128)
throw new Error('splitScalarEndo: Endomorphism failed');
if (k1 > POW_2_128 || k2 > POW_2_128) {
throw new Error('splitScalarEndo: Endomorphism failed, k=' + k);
}
return { k1neg, k1, k2neg, k2 };

@@ -992,2 +1001,11 @@ }

},
hashToPrivateKey: (hash) => {
hash = ensureBytes(hash);
if (hash.length < 40 || hash.length > 1024)
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
const num = mod(bytesToNumber(hash), CURVE.n);
if (num === _0n || num === _1n)
throw new Error('Invalid private key');
return numTo32b(num);
},
randomBytes: (bytesLength = 32) => {

@@ -1006,10 +1024,3 @@ if (crypto.web) {

randomPrivateKey: () => {
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');
return utils.hashToPrivateKey(utils.randomBytes(40));
},

@@ -1016,0 +1027,0 @@ bytesToHex,

@@ -108,2 +108,3 @@ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */

isValidPrivateKey(privateKey: PrivKey): boolean;
hashToPrivateKey: (hash: Hex) => Uint8Array;
randomBytes: (bytesLength?: number) => Uint8Array;

@@ -110,0 +111,0 @@ randomPrivateKey: () => Uint8Array;

@@ -53,9 +53,13 @@ "use strict";

equals(other) {
const a = this;
const b = other;
const az2 = mod(a.z * a.z);
const az3 = mod(a.z * az2);
const bz2 = mod(b.z * b.z);
const bz3 = mod(b.z * bz2);
return mod(a.x * bz2) === mod(az2 * b.x) && mod(a.y * bz3) === mod(az3 * b.y);
if (!(other instanceof JacobianPoint))
throw new TypeError('JacobianPoint expected');
const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other;
const Z1Z1 = mod(Z1 ** _2n);
const Z2Z2 = mod(Z2 ** _2n);
const U1 = mod(X1 * Z2Z2);
const U2 = mod(X2 * Z1Z1);
const S1 = mod(mod(Y1 * Z2) * Z2Z2);
const S2 = mod(mod(Y2 * Z1) * Z1Z1);
return U1 === U2 && S1 === S2;
}

@@ -66,9 +70,7 @@ negate() {

double() {
const X1 = this.x;
const Y1 = this.y;
const Z1 = this.z;
const { x: X1, y: Y1, z: Z1 } = this;
const A = mod(X1 ** _2n);
const B = mod(Y1 ** _2n);
const C = mod(B ** _2n);
const D = mod(_2n * (mod(mod((X1 + B) ** _2n)) - A - C));
const D = mod(_2n * (mod((X1 + B) ** _2n) - A - C));
const E = mod(_3n * A);

@@ -82,11 +84,6 @@ const F = mod(E ** _2n);

add(other) {
if (!(other instanceof JacobianPoint)) {
throw new TypeError('JacobianPoint#add: expected JacobianPoint');
}
const X1 = this.x;
const Y1 = this.y;
const Z1 = this.z;
const X2 = other.x;
const Y2 = other.y;
const Z2 = other.z;
if (!(other instanceof JacobianPoint))
throw new TypeError('JacobianPoint expected');
const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other;
if (X2 === _0n || Y2 === _0n)

@@ -100,3 +97,3 @@ return this;

const U2 = mod(X2 * Z1Z1);
const S1 = mod(Y1 * Z2 * Z2Z2);
const S1 = mod(mod(Y1 * Z2) * Z2Z2);
const S2 = mod(mod(Y2 * Z1) * Z1Z1);

@@ -126,4 +123,12 @@ const H = mod(U2 - U1);

let n = normalizeScalar(scalar);
const G = JacobianPoint.BASE;
const P0 = JacobianPoint.ZERO;
if (n === _0n)
return P0;
if (this.equals(P0) || n === _1n)
return this;
if (this.equals(G))
return this.wNAF(n).p;
if (!USE_ENDOMORPHISM) {
let p = JacobianPoint.ZERO;
let p = P0;
let d = this;

@@ -139,4 +144,4 @@ while (n > _0n) {

let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
let k1p = JacobianPoint.ZERO;
let k2p = JacobianPoint.ZERO;
let k1p = P0;
let k2p = P0;
let d = this;

@@ -192,3 +197,3 @@ while (k1 > _0n || k2 > _0n) {

let f = JacobianPoint.ZERO;
const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1;
const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W);
const windowSize = 2 ** (W - 1);

@@ -226,3 +231,3 @@ const mask = BigInt(2 ** W - 1);

if (USE_ENDOMORPHISM) {
let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
const { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);

@@ -239,3 +244,3 @@ let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);

else {
let { p, f } = this.wNAF(n, affinePoint);
const { p, f } = this.wNAF(n, affinePoint);
point = p;

@@ -247,6 +252,12 @@ fake = f;

toAffine(invZ = invert(this.z)) {
const invZ2 = invZ ** _2n;
const x = mod(this.x * invZ2);
const y = mod(this.y * invZ2 * invZ);
return new Point(x, y);
const { x, y, z } = this;
const iz1 = invZ;
const iz2 = mod(iz1 * iz1);
const iz3 = mod(iz2 * iz1);
const ax = mod(x * iz2);
const ay = mod(y * iz3);
const zz = mod(z * iz1);
if (zz !== _1n)
throw new Error('invZ was invalid');
return new Point(ax, ay);
}

@@ -451,9 +462,9 @@ }

toDERHex(isCompressed = false) {
const sHex = sliceDER(numberToHex(this.s));
const sHex = sliceDER(numberToHexUnpadded(this.s));
if (isCompressed)
return sHex;
const rHex = sliceDER(numberToHex(this.r));
const rLen = numberToHex(rHex.length / 2);
const sLen = numberToHex(sHex.length / 2);
const length = numberToHex(rHex.length / 2 + sHex.length / 2 + 4);
const rHex = sliceDER(numberToHexUnpadded(this.r));
const rLen = numberToHexUnpadded(rHex.length / 2);
const sLen = numberToHexUnpadded(sHex.length / 2);
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4);
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;

@@ -510,3 +521,3 @@ }

}
function numberToHex(num) {
function numberToHexUnpadded(num) {
const hex = num.toString(16);

@@ -553,3 +564,3 @@ return hex.length & 1 ? `0${hex}` : hex;

const result = a % b;
return result >= 0 ? result : b + result;
return result >= _0n ? result : b + result;
}

@@ -607,21 +618,18 @@ function pow2(x, power) {

}
function invertBatch(nums, n = CURVE.P) {
const len = nums.length;
const scratch = new Array(len);
let acc = _1n;
for (let i = 0; i < len; i++) {
if (nums[i] === _0n)
continue;
function invertBatch(nums, p = CURVE.P) {
const scratch = new Array(nums.length);
const lastMultiplied = nums.reduce((acc, num, i) => {
if (num === _0n)
return acc;
scratch[i] = acc;
acc = mod(acc * nums[i], n);
}
acc = invert(acc, n);
for (let i = len - 1; i >= 0; i--) {
if (nums[i] === _0n)
continue;
const tmp = mod(acc * nums[i], n);
nums[i] = mod(acc * scratch[i], n);
acc = tmp;
}
return nums;
return mod(acc * num, p);
}, _1n);
const inverted = invert(lastMultiplied, p);
nums.reduceRight((acc, num, i) => {
if (num === _0n)
return acc;
scratch[i] = mod(acc * scratch[i], p);
return mod(acc * num, p);
}, inverted);
return scratch;
}

@@ -646,4 +654,5 @@ const divNearest = (a, b) => (a + b / _2n) / b;

k2 = n - k2;
if (k1 > POW_2_128 || k2 > POW_2_128)
throw new Error('splitScalarEndo: Endomorphism failed');
if (k1 > POW_2_128 || k2 > POW_2_128) {
throw new Error('splitScalarEndo: Endomorphism failed, k=' + k);
}
return { k1neg, k1, k2neg, k2 };

@@ -1005,2 +1014,11 @@ }

},
hashToPrivateKey: (hash) => {
hash = ensureBytes(hash);
if (hash.length < 40 || hash.length > 1024)
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
const num = mod(bytesToNumber(hash), CURVE.n);
if (num === _0n || num === _1n)
throw new Error('Invalid private key');
return numTo32b(num);
},
randomBytes: (bytesLength = 32) => {

@@ -1019,10 +1037,3 @@ if (crypto.web) {

randomPrivateKey: () => {
let i = 8;
while (i--) {
const b32 = exports.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');
return exports.utils.hashToPrivateKey(exports.utils.randomBytes(40));
},

@@ -1029,0 +1040,0 @@ bytesToHex,

{
"name": "@noble/secp256k1",
"version": "1.5.2",
"version": "1.5.3",
"description": "Fastest JS implementation of secp256k1. Independently audited, high-security, 0-dependency ECDSA & Schnorr signatures",

@@ -5,0 +5,0 @@ "files": [

@@ -5,5 +5,5 @@ # noble-secp256k1 ![Node CI](https://github.com/paulmillr/noble-secp256k1/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)

an elliptic curve that could be used for asymmetric encryption,
ECDH key agreement protocol and signature schemes. Supports deterministic **ECDSA** from RFC6979 and **Schnorr** signatures from BIP0340.
ECDH key agreement protocol and signature schemes. Supports deterministic **ECDSA** from RFC6979 and **Schnorr** signatures from [BIP0340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
[**Audited**](#security) with crowdfunding by an independent security firm. Tested against thousands of test vectors from a different library. Check out [the online demo](https://paulmillr.com/ecc) and blog post: [Learning fast elliptic-curve cryptography in JS](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/)
[**Audited**](#security) by an independent security firm. Check out [the online demo](https://paulmillr.com/ecc) and blog post: [Learning fast elliptic-curve cryptography in JS](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/)

@@ -34,28 +34,25 @@ ### This library belongs to *noble* crypto

import * as secp from "@noble/secp256k1";
// If you're using single file, use global variable instead:
// nobleSecp256k1
// If you're using single file, use global variable instead: `window.nobleSecp256k1`
(async () => {
// You pass a hex string, or Uint8Array
const privateKey = "6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e";
const message = "hello world";
const messageHash = await secp.utils.sha256(message);
// keys, messages & other inputs can be Uint8Arrays or hex strings
// Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) === 'deadbeef'
const privateKey = secp.utils.randomPrivateKey();
const messageHash = await secp.utils.sha256("hello world");
const publicKey = secp.getPublicKey(privateKey);
const signature = await secp.sign(messageHash, privateKey);
const isSigned = secp.verify(signature, messageHash, publicKey);
const isValid = secp.verify(signature, messageHash, publicKey);
// Sigs with improved security (see README)
// Signatures with improved security (see extraEntropy docs in README)
const signatureE = await secp.sign(messageHash, privateKey, { extraEntropy: true });
// Malleable signatures, compatible with openssl
const signatureM = await secp.sign(messageHash, privateKey, { canonical: false });
// If you need hex strings
const hex = secp.utils.bytesToHex;
console.log(hex(publicKey));
// Default output is Uint8Array. If you need hex string as an output:
console.log(secp.utils.bytesToHex(publicKey));
// Supports Schnorr signatures
// Schnorr signatures
const rpub = secp.schnorr.getPublicKey(privateKey);
const rsignature = await secp.schnorr.sign(message, privateKey);
const risSigned = await secp.schnorr.verify(rsignature, message, rpub);
const risValid = await secp.schnorr.verify(rsignature, message, rpub);
})();

@@ -68,31 +65,16 @@ ```

- `deno run --import-map=imports.json app.ts`
- app.ts: `import * as secp from "https://deno.land/x/secp256k1/mod.ts";`
- imports.json: `{"imports": {"crypto": "https://deno.land/std@0.119.0/node/crypto.ts"}}`
- `app.ts`
```typescript
import * as secp from "https://deno.land/x/secp256k1/mod.ts";
const publicKey = secp.getPublicKey(secp.utils.randomPrivateKey());
console.log(publicKey);
```
- `imports.json`
```json
{
"imports": {
"crypto": "https://deno.land/std@0.119.0/node/crypto.ts"
}
}
```
## API
- [`getPublicKey(privateKey)`](#getpublickeyprivatekey)
- [`getSharedSecret(privateKeyA, publicKeyB)`](#getsharedsecretprivatekeya-publickeyb)
- [`sign(msgHash, privateKey)`](#signmsghash-privatekey)
- [`verify(signature, msgHash, publicKey)`](#verifysignature-msghash-publickey)
- [`getSharedSecret(privateKeyA, publicKeyB)`](#getsharedsecretprivatekeya-publickeyb)
- [`recoverPublicKey(hash, signature, recovery)`](#recoverpublickeyhash-signature-recovery)
- [`schnorr.getPublicKey(privateKey)`](#schnorrgetpublickeyprivatekey)
- [`schnorr.sign(hash, privateKey)`](#schnorrsignhash-privatekey)
- [`schnorr.verify(signature, hash, publicKey)`](#schnorrverifysignature-hash-publickey)
- [Helpers](#helpers)
- [`schnorr.sign(message, privateKey)`](#schnorrsignmessage-privatekey)
- [`schnorr.verify(signature, message, publicKey)`](#schnorrverifysignature-message-publickey)
- [Utilities](#utilities)

@@ -103,22 +85,9 @@ ##### `getPublicKey(privateKey)`

```
`privateKey` will be used to generate public key.
Public key is generated by doing scalar multiplication of a base Point(x, y) by a fixed
integer. The result is another `Point(x, y)` which we will by default encode to hex Uint8Array.
`isCompressed` (default is `false`) determines whether the output should contain `y` coordinate of the point.
To get Point instance, use `Point.fromPrivateKey(privateKey)`.
Creates public key for the corresponding private key. Could return compressed 33-byte keys, or
uncompressed 65-byte keys.
##### `getSharedSecret(privateKeyA, publicKeyB)`
```typescript
function getSharedSecret(privateKeyA: Uint8Array | string | bigint, publicKeyB: Uint8Array | string | Point): Uint8Array;
```
Internally, it does `Point.BASE.multiply(privateKey)`. If you need actual `Point` instead of
`Uint8Array`, use `Point.fromPrivateKey(privateKey)`.
Computes ECDH (Elliptic Curve Diffie-Hellman) shared secret between a private key and a different public key.
To get Point instance, use `Point.fromHex(publicKeyB).multiply(privateKeyA)`.
To speed-up the function massively by precomputing EC multiplications,
use `getSharedSecret(privateKeyA, secp.utils.precompute(8, publicKeyB))`
##### `sign(msgHash, privateKey)`

@@ -132,29 +101,28 @@ ```typescript

It's strongly recommended to pass `{extraEntropy: true}` to improve security of signatures:
- In case the entropy generator is broken, signatures would be just like they are without the option
- It would help a lot in case there is an error somewhere in `k` generation. Exposing `k` could leak private keys
- Schnorr signatures are adding extra entropy every time
- The only disadvantage to this is the fact signatures won't be exactly equal to fully-deterministic sigs;
think backwards-compatibility with test vectors. They would still be valid, though
`sign` arguments:
- `msgHash: Uint8Array | string` - message hash which would be signed
- `msgHash: Uint8Array | string` - 32-byte 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?.recovered: boolean = false` - 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 = true` - whether a signature `s` should be no more than 1/2 prime order.
`true` makes signatures compatible with libsecp256k1,
`false` makes signatures compatible with openssl
- `options?.extraEntropy: Uint8Array | string | true` - additional entropy `k'` for deterministic signature, follows section 3.6 of RFC6979. When `true`, it would automatically be filled with 32 bytes of cryptographically secure entropy
- `options?.der: boolean = true` - whether the returned signature should be in DER format. If `false`, it would be in Compact format (32-byte r + 32-byte s)
- `options?: Options` - *optional* object related to signature value and format with following keys:
- `recovered: boolean = false` - whether the recovered bit should be included in the result. In this case, the result would be an array of two items.
- `canonical: boolean = true` - whether a signature `s` should be no more than 1/2 prime order.
`true` (default) makes signatures compatible with libsecp256k1,
`false` makes signatures compatible with openssl
- `der: boolean = true` - whether the returned signature should be in DER format. If `false`, it would be in Compact format (32-byte r + 32-byte s)
- `extraEntropy: Uint8Array | string | true` - additional entropy `k'` for deterministic signature, follows section 3.6 of RFC6979. When `true`, it would automatically be filled with 32 bytes of cryptographically secure entropy. **Strongly recommended** to pass `true` to improve security:
- Schnorr signatures are doing it every time
- It would help a lot in case there is an error somewhere in `k` generation. Exposing `k` could leak private keys
- If the entropy generator is broken, signatures would be the same as they are without the option
- Signatures with extra entropy would have different `r` / `s`, which means they
would still be valid, but may break some test vectors if you're cross-testing against other libs
The function is asynchronous because we're utilizing built-in HMAC API to not rely on dependencies.
```typescript
function signSync(msgHash: Uint8Array | string, privateKey: Uint8Array | string, opts?: Options): Uint8Array | [Uint8Array, number];
```
`signSync` counterpart could also be used, you need to set `utils.hmacSha256Sync` to a function with signature `key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array`. Example with `noble-hashes` package:
```ts
const { hmac } = require('@noble/hashes/hmac');
const { sha256 } = require('@noble/hashes/sha256');
import { hmac } = from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
secp256k1.utils.hmacSha256Sync = (key: Uint8Array, ...msgs: Uint8Array[]) => {

@@ -179,7 +147,24 @@ const h = hmac.create(sha256, key);

- `options?: Options` - *optional* object related to signature value and format
- `options?.strict: boolean = true` - whether a signature `s` should be no more than 1/2 prime order.
`true` makes signatures compatible with libsecp256k1,
`false` makes signatures compatible with openssl
- `strict: boolean = true` - whether a signature `s` should be no more than 1/2 prime order.
`true` (default) makes signatures compatible with libsecp256k1,
`false` makes signatures compatible with openssl
- Returns `boolean`: `true` if `signature == hash`; otherwise `false`
##### `getSharedSecret(privateKeyA, publicKeyB)`
```typescript
function getSharedSecret(privateKeyA: Uint8Array | string | bigint, publicKeyB: Uint8Array | string | Point): Uint8Array;
```
Computes ECDH (Elliptic Curve Diffie-Hellman) shared secret between a private key and a different public key.
- To get Point instance, use `Point.fromHex(publicKeyB).multiply(privateKeyA)`.
- If you have one public key you'll be creating lots of secrets against,
consider massive speed-up by using precomputations:
```js
const pub = secp.utils.precompute(8, publicKeyB);
// Use pub everywhere instead of publicKeyB
getSharedSecret(privKey, pub); // Now 12x faster
```
##### `recoverPublicKey(hash, signature, recovery)`

@@ -203,9 +188,9 @@ ```typescript

Returns 32-byte public key. *Warning:* it is incompatible with non-schnorr pubkey.
Calculates 32-byte public key from a private key.
Specifically, its *y* coordinate may be flipped. See BIP340 for clarification.
*Warning:* it is incompatible with non-schnorr pubkey. Specifically, its *y* coordinate may be flipped. See [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) for clarification.
##### `schnorr.sign(hash, privateKey)`
##### `schnorr.sign(message, privateKey)`
```typescript
function schnorrSign(msgHash: Uint8Array | string, privateKey: Uint8Array | string, auxilaryRandom?: Uint8Array): Promise<Uint8Array>;
function schnorrSign(message: Uint8Array | string, privateKey: Uint8Array | string, auxilaryRandom?: Uint8Array): Promise<Uint8Array>;
```

@@ -215,3 +200,3 @@

- `msgHash: Uint8Array | string` - message hash which would be signed
- `message: Uint8Array | string` - message (not hash) which would be signed
- `privateKey: Uint8Array | string | bigint` - private key which will sign the hash

@@ -221,61 +206,47 @@ - `auxilaryRandom?: Uint8Array` — optional 32 random bytes. By default, the method gathers cryptogarphically secure entropy

##### `schnorr.verify(signature, hash, publicKey)`
##### `schnorr.verify(signature, message, publicKey)`
```typescript
function schnorrVerify(signature: Uint8Array | string, msgHash: Uint8Array | string, publicKey: Uint8Array | string): boolean
function schnorrVerify(signature: Uint8Array | string, message: Uint8Array | string, publicKey: Uint8Array | string): boolean
```
- `signature: Uint8Array | string | { r: bigint, s: bigint }` - object returned by the `sign` function
- `msgHash: Uint8Array | string` - message hash that needs to be verified
- `message: Uint8Array | string` - message (not hash) that needs to be verified
- `publicKey: Uint8Array | string | Point` - e.g. that was generated from `privateKey` by `getPublicKey`
- Returns `boolean`: `true` if `signature == hash`; otherwise `false`
#### Point methods
#### Utilities
##### Helpers
secp256k1 exposes a few internal utilities for improved developer experience:
###### `utils.randomBytes(): Uint8Array`
```typescript
const utils: {
// Can take 40 or more bytes of uniform input e.g. from CSPRNG or KDF
// and convert them into private key, with the modulo bias being neglible.
// As per FIPS 186 B.1.1.
hashToPrivateKey: (hash: Hex) => Uint8Array;
// Returns `Uint8Array` of 32 cryptographically secure random bytes that can be used as private key
randomPrivateKey: () => Uint8Array;
// Checks private key for validity
isValidPrivateKey(privateKey: PrivKey): boolean;
Returns `Uint8Array` of 32 cryptographically secure random bytes.
// Returns `Uint8Array` of x cryptographically secure random bytes.
randomBytes: (bytesLength?: number) => Uint8Array;
// Converts Uint8Array to hex string
bytesToHex: typeof bytesToHex;
// Modular division over curve prime
mod: (number: number | bigint, modulo = CURVE.P): bigint;
sha256: (message: Uint8Array) => Promise<Uint8Array>;
hmacSha256: (key: Uint8Array, ...messages: Uint8Array[]) => Promise<Uint8Array>;
Uses `crypto.web.getRandomValues` in browser, `require('crypto').randomBytes` in node.js.
// You can set up your synchronous methods for `signSync` to work. The argument order is
// identical to async methods from above
sha256Sync: undefined;
hmacSha256Sync: undefined;
###### `utils.randomPrivateKey(): Uint8Array`
// 1. Returns cached point which you can use to pass to `getSharedSecret` or to `#multiply` by it.
// 2. Precomputes point multiplication table. Is done by default on first `getPublicKey()` call.
// If you want your first getPublicKey to take 0.16ms instead of 20ms, make sure to call
// utils.precompute() somewhere without arguments first.
precompute(windowSize?: number, point?: Point): Point;
};
Returns `Uint8Array` of 32 cryptographically secure random bytes that can be used as private key. The signature is:
```ts
(key: Uint8Array, ...msgs: Uint8Array[]): Uint8Array;
```
###### `utils.bytesToHex(bytes: Uint8Array): string`
Converts a byte array to hex string.
###### `utils.mod(number: number | bigint, modulo = CURVE.P): bigint`
Modulo operation. Note: JS `%` operator is remainder, not modulo.
###### `utils.sha256` and `utils.hmacSha256`
Asynchronous methods that calculate `SHA256` and `HMAC-SHA256`. Use browser built-ins by default.
###### `utils.sha256Sync` and `utils.hmacSha256Sync`
The functions are not defined by default, but could be used to implement `signSync` method (see above).
###### `utils.precompute(W = 8, point = BASE_POINT): Point`
Returns cached point which you can use to pass to `getSharedSecret` or to `#multiply` by it.
This is done by default, no need to run it unless you want to
disable precomputation or change window size.
We're doing scalar multiplication (used in getPublicKey etc) with
precomputed BASE_POINT values.
This slows down first getPublicKey() by milliseconds (see Speed section),
but allows to speed-up subsequent getPublicKey() calls up to 20x.
You may want to precompute values for your own point.
```typescript
secp256k1.CURVE.P // Field, 2 ** 256 - 2 ** 32 - 977

@@ -326,6 +297,7 @@ secp256k1.CURVE.n // Order, 2 ** 256 - 432420386565659656852420866394968145599

1. The library has been audited by an independent security firm cure53: [PDF](https://cure53.de/pentest-report_noble-lib.pdf). The audit has been [crowdfunded](https://gitcoin.co/grants/2451/audit-of-noble-secp256k1-cryptographic-library) by community with help of [Umbra.cash](https://umbra.cash).
1. The library has been audited by an independent security firm cure53: [PDF](https://cure53.de/pentest-report_noble-lib.pdf). See [changes since audit](https://github.com/paulmillr/noble-secp256k1/compare/1.2.0..main).
- The audit has been [crowdfunded](https://gitcoin.co/grants/2451/audit-of-noble-secp256k1-cryptographic-library) by community with help of [Umbra.cash](https://umbra.cash).
2. The library has also been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz). You can run the fuzzer by yourself to check it.
We're using built-in JS `BigInt`, which is "unsuitable for use in cryptography" as [per official spec](https://github.com/tc39/proposal-bigint#cryptography). This means that the lib is potentially vulnerable to [timing attacks](https://en.wikipedia.org/wiki/Timing_attack). But, *JIT-compiler* and *Garbage Collector* make "constant time" extremely hard to achieve in a scripting language. Which means *any other JS library doesn't use constant-time bigints*. Including bn.js or anything else. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we've hardened implementation of koblitz curve multiplication to be algorithmically constant time.
We're using built-in JS `BigInt`, which is "unsuitable for use in cryptography" as [per official spec](https://github.com/tc39/proposal-bigint#cryptography). This means that the lib is potentially vulnerable to [timing attacks](https://en.wikipedia.org/wiki/Timing_attack). But, *JIT-compiler* and *Garbage Collector* make "constant time" extremely hard to achieve in a scripting language. Which means *any other JS library doesn't use constant-time bigints*. Including bn.js or anything else. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we've hardened implementation of ec curve multiplication to be algorithmically constant time.

@@ -338,11 +310,11 @@ We however consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector.

getPublicKey(utils.randomPrivateKey()) x 6,216 ops/sec @ 160μs/op
sign x 4,789 ops/sec @ 208μs/op
verify x 923 ops/sec @ 1ms/op
recoverPublicKey x 491 ops/sec @ 2ms/op
getSharedSecret aka ecdh x 558 ops/sec @ 1790μs/op
getSharedSecret (precomputed) x 7,105 ops/sec @ 140μs/op
Point.fromHex (decompression) x 12,171 ops/sec @ 82μs/op
schnorr.sign x 409 ops/sec @ 2ms/op
schnorr.verify x 504 ops/sec @ 1ms/op
getPublicKey(utils.randomPrivateKey()) x 6,300 ops/sec @ 158μs/op
sign x 4,888 ops/sec @ 204μs/op
verify x 950 ops/sec @ 1ms/op
recoverPublicKey x 499 ops/sec @ 2ms/op
getSharedSecret aka ecdh x 576 ops/sec @ 1ms/op
getSharedSecret (precomputed) x 6,688 ops/sec @ 149μs/op
Point.fromHex (decompression) x 12,553 ops/sec @ 79μs/op
schnorr.sign x 426 ops/sec @ 2ms/op
schnorr.verify x 520 ops/sec @ 1ms/op

@@ -375,4 +347,4 @@ Compare to other libraries (`openssl` uses native bindings, not JS):

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`
3. `npm run build` to compile TypeScript code
4. `npm test` to run jest on `test/index.ts`

@@ -379,0 +351,0 @@ Special thanks to [Roman Koblov](https://github.com/romankoblov), who have helped to improve scalar multiplication speed.

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