diff --git a/crypto/fixtures.go b/crypto/fixtures.go index 637740e..fb96a4d 100644 --- a/crypto/fixtures.go +++ b/crypto/fixtures.go @@ -46,8 +46,6 @@ func GetMessageFixture() TestingMessageFixture { return fixture } - - func GetBLSValidatorFixture() BLSValidatorFixture { data := GetFixture("bls_validator") @@ -68,8 +66,8 @@ func GetBLSKeysFixture() []BLSKeyFixture { type TestingFixture struct { MultiSignatureAsset MultiSignatureRegistrationAsset `json:"multiSignatureAsset"` - Transaction Transaction `json:"transaction"` - SerializedHex string `json:"serializedHex"` + Transaction Transaction `json:"transaction"` + SerializedHex string `json:"serializedHex"` } type TestingIdentityFixture struct { @@ -97,7 +95,8 @@ type BLSValidatorFixture struct { } type BLSKeyFixture struct { - BLSPublicKey string `json:"bls_public_key"` - BLSPrivateKey string `json:"bls_private_key"` - Passphrase string `json:"passphrase"` + BLSPublicKey string `json:"bls_public_key"` + BLSPrivateKey string `json:"bls_private_key"` + ProofOfPossession string `json:"proof_of_possession"` + Passphrase string `json:"passphrase"` } diff --git a/crypto/fixtures/bls_keys.json b/crypto/fixtures/bls_keys.json index 08251d3..089ee83 100644 --- a/crypto/fixtures/bls_keys.json +++ b/crypto/fixtures/bls_keys.json @@ -2,11 +2,13 @@ { "bls_public_key": "90507d5a1a4cde6729f61a0e8fcc34f854113faf05f995b3ffd320639c4ffd118c335099350c92daa58e9ba22ca71af1", "bls_private_key": "710b0de2981d407d144161a5123f498a88355e3b0a559aa7942d90d49d0d5b34", + "proof_of_possession": "a2fd406fe7d8d171eed27d35f890b9f51e918724f51523cab2e030f8a1993d317871fb7358232b334e36a703abfc798201aaf8bd0043b1247dc0a676cdec5bfe047fcfb3ce35838036d8c9c6297a360b670d04c70035720d06ac8d591985550d", "passphrase": "famous dolphin salad photo spend stock portion outdoor print fiscal element smoke silent ritual verify current better raw visual mom real bubble certain banana" }, { "bls_public_key": "af7f00ec30273a99411aa940ecf40646fa684f82d7d70e536a76c7c02e8ee6f3ffd297c42ad3775922938a31925bc1ab", "bls_private_key": "5a010d4c79c01fc4d26fcce16a601da683bd2dcb67aa09d6d4ab29042020e62b", + "proof_of_possession": "a8ebf3739e4a39d1f1f148cee0448ee4e525a6dcf7dffd35c9af3234abf147d9fa252b570e731ab13dc268629c4ad0a912008d097f36a26e3f600a35acc681f790a22506a90ea0c2ee4c85ad54baed2e664ea46fafa63be9475c70e58f44da37", "passphrase": "good copper economy hope purity budget mistake achieve tail endless travel vibrant office cement inmate gospel effort desert garbage fiscal direct siege bright habit" } ] diff --git a/crypto/proof_of_possession.go b/crypto/proof_of_possession.go new file mode 100644 index 0000000..81235e3 --- /dev/null +++ b/crypto/proof_of_possession.go @@ -0,0 +1,53 @@ +// This file is part of Ark Go Crypto. +// +// (c) Ark Ecosystem +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +package crypto + +import ( + "encoding/hex" + "errors" + + blst "github.com/supranational/blst/bindings/go" + "github.com/tyler-smith/go-bip39" +) + +const popDST = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_" + +type ProofOfPossessionResult struct { + PK []byte + POP []byte +} + +func DeriveBlsPrivateKey(passphrase string) []byte { + return popDeriveChildSk(passphrase).Serialize() +} + +func DeriveBlsPublicKey(passphrase string) string { + sk := popDeriveChildSk(passphrase) + pk := new(blst.P1Affine).From(sk) + return hex.EncodeToString(pk.Compress()) +} + +func BuildProofOfPossession(secretKeyBytes []byte) (*ProofOfPossessionResult, error) { + sk := new(blst.SecretKey) + if sk.Deserialize(secretKeyBytes) == nil { + return nil, errors.New("invalid secret key bytes") + } + pk := new(blst.P1Affine).From(sk) + pkBytes := pk.Compress() + sig := new(blst.P2Affine).Sign(sk, pkBytes, []byte(popDST)) + return &ProofOfPossessionResult{PK: pkBytes, POP: sig.Compress()}, nil +} + +func FromMnemonic(passphrase string) (*ProofOfPossessionResult, error) { + return BuildProofOfPossession(DeriveBlsPrivateKey(passphrase)) +} + +func popDeriveChildSk(passphrase string) *blst.SecretKey { + seed := bip39.NewSeed(passphrase, "") + return blst.KeyGen(seed).DeriveChildEip2333(0) +} diff --git a/crypto/proof_of_possession_test.go b/crypto/proof_of_possession_test.go new file mode 100644 index 0000000..8d41b02 --- /dev/null +++ b/crypto/proof_of_possession_test.go @@ -0,0 +1,61 @@ +// This file is part of Ark Go Crypto. +// +// (c) Ark Ecosystem +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +package crypto + +import ( + "encoding/hex" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDeriveBlsPrivateKey(t *testing.T) { + fixtures := GetBLSKeysFixture() + + for _, f := range fixtures { + keyBytes := DeriveBlsPrivateKey(f.Passphrase) + assert.Equal(t, strings.ToLower(f.BLSPrivateKey), hex.EncodeToString(keyBytes)) + } +} + +func TestDeriveBlsPublicKey(t *testing.T) { + fixtures := GetBLSKeysFixture() + + for _, f := range fixtures { + pubKeyHex := DeriveBlsPublicKey(f.Passphrase) + assert.Equal(t, strings.ToLower(f.BLSPublicKey), strings.ToLower(pubKeyHex)) + } +} + +func TestBuildProofOfPossession(t *testing.T) { + fixtures := GetBLSKeysFixture() + + for _, f := range fixtures { + result, err := BuildProofOfPossession(HexDecode(f.BLSPrivateKey)) + assert.NoError(t, err) + assert.Equal(t, strings.ToLower(f.BLSPublicKey), hex.EncodeToString(result.PK)) + assert.Equal(t, strings.ToLower(f.ProofOfPossession), hex.EncodeToString(result.POP)) + } +} + +func TestBuildProofOfPossessionInvalidKey(t *testing.T) { + _, err := BuildProofOfPossession([]byte("not a valid key")) + assert.Error(t, err) +} + +func TestFromMnemonic(t *testing.T) { + fixtures := GetBLSKeysFixture() + + for _, f := range fixtures { + result, err := FromMnemonic(f.Passphrase) + assert.NoError(t, err) + assert.Equal(t, strings.ToLower(f.BLSPublicKey), hex.EncodeToString(result.PK)) + assert.Equal(t, strings.ToLower(f.ProofOfPossession), hex.EncodeToString(result.POP)) + } +} diff --git a/go.mod b/go.mod index ae12fa8..01aae6b 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ellemouton/schnorr v0.0.0-20230301092540-7b5fdc085456 github.com/fatih/structs v1.1.0 github.com/stretchr/testify v1.9.0 - github.com/supranational/blst v0.3.13 + github.com/supranational/blst v0.3.16 github.com/tyler-smith/go-bip39 v1.1.0 golang.org/x/crypto v0.27.0 ) diff --git a/go.sum b/go.sum index d8d62ef..2786ffb 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= -github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=