npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@solar-republic/wasm-secp256k1

v0.4.2

Published

TypeScript wrapper and Web Assembly module of the libsecp256k1 C library

Downloads

823

Readme

wasm-secp256k1

The libsecp256k1 C library compiled to WASM and wrapped with an ultralight TypeScript API.

Supports the following operations:

  • Computing the corresponding public key for a given secret key
  • Signing (with recovery ID)
  • Verifying
  • Recovering public key
  • ECDH
  • Tweaking secret/public key via addition/multiplication

This module offers substantially greater security than pure JavaScript implementations of Secp256k1 due to the fact that JS runtimes simply make it impossible for JS developers to effectively mitigate side-channel attacks. Anyone who says otherwise doesn't know what they're talking about.

In addition to zero-ing out private keys after use, the wrapper also randomizes the lib context every time a public key is computed or a message is signed.

Demo webapp and its source

Install

npm install @solar-republic/wasm-secp256k1

Usage

First, choose which import method suites your needs:

Default

Import with the WASM binary preloaded and uncompressed. No need to perform fetch, but bundle will be larger (+332 KiB).

import {initWasmSecp256k1} from '@solar-republic/wasm-secp256k1'
const secp256k1 = await initWasmSecp256k1();

Compressed

Import with the WASM binary preloaded and gzipped (requires access to globalThis.DecompressionSteam). No need to perform fetch, but bundle will be still be a bit larger (+175 KiB).

import {initWasmSecp256k1} from '@solar-republic/wasm-secp256k1/gzipped'
const secp256k1 = await initWasmSecp256k1();

Headless

Import without the WASM binary. Produces the smallest bundle size but requires fetching the binary yourself.

import {WasmSecp256k1} from '@solar-republic/wasm-secp256k1/headless';

// provide the binary (the constructor also accepts raw bytes)
const secp256k1 = await WasmSecp256k1(await fetch('secp256k1.wasm'));

Using the instance:

// generate a random private key
const sk = secp256k1.gen_sk()

// get its corresponding public key
const pk = secp256k1.sk_to_pk(sk);

// sign a message hash (caller is responsible for actually hashing the message and providing entropy)
const [signature, recoveryId] = secp256k1.sign(sk, messageHash, entropy);

// verify a given message hash is signed by some public key
const verified = secp256k1.verify(signature, messageHash, pk);

// recovers a public key from a signature, message hash, and recovery ID
const recovered = secp256k1.recover(signature, messageHash, recoveryId);

// derive a shared secret with some other's public key
const shared = secp256k1.ecdh(sk, otherPk);


// zero out private key
sk.fill(0, 0, 32);

Caller is responsible for zero-ing out private keys in the Typed Arrays it passes. Library only zeroes out the bytes in the copies it makes.

API

/**
 * Creates a new instance of the secp256k1 WASM and returns its ES wrapper
 * @param z_src - a Response containing the WASM binary, a Promise that resolves to one,
 * 	or the raw bytes to the WASM binary as a {@link BufferSource}
 * @returns the wrapper API
 */
export declare const WasmSecp256k1 = (dp_res: Promisable<Response> | BufferSource): Promise<Secp256k1>;

/**
 * Wrapper instance providing operations backed by libsecp256k1 WASM module
 */
interface Secp256k1 {
    /**
    * Generates a new private key using crypto secure random bytes and without modulo bias
    * @returns a new private key (32 bytes)
    */
    gen_sk(): Uint8Array;

    /**
    * Asserts that the given private key is valid, throws otherwise
    * @param atu8_sk - the private key (32 bytes)
    * @returns the same `Uint8Array`
    */
    valid_sk(atu8_sk: Uint8Array): Uint8Array;

    /**
    * Computes the public key for a given private key
    * @param atu8_sk - the private key (32 bytes)
    * @param b_uncompressed - optional flag to return the uncompressed (65 byte) public key
    * @returns the public key (compressed to 33 bytes by default, or 65 if uncompressed)
    */
    sk_to_pk(atu8_sk: Uint8Array, b_uncompressed?: boolean): Uint8Array;

