Make scrypt parameters overridable/optional

This commit is contained in:
Lars Hesel Christensen 2014-10-24 08:56:54 +02:00 committed by Dave Collins
parent ec8a5bc10c
commit 402fcf0dd0
4 changed files with 77 additions and 55 deletions

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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