Clean interface
This commit is contained in:
parent
021e741d17
commit
49d509aca4
|
@ -11,14 +11,18 @@ Javascript library for BLS (Boneh-Lynn-Shacham) signatures and signature aggrega
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import {PrivateKey} from "@chainsafe/bls";
|
import bls, {init} from "@chainsafe/bls";
|
||||||
|
|
||||||
const secretKey = PrivateKey.fromKeygen();
|
(async () => {
|
||||||
|
await init("herumi");
|
||||||
|
|
||||||
|
const secretKey = bls.PrivateKey.fromKeygen();
|
||||||
const publicKey = secretKey.toPublicKey();
|
const publicKey = secretKey.toPublicKey();
|
||||||
const message = new Uint8Array(32);
|
const message = new Uint8Array(32);
|
||||||
|
|
||||||
const signature = secretKey.sign(message);
|
const signature = secretKey.sign(message);
|
||||||
console.log("Is valid: ", signature.verify(publicKey, message));
|
console.log("Is valid: ", signature.verify(publicKey, message));
|
||||||
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Browser
|
### Browser
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {functionalInterfaceFactory} from "../functional";
|
||||||
|
|
||||||
export {PrivateKey, PublicKey, Signature};
|
export {PrivateKey, PublicKey, Signature};
|
||||||
|
|
||||||
export async function initBLS(): Promise<void> {
|
export async function init(): Promise<void> {
|
||||||
// Native bindings require no init() call
|
// Native bindings require no init() call
|
||||||
}
|
}
|
||||||
export function destroy(): void {
|
export function destroy(): void {
|
||||||
|
@ -20,7 +20,7 @@ export const bls: IBls = {
|
||||||
Signature,
|
Signature,
|
||||||
|
|
||||||
...functionalInterfaceFactory({PrivateKey, PublicKey, Signature}),
|
...functionalInterfaceFactory({PrivateKey, PublicKey, Signature}),
|
||||||
initBLS,
|
init,
|
||||||
destroy,
|
destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class PrivateKey implements IPrivateKey {
|
||||||
return new PrivateKey(sk);
|
return new PrivateKey(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
signMessage(message: Uint8Array): Signature {
|
sign(message: Uint8Array): Signature {
|
||||||
return new Signature(this.value.sign(message));
|
return new Signature(this.value.sign(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ export class PrivateKey implements IPrivateKey {
|
||||||
return new PublicKey(affine, jacobian);
|
return new PublicKey(affine, jacobian);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Buffer {
|
toBytes(): Uint8Array {
|
||||||
return Buffer.from(this.value.toBytes());
|
return this.value.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
|
|
|
@ -32,8 +32,8 @@ export class PublicKey implements IPublicKey {
|
||||||
return signature.verify(this, message);
|
return signature.verify(this, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Buffer {
|
toBytes(): Uint8Array {
|
||||||
return Buffer.from(this.affine.toBytes());
|
return this.affine.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
|
|
|
@ -39,8 +39,8 @@ export class Signature implements ISignature {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Buffer {
|
toBytes(): Uint8Array {
|
||||||
return Buffer.from(this.affine.toBytes());
|
return this.affine.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
|
|
|
@ -3,5 +3,5 @@ export const SIGNATURE_LENGTH = 96;
|
||||||
export const FP_POINT_LENGTH = 48;
|
export const FP_POINT_LENGTH = 48;
|
||||||
export const PUBLIC_KEY_LENGTH = FP_POINT_LENGTH;
|
export const PUBLIC_KEY_LENGTH = FP_POINT_LENGTH;
|
||||||
export const G2_HASH_PADDING = 16;
|
export const G2_HASH_PADDING = 16;
|
||||||
export const EMPTY_PUBLIC_KEY = Buffer.alloc(PUBLIC_KEY_LENGTH);
|
export const EMPTY_PUBLIC_KEY = Uint8Array.from(Buffer.alloc(PUBLIC_KEY_LENGTH));
|
||||||
export const EMPTY_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH);
|
export const EMPTY_SIGNATURE = Uint8Array.from(Buffer.alloc(SIGNATURE_LENGTH));
|
||||||
|
|
|
@ -13,18 +13,18 @@ export function functionalInterfaceFactory({
|
||||||
* @param secretKey
|
* @param secretKey
|
||||||
* @param messageHash
|
* @param messageHash
|
||||||
*/
|
*/
|
||||||
function sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer {
|
function sign(secretKey: Uint8Array, messageHash: Uint8Array): Uint8Array {
|
||||||
assert(secretKey, "secretKey is null or undefined");
|
assert(secretKey, "secretKey is null or undefined");
|
||||||
assert(messageHash, "messageHash is null or undefined");
|
assert(messageHash, "messageHash is null or undefined");
|
||||||
const privateKey = PrivateKey.fromBytes(secretKey);
|
const privateKey = PrivateKey.fromBytes(secretKey);
|
||||||
return privateKey.signMessage(messageHash).toBytes();
|
return privateKey.sign(messageHash).toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compines all given signature into one.
|
* Compines all given signature into one.
|
||||||
* @param signatures
|
* @param signatures
|
||||||
*/
|
*/
|
||||||
function aggregateSignatures(signatures: Uint8Array[]): Buffer {
|
function aggregateSignatures(signatures: Uint8Array[]): Uint8Array {
|
||||||
const agg = Signature.aggregate(signatures.map((p) => Signature.fromBytes(p)));
|
const agg = Signature.aggregate(signatures.map((p) => Signature.fromBytes(p)));
|
||||||
return agg.toBytes();
|
return agg.toBytes();
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ export function functionalInterfaceFactory({
|
||||||
* Combines all given public keys into single one
|
* Combines all given public keys into single one
|
||||||
* @param publicKeys
|
* @param publicKeys
|
||||||
*/
|
*/
|
||||||
function aggregatePubkeys(publicKeys: Uint8Array[]): Buffer {
|
function aggregatePubkeys(publicKeys: Uint8Array[]): Uint8Array {
|
||||||
const agg = PublicKey.aggregate(publicKeys.map((p) => PublicKey.fromBytes(p)));
|
const agg = PublicKey.aggregate(publicKeys.map((p) => PublicKey.fromBytes(p)));
|
||||||
return agg.toBytes();
|
return agg.toBytes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export async function setupBls(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache a promise for Bls instead of Bls to make sure it is initialized only once
|
// Cache a promise for Bls instead of Bls to make sure it is initialized only once
|
||||||
export async function initBLS(): Promise<void> {
|
export async function init(): Promise<void> {
|
||||||
if (!blsGlobalPromise) {
|
if (!blsGlobalPromise) {
|
||||||
blsGlobalPromise = setupBls();
|
blsGlobalPromise = setupBls();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import {PrivateKey} from "./privateKey";
|
import {PrivateKey} from "./privateKey";
|
||||||
import {PublicKey} from "./publicKey";
|
import {PublicKey} from "./publicKey";
|
||||||
import {Signature} from "./signature";
|
import {Signature} from "./signature";
|
||||||
import {initBLS, destroy} from "./context";
|
import {init, destroy} from "./context";
|
||||||
import {IBls} from "../interface";
|
import {IBls} from "../interface";
|
||||||
import {functionalInterfaceFactory} from "../functional";
|
import {functionalInterfaceFactory} from "../functional";
|
||||||
|
|
||||||
export {PrivateKey, PublicKey, Signature, initBLS, destroy};
|
export {PrivateKey, PublicKey, Signature, init, destroy};
|
||||||
|
|
||||||
const bls: IBls = {
|
const bls: IBls = {
|
||||||
PrivateKey,
|
PrivateKey,
|
||||||
|
@ -13,7 +13,7 @@ const bls: IBls = {
|
||||||
Signature,
|
Signature,
|
||||||
|
|
||||||
...functionalInterfaceFactory({PrivateKey, PublicKey, Signature}),
|
...functionalInterfaceFactory({PrivateKey, PublicKey, Signature}),
|
||||||
initBLS,
|
init,
|
||||||
destroy,
|
destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ export class PrivateKey implements IPrivateKey {
|
||||||
assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes");
|
assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes");
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const secretKey = new context.SecretKey();
|
const secretKey = new context.SecretKey();
|
||||||
secretKey.deserialize(Buffer.from(bytes));
|
secretKey.deserialize(bytes);
|
||||||
return new PrivateKey(secretKey);
|
return new PrivateKey(secretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export class PrivateKey implements IPrivateKey {
|
||||||
return this.fromBytes(sk);
|
return this.fromBytes(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
signMessage(message: Uint8Array): Signature {
|
sign(message: Uint8Array): Signature {
|
||||||
return new Signature(this.value.sign(message));
|
return new Signature(this.value.sign(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ export class PrivateKey implements IPrivateKey {
|
||||||
return new PublicKey(this.value.getPublicKey());
|
return new PublicKey(this.value.getPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Buffer {
|
toBytes(): Uint8Array {
|
||||||
return Buffer.from(this.value.serialize());
|
return this.value.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
|
|
|
@ -47,8 +47,8 @@ export class PublicKey implements IPublicKey {
|
||||||
return this.value.verify(signature.value, messageHash);
|
return this.value.verify(signature.value, messageHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Buffer {
|
toBytes(): Uint8Array {
|
||||||
return Buffer.from(this.value.serialize());
|
return this.value.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
|
|
|
@ -64,8 +64,8 @@ export class Signature implements ISignature {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Buffer {
|
toBytes(): Uint8Array {
|
||||||
return Buffer.from(this.value.serialize());
|
return this.value.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
|
|
10
src/index.ts
10
src/index.ts
|
@ -3,17 +3,13 @@ import blsHerumi from "./herumi";
|
||||||
|
|
||||||
export type Implementation = "herumi" | "blst-native";
|
export type Implementation = "herumi" | "blst-native";
|
||||||
|
|
||||||
// This proxy makes sure a nice error is printed if BLS is used before init()
|
// TODO: Use a Proxy for example to throw an error if it's not initialized yet
|
||||||
export let bls: IBls = new Proxy({} as IBls, {
|
export let bls: IBls;
|
||||||
get: function () {
|
|
||||||
throw Error("BLS not initialized, call init() before");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
async function getImplementation(impl: Implementation) {
|
async function getImplementation(impl: Implementation) {
|
||||||
switch (impl) {
|
switch (impl) {
|
||||||
case "herumi":
|
case "herumi":
|
||||||
await blsHerumi.initBLS();
|
await blsHerumi.init();
|
||||||
return blsHerumi;
|
return blsHerumi;
|
||||||
|
|
||||||
case "blst-native":
|
case "blst-native":
|
||||||
|
|
|
@ -15,14 +15,14 @@ export interface IBls {
|
||||||
aggregate(signatures: ISignature[]): ISignature;
|
aggregate(signatures: ISignature[]): ISignature;
|
||||||
};
|
};
|
||||||
|
|
||||||
sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer;
|
sign(secretKey: Uint8Array, messageHash: Uint8Array): Uint8Array;
|
||||||
aggregatePubkeys(publicKeys: Uint8Array[]): Buffer;
|
aggregatePubkeys(publicKeys: Uint8Array[]): Uint8Array;
|
||||||
aggregateSignatures(signatures: Uint8Array[]): Buffer;
|
aggregateSignatures(signatures: Uint8Array[]): Uint8Array;
|
||||||
verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean;
|
verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean;
|
||||||
verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean;
|
verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean;
|
||||||
verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean;
|
verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean;
|
||||||
|
|
||||||
initBLS(): Promise<void>;
|
init(): Promise<void>;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,12 +32,12 @@ export interface IKeypair {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPublicKey {
|
export interface IPublicKey {
|
||||||
toBytes(): Buffer;
|
toBytes(): Uint8Array;
|
||||||
toHex(): string;
|
toHex(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISignature {
|
export interface ISignature {
|
||||||
toBytes(): Buffer;
|
toBytes(): Uint8Array;
|
||||||
toHex(): string;
|
toHex(): string;
|
||||||
verify(publicKey: IPublicKey, message: Uint8Array): boolean;
|
verify(publicKey: IPublicKey, message: Uint8Array): boolean;
|
||||||
verifyAggregate(publicKeys: IPublicKey[], message: Uint8Array): boolean;
|
verifyAggregate(publicKeys: IPublicKey[], message: Uint8Array): boolean;
|
||||||
|
@ -46,7 +46,7 @@ export interface ISignature {
|
||||||
|
|
||||||
export interface IPrivateKey {
|
export interface IPrivateKey {
|
||||||
toPublicKey(): IPublicKey;
|
toPublicKey(): IPublicKey;
|
||||||
signMessage(message: Uint8Array): ISignature;
|
sign(message: Uint8Array): ISignature;
|
||||||
toBytes(): Buffer;
|
toBytes(): Uint8Array;
|
||||||
toHex(): string;
|
toHex(): string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {runForAllImplementations} from "../switch";
|
||||||
import {IPublicKey, ISignature} from "../../src/interface";
|
import {IPublicKey, ISignature} from "../../src/interface";
|
||||||
|
|
||||||
runForAllImplementations(async (bls, implementation) => {
|
runForAllImplementations(async (bls, implementation) => {
|
||||||
await bls.initBLS();
|
await bls.init();
|
||||||
|
|
||||||
const aggCount = 30;
|
const aggCount = 30;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ runForAllImplementations(async (bls, implementation) => {
|
||||||
const msg = randomMsg();
|
const msg = randomMsg();
|
||||||
const sk = bls.PrivateKey.fromKeygen();
|
const sk = bls.PrivateKey.fromKeygen();
|
||||||
const pk = sk.toPublicKey();
|
const pk = sk.toPublicKey();
|
||||||
const sig = sk.signMessage(msg);
|
const sig = sk.sign(msg);
|
||||||
return {
|
return {
|
||||||
input: {pk, msg, sig},
|
input: {pk, msg, sig},
|
||||||
resultCheck: (valid) => valid === true,
|
resultCheck: (valid) => valid === true,
|
||||||
|
@ -38,7 +38,7 @@ runForAllImplementations(async (bls, implementation) => {
|
||||||
const dataArr = range(aggCount).map(() => {
|
const dataArr = range(aggCount).map(() => {
|
||||||
const sk = bls.PrivateKey.fromKeygen();
|
const sk = bls.PrivateKey.fromKeygen();
|
||||||
const pk = sk.toPublicKey();
|
const pk = sk.toPublicKey();
|
||||||
const sig = sk.signMessage(msg);
|
const sig = sk.sign(msg);
|
||||||
return {pk, sig};
|
return {pk, sig};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ runForAllImplementations(async (bls, implementation) => {
|
||||||
const msg = randomMsg();
|
const msg = randomMsg();
|
||||||
const sigs = range(aggCount).map(() => {
|
const sigs = range(aggCount).map(() => {
|
||||||
const sk = bls.PrivateKey.fromKeygen();
|
const sk = bls.PrivateKey.fromKeygen();
|
||||||
return sk.signMessage(msg);
|
return sk.sign(msg);
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
input: sigs,
|
input: sigs,
|
||||||
|
|
|
@ -27,7 +27,7 @@ export function describeForAllImplementations(callback: (bls: IBls) => void): vo
|
||||||
runForAllImplementations((bls, implementation) => {
|
runForAllImplementations((bls, implementation) => {
|
||||||
describe(implementation, () => {
|
describe(implementation, () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await bls.initBLS();
|
await bls.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
callback(bls);
|
callback(bls);
|
||||||
|
|
|
@ -8,7 +8,7 @@ export function runIndexTests(bls: IBls): void {
|
||||||
const sk = bls.PrivateKey.fromKeygen();
|
const sk = bls.PrivateKey.fromKeygen();
|
||||||
const pk = sk.toPublicKey();
|
const pk = sk.toPublicKey();
|
||||||
const msg = randomMessage();
|
const msg = randomMessage();
|
||||||
const sig = sk.signMessage(msg);
|
const sig = sk.sign(msg);
|
||||||
return {sk, pk, msg, sig};
|
return {sk, pk, msg, sig};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,12 +51,7 @@ export function runIndexTests(bls: IBls): void {
|
||||||
const msgs = getN(2, () => randomMessage());
|
const msgs = getN(2, () => randomMessage());
|
||||||
const pks = sks.map((sk) => sk.toPublicKey());
|
const pks = sks.map((sk) => sk.toPublicKey());
|
||||||
|
|
||||||
const sigs = [
|
const sigs = [sks[0].sign(msgs[0]), sks[1].sign(msgs[0]), sks[2].sign(msgs[1]), sks[3].sign(msgs[1])];
|
||||||
sks[0].signMessage(msgs[0]),
|
|
||||||
sks[1].signMessage(msgs[0]),
|
|
||||||
sks[2].signMessage(msgs[1]),
|
|
||||||
sks[3].signMessage(msgs[1]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const aggPubkeys = [
|
const aggPubkeys = [
|
||||||
bls.aggregatePubkeys([pks[0], pks[1]].map((pk) => pk.toBytes())),
|
bls.aggregatePubkeys([pks[0], pks[1]].map((pk) => pk.toBytes())),
|
||||||
|
@ -74,7 +69,7 @@ export function runIndexTests(bls: IBls): void {
|
||||||
const msg = randomMessage();
|
const msg = randomMessage();
|
||||||
const sks = getN(n, () => bls.PrivateKey.fromKeygen());
|
const sks = getN(n, () => bls.PrivateKey.fromKeygen());
|
||||||
const pks = sks.map((sk) => sk.toPublicKey());
|
const pks = sks.map((sk) => sk.toPublicKey());
|
||||||
const sigs = sks.map((sk) => sk.signMessage(msg));
|
const sigs = sks.map((sk) => sk.sign(msg));
|
||||||
|
|
||||||
const aggregateSignature = bls.aggregateSignatures(sigs.map((sig) => sig.toBytes()));
|
const aggregateSignature = bls.aggregateSignatures(sigs.map((sig) => sig.toBytes()));
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe("herumi", () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
// For consistency with describeForAllImplementations
|
// For consistency with describeForAllImplementations
|
||||||
// eslint-disable-next-line import/no-named-as-default-member
|
// eslint-disable-next-line import/no-named-as-default-member
|
||||||
await herumi.initBLS();
|
await herumi.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
runPrivateKeyTests(herumi);
|
runPrivateKeyTests(herumi);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
export function fromHexString(hex: string): Buffer {
|
export function fromHexString(hex: string): Uint8Array {
|
||||||
return Buffer.from(hex.replace("0x", ""), "hex");
|
return Uint8Array.from(Buffer.from(hex.replace("0x", ""), "hex"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toHexString(bytes: Buffer | Uint8Array): string {
|
export function toHexString(bytes: Buffer | Uint8Array): string {
|
||||||
|
|
Reference in New Issue