Skip to content
This repository has been archived by the owner on Jul 15, 2018. It is now read-only.

Commit

Permalink
Merge pull request #47 from tendermint/release-v0.4.1
Browse files Browse the repository at this point in the history
Release v0.4.1
  • Loading branch information
ebuchman authored Oct 28, 2017
2 parents d1f00be + d2b1a70 commit dd20358
Show file tree
Hide file tree
Showing 14 changed files with 107 additions and 190 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 0.4.1 (October 27, 2017)

This release removes support for bcrypt as it was merged too soon without an upgrade plan
for existing keys.

REVERTS THE FOLLOWING COMMITS:

- Parameterize and lower bcrypt cost - dfc4cdd2d71513e4a9922d679c74f36357c4c862
- Upgrade keys to use bcrypt with salts (#38) - 8e7f0e7701f92206679ad093d013b9b162427631

## 0.4.0 (October 27, 2017)

BREAKING CHANGES:
Expand Down
8 changes: 4 additions & 4 deletions keys/cryptostore/enc_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ type encryptedStorage struct {
}

func (es encryptedStorage) Put(name, pass string, key crypto.PrivKey) error {
saltBytes, encBytes, err := es.coder.Encrypt(key, pass)
secret, err := es.coder.Encrypt(key, pass)
if err != nil {
return err
}

ki := info(name, key)
return es.store.Put(name, saltBytes, encBytes, ki)
return es.store.Put(name, secret, ki)
}

func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.Info, error) {
saltBytes, encBytes, info, err := es.store.Get(name)
secret, info, err := es.store.Get(name)
if err != nil {
return crypto.PrivKey{}, info, err
}
key, err := es.coder.Decrypt(saltBytes, encBytes, pass)
key, err := es.coder.Decrypt(secret, pass)
return key, info, err
}

Expand Down
74 changes: 24 additions & 50 deletions keys/cryptostore/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,7 @@ package cryptostore

import (
"github.com/pkg/errors"

crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-crypto/bcrypt"
)

const (
// BcryptCost is as parameter to increase the resistance of the
// encoded keys to brute force password guessing
//
// Jae: 14 is good today (2016)
//
// Ethan: loading the key (at each signing) takes a second on my desktop,
// this is hard for laptops and deadly for mobile. You can raise it again,
// but for now, I will make this usable
//
// TODO: review value
BCryptCost = 12
)

