Coder Social home page Coder Social logo

paulmillr / noble-secp256k1 Goto Github PK

View Code? Open in Web Editor NEW
695.0 14.0 104.0 1.56 MB

Fastest 4KB JS implementation of secp256k1 signatures and ECDH

Home Page: https://paulmillr.com/noble

License: MIT License

JavaScript 65.98% TypeScript 34.02%
secp256k1 ecdsa ecc cryptography rfc6979 noble bitcoin curve elliptic ethereum

noble-secp256k1's Introduction

noble-secp256k1

Fastest 4KB JS implementation of secp256k1 signatures & ECDH.

  • ✍️ Deterministic ECDSA signatures compliant with RFC6979
  • 🤝 Elliptic Curve Diffie-Hellman ECDH
  • 📦 Pure ESM, can be imported without transpilers
  • 🪶 4KB gzipped, 450 lines of code

Use larger drop-in replacement noble-curves instead, if you need additional features such as common.js, Schnorr signatures, DER encoding or support for different hash functions. To upgrade from v1 to v2, see Upgrading.

This library belongs to noble cryptography

noble-cryptography — high-security, easily auditable set of contained cryptographic libraries and tools.

Usage

npm install @noble/secp256k1

We support all major platforms and runtimes. For node.js <= 18 and React Native, additional polyfills are needed: see below.

import * as secp from '@noble/secp256k1';
// import * as secp from "https://deno.land/x/secp256k1/mod.ts"; // Deno
// import * as secp from "https://unpkg.com/@noble/secp256k1"; // Unpkg
(async () => {
  // keys, messages & other inputs can be Uint8Arrays or hex strings
  // Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) === 'deadbeef'
  const privKey = secp.utils.randomPrivateKey(); // Secure random private key
  // sha256 of 'hello world'
  const msgHash = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9';
  const pubKey = secp.getPublicKey(privKey);
  const signature = await secp.signAsync(msgHash, privKey); // Sync methods below
  const isValid = secp.verify(signature, msgHash, pubKey);

  const alicesPubkey = secp.getPublicKey(secp.utils.randomPrivateKey());
  secp.getSharedSecret(privKey, alicesPubkey); // Elliptic curve diffie-hellman
  signature.recoverPublicKey(msgHash); // Public key recovery
})();

Additional polyfills for some environments:

// 1. Enable synchronous methods.
// Only async methods are available by default, to keep the library dependency-free.
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m))
// Sync methods can be used now:
// secp.sign(msgHash, privKey);

// 2. node.js 18 and older, requires polyfilling globalThis.crypto
import { webcrypto } from 'node:crypto';
// @ts-ignore
if (!globalThis.crypto) globalThis.crypto = webcrypto;

// 3. React Native needs crypto.getRandomValues polyfill and sha512
import 'react-native-get-random-values';
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m));
secp.etc.hmacSha256Async = (k, ...m) => Promise.resolve(secp.etc.hmacSha256Sync(k, ...m));

API

There are 3 main methods: getPublicKey(privateKey), sign(messageHash, privateKey) and verify(signature, messageHash, publicKey). We accept Hex type everywhere:

type Hex = Uint8Array | string

getPublicKey

function getPublicKey(privateKey: Hex, isCompressed?: boolean): Uint8Array;

Generates 33-byte compressed public key from 32-byte private key.

  • If you need uncompressed 65-byte public key, set second argument to false.
  • Use ProjectivePoint.fromPrivateKey(privateKey) for Point instance.
  • Use ProjectivePoint.fromHex(publicKey) to convert Hex / Uint8Array into Point.

sign

function sign(
  messageHash: Hex, // message hash (not message) which would be signed
  privateKey: Hex, // private key which will sign the hash
  opts?: { lowS: boolean, extraEntropy: boolean | Hex } // optional params
): Signature;
function signAsync(
  messageHash: Hex,
  privateKey: Hex,
  opts?: { lowS: boolean; extraEntropy: boolean | Hex }
): Promise<Signature>;

sign(msgHash, privKey, { lowS: false }); // Malleable signature
sign(msgHash, privKey, { extraEntropy: true }); // Improved security

Generates low-s deterministic-k RFC6979 ECDSA signature. Assumes hash of message, which means you'll need to do something like sha256(message) before signing.

  1. lowS: false allows to create malleable signatures, for compatibility with openssl. Default lowS: true prohibits signatures which have (sig.s >= CURVE.n/2n) and is compatible with BTC/ETH.
  2. extraEntropy: true improves security by adding entropy, follows section 3.6 of RFC6979:
    • No disadvantage: if an entropy generator is broken, sigs would be the same as they are without the option
    • It would help a lot in case there is an error somewhere in k gen. Exposing k could leak private keys
    • Sigs 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

verify

function verify(
  signature: Hex | Signature, // returned by the `sign` function
  messageHash: Hex, // message hash (not message) that must be verified
  publicKey: Hex, // public (not private) key
  opts?: { lowS: boolean } // optional params; { lowS: true } by default
): boolean;

Verifies ECDSA signature and ensures it has lowS (compatible with BTC/ETH). lowS: false turns off malleability check, but makes it OpenSSL-compatible.

getSharedSecret

function getSharedSecret(
  privateKeyA: Uint8Array | string, // Alices's private key
  publicKeyB: Uint8Array | string, // Bob's public key
  isCompressed = true // optional arg. (default) true=33b key, false=65b.
): Uint8Array;

Computes ECDH (Elliptic Curve Diffie-Hellman) shared secret between key A and different key B.

Use ProjectivePoint.fromHex(publicKeyB).multiply(privateKeyA) for Point instance

recoverPublicKey

signature.recoverPublicKey(
  msgHash: Uint8Array | string
): Uint8Array | undefined;

Recover public key from Signature instance with recovery bit set.

utils

A bunch of useful utilities are also exposed:

