Generating Secure Crypto Wallets and Accounts with Python
By: Sam Abbassi
Tags: Python, Developer Tools, Cryptocurrency, Developer, Crypto

In this post we will be going over Algorand wallet security and wallet and account interaction. Specifically we will be looking at the steps and mechanisms involved in:

Generating a wallet

Generating accounts in the wallet

Generating accounts outside of the wallet and importing them into the wallet

Backing up a wallet

Backing up a specific account that is within a wallet

Recovering a wallet

Recovering accounts that were generated within a wallet

Importing multi-signature accounts into a wallet

Please make sure you have an Algorand TestNet node running and make sure that you start the kmd process.

goal kmd start -d [DATA DIR]

This can be a standard node through the binaries on github or it can be a dockerized container bootstrapping from an S3 instance.

Also, please make sure you have the Python SDK installed.

Foundational Concepts

Developers (and crypto users in general) typically think of crypto addresses as some identifier of fixed or arbitrary length that has a corresponding secret key that you use to trigger spending functions from an address. Developers (and the general crypto audience, again) are also likely familiar with the concept of mnemonic phrases that are used as a fail-safe to recover an address (or more specifically, to recover access to funds that are associated with an address) if access to a crypto address is restricted, lost or compromised. For an in-depth security tutorial please read these great series of posts by Sharon Halevi.

What is an Address / Account?

Algorand addresses are unique identifiers for public keys. This applies to single pubkeys where there is only one pubkey involved, as well to multisignature addresses where there exists a series of pubkeys. These pubkeys and their corresponding private keys use Ed25519 high-speed, high-security elliptic-curve signatures- which is the EdDSA signature scheme that using SHA-512 and Curve25519. There is no intention to take a deep dive into the world of cryptography with this post, but just so there is point of relation- Bitcoin uses ECDSA signature scheme.

Ed25519 is interesting because unlike ECDSA signatures, this scheme uses something called foolproof session keys which are signatures that are generated deterministically. This accomplishes pseudorandomness by hashing a long-term secret key together with the input message. The benefit here is that it circumvents the risk of leaking private keys due to a faulty random number generator, which is an inherent risk in the ECDSA signature scheme.

An address wraps an Ed25519 public key, which is a 32-byte array and the string representation is computed by appending a 4-byte checksum (computed by taking the last 4 bytes of a SHA512/256 digest of the public key) to the public key, giving us a 36-byte array.

Algorand addresses are often [erroneously] referred to as public keys. Keep this in mind if you are a developer because you do need to be aware of this distinction.

The term account and address seem to be used interchangeably as there is only so much you can do with enforcing vocabulary, but an Algorand account is technically an address on the blockchain that has specific onchain data associated with it. Most basically, this is a minimum balance (of 0.1 Algos (100,000 microalgos)) and other more intricate properties like whether or not the account is online and/or has earned rewards. Accounts can be generated offline and the network will not know of their existence until they have a minimum balance.

This is a code snippet for generating a standalone account offline.

What is a Wallet?

Wallets on the Algorand network are generated using a process called key management daemon (kmd). Wallets store a collection of accounts. The kmd process stores a collection of wallets and manages the interaction of the accounts within the wallets. Accounts/Addresses can be generated inside of the wallet and non-kmd generated accounts can be imported into a kmd wallet. In the previous sentence I said “accounts/addresses” because the addresses don’t need to have a minimum balance (the network does not need to know about them) to be managed or housed inside of a kmd wallet.

The caveat with this functionality is that if a kmd wallet is restored (instantiated on another device), those non-kmd generated accounts need to be imported again and not simply generated.

This code snippet demonstrates the methods necessary to create a wallet and generate an account within that wallet. You will need some accounts in the your kmd wallet to interact with and run the rest of the code snippets, so comment out the delete method after running this code a few times to have an account in your kmd wallet.

To restore a kmd wallet you need the master derivation key (mdk) of the wallet and subsequently, you need the mnemonic that can be extracted from the mdk.

Once the wallet mnemonic is retrieved, you can use the mnemonic.to_master_derivation_key() method to extract the mdk and run create_wallet() using kmd to instantiate your wallet. The reason why the code is showing you how to extract the mnemonic from the mdk and then extract the mdk back from the mnemonic is that for security and practical purposes, the mnemonic is what users of wallets utilize as a backup.

Now let’s take a look at how to import a standalone account into a kmd wallet. In the following code snippet, we loop through all the wallets that are part of your kmd instance using list_wallets() to expose the wallets metadata and select whichever wallet you are looking for by changing the string it’s being matched against.

if arrayitem.get("name") == "wallet1":

walletid = arrayitem.get("id")

break

We, again, run account.generate_account() as we did in the first code snippet and import the account by passing in the private_key and wallethandle into the import_key method.

One of the most important wallet parameters you will need to work with kmd is the wallethandle . So once you loop through your wallets and find the kmd wallet you are interested in interacting with, part of the metadata of the wallet includes the walletid . This walletid is what needs to be passed into init_wallet_handle() to extract the kmd wallethandle :

walletid = None

wallets = kcl.list_wallets()



for arrayitem in wallets:

if arrayitem.get("name") == "wallet1":

walletid = arrayitem.get("id")

break

print("Wallet ID:", walletid)



wallethandle = kcl.init_wallet_handle(walletid, "testpassword")

print("Wallet Handle:", wallethandle)

This wallet handle is then passed into methods like import_key() for importing stand-alone accounts:

importedaccount = kcl.import_key(wallethandle, private_key)

or generate_key() to generate a kmd-based account

address = kcl.generate_key(wallethandle)

as well as importing multsignature accounts with import_multisig()

imported_multisig = kcl.import_multisig(wallethandle, msig)

and finally, one that we’ve seen a few times now list_keys() to list out the accounts that are a part of the kmd wallet

accounts = kcl.list_keys(wallethandle)

This code snippets demonstrates the methods needed to find a wallet, init the wallet to get the wallethandle using the walletid, generate accounts, create a multisig (msig) object and importing the msig into a kmd wallet using the wallethandle.

The final piece of code I wanted to demonstrate before we sign off is backing up a kmd-account. And that is done by 1) initing the wallet to extract the wallethandle 2) exporting the private_key from the account using export_key() and 3) deriving the mnemonic back up phrase from the private_key using from_private_key() .

All of these code samples can be found on github and with these examples you should have a solid foundation to build on with kmd.