var (
Expand All @@ -32,55 +16,45 @@ var (
//
// This should use a well-designed symetric encryption algorithm
type Encoder interface {
Encrypt(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte, err error)
Decrypt(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error)
Encrypt(key crypto.PrivKey, pass string) ([]byte, error)
Decrypt(data []byte, pass string) (crypto.PrivKey, error)
}

type secretbox struct{}
func secret(passphrase string) []byte {
// TODO: Sha256(Bcrypt(passphrase))
return crypto.Sha256([]byte(passphrase))
}

func (e secretbox) Encrypt(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte, err error) {
if passphrase == "" {
return nil, privKey.Bytes(), nil
}
type secretbox struct{}

saltBytes = crypto.CRandBytes(16)
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BCryptCost)
if err != nil {
return nil, nil, errors.Wrap(err, "Couldn't generate bcrypt key from passphrase.")
func (e secretbox) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) {
if pass == "" {
return key.Bytes(), nil
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes := privKey.Bytes()
return saltBytes, crypto.EncryptSymmetric(privKeyBytes, key), nil
s := secret(pass)
cipher := crypto.EncryptSymmetric(key.Bytes(), s)
return cipher, nil
}

func (e secretbox) Decrypt(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
privKeyBytes := encBytes
// NOTE: Some keys weren't encrypted with a passphrase and hence we have the conditional
if passphrase != "" {
var key []byte
key, err = bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BCryptCost)
func (e secretbox) Decrypt(data []byte, pass string) (key crypto.PrivKey, err error) {
private := data
if pass != "" {
s := secret(pass)
private, err = crypto.DecryptSymmetric(data, s)
if err != nil {
return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase")
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes, err = crypto.DecryptSymmetric(encBytes, key)
if err != nil {
return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase")
}
}
privKey, err = crypto.PrivKeyFromBytes(privKeyBytes)
if err != nil {
return crypto.PrivKey{}, errors.Wrap(err, "Private Key")
}
return privKey, nil
key, err = crypto.PrivKeyFromBytes(private)
return key, errors.Wrap(err, "Invalid Passphrase")
}

type noop struct{}

func (n noop) Encrypt(key crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte, err error) {
return []byte{}, key.Bytes(), nil
func (n noop) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) {
return key.Bytes(), nil
}

func (n noop) Decrypt(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
return crypto.PrivKeyFromBytes(encBytes)
func (n noop) Decrypt(data []byte, pass string) (crypto.PrivKey, error) {
return crypto.PrivKeyFromBytes(data)
}
22 changes: 11 additions & 11 deletions keys/cryptostore/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ func TestNoopEncoder(t *testing.T) {
key2, err := cryptostore.GenSecp256k1.Generate(cmn.RandBytes(16))
require.NoError(err)

_, b, err := noop.Encrypt(key, "encode")
b, err := noop.Encrypt(key, "encode")
require.Nil(err)
assert.NotEmpty(b)

_, b2, err := noop.Encrypt(key2, "encode")
b2, err := noop.Encrypt(key2, "encode")
require.Nil(err)
assert.NotEmpty(b2)
assert.NotEqual(b, b2)

// note the decode with a different password works - not secure!
pk, err := noop.Decrypt(nil, b, "decode")
pk, err := noop.Decrypt(b, "decode")
require.Nil(err)
require.NotNil(pk)
assert.Equal(key, pk)

pk2, err := noop.Decrypt(nil, b2, "kggugougp")
pk2, err := noop.Decrypt(b2, "kggugougp")
require.Nil(err)
require.NotNil(pk2)
assert.Equal(key2, pk2)
Expand All @@ -49,17 +49,17 @@ func TestSecretBox(t *testing.T) {
require.NoError(err)
pass := "some-special-secret"

s, b, err := enc.Encrypt(key, pass)
b, err := enc.Encrypt(key, pass)
require.Nil(err)
assert.NotEmpty(b)

// decoding with a different pass is an error
pk, err := enc.Decrypt(s, b, "decode")
pk, err := enc.Decrypt(b, "decode")
require.NotNil(err)
require.True(pk.Empty())

// but decoding with the same passphrase gets us our key
pk, err = enc.Decrypt(s, b, pass)
pk, err = enc.Decrypt(b, pass)
require.Nil(err)
assert.Equal(key, pk)
}
Expand All @@ -84,11 +84,11 @@ func TestSecretBoxNoPass(t *testing.T) {
}

for i, tc := range cases {
s, b, err := enc.Encrypt(key, tc.encode)
b, err := enc.Encrypt(key, tc.encode)
require.Nil(err, "%d: %+v", i, err)
assert.NotEmpty(b, "%d", i)

pk, err := enc.Decrypt(s, b, tc.decode)
pk, err := enc.Decrypt(b, tc.decode)
if tc.valid {
require.Nil(err, "%d: %+v", i, err)
assert.Equal(key, pk, "%d", i)
Expand All @@ -99,7 +99,7 @@ func TestSecretBoxNoPass(t *testing.T) {

// now let's make sure raw bytes also work...
b := key.Bytes()
pk, rerr := enc.Decrypt(nil, b, "")
require.NoError(rerr)
pk, err := enc.Decrypt(b, "")
require.Nil(err, "%+v", err)
assert.Equal(key, pk)
}
1 change: 0 additions & 1 deletion keys/cryptostore/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cryptostore

import (
"github.com/pkg/errors"

crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-crypto/nano"
)
Expand Down
18 changes: 8 additions & 10 deletions keys/cryptostore/holder.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (s Manager) List() (keys.Infos, error) {

// Get returns the public information about one key
func (s Manager) Get(name string) (keys.Info, error) {
_, _, info, err := s.es.store.Get(name)
_, info, err := s.es.store.Get(name)
return info, err
}

Expand All @@ -119,23 +119,21 @@ func (s Manager) Sign(name, passphrase string, tx keys.Signable) error {
//
// This is designed to copy from one device to another, or provide backups
// during version updates.
// TODO: How to handle Export with salt?
func (s Manager) Export(name, oldpass, transferpass string) (salt, data []byte, err error) {
func (s Manager) Export(name, oldpass, transferpass string) ([]byte, error) {
key, _, err := s.es.Get(name, oldpass)
if err != nil {
return nil, nil, err
return nil, err
}

salt, data, err = s.es.coder.Encrypt(key, transferpass)
return salt, data, err
res, err := s.es.coder.Encrypt(key, transferpass)
return res, err
}

// Import accepts bytes generated by Export along with the same transferpass
// If they are valid, it stores the key under the given name with the
// If they are valid, it stores the password under the given name with the
// new passphrase.
// TODO: How to handle Import with salt?
func (s Manager) Import(name, newpass, transferpass string, salt, data []byte) error {
key, err := s.es.coder.Decrypt(salt, data, transferpass)
func (s Manager) Import(name, newpass, transferpass string, data []byte) error {
key, err := s.es.coder.Decrypt(data, transferpass)
if err != nil {
return err
}
Expand Down
10 changes: 5 additions & 5 deletions keys/cryptostore/holder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestImportUnencrypted(t *testing.T) {
pass := "top-secret"

// import raw bytes
err = cstore.Import(name, pass, "", nil, key.Bytes())
err = cstore.Import(name, pass, "", key.Bytes())
require.Nil(err, "%+v", err)

// make sure the address matches
Expand Down Expand Up @@ -273,15 +273,15 @@ func TestAdvancedKeyManagement(t *testing.T) {
assertPassword(assert, cstore, n1, p2, p1)

// exporting requires the proper name and passphrase
_, _, err = cstore.Export(n2, p2, pt)
_, err = cstore.Export(n2, p2, pt)
assert.NotNil(err)
_, _, err = cstore.Export(n1, p1, pt)
_, err = cstore.Export(n1, p1, pt)
assert.NotNil(err)
salt, exported, err := cstore.Export(n1, p2, pt)
exported, err := cstore.Export(n1, p2, pt)
require.Nil(err, "%+v", err)

// import fails on bad transfer pass
err = cstore.Import(n2, p3, p2, salt, exported)
err = cstore.Import(n2, p3, p2, exported)
assert.NotNil(err)
}

Expand Down
2 changes: 1 addition & 1 deletion keys/cryptostore/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (

"github.com/stretchr/testify/assert"

crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"

crypto "github.com/tendermint/go-crypto"
keys "github.com/tendermint/go-crypto/keys"
)

Expand Down
Loading

0 comments on commit dd20358

Please sign in to comment.