Make scrypt parameters overridable/optional
This commit is contained in:
parent
ec8a5bc10c
commit
402fcf0dd0
4 changed files with 77 additions and 55 deletions
|
@ -65,8 +65,3 @@ func hexToBytes(origHex string) []byte {
|
||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Tune the scrypt params down for tests so they execute quickly.
|
|
||||||
waddrmgr.TstSetScryptParams(16, 8, 1)
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,34 +23,24 @@ interface. The functions are only exported while the tests are being run.
|
||||||
|
|
||||||
package waddrmgr
|
package waddrmgr
|
||||||
|
|
||||||
import (
|
import "github.com/conformal/btcwallet/snacl"
|
||||||
"github.com/conformal/btcwallet/snacl"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TstMaxRecentHashes makes the unexported maxRecentHashes constant available
|
// TstMaxRecentHashes makes the unexported maxRecentHashes constant available
|
||||||
// when tests are run.
|
// when tests are run.
|
||||||
var TstMaxRecentHashes = maxRecentHashes
|
var TstMaxRecentHashes = maxRecentHashes
|
||||||
|
|
||||||
// TstSetScryptParams allows the scrypt parameters to be set to much lower
|
// Replace the Manager.newSecretKey function with the given one and calls
|
||||||
// values while the tests are running so they are faster.
|
// the callback function. Afterwards the original newSecretKey
|
||||||
func TstSetScryptParams(n, r, p int) {
|
// function will be restored.
|
||||||
scryptN = n
|
func TstRunWithReplacedNewSecretKey(callback func()) {
|
||||||
scryptR = r
|
orig := newSecretKey
|
||||||
scryptP = p
|
defer func() {
|
||||||
}
|
newSecretKey = orig
|
||||||
|
}()
|
||||||
// TstReplaceNewSecretKeyFunc replaces the new secret key generation function
|
newSecretKey = func(passphrase *[]byte, config *Options) (*snacl.SecretKey, error) {
|
||||||
// with a version that intentionally fails.
|
|
||||||
func TstReplaceNewSecretKeyFunc() {
|
|
||||||
newSecretKey = func(passphrase *[]byte) (*snacl.SecretKey, error) {
|
|
||||||
return nil, snacl.ErrDecryptFailed
|
return nil, snacl.ErrDecryptFailed
|
||||||
}
|
}
|
||||||
}
|
callback()
|
||||||
|
|
||||||
// TstResetNewSecretKeyFunc resets the new secret key generation function to the
|
|
||||||
// original version.
|
|
||||||
func TstResetNewSecretKeyFunc() {
|
|
||||||
newSecretKey = defaultNewSecretKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TstCheckPublicPassphrase return true if the provided public passphrase is
|
// TstCheckPublicPassphrase return true if the provided public passphrase is
|
||||||
|
|
|
@ -76,14 +76,6 @@ const (
|
||||||
internalBranch uint32 = 1
|
internalBranch uint32 = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// scryptN, scryptR, and scryptP are the parameters used for scrypt
|
|
||||||
// password-based key derivation.
|
|
||||||
scryptN = 262144 // 2^18
|
|
||||||
scryptR = 8
|
|
||||||
scryptP = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// addrKey is used to uniquely identify an address even when those addresses
|
// addrKey is used to uniquely identify an address even when those addresses
|
||||||
// would end up being the same bitcoin address (as is the case for pay-to-pubkey
|
// would end up being the same bitcoin address (as is the case for pay-to-pubkey
|
||||||
// and pay-to-pubkey-hash style of addresses).
|
// and pay-to-pubkey-hash style of addresses).
|
||||||
|
@ -123,8 +115,9 @@ type unlockDeriveInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultNewSecretKey returns a new secret key. See newSecretKey.
|
// defaultNewSecretKey returns a new secret key. See newSecretKey.
|
||||||
func defaultNewSecretKey(passphrase *[]byte) (*snacl.SecretKey, error) {
|
func defaultNewSecretKey(passphrase *[]byte, config *Options) (*snacl.SecretKey, error) {
|
||||||
return snacl.NewSecretKey(passphrase, scryptN, scryptR, scryptP)
|
return snacl.NewSecretKey(passphrase, config.ScryptN, config.ScryptR,
|
||||||
|
config.ScryptP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSecretKey is used as a way to replace the new secret key generation
|
// newSecretKey is used as a way to replace the new secret key generation
|
||||||
|
@ -225,6 +218,9 @@ type Manager struct {
|
||||||
// the private extended key (hence nor the underlying private key) in
|
// the private extended key (hence nor the underlying private key) in
|
||||||
// order to encrypt it.
|
// order to encrypt it.
|
||||||
deriveOnUnlock []*unlockDeriveInfo
|
deriveOnUnlock []*unlockDeriveInfo
|
||||||
|
|
||||||
|
// config holds overridable options, such as scrypt parameters.
|
||||||
|
config *Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// lock performs a best try effort to remove and zero all secret keys associated
|
// lock performs a best try effort to remove and zero all secret keys associated
|
||||||
|
@ -650,7 +646,7 @@ func (m *Manager) ChangePassphrase(oldPassphrase, newPassphrase []byte, private
|
||||||
|
|
||||||
// Generate a new master key from the passphrase which is used to secure
|
// Generate a new master key from the passphrase which is used to secure
|
||||||
// the actual secret keys.
|
// the actual secret keys.
|
||||||
newMasterKey, err := newSecretKey(&newPassphrase)
|
newMasterKey, err := newSecretKey(&newPassphrase, m.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to create new master private key"
|
str := "failed to create new master private key"
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
|
@ -788,7 +784,7 @@ func (m *Manager) ExportWatchingOnly(newDbPath string, pubPassphrase []byte) (*M
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadManager(watchingDb, pubPassphrase, m.net)
|
return loadManager(watchingDb, pubPassphrase, m.net, m.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export writes the manager database to the provided writer.
|
// Export writes the manager database to the provided writer.
|
||||||
|
@ -1447,7 +1443,7 @@ func (m *Manager) AllActiveAddresses() ([]btcutil.Address, error) {
|
||||||
func newManager(db *managerDB, net *btcnet.Params, masterKeyPub *snacl.SecretKey,
|
func newManager(db *managerDB, net *btcnet.Params, masterKeyPub *snacl.SecretKey,
|
||||||
masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor,
|
masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor,
|
||||||
cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte,
|
cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte,
|
||||||
syncInfo *syncState) *Manager {
|
syncInfo *syncState, config *Options) *Manager {
|
||||||
|
|
||||||
return &Manager{
|
return &Manager{
|
||||||
db: db,
|
db: db,
|
||||||
|
@ -1463,6 +1459,7 @@ func newManager(db *managerDB, net *btcnet.Params, masterKeyPub *snacl.SecretKey
|
||||||
cryptoKeyPriv: &cryptoKey{},
|
cryptoKeyPriv: &cryptoKey{},
|
||||||
cryptoKeyScriptEncrypted: cryptoKeyScriptEncrypted,
|
cryptoKeyScriptEncrypted: cryptoKeyScriptEncrypted,
|
||||||
cryptoKeyScript: &cryptoKey{},
|
cryptoKeyScript: &cryptoKey{},
|
||||||
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,7 +1540,7 @@ func checkBranchKeys(acctKey *hdkeychain.ExtendedKey) error {
|
||||||
// loadManager returns a new address manager that results from loading it from
|
// loadManager returns a new address manager that results from loading it from
|
||||||
// the passed opened database. The public passphrase is required to decrypt the
|
// the passed opened database. The public passphrase is required to decrypt the
|
||||||
// public keys.
|
// public keys.
|
||||||
func loadManager(db *managerDB, pubPassphrase []byte, net *btcnet.Params) (*Manager, error) {
|
func loadManager(db *managerDB, pubPassphrase []byte, net *btcnet.Params, config *Options) (*Manager, error) {
|
||||||
// Perform all database lookups in a read-only view.
|
// Perform all database lookups in a read-only view.
|
||||||
var watchingOnly bool
|
var watchingOnly bool
|
||||||
var masterKeyPubParams, masterKeyPrivParams []byte
|
var masterKeyPubParams, masterKeyPrivParams []byte
|
||||||
|
@ -1630,7 +1627,7 @@ func loadManager(db *managerDB, pubPassphrase []byte, net *btcnet.Params) (*Mana
|
||||||
// the defaults for the additional fields which are not specified in the
|
// the defaults for the additional fields which are not specified in the
|
||||||
// call to new with the values loaded from the database.
|
// call to new with the values loaded from the database.
|
||||||
mgr := newManager(db, net, &masterKeyPub, &masterKeyPriv, cryptoKeyPub,
|
mgr := newManager(db, net, &masterKeyPub, &masterKeyPriv, cryptoKeyPub,
|
||||||
cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo)
|
cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo, config)
|
||||||
mgr.watchingOnly = watchingOnly
|
mgr.watchingOnly = watchingOnly
|
||||||
return mgr, nil
|
return mgr, nil
|
||||||
}
|
}
|
||||||
|
@ -1640,9 +1637,12 @@ func loadManager(db *managerDB, pubPassphrase []byte, net *btcnet.Params) (*Mana
|
||||||
// public information such as addresses. This is important since access to
|
// public information such as addresses. This is important since access to
|
||||||
// BIP0032 extended keys means it is possible to generate all future addresses.
|
// BIP0032 extended keys means it is possible to generate all future addresses.
|
||||||
//
|
//
|
||||||
|
// If a config structure is passed to the function, that configuration
|
||||||
|
// will override the defaults.
|
||||||
|
//
|
||||||
// A ManagerError with an error code of ErrNoExist will be returned if the
|
// A ManagerError with an error code of ErrNoExist will be returned if the
|
||||||
// passed database does not exist.
|
// passed database does not exist.
|
||||||
func Open(dbPath string, pubPassphrase []byte, net *btcnet.Params) (*Manager, error) {
|
func Open(dbPath string, pubPassphrase []byte, net *btcnet.Params, config *Options) (*Manager, error) {
|
||||||
// Return an error if the specified database does not exist.
|
// Return an error if the specified database does not exist.
|
||||||
if !fileExists(dbPath) {
|
if !fileExists(dbPath) {
|
||||||
str := "the specified address manager does not exist"
|
str := "the specified address manager does not exist"
|
||||||
|
@ -1654,7 +1654,27 @@ func Open(dbPath string, pubPassphrase []byte, net *btcnet.Params) (*Manager, er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadManager(db, pubPassphrase, net)
|
if config == nil {
|
||||||
|
config = defaultConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadManager(db, pubPassphrase, net, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options is used to hold the optional parameters passed to Create or
|
||||||
|
// Load.
|
||||||
|
type Options struct {
|
||||||
|
ScryptN int
|
||||||
|
ScryptR int
|
||||||
|
ScryptP int
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultConfig is an instance of the Options struct initialized with
|
||||||
|
// default configuration options.
|
||||||
|
var defaultConfig = &Options{
|
||||||
|
ScryptN: 262144, // 2^18
|
||||||
|
ScryptR: 8,
|
||||||
|
ScryptP: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create returns a new locked address manager at the given database path. The
|
// Create returns a new locked address manager at the given database path. The
|
||||||
|
@ -1669,9 +1689,12 @@ func Open(dbPath string, pubPassphrase []byte, net *btcnet.Params) (*Manager, er
|
||||||
// private passphrase is required to unlock the address manager in order to gain
|
// private passphrase is required to unlock the address manager in order to gain
|
||||||
// access to any private keys and information.
|
// access to any private keys and information.
|
||||||
//
|
//
|
||||||
|
// If a config structure is passed to the function, that configuration
|
||||||
|
// will override the defaults.
|
||||||
|
//
|
||||||
// A ManagerError with an error code of ErrAlreadyExists will be returned if the
|
// A ManagerError with an error code of ErrAlreadyExists will be returned if the
|
||||||
// passed database already exists.
|
// passed database already exists.
|
||||||
func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcnet.Params) (*Manager, error) {
|
func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcnet.Params, config *Options) (*Manager, error) {
|
||||||
// Return an error if the specified database already exists.
|
// Return an error if the specified database already exists.
|
||||||
if fileExists(dbPath) {
|
if fileExists(dbPath) {
|
||||||
return nil, managerError(ErrAlreadyExists, errAlreadyExists, nil)
|
return nil, managerError(ErrAlreadyExists, errAlreadyExists, nil)
|
||||||
|
@ -1682,6 +1705,10 @@ func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcn
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
config = defaultConfig
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the BIP0044 HD key structure to ensure the provided seed
|
// Generate the BIP0044 HD key structure to ensure the provided seed
|
||||||
// can generate the required structure with no issues.
|
// can generate the required structure with no issues.
|
||||||
|
|
||||||
|
@ -1729,12 +1756,12 @@ func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcn
|
||||||
|
|
||||||
// Generate new master keys. These master keys are used to protect the
|
// Generate new master keys. These master keys are used to protect the
|
||||||
// crypto keys that will be generated next.
|
// crypto keys that will be generated next.
|
||||||
masterKeyPub, err := newSecretKey(&pubPassphrase)
|
masterKeyPub, err := newSecretKey(&pubPassphrase, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to master public key"
|
str := "failed to master public key"
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
masterKeyPriv, err := newSecretKey(&privPassphrase)
|
masterKeyPriv, err := newSecretKey(&privPassphrase, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to master private key"
|
str := "failed to master private key"
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
|
@ -1856,5 +1883,5 @@ func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcn
|
||||||
cryptoKeyPriv.Zero()
|
cryptoKeyPriv.Zero()
|
||||||
cryptoKeyScript.Zero()
|
cryptoKeyScript.Zero()
|
||||||
return newManager(db, net, masterKeyPub, masterKeyPriv, cryptoKeyPub,
|
return newManager(db, net, masterKeyPub, masterKeyPriv, cryptoKeyPub,
|
||||||
cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo), nil
|
cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo, config), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,12 @@ func testNamePrefix(tc *testContext) string {
|
||||||
return prefix + fmt.Sprintf("account #%d", tc.account)
|
return prefix + fmt.Sprintf("account #%d", tc.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fastScrypt = &waddrmgr.Options{
|
||||||
|
ScryptN: 16,
|
||||||
|
ScryptR: 8,
|
||||||
|
ScryptP: 1,
|
||||||
|
}
|
||||||
|
|
||||||
// testManagedPubKeyAddress ensures the data returned by all exported functions
|
// testManagedPubKeyAddress ensures the data returned by all exported functions
|
||||||
// provided by the passed managed p ublic key address matches the corresponding
|
// provided by the passed managed p ublic key address matches the corresponding
|
||||||
// fields in the provided expected address.
|
// fields in the provided expected address.
|
||||||
|
@ -1018,12 +1024,14 @@ func testChangePassphrase(tc *testContext) bool {
|
||||||
// generate a new secret key by replacing the generation function one
|
// generate a new secret key by replacing the generation function one
|
||||||
// that intentionally errors.
|
// that intentionally errors.
|
||||||
testName := "ChangePassphrase (public) with invalid new secret key"
|
testName := "ChangePassphrase (public) with invalid new secret key"
|
||||||
waddrmgr.TstReplaceNewSecretKeyFunc()
|
|
||||||
err := tc.manager.ChangePassphrase(pubPassphrase, pubPassphrase2, false)
|
var err error
|
||||||
|
waddrmgr.TstRunWithReplacedNewSecretKey(func() {
|
||||||
|
err = tc.manager.ChangePassphrase(pubPassphrase, pubPassphrase2, false)
|
||||||
|
})
|
||||||
if !checkManagerError(tc.t, testName, err, waddrmgr.ErrCrypto) {
|
if !checkManagerError(tc.t, testName, err, waddrmgr.ErrCrypto) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
waddrmgr.TstResetNewSecretKeyFunc()
|
|
||||||
|
|
||||||
// Attempt to change public passphrase with invalid old passphrase.
|
// Attempt to change public passphrase with invalid old passphrase.
|
||||||
testName = "ChangePassphrase (public) with invalid old passphrase"
|
testName = "ChangePassphrase (public) with invalid old passphrase"
|
||||||
|
@ -1159,7 +1167,7 @@ func testExportWatchingOnly(tc *testContext) bool {
|
||||||
mgr.Close()
|
mgr.Close()
|
||||||
|
|
||||||
// Open the watching-only manager and run all the tests again.
|
// Open the watching-only manager and run all the tests again.
|
||||||
mgr, err = waddrmgr.Open(woMgrName, pubPassphrase, &btcnet.MainNetParams)
|
mgr, err = waddrmgr.Open(woMgrName, pubPassphrase, &btcnet.MainNetParams, fastScrypt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Open Watching-Only: unexpected error: %v", err)
|
tc.t.Errorf("Open Watching-Only: unexpected error: %v", err)
|
||||||
return false
|
return false
|
||||||
|
@ -1431,14 +1439,15 @@ func TestManager(t *testing.T) {
|
||||||
// returned.
|
// returned.
|
||||||
mgrName := "mgrtest.bin"
|
mgrName := "mgrtest.bin"
|
||||||
_ = os.Remove(mgrName)
|
_ = os.Remove(mgrName)
|
||||||
_, err := waddrmgr.Open(mgrName, pubPassphrase, &btcnet.MainNetParams)
|
_, err := waddrmgr.Open(mgrName, pubPassphrase, &btcnet.MainNetParams,
|
||||||
|
fastScrypt)
|
||||||
if !checkManagerError(t, "Open non-existant", err, waddrmgr.ErrNoExist) {
|
if !checkManagerError(t, "Open non-existant", err, waddrmgr.ErrNoExist) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new manager.
|
// Create a new manager.
|
||||||
mgr, err := waddrmgr.Create(mgrName, seed, pubPassphrase, privPassphrase,
|
mgr, err := waddrmgr.Create(mgrName, seed, pubPassphrase, privPassphrase,
|
||||||
&btcnet.MainNetParams)
|
&btcnet.MainNetParams, fastScrypt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Create: unexpected error: %v", err)
|
t.Errorf("Create: unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
|
@ -1450,7 +1459,7 @@ func TestManager(t *testing.T) {
|
||||||
// Attempt to create the manager again to ensure the expected error is
|
// Attempt to create the manager again to ensure the expected error is
|
||||||
// returned.
|
// returned.
|
||||||
_, err = waddrmgr.Create(mgrName, seed, pubPassphrase, privPassphrase,
|
_, err = waddrmgr.Create(mgrName, seed, pubPassphrase, privPassphrase,
|
||||||
&btcnet.MainNetParams)
|
&btcnet.MainNetParams, fastScrypt)
|
||||||
if !checkManagerError(t, "Create existing", err, waddrmgr.ErrAlreadyExists) {
|
if !checkManagerError(t, "Create existing", err, waddrmgr.ErrAlreadyExists) {
|
||||||
mgr.Close()
|
mgr.Close()
|
||||||
return
|
return
|
||||||
|
@ -1469,7 +1478,8 @@ func TestManager(t *testing.T) {
|
||||||
|
|
||||||
// Open the manager and run all the tests again in open mode which
|
// Open the manager and run all the tests again in open mode which
|
||||||
// avoids reinserting new addresses like the create mode tests do.
|
// avoids reinserting new addresses like the create mode tests do.
|
||||||
mgr, err = waddrmgr.Open(mgrName, pubPassphrase, &btcnet.MainNetParams)
|
mgr, err = waddrmgr.Open(mgrName, pubPassphrase, &btcnet.MainNetParams,
|
||||||
|
fastScrypt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Open: unexpected error: %v", err)
|
t.Errorf("Open: unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue