Clean interface

This commit is contained in:
dapplion 2020-11-25 16:09:44 +00:00
parent 021e741d17
commit 49d509aca4
19 changed files with 59 additions and 64 deletions

View File

@ -11,14 +11,18 @@ Javascript library for BLS (Boneh-Lynn-Shacham) signatures and signature aggrega
## Usage
```ts
import {PrivateKey} from "@chainsafe/bls";
import bls, {init} from "@chainsafe/bls";
const secretKey = PrivateKey.fromKeygen();
const publicKey = secretKey.toPublicKey();
const message = new Uint8Array(32);
(async () => {
await init("herumi");
const signature = secretKey.sign(message);
console.log("Is valid: ", signature.verify(publicKey, message));
const secretKey = bls.PrivateKey.fromKeygen();
const publicKey = secretKey.toPublicKey();
const message = new Uint8Array(32);
const signature = secretKey.sign(message);
console.log("Is valid: ", signature.verify(publicKey, message));
})();
```
### Browser

View File

@ -7,7 +7,7 @@ import {functionalInterfaceFactory} from "../functional";
export {PrivateKey, PublicKey, Signature};
export async function initBLS(): Promise<void> {
export async function init(): Promise<void> {
// Native bindings require no init() call
}
export function destroy(): void {
@ -20,7 +20,7 @@ export const bls: IBls = {
Signature,
...functionalInterfaceFactory({PrivateKey, PublicKey, Signature}),
initBLS,
init,
destroy,
};

View File

@ -26,7 +26,7 @@ export class PrivateKey implements IPrivateKey {
return new PrivateKey(sk);
}
signMessage(message: Uint8Array): Signature {
sign(message: Uint8Array): Signature {
return new Signature(this.value.sign(message));
}
@ -36,8 +36,8 @@ export class PrivateKey implements IPrivateKey {
return new PublicKey(affine, jacobian);
}
toBytes(): Buffer {
return Buffer.from(this.value.toBytes());
toBytes(): Uint8Array {
return this.value.toBytes();
}
toHex(): string {

View File

@ -32,8 +32,8 @@ export class PublicKey implements IPublicKey {
return signature.verify(this, message);
}
toBytes(): Buffer {
return Buffer.from(this.affine.toBytes());
toBytes(): Uint8Array {
return this.affine.toBytes();
}
toHex(): string {

View File

@ -39,8 +39,8 @@ export class Signature implements ISignature {
);
}
toBytes(): Buffer {
return Buffer.from(this.affine.toBytes());
toBytes(): Uint8Array {
return this.affine.toBytes();
}
toHex(): string {

View File

@ -3,5 +3,5 @@ export const SIGNATURE_LENGTH = 96;
export const FP_POINT_LENGTH = 48;
export const PUBLIC_KEY_LENGTH = FP_POINT_LENGTH;
export const G2_HASH_PADDING = 16;
export const EMPTY_PUBLIC_KEY = Buffer.alloc(PUBLIC_KEY_LENGTH);
export const EMPTY_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH);
export const EMPTY_PUBLIC_KEY = Uint8Array.from(Buffer.alloc(PUBLIC_KEY_LENGTH));
export const EMPTY_SIGNATURE = Uint8Array.from(Buffer.alloc(SIGNATURE_LENGTH));

View File

@ -13,18 +13,18 @@ export function functionalInterfaceFactory({
* @param secretKey
* @param messageHash
*/
function sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer {
function sign(secretKey: Uint8Array, messageHash: Uint8Array): Uint8Array {
assert(secretKey, "secretKey is null or undefined");
assert(messageHash, "messageHash is null or undefined");
const privateKey = PrivateKey.fromBytes(secretKey);
return privateKey.signMessage(messageHash).toBytes();
return privateKey.sign(messageHash).toBytes();
}
/**
* Compines all given signature into one.
* @param signatures
*/
function aggregateSignatures(signatures: Uint8Array[]): Buffer {
function aggregateSignatures(signatures: Uint8Array[]): Uint8Array {
const agg = Signature.aggregate(signatures.map((p) => Signature.fromBytes(p)));
return agg.toBytes();
}
@ -33,7 +33,7 @@ export function functionalInterfaceFactory({
* Combines all given public keys into single one
* @param publicKeys
*/
function aggregatePubkeys(publicKeys: Uint8Array[]): Buffer {
function aggregatePubkeys(publicKeys: Uint8Array[]): Uint8Array {
const agg = PublicKey.aggregate(publicKeys.map((p) => PublicKey.fromBytes(p)));
return agg.toBytes();
}

View File

@ -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
export async function initBLS(): Promise<void> {
export async function init(): Promise<void> {
if (!blsGlobalPromise) {
blsGlobalPromise = setupBls();
}

View File

@ -1,11 +1,11 @@
import {PrivateKey} from "./privateKey";
import {PublicKey} from "./publicKey";
import {Signature} from "./signature";
import {initBLS, destroy} from "./context";
import {init, destroy} from "./context";
import {IBls} from "../interface";
import {functionalInterfaceFactory} from "../functional";
export {PrivateKey, PublicKey, Signature, initBLS, destroy};
export {PrivateKey, PublicKey, Signature, init, destroy};
const bls: IBls = {
PrivateKey,
@ -13,7 +13,7 @@ const bls: IBls = {
Signature,
...functionalInterfaceFactory({PrivateKey, PublicKey, Signature}),
initBLS,
init,
destroy,
};

View File

@ -19,7 +19,7 @@ export class PrivateKey implements IPrivateKey {
assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes");
const context = getContext();
const secretKey = new context.SecretKey();
secretKey.deserialize(Buffer.from(bytes));
secretKey.deserialize(bytes);
return new PrivateKey(secretKey);
}
@ -32,7 +32,7 @@ export class PrivateKey implements IPrivateKey {
return this.fromBytes(sk);
}
signMessage(message: Uint8Array): Signature {
sign(message: Uint8Array): Signature {
return new Signature(this.value.sign(message));
}
@ -40,8 +40,8 @@ export class PrivateKey implements IPrivateKey {
return new PublicKey(this.value.getPublicKey());
}
toBytes(): Buffer {
return Buffer.from(this.value.serialize());
toBytes(): Uint8Array {
return this.value.serialize();
}
toHex(): string {

View File

@ -47,8 +47,8 @@ export class PublicKey implements IPublicKey {
return this.value.verify(signature.value, messageHash);
}
toBytes(): Buffer {
return Buffer.from(this.value.serialize());
toBytes(): Uint8Array {
return this.value.serialize();
}
toHex(): string {

View File

@ -64,8 +64,8 @@ export class Signature implements ISignature {
);
}
toBytes(): Buffer {
return Buffer.from(this.value.serialize());
toBytes(): Uint8Array {
return this.value.serialize();
}
toHex(): string {

View File

@ -3,17 +3,13 @@ import blsHerumi from "./herumi";
export type Implementation = "herumi" | "blst-native";
// This proxy makes sure a nice error is printed if BLS is used before init()
export let bls: IBls = new Proxy({} as IBls, {
get: function () {
throw Error("BLS not initialized, call init() before");
},
});
// TODO: Use a Proxy for example to throw an error if it's not initialized yet
export let bls: IBls;
async function getImplementation(impl: Implementation) {
switch (impl) {
case "herumi":
await blsHerumi.initBLS();
await blsHerumi.init();
return blsHerumi;
case "blst-native":

View File

@ -15,14 +15,14 @@ export interface IBls {
aggregate(signatures: ISignature[]): ISignature;
};
sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer;
aggregatePubkeys(publicKeys: Uint8Array[]): Buffer;
aggregateSignatures(signatures: Uint8Array[]): Buffer;
sign(secretKey: Uint8Array, messageHash: Uint8Array): Uint8Array;
aggregatePubkeys(publicKeys: Uint8Array[]): Uint8Array;
aggregateSignatures(signatures: Uint8Array[]): Uint8Array;
verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean;
verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean;
verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean;
initBLS(): Promise<void>;
init(): Promise<void>;
destroy(): void;
}
@ -32,12 +32,12 @@ export interface IKeypair {
}
export interface IPublicKey {
toBytes(): Buffer;
toBytes(): Uint8Array;
toHex(): string;
}
export interface ISignature {
toBytes(): Buffer;
toBytes(): Uint8Array;
toHex(): string;
verify(publicKey: IPublicKey, message: Uint8Array): boolean;
verifyAggregate(publicKeys: IPublicKey[], message: Uint8Array): boolean;
@ -46,7 +46,7 @@ export interface ISignature {
export interface IPrivateKey {
toPublicKey(): IPublicKey;
signMessage(message: Uint8Array): ISignature;
toBytes(): Buffer;
sign(message: Uint8Array): ISignature;
toBytes(): Uint8Array;
toHex(): string;
}

View File

@ -4,7 +4,7 @@ import {runForAllImplementations} from "../switch";
import {IPublicKey, ISignature} from "../../src/interface";
runForAllImplementations(async (bls, implementation) => {
await bls.initBLS();
await bls.init();
const aggCount = 30;
@ -17,7 +17,7 @@ runForAllImplementations(async (bls, implementation) => {
const msg = randomMsg();
const sk = bls.PrivateKey.fromKeygen();
const pk = sk.toPublicKey();
const sig = sk.signMessage(msg);
const sig = sk.sign(msg);
return {
input: {pk, msg, sig},
resultCheck: (valid) => valid === true,
@ -38,7 +38,7 @@ runForAllImplementations(async (bls, implementation) => {
const dataArr = range(aggCount).map(() => {
const sk = bls.PrivateKey.fromKeygen();
const pk = sk.toPublicKey();
const sig = sk.signMessage(msg);
const sig = sk.sign(msg);
return {pk, sig};
});
@ -79,7 +79,7 @@ runForAllImplementations(async (bls, implementation) => {
const msg = randomMsg();
const sigs = range(aggCount).map(() => {
const sk = bls.PrivateKey.fromKeygen();
return sk.signMessage(msg);
return sk.sign(msg);
});
return {
input: sigs,

View File

@ -27,7 +27,7 @@ export function describeForAllImplementations(callback: (bls: IBls) => void): vo
runForAllImplementations((bls, implementation) => {
describe(implementation, () => {
before(async () => {
await bls.initBLS();
await bls.init();
});
callback(bls);

View File

@ -8,7 +8,7 @@ export function runIndexTests(bls: IBls): void {
const sk = bls.PrivateKey.fromKeygen();
const pk = sk.toPublicKey();
const msg = randomMessage();
const sig = sk.signMessage(msg);
const sig = sk.sign(msg);
return {sk, pk, msg, sig};
}
@ -51,12 +51,7 @@ export function runIndexTests(bls: IBls): void {
const msgs = getN(2, () => randomMessage());
const pks = sks.map((sk) => sk.toPublicKey());
const sigs = [
sks[0].signMessage(msgs[0]),
sks[1].signMessage(msgs[0]),
sks[2].signMessage(msgs[1]),
sks[3].signMessage(msgs[1]),
];
const sigs = [sks[0].sign(msgs[0]), sks[1].sign(msgs[0]), sks[2].sign(msgs[1]), sks[3].sign(msgs[1])];
const aggPubkeys = [
bls.aggregatePubkeys([pks[0], pks[1]].map((pk) => pk.toBytes())),
@ -74,7 +69,7 @@ export function runIndexTests(bls: IBls): void {
const msg = randomMessage();
const sks = getN(n, () => bls.PrivateKey.fromKeygen());
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()));

View File

@ -11,7 +11,7 @@ describe("herumi", () => {
before(async () => {
// For consistency with describeForAllImplementations
// eslint-disable-next-line import/no-named-as-default-member
await herumi.initBLS();
await herumi.init();
});
runPrivateKeyTests(herumi);

View File

@ -1,7 +1,7 @@
import crypto from "crypto";
export function fromHexString(hex: string): Buffer {
return Buffer.from(hex.replace("0x", ""), "hex");
export function fromHexString(hex: string): Uint8Array {
return Uint8Array.from(Buffer.from(hex.replace("0x", ""), "hex"));
}
export function toHexString(bytes: Buffer | Uint8Array): string {