Browse Source

Merge pull request #3 from tendermint/feature/go-data

Use go-data for nice json un/marshaling
pull/1782/head
Ethan Buchman 7 years ago
committed by GitHub
parent
commit
562b4cc9ef
8 changed files with 370 additions and 143 deletions
  1. +9
    -0
      Makefile
  2. +6
    -11
      armor_test.go
  3. +112
    -0
      encode_test.go
  4. +57
    -9
      priv_key.go
  5. +67
    -16
      pub_key.go
  6. +47
    -11
      signature.go
  7. +65
    -79
      signature_test.go
  8. +7
    -17
      symmetric_test.go

+ 9
- 0
Makefile View File

@ -0,0 +1,9 @@
.PHONY: docs
REPO:=github.com/tendermint/go-crypto
docs:
@go get github.com/davecheney/godoc2md
godoc2md $(REPO) > README.md
test:
go test ./...

+ 6
- 11
armor_test.go View File

@ -1,25 +1,20 @@
package crypto
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSimpleArmor(t *testing.T) {
blockType := "MINT TEST"
data := []byte("somedata")
armorStr := EncodeArmor(blockType, nil, data)
t.Log("Got armor: ", armorStr)
// Decode armorStr and test for equivalence.
blockType2, _, data2, err := DecodeArmor(armorStr)
if err != nil {
t.Error(err)
}
if blockType != blockType2 {
t.Errorf("Expected block type %v but got %v", blockType, blockType2)
}
if !bytes.Equal(data, data2) {
t.Errorf("Expected data %X but got %X", data2, data)
}
require.Nil(t, err, "%+v", err)
assert.Equal(t, blockType, blockType2)
assert.Equal(t, data, data2)
}

+ 112
- 0
encode_test.go View File

@ -0,0 +1,112 @@
package crypto
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
data "github.com/tendermint/go-data"
)
type byter interface {
Bytes() []byte
}
// go to wire encoding and back
func checkWire(t *testing.T, in byter, reader interface{}, typ byte) {
// test to and from binary
bin, err := data.ToWire(in)
require.Nil(t, err, "%+v", err)
assert.Equal(t, typ, bin[0])
// make sure this is compatible with current (Bytes()) encoding
assert.Equal(t, in.Bytes(), bin)
err = data.FromWire(bin, reader)
require.Nil(t, err, "%+v", err)
}
// go to json encoding and back
func checkJSON(t *testing.T, in interface{}, reader interface{}, typ string) {
// test to and from binary
js, err := data.ToJSON(in)
require.Nil(t, err, "%+v", err)
styp := `"` + typ + `"`
assert.True(t, strings.Contains(string(js), styp))
err = data.FromJSON(js, reader)
require.Nil(t, err, "%+v", err)
// also check text format
text, err := data.ToText(in)
require.Nil(t, err, "%+v", err)
parts := strings.Split(text, ":")
require.Equal(t, 2, len(parts))
// make sure the first part is the typ string
assert.Equal(t, typ, parts[0])
// and the data is also present in the json
assert.True(t, strings.Contains(string(js), parts[1]))
}
func TestKeyEncodings(t *testing.T) {
cases := []struct {
privKey PrivKeyS
keyType byte
keyName string
}{
{
privKey: PrivKeyS{GenPrivKeyEd25519()},
keyType: TypeEd25519,
keyName: NameEd25519,
},
{
privKey: PrivKeyS{GenPrivKeySecp256k1()},
keyType: TypeSecp256k1,
keyName: NameSecp256k1,
},
}
for _, tc := range cases {
// check (de/en)codings of private key
priv2 := PrivKeyS{}
checkWire(t, tc.privKey, &priv2, tc.keyType)
assert.EqualValues(t, tc.privKey, priv2)
priv3 := PrivKeyS{}
checkJSON(t, tc.privKey, &priv3, tc.keyName)
assert.EqualValues(t, tc.privKey, priv3)
// check (de/en)codings of public key
pubKey := PubKeyS{tc.privKey.PubKey()}
pub2 := PubKeyS{}
checkWire(t, pubKey, &pub2, tc.keyType)
assert.EqualValues(t, pubKey, pub2)
pub3 := PubKeyS{}
checkJSON(t, pubKey, &pub3, tc.keyName)
assert.EqualValues(t, pubKey, pub3)
}
}
func toFromJSON(t *testing.T, in interface{}, recvr interface{}) {
js, err := data.ToJSON(in)
require.Nil(t, err, "%+v", err)
err = data.FromJSON(js, recvr)
require.Nil(t, err, "%+v", err)
}
func TestNilEncodings(t *testing.T) {
// make sure sigs are okay with nil
a, b := SignatureS{}, SignatureS{}
toFromJSON(t, a, &b)
assert.EqualValues(t, a, b)
// make sure sigs are okay with nil
c, d := PubKeyS{}, PubKeyS{}
toFromJSON(t, c, &d)
assert.EqualValues(t, c, d)
// make sure sigs are okay with nil
e, f := PrivKeyS{}, PrivKeyS{}
toFromJSON(t, e, &f)
assert.EqualValues(t, e, f)
}