type Bytes = Uint8Array;
const etc: {
  hexToBytes: (hex: string) => Bytes;
  bytesToHex: (b: Bytes) => string;
  concatBytes: (...arrs: Bytes[]) => Bytes;
  bytesToNumberBE: (b: Bytes) => bigint;
  numberToBytesBE: (num: bigint) => Bytes;
  mod: (a: bigint, b?: bigint) => bigint;
  invert: (num: bigint, md?: bigint) => bigint;
  hmacSha256Async: (key: Bytes, ...msgs: Bytes[]) => Promise<Bytes>;
  hmacSha256Sync: HmacFnSync;
  hashToPrivateKey: (hash: Hex) => Bytes;
  randomBytes: (len: number) => Bytes;
};
const utils: {
  normPrivateKeyToScalar: (p: PrivKey) => bigint;
  randomPrivateKey: () => Bytes; // Uses CSPRNG https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
  isValidPrivateKey: (key: Hex) => boolean;
  precompute(p: ProjectivePoint, windowSize?: number): ProjectivePoint;
};
class ProjectivePoint {
  constructor(px: bigint, py: bigint, pz: bigint);
  static readonly BASE: ProjectivePoint;
  static readonly ZERO: ProjectivePoint;
  static fromAffine(point: AffinePoint): ProjectivePoint;
  static fromHex(hex: Hex): ProjectivePoint;
  static fromPrivateKey(n: PrivKey): ProjectivePoint;
  get x(): bigint;
  get y(): bigint;
  add(other: ProjectivePoint): ProjectivePoint;
  assertValidity(): void;
  equals(other: ProjectivePoint): boolean;
  multiply(n: bigint): ProjectivePoint;
  negate(): ProjectivePoint;
  subtract(other: ProjectivePoint): ProjectivePoint;
  toAffine(): AffinePoint;
  toHex(isCompressed?: boolean): string;
  toRawBytes(isCompressed?: boolean): Bytes;
}
class Signature {
  constructor(r: bigint, s: bigint, recovery?: number | undefined);
  static fromCompact(hex: Hex): Signature;
  readonly r: bigint;
  readonly s: bigint;
  readonly recovery?: number | undefined;
  ok(): Signature;
  hasHighS(): boolean;
  normalizeS(): Signature;
  recoverPublicKey(msgh: Hex): Point;
  toCompactRawBytes(): Bytes;
  toCompactHex(): string;
}
CURVE // curve prime; order; equation params, generator coordinates

Security

The module is production-ready. It is cross-tested against noble-curves, and has similar security.

  1. The current version is rewrite of v1, which has been audited by cure53: PDF (funded by Umbra.cash & community).
  2. It's being fuzzed by Guido Vranken's cryptofuzz: run the fuzzer by yourself to check.

Our EC multiplication is hardened to be algorithmically constant time. We're using built-in JS BigInt, which is potentially vulnerable to timing attacks as per MDN. 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 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.

We 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.

As for key generation, we're deferring to built-in crypto.getRandomValues which is considered cryptographically secure (CSPRNG).

Speed

Use noble-curves if you need even higher performance.

Benchmarks measured with Apple M2 on MacOS 13 with node.js 20.

getPublicKey(utils.randomPrivateKey()) x 6,430 ops/sec @ 155μs/op
sign x 3,367 ops/sec @ 296μs/op
verify x 600 ops/sec @ 1ms/op
getSharedSecret x 505 ops/sec @ 1ms/op
recoverPublicKey x 612 ops/sec @ 1ms/op
Point.fromHex (decompression) x 9,185 ops/sec @ 108μs/op

Compare to other libraries on M1 (openssl uses native bindings, not JS):

elliptic#getPublicKey x 1,940 ops/sec
sjcl#getPublicKey x 211 ops/sec

elliptic#sign x 1,808 ops/sec
sjcl#sign x 199 ops/sec
openssl#sign x 4,243 ops/sec
ecdsa#sign x 116 ops/sec

elliptic#verify x 812 ops/sec
sjcl#verify x 166 ops/sec
openssl#verify x 4,452 ops/sec
ecdsa#verify x 80 ops/sec

elliptic#ecdh x 971 ops/sec

Contributing

  1. Clone the repository.
  2. npm install to install build dependencies like TypeScript
  3. npm run build to compile TypeScript code
  4. npm test to run jest on test/index.ts

Special thanks to Roman Koblov, who have helped to improve scalar multiplication speed.

Upgrading

noble-secp256k1 v2 features improved security and smaller attack surface. The goal of v2 is to provide minimum possible JS library which is safe and fast.

That means the library was reduced 4x, to just over 400 lines. In order to achieve the goal, some features were moved to noble-curves, which is even safer and faster drop-in replacement library with same API. Switch to curves if you intend to keep using these features:

  • DER encoding: toDERHex, toDERRawBytes, signing / verification of DER sigs
  • Schnorr signatures
  • Using utils.precompute() for non-base point
  • Support for environments which don't support bigint literals
  • Common.js support
  • Support for node.js 18 and older without shim

Other changes for upgrading from @noble/secp256k1 1.7 to 2.0:

  • getPublicKey
    • now produce 33-byte compressed signatures by default
    • to use old behavior, which produced 65-byte uncompressed keys, set argument isCompressed to false: getPublicKey(priv, false)
  • sign
    • is now sync; use signAsync for async version
    • now returns Signature instance with { r, s, recovery } properties
    • canonical option was renamed to lowS
    • recovered option has been removed because recovery bit is always returned now
    • der option has been removed. There are 2 options:
      1. Use compact encoding: fromCompact, toCompactRawBytes, toCompactHex. Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
      2. If you must use DER encoding, switch to noble-curves (see above).
  • verify
    • strict option was renamed to lowS
  • getSharedSecret
    • now produce 33-byte compressed signatures by default
    • to use old behavior, which produced 65-byte uncompressed keys, set argument isCompressed to false: getSharedSecret(a, b, false)
  • recoverPublicKey(msg, sig, rec) was changed to sig.recoverPublicKey(msg)
  • number type for private keys have been removed: use bigint instead
  • Point (2d xy) has been changed to ProjectivePoint (3d xyz)
  • utils were split into utils (same api as in noble-curves) and etc (hmacSha256Sync and others)

