elliptic curves crypto functions

Usage no npm install needed!

<script type="module">
  import aldenmlEcc from 'https://cdn.skypack.dev/@aldenml/ecc';



javascript npm

This is the javascript version of the ecc library.

It is a WebAssembly compilation with a thin layer on top to expose the cryptographic primitives.


OPRF Oblivious pseudo-random functions using ristretto255

This is an implementation of draft-irtf-cfrg-voprf-08 ciphersuite OPRF(ristretto255, SHA-512) using libsodium.

There are two variants in this protocol: a base mode and verifiable mode. In the base mode, a client and server interact to compute output = F(skS, input, info), where input is the client's private input, skS is the server's private key, info is the public input, and output is the computation output. The client learns output and the server learns nothing. In the verifiable mode, the client also receives proof that the server used skS in computing the function.

The flow is shown below (from the irtf draft):

  Client(input, info)                               Server(skS, info)
  blind, blindedElement = Blind(input)


                 evaluatedElement = Evaluate(skS, blindedElement, info)


  output = Finalize(input, blind, evaluatedElement, blindedElement, info)

In the verifiable mode of the protocol, the server additionally computes a proof in Evaluate. The client verifies this proof using the server's expected public key before completing the protocol and producing the protocol output.

OPAQUE The OPAQUE Asymmetric PAKE Protocol

This is an implementation of draft-irtf-cfrg-opaque-07 using libsodium.

OPAQUE consists of two stages: registration and authenticated key exchange. In the first stage, a client registers its password with the server and stores its encrypted credentials on the server, but the server never knows what the password it.

The registration flow is shown below (from the irtf draft):

       creds                                   parameters
         |                                         |
         v                                         v
       Client                                    Server
                   registration request
                   registration response
         |                                         |
         v                                         v
     export_key                                 record

In the second stage, the client outputs two values, an "export_key" (matching that from registration) and a "session_key". The server outputs a single value "session_key" that matches that of the client.

The authenticated key exchange flow is shown below (from the irtf draft):

       creds                             (parameters, record)
         |                                         |
         v                                         v
       Client                                    Server
                      AKE message 1
                      AKE message 2
                      AKE message 3
         |                                         |
         v                                         v
   (export_key, session_key)                  session_key

The public API for implementing the protocol is:

  • Client
  • Server

Ethereum BLS Signature

Ethereum uses BLS signatures as specified in the IETF draft draft-irtf-cfrg-bls-signature-04 ciphersuite BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_. This library provides the following API:


BLS is a digital signature scheme with aggregation properties that can be applied to signatures and public keys. For this reason, in the context of blockchains, BLS signatures are used for authenticating transactions, votes during the consensus protocol, and to reduce the bandwidth and storage requirements.

BLS12-381 Pairing

In the context of pairing friendly elliptic curves, a pairing is a map e: G1xG2 -> GT such that for each a, b, P and Q

e(a * P, b * Q) = e(P, Q)^(a * b)

You can use this to obtain such pairings:

const libecc = await libecc_module();

const a = new Uint8Array(32);
const b = new Uint8Array(32);

const aP = new Uint8Array(96);
const bQ = new Uint8Array(192);

libecc.ecc_bls12_381_g1_scalarmult_base(aP, a); // a * P
libecc.ecc_bls12_381_g2_scalarmult_base(bQ, b); // b * Q

const pairing = new Uint8Array(576);
libecc.ecc_bls12_381_pairing(pairing, aP, bQ); // e(a * P, b * Q)

Read more at:

Proxy Re-Encryption (PRE)

With a pairing-friendly elliptic curve and a well-defined pairing operation, you can implement a proxy re-encryption scheme. This library provides an implementation using BLS12-381.

Example of how to use it:

// client A setup public/private keys and signing keys
const keysA = await pre_schema1_KeyGen();
const signingA = await pre_schema1_SigningKeyGen();

// client B setup public/private keys (signing keys are not used here)
const keysB = await pre_schema1_KeyGen();

// proxy server setup signing keys
const signingProxy = await pre_schema1_SigningKeyGen();

// client A select a plaintext message, this message
// in itself is random, but can be used as a seed
// for symmetric encryption keys
const message = await pre_schema1_MessageGen();

// client A encrypts the message to itself, making it
// possible to send this ciphertext to the proxy.
const ciphertextLevel1 = await pre_schema1_Encrypt(message, keysA.pk, signingA);

// client A sends ciphertextLevel1 to the proxy server and
// eventually client A allows client B to see the encrypted
// message, in this case the proxy needs to re-encrypt
// ciphertextLevel1 (without ever knowing the plaintext).
// In order to do that, the client A needs to create a re-encryption
// key that the proxy can use to perform such operation.

// client A creates a re-encryption key that the proxy can use
// to re-encrypt the ciphertext (ciphertextLevel1) in order for
// client B be able to recover the original message
const reEncKey = await pre_schema1_ReKeyGen(keysA.sk, keysB.pk, signingA);

// the proxy re-encrypt the ciphertext ciphertextLevel1 with such
// a key that allows client B to recover the original message
const ciphertextLevel2 = await pre_schema1_ReEncrypt(
    signingA.spk, keysB.pk,

// client B is able to decrypt ciphertextLevel2 and the result
// is the original plaintext message
const messageDecrypted = await pre_schema1_DecryptLevel2(
    keysB.sk, signingProxy.spk

// now both client A and client B share the same plaintext message
// messageDecrypted is equal to message

Read more at:
"A Fully Secure Unidirectional and Multi-user Proxy Re-encryption Scheme" by H. Wang and Z. Cao, 2009
"A Multi-User CCA-Secure Proxy Re-Encryption Scheme" by Y. Cai and X. Liu, 2014
"Cryptographically Enforced Orthogonal Access Control at Scale" by B. Wall and P. Walsh, 2018