diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d63200ae..1f91faa17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -176,6 +176,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#10842](https://github.com/cosmos/cosmos-sdk/pull/10842) Fix error when `--generate-only`, `--max-msgs` fags set while executing `WithdrawAllRewards` command. * [\#10897](https://github.com/cosmos/cosmos-sdk/pull/10897) Fix: set a non-zero value on gas overflow. * [#9790](https://github.com/cosmos/cosmos-sdk/pull/10687) Fix behavior of `DecCoins.MulDecTruncate`. +* (crypto) [#11027] Remove dependency on Tendermint core for xsalsa20symmetric. ### State Machine Breaking @@ -191,7 +192,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#10770](https://github.com/cosmos/cosmos-sdk/pull/10770) revert tx when block gas limit exceeded * [\#10868](https://github.com/cosmos/cosmos-sdk/pull/10868) Bump gov to v1beta2. Both v1beta1 and v1beta2 queries and Msgs are accepted. - ### Deprecated +### Deprecated * (x/upgrade) [\#9906](https://github.com/cosmos/cosmos-sdk/pull/9906) Deprecate `UpgradeConsensusState` gRPC query since this functionality is only used for IBC, which now has its own [IBC replacement](https://github.com/cosmos/ibc-go/blob/2c880a22e9f9cc75f62b527ca94aa75ce1106001/proto/ibc/core/client/v1/query.proto#L54) diff --git a/crypto/armor.go b/crypto/armor.go index 6e1c739a4..2a45e67ce 100644 --- a/crypto/armor.go +++ b/crypto/armor.go @@ -8,11 +8,11 @@ import ( "github.com/tendermint/crypto/bcrypt" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/xsalsa20symmetric" "golang.org/x/crypto/openpgp/armor" // nolint: staticcheck "github.com/cosmos/cosmos-sdk/codec/legacy" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/xsalsa20symmetric" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) diff --git a/crypto/xsalsa20symmetric/symmetric.go b/crypto/xsalsa20symmetric/symmetric.go new file mode 100644 index 000000000..cf24daea3 --- /dev/null +++ b/crypto/xsalsa20symmetric/symmetric.go @@ -0,0 +1,63 @@ +package xsalsa20symmetric + +import ( + "crypto/rand" + "errors" + "fmt" + + "golang.org/x/crypto/nacl/secretbox" +) + +// TODO, make this into a struct that implements crypto.Symmetric. + +const nonceLen = 24 +const secretLen = 32 + +// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) +// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. +func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { + if len(secret) != secretLen { + panic(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) + } + nonce := randBytes(nonceLen) + nonceArr := [nonceLen]byte{} + copy(nonceArr[:], nonce) + secretArr := [secretLen]byte{} + copy(secretArr[:], secret) + ciphertext = make([]byte, nonceLen+secretbox.Overhead+len(plaintext)) + copy(ciphertext, nonce) + secretbox.Seal(ciphertext[nonceLen:nonceLen], plaintext, &nonceArr, &secretArr) + return ciphertext +} + +// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) +// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. +func DecryptSymmetric(ciphertext []byte, secret []byte) (plaintext []byte, err error) { + if len(secret) != secretLen { + panic(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) + } + if len(ciphertext) <= secretbox.Overhead+nonceLen { + return nil, errors.New("ciphertext is too short") + } + nonce := ciphertext[:nonceLen] + nonceArr := [nonceLen]byte{} + copy(nonceArr[:], nonce) + secretArr := [secretLen]byte{} + copy(secretArr[:], secret) + plaintext = make([]byte, len(ciphertext)-nonceLen-secretbox.Overhead) + _, ok := secretbox.Open(plaintext[:0], ciphertext[nonceLen:], &nonceArr, &secretArr) + if !ok { + return nil, errors.New("ciphertext decryption failed") + } + return plaintext, nil +} + +// This only uses the OS's randomness +func randBytes(numBytes int) []byte { + b := make([]byte, numBytes) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + return b +} diff --git a/crypto/xsalsa20symmetric/symmetric_test.go b/crypto/xsalsa20symmetric/symmetric_test.go new file mode 100644 index 000000000..b35633b96 --- /dev/null +++ b/crypto/xsalsa20symmetric/symmetric_test.go @@ -0,0 +1,45 @@ +package xsalsa20symmetric + +import ( + "crypto/sha256" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "golang.org/x/crypto/bcrypt" +) + +func TestSimple(t *testing.T) { + + plaintext := []byte("sometext") + secret := []byte("somesecretoflengththirtytwo===32") + ciphertext := EncryptSymmetric(plaintext, secret) + plaintext2, err := DecryptSymmetric(ciphertext, secret) + + require.NoError(t, err, "%+v", err) + assert.Equal(t, plaintext, plaintext2) +} + +func TestSimpleWithKDF(t *testing.T) { + + plaintext := []byte("sometext") + secretPass := []byte("somesecret") + secret, err := bcrypt.GenerateFromPassword(secretPass, 12) + if err != nil { + t.Error(err) + } + secret = sha256Sum(secret) + + ciphertext := EncryptSymmetric(plaintext, secret) + plaintext2, err := DecryptSymmetric(ciphertext, secret) + + require.NoError(t, err, "%+v", err) + assert.Equal(t, plaintext, plaintext2) +} + +func sha256Sum(bytes []byte) []byte { + hasher := sha256.New() + hasher.Write(bytes) + return hasher.Sum(nil) +}