-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Optimize secp256k1 hashing * Add ADR-028 related functions * Update ed25519 * fix errors/handle * fix build * fix build * Add tests and update function names * wip * Use LengthPrefix for composed addresses * add tests for NewComposed * add module hash function * fix append * rollback ed25519 ADR-28 update * rollback ed25519 ADR-28 test * Adding Module tests and convert tests to test suite * convert store_key_test.go to test suite * rollback test check comment * any.pb.go update * generated proto files * wip * renames * wip2 * add String method to PBBytes * wip3 * add pubkey tests * adding cryptotypes.PrivKey methods * re-enable test * fix equals test * fix ecdsa object receiver * add ProtoMarshaler implementation and tests * move code to init and add interface registry * add bytes tests * merge Unmarshal with UnmarshalAmino * implement ProtoMarshaler to ecdsaSK * remove bytes.go * add private key marshaling tests * break tests into 2 suites * add signature tests * remove TODO * remove bytes.proto * adding changelog * Update CHANGELOG.md * Update crypto/keys/ecdsa/ecdsa_privkey.go * Update crypto/keys/ecdsa/ecdsa_pubkey.go * comments: add dot (.) at the end * update comments * update commented code * rename files * remove Amino methods * use 2 spaces in protocgen.sh * rollback changes in protocgen.sh * add MessageName * rework ecdsa proto structure * move ecdsa to internal package * add secp256r1 proto * refactore proto definition for secp256r1 * fix err check * update comments * create const for fieldSize+1 * simplify the PubKey.String test * Apply suggestions from code review Co-authored-by: Jonathan Gimeno <jgimeno@gmail.com> * Update doc comments: SDK Interface -> sdk.Interface * rename init.go to doc.go * Add PubKey.Type() test * Revert "Update doc comments: SDK Interface -> sdk.Interface" This reverts commit 01f2b4f5efcd79a452483bcda152db54a8fbfee2. * Use cryptotypes.Address instead of tmcrypto * Revert "Use cryptotypes.Address instead of tmcrypto" This reverts commit 15b866ae67bdb7ca4872f4089fcab19f9e2e3608. This issue will be solved in cosmos/cosmos-sdk#8775 * add link to ANSI X9.62 * move init.go -> doc.go * use proto.MessageName() Co-authored-by: Alessio Treglia <alessio@tendermint.com> Co-authored-by: Jonathan Gimeno <jgimeno@gmail.com>
- Loading branch information
Showing
16 changed files
with
1,268 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package ECDSA implements Cosmos-SDK compatible ECDSA public and private key. The keys | ||
// can be serialized. | ||
package ecdsa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package ecdsa | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/sha256" | ||
"fmt" | ||
"math/big" | ||
) | ||
|
||
// GenPrivKey generates a new secp256r1 private key. It uses operating system randomness. | ||
func GenPrivKey(curve elliptic.Curve) (PrivKey, error) { | ||
key, err := ecdsa.GenerateKey(curve, rand.Reader) | ||
if err != nil { | ||
return PrivKey{}, err | ||
} | ||
return PrivKey{*key}, nil | ||
} | ||
|
||
type PrivKey struct { | ||
ecdsa.PrivateKey | ||
} | ||
|
||
// PubKey returns ECDSA public key associated with this private key. | ||
func (sk *PrivKey) PubKey() PubKey { | ||
return PubKey{sk.PublicKey, nil} | ||
} | ||
|
||
// Bytes serialize the private key using big-endian. | ||
func (sk *PrivKey) Bytes() []byte { | ||
if sk == nil { | ||
return nil | ||
} | ||
fieldSize := (sk.Curve.Params().BitSize + 7) / 8 | ||
bz := make([]byte, fieldSize) | ||
sk.D.FillBytes(bz) | ||
return bz | ||
} | ||
|
||
// Sign hashes and signs the message usign ECDSA. Implements SDK PrivKey interface. | ||
func (sk *PrivKey) Sign(msg []byte) ([]byte, error) { | ||
digest := sha256.Sum256(msg) | ||
return sk.PrivateKey.Sign(rand.Reader, digest[:], nil) | ||
} | ||
|
||
// String returns a string representation of the public key based on the curveName. | ||
func (sk *PrivKey) String(name string) string { | ||
return name + "{-}" | ||
} | ||
|
||
// MarshalTo implements proto.Marshaler interface. | ||
func (sk *PrivKey) MarshalTo(dAtA []byte) (int, error) { | ||
bz := sk.Bytes() | ||
copy(dAtA, bz) | ||
return len(bz), nil | ||
} | ||
|
||
// Unmarshal implements proto.Marshaler interface. | ||
func (sk *PrivKey) Unmarshal(bz []byte, curve elliptic.Curve, expectedSize int) error { | ||
if len(bz) != expectedSize { | ||
return fmt.Errorf("wrong ECDSA SK bytes, expecting %d bytes", expectedSize) | ||
} | ||
|
||
sk.Curve = curve | ||
sk.D = new(big.Int).SetBytes(bz) | ||
sk.X, sk.Y = curve.ScalarBaseMult(bz) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package ecdsa | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/tendermint/tendermint/crypto" | ||
|
||
"github.com/stretchr/testify/suite" | ||
) | ||
|
||
func TestSKSuite(t *testing.T) { | ||
suite.Run(t, new(SKSuite)) | ||
} | ||
|
||
type SKSuite struct{ CommonSuite } | ||
|
||
func (suite *SKSuite) TestString() { | ||
const prefix = "abc" | ||
suite.Require().Equal(prefix+"{-}", suite.sk.String(prefix)) | ||
} | ||
|
||
func (suite *SKSuite) TestPubKey() { | ||
pk := suite.sk.PubKey() | ||
suite.True(suite.sk.PublicKey.Equal(&pk.PublicKey)) | ||
} | ||
|
||
func (suite *SKSuite) Bytes() { | ||
bz := suite.sk.Bytes() | ||
suite.Len(bz, 32) | ||
var sk *PrivKey | ||
suite.Nil(sk.Bytes()) | ||
} | ||
|
||
func (suite *SKSuite) TestMarshal() { | ||
require := suite.Require() | ||
const size = 32 | ||
|
||
var buffer = make([]byte, size) | ||
suite.sk.MarshalTo(buffer) | ||
|
||
var sk = new(PrivKey) | ||
err := sk.Unmarshal(buffer, secp256r1, size) | ||
require.NoError(err) | ||
require.True(sk.Equal(&suite.sk.PrivateKey)) | ||
} | ||
|
||
func (suite *SKSuite) TestSign() { | ||
require := suite.Require() | ||
|
||
msg := crypto.CRandBytes(1000) | ||
sig, err := suite.sk.Sign(msg) | ||
require.NoError(err) | ||
sigCpy := make([]byte, len(sig)) | ||
copy(sigCpy, sig) | ||
require.True(suite.pk.VerifySignature(msg, sigCpy)) | ||
|
||
// Mutate the signature | ||
for i := range sig { | ||
sigCpy[i] ^= byte(i + 1) | ||
require.False(suite.pk.VerifySignature(msg, sigCpy)) | ||
} | ||
|
||
// Mutate the message | ||
msg[1] ^= byte(2) | ||
require.False(suite.pk.VerifySignature(msg, sig)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package ecdsa | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/sha256" | ||
"encoding/asn1" | ||
"fmt" | ||
"math/big" | ||
|
||
tmcrypto "github.com/tendermint/tendermint/crypto" | ||
|
||
"github.com/cosmos/cosmos-sdk/types/address" | ||
"github.com/cosmos/cosmos-sdk/types/errors" | ||
) | ||
|
||
// signature holds the r and s values of an ECDSA signature. | ||
type signature struct { | ||
R, S *big.Int | ||
} | ||
|
||
type PubKey struct { | ||
ecdsa.PublicKey | ||
|
||
// cache | ||
address tmcrypto.Address | ||
} | ||
|
||
// Address creates an ADR-28 address for ECDSA keys. protoName is a concrete proto structure id. | ||
func (pk *PubKey) Address(protoName string) tmcrypto.Address { | ||
if pk.address == nil { | ||
pk.address = address.Hash(protoName, pk.Bytes()) | ||
} | ||
return pk.address | ||
} | ||
|
||
// Bytes returns the byte representation of the public key using a compressed form | ||
// specified in section 4.3.6 of ANSI X9.62 with first byte being the curve type. | ||
func (pk *PubKey) Bytes() []byte { | ||
if pk == nil { | ||
return nil | ||
} | ||
return elliptic.MarshalCompressed(pk.Curve, pk.X, pk.Y) | ||
} | ||
|
||
// VerifySignature checks if sig is a valid ECDSA signature for msg. | ||
func (pk *PubKey) VerifySignature(msg []byte, sig []byte) bool { | ||
s := new(signature) | ||
if _, err := asn1.Unmarshal(sig, s); err != nil || s == nil { | ||
return false | ||
} | ||
|
||
h := sha256.Sum256(msg) | ||
return ecdsa.Verify(&pk.PublicKey, h[:], s.R, s.S) | ||
} | ||
|
||
// String returns a string representation of the public key based on the curveName. | ||
func (pk *PubKey) String(curveName string) string { | ||
return fmt.Sprintf("%s{%X}", curveName, pk.Bytes()) | ||
} | ||
|
||
// **** Proto Marshaler **** | ||
|
||
// MarshalTo implements proto.Marshaler interface. | ||
func (pk *PubKey) MarshalTo(dAtA []byte) (int, error) { | ||
bz := pk.Bytes() | ||
copy(dAtA, bz) | ||
return len(bz), nil | ||
} | ||
|
||
// Unmarshal implements proto.Marshaler interface. | ||
func (pk *PubKey) Unmarshal(bz []byte, curve elliptic.Curve, expectedSize int) error { | ||
if len(bz) != expectedSize { | ||
return errors.Wrapf(errors.ErrInvalidPubKey, "wrong ECDSA PK bytes, expecting %d bytes, got %d", expectedSize, len(bz)) | ||
} | ||
cpk := ecdsa.PublicKey{Curve: curve} | ||
cpk.X, cpk.Y = elliptic.UnmarshalCompressed(curve, bz) | ||
if cpk.X == nil || cpk.Y == nil { | ||
return errors.Wrapf(errors.ErrInvalidPubKey, "wrong ECDSA PK bytes, unknown curve type: %d", bz[0]) | ||
} | ||
pk.PublicKey = cpk | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package ecdsa | ||
|
||
import ( | ||
"crypto/elliptic" | ||
"encoding/hex" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/suite" | ||
) | ||
|
||
var secp256r1 = elliptic.P256() | ||
|
||
func GenSecp256r1() (PrivKey, error) { | ||
return GenPrivKey(secp256r1) | ||
} | ||
|
||
func TestPKSuite(t *testing.T) { | ||
suite.Run(t, new(PKSuite)) | ||
} | ||
|
||
type CommonSuite struct { | ||
suite.Suite | ||
pk PubKey | ||
sk PrivKey | ||
} | ||
|
||
func (suite *CommonSuite) SetupSuite() { | ||
sk, err := GenSecp256r1() | ||
suite.Require().NoError(err) | ||
suite.sk = sk | ||
suite.pk = sk.PubKey() | ||
} | ||
|
||
type PKSuite struct{ CommonSuite } | ||
|
||
func (suite *PKSuite) TestString() { | ||
assert := suite.Assert() | ||
require := suite.Require() | ||
|
||
prefix := "abc" | ||
pkStr := suite.pk.String(prefix) | ||
assert.Equal(prefix+"{", pkStr[:len(prefix)+1]) | ||
assert.EqualValues('}', pkStr[len(pkStr)-1]) | ||
|
||
bz, err := hex.DecodeString(pkStr[len(prefix)+1 : len(pkStr)-1]) | ||
require.NoError(err) | ||
assert.EqualValues(suite.pk.Bytes(), bz) | ||
} | ||
|
||
func (suite *PKSuite) TestBytes() { | ||
require := suite.Require() | ||
var pk *PubKey | ||
require.Nil(pk.Bytes()) | ||
} | ||
|
||
func (suite *PKSuite) TestMarshal() { | ||
require := suite.Require() | ||
const size = 33 // secp256r1 size | ||
|
||
var buffer = make([]byte, size) | ||
n, err := suite.pk.MarshalTo(buffer) | ||
require.NoError(err) | ||
require.Equal(size, n) | ||
|
||
var pk = new(PubKey) | ||
err = pk.Unmarshal(buffer, secp256r1, size) | ||
require.NoError(err) | ||
require.True(pk.PublicKey.Equal(&suite.pk.PublicKey)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Package secp256r1 implements Cosmos-SDK compatible ECDSA public and private key. The keys | ||
// can be protobuf serialized and packed in Any. | ||
package secp256r1 | ||
|
||
import ( | ||
"crypto/elliptic" | ||
"fmt" | ||
|
||
codectypes "github.com/cosmos/cosmos-sdk/codec/types" | ||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" | ||
) | ||
|
||
const ( | ||
// fieldSize is the curve domain size. | ||
fieldSize = 32 | ||
pubKeySize = fieldSize + 1 | ||
|
||
name = "secp256r1" | ||
) | ||
|
||
var secp256r1 elliptic.Curve | ||
|
||
func init() { | ||
secp256r1 = elliptic.P256() | ||
// pubKeySize is ceil of field bit size + 1 for the sign | ||
expected := (secp256r1.Params().BitSize + 7) / 8 | ||
if expected != fieldSize { | ||
panic(fmt.Sprintf("Wrong secp256r1 curve fieldSize=%d, expecting=%d", fieldSize, expected)) | ||
} | ||
} | ||
|
||
// RegisterInterfaces adds secp256r1 PubKey to pubkey registry | ||
func RegisterInterfaces(registry codectypes.InterfaceRegistry) { | ||
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &PubKey{}) | ||
} |
Oops, something went wrong.