License

MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.

noble-secp256k1's People

Contributors

aulneau avatar blakejakopovic avatar elli610 avatar jacogr avatar legobeat avatar micahzoltu avatar paulmillr avatar reardencode avatar thejoelw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

noble-secp256k1's Issues

Release please?

I'm seeing all sorts of CI failures related to this module:

Error: Error: Command failed with exit code 1: aegir build -b --no-types
Error: Cannot find module '@noble/secp256k1'
Require stack:
- /home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1.js
- /home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1-class.js
- /home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/index.js
- /home/runner/work/js-libp2p/js-libp2p/node_modules/peer-id/src/index.js
- /home/runner/work/js-libp2p/js-libp2p/src/index.js
- /home/runner/work/js-libp2p/js-libp2p/.aegir.js
- /home/runner/work/js-libp2p/js-libp2p/node_modules/lilconfig/dist/index.js
- /home/runner/work/js-libp2p/js-libp2p/node_modules/aegir/src/config/user.js
- /home/runner/work/js-libp2p/js-libp2p/node_modules/aegir/cli.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)
    at Function.Module._load (internal/modules/cjs/loader.js:690:27)
    at Module.require (internal/modules/cjs/loader.js:852:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1.js:4:14)
    at Module._compile (internal/modules/cjs/loader.js:959:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32)
    at Function.Module._load (internal/modules/cjs/loader.js:727:14)
    at Module.require (internal/modules/cjs/loader.js:852:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1.js',
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1-class.js',
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/index.js',
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/peer-id/src/index.js',
    '/home/runner/work/js-libp2p/js-libp2p/src/index.js',
    '/home/runner/work/js-libp2p/js-libp2p/.aegir.js',
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/lilconfig/dist/index.js',
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/aegir/src/config/user.js',
    '/home/runner/work/js-libp2p/js-libp2p/node_modules/aegir/cli.js'
  ]
}
🚨 Build failed.

@parcel/core: Failed to resolve '@noble/secp256k1' from
'/Users/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1.js'


  /Users/runner/work/js-libp2p/js-libp2p/node_modules/libp2p-crypto/src/keys/secp256k1.js:4:22
    3 | const errcode = require('err-code')
  > 4 | const secp = require('@noble/secp256k1')
  >   |                      ^^^^^^^^^^^^^^^^^^
    5 | const { sha256 } = require('multiformats/hashes/sha2')
    6 | 

etc.

It looks like a fix for this was already merged in #37 - can it be released please?

Is it possible to derive a sharedSecret?

Hello,

We want to change if possible the elliptic implementation we have for our browser app with your library, which is better in different areas. However, either we didn't get how to do it (probably lack of knowledge) or it's not possible.

We do have this

const sharedPublicKeyPair = ecInstance.keyFromPublic(sharedPublicKey, 'hex');
const derivedKey = keyPair.derive(sharedPublicKeyPair.getPublic()).toString(16);
const derivedKeySliceBase64 = hexadecimalToBase64(derivedKey.substring(0, 48));

After reading the documentation you provide we need help to achieve our goal.
So we would have

// previously
let privateKey = utils.randomPrivateKey() or window.crypto.getRandomValues(new Uint8Array(32))
const publicKey = getPublicKey(privateKey) 

const sharedPublic = getSharedSecret(privateKey, sharedPublicKey) // 'hex' 04...
// next step

After getting the hex of 130 chars / ArrayBuffer of 65 bytes, we tried to import the key using the WebCrypto API, but the length is above 256bits.

Could we get some help?

Thanks

How to use public key from a PEM file ?

I have the following python program

import base64
import ecdsa
import hashlib

digest = hashlib.sha256(b"Hello world").digest()
with open("key.pem") as f:
   public_key = ecdsa.VerifyingKey.from_pem(f.read())
signature = base64.b64decode("bxw1nS8Q39mnQVkAhvJctXuLHz4n0wUjLbE+phj1XlUeWDxl7DCK5bG4d7YrL7zGtAnUq3YT9AdrrAXjpwhCzQ==")

public_key.verify_digest(signature, digest)

pem file is :

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOC76WxZ/TMzJnRqv/cy9llDqSIMW
lplgREY3jMxDCksPGDDyepeg7fFdCu56N9Br9n5oWmj/iKULrglQehGiiA==
-----END PUBLIC KEY-----

I'd like to compare with your library, but I do not find how to create the Point/Signature from the PEM :

    const digest = crypto.createHash('sha256').update(Buffer.from('Hello world')).digest();

    const publicKey = secp.Point.fromPem(fs.readFileSync('key.pem')); //How to do here ?

    const signature = Buffer.from(
      'bxw1nS8Q39mnQVkAhvJctXuLHz4n0wUjLbE+phj1XlUeWDxl7DCK5bG4d7YrL7zGtAnUq3YT9AdrrAXjpwhCzQ==',
      'base64',
    );
    
    const isSigned = secp.verify(signature, digest, publicKey);
    assert(isSigned)

I also have x/y in JWK (so base64) in case it is easier than PEM :

  {
    "crv": "P-256",
    "kty": "EC",
    "x": "OC76WxZ/TMzJnRqv/cy9llDqSIMWlplgREY3jMxDCks=",
    "y": "Dxgw8nqXoO3xXQruejfQa/Z+aFpo/4ilC64JUHoRoog="
  }

Getting SharedSecret using a privateKey and other publicKey value.

Hi @paulmillr
Thanks for creating a great module. By the way, I'm having some issues using this module. Could you help me?
The following is the code to calculate sharedSecret using "secp256k1-native" npm module.

