keys/wallet: add Destroy/Close/Close

PrivateKey can be destroyed and Account/Wallet can be closed (destroying keys
in the process).
This commit is contained in:
Roman Khimov 2022-09-01 15:28:35 +03:00
parent a30e73a0d7
commit 62be6f959c
6 changed files with 39 additions and 5 deletions

View file

@ -117,6 +117,15 @@ func (p *PrivateKey) WIF() string {
return w
}
// Destroy wipes the contents of the private key from memory. Any operations
// with the key after call to Destroy have undefined behavior.
func (p *PrivateKey) Destroy() {
bits := p.D.Bits()
for i := range bits {
bits[i] = 0
}
}
// Address derives the public NEO address that is coupled with the private key, and
// returns it as a string.
func (p *PrivateKey) Address() string {

View file

@ -2,6 +2,7 @@ package keys
import (
"encoding/hex"
"math/big"
"strings"
"testing"
@ -27,6 +28,9 @@ func TestPrivateKey(t *testing.T) {
assert.Equal(t, testCase.Wif, wif)
pubKey := privKey.PublicKey()
assert.Equal(t, hex.EncodeToString(pubKey.Bytes()), testCase.PublicKey)
oldD := new(big.Int).Set(privKey.D)
privKey.Destroy()
assert.NotEqual(t, oldD, privKey.D)
}
}

View file

@ -175,6 +175,17 @@ func (a *Account) PrivateKey() *keys.PrivateKey {
return a.privateKey
}
// Close cleans up the private key used by Account and disassociates it from
// Account. The Account can no longer sign anything after this call, but Decrypt
// can make it usable again.
func (a *Account) Close() {
if a.privateKey == nil {
return
}
a.privateKey.Destroy()
a.privateKey = nil
}
// NewAccountFromWIF creates a new Account from the given WIF.
func NewAccountFromWIF(wif string) (*Account, error) {
privKey, err := keys.NewPrivateKeyFromWIF(wif)

View file

@ -141,9 +141,11 @@ func TestContractSignTx(t *testing.T) {
require.Error(t, acc2.SignTx(0, tx)) // Locked account.
acc2.Locked = false
acc2.privateKey = nil
acc2.Close()
require.False(t, acc2.CanSign())
require.Error(t, acc2.SignTx(0, tx)) // No private key.
acc2.Close() // No-op.
require.False(t, acc2.CanSign())
tx.Scripts = append(tx.Scripts, transaction.Witness{
VerificationScript: acc.Contract.Script,

View file

@ -168,9 +168,14 @@ func (w *Wallet) JSON() ([]byte, error) {
return json.MarshalIndent(w, " ", " ")
}
// Deprecated: Close is deprecated.
// Close performs nothing and is left for backwards compatibility.
func (w *Wallet) Close() {}
// Close closes all Wallet accounts making them incapable of signing anything
// (unless they're decrypted again). It's not doing anything to the underlying
// wallet file.
func (w *Wallet) Close() {
for _, acc := range w.Accounts {
acc.Close()
}
}
// GetAccount returns an account corresponding to the provided scripthash.
func (w *Wallet) GetAccount(h util.Uint160) *Account {

View file

@ -34,13 +34,16 @@ func TestNewWalletFromFile_Negative_NoFile(t *testing.T) {
require.Errorf(t, err, "open testWallet: no such file or directory")
}
func TestCreateAccount(t *testing.T) {
func TestCreateAccountAndClose(t *testing.T) {
wallet := checkWalletConstructor(t)
errAcc := wallet.CreateAccount("testName", "testPass")
require.NoError(t, errAcc)
accounts := wallet.Accounts
require.Len(t, accounts, 1)
require.True(t, wallet.Accounts[0].CanSign())
wallet.Close()
require.False(t, wallet.Accounts[0].CanSign())
}
func TestAddAccount(t *testing.T) {