signature=0d908749f1a459f387c79219427a1a05,GitHub - paulmillr/noble-bls12-381: Fastest implementatio...

noble-bls12-381 badge.svg68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64655f7374796c652d70726574746965722d6666363962342e7376673f7374796c653d666c61742d737175617265

Construct zk-SNARKs at the 128-bit security

Use threshold signatures,

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.

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

Usage

Node.js and browser:

npm install noble-bls12-381

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)

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. It should work properly with ETH2 and FIL keys.

sign(message, privateKey)

function sign(

message: Uint8Array,

privateKey: Uint8Array

): Promise;

function sign(

message: string,

privateKey: string

): Promise;

function sign(

message: PointG2,

privateKey: Uint8Array | string | bigint

): Promise;

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)

function verify(

signature: Uint8Array | string | PointG2,

message: Uint8Array | string | PointG2,

publicKey: Uint8Array | string | PointG1

): Promise

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: true / false whether the signature matches hash

aggregatePublicKeys(publicKeys)

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)

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)

function verifyBatch(

signature: Uint8Array | string | PointG2,

messages: (Uint8Array | string | PointG2)[],

publicKeys: (Uint8Array | string | PointG1)[]

): Promise

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: true / false whether the signature matches hashes

pairing(G1Point, G2Point)

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

// 𝔽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 34 ops/sec @ 28ms/op

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

Clone the repository.

npm install to install build dependencies like TypeScript

npm run build to compile TypeScript code

npm run test to run jest on test/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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值