Example #1
0
	def test_all(self):
		tests = [
			(
				"topmost Istanbul Pluto vagabond treadmill Pacific brackish dictator goldfish Medusa afflict bravado chatter revolver Dupont midsummer stopwatch whimsical cowbell bottomless",
				ByteArray([
					0xE5, 0x82, 0x94, 0xF2, 0xE9, 0xA2, 0x27, 0x48,
					0x6E, 0x8B, 0x06, 0x1B, 0x31, 0xCC, 0x52, 0x8F, 0xD7,
					0xFA, 0x3F, 0x19
				]),
			),
			(
				"stairway souvenir flytrap recipe adrift upcoming artist positive spearhead Pandora spaniel stupendous tonic concurrent transit Wichita lockup visitor flagpole escapade",
				ByteArray([
					0xD1, 0xD4, 0x64, 0xC0, 0x04, 0xF0, 0x0F, 0xB5,
					0xC9, 0xA4, 0xC8, 0xD8, 0xE4, 0x33, 0xE7, 0xFB, 0x7F,
					0xF5, 0x62, 0x56
				]),
			),
		]
		listToLower = lambda l: [x.lower() for x in l]
		for i, (words, seed) in enumerate(tests):
			unWords = encode(seed)
			self.assertListEqual(listToLower(unWords[:len(unWords)-1]), listToLower(words.split()))
			unSeed = decode(words.split())
			self.assertEqual(seed, unSeed)
Example #2
0
    def tx(self, txid):
        """
        Get the MsgTx. Retreive it from the blockchain if necessary. 

        Args:
            txid (str): A hex encoded transaction ID to fetch. 

        Returns:
            MsgTx: The transaction.
        """
        hashKey = hashFromHex(txid).bytes()
        with self.txDB as txDB:
            try:
                encoded = ByteArray(txDB[hashKey])
                return msgtx.MsgTx.deserialize(encoded)
            except database.NoValue:
                try:
                    # Grab the hex encoded transaction
                    txHex = self.dcrdata.tx.hex(txid)
                    if not txHex:
                        raise Exception(
                            "failed to retrieve tx hex from dcrdata")
                    encoded = ByteArray(txHex)
                    txDB[hashKey] = encoded.bytes()
                    return msgtx.MsgTx.deserialize(encoded)
                except:
                    log.warning(
                        "unable to retrieve tx data from dcrdata at %s" %
                        self.dcrdata.baseUri)
        raise Exception("failed to reteive transaction")
Example #3
0
    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
Example #4
0
 def __init__(self, privVer, pubVer, key, pubKey, chainCode, parentFP,
              depth, childNum, isPrivate):
     """
     Args:
         privVer (byte-like): Network version bytes for extended priv keys.
         pubVer (byte-like): Network version bytes for extended pub keys
         key (byte-like): The key. 
         pubKey (byte-like): Will be the same as `key` for public key. Will
             be generated from key if zero is provided. 
         chainCode (byte-like): Chain code for key derivation.
         parentFP (ByteArray): parent key fingerprint
         depth (int): Key depth. 
         childNum (int): Child number.
         isPrivate (bool): Whether the key is a private or public key. 
     """
     assert len(privVer) == 4 and len(
         pubVer) == 4, "Network version bytes of incorrect length"
     self.privVer = ByteArray(privVer)
     self.pubVer = ByteArray(pubVer)
     self.key = ByteArray(key)
     self.pubKey = ByteArray(pubKey)
     if self.pubKey.iszero():
         if isPrivate:
             self.pubKey = Curve.publicKey(
                 self.key.int()).serializeCompressed()
         else:
             self.pubKey = self.key
     self.chainCode = ByteArray(chainCode)
     self.parentFP = ByteArray(parentFP)
     self.depth = depth
     self.childNum = childNum
     self.isPrivate = isPrivate
Example #5
0
    def blockHeaderByHeight(self, height):
        """
        Get the block header by height. The blcck header is retreived from the
        blockchain if necessary, in which case it is stored. 

        Args:
            height int: The block height

        Returns:
            BlockHeader: The block header.
        """
        with self.heightMap as heightMap, self.headerDB as headers:
            try:
                hashKey = heightMap[height]
                serialized = headers[hashKey]
                return msgblock.BlockHeader.deserialize(serialized)
            except database.NoValue:
                try:
                    hexBlock = self.blockchain.block.header.raw(idx=height)
                    blockHeader = msgblock.BlockHeader.deserialize(
                        ByteArray(hexBlock))
                    self.saveBlockHeader(blockHeader)
                    return blockHeader
                except:
                    log.warning("unable to retrieve block header")
        raise Exception("failed to get block header at height %i" % height)
