From 5258f4fb750bee36750f16fbf8e2b5bff57847c8 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 16 Jan 2024 17:33:43 -0700 Subject: [PATCH 1/6] wip: add chuwt/chiabls for reference --- cmd/chiabls/main.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 cmd/chiabls/main.go diff --git a/cmd/chiabls/main.go b/cmd/chiabls/main.go new file mode 100644 index 0000000..34a21d8 --- /dev/null +++ b/cmd/chiabls/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + + bls "github.com/chuwt/chia-bls-go" +) + +func main() { + secret := []byte("it's a secret") + seed := sha256.Sum256(secret) + privKey := bls.KeyGen(seed[:]) + pubKey := privKey.GetPublicKey() + privBytes := privKey.Bytes() + pubBytes := pubKey.Bytes() + privStr := hex.EncodeToString(privBytes) + pubStr := hex.EncodeToString(pubBytes) + + fmt.Printf("Secret: %q\n", secret) + fmt.Printf("Private: %#v\n", privStr) + fmt.Printf("Pub: %#v\n", pubStr) +} From ee71cd7afffe8b5b9086e6062ddee2791adcb24b Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 16 Jan 2024 17:34:15 -0700 Subject: [PATCH 2/6] chore: add chuwt/chia-bls-go to go.mod --- go.mod | 4 ++++ go.sum | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/go.mod b/go.mod index 00f9d90..062f9f3 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,12 @@ require ( gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) +require github.com/chuwt/chia-bls-go v0.1.0 + require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kilic/bls12-381 v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 58ac3f1..84c9d1a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/chuwt/chia-bls-go v0.1.0 h1:jqgnGi+QG4WiS9k1kh5/q6baPpzRdPpp2mnx37tgG1k= +github.com/chuwt/chia-bls-go v0.1.0/go.mod h1:VPx05R1F9bnTndbKQKvU6MkpPAiCgVm9l21CYGQRy0A= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dashpay/bls-signatures/go-bindings v0.0.0-20230207105415-06df92693ac8 h1:v4K3CiDoFY1gjcWL/scRcwzyjBwh8TVG3ek8cWolK1g= github.com/dashpay/bls-signatures/go-bindings v0.0.0-20230207105415-06df92693ac8/go.mod h1:auvGS60NBZ+a21aCCQh366PdsjDvHinsCvl28VrYPu4= @@ -16,6 +18,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= +github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -28,8 +32,20 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 1f6aeb412aa58cd39dc3ac17912973a805a1a036 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 17 Jan 2024 18:21:17 -0700 Subject: [PATCH 3/6] feat: implement BasicSchemeMPL --- blsaug/blsaug.go | 69 ++++++++++++++++++++++++ blsbasic/blsbasic.go | 67 +++++++++++++++++++++++ go.mod | 6 ++- internal/blscore/blscore.go | 105 ++++++++++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 blsaug/blsaug.go create mode 100644 blsbasic/blsbasic.go create mode 100644 internal/blscore/blscore.go diff --git a/blsaug/blsaug.go b/blsaug/blsaug.go new file mode 100644 index 0000000..a91c8fa --- /dev/null +++ b/blsaug/blsaug.go @@ -0,0 +1,69 @@ +package blsaug + +import ( + chiabls "github.com/chuwt/chia-bls-go" + "github.com/dashpay/tenderdash/internal/blscore" + bls12381 "github.com/kilic/bls12-381" +) + +var AugSchemeDst = blscore.DomainSeparationTag( + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_", +) + +type AugSchemeMPL struct { + // domainSeparationValue string +} + +// Sign +func (asm *AugSchemeMPL) Sign(sk *chiabls.PrivateKey, message []byte) []byte { + + pk := sk.GetPublicKey() + pkBytes := pk.Bytes() + message = append(pkBytes, message...) + sigPoint := blscore.SignMpl(sk, message, AugSchemeDst) + sigBytes := bls12381.NewG2().ToCompressed(sigPoint) + + return sigBytes +} + +// SignWithPrependPK +func (asm *AugSchemeMPL) SignWithPrependPK(sk *chiabls.PrivateKey, prependPK *chiabls.PublicKey, message []byte) []byte { + + pkBytes := prependPK.Bytes() + message = append(pkBytes, message...) + sigPoint := blscore.SignMpl(sk, message, AugSchemeDst) + sigBytes := bls12381.NewG2().ToCompressed(sigPoint) + + return sigBytes +} + +// Verify +func (asm *AugSchemeMPL) Verify(pk *chiabls.PublicKey, message []byte, sig []byte) bool { + + pkBytes := pk.Bytes() + message = append(pkBytes, message...) + verified := blscore.VerifyMpl(pk, message, sig, AugSchemeDst) + + return verified +} + +// Aggregate +func (asm *AugSchemeMPL) Aggregate(signatures ...[]byte) ([]byte, error) { + sigBytes, err := blscore.AggregateMpl(signatures...) + + return sigBytes, err +} + +// AggregateVerify +func (asm *AugSchemeMPL) AggregateVerify(pksBytes [][]byte, messages [][]byte, sig []byte) bool { + + pkMessages := [][]byte{} + for i, pkBytes := range pksBytes { + message := messages[i] + message = append(pkBytes, message...) + pkMessages = append(pkMessages, message) + } + verified := blscore.AggregateVerify(pksBytes, pkMessages, sig, AugSchemeDst) + + return verified +} diff --git a/blsbasic/blsbasic.go b/blsbasic/blsbasic.go new file mode 100644 index 0000000..97d868f --- /dev/null +++ b/blsbasic/blsbasic.go @@ -0,0 +1,67 @@ +package blsbasic + +import ( + "bytes" + "fmt" + + chiabls "github.com/chuwt/chia-bls-go" + "github.com/dashpay/tenderdash/internal/blscore" + bls12381 "github.com/kilic/bls12-381" +) + +var BasicSchemeDst = blscore.DomainSeparationTag( + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_", +) + +type BasicSchemeMPL struct { + // domainSeparationValue string +} + +func New() blscore.SchemeMPL { + bsm := &BasicSchemeMPL{} + + return bsm +} + +// Sign +func (bsm *BasicSchemeMPL) Sign(sk *chiabls.PrivateKey, message []byte) []byte { + sigPoint := blscore.SignMpl(sk, message, BasicSchemeDst) + sigBytes := bls12381.NewG2().ToCompressed(sigPoint) + + return sigBytes +} + +// Verify +func (bsm *BasicSchemeMPL) Verify(pk *chiabls.PublicKey, message []byte, sig []byte) bool { + verified := blscore.VerifyMpl(pk, message, sig, BasicSchemeDst) + + return verified +} + +// Aggregate +func (bsm *BasicSchemeMPL) Aggregate(signatures ...[]byte) ([]byte, error) { + sigBytes, err := blscore.AggregateMpl(signatures...) + + return sigBytes, err +} + +// AggregateVerify +func (bsm *BasicSchemeMPL) AggregateVerify(pks [][]byte, messages [][]byte, sig []byte) (bool, error) { + n := len(messages) + m := 1 + for i, a := range messages { + for j, b := range messages[m:] { + if bytes.Equal(a, b) { + ib := m + j + return false, fmt.Errorf("messages at indexes %d and %d are identical", i, ib) + } + } + m++ + if m >= n { + break + } + } + + verified := blscore.AggregateVerify(pks, messages, sig, BasicSchemeDst) + return verified, nil +} diff --git a/go.mod b/go.mod index 062f9f3..1d2fde2 100644 --- a/go.mod +++ b/go.mod @@ -19,11 +19,13 @@ require ( gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) -require github.com/chuwt/chia-bls-go v0.1.0 +require ( + github.com/chuwt/chia-bls-go v0.1.0 + github.com/kilic/bls12-381 v0.1.0 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kilic/bls12-381 v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/internal/blscore/blscore.go b/internal/blscore/blscore.go new file mode 100644 index 0000000..b5fa9c7 --- /dev/null +++ b/internal/blscore/blscore.go @@ -0,0 +1,105 @@ +package blscore + +import ( + "errors" + + chiabls "github.com/chuwt/chia-bls-go" + bls12381 "github.com/kilic/bls12-381" +) + +type DomainSeparationTag []byte + +type SchemeMPL interface { + Sign(sk *chiabls.PrivateKey, message []byte) []byte + Verify(pk *chiabls.PublicKey, message []byte, sig []byte) bool + Aggregate(signatures ...[]byte) ([]byte, error) + AggregateVerify(pks [][]byte, messages [][]byte, sig []byte) (bool, error) +} + +func SignMpl(sk *chiabls.PrivateKey, message, dst DomainSeparationTag) *bls12381.PointG2 { + g2Map := bls12381.NewG2() + + r := g2Map.New() + pq, _ := g2Map.HashToCurve(message, dst) + ske := bls12381.NewFr().FromBytes(sk.Bytes()) + + sigPoint := g2Map.MulScalar(r, pq, ske) + + return sigPoint +} + +func VerifyMpl(pk *chiabls.PublicKey, message []byte, sig, dst []byte) bool { + + g2Map := bls12381.NewG2() + q, _ := g2Map.HashToCurve(message, dst) + + signature, err := bls12381.NewG2().FromCompressed(sig) + if err != nil { + return false + } + + engine := bls12381.NewEngine() + + g1Neg := new(bls12381.PointG1) + g1Neg = bls12381.NewG1().Neg(g1Neg, chiabls.G1Generator()) + + engine = engine.AddPair(pk.G1(), q) + engine = engine.AddPair(g1Neg, signature) + + return engine.Check() +} + +func AggregateMpl(signatures ...[]byte) ([]byte, error) { + if len(signatures) == 0 { + return nil, errors.New("must aggregate at least 1 signature ") + } + + newG2 := bls12381.NewG2() + aggSig := newG2.New() + + for _, sig := range signatures { + g2, err := bls12381.NewG2().FromCompressed(sig) + if err != nil { + return nil, err + } + aggSig = bls12381.NewG2().Add(newG2.New(), aggSig, g2) + } + + return bls12381.NewG2().ToCompressed(aggSig), nil +} + +func AggregateVerify(pks, messages [][]byte, sig, dst []byte) bool { + pksLen := len(pks) + + if pksLen != len(messages) || pksLen == 0 { + return false + } + + g1Neg := new(bls12381.PointG1) + g1Neg = bls12381.NewG1().Neg(g1Neg, chiabls.G1Generator()) + + signature, err := bls12381.NewG2().FromCompressed(sig) + if err != nil { + return false + } + + engine := bls12381.NewEngine() + engine.AddPair(g1Neg, signature) + + for index, pk := range pks { + p, err := bls12381.NewG1().FromCompressed(pk) + if err != nil { + return false + } + + g2Map := bls12381.NewG2() + message := messages[index] + q, err := g2Map.HashToCurve(message, dst) + if err != nil { + return false + } + + engine.AddPair(p, q) + } + return engine.Check() +} From 6baa78621386c0b10ff3d30a2d58a9d40961a2e7 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 17 Jan 2024 18:21:36 -0700 Subject: [PATCH 4/6] wip: test pure-go signature --- cmd/chiabls/main.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/cmd/chiabls/main.go b/cmd/chiabls/main.go index 34a21d8..e57bca8 100644 --- a/cmd/chiabls/main.go +++ b/cmd/chiabls/main.go @@ -4,14 +4,19 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "log" - bls "github.com/chuwt/chia-bls-go" + blsbasic "github.com/dashpay/tenderdash/blsbasic" + + chiabls "github.com/chuwt/chia-bls-go" ) func main() { secret := []byte("it's a secret") + message := []byte("Hello, World!") + seed := sha256.Sum256(secret) - privKey := bls.KeyGen(seed[:]) + privKey := chiabls.KeyGen(seed[:]) pubKey := privKey.GetPublicKey() privBytes := privKey.Bytes() pubBytes := pubKey.Bytes() @@ -21,4 +26,20 @@ func main() { fmt.Printf("Secret: %q\n", secret) fmt.Printf("Private: %#v\n", privStr) fmt.Printf("Pub: %#v\n", pubStr) + + scheme := blsbasic.New() + + sigBytes := scheme.Sign(&privKey, message) + sigStr := hex.EncodeToString(sigBytes) + + fmt.Printf("\n") + fmt.Printf("Message: %s\n", message) + fmt.Printf("Signature: %#v\n", sigStr) + verify := scheme.Verify(&pubKey, message, sigBytes) + if !verify { + log.Fatal("bad signature using pubkey") + } + fmt.Printf("Verified: %#v\n", verify) + + fmt.Printf("\n") } From daef7acc90ec053640468b6519c97223ed34373b Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 18 Jan 2024 00:54:10 -0700 Subject: [PATCH 5/6] feat: created pure go alternative to ./crypt/bls12381 --- crypto/gobls/bench_test.go | 26 ++++ crypto/gobls/bls12381.go | 285 ++++++++++++++++++++++++++++++++++ crypto/gobls/bls12381_test.go | 75 +++++++++ 3 files changed, 386 insertions(+) create mode 100644 crypto/gobls/bench_test.go create mode 100644 crypto/gobls/bls12381.go create mode 100644 crypto/gobls/bls12381_test.go diff --git a/crypto/gobls/bench_test.go b/crypto/gobls/bench_test.go new file mode 100644 index 0000000..61c5d3c --- /dev/null +++ b/crypto/gobls/bench_test.go @@ -0,0 +1,26 @@ +package gobls + +import ( + "io" + "testing" + + "github.com/dashpay/tenderdash/crypto" + "github.com/dashpay/tenderdash/crypto/internal/benchmarking" +) + +func BenchmarkKeyGeneration(b *testing.B) { + benchmarkKeygenWrapper := func(reader io.Reader) crypto.PrivKey { + return genPrivKey(reader) + } + benchmarking.BenchmarkKeyGeneration(b, benchmarkKeygenWrapper) +} + +func BenchmarkSigning(b *testing.B) { + priv := GenPrivKey() + benchmarking.BenchmarkSigning(b, priv) +} + +func BenchmarkVerification(b *testing.B) { + priv := GenPrivKey() + benchmarking.BenchmarkVerification(b, priv) +} diff --git a/crypto/gobls/bls12381.go b/crypto/gobls/bls12381.go new file mode 100644 index 0000000..dd85abe --- /dev/null +++ b/crypto/gobls/bls12381.go @@ -0,0 +1,285 @@ +package gobls + +import ( + "bytes" + "crypto/rand" + "crypto/subtle" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + + chiabls "github.com/chuwt/chia-bls-go" + "github.com/dashpay/tenderdash/blsbasic" + "github.com/dashpay/tenderdash/internal/blscore" + bls12381 "github.com/kilic/bls12-381" + + "github.com/dashpay/tenderdash/crypto" +) + +//------------------------------------- + +var _ crypto.PrivKey = PrivKey{} + +const ( + PrivKeyName = "tendermint/PrivKeyBLS12381" + PubKeyName = "tendermint/PubKeyBLS12381" + // PubKeySize is is the size, in bytes, of public keys as used in this package. + PubKeySize = 48 + // PrivateKeySize is the size, in bytes, of private keys as used in this package. + PrivateKeySize = 32 + // SignatureSize of an BLS12381 signature. + SignatureSize = 96 + // SeedSize is the size, in bytes, of private key seeds. These are the + // private key representations used by RFC 8032. + SeedSize = 32 + + KeyType = "bls12381" +) + +var ( + errPubKeyIsEmpty = errors.New("public key should not be empty") + errPubKeyInvalidSize = errors.New("invalid public key size") + + emptyPubKeyVal = []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + + schema = blsbasic.New() +) + +// BasicScheme returns basic bls scheme +func BasicScheme() blscore.SchemeMPL { + return schema +} + +// PrivKey implements crypto.PrivKey. +type PrivKey []byte + +// TypeTag satisfies the jsontypes.Tagged interface. +func (PrivKey) TypeTag() string { return PrivKeyName } + +// Bytes returns the privkey byte format. +func (privKey PrivKey) Bytes() []byte { + return privKey +} + +func privKeyFromBytes(privBytes []byte) (*chiabls.PrivateKey, error) { + if keyLen := len(privBytes); keyLen != PrivateKeySize { + err := errInvalidPrivateKeySize(keyLen) + return nil, err + } + + privBigInt := &big.Int{} + privBigInt.SetBytes(privBytes) + + q := bls12381.NewG1().Q() + privWrap := &big.Int{} + privWrap = privWrap.Mod(privBigInt, q) + + privBytes = privWrap.Bytes() + privKey := chiabls.KeyFromBytes(privBytes) + + return &privKey, nil +} + +// Sign produces a signature on the provided message. +// This assumes the privkey is wellformed in the golang format. +// The first 32 bytes should be random, +// corresponding to the normal bls12381 private key. +// The latter 32 bytes should be the compressed public key. +// If these conditions aren't met, Sign will panic or produce an +// incorrect signature. +func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { + privKeyChia, err := privKeyFromBytes(privKey) + if err != nil { + return nil, err + } + + sigBytes := schema.Sign(privKeyChia, msg) + return sigBytes, nil +} + +// SignDigest produces a signature on the message digest (hash). +// This assumes the privkey is well-formed in the golang format. +// The first 32 bytes should be random, +// corresponding to the normal bls12381 private key. +// The latter 32 bytes should be the compressed public key. +// If these conditions aren't met, Sign will panic or produce an +// incorrect signature. +func (privKey PrivKey) SignDigest(hash []byte) ([]byte, error) { + sigBytes, err := privKey.Sign(hash) + return sigBytes, err +} + +// PubKey gets the corresponding public key from the private key. +// +// Panics if the private key is not initialized. +func (privKey PrivKey) PubKey() crypto.PubKey { + blsPrivKey, err := privKeyFromBytes(privKey) + if err != nil { + // should probably change method sign to return an error but since + // that's not available just panic... + panic("bad key") + } + + pubKey := blsPrivKey.GetPublicKey() + pubBytes := pubKey.Bytes() + pubIface := PubKey(pubBytes) + + return pubIface +} + +// Equals - you probably don't need to use this. +// Runs in constant time based on length of the keys. +func (privKey PrivKey) Equals(other crypto.PrivKey) bool { + if otherBLS, ok := other.(PrivKey); ok { + return subtle.ConstantTimeCompare(privKey[:], otherBLS[:]) == 1 + } + + return false +} + +func (privKey PrivKey) Type() string { + return KeyType +} + +func (privKey PrivKey) TypeValue() crypto.KeyType { + return crypto.BLS12381 +} + +// GenPrivKey generates a new bls12381 private key. +// It uses OS randomness in conjunction with the current global random seed +// in tendermint/libs/common to generate the private key. +func GenPrivKey() PrivKey { + return genPrivKey(rand.Reader) +} + +// genPrivKey generates a new bls12381 private key using the provided reader. +func genPrivKey(rand io.Reader) PrivKey { + seed := make([]byte, SeedSize) + + _, err := io.ReadFull(rand, seed) + if err != nil { + panic(err) + } + sk := chiabls.KeyGen(seed) + skBytes := sk.Bytes() + + return skBytes +} + +// GenPrivKeyFromSecret hashes the secret with SHA2, and uses +// that 32 byte output to create the private key. +// NOTE: secret should be the output of a KDF like bcrypt, +// if it's derived from user input. +func GenPrivKeyFromSecret(secret []byte) PrivKey { + seed := crypto.Checksum(secret) // Not Ripemd160 because we want 32 bytes. + + sk := chiabls.KeyGen(seed) + skBytes := sk.Bytes() + + return skBytes +} + +func ReverseProTxHashes(proTxHashes []crypto.ProTxHash) []crypto.ProTxHash { + reversedProTxHashes := make([]crypto.ProTxHash, len(proTxHashes)) + for i := 0; i < len(proTxHashes); i++ { + reversedProTxHashes[i] = proTxHashes[i].ReverseBytes() + } + return reversedProTxHashes +} + +//------------------------------------- + +var _ crypto.PubKey = PubKey{} + +// PubKey PubKeyBLS12381 implements crypto.PubKey for the bls12381 signature scheme. +type PubKey []byte + +// TypeTag satisfies the jsontypes.Tagged interface. +func (PubKey) TypeTag() string { return PubKeyName } + +// Address is the SHA256-20 of the raw pubkey bytes. +func (pubKey PubKey) Address() crypto.Address { + if len(pubKey) != PubKeySize { + panic("pubkey is incorrect size") + } + return crypto.AddressHash(pubKey) +} + +// Bytes returns the PubKey byte format. +func (pubKey PubKey) Bytes() []byte { + return pubKey +} + +func (pubKey PubKey) VerifySignatureDigest(hash []byte, sig []byte) bool { + verified := pubKey.VerifySignature(hash, sig) + return verified +} + +func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool { + // make sure we use the same algorithm to sign + if len(sig) == 0 { + // fmt.Printf("bls verifying error (signature empty) from message %X with key %X\n", msg, pubKey.Bytes()) + return false + } + + if len(sig) != SignatureSize { + // fmt.Printf("bls verifying error (signature size) sig %X from message %X with key %X\n", sig, msg, pubKey.Bytes()) + return false + } + + pubKeyChia, err := chiabls.NewPublicKey(pubKey) + if err != nil { + panic(err) + } + + verified := schema.Verify(&pubKeyChia, msg, sig) + + return verified +} + +func (pubKey PubKey) String() string { + return fmt.Sprintf("PubKeyBLS12381{%X}", []byte(pubKey)) +} + +// HexString returns hex-string representation of pubkey +func (pubKey PubKey) HexString() string { + return hex.EncodeToString(pubKey) +} + +func (pubKey PubKey) TypeValue() crypto.KeyType { + return crypto.BLS12381 +} + +func (pubKey PubKey) Type() string { + return KeyType +} + +func (pubKey PubKey) Equals(other crypto.PubKey) bool { + if otherBLS, ok := other.(PubKey); ok { + return bytes.Equal(pubKey[:], otherBLS[:]) + } + + return false +} + +// Validate validates a public key value +func (pubKey PubKey) Validate() error { + size := len(pubKey) + if size != PubKeySize { + return fmt.Errorf("public key has wrong size %d: %w", size, errPubKeyInvalidSize) + } + if bytes.Equal(pubKey, emptyPubKeyVal) { + return errPubKeyIsEmpty + } + return nil +} + +func errInvalidPrivateKeySize(size int) error { + return fmt.Errorf("incorrect private key %d bytes but expected %d bytes", size, PrivateKeySize) +} diff --git a/crypto/gobls/bls12381_test.go b/crypto/gobls/bls12381_test.go new file mode 100644 index 0000000..e652484 --- /dev/null +++ b/crypto/gobls/bls12381_test.go @@ -0,0 +1,75 @@ +// nolint:lll +package gobls + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/dashpay/tenderdash/crypto" +) + +func TestSignAndValidateBLS12381(t *testing.T) { + privKey := GenPrivKey() + pubKey := privKey.PubKey() + + hash := crypto.CRandBytes(128) + sig, err := privKey.SignDigest(hash) + require.Nil(t, err) + + // Test the signature + assert.True(t, pubKey.VerifySignatureDigest(hash, sig)) +} + +func TestBLSAddress(t *testing.T) { + testCases := []struct { + skB64 string + pkB64 string + addrHex string + }{ + { + skB64: "N3CR8OcoRjvC2n1UbFO59rgd9KHMGrW/KcWQi3FRoy0=", + pkB64: "hiQykLvL/ZrnW97OeYGWU1AgjrXpmwTVzSTpVa2pYfjAoWLe50C+e9xsPAYTui6x", + addrHex: "BB8F983D64252213936C9E962FDB066B3266C335", + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("test-case #%d", i+1), func(t *testing.T) { + skBytes, err := base64.StdEncoding.DecodeString(tc.skB64) + assert.NoError(t, err) + pkBytes, err := base64.StdEncoding.DecodeString(tc.pkB64) + assert.NoError(t, err) + addrBytes, err := hex.DecodeString(tc.addrHex) + assert.NoError(t, err) + privKey := PrivKey(skBytes) + pubKey := privKey.PubKey() + assert.EqualValues(t, pkBytes, pubKey) + assert.EqualValues(t, addrBytes, pubKey.Address()) + }) + } +} + +func TestPublicKeyGeneration(t *testing.T) { + testCases := []struct { + sk string + wantPk string + }{ + { + sk: "Bl1GYvRPgMpa/dpxb6gtph374TkDWoOv3OMH+jEoTWI=", + wantPk: "pWtWZTAnU0lSZKziSOtp8XbueEzfLDlaOuyH9RYteQeWCremf9expxa57A6k33iU", + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("test-case #%d", i+1), func(t *testing.T) { + skBytes, err := base64.StdEncoding.DecodeString(tc.sk) + require.NoError(t, err) + privateKey := PrivKey(skBytes) + pkBytes := base64.StdEncoding.EncodeToString(privateKey.PubKey().Bytes()) + require.Equal(t, tc.wantPk, pkBytes) + }) + } +} From e5dfafba357332a6521e296c806c03deec1a61a7 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 18 Jan 2024 01:18:36 -0700 Subject: [PATCH 6/6] doc: add go (non-cgo), tinygo, and wasm instructions --- README.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1311e37..b9a9463 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,47 @@ -# Build & Test +# Tenderbls + +This copies the BLS parts from Tenderdash to be able to run standalone, and compare against Pure Go implementations of Chia / Relic BLS. + +- Go (Kilic) +- TinyGo +- WASM +- CGO (Relic) + +# Build & Test Go / TinyGo + +```sh +curl https://webi.sh/go | sh +source ~/.config/envman/PATH.env + +go build ./cmd/chiabls +``` + +```sh +curl https://webi.sh/tinygo | sh +source ~/.config/envman/PATH.env + +tinygo build --tags generic ./cmd/chiabls +``` + +## WASM + +```sh +curl https://webi.sh/rust | sh +curl https://webi.sh/tinygo | sh +source ~/.config/envman/PATH.env + +cargo install wasm-opt + +tinygo build --tags generic \ + -o chiabls.wasm -target=wasi \ + ./cmd/chiabls +``` + +```sh +GOOS=wasip1 GOARCH=wasm go build -o chiabls.wasm ./cmd/chiabls +``` + +# Build & Test CGO ```sh git submodule init