bls wasm context and private key converted to wasm implementation

This commit is contained in:
Marin Petrunić 2019-11-27 19:21:37 +01:00
parent 654db6864d
commit d5d8284be5
7 changed files with 109 additions and 46 deletions

View File

@ -41,6 +41,7 @@
"@chainsafe/eth2.0-types": "^0.1.0", "@chainsafe/eth2.0-types": "^0.1.0",
"@chainsafe/milagro-crypto-js": "0.1.3", "@chainsafe/milagro-crypto-js": "0.1.3",
"assert": "^1.4.1", "assert": "^1.4.1",
"bls-wasm": "^0.2.7",
"js-sha256": "^0.9.0", "js-sha256": "^0.9.0",
"secure-random": "^1.1.1" "secure-random": "^1.1.1"
}, },

21
src/context.ts Normal file
View File

@ -0,0 +1,21 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import blsWasmWrapper from "@chainsafe/eth2-bls-wasm";
let blsWrapper: typeof blsWasmWrapper | null = null;
export async function init(): Promise<typeof blsWasmWrapper> {
await blsWasmWrapper.init();
blsWrapper = blsWasmWrapper;
return blsWrapper;
}
export function destroy(): void {
blsWrapper = null;
}
export function getContext(): typeof blsWasmWrapper{
if(blsWrapper) {
return blsWrapper;
}
throw new Error("BLS not initialized");
}

View File

@ -1,63 +1,56 @@
import {BIG} from "@chainsafe/milagro-crypto-js/src/big"; import {SECRET_KEY_LENGTH} from "./constants";
import {FP_POINT_LENGTH, SECRET_KEY_LENGTH} from "./constants";
import assert from "assert"; import assert from "assert";
import ctx from "./ctx"; import {BLSSecretKey, Domain, Hash} from "@chainsafe/eth2.0-types";
import {padLeft} from "./helpers/utils"; import {SecretKeyType, SignatureType} from "@chainsafe/eth2-bls-wasm";
import {G2point} from "./helpers/g2point"; import {getContext} from "./context";
import * as random from "secure-random";
import {BLSSecretKey, Hash, Domain} from "@chainsafe/eth2.0-types";
export class PrivateKey { export class PrivateKey {
private value: BIG; private value: SecretKeyType;
public constructor(value: BIG) { protected constructor(value: SecretKeyType) {
this.value = value; this.value = value;
} }
public static fromBytes(bytes: Uint8Array): PrivateKey { public static fromBytes(bytes: Uint8Array): PrivateKey {
assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes"); assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes");
const value = Buffer.from(bytes); const context = getContext();
return new PrivateKey( const secretKey = new context.SecretKey();
ctx.BIG.frombytearray( secretKey.deserialize(Buffer.from(bytes));
padLeft( return new PrivateKey(secretKey);
value,
48
),
0
)
);
} }
public static fromHexString(value: string): PrivateKey { public static fromHexString(value: string): PrivateKey {
return PrivateKey.fromBytes( value = value.replace("0x", "");
Buffer.from(value.replace("0x", ""), "hex") assert(value.length === SECRET_KEY_LENGTH * 2, "secret key must have 32 bytes");
); const context = getContext();
return new PrivateKey(context.deserializeHexStrToSecretKey(value));
} }
public static random(): PrivateKey { public static random(): PrivateKey {
return PrivateKey.fromBytes(random.randomBuffer(SECRET_KEY_LENGTH)); const context = getContext();
const secretKey = new context.SecretKey();
secretKey.setByCSPRNG();
return new PrivateKey(secretKey);
} }
public getValue(): BIG { public getValue(): SecretKeyType {
return this.value; return this.value;
} }
public sign(message: G2point): G2point { public sign(message: Uint8Array): SignatureType {
return message.mul(this.value); return this.value.sign(message);
} }
public signMessage(message: Hash, domain: Domain): G2point { public signMessage(message: Hash, domain: Domain): SignatureType {
return G2point.hashToG2(message, domain).mul(this.value); return this.value.signHashWithDomain(Buffer.concat([message, domain]));
} }
public toBytes(): BLSSecretKey { public toBytes(): BLSSecretKey {
const buffer = Buffer.alloc(FP_POINT_LENGTH, 0); return Buffer.from(this.value.serialize());
this.value.tobytearray(buffer, 0);
return buffer.slice(FP_POINT_LENGTH - SECRET_KEY_LENGTH);
} }
public toHexString(): string { public toHexString(): string {
return `0x${this.toBytes().toString("hex")}`; return `0x${this.value.serializeToHexStr()}`;
} }
} }

20
test/unit/context.test.ts Normal file
View File

@ -0,0 +1,20 @@
import {init, getContext, destroy} from "../../src/context";
import {expect} from "chai";
describe("bls wasm constext", function () {
afterEach(() => {
destroy();
});
it("initializes and works", async function () {
await init();
expect(getContext().getCurveOrder())
.to.be.equal("52435875175126190479447740508185965837690552500527637822603658699938581184513");
});
it("throws if context not initialized", async function () {
expect(() => getContext().getCurveOrder()).to.throw();
});
});

View File

@ -1,22 +1,40 @@
import {PrivateKey} from "../../src/privateKey"; import {PrivateKey} from "../../src/privateKey";
import {expect} from "chai"; import {expect} from "chai";
import {SECRET_KEY_LENGTH} from "../../src/constants"; import {SECRET_KEY_LENGTH} from "../../src/constants";
import {destroy, init} from "../../src/context";
describe('privateKey', function() { describe("privateKey", function() {
it('should generate random private key', function () { before(async function () {
await init();
});
after(function () {
destroy();
});
it("should generate random private key", function () {
const privateKey1 = PrivateKey.random(); const privateKey1 = PrivateKey.random();
const privateKey2 = PrivateKey.random(); const privateKey2 = PrivateKey.random();
expect(privateKey1).to.not.be.equal(privateKey2); expect(privateKey1.toHexString()).to.not.be.equal(privateKey2.toHexString());
}); });
it('should export private key to hex string', function () { it("should export private key to hex string", function () {
const privateKey = '0x9a88071ff0634f6515c7699c97d069dc4b2fa28455f6b457e92d1c1302f0c6bb'; const privateKey = "0x07656fd676da43883d163f49566c72b9cbf0a5a294f26808c807700732456da7";
expect(PrivateKey.fromHexString(privateKey).toHexString()).to.be.equal(privateKey); expect(PrivateKey.fromHexString(privateKey).toHexString()).to.be.equal(privateKey);
const privateKey2 = "07656fd676da43883d163f49566c72b9cbf0a5a294f26808c807700732456da7";
expect(PrivateKey.fromHexString(privateKey2).toHexString()).to.be.equal(privateKey);
}); });
it('should export private key to bytes', function () { it("should export private key to bytes", function () {
expect(PrivateKey.random().toBytes().length).to.be.equal(SECRET_KEY_LENGTH); expect(PrivateKey.random().toBytes().length).to.be.equal(SECRET_KEY_LENGTH);
}); });
it("should not accept too short private key", function () {
expect(() => PrivateKey.fromHexString("0x2123")).to.throw();
});
}); });

10
tsconfig.build.json Normal file
View File

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig",
"include": ["src"],
"compilerOptions": {
"typeRoots": [
"src/@types",
"../../node_modules/@types"
]
}
}

View File

@ -1,6 +1,6 @@
{ {
"extends": "../../tsconfig", "extends": "../../tsconfig",
"include": ["src"], "include": ["src", "test"],
"compilerOptions": { "compilerOptions": {
"typeRoots": [ "typeRoots": [
"src/@types", "src/@types",