    /**
    * Signs the given message hash using the given private key.
    * @param atu8_sk - the private key
    * @param atu8_hash - the message hash (32 bytes)
    * @param atu8_entropy - optional entropy to use
    * @returns tuple of [compact signature (64 bytes) as concatenation of `r || s`, recovery ID byte]
    */
    sign(atu8_sk: Uint8Array, atu8_hash: Uint8Array, atu8_ent?: Uint8Array): [
        atu8_signature: Uint8Array,
        xc_recovery: RecoveryValue,
    ];

    /**
    * Verifies the signature is valid for the given message hash and public key
    * @param atu8_signature - compact signature in `r || s` form (64 bytes)
    * @param atu8_msg - the message hash (32 bytes)
    * @param atu8_pk - the public key
    */
    verify(atu8_signature: Uint8Array, atu8_hash: Uint8Array, atu8_pk: Uint8Array): boolean;

    /**
     * Recovers a public key from the given signature and recovery ID
     * @param atu8_signature - compact signature in `r || s` form (64 bytes)
     * @param xc_recovery - the recovery ID
	 * @param b_uncompressed - optional flag to return the uncompressed (65 byte) public key
     */
    recover(atu8_signature: Uint8Array, atu8_hash: Uint8Array, xc_recovery: number, b_uncompressed?: boolean): Uint8Array;

    /**
    * ECDH key exchange. Computes a shared secret given a private key some public key
    * @param atu8_sk - the private key (32 bytes)
    * @param atu8_pk - the public key (33 or 65 bytes)
    * @returns the shared secret (32 bytes)
    */
    ecdh(atu8_sk: Uint8Array, atu8_pk: Uint8Array): Uint8Array;

    /**
     * Tweak the given private key by adding to it
     * @param atu8_sk - the private key (32 bytes)
     * @param atu8_tweak - the tweak vector (32 bytes)
     * @returns the tweaked private key
     */
    tweak_sk_add(atu8_sk: Uint8Array, atu8_tweak: Uint8Array): Uint8Array;

    /**
     * Tweak the given private key by multiplying it
     * @param atu8_sk - the private key (32 bytes)
     * @param atu8_tweak - the tweak vector (32 bytes)
     * @returns the tweaked private key
     */
    tweak_sk_mul(atu8_sk: Uint8Array, atu8_tweak: Uint8Array): Uint8Array;

    /**
     * Tweak the given public key by adding to it
     * @param atu8_pk - the public key (33 or 65 bytes)
     * @param atu8_tweak - the tweak vector (32 bytes)
     * @returns the tweaked public key
     */
    tweak_pk_add(atu8_pk: Uint8Array, atu8_tweak: Uint8Array, b_uncompressed?: boolean): Uint8Array;

    /**
     * Tweak the given public key by multiplying it
     * @param atu8_pk - the public key (33 or 65 bytes)
     * @param atu8_tweak - the tweak vector (32 bytes)
     * @returns the tweaked public key
     */
    tweak_pk_mul(atu8_pk: Uint8Array, atu8_tweak: Uint8Array, b_uncompressed?: boolean): Uint8Array;
}

Is libsecp256k1 modified?

No, the library is imported as a git submodule directly from upstream.

Building from source

Prerequisites:

  • Docker
  • Bun - a drop-in replacement for Node.js with native support for executing TypeScript
git clone --recurse-submodules https://github.com/SolarRepublic/wasm-secp256k1
cd wasm-secp256k1
bun install
bun run build

The WASM binary will be output to public/out/secp256k1.wasm.

The Emscripten-generated js file at public/out/secp256k1.js is not needed for production if you are using the provided wrapper.

See also

hash-wasm is a great library that provides performant hashing using optimized WASM binaries. Though its API is asynchronous, it also provides an undocumented synchronous API.