Question: is it possible to generate secp256k1 ecdsa Private Key from a secret string?
Teja2045 opened this issue · 3 comments
I want to check if secp256k1 implementation of cosmosSDK is compatable with gnark-crypto. Let me know if I'm missing something
package accounts
import (
"bytes"
"crypto/sha256"
"fmt"
"github.com/consensys/gnark-crypto/ecc/secp256k1/ecdsa"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
)
func Account() {
// generate private from a secret phrase
secret := "This is a very very big secret, dont share it"
privkey := secp256k1.GenPrivKeyFromSecret([]byte(secret))
// generate gnark private key from a secret phrase
buf := bytes.NewBuffer([]byte{})
buf.Write([]byte(secret))
gnarkPrivkey, err := ecdsa.GenerateKey(buf)
if err != nil {
panic(err)
}
// pubkeys
pubkey := privkey.PubKey()
gnarkPubkey := gnarkPrivkey.PublicKey
// msg to sign
msg := []byte("message")
sign, err := privkey.Sign(msg)
if err != nil {
panic(err)
}
// verify with sdk pubkey
isVerified := pubkey.VerifySignature(msg, sign)
fmt.Println("normal verification:", isVerified)
// verify with gnark pubkey
isVerified, err = gnarkPubkey.Verify(sign, msg, sha256.New())
if err != nil {
panic(err)
}
fmt.Println("gnark verification:", isVerified)
}
currently the only way in gnark-crypto to generate a private key is through crypto/rand.Reader this follows https://pkg.go.dev/crypto/ecdsa#GenerateKey and FIPS 186-4, Appendix B.5.1. Citing crypto/ecdsa here:
Most applications should use crypto/rand.Reader as rand. Note that the returned key does not depend deterministically on the bytes read from rand, and may change between calls and/or between versions.
To achieve gnark-crypto and cosmos-sdk compatibility I would generate the private key in either one and transform it to the format of the other library.
Something like this works on my machine:
package accounts
import (
"bytes"
"crypto/sha256"
"fmt"
"math/big"
"unsafe"
curve "github.com/consensys/gnark-crypto/ecc/secp256k1"
"github.com/consensys/gnark-crypto/ecc/secp256k1/ecdsa"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
)
func byte32(s []byte) (a *[32]byte) {
if len(a) <= len(s) {
a = (*[len(a)]byte)(unsafe.Pointer(&s[0]))
}
return a
}
func Account() {
// generate private from a secret phrase
secret := "This is a very very big secret, dont share it"
privkey := secp256k1.GenPrivKeyFromSecret([]byte(secret))
// translate cosmos-sdk privKey to gnark privKey
var gnarkPrivkey ecdsa.PrivateKey
gnarkPrivkey.Scalar = *byte32(bytes.Clone(privkey.Key))
// pubkeys
pubkey := privkey.PubKey()
var k big.Int
k.SetBytes(gnarkPrivkey.Scalar[:32])
fmt.Println(&k)
_, g := curve.Generators()
gnarkPrivkey.PublicKey.A.ScalarMultiplication(&g, &k)
gnarkPubkey := gnarkPrivkey.PublicKey
// msg to sign
msg := []byte("message")
sign, err := privkey.Sign(msg)
if err != nil {
panic(err)
}
// verify with sdk pubkey
isVerified := pubkey.VerifySignature(msg, sign)
fmt.Println("normal verification:", isVerified)
// verify with gnark pubkey
isVerified, err = gnarkPubkey.Verify(sign, msg, sha256.New())
if err != nil {
panic(err)
}
fmt.Println("gnark verification:", isVerified)
}
but we need to expose scalar
in
gnark-crypto/ecc/secp256k1/ecdsa/ecdsa.go
Line 62 in 564b6f7
diff --git a/ecc/secp256k1/ecdsa/ecdsa.go b/ecc/secp256k1/ecdsa/ecdsa.go
index 689bc5003..77efdd352 100644
--- a/ecc/secp256k1/ecdsa/ecdsa.go
+++ b/ecc/secp256k1/ecdsa/ecdsa.go
@@ -59,7 +59,7 @@ type PublicKey struct {
// PrivateKey represents an ECDSA private key
type PrivateKey struct {
PublicKey PublicKey
- scalar [sizeFr]byte // secret scalar, in big Endian
+ Scalar [sizeFr]byte // secret scalar, in big Endian
}
// Signature represents an ECDSA signature
@@ -96,7 +96,7 @@ func GenerateKey(rand io.Reader) (*PrivateKey, error) {
_, g := secp256k1.Generators()
privateKey := new(PrivateKey)
- k.FillBytes(privateKey.scalar[:sizeFr])
+ k.FillBytes(privateKey.Scalar[:sizeFr])
privateKey.PublicKey.A.ScalarMultiplication(&g, k)
return privateKey, nil
}
@@ -178,7 +178,7 @@ const (
func nonce(privateKey *PrivateKey, hash []byte) (csprng *cipher.StreamReader, err error) {
// This implementation derives the nonce from an AES-CTR CSPRNG keyed by:
//
- // SHA2-512(privateKey.scalar ∥ entropy ∥ hash)[:32]
+ // SHA2-512(privateKey.Scalar ∥ entropy ∥ hash)[:32]
//
// The CSPRNG key is indifferentiable from a random oracle as shown in
// [Coron], the AES-CTR stream is indifferentiable from a random oracle
@@ -197,7 +197,7 @@ func nonce(privateKey *PrivateKey, hash []byte) (csprng *cipher.StreamReader, er
// Initialize an SHA-512 hash context; digest...
md := sha512.New()
- md.Write(privateKey.scalar[:sizeFr]) // the private key,
+ md.Write(privateKey.Scalar[:sizeFr]) // the private key,
md.Write(entropy) // the entropy,
md.Write(hash) // and the input hash;
key := md.Sum(nil)[:32] // and compute ChopMD-256(SHA-512),
@@ -247,7 +247,7 @@ func (privKey *PrivateKey) SignForRecover(message []byte, hFunc hash.Hash) (v ui
r, s = new(big.Int), new(big.Int)
scalar, kInv := new(big.Int), new(big.Int)
- scalar.SetBytes(privKey.scalar[:sizeFr])
+ scalar.SetBytes(privKey.Scalar[:sizeFr])
for {
for {
csprng, err := nonce(privKey, message)
diff --git a/ecc/secp256k1/ecdsa/marshal.go b/ecc/secp256k1/ecdsa/marshal.go
index bc2be013c..1341e2576 100644
--- a/ecc/secp256k1/ecdsa/marshal.go
+++ b/ecc/secp256k1/ecdsa/marshal.go
@@ -98,7 +98,7 @@ func (privKey *PrivateKey) Bytes() []byte {
var res [sizePrivateKey]byte
pubkBin := privKey.PublicKey.A.RawBytes()
subtle.ConstantTimeCopy(1, res[:sizePublicKey], pubkBin[:])
- subtle.ConstantTimeCopy(1, res[sizePublicKey:sizePrivateKey], privKey.scalar[:])
+ subtle.ConstantTimeCopy(1, res[sizePublicKey:sizePrivateKey], privKey.Scalar[:])
return res[:]
}
@@ -116,7 +116,7 @@ func (privKey *PrivateKey) SetBytes(buf []byte) (int, error) {
return 0, err
}
n += sizePublicKey
- subtle.ConstantTimeCopy(1, privKey.scalar[:], buf[sizePublicKey:sizePrivateKey])
+ subtle.ConstantTimeCopy(1, privKey.Scalar[:], buf[sizePublicKey:sizePrivateKey])
n += sizeFr
return n, nil
}
diff --git a/ecc/secp256k1/ecdsa/marshal_test.go b/ecc/secp256k1/ecdsa/marshal_test.go
index a880e6301..7a6b6c776 100644
--- a/ecc/secp256k1/ecdsa/marshal_test.go
+++ b/ecc/secp256k1/ecdsa/marshal_test.go
@@ -55,7 +55,7 @@ func TestSerialization(t *testing.T) {
return false
}
- return end.PublicKey.Equal(&privKey.PublicKey) && subtle.ConstantTimeCompare(end.scalar[:], privKey.scalar[:]) == 1
+ return end.PublicKey.Equal(&privKey.PublicKey) && subtle.ConstantTimeCompare(end.Scalar[:], privKey.Scalar[:]) == 1
},
))
thanks for the reply @yelhousni. It's working.
It would be great if I can check compatibility of sha256 and merkleproofs. Can you cite some examples if possible? thank you
#508