diff --git a/account/account.go b/account/account.go index 54f862feaa6ac6501edc3f07c3c3401e96560c5c..d502bd4c7fec4b56a41f061506f9fe24a51ba713 100644 --- a/account/account.go +++ b/account/account.go @@ -118,7 +118,7 @@ func NewConcreteAccount(pubKey PublicKey) ConcreteAccount { } func NewConcreteAccountFromSecret(secret string) ConcreteAccount { - return NewConcreteAccount(PublicKeyFromPubKey(PrivateKeyFromSecret(secret).PubKey())) + return NewConcreteAccount(PublicKeyFromGoCryptoPubKey(PrivateKeyFromSecret(secret).PubKey())) } // Return as immutable Account diff --git a/account/account_test.go b/account/account_test.go index 07c30618ee9a195a012913e486e606eefa2db82c..716834e6bbad54189b578f46a565fc4b88500e4e 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -19,8 +19,11 @@ import ( "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -99,7 +102,11 @@ func TestMarshalJSON(t *testing.T) { concreteAcc.Code = []byte{60, 23, 45} acc := concreteAcc.Account() bs, err := json.Marshal(acc) - assert.Equal(t, `{"Address":"745BD6BE33020146E04FA0F293A41E389887DE86","PublicKey":{"type":"ed25519","data":"8CEBC16C166A0614AD7C8E330318E774E1A039321F17274DF12ABA3B1BFC773C"},"Balance":0,"Code":"3C172D","Sequence":0,"StorageRoot":"","Permissions":{"Base":{"Perms":0,"SetBit":0},"Roles":[]}}`, - string(bs)) + + pubKeyEd25519 := concreteAcc.PublicKey.PubKey.Unwrap().(crypto.PubKeyEd25519) + expected := fmt.Sprintf(`{"Address":"%s","PublicKey":{"type":"ed25519","data":"%X"},`+ + `"Balance":0,"Code":"3C172D","Sequence":0,"StorageRoot":"","Permissions":{"Base":{"Perms":0,"SetBit":0},"Roles":[]}}`, + concreteAcc.Address, pubKeyEd25519[:]) + assert.Equal(t, expected, string(bs)) assert.NoError(t, err) } diff --git a/account/crypto.go b/account/crypto.go new file mode 100644 index 0000000000000000000000000000000000000000..e04f257fc362b3940feed9ad06cbb32f326c2001 --- /dev/null +++ b/account/crypto.go @@ -0,0 +1,235 @@ +package account + +import ( + "bytes" + "crypto/rand" + "crypto/sha256" + "fmt" + "io" + + "github.com/tendermint/go-crypto" + "golang.org/x/crypto/ed25519" +) + +// The types in this file allow us to control serialisation of keys and signatures, as well as the interface +// exposed regardless of crypto library + +type Signer interface { + Sign(msg []byte) (Signature, error) +} + +// PublicKey + +type PublicKey struct { + crypto.PubKey `json:"unwrap"` +} + +func PublicKeyFromGoCryptoPubKey(pubKey crypto.PubKey) PublicKey { + return PublicKey{PubKey: pubKey} +} + +// Currently this is a stub that reads the raw bytes returned by key_client and returns +// an ed25519 public key. +func PublicKeyFromBytes(bs []byte) (PublicKey, error) { + //TODO: read a typed representation (most likely JSON) and do the right thing here + // Only supports ed25519 currently so no switch on signature scheme + pubKeyEd25519 := crypto.PubKeyEd25519{} + if len(bs) != len(pubKeyEd25519) { + return PublicKey{}, fmt.Errorf("bytes passed have length %v by ed25519 public keys have %v bytes", + len(bs), len(pubKeyEd25519)) + } + copy(pubKeyEd25519[:], bs) + return PublicKey{ + PubKey: pubKeyEd25519.Wrap(), + }, nil +} + +// Returns a copy of the raw untyped public key bytes +func (pk PublicKey) RawBytes() []byte { + switch pubKey := pk.PubKey.Unwrap().(type) { + case crypto.PubKeyEd25519: + pubKeyCopy := crypto.PubKeyEd25519{} + copy(pubKeyCopy[:], pubKey[:]) + return pubKeyCopy[:] + case crypto.PubKeySecp256k1: + pubKeyCopy := crypto.PubKeySecp256k1{} + copy(pubKeyCopy[:], pubKey[:]) + return pubKeyCopy[:] + default: + return nil + } +} + +func (pk PublicKey) VerifyBytes(msg []byte, signature Signature) bool { + return pk.PubKey.VerifyBytes(msg, signature.Signature) +} + +func (pk PublicKey) Address() Address { + return MustAddressFromBytes(pk.PubKey.Address()) +} + +func (pk PublicKey) MarshalJSON() ([]byte, error) { + return pk.PubKey.MarshalJSON() +} + +func (pk *PublicKey) UnmarshalJSON(data []byte) error { + return pk.PubKey.UnmarshalJSON(data) +} + +func (pk PublicKey) MarshalText() ([]byte, error) { + return pk.MarshalJSON() +} + +func (pk *PublicKey) UnmarshalText(text []byte) error { + return pk.UnmarshalJSON(text) +} + +// PrivateKey + +type PrivateKey struct { + crypto.PrivKey `json:"unwrap"` +} + +func PrivateKeyFromGoCryptoPrivKey(privKey crypto.PrivKey) PrivateKey { + return PrivateKey{PrivKey: privKey} +} + +func PrivateKeyFromSecret(secret string) PrivateKey { + hasher := sha256.New() + hasher.Write(([]byte)(secret)) + // No error from a buffer + privateKey, _ := GeneratePrivateKey(bytes.NewBuffer(hasher.Sum(nil))) + return privateKey +} + +// Generates private key from a source of random bytes, if randomReader is nil crypto/rand.Reader is useds +func GeneratePrivateKey(randomReader io.Reader) (PrivateKey, error) { + if randomReader == nil { + randomReader = rand.Reader + } + _, ed25519PrivateKey, err := ed25519.GenerateKey(randomReader) + if err != nil { + return PrivateKey{}, err + } + return Ed25519PrivateKeyFromRawBytes(ed25519PrivateKey) +} + +// Creates an ed25519 key from the raw private key bytes +func Ed25519PrivateKeyFromRawBytes(privKeyBytes []byte) (PrivateKey, error) { + privKeyEd25519 := crypto.PrivKeyEd25519{} + if len(privKeyBytes) != len(privKeyEd25519) { + return PrivateKey{}, fmt.Errorf("bytes passed have length %v by ed25519 private keys have %v bytes", + len(privKeyBytes), len(privKeyEd25519)) + } + err := EnsureEd25519PrivateKeyCorrect(privKeyBytes) + if err != nil { + return PrivateKey{}, err + } + copy(privKeyEd25519[:], privKeyBytes) + return PrivateKey{ + PrivKey: privKeyEd25519.Wrap(), + }, nil +} + +// Ensures the last 32 bytes of the ed25519 private key is the public key derived from the first 32 private bytes +func EnsureEd25519PrivateKeyCorrect(candidatePrivateKey ed25519.PrivateKey) error { + if len(candidatePrivateKey) != ed25519.PrivateKeySize { + return fmt.Errorf("ed25519 key has size %v but %v bytes passed as key", ed25519.PrivateKeySize, + len(candidatePrivateKey)) + } + _, derivedPrivateKey, err := ed25519.GenerateKey(bytes.NewBuffer(candidatePrivateKey)) + if err != nil { + return err + } + if !bytes.Equal(derivedPrivateKey, candidatePrivateKey) { + return fmt.Errorf("ed25519 key generated from prefix of %X should equal %X, but is %X", + candidatePrivateKey, candidatePrivateKey, derivedPrivateKey) + } + return nil +} + +// Returns a copy of the raw untyped private key bytes +func (pk PrivateKey) RawBytes() []byte { + switch privKey := pk.PrivKey.Unwrap().(type) { + case crypto.PrivKeyEd25519: + privKeyCopy := crypto.PrivKeyEd25519{} + copy(privKeyCopy[:], privKey[:]) + return privKeyCopy[:] + case crypto.PrivKeySecp256k1: + privKeyCopy := crypto.PrivKeySecp256k1{} + copy(privKeyCopy[:], privKey[:]) + return privKeyCopy[:] + default: + return nil + } +} + +func (pk PrivateKey) PublicKey() PublicKey { + return PublicKeyFromGoCryptoPubKey(pk.PubKey()) +} + +func (pk PrivateKey) Sign(msg []byte) (Signature, error) { + return Signature{Signature: pk.PrivKey.Sign(msg)}, nil +} + +func (pk PrivateKey) MarshalJSON() ([]byte, error) { + return pk.PrivKey.MarshalJSON() +} + +func (pk *PrivateKey) UnmarshalJSON(data []byte) error { + return pk.PrivKey.UnmarshalJSON(data) +} + +func (pk PrivateKey) MarshalText() ([]byte, error) { + return pk.MarshalJSON() +} + +func (pk *PrivateKey) UnmarshalText(text []byte) error { + return pk.UnmarshalJSON(text) +} + +// Signature + +type Signature struct { + crypto.Signature `json:"unwrap"` +} + +func SignatureFromGoCryptoSignature(signature crypto.Signature) Signature { + return Signature{Signature: signature} +} + +// Currently this is a stub that reads the raw bytes returned by key_client and returns +// an ed25519 signature. +func SignatureFromBytes(bs []byte) (Signature, error) { + //TODO: read a typed representation (most likely JSON) and do the right thing here + // Only supports ed25519 currently so no switch on signature scheme + signatureEd25519 := crypto.SignatureEd25519{} + if len(bs) != len(signatureEd25519) { + return Signature{}, fmt.Errorf("bytes passed have length %v by ed25519 signatures have %v bytes", + len(bs), len(signatureEd25519)) + } + copy(signatureEd25519[:], bs) + return Signature{ + Signature: signatureEd25519.Wrap(), + }, nil +} + +func (s Signature) GoCryptoSignature() crypto.Signature { + return s.Signature +} + +func (s Signature) MarshalJSON() ([]byte, error) { + return s.Signature.MarshalJSON() +} + +func (s *Signature) UnmarshalJSON(data []byte) error { + return s.Signature.UnmarshalJSON(data) +} + +func (s Signature) MarshalText() ([]byte, error) { + return s.MarshalJSON() +} + +func (s *Signature) UnmarshalText(text []byte) error { + return s.UnmarshalJSON(text) +} diff --git a/account/crypto_test.go b/account/crypto_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d412e0fc4c83a2ffc4a08088f3ae8182ce90caf5 --- /dev/null +++ b/account/crypto_test.go @@ -0,0 +1,28 @@ +package account + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGeneratePrivateKey(t *testing.T) { + privateKey, err := GeneratePrivateKey(bytes.NewBuffer([]byte{ + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + })) + require.NoError(t, err) + assert.NoError(t, EnsureEd25519PrivateKeyCorrect(privateKey.RawBytes())) + badKey := privateKey.RawBytes() + // Change part of the public part to not match private part + badKey[35] = 2 + assert.Error(t, EnsureEd25519PrivateKeyCorrect(badKey)) + goodKey := privateKey.RawBytes() + // Change part of the private part invalidating public part + goodKey[31] = 2 + assert.Error(t, EnsureEd25519PrivateKeyCorrect(badKey)) +} diff --git a/account/keys.go b/account/keys.go deleted file mode 100644 index 189a9b318e3882b6209a4beefc83d2d2501d57b2..0000000000000000000000000000000000000000 --- a/account/keys.go +++ /dev/null @@ -1,61 +0,0 @@ -package account - -import "github.com/tendermint/go-crypto" - -// This allows us to control serialisation - -type PublicKey struct { - crypto.PubKey `json:"unwrap"` -} - -func PublicKeyFromPubKey(pubKey crypto.PubKey) PublicKey { - return PublicKey{PubKey: pubKey} -} - -func PrivateKeyFromPrivKey(privKey crypto.PrivKey) PrivateKey { - return PrivateKey{PrivKey: privKey} -} - -func (pk PublicKey) Address() Address { - return MustAddressFromBytes(pk.PubKey.Address()) -} - -func (pk PublicKey) MarshalJSON() ([]byte, error) { - return pk.PubKey.MarshalJSON() -} - -func (pk *PublicKey) UnmarshalJSON(data []byte) error { - return pk.PubKey.UnmarshalJSON(data) -} - -func (pk PublicKey) MarshalText() ([]byte, error) { - return pk.MarshalJSON() -} - -func (pk *PublicKey) UnmarshalText(text []byte) error { - return pk.UnmarshalJSON(text) -} - -type PrivateKey struct { - crypto.PrivKey -} - -func (pk PrivateKey) PublicKey() PublicKey { - return PublicKeyFromPubKey(pk.PubKey()) -} - -func (pk PrivateKey) MarshalJSON() ([]byte, error) { - return pk.PrivKey.MarshalJSON() -} - -func (pk *PrivateKey) UnmarshalJSON(data []byte) error { - return pk.PrivKey.UnmarshalJSON(data) -} - -func (pk PrivateKey) MarshalText() ([]byte, error) { - return pk.MarshalJSON() -} - -func (pk *PrivateKey) UnmarshalText(text []byte) error { - return pk.UnmarshalJSON(text) -} diff --git a/account/private_account.go b/account/private_account.go index 9dc73dbb30e7ec47a3f48ef320b4f30ea460c62f..e1fa11d9b0a57ee78574a62091669d944e5beab7 100644 --- a/account/private_account.go +++ b/account/private_account.go @@ -17,15 +17,9 @@ package account import ( "fmt" - "github.com/tendermint/ed25519" - "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) -type Signer interface { - Sign(msg []byte) (crypto.Signature, error) -} - type PrivateAccount interface { Addressable PrivateKey() PrivateKey @@ -46,6 +40,20 @@ var _ PrivateAccount = concretePrivateAccountWrapper{} var _ = wire.RegisterInterface(struct{ PrivateAccount }{}, wire.ConcreteType{concretePrivateAccountWrapper{}, 0x01}) +func AsConcretePrivateAccount(privateAccount PrivateAccount) *ConcretePrivateAccount { + if privateAccount == nil { + return nil + } + // Avoid a copy + if ca, ok := privateAccount.(concretePrivateAccountWrapper); ok { + return ca.ConcretePrivateAccount + } + return &ConcretePrivateAccount{ + Address: privateAccount.Address(), + PublicKey: privateAccount.PublicKey(), + PrivateKey: privateAccount.PrivateKey(), + } +} func (cpaw concretePrivateAccountWrapper) Address() Address { return cpaw.ConcretePrivateAccount.Address } @@ -58,30 +66,22 @@ func (cpaw concretePrivateAccountWrapper) PrivateKey() PrivateKey { return cpaw.ConcretePrivateAccount.PrivateKey } -func (pa ConcretePrivateAccount) PrivateAccount() concretePrivateAccountWrapper { - return concretePrivateAccountWrapper{&pa} +func (pa ConcretePrivateAccount) PrivateAccount() PrivateAccount { + return concretePrivateAccountWrapper{ConcretePrivateAccount: &pa} } -func (pa ConcretePrivateAccount) Sign(msg []byte) (crypto.Signature, error) { - return pa.PrivateKey.Sign(msg), nil +func (pa ConcretePrivateAccount) Sign(msg []byte) (Signature, error) { + return pa.PrivateKey.Sign(msg) } -func ChainSign(pa PrivateAccount, chainID string, o Signable) crypto.Signature { - sig, _ := pa.Sign(SignBytes(chainID, o)) +func ChainSign(pa PrivateAccount, chainID string, o Signable) Signature { + sig, err := pa.Sign(SignBytes(chainID, o)) + if err != nil { + panic(err) + } return sig } -func (pa *ConcretePrivateAccount) Generate(index int) concretePrivateAccountWrapper { - newPrivKey := PrivateKeyFromPrivKey(pa.PrivateKey.Unwrap().(crypto.PrivKeyEd25519).Generate(index).Wrap()) - newPubKey := PublicKeyFromPubKey(newPrivKey.PubKey()) - newAddress := newPubKey.Address() - return ConcretePrivateAccount{ - Address: newAddress, - PublicKey: newPubKey, - PrivateKey: newPrivKey, - }.PrivateAccount() -} - func (pa *ConcretePrivateAccount) String() string { return fmt.Sprintf("ConcretePrivateAccount{%s}", pa.Address) } @@ -89,48 +89,39 @@ func (pa *ConcretePrivateAccount) String() string { //---------------------------------------- // Generates a new account with private key. -func GeneratePrivateAccount() concretePrivateAccountWrapper { - privKeyBytes := new([64]byte) - copy(privKeyBytes[:32], crypto.CRandBytes(32)) - pubKeyBytes := ed25519.MakePublicKey(privKeyBytes) - publicKey := PublicKeyFromPubKey(crypto.PubKeyEd25519(*pubKeyBytes).Wrap()) - address := publicKey.Address() - privateKey := PrivateKeyFromPrivKey(crypto.PrivKeyEd25519(*privKeyBytes).Wrap()) +func GeneratePrivateAccount() (PrivateAccount, error) { + privateKey, err := GeneratePrivateKey(nil) + if err != nil { + return nil, err + } + publicKey := privateKey.PublicKey() return ConcretePrivateAccount{ - Address: address, + Address: publicKey.Address(), PublicKey: publicKey, PrivateKey: privateKey, - }.PrivateAccount() -} - -func PrivateKeyFromSecret(secret string) PrivateKey { - return PrivateKeyFromPrivKey(crypto.GenPrivKeyEd25519FromSecret(wire.BinarySha256(secret)).Wrap()) + }.PrivateAccount(), nil } // Generates a new account with private key from SHA256 hash of a secret -func GeneratePrivateAccountFromSecret(secret string) concretePrivateAccountWrapper { - privKey := PrivateKeyFromSecret(secret) - pubKey := PublicKeyFromPubKey(privKey.PubKey()) +func GeneratePrivateAccountFromSecret(secret string) PrivateAccount { + privateKey := PrivateKeyFromSecret(secret) + publicKey := privateKey.PublicKey() return ConcretePrivateAccount{ - Address: pubKey.Address(), - PublicKey: pubKey, - PrivateKey: privKey, + Address: publicKey.Address(), + PublicKey: publicKey, + PrivateKey: privateKey, }.PrivateAccount() } -func GeneratePrivateAccountFromPrivateKeyBytes(privKeyBytes []byte) concretePrivateAccountWrapper { - if len(privKeyBytes) != 64 { - panic(fmt.Sprintf("Expected 64 bytes but got %v", len(privKeyBytes))) +func GeneratePrivateAccountFromPrivateKeyBytes(privKeyBytes []byte) (PrivateAccount, error) { + privateKey, err := Ed25519PrivateKeyFromRawBytes(privKeyBytes) + if err != nil { + return nil, err } - var privKeyArray [64]byte - copy(privKeyArray[:], privKeyBytes) - pubKeyBytes := ed25519.MakePublicKey(&privKeyArray) - publicKey := PublicKeyFromPubKey(crypto.PubKeyEd25519(*pubKeyBytes).Wrap()) - address := publicKey.Address() - privateKey := PrivateKeyFromPrivKey(crypto.PrivKeyEd25519(privKeyArray).Wrap()) + publicKey := privateKey.PublicKey() return ConcretePrivateAccount{ - Address: address, + Address: publicKey.Address(), PublicKey: publicKey, PrivateKey: privateKey, - }.PrivateAccount() + }.PrivateAccount(), nil } diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go index 1289774c9aa2bafc5191d09e467ae800ffb1e28c..9ae5f1923c53bc7f17aca2fbd307ddbd52e05a42 100644 --- a/client/mock/client_mock.go +++ b/client/mock/client_mock.go @@ -53,7 +53,7 @@ func (mock *MockNodeClient) GetAccount(address acm.Address) (acm.Account, error) // make zero account return acm.ConcreteAccount{ Address: address, - PublicKey: acm.PublicKeyFromPubKey(crypto.PubKeyEd25519{}.Wrap()), + PublicKey: acm.PublicKeyFromGoCryptoPubKey(crypto.PubKeyEd25519{}.Wrap()), Code: make([]byte, 0), StorageRoot: make([]byte, 0), }.Account(), nil diff --git a/client/rpc/client_util.go b/client/rpc/client_util.go index 8ad34c261e2787ea75e14cf73b9cf860f2548ee8..1afb93c98bbff50fbd809c0195a0985a13bd8e9d 100644 --- a/client/rpc/client_util.go +++ b/client/rpc/client_util.go @@ -26,7 +26,6 @@ import ( "github.com/hyperledger/burrow/permission" ptypes "github.com/hyperledger/burrow/permission/types" "github.com/hyperledger/burrow/txs" - "github.com/tendermint/go-crypto" ) //------------------------------------------------------------------------------------ @@ -115,10 +114,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, err = fmt.Errorf("pubkey is bad hex: %v", err) return } - - pubKeyEd25519 := crypto.PubKeyEd25519{} - copy(pubKeyEd25519[:], pubKeyBytes) - pub = acm.PublicKeyFromPubKey(pubKeyEd25519.Wrap()) + pub, err = acm.PublicKeyFromBytes(pubKeyBytes) + if err != nil { + return + } } else { // grab the pubkey from monax-keys addressBytes, err2 := hex.DecodeString(addr) diff --git a/consensus/tendermint/query/node_view.go b/consensus/tendermint/query/node_view.go index 69949436a27d1966971869927d326d28da5eb33b..7aa451766e419053cfecfe9b31071b0286e27497 100644 --- a/consensus/tendermint/query/node_view.go +++ b/consensus/tendermint/query/node_view.go @@ -47,7 +47,7 @@ func NewNodeView(tmNode *node.Node, txDecoder txs.Decoder) NodeView { } func (nv *nodeView) PrivValidatorPublicKey() acm.PublicKey { - return acm.PublicKeyFromPubKey(nv.tmNode.PrivValidator().GetPubKey()) + return acm.PublicKeyFromGoCryptoPubKey(nv.tmNode.PrivValidator().GetPubKey()) } func (nv *nodeView) NodeInfo() *p2p.NodeInfo { diff --git a/consensus/tendermint/validator/priv_validator_memory.go b/consensus/tendermint/validator/priv_validator_memory.go index f8581144758dcdd0f519f26c4d777d8cc471c0df..90a51fa9f095bf4b4ae202bb7077e1fc80c4e03d 100644 --- a/consensus/tendermint/validator/priv_validator_memory.go +++ b/consensus/tendermint/validator/priv_validator_memory.go @@ -34,13 +34,29 @@ func (pvm *privValidatorMemory) GetPubKey() crypto.PubKey { } func (pvm *privValidatorMemory) SignVote(chainID string, vote *tm_types.Vote) error { - return pvm.lastSignedInfo.SignVote(pvm, chainID, vote) + return pvm.lastSignedInfo.SignVote(asTendermintSigner(pvm.Signer), chainID, vote) } func (pvm *privValidatorMemory) SignProposal(chainID string, proposal *tm_types.Proposal) error { - return pvm.lastSignedInfo.SignProposal(pvm, chainID, proposal) + return pvm.lastSignedInfo.SignProposal(asTendermintSigner(pvm.Signer), chainID, proposal) } func (pvm *privValidatorMemory) SignHeartbeat(chainID string, heartbeat *tm_types.Heartbeat) error { - return pvm.lastSignedInfo.SignHeartbeat(pvm, chainID, heartbeat) + return pvm.lastSignedInfo.SignHeartbeat(asTendermintSigner(pvm.Signer), chainID, heartbeat) +} + +func asTendermintSigner(signer acm.Signer) tm_types.Signer { + return tendermintSigner{Signer: signer} +} + +type tendermintSigner struct { + acm.Signer +} + +func (tms tendermintSigner) Sign(msg []byte) (crypto.Signature, error) { + sig, err := tms.Signer.Sign(msg) + if err != nil { + return crypto.Signature{}, err + } + return sig.GoCryptoSignature(), nil } diff --git a/execution/accounts.go b/execution/accounts.go index 0e9d1bd138e15b360e1d91c53f970664576da7be..8a54e2f5a368542211afd38d5f4eeede165b8c59 100644 --- a/execution/accounts.go +++ b/execution/accounts.go @@ -77,8 +77,11 @@ func newAccounts(state acm.StateIterable) *accounts { // Generate a new Private Key Account. func (accs *accounts) GenPrivAccount() (*acm.ConcretePrivateAccount, error) { - pa := acm.GeneratePrivateAccount().ConcretePrivateAccount - return pa, nil + pa, err := acm.GeneratePrivateAccount() + if err != nil { + return nil, err + } + return acm.AsConcretePrivateAccount(pa), nil } // Generate a new Private Key Account. @@ -88,8 +91,11 @@ func (accs *accounts) GenPrivAccountFromKey(privKey []byte) ( return nil, fmt.Errorf("Private key is not 64 bytes long.") } fmt.Printf("PK BYTES FROM ACCOUNTS: %x\n", privKey) - pa := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey).ConcretePrivateAccount - return pa, nil + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } + return acm.AsConcretePrivateAccount(pa), nil } // Get all accounts. diff --git a/execution/transactor.go b/execution/transactor.go index fc917c07b065cc2c1b1c92230b55043d3da21e83..90df6728900fd3cbfb963916ad4b072eda3a7376 100644 --- a/execution/transactor.go +++ b/execution/transactor.go @@ -175,7 +175,10 @@ func (trans *transactor) Transact(privKey []byte, address acm.Address, data []by } trans.txMtx.Lock() defer trans.txMtx.Unlock() - pa := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } // [Silas] This is puzzling, if the account doesn't exist the CallTx will fail, so what's the point in this? acc, err := trans.state.GetAccount(pa.Address()) if err != nil { @@ -289,7 +292,10 @@ func (trans *transactor) Send(privKey []byte, toAddress acm.Address, amount uint copy(pk[:], privKey) trans.txMtx.Lock() defer trans.txMtx.Unlock() - pa := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } cache := trans.state acc, err := cache.GetAccount(pa.Address()) if err != nil { @@ -359,7 +365,10 @@ func (trans *transactor) SendAndHold(privKey []byte, toAddress acm.Address, amou var rErr error - pa := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if tErr != nil { + return nil, err + } select { case <-toChan: @@ -381,7 +390,10 @@ func (trans *transactor) TransactNameReg(privKey []byte, name, data string, amou } trans.txMtx.Lock() defer trans.txMtx.Unlock() - pa := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } cache := trans.state // XXX: DON'T MUTATE THIS CACHE (used internally for CheckTx) acc, err := cache.GetAccount(pa.Address()) if err != nil { diff --git a/genesis/deterministic_genesis.go b/genesis/deterministic_genesis.go index 7eebd858da93ea33b22e816b6bf2f5ae63337a6c..19d94cbf3cbd1d7c922732fdac67e4b6d5acd0f1 100644 --- a/genesis/deterministic_genesis.go +++ b/genesis/deterministic_genesis.go @@ -1,15 +1,12 @@ package genesis import ( + "fmt" "math/rand" "time" - "fmt" - acm "github.com/hyperledger/burrow/account" "github.com/hyperledger/burrow/permission" - "github.com/tendermint/ed25519" - "github.com/tendermint/go-crypto" ) type deterministicGenesis struct { @@ -67,8 +64,11 @@ func (dg *deterministicGenesis) GenesisDoc(numAccounts int, randBalance bool, mi } func (dg *deterministicGenesis) Account(randBalance bool, minBalance uint64) (acm.Account, acm.PrivateAccount) { - privKey := dg.PrivateKey() - pubKey := acm.PublicKeyFromPubKey(privKey.PubKey()) + privKey, err := acm.GeneratePrivateKey(dg.random) + if err != nil { + panic(fmt.Errorf("could not generate private key deterministically")) + } + pubKey := acm.PublicKeyFromGoCryptoPubKey(privKey.PubKey()) privAccount := &acm.ConcretePrivateAccount{ PublicKey: pubKey, PrivateKey: privKey, @@ -87,12 +87,3 @@ func (dg *deterministicGenesis) Account(randBalance bool, minBalance uint64) (ac } return acc.Account(), privAccount.PrivateAccount() } - -func (dg *deterministicGenesis) PrivateKey() acm.PrivateKey { - privKeyBytes := new([64]byte) - for i := 0; i < 32; i++ { - privKeyBytes[i] = byte(dg.random.Int() % 256) - } - ed25519.MakePublicKey(privKeyBytes) - return acm.PrivateKeyFromPrivKey(crypto.PrivKeyEd25519(*privKeyBytes).Wrap()) -} diff --git a/keys/key_client.go b/keys/key_client.go index 5ca92be2498c77a641445497288af0758ebccbab..2afb5ce3132ccdd266dab9795cc35234479d17a2 100644 --- a/keys/key_client.go +++ b/keys/key_client.go @@ -15,19 +15,17 @@ package keys import ( - "bytes" "encoding/hex" "fmt" acm "github.com/hyperledger/burrow/account" "github.com/hyperledger/burrow/logging" logging_types "github.com/hyperledger/burrow/logging/types" - "github.com/tendermint/go-crypto" ) type KeyClient interface { // Sign returns the signature bytes for given message signed with the key associated with signAddress - Sign(signAddress acm.Address, message []byte) (signature crypto.Signature, err error) + Sign(signAddress acm.Address, message []byte) (signature acm.Signature, err error) // PublicKey returns the public key associated with a given address PublicKey(address acm.Address) (publicKey acm.PublicKey, err error) @@ -35,7 +33,7 @@ type KeyClient interface { // Generate requests that a key be generate within the keys instance and returns the address Generate(keyName string, keyType KeyType) (keyAddress acm.Address, err error) - // Returns nil if the keys isntance is health, error otherwise + // Returns nil if the keys instance is healthy, error otherwise HealthCheck() error } @@ -57,16 +55,16 @@ const ( KeyTypeDefault = KeyTypeEd25519Ripemd160 ) -// NOTE [ben] Compiler check to ensure monaxKeyClient successfully implements +// NOTE [ben] Compiler check to ensure keyClient successfully implements // burrow/keys.KeyClient -var _ KeyClient = (*monaxKeyClient)(nil) +var _ KeyClient = (*keyClient)(nil) -type monaxKeyClient struct { +type keyClient struct { rpcString string logger logging_types.InfoTraceLogger } -type monaxSigner struct { +type signer struct { keyClient KeyClient address acm.Address } @@ -74,7 +72,7 @@ type monaxSigner struct { // Creates a Signer that assumes the address holds an Ed25519 key func Signer(keyClient KeyClient, address acm.Address) acm.Signer { // TODO: we can do better than this and return a typed signature when we reform the keys service - return &monaxSigner{ + return &signer{ keyClient: keyClient, address: address, } @@ -104,18 +102,18 @@ func Addressable(keyClient KeyClient, address acm.Address) (acm.Addressable, err }, nil } -func (ms *monaxSigner) Sign(messsage []byte) (crypto.Signature, error) { +func (ms *signer) Sign(messsage []byte) (acm.Signature, error) { signature, err := ms.keyClient.Sign(ms.address, messsage) if err != nil { - return crypto.Signature{}, err + return acm.Signature{}, err } return signature, nil } -// monaxKeyClient.New returns a new monax-keys client for provided rpc location +// keyClient.New returns a new monax-keys client for provided rpc location // Monax-keys connects over http request-responses -func NewBurrowKeyClient(rpcString string, logger logging_types.InfoTraceLogger) *monaxKeyClient { - return &monaxKeyClient{ +func NewBurrowKeyClient(rpcString string, logger logging_types.InfoTraceLogger) *keyClient { + return &keyClient{ rpcString: rpcString, logger: logging.WithScope(logger, "BurrowKeyClient"), } @@ -123,25 +121,25 @@ func NewBurrowKeyClient(rpcString string, logger logging_types.InfoTraceLogger) // Monax-keys client Sign requests the signature from BurrowKeysClient over rpc for the given // bytes to be signed and the address to sign them with. -func (monaxKeys *monaxKeyClient) Sign(signAddress acm.Address, message []byte) (crypto.Signature, error) { +func (monaxKeys *keyClient) Sign(signAddress acm.Address, message []byte) (acm.Signature, error) { args := map[string]string{ "msg": hex.EncodeToString(message), "addr": signAddress.String(), } sigS, err := RequestResponse(monaxKeys.rpcString, "sign", args, monaxKeys.logger) if err != nil { - return crypto.Signature{}, err + return acm.Signature{}, err } sigBytes, err := hex.DecodeString(sigS) if err != nil { - return crypto.Signature{}, err + return acm.Signature{}, err } - return crypto.SignatureEd25519FromBytes(sigBytes), err + return acm.SignatureFromBytes(sigBytes) } // Monax-keys client PublicKey requests the public key associated with an address from // the monax-keys server. -func (monaxKeys *monaxKeyClient) PublicKey(address acm.Address) (acm.PublicKey, error) { +func (monaxKeys *keyClient) PublicKey(address acm.Address) (acm.PublicKey, error) { args := map[string]string{ "addr": address.String(), } @@ -149,20 +147,22 @@ func (monaxKeys *monaxKeyClient) PublicKey(address acm.Address) (acm.PublicKey, if err != nil { return acm.PublicKey{}, err } - pubKey := crypto.PubKeyEd25519{} pubKeyBytes, err := hex.DecodeString(pubS) if err != nil { return acm.PublicKey{}, err } - copy(pubKey[:], pubKeyBytes) - if !bytes.Equal(address.Bytes(), pubKey.Address()) { - return acm.PublicKey{}, fmt.Errorf("public key %s maps to address %X but was returned for address %s", - pubKey, pubKey.Address(), address) + publicKey, err := acm.PublicKeyFromBytes(pubKeyBytes) + if err != nil { + return acm.PublicKey{}, err + } + if address != publicKey.Address() { + return acm.PublicKey{}, fmt.Errorf("public key %s maps to address %s but was returned for address %s", + publicKey, publicKey.Address(), address) } - return acm.PublicKeyFromPubKey(pubKey.Wrap()), nil + return publicKey, nil } -func (monaxKeys *monaxKeyClient) Generate(keyName string, keyType KeyType) (acm.Address, error) { +func (monaxKeys *keyClient) Generate(keyName string, keyType KeyType) (acm.Address, error) { args := map[string]string{ //"auth": auth, "name": keyName, @@ -175,7 +175,7 @@ func (monaxKeys *monaxKeyClient) Generate(keyName string, keyType KeyType) (acm. return acm.AddressFromHexString(addr) } -func (monaxKeys *monaxKeyClient) HealthCheck() error { +func (monaxKeys *keyClient) HealthCheck() error { _, err := RequestResponse(monaxKeys.rpcString, "name/ls", nil, monaxKeys.logger) return err } diff --git a/keys/key_client_test.go b/keys/key_client_test.go index 4c483c789cafbb6187233595c2ea78686d16d811..127a677e7d4d24c6a365534117864391e717e932 100644 --- a/keys/key_client_test.go +++ b/keys/key_client_test.go @@ -28,7 +28,7 @@ func TestMain(m *testing.M) { fatalf("couldn't create temp dir: %v", err) } go keys.StartServer(keysHost, keysPort) - m.Run() + os.Exit(m.Run()) } func TestMonaxKeyClient_Generate(t *testing.T) { @@ -43,7 +43,7 @@ func TestMonaxKeyClient_PublicKey(t *testing.T) { addr, err := keyClient.Generate("I'm a lovely hat", KeyTypeEd25519Ripemd160) assert.NoError(t, err) pubKey, err := keyClient.PublicKey(addr) - assert.Equal(t, addr[:], pubKey.Address()) + assert.Equal(t, addr, pubKey.Address()) } func TestMonaxKeyClient_PublicKey_NonExistent(t *testing.T) { diff --git a/keys/mock/key_client_mock.go b/keys/mock/key_client_mock.go index 4a31a0fb2831f5372ef5062f4358eb5ac61eec23..53b311aed212eba77c4c7e42ca8fe36e2946e33c 100644 --- a/keys/mock/key_client_mock.go +++ b/keys/mock/key_client_mock.go @@ -21,7 +21,7 @@ import ( acm "github.com/hyperledger/burrow/account" . "github.com/hyperledger/burrow/keys" "github.com/tendermint/ed25519" - "github.com/tendermint/go-crypto" + crypto "github.com/tendermint/go-crypto" "golang.org/x/crypto/ripemd160" ) @@ -59,11 +59,8 @@ func newMockKey() (*MockKey, error) { return key, nil } -func (mockKey *MockKey) Sign(message []byte) (crypto.Signature, error) { - sigEd25519 := crypto.SignatureEd25519{} - signatureBytes := ed25519.Sign(&mockKey.PrivateKey, message) - copy(sigEd25519[:], signatureBytes[:]) - return sigEd25519.Wrap(), nil +func (mockKey *MockKey) Sign(message []byte) (acm.Signature, error) { + return acm.SignatureFromBytes(ed25519.Sign(&mockKey.PrivateKey, message)[:]) } //--------------------------------------------------------------------- @@ -92,10 +89,10 @@ func (mock *MockKeyClient) NewKey() acm.Address { return key.Address } -func (mock *MockKeyClient) Sign(signAddress acm.Address, message []byte) (crypto.Signature, error) { +func (mock *MockKeyClient) Sign(signAddress acm.Address, message []byte) (acm.Signature, error) { key := mock.knownKeys[signAddress] if key == nil { - return crypto.Signature{}, fmt.Errorf("Unknown address (%s)", signAddress) + return acm.Signature{}, fmt.Errorf("Unknown address (%s)", signAddress) } return key.Sign(message) } @@ -107,7 +104,7 @@ func (mock *MockKeyClient) PublicKey(address acm.Address) (acm.PublicKey, error) } pubKeyEd25519 := crypto.PubKeyEd25519{} copy(pubKeyEd25519[:], key.PublicKey) - return acm.PublicKeyFromPubKey(pubKeyEd25519.Wrap()), nil + return acm.PublicKeyFromGoCryptoPubKey(pubKeyEd25519.Wrap()), nil } func (mock *MockKeyClient) Generate(keyName string, keyType KeyType) (acm.Address, error) { diff --git a/rpc/service.go b/rpc/service.go index b2fc8454be2d56643475d6cb46803ed6d87e3a98..8dfcbdd5a85b8801fd97849950bd5e8caa353f62 100644 --- a/rpc/service.go +++ b/rpc/service.go @@ -395,7 +395,11 @@ func (s *service) SignTx(tx txs.Tx, concretePrivateAccounts []*acm.ConcretePriva } func (s *service) GeneratePrivateAccount() (*ResultGeneratePrivateAccount, error) { + privateAccount, err := acm.GeneratePrivateAccount() + if err != nil { + return nil, err + } return &ResultGeneratePrivateAccount{ - PrivAccount: acm.GeneratePrivateAccount().ConcretePrivateAccount, + PrivAccount: acm.AsConcretePrivateAccount(privateAccount), }, nil } diff --git a/txs/tx.go b/txs/tx.go index 1cb2ea1b40e654562bc09457bfd78ff182edc064..6d061b04522d59d623e6be789da4a8be5dc9ea96 100644 --- a/txs/tx.go +++ b/txs/tx.go @@ -22,7 +22,6 @@ import ( acm "github.com/hyperledger/burrow/account" ptypes "github.com/hyperledger/burrow/permission" - "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" "github.com/tendermint/go-wire/data" "golang.org/x/crypto/ripemd160" @@ -149,11 +148,11 @@ type ( } TxInput struct { - Address acm.Address `json:"address"` // Hash of the PublicKey - Amount uint64 `json:"amount"` // Must not exceed account balance - Sequence uint64 `json:"sequence"` // Must be 1 greater than the last committed TxInput - Signature crypto.Signature `json:"signature"` // Depends on the PublicKey type and the whole Tx - PubKey acm.PublicKey `json:"pub_key"` // Must not be nil, may be nil + Address acm.Address `json:"address"` // Hash of the PublicKey + Amount uint64 `json:"amount"` // Must not exceed account balance + Sequence uint64 `json:"sequence"` // Must be 1 greater than the last committed TxInput + Signature acm.Signature `json:"signature"` // Depends on the PublicKey type and the whole Tx + PubKey acm.PublicKey `json:"pub_key"` // Must not be nil, may be nil } TxOutput struct { @@ -311,10 +310,10 @@ func (tx *NameTx) String() string { //----------------------------------------------------------------------------- type BondTx struct { - PubKey acm.PublicKey `json:"pub_key"` // NOTE: these don't have type byte - Signature crypto.Signature `json:"signature"` - Inputs []*TxInput `json:"inputs"` - UnbondTo []*TxOutput `json:"unbond_to"` + PubKey acm.PublicKey `json:"pub_key"` // NOTE: these don't have type byte + Signature acm.Signature `json:"signature"` + Inputs []*TxInput `json:"inputs"` + UnbondTo []*TxOutput `json:"unbond_to"` } func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { @@ -345,9 +344,9 @@ func (tx *BondTx) String() string { //----------------------------------------------------------------------------- type UnbondTx struct { - Address acm.Address `json:"address"` - Height int `json:"height"` - Signature crypto.Signature `json:"signature"` + Address acm.Address `json:"address"` + Height int `json:"height"` + Signature acm.Signature `json:"signature"` } func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { @@ -362,9 +361,9 @@ func (tx *UnbondTx) String() string { //----------------------------------------------------------------------------- type RebondTx struct { - Address acm.Address `json:"address"` - Height int `json:"height"` - Signature crypto.Signature `json:"signature"` + Address acm.Address `json:"address"` + Height int `json:"height"` + Signature acm.Signature `json:"signature"` } func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { @@ -379,7 +378,7 @@ func (tx *RebondTx) String() string { //----------------------------------------------------------------------------- type PermissionsTx struct { - Input *TxInput `json:"input"` + Input *TxInput `json:"input"` PermArgs *ptypes.PermArgs `json:"args"` } diff --git a/txs/tx_test.go b/txs/tx_test.go index c8c39a6200c9c5172594ab88fcf900b10bf5cd93..7ec8f44004742ab8bf7c63bd2a278d44877c88f0 100644 --- a/txs/tx_test.go +++ b/txs/tx_test.go @@ -111,8 +111,7 @@ func TestNameTxSignable(t *testing.T) { } func TestBondTxSignable(t *testing.T) { - privKeyBytes := make([]byte, 64) - privAccount := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKeyBytes) + privAccount := acm.GeneratePrivateAccountFromSecret("foooobars") bondTx := &BondTx{ PubKey: privAccount.PublicKey(), Inputs: []*TxInput{ @@ -138,8 +137,20 @@ func TestBondTxSignable(t *testing.T) { }, }, } - expected := fmt.Sprintf(`{"chain_id":"%s","tx":[17,{"inputs":[{"address":"%s","amount":12345,"sequence":67890},{"address":"%s","amount":111,"sequence":222}],"pub_key":[1,"3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"],"unbond_to":[{"address":"%s","amount":333},{"address":"%s","amount":444}]}]}`, - chainID, bondTx.Inputs[0].Address.String(), bondTx.Inputs[1].Address.String(), bondTx.UnbondTo[0].Address.String(), bondTx.UnbondTo[1].Address.String()) + expected := fmt.Sprintf(`{"chain_id":"%s",`+ + `"tx":[17,{"inputs":[{"address":"%s",`+ + `"amount":12345,"sequence":67890},{"address":"%s",`+ + `"amount":111,"sequence":222}],"pub_key":[1,"%X"],`+ + `"unbond_to":[{"address":"%s",`+ + `"amount":333},{"address":"%s",`+ + `"amount":444}]}]}`, + chainID, + bondTx.Inputs[0].Address.String(), + bondTx.Inputs[1].Address.String(), + bondTx.PubKey.RawBytes(), + bondTx.UnbondTo[0].Address.String(), + bondTx.UnbondTo[1].Address.String()) + assert.Equal(t, expected, string(acm.SignBytes(chainID, bondTx)), "Unexpected sign string for BondTx") } diff --git a/txs/tx_utils.go b/txs/tx_utils.go index 1b2d65fe3f3a693c41c7fe29e51022c8e1971d6f..753f3e3229273d7ae6970f3435f3f2cf975d981e 100644 --- a/txs/tx_utils.go +++ b/txs/tx_utils.go @@ -19,8 +19,6 @@ import ( acm "github.com/hyperledger/burrow/account" ptypes "github.com/hyperledger/burrow/permission" - - "github.com/tendermint/go-crypto" ) //---------------------------------------------------------------------------- @@ -51,7 +49,6 @@ func (tx *SendTx) AddInputWithSequence(pubkey acm.PublicKey, amt uint64, sequenc Address: addr, Amount: amt, Sequence: sequence, - Signature: crypto.SignatureEd25519{}.Wrap(), PubKey: pubkey, }) return nil @@ -99,7 +96,6 @@ func NewCallTxWithSequence(from acm.PublicKey, to *acm.Address, data []byte, Address: from.Address(), Amount: amt, Sequence: sequence, - Signature: crypto.SignatureEd25519{}.Wrap(), PubKey: from, } @@ -139,7 +135,6 @@ func NewNameTxWithSequence(from acm.PublicKey, name, data string, amt, fee, sequ Address: from.Address(), Amount: amt, Sequence: sequence, - Signature: crypto.SignatureEd25519{}.Wrap(), PubKey: from, } @@ -184,7 +179,6 @@ func (tx *BondTx) AddInputWithSequence(pubkey acm.PublicKey, amt uint64, sequenc Address: pubkey.Address(), Amount: amt, Sequence: sequence, - Signature: crypto.SignatureEd25519{}.Wrap(), PubKey: pubkey, }) return nil @@ -262,7 +256,6 @@ func NewPermissionsTxWithSequence(from acm.PublicKey, args *ptypes.PermArgs, seq Address: from.Address(), Amount: 1, // NOTE: amounts can't be 0 ... Sequence: sequence, - Signature: crypto.SignatureEd25519{}.Wrap(), PubKey: from, }