Private Key Space:
Here are some code examples, based on the elliptic curve secp256k1 used by ethereum, as others have noted in order for the 256-bit key to be valid, it must be smaller than the curve's parameter n
which is also a 256-bit value which can be written in hexadecimal format as: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
Error-checking:
Various libraries will produce errors if you try to feed a private key into them that is greater than n
, as an error-checking mechanism (i.e. Exception: Invalid privkey
) See this related answer with examples for greater detail.
Related curve parameters:
We can call the private key s
to denote it as a secret exponent, as this value wraps around the curve using the parameter g
(using scalar multiplication) which denotes a public generator point which is like a universal constant that everyone knows and uses, in order to generate their public key from s
.
So g
stays public, but s
must be kept secret for the ethereum wallet to remain secure, after deriving your ethereum address from your public key.
The public key may be represented either in compressed format totaling 33 bytes in length, or uncompressed as 64 bytes, and usually is denoted by a leading prefix 0x02 for compressed public keys, but the length of the string is a better indicator as the prefix is not also visible or present depending on the step and implementation.
Cryptographically-secure key derivation:
The way that s
is selected also matters immensely in terms of its cryptographic security. In other words, it is not advisable to choose this secret exponent yourself or come up with any sort of clever method as you might for a password (aka brain wallet) as countless such methods have been used for decades to crack secrets using various algorithms and computer software, such as those used to crack passwords.
Therefore, the secret exponent should be generated using a cryptographically-secure pseudo-random number generator (CSPRNG) such as the WorldWideWeb Consortium (W3C) Cryptography API (disclosure: I am one of 12 contributors to that spec on Github), so that there is far less likely a chance that an attacker could predict that value, as the random bits that make up that number are sourced from various places from your local device, and from processes that don't transmit that entropy data online (assuming the software you are using is safe along with a safe CSPRNG).
Example Python code:
Using Python 3, there is a CSPRNG in the secrets library which can be as easy as running the following commands in order from the IDLE interpreter or a .py file after importing the secrets library:
secrets.randbits(256)
The above command will produce a 256-bit binary number which can be used as a private key if it is less than the value of n
, but it will need to be formatted as a bytes object in the Python implementation example below using the eth-keys
library from the Ethereum Foundation Github repository (The example below may require installing the sha3
library (pip install pysha3) which contains Keccak, if not present in the default hashlib
library):
import secrets
import sha3
import eth_keys
from eth_keys import keys
private_key = str(hex(secrets.randbits(256))[2:])
private_key_bytes = bytes.fromhex(private_key)
public_key_hex = keys.PrivateKey(private_key_bytes).public_key
public_key_bytes = bytes.fromhex(str(public_key_hex)[2:])
Keccak256_of_public_key_bytes = sha3.keccak_256(public_key_bytes).hexdigest()
public_address = keys.PublicKey(public_key_bytes).to_address()
print('\n Private_key:',private_key,
'\n Private_key_bytes:',private_key_bytes,
'\n Public_key_hex:',public_key_hex,
'\n Public_key_bytes:',public_key_bytes,
'\n Full_Keccak_digest:',Keccak256_of_public_key_bytes,
'\n Ethereum address:',public_address)
Example output of above code (not to be used on main-net, just for example)
Private_key: 7231bfb75a41481965e391fb6d4406b6c356d20194c5a88935151f05136d2f2e
Private_key_bytes: b'r1\xbf\xb7ZAH\x19e\xe3\x91\xfbmD\x06\xb6\xc3V\xd2\x01\x94\xc5\xa8\x895\x15\x1f\x05\x13m/.'
Public_key_hex: 0x013e81c4a44c5303b11452f649be9427b75605339d8eba90f8e99cc401a8bd4f7494e0d0740bcc0282af75f9bd4571ed493a05ed02f1b968a45a46f4d77be149
Public_key_bytes: b"\x01>\x81\xc4\xa4LS\x03\xb1\x14R\xf6I\xbe\x94'\xb7V\x053\x9d\x8e\xba\x90\xf8\xe9\x9c\xc4\x01\xa8\xbdOt\x94\xe0\xd0t\x0b\xcc\x02\x82\xafu\xf9\xbdEq\xedI:\x05\xed\x02\xf1\xb9h\xa4ZF\xf4\xd7{\xe1I"
Full_Keccak_digest: 3f54dd68163875b594cfdc8e8a2250aafb31638b19a83caa49d1ee61089dcb4b
Ethereum address: 0x8a2250aafb31638b19a83caa49d1ee61089dcb4b
Six Steps from Private Key to Ethereum Address
As can be seen in the above implementation I wrote, the six steps to go from private key to ethereum address can be summarized as follows:
- Generate a 256-bit secure number formated as hex converted to a string with the 0x prefix discarded.
- Convert hex string generated in step 1 into a bytes (b"") object.
- Calculate the public key as hex using the private key bytes object created in step 2.
- Convert the hex public key generated in step 3 into a bytes object.
- Compute the hash digest of the bytes object created in step 4 using Keccak_256.
- Take the right-most/last 40 hex characters (trailing 160bits on little-endian side) of the hash digest created in step 5, which becomes the derived ethereum address.
Alternative dependencies:
In addition to the open-ssl library referenced in the article that @tayvano noted, other libraries that can be used to calculate elliptic curve public addresses include the ecdsa Python library, and Bitcoin's secp256k1 library written in C although the latter will contain tools for formatting bitcoin addresses which are totally different than ethereum addresses due to the formatting steps and different hash algorithms and encoding methods, even if the underlying private key and public key are the same, as an example.
Note: Finally, it's important to have tests in place to make sure that an address generated is not only valid, but that the underlying private key used in the process will be valid to sign transactions (i.e. if a user creates a hash digest of the byte array treated as a string instead of a bytes object that will lead to an incorrect hash digest and thus wrong address for the underlying key).
Example: One such address verification (checksum) tool from the eth-keys library is the following command: keys.PublicKey().to_checksum_address()
which uses the bytes of the public key (i.e. passing the variable public_key_bytes
into the first parenthesis would look like this in the above program keys.PublicKey(public_key_bytes).to_checksum_address()
to be sure the computed address is correct). This is why using existing libraries may be safer, than writing the code from scratch.
P.S. Answers and examples are not meant to be exhaustive of all risks/steps.