Example #6
0
    def blockHeader(self, hexHash):
        """
        blockHeader will produce a blockHeader implements the BlockHeader API.

        Args:
            bHash (str): The block hash of the block header.

        Returns: 
            BlockHeader: An object which implements the BlockHeader API.
        """
        with self.headerDB as headers:
            try:
                serialized = headers[hashFromHex(hexHash).bytes()]
                return msgblock.BlockHeader.deserialize(serialized)
            except database.NoValue:
                try:
                    block = self.dcrdata.block.hash.header.raw(hexHash)
                    blockHeader = msgblock.BlockHeader.deserialize(
                        ByteArray(block["hex"]))
                    self.saveBlockHeader(blockHeader)
                    return blockHeader
                except Exception as e:
                    log.warning("unable to retrieve block header: %s" %
                                formatTraceback(e))
        raise Exception("failed to get block header for block %s" % hexHash)
Example #7
0
    def openAccount(self, acct, pw):
        """
        Open an account. 

        Args:
            acct (int): The acccount index, which is its position in the accounts
                list. 
            net (obj): Network parameters.
            pw (byte-like): An UTF-8-encoded user-supplied password for the
                account.

        Returns:
            Account: The open account. 
        """
        # A string at this point is considered to be ascii, not hex.
        if isinstance(pw, str):
            pw = pw.encode()
        # Generate the master key, which is used to decrypt the crypto keys.
        userSecret = crypto.SecretKey.rekey(pw, self.privParams)
        # Decrypt the crypto keys.
        cryptKeyPriv = ByteArray(
            userSecret.decrypt(self.cryptoKeyPrivEnc.bytes()))
        # Retreive and open the account.
        account = self.accounts[acct]
        account.open(cryptKeyPriv)
        return account
Example #8
0
 def test_decode(self):
     encoded = "060000000bd25508e99bf6f8399efce65762b55873d69dd05a7871631ac8fa7a36f1d05c977ea75040b905415cbc8f7dd519831a031ef5cd9c6a187a9eab8136c8b44fda000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000ffff7f20204e0000000000001b00000066010000aadd025d00000000255221163779dfe800000000000000000000000000000000000000000000000000000000"
     header = BlockHeader.deserialize(ByteArray(encoded))
     print("version: %s" % repr(header.version))
     print("prevBlock: %s" % repr(reversed(header.prevBlock).hex()))
     print("merkleRoot: %s" % repr(reversed(header.merkleRoot).hex()))
     print("stakeRoot: %s" % repr(reversed(header.stakeRoot).hex()))
     print("voteBits: %s" % repr(header.voteBits))
     print("finalState: %s" % repr(reversed(header.finalState).hex()))
     print("voters: %s" % repr(header.voters))
     print("freshStake: %s" % repr(header.freshStake))
     print("revocations: %s" % repr(header.revocations))
     print("poolSize: %s" % repr(header.poolSize))
     print("bits: %s" % repr(header.bits))
     print("sBits: %s" % repr(header.sBits))
     print("height: %s" % repr(header.height))
     print("size: %s" % repr(header.size))
     print("timestamp: %s" % repr(header.timestamp))
     print("nonce: %s" % repr(header.nonce))
     print("extraData: %s" % repr(header.extraData.hex()))
     print("stakeVersion: %s" % repr(header.stakeVersion))
     recoded = header.serialize().hex()
     self.assertEqual(recoded, encoded)
     self.assertEqual(
         header.id(),
         "52dc18bd18910e0e785411305b04f1281353ab29135a144c0fca9ea4746c2b66")
Example #9
0
def mac(key, msg):
    """
    SHA256-based message authentication code.

    Returns:
        ByteArray: The authentication hash.
    """
    return ByteArray(hmacDigest(key.bytes(), msg.bytes(), hashlib.sha256))
Example #10
0
def hashH(b):
    """
    The BLAKE256 hash as a ByteArray.

    Returns:
        ByteArray: The hash.
    """
    return ByteArray(blake_hash(b), length=BLAKE256_SIZE)
Example #11
0
def hash160(b):
    """
    A RIPEMD160 hash of the blake256 hash of the input.

    Returns:
        ByteArray: A 20-byte hash.
    """
    h = hashlib.new("ripemd160")
    h.update(blake_hash(b))
    return ByteArray(h.digest())
Example #12
0
def hashFromHex(s):
    """
    Parse a transaction hash or block hash from a hexadecimal string.
    
    Args:
        s (str): A byte-revesed, hexadecimal string encoded hash.

    Returns:
        ByteArray: Decoded hash
    """
    return reversed(ByteArray(s))