+ 57
- 9
priv_key.go View File

@ -7,6 +7,7 @@ import (
"github.com/tendermint/ed25519"
"github.com/tendermint/ed25519/extra25519"
. "github.com/tendermint/go-common"
data "github.com/tendermint/go-data"
"github.com/tendermint/go-wire"
)
@ -18,18 +19,43 @@ type PrivKey interface {
Equals(PrivKey) bool
}
// Types of PrivKey implementations
// Types of implementations
const (
PrivKeyTypeEd25519 = byte(0x01)
PrivKeyTypeSecp256k1 = byte(0x02)
TypeEd25519 = byte(0x01)
TypeSecp256k1 = byte(0x02)
NameEd25519 = "ed25519"
NameSecp256k1 = "secp256k1"
)
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ PrivKey }{},
wire.ConcreteType{PrivKeyEd25519{}, PrivKeyTypeEd25519},
wire.ConcreteType{PrivKeySecp256k1{}, PrivKeyTypeSecp256k1},
)
var privKeyMapper data.Mapper
// register both private key types with go-data (and thus go-wire)
func init() {
privKeyMapper = data.NewMapper(PrivKeyS{}).
RegisterInterface(PrivKeyEd25519{}, NameEd25519, TypeEd25519).
RegisterInterface(PrivKeySecp256k1{}, NameSecp256k1, TypeSecp256k1)
}
// PrivKeyS add json serialization to PrivKey
type PrivKeyS struct {
PrivKey
}
func (p PrivKeyS) MarshalJSON() ([]byte, error) {
return privKeyMapper.ToJSON(p.PrivKey)
}
func (p *PrivKeyS) UnmarshalJSON(data []byte) (err error) {
parsed, err := privKeyMapper.FromJSON(data)
if err == nil && parsed != nil {
p.PrivKey = parsed.(PrivKey)
}
return
}
func (p PrivKeyS) Empty() bool {
return p.PrivKey == nil
}
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
err = wire.ReadBinaryBytes(privKeyBytes, &privKey)
@ -64,6 +90,17 @@ func (privKey PrivKeyEd25519) Equals(other PrivKey) bool {
}
}
func (p PrivKeyEd25519) MarshalJSON() ([]byte, error) {
return data.Encoder.Marshal(p[:])
}
func (p *PrivKeyEd25519) UnmarshalJSON(enc []byte) error {
var ref []byte
err := data.Encoder.Unmarshal(&ref, enc)
copy(p[:], ref)
return err
}
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte {
keyCurve25519 := new([32]byte)
privKeyBytes := [64]byte(privKey)
@ -136,6 +173,17 @@ func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool {
}
}
func (p PrivKeySecp256k1) MarshalJSON() ([]byte, error) {
return data.Encoder.Marshal(p[:])
}
func (p *PrivKeySecp256k1) UnmarshalJSON(enc []byte) error {
var ref []byte
err := data.Encoder.Unmarshal(&ref, enc)
copy(p[:], ref)
return err
}
func (privKey PrivKeySecp256k1) String() string {
return Fmt("PrivKeySecp256k1{*****}")
}


+ 67
- 16
pub_key.go View File