const secp256k1 = require('secp256k1-native');
const ctx = secp256k1.secp256k1_context_create(secp256k1.secp256k1_context_SIGN);
const sharedSecrect = Buffer.alloc(32);
const point = Buffer.from('A8Nd1J1A+akKMfHe7j8oZkfLf1QDOkTynN27zr9nBgK3', 'base64'); // B- public key
const scalar = Buffer.from('88e5c2a323f371a43abee6ff28966f7f0cd267e65063aeba9033037c7d93d87d', 'hex'); // A- private key
const pubkey64 = Buffer.alloc(64);
secp256k1.secp256k1_ec_pubkey_parse(ctx, pubkey64, point);
secp256k1.secp256k1_ecdh(ctx, sharedSecrect, pubkey64, scalar, null);
console.log('shared secret:', sharedSecrect); // 028c3425493529dddc51a5d2d2de8af9e68e5e835e883d687b4f2ec421a2640c

The result is "028c3425493529dddc51a5d2d2de8af9e68e5e835e883d687b4f2ec421a2640c", but it's different when I use "noble-secp256k1" module.

import * as secp from 'noble-secp256k1';
  const point = Buffer.from('A8Nd1J1A+akKMfHe7j8oZkfLf1QDOkTynN27zr9nBgK3', 'base64'); // B-JS public key

  const scalar = Buffer.from('88e5c2a323f371a43abee6ff28966f7f0cd267e65063aeba9033037c7d93d87d', 'hex'); // A-JS private key

  const sharedSecret = secp.getSharedSecret(scalar, point);
  console.log("sharedSecret:", Buffer.from(sharedSecret).toString('hex')); //04602586145e1e0aeb17289e68e06bb672a03b708cb2c0ce907678903656f181b666c9eada9d630fefab93e02e131b7f28046fc970cdf5e86f640b100851593137

The above is my code.
What's wrong in my code? My result length is 130.
Could you help me?
Thanks

Recovery ids other than 0 or 1?

Hey, I was wondering if recovery IDs for ECDSA signatures other than 0 or 1 are supported. Here, the recovery id is restricted to 0 or 1. However, the signing logic can generate recovery IDs 2 and 3 as I understand here. Is there something I am missing in how these would be treated?

From what little I've read, these other IDs are quite rare. I don't know if there are any readily available tests for these cases.

double Jacobian

Maybe you can shave some yoctoseconds off...

const D = 2n * ((X1 + B) ** 2n - A - C);

can be simplified to

const D = 4n * X1 * B;

Why are signatures incompatible with other BIP-340 libraries?

For BIP-340 signatures I see you're using the test vector provided with the BIP and everything is ok, but https://github.com/fiatjaf/schnorr and https://github.com/guggero/bip-schnorr are also passing these same tests, but weirdly the signatures of these two are compatible, but they are not compatible with the signatures produced by this library.

Is there something wrong here? I don't understand. It could be that I am using the libraries wrong too, of course.

Uncaught TypeError: can't convert BigInt to number

I've got both Firefox and Chrome blowing up once I build a production react build with this library.

this line throws this error:
Uncaught TypeError: can't convert BigInt to number

What's weird is it doesn't throw this error in development, which makes me think there's some build problem happening...

Compressed vs uncompressed `pubkey`

Is there a simple way using the existing API + utils to convert a pubkey between the compressed and uncompressed forms? I'm exploring switching the discv5 library from the bcrypto secp256k1 implementation to this one but we need to be able to switch between compressed and uncompressed public keys without providing a private key in the middle if possible (bcrypto provides this method today).

Clarify whether the library is supported in React Native

This very promising library's npm name seems to suggest that this is a React Native library. However, React Native currently doesn't natively support BigInt nor the n number suffix required for the library to work. These issues could be addressed using the big-integer package and the @babel/plugin-syntax-bigint plugin. I suggest either documenting the React Native library installation procedure or clarifying that the words react native in the library's title don't refer to React Native.

More specific web crypto condition

Testing out using the noble libs in react native, and it seems the check for web crypto is not quite specific enough,
typeof self === 'object' && 'crypto' in self can sometimes be true even when web crypto is not available.

Here is the diff that solved my problem:

diff --git a/node_modules/@noble/secp256k1/lib/esm/index.js b/node_modules/@noble/secp256k1/lib/esm/index.js
index ff76464..c5506a5 100644
--- a/node_modules/@noble/secp256k1/lib/esm/index.js
+++ b/node_modules/@noble/secp256k1/lib/esm/index.js
@@ -1046,7 +1046,7 @@ export const schnorr = {
 Point.BASE._setWindowSize(8);
 const crypto = {
     node: nodeCrypto,
-    web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined,
+    web: typeof self === 'object' && 'crypto' in self && 'subtle' in self.crypto ? self.crypto : undefined,
 };
 const TAGS = {
     challenge: 'BIP0340/challenge',
diff --git a/node_modules/@noble/secp256k1/lib/index.js b/node_modules/@noble/secp256k1/lib/index.js
index e207e74..18cd75d 100644
--- a/node_modules/@noble/secp256k1/lib/index.js
+++ b/node_modules/@noble/secp256k1/lib/index.js
@@ -1056,7 +1056,7 @@ exports.schnorr = {
 Point.BASE._setWindowSize(8);
 const crypto = {
     node: nodeCrypto,
-    web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined,
+    web: typeof self === 'object' && 'crypto' in self && 'subtle' in self.crypto ? self.crypto : undefined,
 };
 const TAGS = {
     challenge: 'BIP0340/challenge',

This issue body was partially generated by patch-package.

Creating a private key deterministically?

I looked for it but it appears that this library does not provide a way to generate a private key deterministically from a secret or seed. Is there anything you'd recommend using for this?

Signatures will randomly return as invalid.

Hello. I believe that I have found a bug with how signatures (and possibly keys) are validated.

I have devised a test where the signature validation will fail 50% on average. I have also confirmed this behavior in two other projects, which caused me to write this test case to figure out what was going on.

Here is the code I am using to reproduce the bug:

import * as Noble from '@noble/secp256k1'
import { webcrypto as crypto } from 'crypto'

let rounds = 10, passed = 0

for (let i = 0; i < rounds; i++) {

  const randomBytes = getRandBytes(32)
  const randomData  = getRandBytes(32)

  const isValidPrivateKey = Noble.utils.isValidPrivateKey(randomBytes)
  const pubKey = Noble.getPublicKey(randomBytes)

  const signature = await Noble.schnorr.sign(randomData, randomBytes)
  const isValid = await Noble.schnorr.verify(signature, randomData, pubKey)

  if (!(isValidPrivateKey && isValid)) {
    console.log('Failed validation!')
    console.log('  message:', Noble.utils.bytesToHex(randomData))
    console.log('  Valid Key:', isValidPrivateKey, Noble.utils.bytesToHex(randomBytes))
    console.log('  Valid Sig:', isValid, Noble.utils.bytesToHex(signature))
    console.log('')
  } else { passed++ }
}

console.log(`Passed: ${passed}\nFailed: ${rounds - passed}\nTotal: ${rounds}`)

function getRandBytes(size = 32) {
  return crypto.getRandomValues(new Uint8Array(size))
}

For the above code, there are no dependencies besides the built-in webcrypto module and (version 1.7.0) the @noble/secp256k1 library.

I haven't run into this issue until recently, and I suspect it may have something to do with the random data being used for signing, but honestly I'm not sure. The random data will pass the isValidPrivateKey check 100% of the time, but the signature will fail 50% of the time on average.

secp.verify() always equals true

This error has been reproduced across many machines by many students using this library for a cryptography project.

secp.verify(signature, messageHash, recoveredPublicKey) seems to always yield true regardless of whether the messageHash is actually the messageHash that was digitally signed. Any hash placed in this function always yields true.

Any advice? Thanks!

Error on Deno runtime

imp

This error after 1.2.10 version

error: TS2580 [ERROR]: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.
  const nodeRequire = typeof module !== 'undefined' && typeof require === 'function';
                             ~~~~~~
    at https://deno.land/x/[email protected]/index.ts:1139:30

TS2580 [ERROR]: Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.
  const nodeRequire = typeof module !== 'undefined' && typeof require === 'function';
                                                              ~~~~~~~
    at https://deno.land/x/[email protected]/index.ts:1139:63

TS2580 [ERROR]: Cannot find name 'require'. Do you need to install type definitions for

1.2.9...1.2.10#diff-e727e4bdf3657fd1d798edcd6b099d6e092f8573cba266154583a746bba0f346R858-R860

[Question] Compatibility to other library

Hi. I'm new at cryptography.
I'm trying to check compatibility to other library ( https://docs.rs/secp256k1/ ).

const public_key_js = secp.getPublicKey("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994");
let public_key_rust = PublicKey::from_str(public_key_js);

and

let secp = Secp256k1::new();
let secret_key = SecretKey::from_str("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994").unwrap();
let public_key_rust = PublicKey::from_secret_key(&secp, &secret_key);

is the same public_key_rust output.

But

const sig = secp.sign("9e71e1c1f3f5d45d42669dc0c191d58ee5c90ff69a8011fc34c66f375ba46a7e", "f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994");
// sig = 30450220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a022100e20c366399e11322a1e56b897c339e6d764fca5c9cee9f3fc0370d597c8eb680
// This signature will not valid to https://docs.rs/secp256k1/

and

let mut hasher = Sha256::new();
hasher.update(b"madoka");
let hashed_value = hasher.finalize();
// sha2-256("madoka") == 9e71e1c1f3f5d45d42669dc0c191d58ee5c90ff69a8011fc34c66f375ba46a7e

let secp = Secp256k1::new();
let sig = secp.sign(&Message::from_slice(&*hashed_value).unwrap(), &SecretKey::from_str("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994").unwrap());
// sig = 30440220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a02201df3c99c661eecdd5e1a947683cc6191445f128a125a00fbff9b513353a78ac1
// This signature will valid to noble-secp256k1

is not the same.

What makes this difference? Is this a normal behavior of crypto library?

Sorry for my not good English and Code.

How to sign ethereum transaction ?

Hello,

Im not a pro, but this library looks faster than other algos.
Is anyone have any example code base signing ethereum transactions with this library ?

Thanks.

TypeError: Cannot convert a BigInt value to a number in react native

if i use this npm in react-native, the error will be like TypeError: Cannot convert a BigInt value to a number occurs. even I have tried shim.js added and imported big-int npm but still not working. please clarify this

// my code

import * as secp from "noble-secp256k1";

const clientPrivatekey = Buffer.from("308193020100301306072a8648ce3d020106082a8648ce3d03010704793077020101042094a2fb3f1b5f49d8e523901392d331dbf1fffecda54204f1c8ed36fa753da1e2a00a06082a8648ce3d030107a144034200047c6a5ccaed72b3b274f176bf00d26c80ba4ab3e4c23119339688d73b4b2a0eb3b2dd904b3c13d6299bbd2034b0e44ec95f928876a8c1cd7e86b32a92220d8b23", "base64")
    const servPublicKey = Buffer.from(serverPublicKey, "base64")
const ecdhSecrtKey = secp.getSharedSecret(clientPrivatekey, servPublicKey);

Security audit

Hi there!

I saw some commits related to preparing for an audit -- was curious if there was any audit planned, and if so what kind of timeline?

Thanks!

unpkg.com browser Error: Uncaught RefereceError: exports is not defined

[email protected]:3 Uncaught ReferenceError: exports is not defined
    at [email protected]:3:23
(anonymous) @ [email protected]:3

Hello. I am getting this error when trying to import the library into the browser, from the following URL: https://unpkg.com/@noble/[email protected]

I do not get this problem when I import the file from the github releases page, or locally from my machine.

I think there may be a mis-configuration somewhere (package.json?), as unpkg.com is not properly hosting a browser version of the file , even though it seems to work fine when I import the file from other sources.

This is not a critical issue. I thought to bring it up since unpkg.com is useful as a CDN, and it would be nice to pull a working version from there.

Might be worth committing build products

Arguments in favor of committing build products:

  • Puts a PGP signature on the build products that can be matched against published artifacts
  • Allows depending on github versions directly
  • Easier direct audit of the code that will actually be provided to the runtime for execution

See https://github.com/bitcoinjs/bitcoinjs-lib for an example of a project that checks in the build products.

tweakUtils when the tweak is 0 throws

Hi @paulmillr ,

I am coming from bitcoinjs/ecpair#13.

I am using noble-secp256k1 with ecpair.

Since I still need tweakUtils' privatedAdd, ... I decided to copy the code on your tests to my wrapper as you suggest in Release Notes of 1.7.0.

Starting from version 2.1.0, ecpair runs additional tests on the embedded library.

There is this one new test that does not pass:

      ecc.privateAdd(
        h('0000000000000000000000000000000000000000000000000000000000000001'),
        h('0000000000000000000000000000000000000000000000000000000000000000'),
      ),

The reason is normalization of the tweak const t = normalizePrivateKey(tweak); throws because the tweak is zero.

I checked your tests for tweakUtils:

    it('privateAdd()', () => {
      for (const vector of privates.valid.add) {
        const { a, b, expected } = vector;
        expect(secp.utils.bytesToHex(tweakUtils.privateAdd(a, b))).toBe(expected);
      }

This passes.

Then I saw you have other test vectors for privateAdd in test/vectors/privates.json that include the tweak = 0 case:

    "privateAdd": [
      {
        "description": "1 + 0 == 1",
        "d": "0000000000000000000000000000000000000000000000000000000000000001",
        "tweak": "0000000000000000000000000000000000000000000000000000000000000000",
        "expected": "0000000000000000000000000000000000000000000000000000000000000001"
      }

But, for some reason, these vectors are not tested in your tests.

If I add them (see below), they do not pass (because the tweak = 0).

      for (const vector of privates.valid.privateAdd) {
        const { d, tweak, expected } = vector;
        expect(secp.utils.bytesToHex(tweakUtils.privateAdd(d, tweak))).toBe(expected);
      }
    });

(the test above fails)

I know tweakUtils are not part of released noble-secp256k1 anymore but decided to let you know anyway in case you want to take a look to your tests.

Remove utilities

Because Schnorr requires different implementations of these utils, and we don't want to provide two versions, and because it's unused in scure/bip32; those should be removed:

  privateAdd: (privateKey: PrivKey, tweak: Hex): Uint8Array => {
    const p = normalizePrivateKey(privateKey);
    const t = normalizePrivateKey(tweak);
    return numTo32b(mod(p + t, CURVE.n));
  },

  privateNegate: (privateKey: PrivKey): Uint8Array => {
    const p = normalizePrivateKey(privateKey);
    return numTo32b(CURVE.n - p);
  },

  pointAddScalar: (p: Hex, tweak: Hex, isCompressed?: boolean): Uint8Array => {
    const P = Point.fromHex(p);
    const t = normalizePrivateKey(tweak);
    const Q = Point.BASE.multiplyAndAddUnsafe(P, t, _1n);
    if (!Q) throw new Error('Tweaked point at infinity');
    return Q.toRawBytes(isCompressed);
  },

  pointMultiply: (p: Hex, tweak: Hex, isCompressed?: boolean): Uint8Array => {
    const P = Point.fromHex(p);
    const t = bytesToNumber(ensureBytes(tweak));
    return P.multiply(t).toRawBytes(isCompressed);
  },

Investigate using complete formulas

Renes–Costello–Batina provides complete formulas for addition & doubling of short weistrass curves.

Here's the implementation. Note that it requires homogenous coordinates, not jacobian: x=x/z, y=y/z. Seems like a 10-20% slowdown. Benchmarks (Feb 7, 2022 @ M1 / macos 12):

getPublicKey(utils.randomPrivateKey()) x 5,182 ops/sec @ 192μs/op
sign x 4,035 ops/sec @ 247μs/op
verify x 868 ops/sec @ 1ms/op
recoverPublicKey x 462 ops/sec @ 2ms/op
getSharedSecret aka ecdh x 533 ops/sec @ 1ms/op
getSharedSecret (precomputed) x 5,525 ops/sec @ 180μs/op
Point.fromHex (decompression) x 12,176 ops/sec @ 82μs/op
schnorr.sign x 386 ops/sec @ 2ms/op
schnorr.verify x 469 ops/sec @ 2ms/op

Code:

  // Actually HomogenousPoint
  double(): JacobianPoint {
    const { x, y, z } = this;
    let X3 = _0n, Y3 = _0n, Z3 = _0n; // prettier-ignore
    let b3 = CURVE.b * 3n;
    let t0 = mod(y * y); // step 1
    Z3 = t0 + t0;
    Z3 = Z3 + Z3;
    Z3 = Z3 + Z3;
    let t1 = mod(y * z); // step 5
    let t2 = mod(z * z);
    t2 = mod(b3 * t2);
    X3 = mod(t2 * Z3);
    Y3 = t0 + t2;
    Z3 = mod(t1 * Z3); // step 10
    t1 = t2 + t2;
    t2 = t1 + t2;
    t0 = t0 - t2;
    Y3 = mod(t0 * Y3);
    Y3 = mod(X3 + Y3); // step 15
    t1 = mod(x * y);
    X3 = mod(t0 * t1);
    X3 = mod(X3 + X3);
    return new JacobianPoint(X3, Y3, Z3);
  }
  // Algorithm 7 of 2015/1060
  add(other: JacobianPoint): JacobianPoint {
    const { x: X1, y: Y1, z: Z1 } = this;
    const { x: X2, y: Y2, z: Z2 } = other;
    let X3 = _0n, Y3 = _0n, Z3 = _0n; // prettier-ignore
    let b3 = CURVE.b * 3n;
    //if (this.equals(other)) return this.double();
    let t0 = mod(X1 * X2); // step 1
    let t1 = mod(Y1 * Y2);
    let t2 = mod(Z1 * Z2);
    let t3 = X1 + Y1;
    let t4 = X2 + Y2; // step 5
    t3 = mod(t3 * t4);
    t4 = t0 + t1;
    t3 = t3 - t4;
    t4 = Y1 + Z1;
    X3 = Y2 + Z2; // step 10
    t4 = mod(t4 * X3);
    X3 = t1 + t2;
    t4 = t4 - X3;
    X3 = X1 + Z1;
    Y3 = X2 + Z2; // step 15
    X3 = mod(X3 * Y3);
    Y3 = t0 + t2;
    Y3 = X3 - Y3;
    X3 = t0 + t0;
    t0 = X3 + t0; // step 20
    t2 = mod(b3 * t2);
    Z3 = t1 + t2;
    t1 = t1 - t2;
    Y3 = mod(b3 * Y3);
    X3 = mod(t4 * Y3); // step 25
    t2 = mod(t3 * t1);
    X3 = mod(t2 - X3);
    Y3 = mod(Y3 * t0);
    t1 = mod(t1 * Z3);
    Y3 = mod(t1 + Y3); // step 30
    t0 = mod(t0 * t3);
    Z3 = mod(Z3 * t4);
    Z3 = mod(Z3 + t0);
    return new JacobianPoint(X3, Y3, Z3);
  }
  equals(other: JacobianPoint): boolean {
    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 U1 = mod(X1 * Z2) === mod(X2 * Z1);
    const U2 = mod(Y1 * Z2) === mod(Y2 * Z1);
    return U1 && U2;
  }
  toAffine(invZ: bigint = invert(this.z)): Point {
    const { x, y, z } = this;
    const ax = mod(x * invZ);
    const ay = mod(y * invZ);
    const zz = mod(z * invZ);
    if (zz !== _1n) throw new Error('invZ was invalid');
    return new Point(ax, ay);
  }

Consider using subtle from global object instead of crypto module

Importing the "crypto" nodejs module is constantly causing trouble for us at CosmJS. E.g. in Webpack 5 you need to set a crypto: false, fallback but our Create React App users do not have access to a Webpack config as it is hidden by the framework. Recently a user reported issues around import("crypto") using the Metro bundler (React Native).

Now I see https://github.com/paulmillr/noble-secp256k1/blob/1.7.0/index.ts#L5 which implies there are two builds of this library, one for browsers. But when I use this library in a library, does this still work? How does noble-secp256k1 know where it will run eventually?

I wonder if the Node.js specific import is really needed. In all versions of Node 15+, subtle is available in globalThis?.crypto?.webcrypto?.subtle. From Node.js onwards it is avalable in globalThis?.crypto?.subtle, i.e. the same location as for browsers. This is how we now search subtle in CosmJS: cosmos/cosmjs@7b32660.

Using this method would remove support for Node.js 14 and lower.

utils.hashToPrivateKey() not available

Hi. hashToPrivateKey() doesn't seem to be available to me (TypeError: secp.utils.hashToPrivateKey is not a function). I'm using version 1.5.2. This is not a big issue because sign() actually takes hexstring as an input.

I checked index.ts and hashToPrivateKey() is actually there. Am I uninstalling it incorrectly?

{Help wanted} TypeError: undefined is not a function

Im new to cryptography and just wanted to try this package out in React Native but it only returns "TypeError: undefined is not a function". So two questions:

  1. is this package compatible with RN? I thought it would be since its browser compatible.

  2. If it is compatible then what am I missing? Example:

import * as secp from '@noble/secp256k1';
/..../
  const testHash = async () => {
    const msgHash = await secp.utils.sha256('hello world');
    console.log({msgHash});
  }

Response:

TypeError: undefined is not a function
TypeError: undefined is not a function
    at ?anon_0_ (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:104329:32)
    at next (native)
    at asyncGeneratorStep (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21963:26)
    at _next (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21982:29)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21987:14)
    at tryCallTwo (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/443z3r4v/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/443z3r4v/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/443z3r4v/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:82:14)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21979:25)
    at apply (native)
    at sha256 (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:104339:26)
    at ?anon_0_ (http://localhost:8081/src/screens/Home.bundle?platform=android&app=com.getin&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:42:46)
    at next (native)
    at asyncGeneratorStep (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21963:26)
    at _next (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21982:29)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21987:14)
    at tryCallTwo (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/443z3r4v/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/443z3r4v/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/root/react-native/ReactAndroid/hermes-engine/.cxx/Release/443z3r4v/arm64-v8a/lib/InternalBytecode/InternalBytecode.js:82:14)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:21979:25)
    at apply (native)
    at testHash (http://localhost:8081/src/screens/Home.bundle?platform=android&app=com.getin&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:48:27)
    at Home (http://localhost:8081/src/screens/Home.bundle?platform=android&app=com.getin&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:97:13)
    at renderWithHooks (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:9525:33)
    at updateFunctionComponent (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:11893:41)
    at beginWork (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:13128:45)
    at beginWork$1 (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:16707:29)
    at performUnitOfWork (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:16187:29)
    at workLoopSync (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:16131:28)
    at renderRootSync (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:16112:25)
    at performSyncWorkOnRoot (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:15888:40)
    at flushSyncCallbacks (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:6866:36)
    at flushSync (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:15953:31)
    at scheduleRefresh (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:16979:20)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:48507:38)
    at forEach (native)
    at performReactRefresh (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:48501:31)
    at performReactRefresh (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:48319:48)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:461:40)
    at apply (native)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:27671:26)
    at _callTimer (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:27606:17)
    at callTimers (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:27766:19)
    at apply (native)
    at __callFunction (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:2515:36)
    at anonymous (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:2265:31)
    at __guard (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:2463:15)
    at callFunctionReturnFlushedQueue (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.getin&modulesOnly=false&runModule=true:2264:21)

verifySync returns false when it should return true

Library version: 1.6.3

schnorr.verifySync is not working as expected

const sig = "ec8b2bc640c8c7e92fbc0e0a6f539da2635068a99809186f15106174d727456132977c78f3371d0ab01c108173df75750f33d8e04c4d7980bbb3fb70ba1e3848";
const message = "b1601d26958e6508b7b9df0af609c652346c09392b6534d93aead9819a51b4ef";
const pubkey = "22e804d26ed16b68db5259e78449e96dab5d464c8f470bda3eb1a70467f2c793";

const secp = require("@noble/secp256k1")

secp.schnorr.verifySync(sig, message, pubkey) 
// expected: true
// actual: false (incorrect)

secp.schnorr.verifySync(secp.utils.hexToBytes(sig), secp.utils.hexToBytes(message), secp.utils.hexToBytes(pubkey)) 
// expected: true
// actual: false (incorrect)

secp.schnorr.verifySync(Buffer.from(sig, 'hex'), Buffer.from(message, 'hex'), Buffer.from(pubkey, 'hex')) 
// expected: true
// actual: false (incorrect)

await secp.schnorr.verify(sig, message, pubkey)
// expected: true
// actual: true (correct)

await secp.schnorr.verify(secp.utils.hexToBytes(sig), secp.utils.hexToBytes(message), secp.utils.hexToBytes(pubkey))
// expected: true
// actual: true (correct)

await secp.schnorr.verify(Buffer.from(sig, 'hex'), Buffer.from(message, 'hex'), Buffer.from(pubkey, 'hex'))
// expected: true
// actual: true (correct)

Add isValidPublicKey?

Need to understand if it'll be used anywhere in scure/bip32, micro-signers, etc

  isValidPublicKey(publicKey: Hex, type: 'ecdsa' | 'schnorr') {
    const arr = ensureBytes(publicKey);
    const len = arr.length;
    if (type === 'ecdsa') {
      if (len === 32) throw new Error('Expected non-Schnorr key');
    } else if (type === 'schnorr') {
      if (len !== 32) throw new Error('Expected 32-byte Schnorr key');
    } else {
      throw new Error('Unknown key type')
    }
    Point.fromHex(publicKey); // does assertValidity
    return true;
  },

Assert validity incorrect

The function assertValidity() will accept points satisfying either the equation "y² = x³ + ax +b" or the equation "-y² = x³ + ax +b". For instance, the function will accept the point

let x = mod(-2n);
let y = mod(-1n);
let point = new Point(x, y);
point.assertValidity();

even though it's clearly not in the curve, as shown by the results of mod(y**2n) and weistrass(x), respectively equal to 1n and mod(-1n).

ECMH implementation based on this library

Hi

I have been using this excellent library to implement the multiset hash using elliptic curves algorithm described in ecmh.wiki. The implementation can be found here: https://github.com/arj03/ecmh-js. I'm using Point.fromHex to figure out if point is on the curve. Sadly that function throws an exception which normally is rather slow. Do you have any input on a better way to do that part of the algorithm?

Cheers

PS: the post https://paulmillr.com/posts/noble-secp256k1-fast-ecc/ was very good in helping me implement this

Questions and thanks

Hi there!

First off wanted to say thank you for your work on this, I think this is a fantastic endeavor!

( please forgive my ignorance when it comes to cryptography -- much of my time is spent as a frontend engineer :~) )

