rewritten bls to use wasm

This commit is contained in:
Marin Petrunić 2019-11-27 21:58:41 +01:00
parent d5d8284be5
commit 2d0725a5cc
31 changed files with 351 additions and 963 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@chainsafe/bls", "name": "@chainsafe/bls",
"version": "0.1.7", "version": "0.2.0",
"description": "Implementation of bls signature verification for ethereum 2.0", "description": "Implementation of bls signature verification for ethereum 2.0",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -23,7 +23,7 @@
"build": "yarn build-lib && yarn build-types", "build": "yarn build-lib && yarn build-types",
"build:release": "yarn clean && yarn build && yarn build-web", "build:release": "yarn clean && yarn build && yarn build-web",
"build-lib": "babel src -x .ts -d lib", "build-lib": "babel src -x .ts -d lib",
"build-types": "tsc --declaration --incremental --outDir lib --emitDeclarationOnly", "build-types": "tsc --declaration --project tsconfig.build.json --incremental --outDir lib --emitDeclarationOnly",
"build-web": "webpack --mode production --entry ./lib/web.js --output ./dist/bls.min.js", "build-web": "webpack --mode production --entry ./lib/web.js --output ./dist/bls.min.js",
"check-types": "tsc --noEmit", "check-types": "tsc --noEmit",
"lint": "eslint --ext .ts src/", "lint": "eslint --ext .ts src/",

View File

@ -1,3 +0,0 @@
declare module "secure-random" {
export function randomBuffer(length: number): Buffer;
}

View File

@ -1,3 +0,0 @@
import CTX from "@chainsafe/milagro-crypto-js";
export default new CTX("BLS381");

View File

@ -1,14 +0,0 @@
import {G1point} from "./g1point";
import {G2point} from "./g2point";
import {FP12} from "@chainsafe/milagro-crypto-js/src/fp12";
import ctx from "../ctx";
export class ElipticCurvePairing {
public static pair(p1: G1point, p2: G2point): FP12 {
const e = ctx.PAIR.ate(p2.getPoint(), p1.getPoint());
return ctx.PAIR.fexp(e);
}
}

View File

@ -1,134 +0,0 @@
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
import {ECP} from "@chainsafe/milagro-crypto-js/src/ecp";
import ctx from "../ctx";
import assert from "assert";
import {calculateYFlag, getModulus} from "./utils";
import * as random from "secure-random";
import {FP_POINT_LENGTH} from "../constants";
import {bytes48} from "@chainsafe/eth2.0-types";
export class G1point {
private point: ECP;
public constructor(point: ECP) {
this.point = point;
}
public static fromBytesCompressed(value: bytes48): G1point {
assert(value.length === FP_POINT_LENGTH, `Expected g1 compressed input to have ${FP_POINT_LENGTH} bytes`);
value = Buffer.from(value);
const aIn = (value[0] & (1 << 5)) != 0;
const bIn = (value[0] & (1 << 6)) != 0;
const cIn = (value[0] & (1 << 7)) != 0;
value[0] &= 31;
if (!cIn) {
throw new Error("The serialised input does not have the C flag set.");
}
const x = ctx.BIG.frombytearray(value, 0);
if (bIn) {
if (!aIn && x.iszilch()) {
// This is a correctly formed serialisation of infinity
return new G1point(new ctx.ECP());
} else {
// The input is malformed
throw new Error(
"The serialised input has B flag set, but A flag is set, or X is non-zero.");
}
}
const modulus = getModulus();
if (ctx.BIG.comp(modulus, x) <= 0) {
throw new Error("X coordinate is too large.");
}
const point = new ctx.ECP();
point.setx(x);
if (point.is_infinity()) {
throw new Error("X coordinate is not on the curve.");
}
// Did we get the right branch of the sqrt?
if (!point.is_infinity() && aIn != calculateYFlag(point.getY())) {
// We didn't: so choose the other branch of the sqrt.
const x = new ctx.FP(point.getX());
const yneg = new ctx.FP(point.getY());
yneg.neg();
point.setxy(x.redc(), yneg.redc());
}
return new G1point(point);
}
public static aggregate(values: bytes48[]): G1point {
return values.map((value) => {
return G1point.fromBytesCompressed(value);
}).reduce((previousValue, currentValue): G1point => {
return previousValue.add(currentValue);
});
}
public static generator(): G1point {
return new G1point(ctx.ECP.generator());
}
public static random(): G1point {
let ecp: ECP;
do {
ecp = new ctx.ECP();
ecp.setx(
ctx.BIG.frombytearray(
random.randomBuffer(FP_POINT_LENGTH),
0
)
);
} while (ecp.is_infinity());
return new G1point(ecp);
}
public mul(value: BIG): G1point {
const newPoint = this.point.mul(value);
return new G1point(newPoint);
}
public add(other: G1point): G1point {
const sum = new ctx.ECP();
sum.add(this.point);
sum.add(other.point);
sum.affine();
return new G1point(sum);
}
public addRaw(other: bytes48): G1point {
return this.add(G1point.fromBytesCompressed(other));
}
public equal(other: G1point): boolean {
return this.point.equals(other.point);
}
public toBytes(): bytes48 {
const buffer = Buffer.alloc(FP_POINT_LENGTH, 0);
this.point.getX().tobytearray(buffer, 0);
return buffer;
}
public getPoint(): ECP {
return this.point;
}
public toBytesCompressed(): bytes48 {
const output = this.toBytes();
const c = true;
const b = this.point.is_infinity();
const a = !b && calculateYFlag(this.point.getY());
const flags = ((a ? 1 << 5 : 0) | (b ? 1 << 6 : 0) | (c ? 1 << 7 : 0));
const mask = 31;
output[0] &= mask;
output[0] |= flags;
return output;
}
}

View File

