From 37b129fba0630a26119bc5e4636e51083ffb282b Mon Sep 17 00:00:00 2001 From: Nathan Seva Date: Thu, 12 Oct 2023 09:32:32 +0200 Subject: [PATCH] update account package and encrypted private key --- pkg/types/encrypted_private_key.go | 19 ++++++++++--------- pkg/wallet/account/account.go | 16 +++++++--------- pkg/wallet/account/account_test.go | 15 +++++++++++++++ pkg/wallet/account/validation.go | 7 ++++++- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/pkg/types/encrypted_private_key.go b/pkg/types/encrypted_private_key.go index 5aefcc2ba..8e66f89b3 100644 --- a/pkg/types/encrypted_private_key.go +++ b/pkg/types/encrypted_private_key.go @@ -2,6 +2,7 @@ package types import ( "crypto/ed25519" + "fmt" "github.com/awnumar/memguard" "github.com/btcsuite/btcutil/base58" @@ -83,9 +84,9 @@ func (a *EncryptedPrivateKey) UnmarshalBinary(data []byte) error { func (e *EncryptedPrivateKey) Sign(guardedPassword *memguard.LockedBuffer, salt, nonce, data []byte) ([]byte, error) { digest := blake3.Sum256(data) - privateKeyInClear, err := PrivateKey(guardedPassword, salt, nonce, e.Data) + privateKeyInClear, err := privateKey(guardedPassword, salt, nonce, e.Data) if err != nil { - return nil, err + return nil, fmt.Errorf("Sign: %w", err) } defer privateKeyInClear.Destroy() @@ -95,9 +96,9 @@ func (e *EncryptedPrivateKey) Sign(guardedPassword *memguard.LockedBuffer, salt, // PublicKey returns the public key corresponding to the private key. Guarded password is destroyed. func (e *EncryptedPrivateKey) PublicKey(guardedPassword *memguard.LockedBuffer, salt, nonce []byte) (*PublicKey, error) { - privateKeyInClear, err := PrivateKey(guardedPassword, salt, nonce, e.Data) + privateKeyInClear, err := privateKey(guardedPassword, salt, nonce, e.Data) if err != nil { - return nil, err + return nil, fmt.Errorf("PublicKey: %w", err) } publicKeyBytes := ed25519.PrivateKey(privateKeyInClear.Bytes()).Public().(ed25519.PublicKey) @@ -115,9 +116,9 @@ func (e *EncryptedPrivateKey) PublicKey(guardedPassword *memguard.LockedBuffer, // PrivateKeyTextInClear returns the private key in clear. Guarded password is destroyed. func (e *EncryptedPrivateKey) PrivateKeyTextInClear(guardedPassword *memguard.LockedBuffer, salt, nonce, encryptedKey []byte) (*memguard.LockedBuffer, error) { - privateKeyInClear, err := PrivateKey(guardedPassword, salt, nonce, encryptedKey) + privateKeyInClear, err := privateKey(guardedPassword, salt, nonce, encryptedKey) if err != nil { - return nil, err + return nil, fmt.Errorf("PrivateKeyTextInClear: %w", err) } seed := ed25519.PrivateKey(privateKeyInClear.Bytes()).Seed() @@ -133,7 +134,7 @@ func (e *EncryptedPrivateKey) PrivateKeyTextInClear(guardedPassword *memguard.Lo // PasswordIsValid returns true if the password is valid for the account. It destroys the guarded password. func (e *EncryptedPrivateKey) PasswordIsValid(guardedPassword *memguard.LockedBuffer, salt, nonce, encryptedKey []byte) bool { - privateKeyInClear, err := PrivateKey(guardedPassword, salt, nonce, e.Data) + privateKeyInClear, err := privateKey(guardedPassword, salt, nonce, e.Data) if err != nil { return false } @@ -142,9 +143,9 @@ func (e *EncryptedPrivateKey) PasswordIsValid(guardedPassword *memguard.LockedBu return err == nil } -// PrivateKey returns the private key in clear. +// privateKey returns the private key in clear. // Guarded password is destroyed. -func PrivateKey(guardedPassword *memguard.LockedBuffer, salt, nonce, encryptedKey []byte) (*memguard.LockedBuffer, error) { +func privateKey(guardedPassword *memguard.LockedBuffer, salt, nonce, encryptedKey []byte) (*memguard.LockedBuffer, error) { aeadCipher, secretKey, err := crypto.NewSecretCipher(guardedPassword.Bytes(), salt[:]) defer guardedPassword.Destroy() defer secretKey.Destroy() diff --git a/pkg/wallet/account/account.go b/pkg/wallet/account/account.go index 2bc127a65..2d14139be 100644 --- a/pkg/wallet/account/account.go +++ b/pkg/wallet/account/account.go @@ -3,6 +3,7 @@ package account import ( "crypto/ed25519" "crypto/rand" + "errors" "fmt" "github.com/awnumar/memguard" @@ -17,6 +18,8 @@ const ( AccountLastVersion = 1 ) +var ErrInvalidPrivateKey = errors.New("invalid private key") + type Account struct { Version *uint8 `yaml:"Version"` Nickname string `yaml:"Nickname,omitempty"` @@ -38,7 +41,7 @@ func New(version *uint8, nickname string, address types.Address, salt [16]byte, } if !NicknameIsValid(nickname) { - return nil, fmt.Errorf("invalid nickname: %s", nickname) + return nil, fmt.Errorf("%w: %s", ErrInvalidNickname, nickname) } if err := address.Validate(address.Version, object.UserAddress, object.SmartContractAddress); err != nil { @@ -123,8 +126,7 @@ func NewGenerated(guardedPassword *memguard.LockedBuffer, nickname string) (*Acc return nil, fmt.Errorf("creating secret cipher: %w", err) } - // secretBuffer is the secret key in clear without the version. - secretBuffer := memguard.NewBufferFromBytes(privateKeyBytes.Seed()) + secretBuffer := memguard.NewBufferFromBytes(privateKeyBytes) encryptedSecret := crypto.SealSecret(aeadCipher, nonce[:], secretBuffer) secretKey.Destroy() @@ -174,7 +176,7 @@ func NewFromPrivateKey(guardedPassword *memguard.LockedBuffer, nickname string, seedBuffer := memguard.NewBufferFromBytes(seed) if err != nil { seedBuffer.Destroy() - return nil, fmt.Errorf("decoding base58 private key: %w", err) + return nil, fmt.Errorf("%w: decoding base58 private key: %w", ErrInvalidPrivateKey, err) } privateKey := memguard.NewBufferFromBytes(ed25519.NewKeyFromSeed(seedBuffer.Bytes())) @@ -234,7 +236,7 @@ func (a *Account) PasswordIsValid(guardedPassword *memguard.LockedBuffer) bool { func (a *Account) Unmarshal(data []byte) error { err := yaml.Unmarshal(data, &a) if err != nil { - return fmt.Errorf("unmarshalling account: %w", err) + return err } if len(a.Salt) == 0 || a.Salt == [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} { @@ -257,9 +259,5 @@ func (a *Account) Unmarshal(data []byte) error { // return fmt.Errorf("missing version") // } - if !NicknameIsValid(a.Nickname) { - return fmt.Errorf("invalid nickname: %s", a.Nickname) // TODO: add unit test - } - return nil } diff --git a/pkg/wallet/account/account_test.go b/pkg/wallet/account/account_test.go index 18533da9f..c29bbdc68 100644 --- a/pkg/wallet/account/account_test.go +++ b/pkg/wallet/account/account_test.go @@ -84,6 +84,21 @@ func TestSign(t *testing.T) { assert.Equal(t, byte(types.EncryptedPrivateKeyLastVersion), signature[0]) expectedSignature := []byte{0x0, 0xe7, 0xeb, 0xd0, 0x39, 0xd3, 0xa3, 0x70, 0x70, 0xee, 0x38, 0xee, 0x95, 0x78, 0xd7, 0x3d, 0x7d, 0x74, 0xc4, 0x1a, 0x3, 0x1c, 0xfa, 0x3, 0xd4, 0x34, 0x1d, 0x67, 0x81, 0x64, 0x2c, 0xb7, 0xb0, 0x7c, 0xab, 0x30, 0xf1, 0x1d, 0x22, 0x39, 0x27, 0x7c, 0x9d, 0x5b, 0x4c, 0x9e, 0xcb, 0xa4, 0xe9, 0x8a, 0x5, 0x42, 0x20, 0xbb, 0x97, 0x7, 0x5e, 0x71, 0x87, 0x10, 0x40, 0xec, 0x8e, 0x62, 0x7} assert.Equal(t, expectedSignature, signature) + + t.Run("sign with new generated account", func(t *testing.T) { + // Create test values for the password and nickname + samplePassword := memguard.NewBufferFromBytes([]byte(password)) + + // Call the New function with the test values + account, err := NewGenerated(samplePassword, nickname) + assert.NoError(t, err) + + samplePassword = memguard.NewBufferFromBytes([]byte(password)) + + signature, err := account.Sign(samplePassword, sampleData) + assert.NoError(t, err) + assert.Len(t, signature, 64+1) // +1 for the prefix + }) } func TestMarshal(t *testing.T) { diff --git a/pkg/wallet/account/validation.go b/pkg/wallet/account/validation.go index 3d8dc4c79..1e468f3b4 100644 --- a/pkg/wallet/account/validation.go +++ b/pkg/wallet/account/validation.go @@ -1,11 +1,16 @@ package account -import "regexp" +import ( + "errors" + "regexp" +) const ( MaxNicknameLength = 32 ) +var ErrInvalidNickname = errors.New("nickname invalid") + // NicknameIsValid validates the nickname using the following rules: // - must have at least 1 character // - must contain only alphanumeric characters, underscores and dashes