I work at Hiro systems PBC (building tools for the Stacks blockchain) and I really want to build alternatives to our libraries that depend on elliptic. I have a semi-working implementation here, but I am having a bit of a hard time getting a compatibility layer going between our other libraries that use elliptic. Here is an example of something I don't know how to replicate with your library:

  const ecPK = ecurve.keyFromPublic(publicKey, 'hex').getPublic();
  const ephemeralSK = ecurve.genKeyPair();
  const ephemeralPK = Buffer.from(ephemeralSK.getPublic().encodeCompressed());
  const sharedSecret = ephemeralSK.derive(ecPK) as BN;

(taken from these lines)

From debugging and trying to understand the differences, I think there are issues around how elliptic uses/expects data to be in a Point format, or a BigInt. I'm wondering if you might be interested in giving an example of how to take something from noble-secp256k1 and decrypt it with elliptic? Here is a test that I would love to get passing.

Any help or guidance would be greatly appreciated!

Expose decrypt method?

Hi there!

This lib is fantastic, thank you for all your work on it :)

I'm hoping to use this for data encryption/decryption in the browser. There isn't currently a decrypt method exposed. I'm curious would it be much more effort to expose a method for this use case?

Thanks!

options @param sign(msgHash, privateKey, opts?)

the sign API which has an optional options parameter takes in an object, one of which is {recovered : boolean} and whose
value for the recovered key should be true instead of false as the description(README file) states what happens when true is given as the value for the key.
Screenshot 2022-11-25 at 09 59 22

Add low-S signature normalization

Something like secp256k1_ecdsa_signature_normalize. This would be helpful to read historic Bitcoin transactions or converting signatures to work with other applications which only accepts low-S signatures.

Not a bug, thanks for this project

Thank you for this project, thank you for your post, but I don’t know much about wNAF and Endomorphism, I hope the author can post a post to talk about it in detail, thank you

p256

Would this work for p256 curve just by modifying the CURVE parameters ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.