Consolidate and optimize zero functions.
This introduce a new internal package to deal with the explicit clearing of data (such as private keys) in byte slices, byte arrays (32 and 64-bytes long), and multi-precision "big" integers. Benchmarks from a xeon e3 (Xor is the zeroing funcion which Bytes replaces): BenchmarkXor32 30000000 52.1 ns/op BenchmarkXor64 20000000 91.5 ns/op BenchmarkRange32 50000000 31.8 ns/op BenchmarkRange64 30000000 49.5 ns/op BenchmarkBytes32 200000000 10.1 ns/op BenchmarkBytes64 100000000 15.4 ns/op BenchmarkBytea32 1000000000 2.24 ns/op BenchmarkBytea64 300000000 4.46 ns/op Removes an XXX from the votingpool package.
This commit is contained in:
parent
62c7167504
commit
4d9c43593d
7 changed files with 302 additions and 68 deletions
83
internal/zero/benchmark_test.go
Normal file
83
internal/zero/benchmark_test.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package zero_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/btcsuite/btcwallet/internal/zero"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bytes32 = make([]byte, 32) // typical key size
|
||||||
|
bytes64 = make([]byte, 64) // passphrase hash size
|
||||||
|
bytea32 = new([32]byte)
|
||||||
|
bytea64 = new([64]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
// xor is the "slow" byte zeroing implementation which this package
|
||||||
|
// originally replaced. If this function benchmarks faster than the
|
||||||
|
// functions exported by this package in a future Go version (perhaps
|
||||||
|
// by calling runtime.memclr), replace the "optimized" versions with
|
||||||
|
// this.
|
||||||
|
func xor(b []byte) {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= b[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// zrange is an alternative zero implementation that, while currently
|
||||||
|
// slower than the functions provided by this package, may be faster
|
||||||
|
// in a future Go release. Switch to this or the xor implementation
|
||||||
|
// if they ever become faster.
|
||||||
|
func zrange(b []byte) {
|
||||||
|
for i := range b {
|
||||||
|
b[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkXor32(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
xor(bytes32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkXor64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
xor(bytes64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRange32(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
zrange(bytes32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRange64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
zrange(bytes64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytes32(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Bytes(bytes32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytes64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Bytes(bytes64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytea32(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Bytea32(bytea32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytea64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Bytea64(bytea64)
|
||||||
|
}
|
||||||
|
}
|
49
internal/zero/zero.go
Normal file
49
internal/zero/zero.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Package zero contains functions to clear data from byte slices and
|
||||||
|
// multi-precision integers.
|
||||||
|
package zero
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bytes sets all bytes in the passed slice to zero. This is used to
|
||||||
|
// explicitly clear private key material from memory.
|
||||||
|
//
|
||||||
|
// In general, prefer to use the fixed-sized zeroing functions (Bytea*)
|
||||||
|
// when zeroing bytes as they are much more efficient than the variable
|
||||||
|
// sized zeroing func Bytes.
|
||||||
|
func Bytes(b []byte) {
|
||||||
|
z := [32]byte{}
|
||||||
|
n := uint(copy(b, z[:]))
|
||||||
|
for n < uint(len(b)) {
|
||||||
|
copy(b[n:], b[:n])
|
||||||
|
n <<= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytea32 clears the 32-byte array by filling it with the zero value.
|
||||||
|
// This is used to explicitly clear private key material from memory.
|
||||||
|
func Bytea32(b *[32]byte) {
|
||||||
|
*b = [32]byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytea64 clears the 64-byte array by filling it with the zero value.
|
||||||
|
// This is used to explicitly clear sensitive material from memory.
|
||||||
|
func Bytea64(b *[64]byte) {
|
||||||
|
*b = [64]byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigInt sets all bytes in the passed big int to zero and then sets the
|
||||||
|
// value to 0. This differs from simply setting the value in that it
|
||||||
|
// specifically clears the underlying bytes whereas simply setting the value
|
||||||
|
// does not. This is mostly useful to forcefully clear private keys.
|
||||||
|
func BigInt(x *big.Int) {
|
||||||
|
b := x.Bits()
|
||||||
|
z := [16]big.Word{}
|
||||||
|
n := uint(copy(b, z[:]))
|
||||||
|
for n < uint(len(b)) {
|
||||||
|
copy(b[n:], b[:n])
|
||||||
|
n <<= 1
|
||||||
|
}
|
||||||
|
x.SetInt64(0)
|
||||||
|
}
|
143
internal/zero/zero_test.go
Normal file
143
internal/zero/zero_test.go
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package zero_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/btcsuite/btcwallet/internal/zero"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeOneBytes(n int) []byte {
|
||||||
|
b := make([]byte, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = 1
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkZeroBytes(b []byte) error {
|
||||||
|
for i, v := range b {
|
||||||
|
if v != 0 {
|
||||||
|
return fmt.Errorf("b[%d] = %d", i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
tests := []int{
|
||||||
|
0,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
127,
|
||||||
|
128,
|
||||||
|
129,
|
||||||
|
255,
|
||||||
|
256,
|
||||||
|
256,
|
||||||
|
257,
|
||||||
|
383,
|
||||||
|
384,
|
||||||
|
385,
|
||||||
|
511,
|
||||||
|
512,
|
||||||
|
513,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := range tests {
|
||||||
|
b := makeOneBytes(n)
|
||||||
|
Bytes(b)
|
||||||
|
err := checkZeroBytes(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test %d (n=%d) failed: %v", i, n, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkZeroWords(b []big.Word) error {
|
||||||
|
for i, v := range b {
|
||||||
|
if v != 0 {
|
||||||
|
return fmt.Errorf("b[%d] = %d", i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var bigZero = new(big.Int)
|
||||||
|
|
||||||
|
func TestBigInt(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
// 16 0xFFFFFFFF 32-bit uintptrs
|
||||||
|
strings.Repeat("FFFFFFFF", 16),
|
||||||
|
|
||||||
|
// 17 32-bit uintptrs, minimum value which enters loop on 32-bit
|
||||||
|
"01" + strings.Repeat("00000000", 16),
|
||||||
|
|
||||||
|
// 32 0xFFFFFFFF 32-bit uintptrs, maximum value which enters loop exactly once on 32-bit
|
||||||
|
strings.Repeat("FFFFFFFF", 32),
|
||||||
|
|
||||||
|
// 33 32-bit uintptrs, minimum value which enters loop twice on 32-bit
|
||||||
|
"01" + strings.Repeat("00000000", 32),
|
||||||
|
|
||||||
|
// 16 0xFFFFFFFFFFFFFFFF 64-bit uintptrs
|
||||||
|
strings.Repeat("FFFFFFFFFFFFFFFF", 16),
|
||||||
|
|
||||||
|
// 17 64-bit uintptrs, minimum value which enters loop on 64-bit
|
||||||
|
"01" + strings.Repeat("0000000000000000", 16),
|
||||||
|
|
||||||
|
// 32 0xFFFFFFFFFFFFFFFF 64-bit uintptrs, maximum value which enters loop exactly once on 64-bit
|
||||||
|
strings.Repeat("FFFFFFFFFFFFFFFF", 32),
|
||||||
|
|
||||||
|
// 33 64-bit uintptrs, minimum value which enters loop twice on 64-bit
|
||||||
|
"01" + strings.Repeat("0000000000000000", 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, s := range tests {
|
||||||
|
v, ok := new(big.Int).SetString(s, 16)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Test %d includes invalid hex number %s", i, s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInt(v)
|
||||||
|
err := checkZeroWords(v.Bits())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test %d (s=%s) failed: %v", i, s, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v.Cmp(bigZero) != 0 {
|
||||||
|
t.Errorf("Test %d (s=%s) zeroed big.Int represents non-zero number %v", i, s, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytea32(t *testing.T) {
|
||||||
|
const sz = 32
|
||||||
|
var b [sz]byte
|
||||||
|
copy(b[:], makeOneBytes(sz))
|
||||||
|
|
||||||
|
Bytea32(&b)
|
||||||
|
|
||||||
|
err := checkZeroBytes(b[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytea64(t *testing.T) {
|
||||||
|
const sz = 64
|
||||||
|
var b [sz]byte
|
||||||
|
copy(b[:], makeOneBytes(sz))
|
||||||
|
|
||||||
|
Bytea64(&b)
|
||||||
|
|
||||||
|
err := checkZeroBytes(b[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcwallet/internal/zero"
|
||||||
"github.com/btcsuite/fastsha256"
|
"github.com/btcsuite/fastsha256"
|
||||||
"github.com/btcsuite/golangcrypto/nacl/secretbox"
|
"github.com/btcsuite/golangcrypto/nacl/secretbox"
|
||||||
"github.com/btcsuite/golangcrypto/scrypt"
|
"github.com/btcsuite/golangcrypto/scrypt"
|
||||||
|
@ -23,13 +24,6 @@ var (
|
||||||
ErrDecryptFailed = errors.New("unable to decrypt")
|
ErrDecryptFailed = errors.New("unable to decrypt")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Zero out a byte slice.
|
|
||||||
func zero(b []byte) {
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= b[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Expose secretbox's Overhead const here for convenience.
|
// Expose secretbox's Overhead const here for convenience.
|
||||||
Overhead = secretbox.Overhead
|
Overhead = secretbox.Overhead
|
||||||
|
@ -79,7 +73,7 @@ func (ck *CryptoKey) Decrypt(in []byte) ([]byte, error) {
|
||||||
// rather than waiting until it's reclaimed by the garbage collector. The
|
// rather than waiting until it's reclaimed by the garbage collector. The
|
||||||
// key is no longer usable after this call.
|
// key is no longer usable after this call.
|
||||||
func (ck *CryptoKey) Zero() {
|
func (ck *CryptoKey) Zero() {
|
||||||
zero(ck[:])
|
zero.Bytea32((*[KeySize]byte)(ck))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateCryptoKey generates a new crypotgraphically random key.
|
// GenerateCryptoKey generates a new crypotgraphically random key.
|
||||||
|
@ -120,7 +114,7 @@ func (sk *SecretKey) deriveKey(password *[]byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
copy(sk.Key[:], key)
|
copy(sk.Key[:], key)
|
||||||
zero(key)
|
zero.Bytes(key)
|
||||||
|
|
||||||
// I'm not a fan of forced garbage collections, but scrypt allocates a
|
// I'm not a fan of forced garbage collections, but scrypt allocates a
|
||||||
// ton of memory and calling it back to back without a GC cycle in
|
// ton of memory and calling it back to back without a GC cycle in
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
|
"github.com/btcsuite/btcwallet/internal/zero"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
)
|
)
|
||||||
|
@ -326,7 +327,7 @@ func (vp *Pool) decryptExtendedKey(keyType waddrmgr.CryptoKeyType, encrypted []b
|
||||||
return nil, managerError(waddrmgr.ErrCrypto, str, err)
|
return nil, managerError(waddrmgr.ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
result, err := hdkeychain.NewKeyFromString(string(decrypted))
|
result, err := hdkeychain.NewKeyFromString(string(decrypted))
|
||||||
zero(decrypted)
|
zero.Bytes(decrypted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("cannot get key from string %v", decrypted)
|
str := fmt.Sprintf("cannot get key from string %v", decrypted)
|
||||||
return nil, managerError(waddrmgr.ErrKeyChain, str, err)
|
return nil, managerError(waddrmgr.ErrKeyChain, str, err)
|
||||||
|
@ -608,15 +609,3 @@ func (s *SeriesData) IsEmpowered() bool {
|
||||||
func managerError(c waddrmgr.ErrorCode, desc string, err error) waddrmgr.ManagerError {
|
func managerError(c waddrmgr.ErrorCode, desc string, err error) waddrmgr.ManagerError {
|
||||||
return waddrmgr.ManagerError{ErrorCode: c, Description: desc, Err: err}
|
return waddrmgr.ManagerError{ErrorCode: c, Description: desc, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// zero sets all bytes in the passed slice to zero. This is used to
|
|
||||||
// explicitly clear private key material from memory.
|
|
||||||
//
|
|
||||||
// XXX(lars) there exists currently around 4-5 other zero functions
|
|
||||||
// with at least 3 different implementations. We should try to
|
|
||||||
// consolidate these.
|
|
||||||
func zero(b []byte) {
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= b[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,39 +19,14 @@ package waddrmgr
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
|
"github.com/btcsuite/btcwallet/internal/zero"
|
||||||
)
|
)
|
||||||
|
|
||||||
// zero sets all bytes in the passed slice to zero. This is used to
|
|
||||||
// explicitly clear private key material from memory.
|
|
||||||
func zero(b []byte) {
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= b[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// zeroBigInt sets all bytes in the passed big int to zero and then sets the
|
|
||||||
// value to 0. This differs from simply setting the value in that it
|
|
||||||
// specifically clears the underlying bytes whereas simply setting the value
|
|
||||||
// does not. This is mostly useful to forcefully clear private keys.
|
|
||||||
func zeroBigInt(x *big.Int) {
|
|
||||||
// NOTE: This could make use of .Xor, however this is safer since the
|
|
||||||
// specific implementation of Xor could technically change in such a way
|
|
||||||
// as the original bits aren't cleared. This function would silenty
|
|
||||||
// fail in that case and it's best to avoid that possibility.
|
|
||||||
bits := x.Bits()
|
|
||||||
numBits := len(bits)
|
|
||||||
for i := 0; i < numBits; i++ {
|
|
||||||
bits[i] ^= bits[i]
|
|
||||||
}
|
|
||||||
x.SetInt64(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ManagedAddress is an interface that provides acces to information regarding
|
// ManagedAddress is an interface that provides acces to information regarding
|
||||||
// an address managed by an address manager. Concrete implementations of this
|
// an address managed by an address manager. Concrete implementations of this
|
||||||
// type may provide further fields to provide information specific to that type
|
// type may provide further fields to provide information specific to that type
|
||||||
|
@ -159,7 +134,7 @@ func (a *managedAddress) lock() {
|
||||||
// Zero and nil the clear text private key associated with this
|
// Zero and nil the clear text private key associated with this
|
||||||
// address.
|
// address.
|
||||||
a.privKeyMutex.Lock()
|
a.privKeyMutex.Lock()
|
||||||
zero(a.privKeyCT)
|
zero.Bytes(a.privKeyCT)
|
||||||
a.privKeyCT = nil
|
a.privKeyCT = nil
|
||||||
a.privKeyMutex.Unlock()
|
a.privKeyMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
@ -260,7 +235,7 @@ func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyCopy)
|
privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyCopy)
|
||||||
zero(privKeyCopy)
|
zero.Bytes(privKeyCopy)
|
||||||
return privKey, nil
|
return privKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +326,7 @@ func newManagedAddressFromExtKey(m *Manager, account uint32, key *hdkeychain.Ext
|
||||||
|
|
||||||
// Ensure the temp private key big integer is cleared after use.
|
// Ensure the temp private key big integer is cleared after use.
|
||||||
managedAddr, err = newManagedAddress(m, account, privKey, true)
|
managedAddr, err = newManagedAddress(m, account, privKey, true)
|
||||||
zeroBigInt(privKey.D)
|
zero.BigInt(privKey.D)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -413,7 +388,7 @@ func (a *scriptAddress) unlock(key EncryptorDecryptor) ([]byte, error) {
|
||||||
func (a *scriptAddress) lock() {
|
func (a *scriptAddress) lock() {
|
||||||
// Zero and nil the clear text script associated with this address.
|
// Zero and nil the clear text script associated with this address.
|
||||||
a.scriptMutex.Lock()
|
a.scriptMutex.Lock()
|
||||||
zero(a.scriptCT)
|
zero.Bytes(a.scriptCT)
|
||||||
a.scriptCT = nil
|
a.scriptCT = nil
|
||||||
a.scriptMutex.Unlock()
|
a.scriptMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
|
"github.com/btcsuite/btcwallet/internal/zero"
|
||||||
"github.com/btcsuite/btcwallet/snacl"
|
"github.com/btcsuite/btcwallet/snacl"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
)
|
)
|
||||||
|
@ -298,7 +299,7 @@ func (m *Manager) lock() {
|
||||||
m.masterKeyPriv.Zero()
|
m.masterKeyPriv.Zero()
|
||||||
|
|
||||||
// Zero the hashed passphrase.
|
// Zero the hashed passphrase.
|
||||||
zero(m.hashedPrivPassphrase[:])
|
zero.Bytea64(&m.hashedPrivPassphrase)
|
||||||
|
|
||||||
// NOTE: m.cryptoKeyPub is intentionally not cleared here as the address
|
// NOTE: m.cryptoKeyPub is intentionally not cleared here as the address
|
||||||
// manager needs to be able to continue to read and decrypt public data
|
// manager needs to be able to continue to read and decrypt public data
|
||||||
|
@ -723,7 +724,7 @@ func (m *Manager) ChangePassphrase(oldPassphrase, newPassphrase []byte, private
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
encPriv, err := newMasterKey.Encrypt(decPriv)
|
encPriv, err := newMasterKey.Encrypt(decPriv)
|
||||||
zero(decPriv)
|
zero.Bytes(decPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to encrypt crypto private key"
|
str := "failed to encrypt crypto private key"
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
|
@ -737,7 +738,7 @@ func (m *Manager) ChangePassphrase(oldPassphrase, newPassphrase []byte, private
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
encScript, err := newMasterKey.Encrypt(decScript)
|
encScript, err := newMasterKey.Encrypt(decScript)
|
||||||
zero(decScript)
|
zero.Bytes(decScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to encrypt crypto script key"
|
str := "failed to encrypt crypto script key"
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
|
@ -754,7 +755,7 @@ func (m *Manager) ChangePassphrase(oldPassphrase, newPassphrase []byte, private
|
||||||
saltedPassphrase := append(passphraseSalt[:],
|
saltedPassphrase := append(passphraseSalt[:],
|
||||||
newPassphrase...)
|
newPassphrase...)
|
||||||
hashedPassphrase = sha512.Sum512(saltedPassphrase)
|
hashedPassphrase = sha512.Sum512(saltedPassphrase)
|
||||||
zero(saltedPassphrase)
|
zero.Bytes(saltedPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the new keys and params to the the db in a single
|
// Save the new keys and params to the the db in a single
|
||||||
|
@ -856,7 +857,7 @@ func (m *Manager) ConvertToWatchingOnly() error {
|
||||||
|
|
||||||
// Clear and remove all of the encrypted acount private keys.
|
// Clear and remove all of the encrypted acount private keys.
|
||||||
for _, acctInfo := range m.acctInfo {
|
for _, acctInfo := range m.acctInfo {
|
||||||
zero(acctInfo.acctKeyEncrypted)
|
zero.Bytes(acctInfo.acctKeyEncrypted)
|
||||||
acctInfo.acctKeyEncrypted = nil
|
acctInfo.acctKeyEncrypted = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,19 +866,19 @@ func (m *Manager) ConvertToWatchingOnly() error {
|
||||||
for _, ma := range m.addrs {
|
for _, ma := range m.addrs {
|
||||||
switch addr := ma.(type) {
|
switch addr := ma.(type) {
|
||||||
case *managedAddress:
|
case *managedAddress:
|
||||||
zero(addr.privKeyEncrypted)
|
zero.Bytes(addr.privKeyEncrypted)
|
||||||
addr.privKeyEncrypted = nil
|
addr.privKeyEncrypted = nil
|
||||||
case *scriptAddress:
|
case *scriptAddress:
|
||||||
zero(addr.scriptEncrypted)
|
zero.Bytes(addr.scriptEncrypted)
|
||||||
addr.scriptEncrypted = nil
|
addr.scriptEncrypted = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear and remove encrypted private and script crypto keys.
|
// Clear and remove encrypted private and script crypto keys.
|
||||||
zero(m.cryptoKeyScriptEncrypted)
|
zero.Bytes(m.cryptoKeyScriptEncrypted)
|
||||||
m.cryptoKeyScriptEncrypted = nil
|
m.cryptoKeyScriptEncrypted = nil
|
||||||
m.cryptoKeyScript = nil
|
m.cryptoKeyScript = nil
|
||||||
zero(m.cryptoKeyPrivEncrypted)
|
zero.Bytes(m.cryptoKeyPrivEncrypted)
|
||||||
m.cryptoKeyPrivEncrypted = nil
|
m.cryptoKeyPrivEncrypted = nil
|
||||||
m.cryptoKeyPriv = nil
|
m.cryptoKeyPriv = nil
|
||||||
|
|
||||||
|
@ -976,7 +977,7 @@ func (m *Manager) ImportPrivateKey(wif *btcutil.WIF, bs *BlockStamp) (ManagedPub
|
||||||
if !m.watchingOnly {
|
if !m.watchingOnly {
|
||||||
privKeyBytes := wif.PrivKey.Serialize()
|
privKeyBytes := wif.PrivKey.Serialize()
|
||||||
encryptedPrivKey, err = m.cryptoKeyPriv.Encrypt(privKeyBytes)
|
encryptedPrivKey, err = m.cryptoKeyPriv.Encrypt(privKeyBytes)
|
||||||
zero(privKeyBytes)
|
zero.Bytes(privKeyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to encrypt private key for %x",
|
str := fmt.Sprintf("failed to encrypt private key for %x",
|
||||||
serializedPubKey)
|
serializedPubKey)
|
||||||
|
@ -1196,7 +1197,7 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
||||||
saltedPassphrase := append(m.privPassphraseSalt[:],
|
saltedPassphrase := append(m.privPassphraseSalt[:],
|
||||||
passphrase...)
|
passphrase...)
|
||||||
hashedPassphrase := sha512.Sum512(saltedPassphrase)
|
hashedPassphrase := sha512.Sum512(saltedPassphrase)
|
||||||
zero(saltedPassphrase)
|
zero.Bytes(saltedPassphrase)
|
||||||
if hashedPassphrase != m.hashedPrivPassphrase {
|
if hashedPassphrase != m.hashedPrivPassphrase {
|
||||||
m.lock()
|
m.lock()
|
||||||
str := "invalid passphrase for master private key"
|
str := "invalid passphrase for master private key"
|
||||||
|
@ -1225,7 +1226,7 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
m.cryptoKeyPriv.CopyBytes(decryptedKey)
|
m.cryptoKeyPriv.CopyBytes(decryptedKey)
|
||||||
zero(decryptedKey)
|
zero.Bytes(decryptedKey)
|
||||||
|
|
||||||
// Use the crypto private key to decrypt all of the account private
|
// Use the crypto private key to decrypt all of the account private
|
||||||
// extended keys.
|
// extended keys.
|
||||||
|
@ -1239,7 +1240,7 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
acctKeyPriv, err := hdkeychain.NewKeyFromString(string(decrypted))
|
acctKeyPriv, err := hdkeychain.NewKeyFromString(string(decrypted))
|
||||||
zero(decrypted)
|
zero.Bytes(decrypted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.lock()
|
m.lock()
|
||||||
str := fmt.Sprintf("failed to regenerate account %d "+
|
str := fmt.Sprintf("failed to regenerate account %d "+
|
||||||
|
@ -1267,7 +1268,7 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
||||||
|
|
||||||
privKeyBytes := privKey.Serialize()
|
privKeyBytes := privKey.Serialize()
|
||||||
privKeyEncrypted, err := m.cryptoKeyPriv.Encrypt(privKeyBytes)
|
privKeyEncrypted, err := m.cryptoKeyPriv.Encrypt(privKeyBytes)
|
||||||
zeroBigInt(privKey.D)
|
zero.BigInt(privKey.D)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.lock()
|
m.lock()
|
||||||
str := fmt.Sprintf("failed to encrypt private key for "+
|
str := fmt.Sprintf("failed to encrypt private key for "+
|
||||||
|
@ -1285,7 +1286,7 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
||||||
m.locked = false
|
m.locked = false
|
||||||
saltedPassphrase := append(m.privPassphraseSalt[:], passphrase...)
|
saltedPassphrase := append(m.privPassphraseSalt[:], passphrase...)
|
||||||
m.hashedPrivPassphrase = sha512.Sum512(saltedPassphrase)
|
m.hashedPrivPassphrase = sha512.Sum512(saltedPassphrase)
|
||||||
zero(saltedPassphrase)
|
zero.Bytes(saltedPassphrase)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1796,7 +1797,7 @@ func loadManager(namespace walletdb.Namespace, pubPassphrase []byte, chainParams
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
cryptoKeyPub.CopyBytes(cryptoKeyPubCT)
|
cryptoKeyPub.CopyBytes(cryptoKeyPubCT)
|
||||||
zero(cryptoKeyPubCT)
|
zero.Bytes(cryptoKeyPubCT)
|
||||||
|
|
||||||
// Create the sync state struct.
|
// Create the sync state struct.
|
||||||
syncInfo := newSyncState(startBlock, syncedTo, recentHeight, recentHashes)
|
syncInfo := newSyncState(startBlock, syncedTo, recentHeight, recentHashes)
|
||||||
|
|
Loading…
Reference in a new issue