Example #13
0
    def string(self):
        """
        string returns the extended key as a base58-encoded string. See
        `decodeExtendedKey` for decoding.

        Returns:
            str: The encoded extended key.
        """
        if self.key.iszero():
            raise ZeroBytesError("unexpected zero key")

        childNumBytes = ByteArray(self.childNum, length=4)
        depthByte = ByteArray(self.depth % 256, length=1)

        # The serialized format is:
        #   version (4) || depth (1) || parent fingerprint (4)) ||
        #   child num (4) || chain code (32) || key data (33) || checksum (4)
        serializedBytes = ByteArray(
            bytearray(0))  # length serializedKeyLen + 4 after appending
        if self.isPrivate:
            serializedBytes += self.privVer
        else:
            serializedBytes += self.pubVer

        serializedBytes += depthByte
        serializedBytes += self.parentFP
        serializedBytes += childNumBytes
        serializedBytes += self.chainCode
        if self.isPrivate:
            serializedBytes += bytearray(1)
            serializedBytes += self.key
        else:
            serializedBytes += self.pubKey

        checkSum = checksum(serializedBytes.b)[:4]
        serializedBytes += checkSum
        return b58encode(serializedBytes.bytes()).decode()
Example #14
0
 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)
Example #15
0
    def string(self):
        """
        A base-58 encoding of the pubkey hash.

        Returns: 
            str: The encoded address.
        """
        b = ByteArray(self.netID)
        b += self.pkHash
        b += checksum(b.b)
        x = b.int()

        answer = ""

        while x > 0:
            m = x % RADIX
            x = x // RADIX
            answer += ALPHABET[m]

        while len(answer) < len(b) * 136 // 100:
            answer += ALPHABET[0]

        # reverse
        return answer[::-1]
Example #16
0
 def test_accounts(self):
     pw = "abc".encode()
     am = createNewAccountManager(testSeed, bytearray(0), pw, nets.mainnet)
     rekey = am.acctPrivateKey(0, nets.mainnet, pw)
     pubFromPriv = rekey.neuter()
     addr1 = pubFromPriv.deriveChildAddress(5, nets.mainnet)
     pubKey = am.acctPublicKey(0, nets.mainnet, "")
     addr2 = pubKey.deriveChildAddress(5, nets.mainnet)
     self.assertEqual(addr1, addr2)
     acct = am.openAccount(0, pw)
     for n in range(20):
         acct.getNextPaymentAddress()
     v = 5
     satoshis = v * 1e8
     txid = "abcdefghijkl"
     vout = 2
     from tinydecred.pydecred import dcrdata
     utxo = dcrdata.UTXO(
         address=None,
         txid=txid,
         vout=vout,
         scriptPubKey=ByteArray(0),
         amount=v,
         satoshis=satoshis,
         maturity=1,
     )
     utxocount = lambda: len(list(acct.utxoscan()))
     acct.addUTXO(utxo)
     self.assertEqual(utxocount(), 1)
     self.assertEqual(acct.calcBalance(1).total, satoshis)
     self.assertEqual(acct.calcBalance(1).available, satoshis)
     self.assertEqual(acct.calcBalance(0).available, 0)
     self.assertIsNot(acct.getUTXO(txid, vout), None)
     self.assertIs(acct.getUTXO("", -1), None)
     self.assertTrue(acct.caresAboutTxid(txid))
     utxos = acct.UTXOsForTXID(txid)
     self.assertEqual(len(utxos), 1)
     acct.spendUTXOs(utxos)
     self.assertEqual(utxocount(), 0)
     acct.addUTXO(utxo)
     self.assertEqual(utxocount(), 1)
     acct.spendUTXO(utxo)
     self.assertEqual(utxocount(), 0)
Example #17
0
def decode(words):
	"""
	DecodeMnemonics returns the decoded value that is encoded by words.  Any
	words that are whitespace are empty are skipped.
	"""
	_, byteMap = pgWords()
	decoded = [0]*len(words)
	idx = 0
	for word in words:
		word = word.strip().lower()
		if word == "":
			continue
		if word not in byteMap:
			raise Exception("unknown words in mnemonic key: %s" % word)
		b = byteMap[word]
		if int(b%2) != idx%2:
			raise Exception("word %v is not valid at position %v, check for missing words" % (w, idx))
		decoded[idx] = b // 2
		idx += 1
	return ByteArray(decoded[:idx])
Example #18
0
def writeVarInt(pver, val):
    """ 
	writeVarInt serializes val to w using a variable number of bytes depending
	on its value.
	"""

    if val < 0xfd:
        return ByteArray(val, length=1)  # will be length 1

    if val <= MaxUint16:
        b = ByteArray(0xfd)
        b += ByteArray(val, length=2).littleEndian()
        return b

    if val <= MaxUint32:
        b = ByteArray(0xfe)
        b += ByteArray(val, length=4).littleEndian()
        return b

    b = ByteArray(0xff)
    b += ByteArray(val, length=8).littleEndian()
    return b
Example #19
0
    def test_priv_keys(self):
        key = ByteArray(
            "eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694")
        pk = privKeyFromBytes(key)

        inHash = ByteArray("00010203040506070809")
Example #20
0
        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


testSeed = ByteArray(
    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").b


def addressForPubkeyBytes(b, net):
    return crypto.newAddressPubKeyHash(crypto.hash160(b), net,
                                       crypto.STEcdsaSecp256k1).string()


class TestAccounts(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        helpers.prepareLogger("TestTinyCrypto")
        # log.setLevel(0)
    def test_child_neuter(self):
        extKey = newMaster(testSeed, nets.mainnet)
        extKey.child(0)
Example #21
0
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
Example #22
0
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
Example #23
0
class ExtendedKey:
    """
    ExtendedKey houses all the information needed to support a BIP0044 
    hierarchical deterministic extended key.
    """
    def __init__(self, privVer, pubVer, key, pubKey, chainCode, parentFP,
                 depth, childNum, isPrivate):
        """
        Args:
            privVer (byte-like): Network version bytes for extended priv keys.
            pubVer (byte-like): Network version bytes for extended pub keys
            key (byte-like): The key. 
            pubKey (byte-like): Will be the same as `key` for public key. Will
                be generated from key if zero is provided. 
            chainCode (byte-like): Chain code for key derivation.
            parentFP (ByteArray): parent key fingerprint
            depth (int): Key depth. 
            childNum (int): Child number.
            isPrivate (bool): Whether the key is a private or public key. 
        """
        assert len(privVer) == 4 and len(
            pubVer) == 4, "Network version bytes of incorrect length"
        self.privVer = ByteArray(privVer)
        self.pubVer = ByteArray(pubVer)
        self.key = ByteArray(key)
        self.pubKey = ByteArray(pubKey)
        if self.pubKey.iszero():
            if isPrivate:
                self.pubKey = Curve.publicKey(
                    self.key.int()).serializeCompressed()
            else:
                self.pubKey = self.key
        self.chainCode = ByteArray(chainCode)
        self.parentFP = ByteArray(parentFP)
        self.depth = depth
        self.childNum = childNum
        self.isPrivate = isPrivate

    # def __tojson__(self):
    #     return {
    #         "privVer": self.privVer,
    #         "pubVer": self.pubVer,
    #         "key": self.key,
    #         "pubKey": self.pubKey,
    #         "chainCode": self.chainCode,
    #         "parentFP": self.parentFP,
    #         "depth": self.depth,
    #         "childNum": self.childNum,
    #         "isPrivate": self.isPrivate,
    #     }
    # @staticmethod
    # def __fromjson__(obj):
    #     return ExtendedKey(
    #         privVer = obj["privVer"],
    #         pubVer = obj["pubVer"],
    #         key = obj["key"],
    #         pubKey = obj["pubKey"],
    #         chainCode = obj["chainCode"],
    #         parentFP = obj["parentFP"],
    #         depth = obj["depth"],
    #         childNum = obj["childNum"],
    #         isPrivate = obj["isPrivate"],
    #     )
    def deriveCoinTypeKey(self, coinType):
        """
        First two hardened child derivations in accordance with BIP0044. 

        Args:
            coinType (int): The BIP0044 coin type. For a full list, see
                https://github.com/satoshilabs/slips/blob/master/slip-0044.md

        Returns:
            ExtendedKey: The coin-type key.
        """
        if coinType > MAX_COIN_TYPE:
            raise ParameterRangeError("coinType too high. %i > %i" %
                                      (coinType, MAX_COIN_TYPE))

        purpose = self.child(44 + HARDENED_KEY_START)

        # Derive the purpose key as a child of the master node.
        return purpose.child(coinType + HARDENED_KEY_START)

    def child(self, i):
        """
        Child returns a derived child extended key at the given index.  When this
        extended key is a private extended key (as determined by the IsPrivate
        function), a private extended key will be derived.  Otherwise, the derived
        extended key will be also be a public extended key.
                
        When the index is greater to or equal than the HardenedKeyStart constant, the
        derived extended key will be a hardened extended key.  It is only possible to
        derive a hardended extended key from a private extended key.  Consequently,
        this function will return ErrDeriveHardFromPublic if a hardened child
        extended key is requested from a public extended key.
                
        A hardened extended key is useful since, as previously mentioned, it requires
        a parent private extended key to derive.  In other words, normal child
        extended public keys can be derived from a parent public extended key (no
        knowledge of the parent private key) whereas hardened extended keys may not
        be.
                
        NOTE: There is an extremely small chance (< 1 in 2^127) the specific child
        index does not derive to a usable child.  The ErrInvalidChild error will be
        returned if this should occur, and the caller is expected to ignore the
        invalid child and simply increment to the next index.

        There are four scenarios that could happen here:
        1) Private extended key -> Hardened child private extended key
        2) Private extended key -> Non-hardened child private extended key
        3) Public extended key -> Non-hardened child public extended key
        4) Public extended key -> Hardened child public extended key (INVALID!)

        Args:
            i (int): Child number.

        Returns:
            ExtendedKey: The child key.
        """

        # Case #4 is invalid, so error out early.
        # A hardened child extended key may not be created from a public
        # extended key.
        isChildHardened = i >= HARDENED_KEY_START
        if not self.isPrivate and isChildHardened:
            raise ParameterRangeError(
                "cannot generate hardened child from public extended key")

        # The data used to derive the child key depends on whether or not the
        # child is hardened per [BIP32].
        #
        # For hardened children:
        #   0x00 || ser256(parentKey) || ser32(i)
        #
        # For normal children:
        #   serP(parentPubKey) || ser32(i)
        keyLen = 33
        data = ByteArray(bytearray(keyLen + 4))

        if isChildHardened:
            # Case #1.
            # When the child is a hardened child, the key is known to be a
            # private key due to the above early return.  Pad it with a
            # leading zero as required by [BIP32] for deriving the child.
            data[1] = self.key
        else:
            # Case #2 or #3.
            # This is either a public or private extended key, but in
            # either case, the data which is used to derive the child key
            # starts with the secp256k1 compressed public key bytes.
            data[0] = ByteArray(self.pubKey)
        data[keyLen] = ByteArray(i, length=len(data) - keyLen)

        data |= i  # ByteArray will handle the type conversion

        # Take the HMAC-SHA512 of the current key's chain code and the derived
        # data:
        #   I = HMAC-SHA512(Key = chainCode, Data = data)
        ilr = ByteArray(hmacDigest(self.chainCode.b, data.b, hashlib.sha512))

        # Split "I" into two 32-byte sequences Il and Ir where:
        #   Il = intermediate key used to derive the child
        #   Ir = child chain code
        il = ilr[:len(ilr) // 2]
        childChainCode = ilr[len(ilr) // 2:]

        # Both derived public or private keys rely on treating the left 32-byte
        # sequence calculated above (Il) as a 256-bit integer that must be
        # within the valid range for a secp256k1 private key.  There is a small
        # chance (< 1 in 2^127) this condition will not hold, and in that case,
        # a child extended key can't be created for this index and the caller
        # should simply increment to the next index.
        if il.int() >= Curve.N or il.iszero():
            raise ParameterRangeError(
                "ExtendedKey.child: generated Il outside valid range")

        # The algorithm used to derive the child key depends on whether or not
        # a private or public child is being derived.
        #
        # For private children:
        #   childKey = parse256(Il) + parentKey
        #
        # For public children:
        #   childKey = serP(point(parse256(Il)) + parentKey)
        isPrivate = False
        if self.isPrivate:
            # Case #1 or #2.
            # Add the parent private key to the intermediate private key to
            # derive the final child key.
            #
            # childKey = parse256(Il) + parenKey
            childKey = ByteArray((self.key.int() + il.int()) % Curve.N)
            isPrivate = True
        else:
            # Case #3.
            # Calculate the corresponding intermediate public key for
            # intermediate private key.

            x, y = Curve.scalarBaseMult(il.int())  # Curve.G as ECPointJacobian
            if x == 0 or y == 0:
                raise ParameterRangeError(
                    "ExtendedKey.child: generated pt outside valid range")
            # Convert the serialized compressed parent public key into X
            # and Y coordinates so it can be added to the intermediate
            # public key.
            pubKey = Curve.parsePubKey(self.key)
            # Add the intermediate public key to the parent public key to
            # derive the final child key.
            #
            # childKey = serP(point(parse256(Il)) + parentKey)
            # childX, childY := curve.Add(pt.x, pt.y, pubKey.X, pubKey.Y)
            childX, childY = Curve.add(x, y, pubKey.x, pubKey.y)
            # pk := secp256k1.PublicKey{Curve: secp256k1.S256(), X: childX, Y: childY}
            # childKey = pk.SerializeCompressed()
            childKey = PublicKey(Curve, childX, childY).serializeCompressed()

        # The fingerprint of the parent for the derived child is the first 4
        # bytes of the RIPEMD160(BLAKE256(parentPubKey)).
        parentFP = hash160(self.pubKey.b)[:4]

        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=childKey,
            pubKey="",
            chainCode=childChainCode,
            parentFP=parentFP,
            depth=self.depth + 1,
            childNum=i,
            isPrivate=isPrivate,
        )

    def deriveAccountKey(self, account):
        """
        deriveAccountKey derives the extended key for an account according to the
        hierarchy described by BIP0044 given the master node.
                
        In particular this is the hierarchical deterministic extended key path:
          m/44'/<coin type>'/<account>'

        Args:
            account (int): Account number.

        Returns: 
            ExtendedKey: Account key.
        """
        # Enforce maximum account number.
        if account > MAX_ACCOUNT_NUM:
            raise ParameterRangeError(
                "deriveAccountKey: account number greater than MAX_ACCOUNT_NUM"
            )

        # Derive the account key as a child of the coin type key.
        return self.child(account + HARDENED_KEY_START)

    def neuter(self):
        """
        neuter returns a new extended public key from this extended private key.  The
        same extended key will be returned unaltered if it is already an extended
        public key.

        As the name implies, an extended public key does not have access to the
        private key, so it is not capable of signing transactions or deriving
        child extended private keys.  However, it is capable of deriving further
        child extended public keys.

        Returns:
            ExtendedKey: The public extended key.
        """
        # Already an extended public key.
        if not self.isPrivate:
            return self

        # Convert it to an extended public key.  The key for the new extended
        # key will simply be the pubkey of the current extended private key.
        #
        # This is the function N((k,c)) -> (K, c) from [BIP32].
        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=self.pubKey,
            pubKey=self.pubKey,
            chainCode=self.chainCode,
            parentFP=self.parentFP,
            depth=self.depth,
            childNum=self.childNum,
            isPrivate=False,
        )

    def string(self):
        """
        string returns the extended key as a base58-encoded string. See
        `decodeExtendedKey` for decoding.

        Returns:
            str: The encoded extended key.
        """
        if self.key.iszero():
            raise ZeroBytesError("unexpected zero key")

        childNumBytes = ByteArray(self.childNum, length=4)
        depthByte = ByteArray(self.depth % 256, length=1)

        # The serialized format is:
        #   version (4) || depth (1) || parent fingerprint (4)) ||
        #   child num (4) || chain code (32) || key data (33) || checksum (4)
        serializedBytes = ByteArray(
            bytearray(0))  # length serializedKeyLen + 4 after appending
        if self.isPrivate:
            serializedBytes += self.privVer
        else:
            serializedBytes += self.pubVer

        serializedBytes += depthByte
        serializedBytes += self.parentFP
        serializedBytes += childNumBytes
        serializedBytes += self.chainCode
        if self.isPrivate:
            serializedBytes += bytearray(1)
            serializedBytes += self.key
        else:
            serializedBytes += self.pubKey

        checkSum = checksum(serializedBytes.b)[:4]
        serializedBytes += checkSum
        return b58encode(serializedBytes.bytes()).decode()

    def deriveChildAddress(self, i, net):
        """
        The base-58 encoded address for the i'th child.

        Args:
            i (int): Child number.
            net (obj): Network parameters.
        """
        child = self.child(i)
        return newAddressPubKeyHash(
            hash160(child.publicKey().serializeCompressed().b), net,
            STEcdsaSecp256k1).string()

    def privateKey(self):
        """
        A PrivateKey structure that can be used for signatures.

        Returns:
            PrivateKey: The private key structure.
        """
        return privKeyFromBytes(self.key)

    def publicKey(self):
        """
        A PublicKey structure of the pubKey.

        Returns:
            PublicKey: The public key structure.
        """
        return Curve.parsePubKey(self.pubKey)
Example #24
0
def decodeExtendedKey(net, pw, key):
    """
    Decode an base58 ExtendedKey using the passphrase and network parameters.

    Args:
        pw (SecretKey or ByteAray): The encryption key.
        key (str): Base-58 encoded extended key.

    Returns:
        ExtendedKey: The decoded key.
    """
    decoded = ByteArray(b58decode(pw.decrypt(key.bytes())))
    if len(decoded) != SERIALIZED_KEY_LENGTH + 4:
        raise Exception("decoded private key is wrong length")

    # The serialized format is:
    #   version (4) || depth (1) || parent fingerprint (4)) ||
    #   child num (4) || chain code (32) || key data (33) || checksum (4)

    # Split the payload and checksum up and ensure the checksum matches.
    payload = decoded[:len(decoded) - 4]
    checkSum = decoded[len(decoded) - 4:]
    if checkSum != checksum(payload.b)[:4]:
        raise Exception("wrong checksum")

    # Ensure the version encoded in the payload matches the provided network.
    privVersion = net.HDPrivateKeyID
    pubVersion = net.HDPublicKeyID
    version = payload[:4]
    if version != privVersion and version != pubVersion:
        raise Exception("unknown version")

    # Deserialize the remaining payload fields.
    depth = payload[4:5].int()
    parentFP = payload[5:9]
    childNum = payload[9:13].int()
    chainCode = payload[13:45]
    keyData = payload[45:78]

    # The key data is a private key if it starts with 0x00.  Serialized
    # compressed pubkeys either start with 0x02 or 0x03.
    isPrivate = keyData[0] == 0x00
    if isPrivate:
        # Ensure the private key is valid.  It must be within the range
        # of the order of the secp256k1 curve and not be 0.
        keyData = keyData[1:]
        # if keyNum.Cmp(secp256k1.S256().N) >= 0 || keyNum.Sign() == 0 {
        if keyData >= Curve.N or keyData.iszero():
            raise Exception("unusable key")
        # Ensure the public key parses correctly and is actually on the
        # secp256k1 curve.
        Curve.publicKey(keyData.int())

    return ExtendedKey(
        privVer=privVersion,
        pubVer=pubVersion,
        key=keyData,
        pubKey="",
        chainCode=chainCode,
        parentFP=parentFP,
        depth=depth,
        childNum=childNum,
        isPrivate=isPrivate,
    )
Example #25
0
    def btcEncode(self, pver):

        # 	sec := uint32(bh.Timestamp.Unix())
        # return writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
        # 	&bh.StakeRoot, bh.VoteBits, bh.FinalState, bh.Voters,
        # 	bh.FreshStake, bh.Revocations, bh.PoolSize, bh.Bits, bh.SBits,
        # 	bh.Height, bh.Size, sec, bh.Nonce, bh.ExtraData,
        # 	bh.StakeVersion)

        # byte sizes
        int64 = 8
        int32 = uint32 = 4
        uint16 = 2
        finalStateSize = 6
        uint8 = 1
        extraDataSize = 32

        b = ByteArray(0, length=MaxHeaderSize)
        i = 0
        b[i] = ByteArray(self.version, length=int32).littleEndian()
        i += int32
        b[i] = ByteArray(self.prevBlock, length=HASH_SIZE)
        i += HASH_SIZE
        b[i] = ByteArray(self.merkleRoot, length=HASH_SIZE)
        i += HASH_SIZE
        b[i] = ByteArray(self.stakeRoot, length=HASH_SIZE)
        i += HASH_SIZE
        b[i] = ByteArray(self.voteBits, length=uint16).littleEndian()
        i += uint16
        b[i] = ByteArray(self.finalState, length=finalStateSize)
        i += finalStateSize
        b[i] = ByteArray(self.voters, length=uint16).littleEndian()
        i += uint16
        b[i] = ByteArray(self.freshStake, length=uint8).littleEndian()
        i += uint8
        b[i] = ByteArray(self.revocations, length=uint8).littleEndian()
        i += uint8
        b[i] = ByteArray(self.poolSize, length=uint32).littleEndian()
        i += uint32
        b[i] = ByteArray(self.bits, length=uint32).littleEndian()
        i += uint32
        b[i] = ByteArray(self.sBits, length=int64).littleEndian()
        i += int64
        b[i] = ByteArray(self.height, length=uint32).littleEndian()
        i += uint32
        b[i] = ByteArray(self.size, length=uint32).littleEndian()
        i += uint32
        b[i] = ByteArray(self.timestamp, length=uint32).littleEndian()
        i += uint32
        b[i] = ByteArray(self.nonce, length=uint32).littleEndian()
        i += uint32
        b[i] = ByteArray(self.extraData, length=extraDataSize)
        i += extraDataSize
        b[i] = ByteArray(self.stakeVersion, length=uint32).littleEndian()
        i += uint32
        if i != MaxHeaderSize:
            raise Exception("unexpected BlockHeader enocoded size")
        return b
Example #26
0
 def acctPublicKey(self, acct, net, pw):
     userSecret = crypto.SecretKey.rekey(pw, self.pubParams)
     cryptKeyPub = ByteArray(
         userSecret.decrypt(self.cryptoKeyPubEnc.bytes()))
     account = self.accounts[acct]
     return account.publicExtendedKey(cryptKeyPub)
Example #27
0
    def child(self, i):
        """
        Child returns a derived child extended key at the given index.  When this
        extended key is a private extended key (as determined by the IsPrivate
        function), a private extended key will be derived.  Otherwise, the derived
        extended key will be also be a public extended key.
                
        When the index is greater to or equal than the HardenedKeyStart constant, the
        derived extended key will be a hardened extended key.  It is only possible to
        derive a hardended extended key from a private extended key.  Consequently,
        this function will return ErrDeriveHardFromPublic if a hardened child
        extended key is requested from a public extended key.
                
        A hardened extended key is useful since, as previously mentioned, it requires
        a parent private extended key to derive.  In other words, normal child
        extended public keys can be derived from a parent public extended key (no
        knowledge of the parent private key) whereas hardened extended keys may not
        be.
                
        NOTE: There is an extremely small chance (< 1 in 2^127) the specific child
        index does not derive to a usable child.  The ErrInvalidChild error will be
        returned if this should occur, and the caller is expected to ignore the
        invalid child and simply increment to the next index.

        There are four scenarios that could happen here:
        1) Private extended key -> Hardened child private extended key
        2) Private extended key -> Non-hardened child private extended key
        3) Public extended key -> Non-hardened child public extended key
        4) Public extended key -> Hardened child public extended key (INVALID!)

        Args:
            i (int): Child number.

        Returns:
            ExtendedKey: The child key.
        """

        # Case #4 is invalid, so error out early.
        # A hardened child extended key may not be created from a public
        # extended key.
        isChildHardened = i >= HARDENED_KEY_START
        if not self.isPrivate and isChildHardened:
            raise ParameterRangeError(
                "cannot generate hardened child from public extended key")

        # The data used to derive the child key depends on whether or not the
        # child is hardened per [BIP32].
        #
        # For hardened children:
        #   0x00 || ser256(parentKey) || ser32(i)
        #
        # For normal children:
        #   serP(parentPubKey) || ser32(i)
        keyLen = 33
        data = ByteArray(bytearray(keyLen + 4))

        if isChildHardened:
            # Case #1.
            # When the child is a hardened child, the key is known to be a
            # private key due to the above early return.  Pad it with a
            # leading zero as required by [BIP32] for deriving the child.
            data[1] = self.key
        else:
            # Case #2 or #3.
            # This is either a public or private extended key, but in
            # either case, the data which is used to derive the child key
            # starts with the secp256k1 compressed public key bytes.
            data[0] = ByteArray(self.pubKey)
        data[keyLen] = ByteArray(i, length=len(data) - keyLen)

        data |= i  # ByteArray will handle the type conversion

        # Take the HMAC-SHA512 of the current key's chain code and the derived
        # data:
        #   I = HMAC-SHA512(Key = chainCode, Data = data)
        ilr = ByteArray(hmacDigest(self.chainCode.b, data.b, hashlib.sha512))

        # Split "I" into two 32-byte sequences Il and Ir where:
        #   Il = intermediate key used to derive the child
        #   Ir = child chain code
        il = ilr[:len(ilr) // 2]
        childChainCode = ilr[len(ilr) // 2:]

        # Both derived public or private keys rely on treating the left 32-byte
        # sequence calculated above (Il) as a 256-bit integer that must be
        # within the valid range for a secp256k1 private key.  There is a small
        # chance (< 1 in 2^127) this condition will not hold, and in that case,
        # a child extended key can't be created for this index and the caller
        # should simply increment to the next index.
        if il.int() >= Curve.N or il.iszero():
            raise ParameterRangeError(
                "ExtendedKey.child: generated Il outside valid range")

        # The algorithm used to derive the child key depends on whether or not
        # a private or public child is being derived.
        #
        # For private children:
        #   childKey = parse256(Il) + parentKey
        #
        # For public children:
        #   childKey = serP(point(parse256(Il)) + parentKey)
        isPrivate = False
        if self.isPrivate:
            # Case #1 or #2.
            # Add the parent private key to the intermediate private key to
            # derive the final child key.
            #
            # childKey = parse256(Il) + parenKey
            childKey = ByteArray((self.key.int() + il.int()) % Curve.N)
            isPrivate = True
        else:
            # Case #3.
            # Calculate the corresponding intermediate public key for
            # intermediate private key.

            x, y = Curve.scalarBaseMult(il.int())  # Curve.G as ECPointJacobian
            if x == 0 or y == 0:
                raise ParameterRangeError(
                    "ExtendedKey.child: generated pt outside valid range")
            # Convert the serialized compressed parent public key into X
            # and Y coordinates so it can be added to the intermediate
            # public key.
            pubKey = Curve.parsePubKey(self.key)
            # Add the intermediate public key to the parent public key to
            # derive the final child key.
            #
            # childKey = serP(point(parse256(Il)) + parentKey)
            # childX, childY := curve.Add(pt.x, pt.y, pubKey.X, pubKey.Y)
            childX, childY = Curve.add(x, y, pubKey.x, pubKey.y)
            # pk := secp256k1.PublicKey{Curve: secp256k1.S256(), X: childX, Y: childY}
            # childKey = pk.SerializeCompressed()
            childKey = PublicKey(Curve, childX, childY).serializeCompressed()

        # The fingerprint of the parent for the derived child is the first 4
        # bytes of the RIPEMD160(BLAKE256(parentPubKey)).
        parentFP = hash160(self.pubKey.b)[:4]

        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=childKey,
            pubKey="",
            chainCode=childChainCode,
            parentFP=parentFP,
            depth=self.depth + 1,
            childNum=i,
            isPrivate=isPrivate,
        )
Example #28
0
 def acctPrivateKey(self, acct, net, pw):
     userSecret = crypto.SecretKey.rekey(pw, self.privParams)
     cryptKeyPriv = ByteArray(
         userSecret.decrypt(self.cryptoKeyPrivEnc.bytes()))
     account = self.accounts[acct]
     return account.privateExtendedKey(cryptKeyPriv)