# noble-bls12-381 ![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)

**[Fastest](#speed)** implementation of BLS12-381 in a scripting language. The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:

- Construct [zk-SNARKs](https://z.cash/technology/zksnarks/) at the 128-bit security
- Use [threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
  which allows a user to sign lots of messages with one signature and verify them swiftly in a batch,
  using Boneh-Lynn-Shacham signature scheme.

Compatible with ETH2, matches specs [pairing-curves-09](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-09), [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04), [hash-to-curve-10](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10).

To learn more about internals, check out [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381) & [key concepts of pairings](https://medium.com/@alonmuroch_65570/bls-signatures-part-2-key-concepts-of-pairings-27a8a9533d0c). To try it live, see [the online demo](https://paulmillr.com/ecc) & [threshold sigs demo](https://genthresh.com).

### This library belongs to *noble* crypto

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

- Just two files
- No dependencies
- Easily auditable TypeScript/JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Check out all libraries:
  [secp256k1](https://github.com/paulmillr/noble-secp256k1),
  [ed25519](https://github.com/paulmillr/noble-ed25519),
  [bls12-381](https://github.com/paulmillr/noble-bls12-381),
  [ripemd160](https://github.com/paulmillr/noble-ripemd160)

## Usage

Node.js and browser:

> npm install noble-bls12-381

```js
const bls = require('noble-bls12-381');

// You can use Uint8Array, or hex string for readability
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
const privateKeys = [
  '18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
  'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
  '16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5'
];
const message = '64726e3da8';
const messages = ['d2', '0d98', '05caf3'];

(async () => {
  const publicKey = bls.getPublicKey(privateKey);
  const publicKeys = privateKeys.map(bls.getPublicKey);

  const signature = await bls.sign(message, privateKey);
  const isCorrect = await bls.verify(signature, message, publicKey);
  console.log('key', publicKey);
  console.log('signature', signature);
  console.log('is correct:', isCorrect);

  // Sign 1 msg with 3 keys
  const signatures2 = await Promise.all(privateKeys.map(p => bls.sign(message, p)));
  const aggPubKey2 = bls.aggregatePublicKeys(publicKeys);
  const aggSignature2 = bls.aggregateSignatures(signatures2);
  const isCorrect2 = await bls.verify(aggSignature2, message, aggPubKey2);
  console.log();
  console.log('signatures are', signatures2);
  console.log('merged to one signature', aggSignature2);
  console.log('is correct:', isCorrect2);

  // Sign 3 msgs with 3 keys
  const signatures3 = await Promise.all(privateKeys.map((p, i) => bls.sign(messages[i], p)));
  const aggSignature3 = bls.aggregateSignatures(signatures3);
  const isCorrect3 = await bls.verifyBatch(aggSignature3, messages, publicKeys);
  console.log();
  console.log('keys', publicKeys);
  console.log('signatures', signatures3);
  console.log('merged to one signature', aggSignature3);
  console.log('is correct:', isCorrect3);
})();
```

## API

- [`getPublicKey(privateKey)`](#getpublickeyprivatekey)
- [`sign(message, privateKey)`](#signmessage-privatekey)
- [`verify(signature, message, publicKey)`](#verifysignature-message-publickey)
- [`aggregatePublicKeys(publicKeys)`](#aggregatepublickeyspublickeys)
- [`aggregateSignatures(signatures)`](#aggregatesignaturessignatures)
- [`verifyBatch(signature, messages, publicKeys)`](#verifybatchsignature-messages-publickeys)
- [`pairing(G1Point, G2Point)`](#pairingg1point-g2point)

##### `getPublicKey(privateKey)`
```typescript
function getPublicKey(privateKey: Uint8Array | bigint): Uint8Array;
function getPublicKey(privateKey: string): string;
```
- `privateKey: Uint8Array | string | bigint` will be used to generate public key.
  Public key is generated by executing 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.
- Returns `Uint8Array`: encoded publicKey for signature verification

**Note:** if you need spec-based `KeyGen`, use [paulmillr/bls12-381-keygen](https://github.com/paulmillr/bls12-381-keygen). It should work properly with ETH2 and FIL keys.

##### `sign(message, privateKey)`
```typescript
function sign(
  message: Uint8Array,
  privateKey: Uint8Array
): Promise<Uint8Array>;
function sign(
  message: string,
  privateKey: string
): Promise<string>;
function sign(
  message: PointG2,
  privateKey: Uint8Array | string | bigint
): Promise<PointG2>;
```
- `message: Uint8Array | string` - message which would be hashed & signed
- `privateKey: Uint8Array | string | bigint` - private key which will sign the hash
- Returns `Uint8Array | string | PointG2`: encoded signature

Default domain (DST) is `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_`, use `bls.DST` to change it.

##### `verify(signature, message, publicKey)`
```typescript
function verify(
  signature: Uint8Array | string | PointG2,
  message: Uint8Array | string | PointG2,
  publicKey: Uint8Array | string | PointG1
): Promise<boolean>
```
- `signature: Uint8Array | string` - object returned by the `sign` or `aggregateSignatures` function
- `message: Uint8Array | string` - message hash that needs to be verified
- `publicKey: Uint8Array | string` - e.g. that was generated from `privateKey` by `getPublicKey`
- Returns `Promise<boolean>`: `true` / `false` whether the signature matches hash

##### `aggregatePublicKeys(publicKeys)`
```typescript
function aggregatePublicKeys(publicKeys: Uint8Array[]): Uint8Array;
function aggregatePublicKeys(publicKeys: string[]): string;
function aggregatePublicKeys(publicKeys: PointG1[]): PointG1;
```
- `publicKeys: (Uint8Array | string | PointG1)[]` - e.g. that have been generated from `privateKey` by `getPublicKey`
- Returns `Uint8Array | PointG1`: one aggregated public key which calculated from public keys

##### `aggregateSignatures(signatures)`
```typescript
function aggregateSignatures(signatures: Uint8Array[]): Uint8Array;
function aggregateSignatures(signatures: string[]): string;
function aggregateSignatures(signatures: PointG2[]): PointG2;
```
- `signatures: (Uint8Array | string | PointG2)[]` - e.g. that have been generated by `sign`
- Returns `Uint8Array | PointG2`: one aggregated signature which calculated from signatures

##### `verifyBatch(signature, messages, publicKeys)`
```typescript
function verifyBatch(
  signature: Uint8Array | string | PointG2,
  messages: (Uint8Array | string | PointG2)[],
  publicKeys: (Uint8Array | string | PointG1)[]
): Promise<boolean>
```
- `signature: Uint8Array | string | PointG2` - object returned by the `aggregateSignatures` function
- `messages: (Uint8Array | string | PointG2)[]` - messages hashes that needs to be verified
- `publicKeys: (Uint8Array | string | PointG1)[]` - e.g. that were generated from `privateKeys` by `getPublicKey`
- Returns `Promise<boolean>`: `true` / `false` whether the signature matches hashes

##### `pairing(G1Point, G2Point)`
```typescript
function pairing(
  g1Point: PointG1,
  g2Point: PointG2,
  withFinalExponent: boolean = true
): Fq12
```
- `g1Point: PointG1` - simple point, `x, y` are bigints
- `g2Point: PointG2` - point over curve with imaginary numbers (`(x, x_1), (y, y_1)`)
- `withFinalExponent: boolean` - should the result be powered by curve order. Very slow.
- Returns `Fq12`: paired point over 12-degree extension field.

##### Helpers

```typescript
// 𝔽p
bls.CURVE.P // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn

// Prime order
bls.CURVE.r // 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n

// Hash base point (x, y)
bls.CURVE.Gx // 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569

// Signature base point ((x_1, x_2), (y_1, y_2))
bls.CURVE.Gy
// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758, 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582, 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905

// Classes
bls.Fq
bls.Fq2
bls.Fq12
bls.G1Point
bls.G2Point
```

## Internals

The library uses G1 for public keys and G2 for signatures. Adding support for G1 signatures is planned.

- BLS Relies on Bilinear Pairing (expensive)
- Private Keys: 32 bytes
- Public Keys: 48 bytes: 381 bit affine x coordinate, encoded into 48 big-endian bytes.
- Signatures: 96 bytes: two 381 bit integers (affine x coordinate), encoded into two 48 big-endian byte arrays.
    - The signature is a point on the G2 subgroup, which is defined over a finite field
    with elements twice as big as the G1 curve (G2 is over Fq2 rather than Fq. Fq2 is analogous to the complex numbers).
- The 12 stands for the Embedding degree.

Formulas:

- `P = pk x G` - public keys
- `S = pk x H(m)` - signing
- `e(P, H(m)) == e(G, S)` - verification using pairings
- `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation

The BLS parameters for the library are:

- `PK_IN` `G1`
- `HASH_OR_ENCODE` `true`
- `DST` `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`
- `RAND_BITS` `64`

Filecoin uses little endian byte arrays for private keys - so ensure to reverse byte order if you'll use it with FIL.

## Speed

To achieve the best speed out of all JS / Python implementations, the library employs optimizations:

- cyclotomic exponentation
- frobenius coefficients
- endomorphism for clearing cofactor

Benchmarks measured with Apple M1:

```
getPublicKey x 1,639 ops/sec @ 609μs/op
sign x 33 ops/sec @ 30ms/opverify x 32 ops/sec @ 30ms/op
pairing x 75 ops/sec @ 13ms/op
aggregatePublicKeys/8 x 396 ops/sec @ 2ms/op
aggregateSignatures/8 x 74 ops/sec @ 13ms/op

with compression / decompression disabled:
sign/nc x 51 ops/sec @ 19ms/op
verify/nc x 55 ops/sec @ 17ms/op
aggregatePublicKeys/32 x 5,402 ops/sec @ 185μs/op
aggregatePublicKeys/128 x 1,305 ops/sec @ 766μs/op
aggregatePublicKeys/512 x 332 ops/sec @ 3ms/op
aggregatePublicKeys/2048 x 81 ops/sec @ 12ms/op
aggregateSignatures/32 x 1,424 ops/sec @ 701μs/op
aggregateSignatures/128 x 347 ops/sec @ 2ms/op
aggregateSignatures/512 x 85 ops/sec @ 11ms/op
aggregateSignatures/2048 x 21 ops/sec @ 46ms/op
```

## Security

Noble is production-ready. Our goal is to have it audited by a good security expert.

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:

1. JIT-compiler and Garbage Collector make "constant time" extremely hard to achieve in a scripting language.
2. 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.
3. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages.
4. 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 rootkits with every `npm install`. Our goal is to minimize this attack vector.

## Contributing

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

Special thanks to [Roman Koblov](https://github.com/romankoblov), who have helped to improve pairing speed.

## License

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