Skip to content

Commit

Permalink
Migrate Tendermint PubKey types to the SDK (cosmos#7047)
Browse files Browse the repository at this point in the history
* Move crypto packages from tm

* Revert refactor changes

* Fix lint

* Move to crypto/keys

* Update to tm 0.34-rc3

* Update benchmark tests

* Fix tests

* Update to tm master

Co-authored-by: Aaron Craelius <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 18, 2020
1 parent 94b3cc5 commit 443e0c1
Show file tree
Hide file tree
Showing 113 changed files with 22,976 additions and 5 deletions.
27 changes: 27 additions & 0 deletions crypto/keys/ed25519/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ed25519

import (
"io"
"testing"

"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/cosmos-sdk/crypto/keys/internal/benchmarking"
)

func BenchmarkKeyGeneration(b *testing.B) {
benchmarkKeygenWrapper := func(reader io.Reader) crypto.PrivKey {
return genPrivKey(reader)
}
benchmarking.BenchmarkKeyGeneration(b, benchmarkKeygenWrapper)
}

func BenchmarkSigning(b *testing.B) {
priv := GenPrivKey()
benchmarking.BenchmarkSigning(b, priv)
}

func BenchmarkVerification(b *testing.B) {
priv := GenPrivKey()
benchmarking.BenchmarkVerification(b, priv)
}
163 changes: 163 additions & 0 deletions crypto/keys/ed25519/ed25519.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package ed25519

import (
"bytes"
"crypto/subtle"
"fmt"
"io"

"golang.org/x/crypto/ed25519"

"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
)

//-------------------------------------

var _ crypto.PrivKey = PrivKey{}

const (
// PubKeySize is is the size, in bytes, of public keys as used in this package.
PubKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// Size of an Edwards25519 signature. Namely the size of a compressed
// Edwards25519 point, and a field element. Both of which are 32 bytes.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the
// private key representations used by RFC 8032.
SeedSize = 32

keyType = "ed25519"
)

// PrivKey implements crypto.PrivKey.
type PrivKey []byte

// Bytes returns the privkey byte format.
func (privKey PrivKey) Bytes() []byte {
return []byte(privKey)
}

// Sign produces a signature on the provided message.
// This assumes the privkey is wellformed in the golang format.
// The first 32 bytes should be random,
// corresponding to the normal ed25519 private key.
// The latter 32 bytes should be the compressed public key.
// If these conditions aren't met, Sign will panic or produce an
// incorrect signature.
func (privKey PrivKey) Sign(msg []byte) ([]byte, error) {
signatureBytes := ed25519.Sign(ed25519.PrivateKey(privKey), msg)
return signatureBytes, nil
}

// PubKey gets the corresponding public key from the private key.
//
// Panics if the private key is not initialized.
func (privKey PrivKey) PubKey() crypto.PubKey {
// If the latter 32 bytes of the privkey are all zero, privkey is not
// initialized.
initialized := false
for _, v := range privKey[32:] {
if v != 0 {
initialized = true
break
}
}

if !initialized {
panic("Expected ed25519 PrivKey to include concatenated pubkey bytes")
}

pubkeyBytes := make([]byte, PubKeySize)
copy(pubkeyBytes, privKey[32:])
return PubKey(pubkeyBytes)
}

// Equals - you probably don't need to use this.
// Runs in constant time based on length of the keys.
func (privKey PrivKey) Equals(other crypto.PrivKey) bool {
if otherEd, ok := other.(PrivKey); ok {
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
}

return false
}

func (privKey PrivKey) Type() string {
return keyType
}

// GenPrivKey generates a new ed25519 private key.
// It uses OS randomness in conjunction with the current global random seed
// in tendermint/libs/common to generate the private key.
func GenPrivKey() PrivKey {
return genPrivKey(crypto.CReader())
}

// genPrivKey generates a new ed25519 private key using the provided reader.
func genPrivKey(rand io.Reader) PrivKey {
seed := make([]byte, SeedSize)

_, err := io.ReadFull(rand, seed)
if err != nil {
panic(err)
}

return PrivKey(ed25519.NewKeyFromSeed(seed))
}

// GenPrivKeyFromSecret hashes the secret with SHA2, and uses
// that 32 byte output to create the private key.
// NOTE: secret should be the output of a KDF like bcrypt,
// if it's derived from user input.
func GenPrivKeyFromSecret(secret []byte) PrivKey {
seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.

return PrivKey(ed25519.NewKeyFromSeed(seed))
}

//-------------------------------------

var _ crypto.PubKey = PubKey{}

// PubKeyEd25519 implements crypto.PubKey for the Ed25519 signature scheme.
type PubKey []byte

// Address is the SHA256-20 of the raw pubkey bytes.
func (pubKey PubKey) Address() crypto.Address {
if len(pubKey) != PubKeySize {
panic("pubkey is incorrect size")
}
return crypto.Address(tmhash.SumTruncated(pubKey))
}

// Bytes returns the PubKey byte format.
func (pubKey PubKey) Bytes() []byte {
return []byte(pubKey)
}

func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool {
// make sure we use the same algorithm to sign
if len(sig) != SignatureSize {
return false
}

return ed25519.Verify(ed25519.PublicKey(pubKey), msg, sig)
}

func (pubKey PubKey) String() string {
return fmt.Sprintf("PubKeyEd25519{%X}", []byte(pubKey))
}

func (pubKey PubKey) Type() string {
return keyType
}

func (pubKey PubKey) Equals(other crypto.PubKey) bool {
if otherEd, ok := other.(PubKey); ok {
return bytes.Equal(pubKey[:], otherEd[:])
}

return false
}
30 changes: 30 additions & 0 deletions crypto/keys/ed25519/ed25519_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ed25519_test

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
)

