crypto: add secp256r1 (#8559)

* 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 https://github.com/cosmos/cosmos-sdk/issues/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>
This commit is contained in:
Robert Zaremba 2021-03-04 13:29:48 +01:00 committed by GitHub
parent eef8d4dc5d
commit c66f1f7efe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1269 additions and 9 deletions

View File

@ -36,6 +36,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
## Features
* [\#8559](https://github.com/cosmos/cosmos-sdk/pull/8559) Adding Protobuf compatible secp256r1 ECDSA signatures.
### Client Breaking Changes
* [\#8363](https://github.com/cosmos/cosmos-sdk/pull/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address.
@ -137,7 +141,7 @@ he Cosmos Hub) should not use this release or any release in the v0.41.x series.
### State Machine Breaking
* (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON support for IBC MsgTransfer in order to support Ledger text signing transfer transactions.
* (x/ibc) [\#8404](https://github.com/cosmos/cosmos-sdk/pull/8404) Reorder IBC `ChanOpenAck` and `ChanOpenConfirm` handler execution to perform core handler first, followed by application callbacks.
* (x/ibc) [\#8404](https://github.com/cosmos/cosmos-sdk/pull/8404) Reorder IBC `ChanOpenAck` and `ChanOpenConfirm` handler execution to perform core handler first, followed by application callbacks.
### Bug Fixes

View File

@ -476,10 +476,7 @@ func (m *Any) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthAny
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthAny
}
if (iNdEx + skippy) > l {

View File

@ -5,13 +5,16 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
// RegisterInterfaces registers the sdk.Tx interface.
func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterInterface("cosmos.crypto.PubKey", (*cryptotypes.PubKey)(nil))
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &ed25519.PubKey{})
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &secp256k1.PubKey{})
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &multisig.LegacyAminoPubKey{})
var pk *cryptotypes.PubKey
registry.RegisterInterface("cosmos.crypto.PubKey", pk)
registry.RegisterImplementations(pk, &ed25519.PubKey{})
registry.RegisterImplementations(pk, &secp256k1.PubKey{})
registry.RegisterImplementations(pk, &multisig.LegacyAminoPubKey{})
secp256r1.RegisterInterfaces(registry)
}

View File

@ -0,0 +1,3 @@
// Package ECDSA implements Cosmos-SDK compatible ECDSA public and private key. The keys
// can be serialized.
package ecdsa

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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{})
}

View File