@ -1,250 +0,0 @@
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
import {ECP2} from "@chainsafe/milagro-crypto-js/src/ecp2";
import {sha256} from "js-sha256";
import ctx from "../ctx";
import * as random from "secure-random";
import {calculateYFlag, getModulus, padLeft} from "./utils";
import assert from "assert";
import {FP_POINT_LENGTH, G2_HASH_PADDING} from "../constants";
import {bytes48, Domain, Hash} from "@chainsafe/eth2.0-types";
export class G2point {
private point: ECP2;
public constructor(point: ECP2) {
this.point = point;
}
public static hashToG2(message: Hash, domain: Domain): G2point {
const padding = Buffer.alloc(G2_HASH_PADDING, 0);
const xReBytes = Buffer.concat([
padding,
Buffer.from(sha256.arrayBuffer(
Buffer.concat([
message,
padLeft(domain, 8),
Buffer.from("01", "hex")
])
))
]);
const xImBytes = Buffer.concat([
padding,
Buffer.from(sha256.arrayBuffer(
Buffer.concat([
message,
padLeft(domain, 8),
Buffer.from("02", "hex")
])
))
]);
const xRe = ctx.BIG.frombytearray(xReBytes, 0);
const xIm = ctx.BIG.frombytearray(xImBytes, 0);
const one = new ctx.BIG(1);
let point = new ctx.ECP2();
point.setx(new ctx.FP2(xRe, xIm));
while (point.is_infinity()) {
xRe.add(one);
xRe.norm();
point = new ctx.ECP2();
point.setx(new ctx.FP2(xRe, xIm));
}
return new G2point(G2point.scaleWithCofactor(G2point.normaliseY(point)));
}
public static fromCompressedBytes(value: bytes48): G2point {
assert(value.length === 2 * FP_POINT_LENGTH, "Expected signature of 96 bytes");
value = Buffer.from(value);
const xImBytes = value.slice(0, FP_POINT_LENGTH);
const xReBytes = value.slice(FP_POINT_LENGTH);
const aIn = (xImBytes[0] & (1 << 5)) != 0;
const bIn = (xImBytes[0] & (1 << 6)) != 0;
const cIn = (xImBytes[0] & (1 << 7)) != 0;
//clear bits
xImBytes[0] &= 31;
if((xReBytes[0] & 224) != 0) {
throw new Error("The input has non-zero a2, b2 or c2 flag on xRe");
}
if(!cIn) {
throw new Error("The serialised input does not have the C flag set.");
}
const xIm = ctx.BIG.frombytearray(xImBytes, 0);
const xRe = ctx.BIG.frombytearray(xReBytes, 0);
if (bIn) {
if (!aIn
&& xIm.iszilch()
&& xRe.iszilch() ) {
// This is a correctly formed serialisation of infinity
return new G2point(new ctx.ECP2());
} else {
// The input is malformed
throw new Error(
"The serialised input has B flag set, but A flag is set, or X is non-zero.");
}
}
const modulus = getModulus();
if(ctx.BIG.comp(modulus, xRe) <= 0 || ctx.BIG.comp(modulus, xIm) <= 0) {
throw new Error(
"The deserialised X real or imaginary coordinate is too large.");
}
const point = new ctx.ECP2();
point.setx(new ctx.FP2(xRe, xIm));
if(point.is_infinity()) {
throw new Error("X coordinate is not on the curve.");
}
if (!point.is_infinity() && aIn != calculateYFlag(point.getY().getB())) {
// We didn't: so choose the other branch of the sqrt.
const x = point.getX();
const yneg = point.getY();
yneg.neg();
point.setxy(x, yneg);
}
return new G2point(point);
}
public static fromUncompressedInput(
xReBytes: Buffer,
xImBytes: Buffer,
yReBytes: Buffer,
yImBytes: Buffer,
zReBytes: Buffer,
zImBytes: Buffer): G2point {
const xRe = ctx.BIG.frombytearray(padLeft(xReBytes, FP_POINT_LENGTH), 0);
const xIm = ctx.BIG.frombytearray(padLeft(xImBytes, FP_POINT_LENGTH), 0);
const yRe = ctx.BIG.frombytearray(padLeft(yReBytes, FP_POINT_LENGTH), 0);
const yIm = ctx.BIG.frombytearray(padLeft(yImBytes, FP_POINT_LENGTH), 0);
const zRe = ctx.BIG.frombytearray(padLeft(zReBytes, FP_POINT_LENGTH), 0);
const zIm = ctx.BIG.frombytearray(padLeft(zImBytes, FP_POINT_LENGTH), 0);
const x = new ctx.FP2(xRe, xIm);
const y = new ctx.FP2(yRe, yIm);
const z = new ctx.FP2(zRe, zIm);
z.inverse();
x.mul(z);
x.reduce();
y.mul(z);
y.reduce();
const point = new ctx.ECP2();
point.setxy(x, y);
return new G2point(point);
}
public static random(): G2point {
let point: ECP2;
do {
point = new ctx.ECP2();
point.setx(
new ctx.FP2(
ctx.BIG.frombytearray(
random.randomBuffer(FP_POINT_LENGTH),
0
),
ctx.BIG.frombytearray(
random.randomBuffer(FP_POINT_LENGTH),
0
)
)
);
} while (point.is_infinity());
return new G2point(point);
}
public static scaleWithCofactor(point: ECP2): ECP2 {
const upper = ctx.BIG.frombytearray(
Buffer.from(
"0000000000000000000000000000000005d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddf",
"hex"
),
0
);
const lower = ctx.BIG.frombytearray(
Buffer.from(
"00000000000000000000000000000000a628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5",
"hex"
),
0
);
const shift = ctx.BIG.frombytearray(
Buffer.from(
"000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000",
"hex"
),
0
);
let sum = new ctx.ECP2();
sum.copy(point);
sum = sum.mul(upper);
sum = sum.mul(shift);
let tmp = new ctx.ECP2();
tmp.copy(point);
tmp = tmp.mul(lower);
sum.add(tmp);
return sum;
}
public static normaliseY(point: ECP2): ECP2 {
const y = point.getY();
const yNeg = new ctx.FP2(y);
yNeg.neg();
if (ctx.BIG.comp(y.getB(), yNeg.getB()) < 0
|| ((ctx.BIG.comp(y.getB(), yNeg.getB()) == 0)
&& ctx.BIG.comp(y.getA(), yNeg.getA()) < 0)
) {
const newPoint = new ctx.ECP2();
newPoint.setxy(point.getX(), yNeg);
return newPoint;
} else {
return point;
}
}
public add(other: G2point): G2point {
const sum = new ctx.ECP2();
sum.add(this.point);
sum.add(other.point);
sum.affine();
return new G2point(sum);
}
public mul(value: BIG): G2point {
const newPoint = this.point.mul(value);
return new G2point(newPoint);
}
public equal(other: G2point): boolean {
return this.point.equals(other.point);
}
public getPoint(): ECP2 {
return this.point;
}
public toBytesCompressed(): Buffer {
const xReBytes = Buffer.alloc(FP_POINT_LENGTH, 0);
const xImBytes = Buffer.alloc(FP_POINT_LENGTH, 0);
this.point.getX().getA().tobytearray(xReBytes, 0);
this.point.getX().getB().tobytearray(xImBytes, 0);
const c1 = true;
const b1 = this.point.is_infinity();
const a1 = !b1 && calculateYFlag(this.point.getY().getB());
const flags = ((a1 ? 1 << 5 : 0) | (b1 ? 1 << 6 : 0) | (c1 ? 1 << 7 : 0));
const mask = 31;
xImBytes[0] &= mask;
xImBytes[0] |= flags;
xReBytes[0] &= mask;
return Buffer.concat([
xImBytes,
xReBytes
]);
}
}

View File