func TestSignAndValidateEd25519(t *testing.T) {

privKey := ed25519.GenPrivKey()
pubKey := privKey.PubKey()

msg := crypto.CRandBytes(128)
sig, err := privKey.Sign(msg)
require.Nil(t, err)

// Test the signature
assert.True(t, pubKey.VerifySignature(msg, sig))

// Mutate the signature, just one bit.
// TODO: Replace this with a much better fuzzer, tendermint/ed25519/issues/10
sig[7] ^= byte(0x01)

assert.False(t, pubKey.VerifySignature(msg, sig))
}
92 changes: 92 additions & 0 deletions crypto/keys/internal/benchmarking/bench.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package benchmarking

import (
"io"
"testing"

"github.com/tendermint/tendermint/crypto"
)

// The code in this file is adapted from agl/ed25519.
// As such it is under the following license.
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found at the bottom of this file.

type zeroReader struct{}

func (zeroReader) Read(buf []byte) (int, error) {
for i := range buf {
buf[i] = 0
}
return len(buf), nil
}

// BenchmarkKeyGeneration benchmarks the given key generation algorithm using
// a dummy reader.
func BenchmarkKeyGeneration(b *testing.B, generateKey func(reader io.Reader) crypto.PrivKey) {
var zero zeroReader
for i := 0; i < b.N; i++ {
generateKey(zero)
}
}

// BenchmarkSigning benchmarks the given signing algorithm using
// the provided privkey.
func BenchmarkSigning(b *testing.B, priv crypto.PrivKey) {
message := []byte("Hello, world!")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := priv.Sign(message)

if err != nil {
b.FailNow()
}
}
}

// BenchmarkVerification benchmarks the given verification algorithm using
// the provided privkey on a constant message.
func BenchmarkVerification(b *testing.B, priv crypto.PrivKey) {
pub := priv.PubKey()
// use a short message, so this time doesn't get dominated by hashing.
message := []byte("Hello, world!")
signature, err := priv.Sign(message)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
pub.VerifySignature(message, signature)
}
}

// Below is the aforementioned license.

// Copyright (c) 2012 The Go Authors. All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 changes: 27 additions & 0 deletions crypto/keys/secp256k1/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package secp256k1

import (
"io"
"testing"

"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/cosmos-sdk/crypto/keys/internal/benchmarking"
)

func BenchmarkKeyGeneration(b *testing.B) {
benchmarkKeygenWrapper := func(reader io.Reader) crypto.PrivKey {
return genPrivKey(reader)
}
benchmarking.BenchmarkKeyGeneration(b, benchmarkKeygenWrapper)
}

func BenchmarkSigning(b *testing.B) {
priv := GenPrivKey()
benchmarking.BenchmarkSigning(b, priv)
}

func BenchmarkVerification(b *testing.B) {
priv := GenPrivKey()
benchmarking.BenchmarkVerification(b, priv)
}
24 changes: 24 additions & 0 deletions crypto/keys/secp256k1/internal/secp256k1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe

*~
Loading

0 comments on commit 443e0c1

Please sign in to comment.