noble-bls12-381

bls12-381, a pairing-friendly elliptic curve construction.
This is a Barreto-Lynn-Scott curve with an embedding degree of 12. It’s optimal for zk-SNARKs at the 128-bit security level.
Implements Boneh-Lynn-Shacham signature scheme allowing construction of threshold signatures, which allows a user to sign lots of messages with one signature and verify them swiftly in a batch.
Matches following specs:
Check out BLS12-381 For The Rest Of Us to get started with the primitives.
This library belongs to noble crypto
noble-crypto — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, one small file
- Easily auditable TypeScript/JS code
- Uses es2020 bigint. Supported in Chrome, Firefox, node 10+
- All releases are signed and trusted
- Check out all libraries: secp256k1, ed25519, bls12-381, ripemd160
Usage
npm install noble-bls12-381
import * as bls from "bls12-381";
// Use hex or Uint8Arrays
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
const msg = 'hello';
(async () => {
const publicKey = bls.getPublicKey(privateKey);
const signature1 = await bls.sign(msg, PRIprivateKeyVATE_KEY);
const isCorrect1 = await bls.verify(msg, publicKey, signature);
// Sign 1 msg with 3 keys
const privateKeys = [
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5'
];
const signatures = await Promise.all(privateKeys.map(p => bls.sign(msg, p)));
const aggPubKey = await bls.aggregatePublicKeys(publicKeys);
const aggSignature = await bls.aggregateSignatures(signatures);
const isCorrect2 = await bls.verify(signature, msg, aggPubKey);
// Sign 3 msgs with 3 keys
const messages = ['whatsup', 'all good', 'thanks'];
const publicKeys = privateKeys.map(bls.getPublicKey);
const signatures2 = await Promise.all(privateKeys.map((p, i) => bls.sign(messages[i], p)));
const aggSignature2 = await bls.aggregateSignatures(signatures);
const isCorrect3 = await bls.verifyMultiple(signature, messages, publicKeys);
})();API
getPublicKey(privateKey)sign(message, privateKey)verify(signature, hash, publicKey)aggregatePublicKeys(publicKeys)aggregateSignatures(signatures)verifyMultiple(hashes, publicKeys, signature)pairing(G1Point, G2Point)
getPublicKey(privateKey)
function getPublicKey(privateKey: Uint8Array | string | bigint): Uint8Array;privateKey: Uint8Array | string | bigintwill 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 anotherPoint(x, y)which we will by default encode to hex Uint8Array.- Returns
Uint8Array: encoded publicKey for signature verification
sign(message, privateKey)
function sign(
message: Uint8Array | string,
privateKey: Uint8Array | string | bigint
): Promise<Uint8Array>;message: Uint8Array | string- message which would be hashed & signedprivateKey: Uint8Array | string | bigint- private key which will sign the hash- Returns
Uint8Array: encoded signature
Default domain (DST) is BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_, use bls.DST to change it.
verify(signature, hash, publicKey)
function verify(
signature: Uint8Array | string,
hash: Uint8Array | string,
publicKey: Uint8Array | string
): Promise<boolean>hash: Uint8Array | string- message hash that needs to be verifiedpublicKey: Uint8Array | string- e.g. that was generated fromprivateKeybygetPublicKeysignature: Uint8Array | string- object returned by thesignoraggregateSignaturesfunction- Returns
Promise<boolean>:true/falsewhether the signature matches hash
aggregatePublicKeys(publicKeys)
function aggregatePublicKeys(publicKeys: Uint8Array[] | string[]): Uint8Array;publicKeys: Uint8Array[] | string[]- e.g. that have been generated fromprivateKeybygetPublicKey- Returns
Uint8Array: one aggregated public key which calculated from public keys
aggregateSignatures(signatures)
function aggregateSignatures(signatures: Uint8Array[] | string[]): Uint8Array;signatures: Uint8Array[] | string[]- e.g. that have been generated bysign- Returns
Uint8Array: one aggregated signature which calculated from signatures
verifyMultiple(hashes, publicKeys, signature)
function verifyMultiple(
hashes: Uint8Array[] | string[],
publicKeys: Uint8Array[] | string[],
signature: Uint8Array | string
): Promise<boolean>hashes: Uint8Array[] | string[]- messages hashes that needs to be verifiedpublicKeys: Uint8Array[] | string[]- e.g. that were generated fromprivateKeysbygetPublicKeysignature: Uint8Array | string- object returned by theaggregateSignaturesfunction- Returns
Promise<boolean>:true/falsewhether the signature matches hashes
pairing(G1Point, G2Point)
function pairing(
g1Point: Point<bigint>,
g2Point: Point<[bigint, bigint]>,
withFinalExponent: boolean = true
): Point<[bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]>g1Point: Point<bigint>- simple point,x, yare bigintsg2Point: Point<[bigint, bigint]>- 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
Point<BigintTwelve>: paired point over 12-degree extension field.
Helpers
// 𝔽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.Fp // Subgroup
bls.Fp2 // 2-dimensional number
bls.Fp12
bls.G1Point
bls.G2Point
bls.G12PointInternals
- 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 keysS = pk x H(m)- signinge(P, H(m)) == e(G,S)- verification using pairingse(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))- signature aggregation
Speed
Pairing performance is the fastest of all pure-scripting-language implementations. That includes js, python, etc.
init x 46 ops/sec @ 21ms/op
getPublicKey(small) x 1273 ops/sec @ 785μs/op
getPublicKey(big) x 1124 ops/sec @ 889μs/op
sign x 11 ops/sec @ 90ms/op
aggregateSignatures x 187 ops/sec @ 5ms/op
verify x 15 ops/sec @ 66ms/op
pairing (batch) x 60 ops/sec @ 16ms/op
pairing (single) x 53 ops/sec @ 18ms/opSecurity
Noble is production-ready & secure. 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. This means that the lib is potentially vulnerable to timing attacks. 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 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
Check out a blog post about this library: Learning fast elliptic-curve cryptography in JS.
- Clone the repository.
npm installto install build dependencies like TypeScriptnpm run compileto compile TypeScript codenpm run testto run jest ontest/index.ts
Special thanks to Roman Koblov, who have helped to improve pairing speed.
License
MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.