Merge branch 'master' into interop
This commit is contained in:
commit
5c1b1c22d0
|
@ -24,7 +24,7 @@
|
||||||
"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/",
|
||||||
"lint-fix": "eslint --ext .ts src/ --fix",
|
"lint:fix": "eslint --ext .ts src/ --fix",
|
||||||
"pretest": "yarn check-types",
|
"pretest": "yarn check-types",
|
||||||
"prepublishOnly": "yarn build",
|
"prepublishOnly": "yarn build",
|
||||||
"test:unit": "nyc --cache-dir .nyc_output/.cache -r lcov -e .ts mocha --colors -r ./.babel-register 'test/unit/**/*.test.ts' && nyc report",
|
"test:unit": "nyc --cache-dir .nyc_output/.cache -r lcov -e .ts mocha --colors -r ./.babel-register 'test/unit/**/*.test.ts' && nyc report",
|
||||||
|
|
|
@ -5,7 +5,7 @@ import assert from "assert";
|
||||||
import {calculateYFlag, getModulus} from "./utils";
|
import {calculateYFlag, getModulus} from "./utils";
|
||||||
import * as random from "secure-random";
|
import * as random from "secure-random";
|
||||||
import {FP_POINT_LENGTH} from "../constants";
|
import {FP_POINT_LENGTH} from "../constants";
|
||||||
import {BLSPubkey, bytes48} from "@chainsafe/eth2.0-types";
|
import {bytes48} from "@chainsafe/eth2.0-types";
|
||||||
|
|
||||||
export class G1point {
|
export class G1point {
|
||||||
|
|
||||||
|
@ -15,6 +15,79 @@ export class G1point {
|
||||||
this.point = point;
|
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 {
|
public mul(value: BIG): G1point {
|
||||||
const newPoint = this.point.mul(value);
|
const newPoint = this.point.mul(value);
|
||||||
return new G1point(newPoint);
|
return new G1point(newPoint);
|
||||||
|
@ -58,77 +131,4 @@ export class G1point {
|
||||||
output[0] |= flags;
|
output[0] |= flags;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
|
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
|
||||||
import {ECP2} from "@chainsafe/milagro-crypto-js/src/ecp2";
|
import {ECP2} from "@chainsafe/milagro-crypto-js/src/ecp2";
|
||||||
import {sha256} from 'js-sha256';
|
import {sha256} from "js-sha256";
|
||||||
import ctx from "../ctx";
|
import ctx from "../ctx";
|
||||||
import * as random from "secure-random";
|
import * as random from "secure-random";
|
||||||
import {calculateYFlag, getModulus, padLeft} from "./utils";
|
import {calculateYFlag, getModulus, padLeft} from "./utils";
|
||||||
|
@ -16,47 +16,6 @@ export class G2point {
|
||||||
this.point = point;
|
this.point = 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(): bytes48 {
|
|
||||||
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
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static hashToG2(message: Hash, domain: Domain): G2point {
|
public static hashToG2(message: Hash, domain: Domain): G2point {
|
||||||
const padding = Buffer.alloc(G2_HASH_PADDING, 0);
|
const padding = Buffer.alloc(G2_HASH_PADDING, 0);
|
||||||
|
@ -66,7 +25,7 @@ export class G2point {
|
||||||
Buffer.concat([
|
Buffer.concat([
|
||||||
message,
|
message,
|
||||||
padLeft(domain, 8),
|
padLeft(domain, 8),
|
||||||
Buffer.from('01', 'hex')
|
Buffer.from("01", "hex")
|
||||||
])
|
])
|
||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
|
@ -76,7 +35,7 @@ export class G2point {
|
||||||
Buffer.concat([
|
Buffer.concat([
|
||||||
message,
|
message,
|
||||||
padLeft(domain, 8),
|
padLeft(domain, 8),
|
||||||
Buffer.from('02', 'hex')
|
Buffer.from("02", "hex")
|
||||||
])
|
])
|
||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
|
@ -95,7 +54,7 @@ export class G2point {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromCompressedBytes(value: bytes48): G2point {
|
public static fromCompressedBytes(value: bytes48): G2point {
|
||||||
assert(value.length === 2 * FP_POINT_LENGTH, 'Expected signature of 96 bytes');
|
assert(value.length === 2 * FP_POINT_LENGTH, "Expected signature of 96 bytes");
|
||||||
value = Buffer.from(value);
|
value = Buffer.from(value);
|
||||||
const xImBytes = value.slice(0, FP_POINT_LENGTH);
|
const xImBytes = value.slice(0, FP_POINT_LENGTH);
|
||||||
const xReBytes = value.slice(FP_POINT_LENGTH);
|
const xReBytes = value.slice(FP_POINT_LENGTH);
|
||||||
|
@ -114,8 +73,8 @@ export class G2point {
|
||||||
const xRe = ctx.BIG.frombytearray(xReBytes, 0);
|
const xRe = ctx.BIG.frombytearray(xReBytes, 0);
|
||||||
if (bIn) {
|
if (bIn) {
|
||||||
if (!aIn
|
if (!aIn
|
||||||
&& xIm.iszilch()
|
&& xIm.iszilch()
|
||||||
&& xRe.iszilch() ) {
|
&& xRe.iszilch() ) {
|
||||||
// This is a correctly formed serialisation of infinity
|
// This is a correctly formed serialisation of infinity
|
||||||
return new G2point(new ctx.ECP2());
|
return new G2point(new ctx.ECP2());
|
||||||
} else {
|
} else {
|
||||||
|
@ -131,7 +90,7 @@ export class G2point {
|
||||||
"The deserialised X real or imaginary coordinate is too large.");
|
"The deserialised X real or imaginary coordinate is too large.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = new ctx.ECP2();
|
const point = new ctx.ECP2();
|
||||||
point.setx(new ctx.FP2(xRe, xIm));
|
point.setx(new ctx.FP2(xRe, xIm));
|
||||||
if(point.is_infinity()) {
|
if(point.is_infinity()) {
|
||||||
throw new Error("X coordinate is not on the curve.");
|
throw new Error("X coordinate is not on the curve.");
|
||||||
|
@ -235,8 +194,8 @@ export class G2point {
|
||||||
const yNeg = new ctx.FP2(y);
|
const yNeg = new ctx.FP2(y);
|
||||||
yNeg.neg();
|
yNeg.neg();
|
||||||
if (ctx.BIG.comp(y.getB(), yNeg.getB()) < 0
|
if (ctx.BIG.comp(y.getB(), yNeg.getB()) < 0
|
||||||
|| ((ctx.BIG.comp(y.getB(), yNeg.getB()) == 0)
|
|| ((ctx.BIG.comp(y.getB(), yNeg.getB()) == 0)
|
||||||
&& ctx.BIG.comp(y.getA(), yNeg.getA()) < 0)
|
&& ctx.BIG.comp(y.getA(), yNeg.getA()) < 0)
|
||||||
) {
|
) {
|
||||||
const newPoint = new ctx.ECP2();
|
const newPoint = new ctx.ECP2();
|
||||||
newPoint.setxy(point.getX(), yNeg);
|
newPoint.setxy(point.getX(), yNeg);
|
||||||
|
@ -245,4 +204,47 @@ export class G2point {
|
||||||
return point;
|
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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import ctx from "../ctx";
|
||||||
* @param length
|
* @param length
|
||||||
*/
|
*/
|
||||||
export function padLeft(source: Buffer, length: number): Buffer {
|
export function padLeft(source: Buffer, length: number): Buffer {
|
||||||
assert(source.length <= length, 'Given array must be smaller or equal to desired array size');
|
assert(source.length <= length, "Given array must be smaller or equal to desired array size");
|
||||||
const result = Buffer.alloc(length, 0);
|
const result = Buffer.alloc(length, 0);
|
||||||
source.copy(result, length - source.length);
|
source.copy(result, length - source.length);
|
||||||
return result;
|
return result;
|
||||||
|
@ -19,8 +19,8 @@ export function padLeft(source: Buffer, length: number): Buffer {
|
||||||
export function getModulus(): BIG {
|
export function getModulus(): BIG {
|
||||||
return ctx.BIG.frombytearray(
|
return ctx.BIG.frombytearray(
|
||||||
Buffer.from(
|
Buffer.from(
|
||||||
'1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab',
|
"1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab",
|
||||||
'hex'
|
"hex"
|
||||||
),
|
),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
15
src/index.ts
15
src/index.ts
|
@ -91,7 +91,12 @@ export function verify(publicKey: BLSPubkey, messageHash: Hash, signature: BLSSi
|
||||||
* @param signature
|
* @param signature
|
||||||
* @param domain
|
* @param domain
|
||||||
*/
|
*/
|
||||||
export function verifyMultiple(publicKeys: BLSPubkey[], messageHashes: Hash[], signature: BLSSignature, domain: Domain): boolean {
|
export function verifyMultiple(
|
||||||
|
publicKeys: BLSPubkey[],
|
||||||
|
messageHashes: Hash[],
|
||||||
|
signature: BLSSignature,
|
||||||
|
domain: Domain
|
||||||
|
): boolean {
|
||||||
if(publicKeys.length === 0 || publicKeys.length != messageHashes.length) {
|
if(publicKeys.length === 0 || publicKeys.length != messageHashes.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -101,19 +106,25 @@ export function verifyMultiple(publicKeys: BLSPubkey[], messageHashes: Hash[], s
|
||||||
|
|
||||||
const eCombined = new ctx.FP12(1);
|
const eCombined = new ctx.FP12(1);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const reduction = messageHashes.reduce((previous, current, index) => {
|
const reduction = messageHashes.reduce((previous, current, index) => {
|
||||||
|
// @ts-ignore
|
||||||
if(previous.hash && current.equals(previous.hash)) {
|
if(previous.hash && current.equals(previous.hash)) {
|
||||||
return {
|
return {
|
||||||
hash: previous.hash,
|
hash: previous.hash,
|
||||||
|
// @ts-ignore
|
||||||
publicKey: previous.publicKey ?
|
publicKey: previous.publicKey ?
|
||||||
|
// @ts-ignore
|
||||||
previous.publicKey.addRaw(publicKeys[index])
|
previous.publicKey.addRaw(publicKeys[index])
|
||||||
:
|
:
|
||||||
G1point.fromBytesCompressed(publicKeys[index]),
|
G1point.fromBytesCompressed(publicKeys[index]),
|
||||||
};
|
};
|
||||||
} else if(!!previous.hash) {
|
} else if(previous.hash) {
|
||||||
|
// @ts-ignore
|
||||||
const g2 = G2point.hashToG2(previous.hash, domain);
|
const g2 = G2point.hashToG2(previous.hash, domain);
|
||||||
eCombined.mul(
|
eCombined.mul(
|
||||||
ElipticCurvePairing.pair(
|
ElipticCurvePairing.pair(
|
||||||
|
// @ts-ignore
|
||||||
previous.publicKey,
|
previous.publicKey,
|
||||||
g2
|
g2
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,6 +15,30 @@ export class PrivateKey {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static fromBytes(bytes: Uint8Array): PrivateKey {
|
||||||
|
assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes");
|
||||||
|
const value = Buffer.from(bytes);
|
||||||
|
return new PrivateKey(
|
||||||
|
ctx.BIG.frombytearray(
|
||||||
|
padLeft(
|
||||||
|
value,
|
||||||
|
48
|
||||||
|
),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromHexString(value: string): PrivateKey {
|
||||||
|
return PrivateKey.fromBytes(
|
||||||
|
Buffer.from(value.replace("0x", ""), "hex")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static random(): PrivateKey {
|
||||||
|
return PrivateKey.fromBytes(random.randomBuffer(SECRET_KEY_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
public getValue(): BIG {
|
public getValue(): BIG {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
@ -34,31 +58,6 @@ export class PrivateKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
public toHexString(): string {
|
public toHexString(): string {
|
||||||
return `0x${this.toBytes().toString('hex')}`;
|
return `0x${this.toBytes().toString("hex")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromBytes(bytes: Uint8Array): PrivateKey {
|
|
||||||
assert(bytes.length === SECRET_KEY_LENGTH, 'Private key should have 32 bytes');
|
|
||||||
const value = Buffer.from(bytes);
|
|
||||||
return new PrivateKey(
|
|
||||||
ctx.BIG.frombytearray(
|
|
||||||
padLeft(
|
|
||||||
value,
|
|
||||||
48
|
|
||||||
),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromHexString(value: string): PrivateKey {
|
|
||||||
return PrivateKey.fromBytes(
|
|
||||||
Buffer.from(value.replace('0x', ''), 'hex')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static random(): PrivateKey {
|
|
||||||
return PrivateKey.fromBytes(random.randomBuffer(SECRET_KEY_LENGTH));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,19 +9,7 @@ export class PublicKey {
|
||||||
public constructor(point: G1point) {
|
public constructor(point: G1point) {
|
||||||
this.point = point;
|
this.point = point;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPoint(): G1point {
|
|
||||||
return this.point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toBytesCompressed(): BLSPubkey {
|
|
||||||
return this.point.toBytesCompressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public toHexString(): string {
|
|
||||||
return `0x${this.toBytesCompressed().toString('hex')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static fromPrivateKey(privateKey: PrivateKey): PublicKey {
|
public static fromPrivateKey(privateKey: PrivateKey): PublicKey {
|
||||||
return new PublicKey(
|
return new PublicKey(
|
||||||
G1point.generator().mul(privateKey.getValue())
|
G1point.generator().mul(privateKey.getValue())
|
||||||
|
@ -33,4 +21,16 @@ export class PublicKey {
|
||||||
G1point.fromBytesCompressed(publicKey)
|
G1point.fromBytesCompressed(publicKey)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getPoint(): G1point {
|
||||||
|
return this.point;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toBytesCompressed(): BLSPubkey {
|
||||||
|
return this.point.toBytesCompressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public toHexString(): string {
|
||||||
|
return `0x${this.toBytesCompressed().toString("hex")}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,14 @@ export class Signature {
|
||||||
this.point = point;
|
this.point = point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static fromCompressedBytes(signature: BLSSignature): Signature {
|
||||||
|
assert(
|
||||||
|
signature.length === 2 * FP_POINT_LENGTH,
|
||||||
|
`Signature must have ${2 * FP_POINT_LENGTH} bytes`
|
||||||
|
);
|
||||||
|
return new Signature(G2point.fromCompressedBytes(signature));
|
||||||
|
}
|
||||||
|
|
||||||
public add(other: Signature): Signature {
|
public add(other: Signature): Signature {
|
||||||
return new Signature(
|
return new Signature(
|
||||||
this.point.add(other.point)
|
this.point.add(other.point)
|
||||||
|
@ -24,12 +32,4 @@ export class Signature {
|
||||||
public toBytesCompressed(): BLSSignature {
|
public toBytesCompressed(): BLSSignature {
|
||||||
return this.point.toBytesCompressed();
|
return this.point.toBytesCompressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromCompressedBytes(signature: BLSSignature): Signature {
|
|
||||||
assert(
|
|
||||||
signature.length === 2 * FP_POINT_LENGTH,
|
|
||||||
`Signature must have ${2 * FP_POINT_LENGTH} bytes`
|
|
||||||
);
|
|
||||||
return new Signature(G2point.fromCompressedBytes(signature));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import bls from "./index";
|
import bls from "./index";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
// @ts-ignore
|
|
||||||
(function (window: any) {
|
(function (window: any) {
|
||||||
window.bls = bls;
|
window.bls = bls;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import bls from "../../src";
|
import bls from "../../src";
|
||||||
import {Keypair} from "../../src/keypair";
|
import {Keypair} from "../../src/keypair";
|
||||||
import {sha256} from 'js-sha256';
|
import {sha256} from "js-sha256";
|
||||||
import {G2point} from "../../src/helpers/g2point";
|
import {G2point} from "../../src/helpers/g2point";
|
||||||
import {expect} from "chai";
|
import {expect} from "chai";
|
||||||
|
import {describe, it} from "mocha";
|
||||||
|
|
||||||
describe('test bls', function () {
|
describe("test bls", function () {
|
||||||
|
|
||||||
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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verify', function() {
|
describe("verify", function() {
|
||||||
it('should verify signature', () => {
|
it("should verify signature", () => {
|
||||||
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);
|
||||||
|
@ -30,7 +31,7 @@ describe('test bls', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not modify original pubkey when verifying', () => {
|
it("should not modify original pubkey when verifying", () => {
|
||||||
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);
|
||||||
|
@ -44,14 +45,14 @@ describe('test bls', function () {
|
||||||
signature.toBytesCompressed(),
|
signature.toBytesCompressed(),
|
||||||
domain
|
domain
|
||||||
);
|
);
|
||||||
expect('0x' + pubKey.toString('hex')).to.be.equal(keypair.publicKey.toHexString());
|
expect("0x" + pubKey.toString("hex")).to.be.equal(keypair.publicKey.toHexString());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fail verify empty signature', () => {
|
it("should fail verify empty signature", () => {
|
||||||
const keypair = Keypair.generate();
|
const keypair = Keypair.generate();
|
||||||
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 = bls.verify(
|
||||||
keypair.publicKey.toBytesCompressed(),
|
keypair.publicKey.toBytesCompressed(),
|
||||||
|
@ -62,11 +63,11 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.false;
|
expect(result).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail verify signature of different message', () => {
|
it("should fail verify signature of different message", () => {
|
||||||
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 = Buffer.from("01", "hex");
|
||||||
const signature = keypair.privateKey.sign(
|
const signature = keypair.privateKey.sign(
|
||||||
G2point.hashToG2(messageHash, domain)
|
G2point.hashToG2(messageHash, domain)
|
||||||
);
|
);
|
||||||
|
@ -79,11 +80,11 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.false;
|
expect(result).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
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 = Buffer.from("01", "hex");
|
||||||
const domain2 = Buffer.from("02", 'hex');
|
const domain2 = Buffer.from("02", "hex");
|
||||||
const signature = keypair.privateKey.sign(
|
const signature = keypair.privateKey.sign(
|
||||||
G2point.hashToG2(messageHash, domain)
|
G2point.hashToG2(messageHash, domain)
|
||||||
);
|
);
|
||||||
|
@ -96,11 +97,11 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.false;
|
expect(result).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail verify signature signed by different key', () => {
|
it("should fail verify signature signed by different key", () => {
|
||||||
const keypair = Keypair.generate();
|
const keypair = Keypair.generate();
|
||||||
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.sign(
|
||||||
G2point.hashToG2(messageHash, domain)
|
G2point.hashToG2(messageHash, domain)
|
||||||
);
|
);
|
||||||
|
@ -114,9 +115,9 @@ describe('test bls', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verify multiple', function() {
|
describe("verify multiple", function() {
|
||||||
|
|
||||||
it('should verify aggregated signatures', function () {
|
it("should verify aggregated signatures", function () {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,8 +128,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("Test1", "utf-8");
|
||||||
const message2 = Buffer.from("Test2", 'utf-8');
|
const message2 = Buffer.from("Test2", "utf-8");
|
||||||
|
|
||||||
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);
|
||||||
|
@ -162,7 +163,7 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.true;
|
expect(result).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should verify aggregated signatures - same message', function () {
|
it("should verify aggregated signatures - same message", function () {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,7 +174,7 @@ 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("Test1", "utf-8");
|
||||||
|
|
||||||
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);
|
||||||
|
@ -202,7 +203,7 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.true;
|
expect(result).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail to verify aggregated signatures - swapped messages', function () {
|
it("should fail to verify aggregated signatures - swapped messages", function () {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
|
|
||||||
const domain = Buffer.alloc(8, 0);
|
const domain = Buffer.alloc(8, 0);
|
||||||
|
@ -212,8 +213,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("Test1", "utf-8");
|
||||||
const message2 = Buffer.from("Test2", 'utf-8');
|
const message2 = Buffer.from("Test2", "utf-8");
|
||||||
|
|
||||||
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);
|
||||||
|
@ -247,7 +248,7 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.false;
|
expect(result).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail to verify aggregated signatures - different pubkeys and messsages', () => {
|
it("should fail to verify aggregated signatures - different pubkeys and messsages", () => {
|
||||||
|
|
||||||
const domain = Buffer.alloc(8, 0);
|
const domain = Buffer.alloc(8, 0);
|
||||||
|
|
||||||
|
@ -256,8 +257,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("Test1", "utf-8");
|
||||||
const message2 = Buffer.from("Test2", 'utf-8');
|
const message2 = Buffer.from("Test2", "utf-8");
|
||||||
|
|
||||||
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);
|
||||||
|
@ -287,7 +288,7 @@ describe('test bls', function () {
|
||||||
expect(result).to.be.false;
|
expect(result).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail to verify aggregated signatures - different domain', () => {
|
it("should fail to verify aggregated signatures - different domain", () => {
|
||||||
|
|
||||||
const domain = Buffer.alloc(8, 0);
|
const domain = Buffer.alloc(8, 0);
|
||||||
const domain2 = Buffer.alloc(8, 1);
|
const domain2 = Buffer.alloc(8, 1);
|
||||||
|
@ -297,8 +298,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("Test1", "utf-8");
|
||||||
const message2 = Buffer.from("Test2", 'utf-8');
|
const message2 = Buffer.from("Test2", "utf-8");
|
||||||
|
|
||||||
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);
|
||||||
|
@ -329,14 +330,14 @@ describe('test bls', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fail to verify aggregated signatures - no public keys', () => {
|
it("should fail to verify aggregated signatures - no public keys", () => {
|
||||||
|
|
||||||
const domain = Buffer.alloc(8, 0);
|
const domain = Buffer.alloc(8, 0);
|
||||||
|
|
||||||
const signature = Buffer.alloc(96);
|
const signature = Buffer.alloc(96);
|
||||||
|
|
||||||
const message1 = Buffer.from("Test1", 'utf-8');
|
const message1 = Buffer.from("Test1", "utf-8");
|
||||||
const message2 = Buffer.from("Test2", 'utf-8');
|
const message2 = Buffer.from("Test2", "utf-8");
|
||||||
|
|
||||||
const result = bls.verifyMultiple(
|
const result = bls.verifyMultiple(
|
||||||
[],
|
[],
|
||||||
|
|
Reference in New Issue