Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bindings for GO #297

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ead6a1a
feat: add go-bindings for chiabls librarry version 1.0.7
shotonoff Nov 26, 2021
2c41701
feat: add Readme.md
shotonoff Nov 26, 2021
fe4e031
feat: update go.mod and go.sum files
shotonoff Nov 26, 2021
9b96d18
feat: clean up unit tests and add checking on invalid input for g1 an…
shotonoff Nov 26, 2021
d4f584c
feat: update github test pipeline
shotonoff Nov 26, 2021
000028e
feat: some code style update
shotonoff Nov 26, 2021
300c36f
feat: update README.md, add missed doc comments, allocate and release…
shotonoff Nov 30, 2021
c5b615f
Update go-bindings/elements.h
dmitriigolubevdh Nov 29, 2021
afec63f
feat: change the approach of linking deps libraries, add Makefile to …
shotonoff Dec 1, 2021
22d9a8b
feat: update go-bindings CI pipeline
shotonoff Dec 1, 2021
a1ac8cf
feat: update README.md
shotonoff Dec 1, 2021
9e35da5
feat: change a module ID on Chia-Network, update README.md
shotonoff Dec 1, 2021
13295aa
feat: remove "dash" copyright
shotonoff Dec 1, 2021
0701e76
feat: minor improvements
shotonoff Dec 3, 2021
0be2cef
fix: declare a template for converting elements between cgo and c++ f…
shotonoff Dec 1, 2021
512fc82
refactor: add copyright to the files where it was missed
shotonoff Dec 3, 2021
9c850ad
refactor: update copyright
shotonoff Dec 3, 2021
988043a
fix: change the chiabls input
shotonoff Dec 5, 2021
eb9557f
fix: explicit linking the libs
shotonoff Dec 22, 2021
ccc3769
fix: change typo in linking bls library
shotonoff Feb 14, 2022
358d091
Merge remote-tracking branch 'chia/main' into add-bindings-for-go-lang
shotonoff Feb 15, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/build-test.yaml
Expand Up @@ -78,3 +78,12 @@ jobs:
emcc -v
sh emsdk_build.sh
sh js_test.sh

- uses: actions/setup-go@v2
with:
go-version: "^1.17"

- name: Test go bindings
run: |
cd go-bindings
make
1 change: 1 addition & 0 deletions go-bindings/.gitignore
@@ -0,0 +1 @@
!Makefile
45 changes: 45 additions & 0 deletions go-bindings/Makefile
@@ -0,0 +1,45 @@
SRC_DIR=$(PWD)/../src
BUILD_DIR=$(PWD)/../build
CACHE_DIR=$(PWD)/cache

CGO_LDFLAGS="-L$(CACHE_DIR)/lib -lbls -lrelic_s -lgmp"
CGO_CXXFLAGS="-I$(CACHE_DIR)"

GO="go"
COVERAGE_OUTPUT ?= coverage.out

.PHONY: default vet test clean

default: prepare vet test clean