@ -1,6 +1,4 @@
import assert from "assert"; import assert from "assert";
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
import ctx from "../ctx";
/** /**
* Pads byte array with zeroes on left side up to desired length. * Pads byte array with zeroes on left side up to desired length.
@ -14,21 +12,3 @@ export function padLeft(source: Buffer, length: number): Buffer {
source.copy(result, length - source.length); source.copy(result, length - source.length);
return result; return result;
} }
//TODO: find a way to convert ctx.ROM_FIELD.MODULUS to BIG (MODULUS basebit = 58, BIG basebit=23
export function getModulus(): BIG {
return ctx.BIG.frombytearray(
Buffer.from(
"1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab",
"hex"
),
0
);
}
export function calculateYFlag(yIm: BIG): boolean {
const tmp = new ctx.BIG(yIm);
tmp.add(yIm);
tmp.div(getModulus());
return tmp.isunity();
}

View File

@ -1,15 +1,17 @@
import {Keypair} from "./keypair"; import {Keypair} from "./keypair";
import {PrivateKey} from "./privateKey"; import {PrivateKey} from "./privateKey";
import {G2point} from "./helpers/g2point";
import {G1point} from "./helpers/g1point";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {Signature} from "./signature"; import {Signature} from "./signature";
import {ElipticCurvePairing} from "./helpers/ec-pairing";
import ctx from "./ctx";
import {BLSPubkey, BLSSecretKey, BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types"; import {BLSPubkey, BLSSecretKey, BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types";
import {init} from "./context";
import {PUBLIC_KEY_LENGTH} from "./constants";
export {Keypair, PrivateKey, PublicKey, Signature}; export {Keypair, PrivateKey, PublicKey, Signature};
export async function initLibrary(): Promise<void> {
await init();
}
/** /**
* Generates new secret and public key * Generates new secret and public key
*/ */
@ -34,8 +36,7 @@ export function generatePublicKey(secretKey: BLSSecretKey): BLSPubkey {
*/ */
export function sign(secretKey: BLSSecretKey, messageHash: Hash, domain: Domain): BLSSignature { export function sign(secretKey: BLSSecretKey, messageHash: Hash, domain: Domain): BLSSignature {
const privateKey = PrivateKey.fromBytes(secretKey); const privateKey = PrivateKey.fromBytes(secretKey);
const hash = G2point.hashToG2(messageHash, domain); return privateKey.signMessage(messageHash, domain).toBytesCompressed();
return privateKey.sign(hash).toBytesCompressed();
} }
/** /**
@ -56,9 +57,16 @@ export function aggregateSignatures(signatures: BLSSignature[]): BLSSignature {
*/ */
export function aggregatePubkeys(publicKeys: BLSPubkey[]): BLSPubkey { export function aggregatePubkeys(publicKeys: BLSPubkey[]): BLSPubkey {
if(publicKeys.length === 0) { if(publicKeys.length === 0) {
return new G1point(new ctx.ECP()).toBytesCompressed(); return Buffer.alloc(PUBLIC_KEY_LENGTH);
} }
return G1point.aggregate(publicKeys).toBytesCompressed(); return publicKeys.map(PublicKey.fromBytes).reduce((agg, pubKey) => {
if(agg) {
return agg.add(pubKey);
} else {
return pubKey;
}
}
).toBytesCompressed();
} }
/** /**
@ -70,15 +78,9 @@ export function aggregatePubkeys(publicKeys: BLSPubkey[]): BLSPubkey {
*/ */
export function verify(publicKey: BLSPubkey, messageHash: Hash, signature: BLSSignature, domain: Domain): boolean { export function verify(publicKey: BLSPubkey, messageHash: Hash, signature: BLSSignature, domain: Domain): boolean {
try { try {
const key = PublicKey.fromBytes(publicKey); return PublicKey
const sig = Signature.fromCompressedBytes(signature); .fromBytes(publicKey)
.verifyMessage(Signature.fromCompressedBytes(signature), messageHash, domain);
key.getPoint().getPoint().affine();
sig.getPoint().getPoint().affine();
const g1Generated = G1point.generator();
const e1 = ElipticCurvePairing.pair(key.getPoint(), G2point.hashToG2(messageHash, domain));
const e2 = ElipticCurvePairing.pair(g1Generated, sig.getPoint());
return e1.equals(e2);
} catch (e) { } catch (e) {
return false; return false;
} }
@ -101,54 +103,13 @@ export function verifyMultiple(
return false; return false;
} }
try { try {
const sig = Signature.fromCompressedBytes(signature).getPoint(); return Signature
sig.getPoint().affine(); .fromCompressedBytes(signature)
.verifyMultiple(
const eCombined = new ctx.FP12(1); publicKeys.map((key) => PublicKey.fromBytes(key)),
messageHashes,
// @ts-ignore domain
const reduction = messageHashes.reduce((previous, current, index) => { );
// @ts-ignore
if(previous.hash && current.equals(previous.hash)) {
return {
hash: previous.hash,
// @ts-ignore
publicKey: previous.publicKey ?
// @ts-ignore
previous.publicKey.addRaw(publicKeys[index])
:
G1point.fromBytesCompressed(publicKeys[index]),
};
} else if(previous.hash) {
// @ts-ignore
const g2 = G2point.hashToG2(previous.hash, domain);
eCombined.mul(
ElipticCurvePairing.pair(
// @ts-ignore
previous.publicKey,
g2
)
);
return {hash: current, publicKey: G1point.fromBytesCompressed(publicKeys[index])};
} else {
return {
hash: current,
publicKey: G1point.fromBytesCompressed(publicKeys[index])
};
}
}, {hash: null, publicKey: null});
const g2Final = G2point.hashToG2(reduction.hash, domain);
const keyFinal = reduction.publicKey;
eCombined.mul(
ElipticCurvePairing.pair(
keyFinal,
g2Final
)
);
const e2 = ElipticCurvePairing.pair(G1point.generator(), sig);
return e2.equals(eCombined);
} catch (e) { } catch (e) {
return false; return false;
} }

View File

@ -4,9 +4,9 @@ import {PrivateKey} from "./privateKey";
export class Keypair { export class Keypair {
private _publicKey: PublicKey; private readonly _publicKey: PublicKey;
private _privateKey: PrivateKey; private readonly _privateKey: PrivateKey;
public constructor(privateKey: PrivateKey, publicKey?: PublicKey) { public constructor(privateKey: PrivateKey, publicKey?: PublicKey) {
this._privateKey = privateKey; this._privateKey = privateKey;

View File

@ -1,8 +1,11 @@
import {SECRET_KEY_LENGTH} from "./constants"; import {SECRET_KEY_LENGTH} from "./constants";
import assert from "assert"; import assert from "assert";
import {BLSSecretKey, Domain, Hash} from "@chainsafe/eth2.0-types"; import {BLSSecretKey, Domain, Hash} from "@chainsafe/eth2.0-types";
import {SecretKeyType, SignatureType} from "@chainsafe/eth2-bls-wasm"; import {SecretKeyType} from "@chainsafe/eth2-bls-wasm";
import {getContext} from "./context"; import {getContext} from "./context";
import {PublicKey} from "./publicKey";
import {Signature} from "./signature";
import {padLeft} from "./helpers/utils";
export class PrivateKey { export class PrivateKey {
@ -38,12 +41,17 @@ export class PrivateKey {
return this.value; return this.value;
} }
public sign(message: Uint8Array): SignatureType { // public sign(message: Uint8Array): Signature {
return this.value.sign(message); // return Signature.fromValue(this.value.sign(message));
// }
public signMessage(message: Hash, domain: Domain): Signature {
domain = padLeft(domain, 8);
return Signature.fromValue(this.value.signHashWithDomain(Buffer.concat([message, domain])));
} }
public signMessage(message: Hash, domain: Domain): SignatureType { public toPublicKey(): PublicKey {
return this.value.signHashWithDomain(Buffer.concat([message, domain])); return PublicKey.fromPublicKeyType(this.value.getPublicKey());
} }
public toBytes(): BLSSecretKey { public toBytes(): BLSSecretKey {

View File

@ -1,36 +1,64 @@
import {G1point} from "./helpers/g1point";
import {PrivateKey} from "./privateKey"; import {PrivateKey} from "./privateKey";
import {BLSPubkey} from "@chainsafe/eth2.0-types"; import {BLSPubkey, BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types";
import {PublicKeyType} from "@chainsafe/eth2-bls-wasm";
import {getContext} from "./context";
import {PUBLIC_KEY_LENGTH} from "./constants";
import assert from "assert";
import {Signature} from "./signature";
export class PublicKey { export class PublicKey {
private point: G1point; private value: PublicKeyType;
public constructor(point: G1point) { protected constructor(value: PublicKeyType) {
this.point = point; this.value = value;
} }
public static fromPrivateKey(privateKey: PrivateKey): PublicKey { public static fromPrivateKey(privateKey: PrivateKey): PublicKey {
return privateKey.toPublicKey();
}
public static fromBytes(bytes: BLSPubkey): PublicKey {
const context = getContext();
const publicKey = new context.PublicKey();
publicKey.deserialize(bytes);
return new PublicKey( return new PublicKey(
G1point.generator().mul(privateKey.getValue()) publicKey
); );
} }
public static fromBytes(publicKey: BLSPubkey): PublicKey { public static fromHex(value: string): PublicKey {
value = value.replace("0x", "");
assert(value.length === PUBLIC_KEY_LENGTH * 2);
const context = getContext();
return new PublicKey( return new PublicKey(
G1point.fromBytesCompressed(publicKey) context.deserializeHexStrToPublicKey(value)
); );
} }
public getPoint(): G1point { public static fromPublicKeyType(value: PublicKeyType): PublicKey {
return this.point; return new PublicKey(value);
}
public add(other: PublicKey): PublicKey {
const agg = new PublicKey(this.value.clone());
agg.value.add(other.value);
return agg;
}
public verifyMessage(signature: Signature, messageHash: Hash, domain: Domain): boolean {
return this.value.verifyHashWithDomain(signature.getValue(), Buffer.concat([messageHash, domain]));
} }
public toBytesCompressed(): BLSPubkey { public toBytesCompressed(): BLSPubkey {
return this.point.toBytesCompressed(); return Buffer.from(this.value.serialize());
} }
public toHexString(): string { public toHexString(): string {
return `0x${this.toBytesCompressed().toString("hex")}`; return `0x${this.toBytesCompressed().toString("hex")}`;
} }
public getValue(): PublicKeyType {
return this.value;
}
} }

View File

@ -1,35 +1,64 @@
import {G2point} from "./helpers/g2point";
import assert from "assert"; import assert from "assert";
import {FP_POINT_LENGTH} from "./constants"; import {FP_POINT_LENGTH} from "./constants";
import {BLSSignature} from "@chainsafe/eth2.0-types"; import {BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types";
import {SignatureType} from "@chainsafe/eth2-bls-wasm";
import {getContext} from "./context";
import {PublicKey} from "./publicKey";
import {padLeft} from "./helpers/utils";
export class Signature { export class Signature {
private point: G2point; private value: SignatureType;
public constructor(point: G2point) { protected constructor(value: SignatureType) {
this.point = point; this.value = value;
} }
public static fromCompressedBytes(signature: BLSSignature): Signature { public static fromCompressedBytes(value: BLSSignature): Signature {
assert( assert(
signature.length === 2 * FP_POINT_LENGTH, value.length === 2 * FP_POINT_LENGTH,
`Signature must have ${2 * FP_POINT_LENGTH} bytes` `Signature must have ${2 * FP_POINT_LENGTH} bytes`
); );
return new Signature(G2point.fromCompressedBytes(signature)); const context = getContext();
const signature = new context.Signature();
signature.deserialize(value);
return new Signature(signature);
}
public static fromValue(signature: SignatureType): Signature {
return new Signature(signature);
} }
public add(other: Signature): Signature { public add(other: Signature): Signature {
const agg = this.value.clone();
agg.add(other.value);
return new Signature( return new Signature(
this.point.add(other.point) agg
); );
} }
public getPoint(): G2point { public getValue(): SignatureType {
return this.point; return this.value;
}
public verify(publicKey: PublicKey, message: Hash, domain: Domain): boolean {
domain = padLeft(domain, 8);
return publicKey.verifyMessage(this, message, domain);
}
public verifyMultiple(publicKeys: PublicKey[], messages: Hash[], domain: Domain): boolean {
domain = padLeft(domain, 8);
return this.value.verifyAggregatedHashWithDomain(
publicKeys.map((key) => key.getValue()),
messages.map((message) => Buffer.concat([message, domain]))
);
} }
public toBytesCompressed(): BLSSignature { public toBytesCompressed(): BLSSignature {
return this.point.toBytesCompressed(); return Buffer.from(this.value.serialize());
}
public toHex(): string {
return "0x" + this.value.serializeToHexStr();
} }
} }

View File

@ -1,11 +1,13 @@
// Import benchmarks // Import benchmarks
import * as suites from "./suites"; import * as suites from "./suites";
import {createReportDir, runSuite} from "@chainsafe/benchmark-utils"; import {createReportDir, runSuite} from "@chainsafe/benchmark-utils";
import {initLibrary} from "../../src";
// Create file // Create file
const directory: string = createReportDir(); const directory: string = createReportDir();
initLibrary().then(() => {
// Run benchmarks // Run benchmarks
Object.values(suites).forEach((suite) => { Object.values(suites).forEach((suite) => {
runSuite(suite(directory)); runSuite(suite(directory));
});
}); });

View File

@ -1,5 +1,5 @@
// export {verifyInValidSignatureBenchmark} from './verifyInValidSignature'; export {verifyInValidSignatureBenchmark} from './verifyInValidSignature';
// export {verifyValidSignatureBenchmark} from './verifyValidSignature'; export {verifyValidSignatureBenchmark} from './verifyValidSignature';
export {verifyValidAggregatedSignature} from './verifyValidAggregatedSignature'; export {verifyValidAggregatedSignature} from './verifyValidAggregatedSignature';
// export {verifyInvalidAggregatedSignature} from './verifyInvalidAggregatedSignature'; export {verifyInvalidAggregatedSignature} from './verifyInvalidAggregatedSignature';
// export {aggregateSignaturesBenchmark} from './signatureAggregation'; export {aggregateSignaturesBenchmark} from './signatureAggregation';

View File

@ -25,6 +25,7 @@ export function aggregateSignaturesBenchmark(dir: string): BenchSuite {
}; };
return { return {
name: FUNCTION_NAME,
testFunctions: [aggregateSignatures], testFunctions: [aggregateSignatures],
setup: function() { setup: function() {
global.signatures = []; global.signatures = [];

View File

@ -27,6 +27,7 @@ export function verifyInValidSignatureBenchmark(dir: string): BenchSuite {
}; };
return { return {
name: FUNCTION_NAME,
testFunctions: [verifyInValidSignature], testFunctions: [verifyInValidSignature],
setup: function() { setup: function() {
const {Keypair} = require("../../../src"); const {Keypair} = require("../../../src");

View File

@ -27,6 +27,7 @@ export function verifyInvalidAggregatedSignature(dir: string): BenchSuite {
}; };
return { return {
name: FUNCTION_NAME,
testFunctions: [verifyInvalidAggregatedSignature], testFunctions: [verifyInvalidAggregatedSignature],
setup: function() { setup: function() {
const {Keypair, aggregateSignatures} = require("../../../src"); const {Keypair, aggregateSignatures} = require("../../../src");

View File

@ -32,13 +32,13 @@ export function verifyValidAggregatedSignature(dir: string): BenchSuite {
const FUNCTION_NAME = "verifyValidAggregatedSignature"; // PLEASE FILL THIS OUT const FUNCTION_NAME = "verifyValidAggregatedSignature"; // PLEASE FILL THIS OUT
const verifyValidAggregatedSignature = function (): void { const verifyValidAggregatedSignature = function (): void {
global.verify(global.publicKeys, global.messages, global.signature, global.domain) global.verify(global.publicKeys, global.messages, global.signature, global.domain);
}; };
return { return {
testFunctions: [verifyValidAggregatedSignature], testFunctions: [verifyValidAggregatedSignature],
setup: function() { setup: function() {
const sha256 = require('js-sha256'); const sha256 = require("js-sha256");
const {aggregateSignatures} = require("../../../src"); const {aggregateSignatures} = require("../../../src");
const message = Buffer.from(sha256.arrayBuffer(Math.random().toString(36))); const message = Buffer.from(sha256.arrayBuffer(Math.random().toString(36)));
const signatures = []; const signatures = [];

View File

@ -29,6 +29,7 @@ export function verifyValidSignatureBenchmark(dir: string): BenchSuite {
}; };
return { return {
name: FUNCTION_NAME,
testFunctions: [verifyValidSignature], testFunctions: [verifyValidSignature],
setup: function() { setup: function() {
const {Keypair} = require("../../../src"); const {Keypair} = require("../../../src");

View File

@ -1,4 +1,4 @@
import bls from "../../src"; import bls, {initLibrary} from "../../src";
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single"; import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
import path from "path"; import path from "path";
@ -9,6 +9,10 @@ interface IAggregatePubKeysTestCase {
}; };
} }
before(async function f() {
await initLibrary();
});
describeDirectorySpecTest<IAggregatePubKeysTestCase, string>( describeDirectorySpecTest<IAggregatePubKeysTestCase, string>(
"aggregate pubkeys", "aggregate pubkeys",
path.join( path.join(

View File

@ -1,5 +1,5 @@
import path from "path"; import path from "path";
import bls from "../../src"; import bls, {initLibrary} from "../../src";
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single"; import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
interface AggregateSigsTestCase { interface AggregateSigsTestCase {
@ -9,6 +9,10 @@ interface AggregateSigsTestCase {
}; };
} }
before(async function f() {
await initLibrary();
});
describeDirectorySpecTest<AggregateSigsTestCase, string>( describeDirectorySpecTest<AggregateSigsTestCase, string>(
"aggregate sigs", "aggregate sigs",
path.join( path.join(

View File

@ -1,38 +1,36 @@
import path from "path"; // import path from "path";
import {padLeft} from "../../src/helpers/utils"; // import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
import {G2point} from "../../src/helpers/g2point"; //
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single"; // interface IMsgHHashCOmpressed {
// data: {
interface IMsgHHashCOmpressed { // input: {
data: { // message: string;
input: { // domain: string;
message: string; // };
domain: string; // output: string[];
}; // };
output: string[]; // }
}; //
} // describeDirectorySpecTest<IMsgHHashCOmpressed, string>(
// "msg_hash_compressed",
describeDirectorySpecTest<IMsgHHashCOmpressed, string>( // path.join(
"msg_hash_compressed", // __dirname,
path.join( // "../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_compressed/small"
__dirname, // ),
"../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_compressed/small" // (testCase => {
), // const domain = Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex");
(testCase => { // const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex");
const domain = padLeft(Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex"), 8); // const result = G2point.hashToG2(input, domain);
const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex"); // return `0x${result.toBytesCompressed().toString("hex")}`;
const result = G2point.hashToG2(input, domain); // }),
return `0x${result.toBytesCompressed().toString("hex")}`; // {
}), // inputTypes: {
{ // data: InputType.YAML,
inputTypes: { // },
data: InputType.YAML, // getExpected: (testCase => {
}, // const xReExpected = padLeft(Buffer.from(testCase.data.output[0].replace("0x", ""), "hex"), 48);
getExpected: (testCase => { // const xImExpected = padLeft(Buffer.from(testCase.data.output[1].replace("0x", ""), "hex"), 48);
const xReExpected = padLeft(Buffer.from(testCase.data.output[0].replace("0x", ""), "hex"), 48); // return "0x" + Buffer.concat([xReExpected, xImExpected]).toString("hex");
const xImExpected = padLeft(Buffer.from(testCase.data.output[1].replace("0x", ""), "hex"), 48); // })
return "0x" + Buffer.concat([xReExpected, xImExpected]).toString("hex"); // }
}) // );
}
);

View File

@ -1,43 +1,43 @@
import path from "path"; // import path from "path";
import {padLeft} from "../../src/helpers/utils"; // import {padLeft} from "../../src/helpers/utils";
import {G2point} from "../../src/helpers/g2point"; // import {G2point} from "../../src/helpers/g2point";
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single"; // import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
//
interface IMsgHHashUnCompressed { // interface IMsgHHashUnCompressed {
data: { // data: {
input: { // input: {
message: string; // message: string;
domain: string; // domain: string;
}; // };
output: string[][]; // output: string[][];
}; // };
} // }
//
describeDirectorySpecTest<IMsgHHashUnCompressed, string>( // describeDirectorySpecTest<IMsgHHashUnCompressed, string>(
"msg_hash_uncompressed", // "msg_hash_uncompressed",
path.join( // path.join(
__dirname, // __dirname,
"../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_uncompressed/small" // "../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_uncompressed/small"
), // ),
(testCase => { // (testCase => {
const domain = padLeft(Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex"), 8); // const domain = padLeft(Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex"), 8);
const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex"); // const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex");
const result = G2point.hashToG2(input, domain); // const result = G2point.hashToG2(input, domain);
return `0x${result.toBytesCompressed().toString("hex")}`; // return `0x${result.toBytesCompressed().toString("hex")}`;
}), // }),
{ // {
inputTypes: { // inputTypes: {
data: InputType.YAML, // data: InputType.YAML,
}, // },
getExpected: (testCase => { // getExpected: (testCase => {
return "0x" + G2point.fromUncompressedInput( // return "0x" + G2point.fromUncompressedInput(
Buffer.from(testCase.data.output[0][0].replace("0x", ""), "hex"), // Buffer.from(testCase.data.output[0][0].replace("0x", ""), "hex"),
Buffer.from(testCase.data.output[0][1].replace("0x", ""), "hex"), // Buffer.from(testCase.data.output[0][1].replace("0x", ""), "hex"),
Buffer.from(testCase.data.output[1][0].replace("0x", ""), "hex"), // Buffer.from(testCase.data.output[1][0].replace("0x", ""), "hex"),
Buffer.from(testCase.data.output[1][1].replace("0x", ""), "hex"), // Buffer.from(testCase.data.output[1][1].replace("0x", ""), "hex"),
Buffer.from(testCase.data.output[2][0].replace("0x", ""), "hex"), // Buffer.from(testCase.data.output[2][0].replace("0x", ""), "hex"),
Buffer.from(testCase.data.output[2][1].replace("0x", ""), "hex"), // Buffer.from(testCase.data.output[2][1].replace("0x", ""), "hex"),
).toBytesCompressed().toString("hex"); // ).toBytesCompressed().toString("hex");
}) // })
} // }
); // );

View File

@ -1,4 +1,4 @@
import bls from "../../src"; import bls, {initLibrary} from "../../src";
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single"; import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
import path from "path"; import path from "path";
@ -9,6 +9,10 @@ interface IPrivToPubTestCase {
}; };
} }
before(async function f() {
await initLibrary();
});
describeDirectorySpecTest<IPrivToPubTestCase, string>( describeDirectorySpecTest<IPrivToPubTestCase, string>(
"priv_to_pub", "priv_to_pub",
path.join( path.join(

View File

@ -1,5 +1,5 @@
import path from "path"; import path from "path";
import bls from "../../src"; import bls, {initLibrary} from "../../src";
import {padLeft} from "../../src/helpers/utils"; import {padLeft} from "../../src/helpers/utils";
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single"; import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
@ -14,6 +14,10 @@ interface ISignMessageTestCase {
}; };
} }
before(async function f() {
await initLibrary();
});
describeDirectorySpecTest<ISignMessageTestCase, string>( describeDirectorySpecTest<ISignMessageTestCase, string>(
"priv_to_pub", "priv_to_pub",
path.join( path.join(

View File

@ -1,109 +0,0 @@
import {G1point} from "../../../src/helpers/g1point";
import {expect} from "chai";
describe('g1point', function() {
it('should generate different random point', () => {
const g1 = G1point.random();
const g2 = G1point.random();
expect(g1.equal(g2)).to.be.false;
});
it('should be same', () => {
const g1 = G1point.random();
expect(g1.equal(g1)).to.be.true;
});
it('serialize adn deserialize should produce same result', () => {
const g1 = G1point.random();
const g2 = G1point.fromBytesCompressed(g1.toBytesCompressed());
expect(g1.equal(g2)).to.be.true;
});
it('deserialize correct point doesn not throw', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
'hex'
)
)
}).to.not.throw;
});
it('deserialize incorrect point throws', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde0',
'hex'
)
)
}).to.throw('X coordinate is not on the curve.');
});
it('deserialize incorrect point throws 2', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab',
'hex'
)
)
}).to.throw('X coordinate is too large.');
});
it('deserialize incorrect point throws 3', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaac',
'hex'
)
)
}).to.throw('X coordinate is too large.');
});
it('deserialize incorrect point throws to few bytes', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa',
'hex'
)
)
}).to.throw('Expected g1 compressed input to have 48 bytes');
});
it('deserialize incorrect point throws to many bytes', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa900',
'hex'
)
)
}).to.throw('Expected g1 compressed input to have 48 bytes');
});
it('deserialize infinity', () => {
const g1 = G1point.fromBytesCompressed(
Buffer.from(
'c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
'hex'
)
);
expect(g1.getPoint().is_infinity()).to.be.true
});
it('wrong infinity serialization', () => {
expect(() => {
G1point.fromBytesCompressed(
Buffer.from(
'e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
'hex'
)
)
}).to.throw('The serialised input has B flag set, but A flag is set, or X is non-zero.');
});
});

View File

@ -1,153 +0,0 @@
import {G2point} from "../../../src/helpers/g2point";
import {expect} from "chai";
describe('g2point', function() {
it('should be equals', () => {
const g2 = G2point.random();
expect(g2.equal(g2)).to.be.true;
});
it('should not be equals', () => {
const g2 = G2point.random();
const g22 = G2point.random();
expect(g2.equal(g22)).to.be.false;
});
it('serialize deserialize should be equal', () => {
const g2 = G2point.random();
expect(G2point.fromCompressedBytes(g2.toBytesCompressed()).equal(g2)).to.be.true;
});
it('should deserialize from compress', () => {
const x =
"8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.not.throw;
});
it('should fail to deserialize', () => {
const x =
"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
});
it('should fail to deserialize 2', () => {
const x =
"9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
});
it('should fail to deserialize 3', () => {
const x =
"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaac";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
});
it('should fail to deserialize 4', () => {
const x =
"9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaac"
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
});
it('should fail to deserialize 5', () => {
const x =
"8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde0";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('X coordinate is not on the curve.');
});
it('should fail to deserialize infinity', () => {
const x =
"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('X coordinate is not on the curve.');
});
it('should fail to deserialize - too few bytes', () => {
const x = "8123456789abcd";
expect(() => {
G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
)
}).to.throw('Expected signature of 96 bytes');
});
it('should fail to deserialize - too many bytes', () => {
expect(() => {
G2point.fromCompressedBytes(
Buffer.alloc(100, 1),
)
}).to.throw('Expected signature of 96 bytes');
});
it('should deserialize infinity', () => {
const x =
"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
const g2 = G2point.fromCompressedBytes(
Buffer.from(
x,
'hex'
)
);
expect(g2.getPoint().is_infinity()).to.be.true;
})
});

View File

@ -1,27 +0,0 @@
import {expect} from "chai";
import {padLeft} from "../../../src/helpers/utils";
import {G1point} from "../../../src/helpers/g1point";
describe('helpers tests', function() {
describe('padLeft', function() {
it('throw if source larger than target', () => {
expect(
() => padLeft(Buffer.alloc(2, 0), 1)
).to.throw;
});
it('pad one 0 on left side', () => {
const result = padLeft(
Buffer.alloc(1, 1),
2
);
expect(result.length).to.be.equal(2);
expect(result[0]).to.be.equal(0);
expect(result[1]).to.be.equal(1);
});
});
});

View File

@ -1,11 +1,19 @@
import bls from "../../src"; import bls, {aggregatePubkeys, aggregateSignatures, initLibrary, Keypair, verify, verifyMultiple} from "../../src";
import {Keypair} from "../../src/keypair";
import {sha256} from "js-sha256"; import {sha256} from "js-sha256";
import {G2point} from "../../src/helpers/g2point";
import {expect} from "chai"; import {expect} from "chai";
import {destroy} from "../../src/context";
import {padLeft} from "../../lib/helpers/utils";
describe("test bls", function () { describe("test bls", function () {
before(async function() {
await initLibrary();
});
after(function () {
destroy();
});
describe("aggregate pubkey", function () { describe("aggregate pubkey", function () {
it("should aggregate empty array", function () { it("should aggregate empty array", function () {
expect(bls.aggregatePubkeys([])).to.not.throw; expect(bls.aggregatePubkeys([])).to.not.throw;
@ -17,10 +25,11 @@ describe("test bls", function () {
const keypair = Keypair.generate(); const keypair = Keypair.generate();
const messageHash = Buffer.from(sha256.arrayBuffer("Test")); const messageHash = Buffer.from(sha256.arrayBuffer("Test"));
const domain = Buffer.alloc(8, 1); const domain = Buffer.alloc(8, 1);
const signature = keypair.privateKey.sign( const signature = keypair.privateKey.signMessage(
G2point.hashToG2(messageHash, domain) messageHash,
domain
); );
const result = bls.verify( const result = verify(
keypair.publicKey.toBytesCompressed(), keypair.publicKey.toBytesCompressed(),
messageHash, messageHash,
signature.toBytesCompressed(), signature.toBytesCompressed(),
@ -34,11 +43,12 @@ describe("test bls", function () {
const keypair = Keypair.generate(); const keypair = Keypair.generate();
const messageHash = Buffer.from(sha256.arrayBuffer("Test")); const messageHash = Buffer.from(sha256.arrayBuffer("Test"));
const domain = Buffer.alloc(8, 1); const domain = Buffer.alloc(8, 1);
const signature = keypair.privateKey.sign( const signature = keypair.privateKey.signMessage(
G2point.hashToG2(messageHash, domain) messageHash,
domain
); );
const pubKey = keypair.publicKey.toBytesCompressed(); const pubKey = keypair.publicKey.toBytesCompressed();
bls.verify( verify(
pubKey, pubKey,
messageHash, messageHash,
signature.toBytesCompressed(), signature.toBytesCompressed(),
@ -53,7 +63,7 @@ describe("test bls", function () {
const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2")); const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2"));
const domain = Buffer.from("01", "hex"); const domain = Buffer.from("01", "hex");
const signature = Buffer.alloc(96); const signature = Buffer.alloc(96);
const result = bls.verify( const result = verify(
keypair.publicKey.toBytesCompressed(), keypair.publicKey.toBytesCompressed(),
messageHash2, messageHash2,
signature, signature,
@ -66,11 +76,12 @@ describe("test bls", function () {
const keypair = Keypair.generate(); const keypair = Keypair.generate();
const messageHash = Buffer.from(sha256.arrayBuffer("Test message")); const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2")); const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2"));
const domain = Buffer.from("01", "hex"); const domain = padLeft(Buffer.from("01", "hex"), 8);
const signature = keypair.privateKey.sign( const signature = keypair.privateKey.signMessage(
G2point.hashToG2(messageHash, domain) messageHash,
domain
); );
const result = bls.verify( const result = verify(
keypair.publicKey.toBytesCompressed(), keypair.publicKey.toBytesCompressed(),
messageHash2, messageHash2,
signature.toBytesCompressed(), signature.toBytesCompressed(),
@ -82,12 +93,13 @@ describe("test bls", function () {
it("should fail verify signature of different domain", () => { it("should fail verify signature of different domain", () => {
const keypair = Keypair.generate(); const keypair = Keypair.generate();
const messageHash = Buffer.from(sha256.arrayBuffer("Test message")); const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
const domain = Buffer.from("01", "hex"); const domain = padLeft(Buffer.from("01", "hex"), 8);
const domain2 = Buffer.from("02", "hex"); const domain2 = padLeft(Buffer.from("02", "hex"), 8);
const signature = keypair.privateKey.sign( const signature = keypair.privateKey.signMessage(
G2point.hashToG2(messageHash, domain) messageHash,
domain
); );
const result = bls.verify( const result = verify(
keypair.publicKey.toBytesCompressed(), keypair.publicKey.toBytesCompressed(),
messageHash, messageHash,
signature.toBytesCompressed(), signature.toBytesCompressed(),
@ -101,10 +113,11 @@ describe("test bls", function () {
const keypair2 = Keypair.generate(); const keypair2 = Keypair.generate();
const messageHash = Buffer.from(sha256.arrayBuffer("Test message")); const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
const domain = Buffer.from("01", "hex"); const domain = Buffer.from("01", "hex");
const signature = keypair.privateKey.sign( const signature = keypair.privateKey.signMessage(
G2point.hashToG2(messageHash, domain) messageHash,
domain
); );
const result = bls.verify( const result = verify(
keypair2.publicKey.toBytesCompressed(), keypair2.publicKey.toBytesCompressed(),
messageHash, messageHash,
signature.toBytesCompressed(), signature.toBytesCompressed(),
@ -127,32 +140,32 @@ describe("test bls", function () {
const keypair3 = Keypair.generate(); const keypair3 = Keypair.generate();
const keypair4 = Keypair.generate(); const keypair4 = Keypair.generate();
const message1 = Buffer.from("Test1", "utf-8"); const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
const message2 = Buffer.from("Test2", "utf-8"); const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
const signature1 = keypair1.privateKey.signMessage(message1, domain); const signature1 = keypair1.privateKey.signMessage(message1, domain);
const signature2 = keypair2.privateKey.signMessage(message1, domain); const signature2 = keypair2.privateKey.signMessage(message1, domain);
const signature3 = keypair3.privateKey.signMessage(message2, domain); const signature3 = keypair3.privateKey.signMessage(message2, domain);
const signature4 = keypair4.privateKey.signMessage(message2, domain); const signature4 = keypair4.privateKey.signMessage(message2, domain);
const aggregatePubKey12 = bls.aggregatePubkeys([ const aggregatePubKey12 = aggregatePubkeys([
keypair1.publicKey.toBytesCompressed(), keypair1.publicKey.toBytesCompressed(),
keypair2.publicKey.toBytesCompressed(), keypair2.publicKey.toBytesCompressed(),
]); ]);
const aggregatePubKey34 = bls.aggregatePubkeys([ const aggregatePubKey34 = aggregatePubkeys([
keypair3.publicKey.toBytesCompressed(), keypair3.publicKey.toBytesCompressed(),
keypair4.publicKey.toBytesCompressed(), keypair4.publicKey.toBytesCompressed(),
]); ]);
const aggregateSignature = bls.aggregateSignatures([ const aggregateSignature = aggregateSignatures([
signature1.toBytesCompressed(), signature1.toBytesCompressed(),
signature2.toBytesCompressed(), signature2.toBytesCompressed(),
signature3.toBytesCompressed(), signature3.toBytesCompressed(),
signature4.toBytesCompressed(), signature4.toBytesCompressed(),
]); ]);
const result = bls.verifyMultiple( const result = verifyMultiple(
[aggregatePubKey12, aggregatePubKey34], [aggregatePubKey12, aggregatePubKey34],
[message1, message2], [message1, message2],
aggregateSignature, aggregateSignature,
@ -173,21 +186,21 @@ describe("test bls", function () {
const keypair3 = Keypair.generate(); const keypair3 = Keypair.generate();
const keypair4 = Keypair.generate(); const keypair4 = Keypair.generate();
const message = Buffer.from("Test1", "utf-8"); const message = Buffer.from(sha256.arrayBuffer("Test1"));
const signature1 = keypair1.privateKey.signMessage(message, domain); const signature1 = keypair1.privateKey.signMessage(message, domain);
const signature2 = keypair2.privateKey.signMessage(message, domain); const signature2 = keypair2.privateKey.signMessage(message, domain);
const signature3 = keypair3.privateKey.signMessage(message, domain); const signature3 = keypair3.privateKey.signMessage(message, domain);
const signature4 = keypair4.privateKey.signMessage(message, domain); const signature4 = keypair4.privateKey.signMessage(message, domain);
const aggregateSignature = bls.aggregateSignatures([ const aggregateSignature = aggregateSignatures([
signature1.toBytesCompressed(), signature1.toBytesCompressed(),
signature2.toBytesCompressed(), signature2.toBytesCompressed(),
signature3.toBytesCompressed(), signature3.toBytesCompressed(),
signature4.toBytesCompressed(), signature4.toBytesCompressed(),
]); ]);
const result = bls.verifyMultiple( const result = verifyMultiple(
[ [
keypair1.publicKey.toBytesCompressed(), keypair1.publicKey.toBytesCompressed(),
keypair2.publicKey.toBytesCompressed(), keypair2.publicKey.toBytesCompressed(),
@ -212,8 +225,8 @@ describe("test bls", function () {
const keypair3 = Keypair.generate(); const keypair3 = Keypair.generate();
const keypair4 = Keypair.generate(); const keypair4 = Keypair.generate();
const message1 = Buffer.from("Test1", "utf-8"); const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
const message2 = Buffer.from("Test2", "utf-8"); const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
const signature1 = keypair1.privateKey.signMessage(message1, domain); const signature1 = keypair1.privateKey.signMessage(message1, domain);
const signature2 = keypair2.privateKey.signMessage(message1, domain); const signature2 = keypair2.privateKey.signMessage(message1, domain);
@ -256,8 +269,8 @@ describe("test bls", function () {
const keypair3 = Keypair.generate(); const keypair3 = Keypair.generate();
const keypair4 = Keypair.generate(); const keypair4 = Keypair.generate();
const message1 = Buffer.from("Test1", "utf-8"); const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
const message2 = Buffer.from("Test2", "utf-8"); const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
const signature1 = keypair1.privateKey.signMessage(message1, domain); const signature1 = keypair1.privateKey.signMessage(message1, domain);
const signature2 = keypair2.privateKey.signMessage(message1, domain); const signature2 = keypair2.privateKey.signMessage(message1, domain);
@ -297,8 +310,8 @@ describe("test bls", function () {
const keypair3 = Keypair.generate(); const keypair3 = Keypair.generate();
const keypair4 = Keypair.generate(); const keypair4 = Keypair.generate();
const message1 = Buffer.from("Test1", "utf-8"); const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
const message2 = Buffer.from("Test2", "utf-8"); const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
const signature1 = keypair1.privateKey.signMessage(message1, domain); const signature1 = keypair1.privateKey.signMessage(message1, domain);
const signature2 = keypair2.privateKey.signMessage(message1, domain); const signature2 = keypair2.privateKey.signMessage(message1, domain);
@ -335,8 +348,8 @@ describe("test bls", function () {
const signature = Buffer.alloc(96); const signature = Buffer.alloc(96);
const message1 = Buffer.from("Test1", "utf-8"); const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
const message2 = Buffer.from("Test2", "utf-8"); const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
const result = bls.verifyMultiple( const result = bls.verifyMultiple(
[], [],

View File

@ -2,24 +2,33 @@ import {PrivateKey} from "../../src/privateKey";
import {PublicKey} from "../../src/publicKey"; import {PublicKey} from "../../src/publicKey";
import {Keypair} from "../../src/keypair"; import {Keypair} from "../../src/keypair";
import {expect} from "chai"; import {expect} from "chai";
import {destroy, init} from "../../src/context";
describe('keypair', function() { describe("keypair", function() {
it('should create from private and public key', () => { before(async function () {
const secret = PrivateKey.random(); await init();
const secret2 = PrivateKey.random(); });
const publicKey = PublicKey.fromBytes(PublicKey.fromPrivateKey(secret2).toBytesCompressed());
const keypair = new Keypair(secret, publicKey);
expect(keypair.publicKey).to.be.equal(publicKey);
expect(keypair.privateKey).to.be.equal(secret);
expect(keypair.privateKey).to.not.be.equal(secret2);
});
it('should create from private', () => { after(function () {
const secret = PrivateKey.random(); destroy();
const publicKey = PublicKey.fromPrivateKey(secret); });
const keypair = new Keypair(secret);
expect(keypair.publicKey.toBytesCompressed().toString('hex')) it("should create from private and public key", () => {
.to.be.equal(publicKey.toBytesCompressed().toString('hex')); const secret = PrivateKey.random();
}) const secret2 = PrivateKey.random();
const publicKey = PublicKey.fromBytes(PublicKey.fromPrivateKey(secret2).toBytesCompressed());
const keypair = new Keypair(secret, publicKey);
expect(keypair.publicKey).to.be.equal(publicKey);
expect(keypair.privateKey).to.be.equal(secret);
expect(keypair.privateKey).to.not.be.equal(secret2);
});
it("should create from private", () => {
const secret = PrivateKey.random();
const publicKey = PublicKey.fromPrivateKey(secret);
const keypair = new Keypair(secret);
expect(keypair.publicKey.toBytesCompressed().toString("hex"))
.to.be.equal(publicKey.toBytesCompressed().toString("hex"));
});
}); });

View File

@ -0,0 +1,33 @@
import {destroy, init} from "../../src/context";
import {PublicKey} from "../../src/publicKey";
import {expect} from "chai";
import { PrivateKey } from "../../src/privateKey";
describe("public key", function () {
before(async function f() {
await init();
});
after(function () {
destroy();
});
it("from hex", function () {
const publicKey =
"0xb6f21199594b56d77670564bf422cb331d5281ca2c1f9a45588a56881d8287ef8619efa6456d6cd2ef61306aa5b21311";
expect(PublicKey.fromHex(publicKey).toHexString()).to.be.equal(publicKey);
});
it("from bytes", function () {
const publicKey =
"b6f21199594b56d77670564bf422cb331d5281ca2c1f9a45588a56881d8287ef8619efa6456d6cd2ef61306aa5b21311";
expect(PublicKey.fromBytes(Buffer.from(publicKey, "hex")).toHexString()).to.be.equal(`0x${publicKey}`);
});
it("from private key", function () {
PublicKey.fromPrivateKey(PrivateKey.random());
});
});