@ -7,6 +7,7 @@ import (
"github.com/tendermint/ed25519"
"github.com/tendermint/ed25519/extra25519"
. "github.com/tendermint/go-common"
data "github.com/tendermint/go-data"
"github.com/tendermint/go-wire"
"golang.org/x/crypto/ripemd160"
)
@ -20,18 +21,35 @@ type PubKey interface {
Equals(PubKey) bool
}
// Types of PubKey implementations
const (
PubKeyTypeEd25519 = byte(0x01)
PubKeyTypeSecp256k1 = byte(0x02)
)
var pubKeyMapper data.Mapper
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ PubKey }{},
wire.ConcreteType{PubKeyEd25519{}, PubKeyTypeEd25519},
wire.ConcreteType{PubKeySecp256k1{}, PubKeyTypeSecp256k1},
)
// register both public key types with go-data (and thus go-wire)
func init() {
pubKeyMapper = data.NewMapper(PubKeyS{}).
RegisterInterface(PubKeyEd25519{}, NameEd25519, TypeEd25519).
RegisterInterface(PubKeySecp256k1{}, NameSecp256k1, TypeSecp256k1)
}
// PubKeyS add json serialization to PubKey
type PubKeyS struct {
PubKey
}
func (p PubKeyS) MarshalJSON() ([]byte, error) {
return pubKeyMapper.ToJSON(p.PubKey)
}
func (p *PubKeyS) UnmarshalJSON(data []byte) (err error) {
parsed, err := pubKeyMapper.FromJSON(data)
if err == nil && parsed != nil {
p.PubKey = parsed.(PubKey)
}
return
}
func (p PubKeyS) Empty() bool {
return p.PubKey == nil
}
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) {
err = wire.ReadBinaryBytes(pubKeyBytes, &pubKey)
@ -50,7 +68,7 @@ func (pubKey PubKeyEd25519) Address() []byte {
PanicCrisis(*err)
}
// append type byte
encodedPubkey := append([]byte{PubKeyTypeEd25519}, w.Bytes()...)
encodedPubkey := append([]byte{TypeEd25519}, w.Bytes()...)
hasher := ripemd160.New()
hasher.Write(encodedPubkey) // does not error
return hasher.Sum(nil)
@ -61,6 +79,11 @@ func (pubKey PubKeyEd25519) Bytes() []byte {
}
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
// unwrap if needed
if wrap, ok := sig_.(SignatureS); ok {
sig_ = wrap.Signature
}
// make sure we use the same algorithm to sign
sig, ok := sig_.(SignatureEd25519)
if !ok {
return false
@ -70,6 +93,17 @@ func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes)
}
func (p PubKeyEd25519) MarshalJSON() ([]byte, error) {
return data.Encoder.Marshal(p[:])
}
func (p *PubKeyEd25519) UnmarshalJSON(enc []byte) error {
var ref []byte
err := data.Encoder.Unmarshal(&ref, enc)
copy(p[:], ref)
return err
}
// For use with golang/crypto/nacl/box
// If error, returns nil.
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte {
@ -111,7 +145,7 @@ func (pubKey PubKeySecp256k1) Address() []byte {
PanicCrisis(*err)
}
// append type byte
encodedPubkey := append([]byte{PubKeyTypeSecp256k1}, w.Bytes()...)
encodedPubkey := append([]byte{TypeSecp256k1}, w.Bytes()...)
hasher := ripemd160.New()
hasher.Write(encodedPubkey) // does not error
return hasher.Sum(nil)
@ -122,14 +156,20 @@ func (pubKey PubKeySecp256k1) Bytes() []byte {
}
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool {
pub__, err := secp256k1.ParsePubKey(append([]byte{0x04}, pubKey[:]...), secp256k1.S256())
if err != nil {
return false
// unwrap if needed
if wrap, ok := sig_.(SignatureS); ok {
sig_ = wrap.Signature
}
// and assert same algorithm to sign and verify
sig, ok := sig_.(SignatureSecp256k1)
if !ok {
return false
}
pub__, err := secp256k1.ParsePubKey(append([]byte{0x04}, pubKey[:]...), secp256k1.S256())
if err != nil {
return false
}
sig__, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256())
if err != nil {
return false
@ -137,6 +177,17 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool {
return sig__.Verify(Sha256(msg), pub__)
}
func (p PubKeySecp256k1) MarshalJSON() ([]byte, error) {
return data.Encoder.Marshal(p[:])
}
func (p *PubKeySecp256k1) UnmarshalJSON(enc []byte) error {
var ref []byte
err := data.Encoder.Unmarshal(&ref, enc)
copy(p[:], ref)
return err
}
func (pubKey PubKeySecp256k1) String() string {
return Fmt("PubKeySecp256k1{%X}", pubKey[:])
}


+ 47
- 11
signature.go View File

@ -5,6 +5,7 @@ import (
"fmt"
. "github.com/tendermint/go-common"
data "github.com/tendermint/go-data"
"github.com/tendermint/go-wire"
)
@ -16,18 +17,35 @@ type Signature interface {
Equals(Signature) bool
}
// Types of Signature implementations
const (
SignatureTypeEd25519 = byte(0x01)
SignatureTypeSecp256k1 = byte(0x02)
)
var sigMapper data.Mapper
// for wire.readReflect
var _ = wire.RegisterInterface(
struct{ Signature }{},
wire.ConcreteType{SignatureEd25519{}, SignatureTypeEd25519},
wire.ConcreteType{SignatureSecp256k1{}, SignatureTypeSecp256k1},
)
// register both public key types with go-data (and thus go-wire)
func init() {
sigMapper = data.NewMapper(SignatureS{}).
RegisterInterface(SignatureEd25519{}, NameEd25519, TypeEd25519).
RegisterInterface(SignatureSecp256k1{}, NameSecp256k1, TypeSecp256k1)
}
// SignatureS add json serialization to Signature
type SignatureS struct {
Signature
}
func (p SignatureS) MarshalJSON() ([]byte, error) {
return sigMapper.ToJSON(p.Signature)
}
func (p *SignatureS) UnmarshalJSON(data []byte) (err error) {
parsed, err := sigMapper.FromJSON(data)
if err == nil && parsed != nil {
p.Signature = parsed.(Signature)
}
return
}
func (p SignatureS) Empty() bool {
return p.Signature == nil
}
func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) {
err = wire.ReadBinaryBytes(sigBytes, &sig)
@ -55,6 +73,17 @@ func (sig SignatureEd25519) Equals(other Signature) bool {
}
}
func (p SignatureEd25519) MarshalJSON() ([]byte, error) {
return data.Encoder.Marshal(p[:])
}
func (p *SignatureEd25519) UnmarshalJSON(enc []byte) error {
var ref []byte
err := data.Encoder.Unmarshal(&ref, enc)
copy(p[:], ref)
return err
}
//-------------------------------------
// Implements Signature
@ -75,3 +104,10 @@ func (sig SignatureSecp256k1) Equals(other Signature) bool {
return false
}
}
func (p SignatureSecp256k1) MarshalJSON() ([]byte, error) {
return data.Encoder.Marshal(p)
}
func (p *SignatureSecp256k1) UnmarshalJSON(enc []byte) error {
return data.Encoder.Unmarshal((*[]byte)(p), enc)
}

+ 65
- 79
signature_test.go View File

@ -1,11 +1,13 @@
package crypto
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/ed25519"
"github.com/tendermint/go-wire"
data "github.com/tendermint/go-data"
)
func TestSignAndValidateEd25519(t *testing.T) {
@ -15,109 +17,93 @@ func TestSignAndValidateEd25519(t *testing.T) {
msg := CRandBytes(128)
sig := privKey.Sign(msg)
t.Logf("msg: %X, sig: %X", msg, sig)
// Test the signature
if !pubKey.VerifyBytes(msg, sig) {
t.Errorf("Account message signature verification failed")
}
assert.True(t, pubKey.VerifyBytes(msg, sig))
// Mutate the signature, just one bit.
sigEd := sig.(SignatureEd25519)
sigEd[0] ^= byte(0x01)
sig = Signature(sigEd)
if pubKey.VerifyBytes(msg, sig) {
t.Errorf("Account message signature verification should have failed but passed instead")
}
assert.False(t, pubKey.VerifyBytes(msg, sig))
}
func TestSignAndValidateSecp256k1(t *testing.T) {
privKey := GenPrivKeySecp256k1()
pubKey := privKey.PubKey()
msg := CRandBytes(128)
sig := privKey.Sign(msg)
t.Logf("msg: %X, sig: %X", msg, sig)
// Test the signature
if !pubKey.VerifyBytes(msg, sig) {
t.Errorf("Account message signature verification failed")
}
assert.True(t, pubKey.VerifyBytes(msg, sig))
// Mutate the signature, just one bit.
sigEd := sig.(SignatureSecp256k1)
sigEd[0] ^= byte(0x01)
sig = Signature(sigEd)
if pubKey.VerifyBytes(msg, sig) {
t.Errorf("Account message signature verification should have failed but passed instead")
}
}
func TestBinaryDecodeEd25519(t *testing.T) {
privKey := GenPrivKeyEd25519()
pubKey := privKey.PubKey()
msg := CRandBytes(128)
sig := privKey.Sign(msg)
t.Logf("msg: %X, sig: %X", msg, sig)
buf, n, err := new(bytes.Buffer), new(int), new(error)
wire.WriteBinary(struct{ Signature }{sig}, buf, n, err)
if *err != nil {
t.Fatalf("Failed to write Signature: %v", err)
}
if len(buf.Bytes()) != ed25519.SignatureSize+1 {
// 1 byte TypeByte, 64 bytes signature bytes
t.Fatalf("Unexpected signature write size: %v", len(buf.Bytes()))
}
if buf.Bytes()[0] != SignatureTypeEd25519 {
t.Fatalf("Unexpected signature type byte")
}
sigStruct := struct{ Signature }{}
sig2 := wire.ReadBinary(sigStruct, buf, 0, n, err)
if *err != nil {
t.Fatalf("Failed to read Signature: %v", err)
}
// Test the signature
if !pubKey.VerifyBytes(msg, sig2.(struct{ Signature }).Signature.(SignatureEd25519)) {
t.Errorf("Account message signature verification failed")
}
assert.False(t, pubKey.VerifyBytes(msg, sig))
}
func TestBinaryDecodeSecp256k1(t *testing.T) {
privKey := GenPrivKeySecp256k1()
pubKey := privKey.PubKey()
msg := CRandBytes(128)
sig := privKey.Sign(msg)
t.Logf("msg: %X, sig: %X", msg, sig)
buf, n, err := new(bytes.Buffer), new(int), new(error)
wire.WriteBinary(struct{ Signature }{sig}, buf, n, err)
if *err != nil {
t.Fatalf("Failed to write Signature: %v", err)
func TestSignatureEncodings(t *testing.T) {
cases := []struct {
privKey PrivKeyS
sigSize int
sigType byte
sigName string
}{
{
privKey: PrivKeyS{GenPrivKeyEd25519()},
sigSize: ed25519.SignatureSize,
sigType: TypeEd25519,
sigName: NameEd25519,
},
{
privKey: PrivKeyS{GenPrivKeySecp256k1()},
sigSize: 0, // unknown
sigType: TypeSecp256k1,
sigName: NameSecp256k1,
},
}
if buf.Bytes()[0] != SignatureTypeSecp256k1 {
t.Fatalf("Unexpected signature type byte")
}
sigStruct := struct{ Signature }{}
sig2 := wire.ReadBinary(sigStruct, buf, 0, n, err)
if *err != nil {
t.Fatalf("Failed to read Signature: %v", err)
}
// Test the signature
if !pubKey.VerifyBytes(msg, sig2.(struct{ Signature }).Signature.(SignatureSecp256k1)) {
t.Errorf("Account message signature verification failed")
for _, tc := range cases {
// note we embed them from the beginning....
pubKey := PubKeyS{tc.privKey.PubKey()}
msg := CRandBytes(128)
sig := SignatureS{tc.privKey.Sign(msg)}
// store as wire
bin, err := data.ToWire(sig)
require.Nil(t, err, "%+v", err)
if tc.sigSize != 0 {
assert.Equal(t, tc.sigSize+1, len(bin))
}
assert.Equal(t, tc.sigType, bin[0])
// and back
sig2 := SignatureS{}
err = data.FromWire(bin, &sig2)
require.Nil(t, err, "%+v", err)
assert.EqualValues(t, sig, sig2)
assert.True(t, pubKey.VerifyBytes(msg, sig2))
// store as json
js, err := data.ToJSON(sig)
require.Nil(t, err, "%+v", err)
assert.True(t, strings.Contains(string(js), tc.sigName))
// and back
sig3 := SignatureS{}
err = data.FromJSON(js, &sig3)
require.Nil(t, err, "%+v", err)
assert.EqualValues(t, sig, sig3)
assert.True(t, pubKey.VerifyBytes(msg, sig3))
// and make sure we can textify it
text, err := data.ToText(sig)
require.Nil(t, err, "%+v", err)
assert.True(t, strings.HasPrefix(text, tc.sigName))
}
}

+ 7
- 17
symmetric_test.go View File

@ -1,9 +1,11 @@
package crypto
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
)
@ -14,16 +16,10 @@ func TestSimple(t *testing.T) {
plaintext := []byte("sometext")
secret := []byte("somesecretoflengththirtytwo===32")
ciphertext := EncryptSymmetric(plaintext, secret)
plaintext2, err := DecryptSymmetric(ciphertext, secret)
if err != nil {
t.Error(err)
}
if !bytes.Equal(plaintext, plaintext2) {
t.Errorf("Decrypted plaintext was %X, expected %X", plaintext2, plaintext)
}
require.Nil(t, err, "%+v", err)
assert.Equal(t, plaintext, plaintext2)
}
func TestSimpleWithKDF(t *testing.T) {
@ -39,14 +35,8 @@ func TestSimpleWithKDF(t *testing.T) {
secret = Sha256(secret)
ciphertext := EncryptSymmetric(plaintext, secret)
plaintext2, err := DecryptSymmetric(ciphertext, secret)
if err != nil {
t.Error(err)
}
if !bytes.Equal(plaintext, plaintext2) {
t.Errorf("Decrypted plaintext was %X, expected %X", plaintext2, plaintext)
}
require.Nil(t, err, "%+v", err)
assert.Equal(t, plaintext, plaintext2)
}

Loading…
Cancel
Save