From 402fcf0dd074aa3633be59f1fb800dcbb9f89cdd Mon Sep 17 00:00:00 2001 From: Lars Hesel Christensen Date: Fri, 24 Oct 2014 08:56:54 +0200 Subject: [PATCH] Make scrypt parameters overridable/optional --- waddrmgr/common_test.go | 5 --- waddrmgr/internal_test.go | 32 +++++++----------- waddrmgr/manager.go | 69 +++++++++++++++++++++++++++------------ waddrmgr/manager_test.go | 26 ++++++++++----- 4 files changed, 77 insertions(+), 55 deletions(-) diff --git a/waddrmgr/common_test.go b/waddrmgr/common_test.go index 1e1db14..e52acca 100644 --- a/waddrmgr/common_test.go +++ b/waddrmgr/common_test.go @@ -65,8 +65,3 @@ func hexToBytes(origHex string) []byte { } return buf } - -func init() { - // Tune the scrypt params down for tests so they execute quickly. - waddrmgr.TstSetScryptParams(16, 8, 1) -} diff --git a/waddrmgr/internal_test.go b/waddrmgr/internal_test.go index 3485d82..211a8c7 100644 --- a/waddrmgr/internal_test.go +++ b/waddrmgr/internal_test.go @@ -23,34 +23,24 @@ interface. The functions are only exported while the tests are being run. package waddrmgr -import ( - "github.com/conformal/btcwallet/snacl" -) +import "github.com/conformal/btcwallet/snacl" // TstMaxRecentHashes makes the unexported maxRecentHashes constant available // when tests are run. var TstMaxRecentHashes = maxRecentHashes -// TstSetScryptParams allows the scrypt parameters to be set to much lower -// values while the tests are running so they are faster. -func TstSetScryptParams(n, r, p int) { - scryptN = n - scryptR = r - scryptP = p -} - -// TstReplaceNewSecretKeyFunc replaces the new secret key generation function -// with a version that intentionally fails. -func TstReplaceNewSecretKeyFunc() { - newSecretKey = func(passphrase *[]byte) (*snacl.SecretKey, error) { +// Replace the Manager.newSecretKey function with the given one and calls +// the callback function. Afterwards the original newSecretKey +// function will be restored. +func TstRunWithReplacedNewSecretKey(callback func()) { + orig := newSecretKey + defer func() { + newSecretKey = orig + }() + newSecretKey = func(passphrase *[]byte, config *Options) (*snacl.SecretKey, error) { return nil, snacl.ErrDecryptFailed } -} - -// TstResetNewSecretKeyFunc resets the new secret key generation function to the -// original version. -func TstResetNewSecretKeyFunc() { - newSecretKey = defaultNewSecretKey + callback() } // TstCheckPublicPassphrase return true if the provided public passphrase is diff --git a/waddrmgr/manager.go b/waddrmgr/manager.go index 8ca2833..b6ee397 100644 --- a/waddrmgr/manager.go +++ b/waddrmgr/manager.go @@ -76,14 +76,6 @@ const ( 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 // would end up being the same bitcoin address (as is the case for pay-to-pubkey // and pay-to-pubkey-hash style of addresses). @@ -123,8 +115,9 @@ type unlockDeriveInfo struct { } // defaultNewSecretKey returns a new secret key. See newSecretKey. -func defaultNewSecretKey(passphrase *[]byte) (*snacl.SecretKey, error) { - return snacl.NewSecretKey(passphrase, scryptN, scryptR, scryptP) +func defaultNewSecretKey(passphrase *[]byte, config *Options) (*snacl.SecretKey, error) { + return snacl.NewSecretKey(passphrase, config.ScryptN, config.ScryptR, + config.ScryptP) } // 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 // order to encrypt it. 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 @@ -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 // the actual secret keys. - newMasterKey, err := newSecretKey(&newPassphrase) + newMasterKey, err := newSecretKey(&newPassphrase, m.config) if err != nil { str := "failed to create new master private key" return managerError(ErrCrypto, str, err) @@ -788,7 +784,7 @@ func (m *Manager) ExportWatchingOnly(newDbPath string, pubPassphrase []byte) (*M 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. @@ -1447,7 +1443,7 @@ func (m *Manager) AllActiveAddresses() ([]btcutil.Address, error) { func newManager(db *managerDB, net *btcnet.Params, masterKeyPub *snacl.SecretKey, masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor, cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte, - syncInfo *syncState) *Manager { + syncInfo *syncState, config *Options) *Manager { return &Manager{ db: db, @@ -1463,6 +1459,7 @@ func newManager(db *managerDB, net *btcnet.Params, masterKeyPub *snacl.SecretKey cryptoKeyPriv: &cryptoKey{}, cryptoKeyScriptEncrypted: cryptoKeyScriptEncrypted, 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 // the passed opened database. The public passphrase is required to decrypt the // 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. var watchingOnly bool 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 // call to new with the values loaded from the database. mgr := newManager(db, net, &masterKeyPub, &masterKeyPriv, cryptoKeyPub, - cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo) + cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo, config) mgr.watchingOnly = watchingOnly 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 // 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 // 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. if !fileExists(dbPath) { 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 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 @@ -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 // 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 // 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. if fileExists(dbPath) { return nil, managerError(ErrAlreadyExists, errAlreadyExists, nil) @@ -1682,6 +1705,10 @@ func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcn return nil, err } + if config == nil { + config = defaultConfig + } + // Generate the BIP0044 HD key structure to ensure the provided seed // 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 // crypto keys that will be generated next. - masterKeyPub, err := newSecretKey(&pubPassphrase) + masterKeyPub, err := newSecretKey(&pubPassphrase, config) if err != nil { str := "failed to master public key" return nil, managerError(ErrCrypto, str, err) } - masterKeyPriv, err := newSecretKey(&privPassphrase) + masterKeyPriv, err := newSecretKey(&privPassphrase, config) if err != nil { str := "failed to master private key" return nil, managerError(ErrCrypto, str, err) @@ -1856,5 +1883,5 @@ func Create(dbPath string, seed, pubPassphrase, privPassphrase []byte, net *btcn cryptoKeyPriv.Zero() cryptoKeyScript.Zero() return newManager(db, net, masterKeyPub, masterKeyPriv, cryptoKeyPub, - cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo), nil + cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo, config), nil } diff --git a/waddrmgr/manager_test.go b/waddrmgr/manager_test.go index d82b177..1afbabb 100644 --- a/waddrmgr/manager_test.go +++ b/waddrmgr/manager_test.go @@ -83,6 +83,12 @@ func testNamePrefix(tc *testContext) string { 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 // provided by the passed managed p ublic key address matches the corresponding // 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 // that intentionally errors. 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) { return false } - waddrmgr.TstResetNewSecretKeyFunc() // Attempt to change public passphrase with invalid old passphrase. testName = "ChangePassphrase (public) with invalid old passphrase" @@ -1159,7 +1167,7 @@ func testExportWatchingOnly(tc *testContext) bool { mgr.Close() // 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 { tc.t.Errorf("Open Watching-Only: unexpected error: %v", err) return false @@ -1431,14 +1439,15 @@ func TestManager(t *testing.T) { // returned. mgrName := "mgrtest.bin" _ = 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) { return } // Create a new manager. mgr, err := waddrmgr.Create(mgrName, seed, pubPassphrase, privPassphrase, - &btcnet.MainNetParams) + &btcnet.MainNetParams, fastScrypt) if err != nil { t.Errorf("Create: unexpected error: %v", err) return @@ -1450,7 +1459,7 @@ func TestManager(t *testing.T) { // Attempt to create the manager again to ensure the expected error is // returned. _, err = waddrmgr.Create(mgrName, seed, pubPassphrase, privPassphrase, - &btcnet.MainNetParams) + &btcnet.MainNetParams, fastScrypt) if !checkManagerError(t, "Create existing", err, waddrmgr.ErrAlreadyExists) { mgr.Close() return @@ -1469,7 +1478,8 @@ func TestManager(t *testing.T) { // Open the manager and run all the tests again in open mode which // 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 { t.Errorf("Open: unexpected error: %v", err) return