class SecretKey(object): """ SecretKey is a password-derived key that can be used for encryption and decryption. """ def __init__(self, pw): """ Args: pw (byte-like): A password that deterministically generates the key. """ super().__init__() salt = ByteArray(generateSeed(KEY_SIZE)) b = lambda v: ByteArray(v).bytes() func, hashName, iterations = defaultKDFParams() self.key = ByteArray( hashlib.pbkdf2_hmac(hashName, b(pw), salt.bytes(), iterations)) digest = ByteArray(hashlib.sha256(self.key.b).digest()) self.keyParams = KDFParams(salt, digest) def params(self): """ The key params can be stored in plain text. They must be provided to `rekey` to regenerate a key. Returns: KDFParams: The hash parameters and salt used to generate the key. """ return self.keyParams def encrypt(self, thing): """ Encrypt the input using the key. Args: thing (byte-like): The thing to encrypt. """ return self.key.encrypt(thing) def decrypt(self, thing): """ Decrypt the input using the key. Args: thing (byte-like): The thing to decrypt. """ return self.key.decrypt(thing) @staticmethod def rekey(password, kp): """ Regenerate a key using its origin key parameters, as returned by `params`. Args: kp (KDFParams): The key parameters from the original generation of the key being regenerated. Returns: SecretKey: The regenerated key. """ sk = SecretKey(b"") sk.keyParams = kp b = lambda v: ByteArray(v).bytes() func = kp.kdfFunc if func == "pbkdf2_hmac": sk.key = ByteArray( hashlib.pbkdf2_hmac(kp.hashName, b(password), b(kp.salt), kp.iterations)) else: raise Exception("unkown key derivation function") checkDigest = ByteArray(hashlib.sha256(sk.key.b).digest()) if checkDigest != kp.digest: raise PasswordError("rekey digest check failed") return sk
def createNewAccountManager(seed, pubPassphrase, privPassphrase, chainParams): """ Create a new account manager and a set of BIP0044 keys for creating accounts. The zeroth account is created for the provided network parameters. Args: pubPassphrase (byte-like): A user-supplied password to protect the public keys. The public keys can always be generated from the private keys, but it may be convenient to perform some actions, such as address generation, without decrypting the private keys. privPassphrase (byte-like): A user-supplied password to protect the private the account private keys. chainParams (obj): Network parameters. """ # Ensure the private passphrase is not empty. if len(privPassphrase) == 0: raise Exception( "createAddressManager: private passphrase cannot be empty") # Derive the master extended key from the seed. root = newMaster(seed, chainParams) # Derive the cointype keys according to BIP0044. legacyCoinType, slip0044CoinType = coinTypes(chainParams) coinTypeLegacyKeyPriv = root.deriveCoinTypeKey(legacyCoinType) coinTypeSLIP0044KeyPriv = root.deriveCoinTypeKey(slip0044CoinType) # Derive the account key for the first account according to BIP0044. acctKeyLegacyPriv = coinTypeLegacyKeyPriv.deriveAccountKey(0) acctKeySLIP0044Priv = coinTypeSLIP0044KeyPriv.deriveAccountKey(0) # Ensure the branch keys can be derived for the provided seed according # to BIP0044. checkBranchKeys(acctKeyLegacyPriv) checkBranchKeys(acctKeySLIP0044Priv) # The address manager needs the public extended key for the account. acctKeyLegacyPub = acctKeyLegacyPriv.neuter() acctKeySLIP0044Pub = acctKeySLIP0044Priv.neuter() # Generate new master keys. These master keys are used to protect the # crypto keys that will be generated next. masterKeyPub = crypto.SecretKey(pubPassphrase) masterKeyPriv = crypto.SecretKey(privPassphrase) # Generate new crypto public, private, and script keys. These keys are # used to protect the actual public and private data such as addresses, # extended keys, and scripts. cryptoKeyPub = ByteArray(generateSeed(crypto.KEY_SIZE)) cryptoKeyPriv = ByteArray(generateSeed(crypto.KEY_SIZE)) cryptoKeyScript = ByteArray(generateSeed(crypto.KEY_SIZE)) # // Encrypt the crypto keys with the associated master keys. cryptoKeyPubEnc = masterKeyPub.encrypt(cryptoKeyPub.b) cryptoKeyPrivEnc = masterKeyPriv.encrypt(cryptoKeyPriv.b) cryptoKeyScriptEnc = masterKeyPriv.encrypt(cryptoKeyScript.b) # Encrypt the legacy cointype keys with the associated crypto keys. coinTypeLegacyKeyPub = coinTypeLegacyKeyPriv.neuter() ctpes = coinTypeLegacyKeyPub.string() coinTypeLegacyPubEnc = cryptoKeyPub.encrypt(ctpes.encode()) ctpes = coinTypeLegacyKeyPriv.string() coinTypeLegacyPrivEnc = cryptoKeyPriv.encrypt(ctpes.encode()) # Encrypt the SLIP0044 cointype keys with the associated crypto keys. coinTypeSLIP0044KeyPub = coinTypeSLIP0044KeyPriv.neuter() ctpes = coinTypeSLIP0044KeyPub.string() coinTypeSLIP0044PubEnc = cryptoKeyPub.encrypt(ctpes.encode()) ctpes = coinTypeSLIP0044KeyPriv.string() coinTypeSLIP0044PrivEnc = cryptoKeyPriv.encrypt(ctpes.encode()) # Encrypt the default account keys with the associated crypto keys. apes = acctKeyLegacyPub.string() acctPubLegacyEnc = cryptoKeyPub.encrypt(apes.encode()) apes = acctKeyLegacyPriv.string() acctPrivLegacyEnc = cryptoKeyPriv.encrypt(apes.encode()) apes = acctKeySLIP0044Pub.string() acctPubSLIP0044Enc = cryptoKeyPub.encrypt(apes.encode()) apes = acctKeySLIP0044Priv.string() acctPrivSLIP0044Enc = cryptoKeyPriv.encrypt(apes.encode()) # Save the information for the default account to the database. This # account is derived from the legacy coin type. baseAccount = Account(acctPubLegacyEnc, acctPrivLegacyEnc, DEFAULT_ACCOUNT_NAME, CoinSymbols.decred, chainParams.Name) # Save the account row for the 0th account derived from the coin type # 42 key. zerothAccount = Account(acctPubSLIP0044Enc, acctPrivSLIP0044Enc, DEFAULT_ACCOUNT_NAME, CoinSymbols.decred, chainParams.Name) # Open the account zerothAccount.open(cryptoKeyPriv) # Create the first payment address zerothAccount.generateNextPaymentAddress() # Close the account to zero the key zerothAccount.close() # ByteArray is mutable, so erase the keys. cryptoKeyPriv.zero() cryptoKeyScript.zero() log.debug("coinTypeLegacyKeyPriv: %s\n" % coinTypeLegacyKeyPriv.string()) log.debug("coinTypeSLIP0044KeyPriv: %s\n" % coinTypeSLIP0044KeyPriv.string()) log.debug("acctKeyLegacyPriv: %s\n" % acctKeyLegacyPriv.string()) log.debug("acctKeySLIP0044Priv: %s\n" % acctKeySLIP0044Priv.string()) log.debug("acctKeyLegacyPub: %s\n" % acctKeyLegacyPub.string()) log.debug("acctKeySLIP0044Pub: %s\n" % acctKeySLIP0044Pub.string()) log.debug("cryptoKeyPubEnc: %s\n" % cryptoKeyPubEnc.hex()) log.debug("cryptoKeyPrivEnc: %s\n" % cryptoKeyPrivEnc.hex()) log.debug("cryptoKeyScriptEnc: %s\n" % cryptoKeyScriptEnc.hex()) log.debug("coinTypeLegacyKeyPub: %s\n" % coinTypeLegacyKeyPub.string()) log.debug("coinTypeLegacyPubEnc: %s\n" % coinTypeLegacyPubEnc.hex()) log.debug("coinTypeLegacyPrivEnc: %s\n" % coinTypeLegacyPrivEnc.hex()) log.debug("coinTypeSLIP0044KeyPub: %s\n" % coinTypeSLIP0044KeyPub.string()) log.debug("coinTypeSLIP0044PubEnc: %s\n" % coinTypeSLIP0044PubEnc.hex()) log.debug("coinTypeSLIP0044PrivEnc: %s\n" % coinTypeSLIP0044PrivEnc.hex()) log.debug("acctPubLegacyEnc: %s\n" % acctPubLegacyEnc.hex()) log.debug("acctPrivLegacyEnc: %s\n" % acctPrivLegacyEnc.hex()) log.debug("acctPubSLIP0044Enc: %s\n" % acctPubSLIP0044Enc.hex()) log.debug("acctPrivSLIP0044Enc: %s\n" % acctPrivSLIP0044Enc.hex()) manager = AccountManager( cryptoKeyPubEnc=cryptoKeyPubEnc, cryptoKeyPrivEnc=cryptoKeyPrivEnc, cryptoKeyScriptEnc=cryptoKeyScriptEnc, coinTypeLegacyPubEnc=coinTypeLegacyPubEnc, coinTypeLegacyPrivEnc=coinTypeLegacyPrivEnc, coinTypeSLIP0044PubEnc=coinTypeSLIP0044PubEnc, coinTypeSLIP0044PrivEnc=coinTypeSLIP0044PrivEnc, baseAccount=baseAccount, privParams=masterKeyPriv.params(), pubParams=masterKeyPub.params(), ) manager.addAccount(zerothAccount) return manager