Skip to content
This repository has been archived by the owner on Oct 4, 2019. It is now read-only.

Commit

Permalink
problem: nondeterministic K-value in ECDSA algo
Browse files Browse the repository at this point in the history
solution: move to a deterministic k-value using RFC6979

rel ethereum/go-ethereum#2190
rel ethereum/go-ethereum#3561
rel #245
  • Loading branch information
whilei committed Apr 14, 2018
1 parent 480f90a commit 876296f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
3 changes: 3 additions & 0 deletions crypto/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func TestSign(t *testing.T) {
if err != nil {
t.Errorf("Sign error: %s", err)
}
if len(sig) != 65 {
t.Error("wrong len sig", len(sig))
}
recoveredPub, err := Ecrecover(msg, sig)
if err != nil {
t.Errorf("ECRecover error: %s", err)
Expand Down
45 changes: 44 additions & 1 deletion crypto/secp256k1/secp256.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ var (
ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
ErrInvalidSignatureLen = errors.New("invalid signature length")
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
ErrInvalidKey = errors.New("invalid private key")
ErrInvalidPubkey = errors.New("invalid public key")
ErrSignFailed = errors.New("signing failed")
ErrRecoverFailed = errors.New("recovery failed")
)

func GenerateKeyPair() ([]byte, []byte) {
Expand Down Expand Up @@ -132,7 +136,9 @@ func GeneratePubKey(seckey []byte) ([]byte, error) {
return pubkey, nil
}

func Sign(msg []byte, seckey []byte) ([]byte, error) {
// SignNondeterministic generates nondeterministic signature b/c of a random k-value in the ECDSA algorithm. This function is included
// only for purpose of demonstration and comparison with the deterministic Sign function.
func SignNondeterministic(msg []byte, seckey []byte) ([]byte, error) {
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))

Expand Down Expand Up @@ -178,6 +184,43 @@ func Sign(msg []byte, seckey []byte) ([]byte, error) {

}

// Sign creates a recoverable ECDSA signature.
// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1.
//
// The caller is responsible for ensuring that msg cannot be chosen
// directly by an attacker. It is usually preferable to use a cryptographic
// hash function on any input before handing it to this function.
func Sign(msg []byte, seckey []byte) ([]byte, error) {
if len(msg) != 32 {
return nil, ErrInvalidMsgLen
}
if len(seckey) != 32 {
return nil, ErrInvalidKey
}
seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0]))
if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 {
return nil, ErrInvalidKey
}

var (
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
noncefunc = C.secp256k1_nonce_function_rfc6979
sigstruct C.secp256k1_ecdsa_recoverable_signature
)
if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, nil) == 0 {
return nil, ErrSignFailed
}

var (
sig = make([]byte, 65)
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
recid C.int
)
C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct)
sig[64] = byte(recid) // add back recid to get 65 bytes sig
return sig, nil
}

func VerifySeckeyValidity(seckey []byte) error {
if len(seckey) != 32 {
return errors.New("priv key is not 32 bytes")
Expand Down
53 changes: 53 additions & 0 deletions crypto/secp256k1/secp256_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/hex"
"testing"

"github.com/ethereumproject/go-ethereum/common/hexutil"
"github.com/ethereumproject/go-ethereum/crypto/randentropy"
)

Expand Down Expand Up @@ -67,6 +68,58 @@ func TestInvalidRecoveryID(t *testing.T) {
}
}

func TestSignDeterministic(t *testing.T) {
_, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig1, err := Sign(msg, seckey)
if err != nil {
t.Errorf("signature 1 error: %s", err)
}
sig2, err := Sign(msg, seckey)
if err != nil {
t.Errorf("signature 2 error: %s", err)
}
if !bytes.Equal(sig1, sig2) {
t.Fatal("sig1 != sig2", hexutil.Encode(sig1), hexutil.Encode(sig2))
}
}

// TestSignNondeterministic shows that TestSignNondeterministic does not generate deterministic signatures.
func TestSignNondeterministic(t *testing.T) {
_, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig1, err := SignNondeterministic(msg, seckey)
if err != nil {
t.Errorf("signature 1 error: %s", err)
}
sig2, err := SignNondeterministic(msg, seckey)
if err != nil {
t.Errorf("signature 2 error: %s", err)
}
// Of course the signatures should be the same (1:1), but they are not.
if bytes.Equal(sig1, sig2) {
t.Error("sig1 == sig2", hexutil.Encode(sig1), hexutil.Encode(sig2))
}
}

// TestSignNondeterministic2 shows that TestSignNondeterministic does not generate the same signature as deterministic Sign.
func TestSignNondeterministic2(t *testing.T) {
_, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig1, err := SignNondeterministic(msg, seckey)
if err != nil {
t.Errorf("signature 1 error: %s", err)
}
sig2, err := Sign(msg, seckey)
if err != nil {
t.Errorf("signature 2 error: %s", err)
}
// Of course the signatures should be the same (1:1), but they are not.
if bytes.Equal(sig1, sig2) {
t.Error("sig1 == sig2", hexutil.Encode(sig1), hexutil.Encode(sig2))
}
}

func TestSignAndRecover(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
Expand Down

0 comments on commit 876296f

Please sign in to comment.