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 ## Usage
```ts ```ts
import {PrivateKey} from "@chainsafe/bls"; import bls, {init} from "@chainsafe/bls";
const secretKey = PrivateKey.fromKeygen(); (async () => {
const publicKey = secretKey.toPublicKey(); await init("herumi");
const message = new Uint8Array(32);
const signature = secretKey.sign(message); const secretKey = bls.PrivateKey.fromKeygen();
console.log("Is valid: ", signature.verify(publicKey, message)); const publicKey = secretKey.toPublicKey();
const message = new Uint8Array(32);
const signature = secretKey.sign(message);
console.log("Is valid: ", signature.verify(publicKey, message));
})();
``` ```
### Browser ### Browser

View File

@ -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,
}; };

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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));

View File

@ -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();
} }

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 // 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();
} }

View File

@ -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,
}; };

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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":

View File

@ -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;
} }

View File

@ -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,

View File

@ -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);

View File

@ -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()));

View File

@ -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);

View File

@ -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 {