@ -0,0 +1,503 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: cosmos/crypto/secp256r1/keys.proto
package secp256r1
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// PubKey defines a secp256r1 ECDSA public key.
type PubKey struct {
// Point on secp256r1 curve in a compressed representation as specified in section
// 4.3.6 of ANSI X9.62.
Key *ecdsaPK `protobuf:"bytes,1,opt,name=key,proto3,customtype=ecdsaPK" json:"key,omitempty"`
}
func (m *PubKey) Reset() { *m = PubKey{} }
func (*PubKey) ProtoMessage() {}
func (*PubKey) Descriptor() ([]byte, []int) {
return fileDescriptor_b90c18415095c0c3, []int{0}
}
func (m *PubKey) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PubKey.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PubKey) XXX_Merge(src proto.Message) {
xxx_messageInfo_PubKey.Merge(m, src)
}
func (m *PubKey) XXX_Size() int {
return m.Size()
}
func (m *PubKey) XXX_DiscardUnknown() {
xxx_messageInfo_PubKey.DiscardUnknown(m)
}
var xxx_messageInfo_PubKey proto.InternalMessageInfo
func (*PubKey) XXX_MessageName() string {
return "cosmos.crypto.secp256r1.PubKey"
}
// PrivKey defines a secp256r1 ECDSA private key.
type PrivKey struct {
// secret number serialized using big-endian encoding
Secret *ecdsaSK `protobuf:"bytes,1,opt,name=secret,proto3,customtype=ecdsaSK" json:"secret,omitempty"`
}
func (m *PrivKey) Reset() { *m = PrivKey{} }
func (*PrivKey) ProtoMessage() {}
func (*PrivKey) Descriptor() ([]byte, []int) {
return fileDescriptor_b90c18415095c0c3, []int{1}
}
func (m *PrivKey) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PrivKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PrivKey.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PrivKey) XXX_Merge(src proto.Message) {
xxx_messageInfo_PrivKey.Merge(m, src)
}
func (m *PrivKey) XXX_Size() int {
return m.Size()
}
func (m *PrivKey) XXX_DiscardUnknown() {
xxx_messageInfo_PrivKey.DiscardUnknown(m)
}
var xxx_messageInfo_PrivKey proto.InternalMessageInfo
func (*PrivKey) XXX_MessageName() string {
return "cosmos.crypto.secp256r1.PrivKey"
}
func init() {
proto.RegisterType((*PubKey)(nil), "cosmos.crypto.secp256r1.PubKey")
proto.RegisterType((*PrivKey)(nil), "cosmos.crypto.secp256r1.PrivKey")
}
func init() {
proto.RegisterFile("cosmos/crypto/secp256r1/keys.proto", fileDescriptor_b90c18415095c0c3)
}
var fileDescriptor_b90c18415095c0c3 = []byte{
// 221 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0xce, 0x2f, 0xce,
0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x2f, 0x4e, 0x4d, 0x2e, 0x30, 0x32,
0x35, 0x2b, 0x32, 0xd4, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12,
0x87, 0xa8, 0xd1, 0x83, 0xa8, 0xd1, 0x83, 0xab, 0x91, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab,
0xd1, 0x07, 0xb1, 0x20, 0xca, 0x95, 0xd4, 0xb9, 0xd8, 0x02, 0x4a, 0x93, 0xbc, 0x53, 0x2b, 0x85,
0x64, 0xb9, 0x98, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x9c, 0xb8, 0x6f, 0xdd,
0x93, 0x67, 0x4f, 0x4d, 0x4e, 0x29, 0x4e, 0x0c, 0xf0, 0x0e, 0x02, 0x89, 0x2b, 0xe9, 0x71, 0xb1,
0x07, 0x14, 0x65, 0x96, 0x81, 0x54, 0x2a, 0x73, 0xb1, 0x15, 0xa7, 0x26, 0x17, 0xa5, 0x96, 0x60,
0x28, 0x0e, 0xf6, 0x0e, 0x82, 0x4a, 0x39, 0x45, 0x9c, 0x78, 0x28, 0xc7, 0x70, 0xe3, 0xa1, 0x1c,
0xc3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1,
0x1c, 0xc3, 0x89, 0xc7, 0x72, 0x8c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65,
0x94, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0xf3, 0x1c, 0x98, 0xd2,
0x2d, 0x4e, 0xc9, 0x86, 0xf9, 0x13, 0xe4, 0x3b, 0x84, 0x67, 0x93, 0xd8, 0xc0, 0x2e, 0x37, 0x06,
0x04, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x65, 0x08, 0x5c, 0x0e, 0x01, 0x00, 0x00,
}
func (m *PubKey) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PubKey) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Key != nil {
{
size := m.Key.Size()
i -= size
if _, err := m.Key.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintKeys(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *PrivKey) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PrivKey) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PrivKey) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Secret != nil {
{
size := m.Secret.Size()
i -= size
if _, err := m.Secret.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintKeys(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintKeys(dAtA []byte, offset int, v uint64) int {
offset -= sovKeys(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *PubKey) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Key != nil {
l = m.Key.Size()
n += 1 + l + sovKeys(uint64(l))
}
return n
}
func (m *PrivKey) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Secret != nil {
l = m.Secret.Size()
n += 1 + l + sovKeys(uint64(l))
}
return n
}
func sovKeys(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozKeys(x uint64) (n int) {
return sovKeys(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *PubKey) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKeys
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PubKey: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PubKey: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKeys
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthKeys
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthKeys
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
var v ecdsaPK
m.Key = &v
if err := m.Key.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipKeys(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthKeys
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *PrivKey) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKeys
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PrivKey: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PrivKey: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKeys
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthKeys
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthKeys
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
var v ecdsaSK
m.Secret = &v
if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipKeys(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthKeys
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipKeys(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowKeys
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowKeys
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowKeys
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthKeys
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupKeys
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthKeys
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthKeys = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowKeys = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupKeys = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,63 @@
package secp256r1
import (
"github.com/cosmos/cosmos-sdk/crypto/keys/internal/ecdsa"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
// GenPrivKey generates a new secp256r1 private key. It uses operating system randomness.
func GenPrivKey() (*PrivKey, error) {
key, err := ecdsa.GenPrivKey(secp256r1)
return &PrivKey{&ecdsaSK{key}}, err
}
// PubKey implements SDK PrivKey interface.
func (m *PrivKey) PubKey() cryptotypes.PubKey {
return &PubKey{&ecdsaPK{m.Secret.PubKey()}}
}
// String implements SDK proto.Message interface.
func (m *PrivKey) String() string {
return m.Secret.String(name)
}
// Type returns key type name. Implements SDK PrivKey interface.
func (m *PrivKey) Type() string {
return name
}
// Sign hashes and signs the message usign ECDSA. Implements sdk.PrivKey interface.
func (m *PrivKey) Sign(msg []byte) ([]byte, error) {
return m.Secret.Sign(msg)
}
// Bytes serialize the private key.
func (m *PrivKey) Bytes() []byte {
return m.Secret.Bytes()
}
// Equals implements SDK PrivKey interface.
func (m *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool {
sk2, ok := other.(*PrivKey)
if !ok {
return false
}
return m.Secret.Equal(&sk2.Secret.PrivateKey)
}
type ecdsaSK struct {
ecdsa.PrivKey
}
// Size implements proto.Marshaler interface
func (sk *ecdsaSK) Size() int {
if sk == nil {
return 0
}
return fieldSize
}
// Unmarshal implements proto.Marshaler interface
func (sk *ecdsaSK) Unmarshal(bz []byte) error {
return sk.PrivKey.Unmarshal(bz, secp256r1, fieldSize)
}

View File

@ -0,0 +1,115 @@
package secp256r1
import (
"testing"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
proto "github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
)
var _ cryptotypes.PrivKey = &PrivKey{}
func TestSKSuite(t *testing.T) {
suite.Run(t, new(SKSuite))
}
type SKSuite struct{ CommonSuite }
func (suite *SKSuite) TestString() {
suite.Require().Equal("secp256r1{-}", suite.sk.String())
}
func (suite *SKSuite) TestEquals() {
require := suite.Require()
skOther, err := GenPrivKey()
require.NoError(err)
require.False(suite.sk.Equals(skOther))
skOther2 := &PrivKey{skOther.Secret}
require.True(skOther.Equals(skOther2))
require.True(skOther2.Equals(skOther), "Equals must be reflexive")
}
func (suite *SKSuite) TestPubKey() {
pk := suite.sk.PubKey()
suite.True(suite.sk.(*PrivKey).Secret.PublicKey.Equal(&pk.(*PubKey).Key.PublicKey))
}
func (suite *SKSuite) Bytes() {
bz := suite.sk.Bytes()
suite.Len(bz, fieldSize)
var sk *PrivKey
suite.Nil(sk.Bytes())
}
func (suite *SKSuite) TestMarshalProto() {
require := suite.Require()
/**** test structure marshalling ****/
var sk PrivKey
bz, err := proto.Marshal(suite.sk)
require.NoError(err)
require.NoError(proto.Unmarshal(bz, &sk))
require.True(sk.Equals(suite.sk))
/**** test structure marshalling with codec ****/
sk = PrivKey{}
registry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
bz, err = cdc.MarshalBinaryBare(suite.sk.(*PrivKey))
require.NoError(err)
require.NoError(cdc.UnmarshalBinaryBare(bz, &sk))
require.True(sk.Equals(suite.sk))
const bufSize = 100
bz2 := make([]byte, bufSize)
skCpy := suite.sk.(*PrivKey)
_, err = skCpy.MarshalTo(bz2)
require.NoError(err)
require.Len(bz2, bufSize)
require.Equal(bz, bz2[:sk.Size()])
bz2 = make([]byte, bufSize)
_, err = skCpy.MarshalToSizedBuffer(bz2)
require.NoError(err)
require.Len(bz2, bufSize)
require.Equal(bz, bz2[(bufSize-sk.Size()):])
}
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))
}
func (suite *SKSuite) TestSize() {
require := suite.Require()
var pk ecdsaSK
require.Equal(pk.Size(), len(suite.sk.Bytes()))
var nilPk *ecdsaSK
require.Equal(0, nilPk.Size(), "nil value must have zero size")
}

View File

@ -0,0 +1,60 @@
package secp256r1
import (
"github.com/gogo/protobuf/proto"
tmcrypto "github.com/tendermint/tendermint/crypto"
ecdsa "github.com/cosmos/cosmos-sdk/crypto/keys/internal/ecdsa"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
// String implements proto.Message interface.
func (m *PubKey) String() string {
return m.Key.String(name)
}
// Bytes implements SDK PubKey interface.
func (m *PubKey) Bytes() []byte {
return m.Key.Bytes()
}
// Equals implements SDK PubKey interface.
func (m *PubKey) Equals(other cryptotypes.PubKey) bool {
pk2, ok := other.(*PubKey)
if !ok {
return false
}
return m.Key.Equal(&pk2.Key.PublicKey)
}
// Address implements SDK PubKey interface.
func (m *PubKey) Address() tmcrypto.Address {
return m.Key.Address(proto.MessageName(m))
}
// Type returns key type name. Implements SDK PubKey interface.
func (m *PubKey) Type() string {
return name
}
// VerifySignature implements SDK PubKey interface.
func (m *PubKey) VerifySignature(msg []byte, sig []byte) bool {
return m.Key.VerifySignature(msg, sig)
}
type ecdsaPK struct {
ecdsa.PubKey
}
// Size implements proto.Marshaler interface
func (pk *ecdsaPK) Size() int {
if pk == nil {
return 0
}
return pubKeySize
}
// Unmarshal implements proto.Marshaler interface
func (pk *ecdsaPK) Unmarshal(bz []byte) error {
return pk.PubKey.Unmarshal(bz, secp256r1, pubKeySize)
}

View File

@ -0,0 +1,118 @@
package secp256r1
import (
"testing"
proto "github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
var _ cryptotypes.PubKey = (*PubKey)(nil)
func TestPKSuite(t *testing.T) {
suite.Run(t, new(PKSuite))
}
type CommonSuite struct {
suite.Suite
pk *PubKey // cryptotypes.PubKey
sk cryptotypes.PrivKey
}
func (suite *CommonSuite) SetupSuite() {
sk, err := GenPrivKey()
suite.Require().NoError(err)
suite.sk = sk
suite.pk = sk.PubKey().(*PubKey)
}
type PKSuite struct{ CommonSuite }
func (suite *PKSuite) TestString() {
require := suite.Require()
pkStr := suite.pk.String()
prefix := "secp256r1{"
require.Equal(prefix, pkStr[:len(prefix)])
}
func (suite *PKSuite) TestType() {
suite.Require().Equal(name, suite.pk.Type())
}
func (suite *PKSuite) TestEquals() {
require := suite.Require()
skOther, err := GenPrivKey()
require.NoError(err)
pkOther := skOther.PubKey()
pkOther2 := &PubKey{&ecdsaPK{skOther.Secret.PubKey()}}
require.False(suite.pk.Equals(pkOther))
require.True(pkOther.Equals(pkOther2))
require.True(pkOther2.Equals(pkOther))
require.True(pkOther.Equals(pkOther), "Equals must be reflexive")
}
func (suite *PKSuite) TestMarshalProto() {
require := suite.Require()
/**** test structure marshalling ****/
var pk PubKey
bz, err := proto.Marshal(suite.pk)
require.NoError(err)
require.NoError(proto.Unmarshal(bz, &pk))
require.True(pk.Equals(suite.pk))
/**** test structure marshalling with codec ****/
pk = PubKey{}
registry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
bz, err = cdc.MarshalBinaryBare(suite.pk)
require.NoError(err)
require.NoError(cdc.UnmarshalBinaryBare(bz, &pk))
require.True(pk.Equals(suite.pk))
const bufSize = 100
bz2 := make([]byte, bufSize)
pkCpy := suite.pk
_, err = pkCpy.MarshalTo(bz2)
require.NoError(err)
require.Len(bz2, bufSize)
require.Equal(bz, bz2[:pk.Size()])
bz2 = make([]byte, bufSize)
_, err = pkCpy.MarshalToSizedBuffer(bz2)
require.NoError(err)
require.Len(bz2, bufSize)
require.Equal(bz, bz2[(bufSize-pk.Size()):])
/**** test interface marshalling ****/
bz, err = cdc.MarshalInterface(suite.pk)
require.NoError(err)
var pkI cryptotypes.PubKey
err = cdc.UnmarshalInterface(bz, &pkI)
require.EqualError(err, "no registered implementations of type types.PubKey")
RegisterInterfaces(registry)
require.NoError(cdc.UnmarshalInterface(bz, &pkI))
require.True(pkI.Equals(suite.pk))
cdc.UnmarshalInterface(bz, nil)
require.Error(err, "nil should fail")
}
func (suite *PKSuite) TestSize() {
require := suite.Require()
var pk ecdsaPK
require.Equal(pk.Size(), len(suite.pk.Bytes()))
var nilPk *ecdsaPK
require.Equal(0, nilPk.Size(), "nil value must have zero size")
}

View File

@ -190,6 +190,10 @@
- [PrivKey](#cosmos.crypto.secp256k1.PrivKey)
- [PubKey](#cosmos.crypto.secp256k1.PubKey)
- [cosmos/crypto/secp256r1/keys.proto](#cosmos/crypto/secp256r1/keys.proto)
- [PrivKey](#cosmos.crypto.secp256r1.PrivKey)
- [PubKey](#cosmos.crypto.secp256r1.PubKey)
- [cosmos/distribution/v1beta1/distribution.proto](#cosmos/distribution/v1beta1/distribution.proto)
- [CommunityPoolSpendProposal](#cosmos.distribution.v1beta1.CommunityPoolSpendProposal)
- [CommunityPoolSpendProposalWithDeposit](#cosmos.distribution.v1beta1.CommunityPoolSpendProposalWithDeposit)
@ -3115,6 +3119,52 @@ This prefix is followed with the x-coordinate.
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<!-- end services -->
<a name="cosmos/crypto/secp256r1/keys.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## cosmos/crypto/secp256r1/keys.proto
<a name="cosmos.crypto.secp256r1.PrivKey"></a>
### PrivKey
PrivKey defines a secp256r1 ECDSA private key.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `secret` | [bytes](#bytes) | | secret number serialized using big-endian encoding |
<a name="cosmos.crypto.secp256r1.PubKey"></a>
### PubKey
PubKey defines a secp256r1 ECDSA public key.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `key` | [bytes](#bytes) | | Point on secp256r1 curve in a compressed representation as specified in section 4.3.6 of ANSI X9.62. |
<!-- end messages -->
<!-- end enums -->

View File

@ -0,0 +1,22 @@
syntax = "proto3";
package cosmos.crypto.secp256r1;
import "gogoproto/gogo.proto";
option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1";
option (gogoproto.messagename_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.goproto_getters_all) = false;
// PubKey defines a secp256r1 ECDSA public key.
message PubKey {
// Point on secp256r1 curve in a compressed representation as specified in section
// 4.3.6 of ANSI X9.62: https://webstore.ansi.org/standards/ascx9/ansix9621998
bytes key = 1 [(gogoproto.customtype) = "ecdsaPK"];
}
// PrivKey defines a secp256r1 ECDSA private key.
message PrivKey {
// secret number serialized using big-endian encoding
bytes secret = 1 [(gogoproto.customtype) = "ecdsaSK"];
}