prepare:
ifeq ("$(wildcard $(CACHE_DIR))", "")
@mkdir -p $(CACHE_DIR)/chiabls $(CACHE_DIR)/lib
@cp -r $(BUILD_DIR)/_deps/relic-src/include/* $(CACHE_DIR)
@cp -r $(BUILD_DIR)/_deps/relic-build/include/* $(CACHE_DIR)
@cp -r $(SRC_DIR)/* $(CACHE_DIR)/chiabls
@find $(BUILD_DIR) -name "lib*.a" | xargs -I{} cp {} $(CACHE_DIR)/lib
endif

fmt: ## Run go fmt to format Go files
$(GO) fmt

test: ## Run a basic test suite
CGO_CXXFLAGS=$(CGO_CXXFLAGS) CGO_LDFLAGS=$(CGO_LDFLAGS) $(GO) test

cover: ## Run tests and generate test coverage file, output coverage results and HTML coverage file.
CGO_CXXFLAGS=$(CGO_CXXFLAGS) CGO_LDFLAGS=$(CGO_LDFLAGS) $(GO) test -coverprofile $(COVERAGE_OUTPUT)
CGO_CXXFLAGS=$(CGO_CXXFLAGS) CGO_LDFLAGS=$(CGO_LDFLAGS) $(GO) tool cover -func=$(COVERAGE_OUTPUT)
CGO_CXXFLAGS=$(CGO_CXXFLAGS) CGO_LDFLAGS=$(CGO_LDFLAGS) $(GO) tool cover -html=$(COVERAGE_OUTPUT)
rm -f $(COVERAGE_OUTPUT)

vet: ## Go vet all project code
CGO_CXXFLAGS=$(CGO_CXXFLAGS) CGO_LDFLAGS=$(CGO_LDFLAGS) go vet ./...

help: ## Show This Help
@for line in $$(cat Makefile | grep "##" | grep -v "grep" | sed "s/:.*##/:/g" | sed "s/\ /!/g"); do verb=$$(echo $$line | cut -d ":" -f 1); desc=$$(echo $$line | cut -d ":" -f 2 | sed "s/!/\ /g"); printf "%-30s--%s\n" "$$verb" "$$desc"; done

clean: ## Clean up transient (generated) files
$(GO) clean
rm -f $(COVERAGE_OUTPUT)
rm -rf $(CACHE_DIR)
195 changes: 195 additions & 0 deletions go-bindings/README.md
@@ -0,0 +1,195 @@
## bls-signatures

Go 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 Go port of the [Chia Network's BLS lib](https://github.com/Chia-Network/bls-signatures).

### Usage

```bash
go get github.com/Chia-Network/bls-signatures/go-bindings
```

### Creating keys and signatures

```go
// seed data must not be less 32 bytes length
seed := []byte{
0, 50, 6, 244, 24, 199, 1, 25,
52, 88, 192, 19, 18, 12, 89, 6,
220, 18, 102, 58, 209, 82, 12, 62,
89, 110, 182, 9, 44, 20, 254, 22,
}

// create a scheme, available three schemes: BasicSchemeMPL, AugSchemeMPL and PopSchemeMPL
scheme := NewAugSchemeMPL()

// generate a private key using a seed data
sk, err := scheme.KeyGen(seed)
if err != nil {
panic(err.Error())
}
// get a public key
pk, err := sk.G1Element()
if err != nil {
panic(err.Error())
}

msg := []byte{1, 2, 3, 4, 5}

// make a signature for a message
sig := scheme.Sign(sk, msg)

// verify the message signature
if !scheme.Verify(pk, msg, sig) {
panic("failed the signature verification")
}
```

### Serializing keys and signatures to bytes

```go
skBytes := sk.Serialize()
pkBytes := pk.Serialize()
sigBytes := sig.Serialize()
```

### Loading keys and signatures from bytes

```go
sk1, _ := PrivateKeyFromBytes(skBytes, false)
pk1, _ := G1ElementFromBytes(pkBytes)
sig1, _ := G2ElementFromBytes(sigBytes)
```

### Create aggregate signatures

```go
// Generate some more private keys
seed[0] = 1
sk1, _ := scheme.KeyGen(seed)
seed[0] = 2
sk2, _ := scheme.KeyGen(seed)
msg2 := []byte{1, 2, 3, 4, 5,6, 7}

// Generate first sig
pk1, _ := sk1.G1Element()
sig1 := scheme.Sign(sk1, msg)

// Generate second sig
pk2, _ := sk2.G1Element()
sig2 := scheme.Sign(sk2, msg2)

// Signatures can be non-interactively combined by anyone
var aggSig = scheme.AggregateSigs(sig1, sig2)

ok := scheme.AggregateVerify([]*G1Element{pk1, pk2}, [][]byte{msg, msg2}, aggSig)
if !ok {
panic("failed a verification of the aggregated signature ")
}
```

### Arbitrary trees of aggregates

```go
seed[0] = 3
sk3, _ := scheme.KeyGen(seed)
pk3, _ := sk3.G1Element()
msg3 := []byte{100, 2, 254, 88, 90, 45, 23}
sig3 := scheme.Sign(sk3, msg3)

aggSigFinal := scheme.AggregateSigs(aggSig, sig3)
ok = scheme.AggregateVerify([]*G1Element{pk1, pk2, pk3}, [][]byte{msg, msg2, msg3}, aggSigFinal)
if !ok {
panic("failed a verification of the aggregated signature ")
}
```

### Very fast verification with Proof of Possession scheme

```go
// create a proof possession scheme
popScheme := NewPopSchemeMPL()

// If the same msg is signed, you can use Proof of Possession (PopScheme) for efficiency
// A proof of possession MUST be passed around with the PK to ensure security.
popSig1 := popScheme.Sign(sk1, msg)
popSig2 := popScheme.Sign(sk2, msg)
popSig3 := popScheme.Sign(sk3, msg)
pop1 := popScheme.PopProve(sk1)
pop2 := popScheme.PopProve(sk2)
pop3 := popScheme.PopProve(sk3)

ok = popScheme.PopVerify(pk1, pop1)
if !ok {
panic("failed a verification")
}
ok = popScheme.PopVerify(pk2, pop2)
if !ok {
panic("failed a verification")
}
ok = popScheme.PopVerify(pk3, pop3)
if !ok {
panic("failed a verification")
}

popSigAgg := popScheme.AggregateSigs(popSig1, popSig2, popSig3)
ok = popScheme.FastAggregateVerify([]*G1Element{pk1, pk2, pk3}, msg, popSigAgg)
if !ok {
panic("failed a verification")
}

// Aggregate public key, indistinguishable from a single public key
var popAggPk = pk1.Add(pk2).Add(pk3)
ok = popScheme.Verify(popAggPk, msg, popSigAgg)
if !ok {
panic("failed a verification")
}

// Aggregate private keys
var aggSk = PrivateKeyAggregate(sk1, sk2, sk3)
ok = popScheme.Sign(aggSk, msg).EqualTo(popSigAgg)
if !ok {
panic("failed a verification")
}
```

### HD keys using [EIP-2333](https://github.com/ethereum/EIPs/pull/2333)

```go
// You can derive 'child' keys from any key, to create arbitrary trees. 4 byte indeces are used.
// Hardened (more secure, but no parent pk -> child pk)
masterSk, _ := augScheme.KeyGen(seed)

// Unhardened (less secure, but can go from parent pk -> child pk), BIP32 style
masterPk, _ := masterSk.G1Element()
childU := augScheme.DeriveChildSkUnhardened(masterSk, 22)
grandchildU := augScheme.DeriveChildSkUnhardened(childU, 0)

childUPk := augScheme.DeriveChildPkUnhardened(masterPk, 22)
grandchildUPk := augScheme.DeriveChildPkUnhardened(childUPk, 0)

pkChildU, _ := grandchildU.G1Element()
ok = grandchildUPk.EqualTo(pkChildU)
if !ok {
panic("keys are not equal")
}
```

Do not forget to handle the errors properly, this part of the code was omitted deliberately

Use cases can be found in the [original lib's readme](../README.md).

__Important note:__ Since this library is a port of the c++ library, so every piece of memory allocated by the library
MUST be released on our own in GO. To release the memory is used `runtime.SetFinalizer` function, that will invoke
automatically before GC release a memory allocated by GO.

### Run tests

To run tests, build the library, then go to the `go-bindings` folder in the build directory and run

```bash
make test
```
52 changes: 52 additions & 0 deletions go-bindings/blschia.cpp
@@ -0,0 +1,52 @@
// 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 <string>
#include <stdlib.h>
#include "chiabls/bls.hpp"
#include "error.h"
#include "blschia.h"

std::string gErrMsg;

void SecFree(void *p) {
bls::Util::SecFree(p);
}

void** AllocPtrArray(size_t len) {
// caller to free
return (void**)bls::Util::SecAlloc<uint8_t>(sizeof(void*) * len);
}

void SetPtrArray(void** arrPtr, void* elemPtr, int index) {
arrPtr[index] = elemPtr;
}

void FreePtrArray(void** inPtr) {
bls::Util::SecFree(inPtr);
}

void* GetPtrAtIndex(void** arrPtr, int index) {
return arrPtr[index];
}

uint8_t* SecAllocBytes(size_t len) {
return (uint8_t*)bls::Util::SecAlloc<uint8_t>(sizeof(uint8_t) * len);
}

void* GetAddressAtIndex(uint8_t* ptr, int index) {
return (void*)&ptr[index];
}

const char* GetLastErrorMsg() {
return gErrMsg.c_str();
}
44 changes: 44 additions & 0 deletions go-bindings/blschia.h
@@ -0,0 +1,44 @@
// 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.

#ifndef GO_BINDINGS_BLSCHIA_H_
#define GO_BINDINGS_BLSCHIA_H_
#include <stdbool.h>
#include <stdint.h>
#include "privatekey.h"

#ifdef __cplusplus
extern "C" {
#endif

// Export the BLS SecFree method
void SecFree(void *p);

typedef void** carr;

// Additional C++ helper funcs for allocations
void** AllocPtrArray(size_t len);
void SetPtrArray(void **arrPtr, void *elemPtr, int index);
void FreePtrArray(void **inPtr);
void* GetPtrAtIndex(void **arrPtr, int index);

// Allocates an array of bytes with size of passed in len argument
uint8_t* SecAllocBytes(size_t len);

void* GetAddressAtIndex(uint8_t *ptr, int index);

const char* GetLastErrorMsg();

#ifdef __cplusplus
}
#endif
#endif // GO_BINDINGS_BLSCHIA_H_