Skip to content

Commit

Permalink
JS bindings (#55)
Browse files Browse the repository at this point in the history
* Add simple build script

* Add simple bindings

* Changed WSIZE to 32 to make emscripten work (it's a 32-bit compiler); Added some bindings and tests for the bindings

* Add simple bindings for the Signature class

* Add a new helper to copy c++ vector<uin8_t> to js Buffer in order to avoid raw memory views, as they can lead to a data corruption; Modify CMake bindings config to make CMake working properly;

* Add a new helper to copy c++ vector<uin8_t> to js Buffer in order to avoid raw memory views, as they can lead to a data corruption; Modify CMake bindings config to make CMake working properly;

* Add bindings for the PublicKey and a test for the PrivateKey

* Add test for PublicKey

* Add aggregation methods for public and private keys

* Reformat code a bit

* Add more helpers for bns, byte arrays and vectors; Add AggregationInfo class; Separated helpers to header and implementation

* Modify main cmake file to include specific emscripten instructions

* Add some works on signature aggregation

* Fixed cmake build for emscripten

* Make signature aggregation work

* Reformat code

* Reformat bindings

* Refactor helpers and wrappers

* Revert main cmake

* Add wrapper for extended private key

* Add extended public key wrapper; Add extended private and public keys to bindings

* Add ChainCode wrapper and bindings

* Add initial typings

* Add more ts typings; Add InsecureSignature wrapper and JS binding;

* Refactored wrappers a bit

* Add a base wrapper

* Add simple readme

* Fix typos in readme

* Add license to js bindings

* Make CMake copy js package files to the build dir; Make CMake to copy js tests to the build dir; Make CMake install npm dependencies after the build; Make tests to require lib from the parent dir

* Modify readme a bit

* Modify readme a bit

* Modify readme a bit

* Modify readme a bit

* Modify readme a bit

* Add repository to package.json

* Add some content to threshold wrapper; Port thershold signature test

* Add test for Threshold

* Add tests for ExtendedPrivateKey, ExtendedPublicKey, ChainCode, add more tests for PrivateKey, fixed typings a bit

* Update name in package.json

* Add bundling memfile to bindings; Fix bindings readme; Bumped patch version

* Reformat wrappers code

* Apply README grammar fixes

* Make lib interface to interact with Uint8Array instead of Buffer; Changed default compilation target to wasm instead of asmjs

* Add information about deleting objects to README; Add .delete method to all objects in typings

* Add browser tests

* Mentioned firefox tests in Readme

* Add simple test for typings

* Add tests for typings

* Add constants from bls classes

* Add GROUP_ORDER constant

* Fix cpplint errors

* Fix readme grammar

* Add bindings for DHKeyExchange

* Remove relic headers from helpers
  • Loading branch information
antouhou authored and mariano54 committed Apr 5, 2019
1 parent 5401869 commit 683a148
Show file tree
Hide file tree
Showing 40 changed files with 8,031 additions and 3 deletions.
19 changes: 16 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@ else()
set(ARITH "easy" CACHE STRING "")
endif()

if(EMSCRIPTEN)
# emscripten needs arch set to be none since it can't compile assembly
set(ARCH "" CACHE STRING "")
# emscripten is a 32 bit compiler
set(WSIZE 32 CACHE INTEGER "")
else()
set(WSIZE 64 CACHE INTEGER "")
endif()

set(WSIZE 64 CACHE INTEGER "")
set(TIMER "CYCLE" CACHE STRING "")
set(CHECK "off" CACHE STRING "")
set(VERBS "off" CACHE STRING "")
Expand Down Expand Up @@ -68,6 +75,12 @@ set(PP_EXT "LAZYR" CACHE STRING "")
set(PP_METHD "LAZYR;OATEP" CACHE STRING "")

add_subdirectory(contrib/relic)
add_subdirectory(contrib/pybind11)
add_subdirectory(src)
add_subdirectory(python-bindings)

if (EMSCRIPTEN)
add_subdirectory(js-bindings)
else()
# emscripten can't build python bindings, it produces only javascript
add_subdirectory(contrib/pybind11)
add_subdirectory(python-bindings)
endif()
39 changes: 39 additions & 0 deletions js-bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.1.0 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 11)

include_directories(
${INCLUDE_DIRECTORIES}
${CMAKE_CURRENT_SOURCE_DIR}/../contrib/relic/include
${CMAKE_BINARY_DIR}/contrib/relic/include
${CMAKE_CURRENT_SOURCE_DIR}/../contrib/catch
)

file(GLOB_RECURSE WRAP_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/wrappers/*.h)
file(GLOB_RECURSE WRAP_SRC ${CMAKE_CURRENT_SOURCE_DIR}/wrappers/*.cpp)

add_executable(blsjs ${CMAKE_CURRENT_SOURCE_DIR}/jsbindings.cpp
${WRAP_HEADERS} ${WRAP_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/helpers.h ${CMAKE_CURRENT_SOURCE_DIR}/helpers.cpp
)
add_custom_target(install_npm_dependencies npm ci)
add_dependencies(blsjs install_npm_dependencies)

if (SODIUM_FOUND)
target_link_libraries(blsjs PRIVATE blstmp relic_s sodium)
else ()
target_link_libraries(blsjs PRIVATE blstmp relic_s)
endif ()

# Copy necessary files for the npm package
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/package.json package.json COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json package-lock.json COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/blsjs.d.ts blsjs.d.ts COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/README.md README.md COPYONLY)

# Copy test files
file(GLOB JS_BINDINGS_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/tests/ ${CMAKE_CURRENT_SOURCE_DIR}/tests/*)
foreach(file ${JS_BINDINGS_TESTS})
message(FILE ${file})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tests/${file} tests/${file} COPYONLY)
endforeach()

set_target_properties(blsjs PROPERTIES LINK_FLAGS "--bind -s SINGLE_FILE=1 -s MODULARIZE_INSTANCE=1 -s BINARYEN_ASYNC_COMPILATION=0")
49 changes: 49 additions & 0 deletions js-bindings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## bls-signatures

JavaScript library that implements BLS signatures with aggregation as in [Boneh, Drijvers, Neven 2018](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html), using the relic toolkit for cryptographic primitives (pairings, EC, hashing).

This library is a JavaScript port of the [Chia Network's BLS lib](https://github.com/Chia-Network/bls-signatures). We also have typings, so you can use it with TypeScript too!

### Usage

```bash
npm i bls-signatures --save
```
```javascript
const { PrivateKey } = require('bls-signatures');
const privateKey = PrivateKey.fromSeed(Uint8Array.from([1,2,3]));
const sig = privateKey.sign(Uint8Array.from(Buffer.from("Hello world!")));
const isValidSignature = sig.verify();

if (isValidSignature) {
// Do stuff...
}

privateKey.delete();
sig.delete();
```

Please refer to the library's [typings](../../js-bindings/blsjs.d.ts) for detailed API information. Use cases can be found in the [original lib's readme](../../README.md).

__Important note on usage:__ Since this library is a WebAssembly port of the c++ library, JavaScript's automatic memory management isn't available. Please, delete all objects manually if they are not needed anymore by calling the delete method on them, as shown in the example above.

### Build

Building requires Node.js (with npm) and [Emscripten](https://emscripten.org/docs/getting_started/downloads.html) to be installed.
The build process is the same as for the c++ lib, with one additional step: pass the Emscripten toolchain file as an option to CMake.
From the project root directory, run:
```
git submodule update --init --recursive
mkdir js_build
cd js_build
cmake ../ -DCMAKE_TOOLCHAIN_FILE={path_to_your_emscripten_installation}/emsdk/emscripten/{version}/cmake/Modules/Platform/Emscripten.cmake
cmake --build . --
```

Run the build after any changes to the library, including readme and tests, as the library will be deployed from the build directory, and the build system copies all the files from the source dir.
### Run tests
Tests are run in node.js and Firefox, therefore you need to install node.js and Firefox.
To run tests, build the library, then go to the `js_bindings` folder in the build directory and run
```bash
npm test
```
172 changes: 172 additions & 0 deletions js-bindings/blsjs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
export class PrivateKey {
static PRIVATE_KEY_SIZE: number;

static fromSeed(seed: Uint8Array): PrivateKey;

static fromBytes(bytes: Uint8Array, modOrder: boolean): PrivateKey;

static aggregate(privateKeys: PrivateKey[], publicKeys: PublicKey[]): PrivateKey;

static aggregateInsecure(privateKeys: PrivateKey[]): PrivateKey;

getPublicKey(): PublicKey;

serialize(): Uint8Array;

sign(message: Uint8Array): Signature;

signInsecure(message: Uint8Array): InsecureSignature;

signPrehashed(messageHash: Uint8Array): Signature;

delete(): void;
}

export class InsecureSignature {
static SIGNATURE_SIZE: number;

static fromBytes(bytes: Uint8Array);

static aggregate(signatures: InsecureSignature[]): InsecureSignature;

verify(hashes: Uint8Array[], pubKeys: PublicKey[]): boolean;

divideBy(insecureSignatures: InsecureSignature[]): InsecureSignature;

serialize(): Uint8Array;

delete(): void;
}

export class Signature {
static SIGNATURE_SIZE: number;

static fromBytes(bytes: Uint8Array): Signature;

static fromBytesAndAggregationInfo(bytes: Uint8Array, aggregationInfo: AggregationInfo): Signature;

static aggregateSigs(signatures: Signature[]): Signature;

serialize(): Uint8Array;

verify(): boolean;

getAggregationInfo(): AggregationInfo;

setAggregationInfo(aggregationInfo: AggregationInfo): void;

delete(): void;
}

export class PublicKey {
static PUBLIC_KEY_SIZE: number;

static fromBytes(bytes: Uint8Array): PublicKey;

static aggregate(publicKeys: PublicKey[]): PublicKey;

static aggregateInsecure(publicKeys: PublicKey[]): PublicKey;

getFingerprint(): number;

serialize(): Uint8Array;

delete(): void;
}

export class AggregationInfo {
static fromMsgHash(publicKey: PublicKey, messageHash: Uint8Array): AggregationInfo;

static fromMsg(publicKey: PublicKey, message: Uint8Array): AggregationInfo;

static fromBuffers(pubKeys: PublicKey[], msgHashes: Uint8Array[], exponents: Uint8Array[]): AggregationInfo;

getPublicKeys(): PublicKey[];

getMessageHashes(): Uint8Array[];

getExponents(): Uint8Array[];

delete(): void;
}

export class ExtendedPrivateKey {
static EXTENDED_PRIVATE_KEY_SIZE: number;

static fromSeed(seed: Uint8Array): ExtendedPrivateKey;

static fromBytes(bytes: Uint8Array): ExtendedPrivateKey;

privateChild(index: number): ExtendedPrivateKey;

publicChild(index: number): ExtendedPublicKey;

getVersion(): number;

getDepth(): number;

getParentFingerprint(): number;

getChildNumber(): number;

getChainCode(): ChainCode;

getPrivateKey(): PrivateKey;

getPublicKey(): PublicKey;

getExtendedPublicKey(): ExtendedPublicKey;

serialize(): Uint8Array;

delete(): void;
}

export class ExtendedPublicKey {
static VERSION: number;
static EXTENDED_PUBLIC_KEY_SIZE: number;

static fromBytes(bytes: Uint8Array): ExtendedPublicKey;

publicChild(index: number): ExtendedPublicKey;

getVersion(): number;

getDepth(): number;

getParentFingerprint(): number;

getChildNumber(): number;

getPublicKey(): PublicKey;

getChainCode(): ChainCode;

serialize(): Uint8Array;

delete(): void;
}

export class ChainCode {
static CHAIN_CODE_SIZE: number;

static fromBytes(bytes: Uint8Array);

serialize(): Uint8Array;

delete(): void;
}

export namespace Threshold {
export function create(commitment: PublicKey[], secretFragments: PrivateKey[], threshold: number, playersCount: number): PrivateKey;

export function signWithCoefficient(sk: PrivateKey, message: Uint8Array, playerIndex: number, players: number[]): InsecureSignature;

export function aggregateUnitSigs(signatures: InsecureSignature[], message: Uint8Array, players: number[]): InsecureSignature;

export function verifySecretFragment(playerIndex: number, secretFragment: PrivateKey, commitment: PublicKey[], threshold: number): boolean;
}

export function DHKeyExchange(privateKey: PrivateKey, publicKey: PublicKey);

export const GROUP_ORDER: string;
90 changes: 90 additions & 0 deletions js-bindings/helpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2018 Chia Network Inc

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "./helpers.h"

namespace helpers {
val toUint8Array(std::vector<uint8_t> vec) {
val arr = helpers::toJSArray<uint8_t>(vec);
return val::global("Uint8Array").call<val>("from", arr);
}

val toUint8Array(uint8_t *pointer, size_t data_size) {
std::vector<uint8_t> vec = toVector(pointer, data_size);
val buffer = toUint8Array(vec);
return buffer;
}

val toUint8Array(bn_t bn) {
std::vector<uint8_t> vec = toVector(bn);
val buffer = toUint8Array(vec);
return buffer;
}

std::vector<uint8_t> toVector(uint8_t *pointer, size_t data_size) {
std::vector<uint8_t> data;
data.reserve(data_size);
std::copy(pointer, pointer + data_size, std::back_inserter(data));
return data;
}

std::vector<uint8_t> toVector(val jsUint8Array) {
auto l = jsUint8Array["length"].as<unsigned>();
std::vector<uint8_t> vec;
for (unsigned i = 0; i < l; ++i) {
vec.push_back(jsUint8Array[i].as<uint8_t>());
}
return vec;
}

std::vector<uint8_t> toVector(bn_t bn) {
uint8_t buf[bn_size_bin(bn)];
bn_write_bin(buf, bn_size_bin(bn), bn);
std::vector<uint8_t> vec = helpers::toVector(buf, bn_size_bin(bn));
return vec;
}

std::vector<std::vector<uint8_t>> jsBuffersArrayToVector(val buffersArray) {
auto l = buffersArray["length"].as<unsigned>();
std::vector<std::vector<uint8_t>> vec;
for (unsigned i = 0; i < l; ++i) {
vec.push_back(toVector(buffersArray[i].as<val>()));
}
return vec;
}

std::vector<bn_t *> jsBuffersArrayToBnVector(val buffersArray) {
auto l = buffersArray["length"].as<unsigned>();
std::vector<bn_t *> vec;
for (unsigned i = 0; i < l; ++i) {
bn_t data;
bn_new(data);
std::vector<uint8_t> bnVec = toVector(buffersArray[i]);
bn_read_bin(data, bnVec.data(), static_cast<int>(bnVec.size()));
bn_t *point = &data;
vec.push_back(point);
}
return vec;
}

val byteArraysVectorToJsBuffersArray(std::vector<uint8_t *> arraysVector, size_t element_size) {
auto vecSize = arraysVector.size();
std::vector<val> valVector;
for (unsigned i = 0; i < vecSize; ++i) {
valVector.push_back(toUint8Array(arraysVector[i], element_size));
}
val arr = helpers::toJSArray<val>(valVector);
return arr;
}
} // namespace helpers
Loading

0 comments on commit 683a148

Please sign in to comment.