159

What criteria does a valid Ethereum address need to fulfill, is it just a random number in hexadecimal?

Or does it need to be derived in a specific way, according to some cryptographic algorithms?

What algorithms and standards are used to generate the key-pair?

4
  • 4
    I don't agree about the duplicate. Question is not about verifying if the address is valid but rather how the address is built and if it follows a format or is just random. Indeed it's not random but the result of some processes. The fact that the word "valid" is in the question is not a criteria, you won't mark all questions with the "valid" word as duplicates ! May 3, 2016 at 15:38
  • 1
    @tayvano I'm not the one who asked this question May 4, 2016 at 5:55
  • 2
    It's totally fine that questions get edited/improved and voted for reopening. Here we go.
    – q9f
    May 4, 2016 at 11:51
  • Might be worth keeping the comments pointing to possible dupes as they have value in offering a different perspective to those of us doing the learning. Sep 16, 2021 at 19:53

6 Answers 6

190

Recently this article came to my attention that is way more in depth and technical than my more accessible version below. It also walks you through how to generate one on your own. I highly recommend it: https://kobl.one/blog/create-full-ethereum-keypair-and-address/


From the Yellow Paper

yellow paper

There are three main steps to get from private -> address:

  1. Create a random private key (64 (hex) characters / 256 bits / 32 bytes)

  2. Derive the public key from this private key (128 (hex) characters / 512 bits / 64 bytes)

  3. Derive the address from this public key. (40 (hex) characters / 160 bits / 20 bytes)

Even though a lot of people call the address the public key, it's actually not the case in Ethereum. There is a separate public key that acts as a middleman that you won't ever see, unless you go poking around a pre-sale wallet JSON file.

1. Generating private key

The private key is 64 hexadecimal characters. Every single string of 64 hex are, hypothetically, an Ethereum private key (see link at top for why this isn't totally accurate) that will access an account. If you plan on generating a new account, you should be sure these are seeded with a proper RNG. Once you have that string..

2. Private Key -> Public Key

This is hard and beyond me. There is something with Elliptic Curve Digital Signature Algorithm (ECDSA) and stuff. But in the end you end up with a public key that is 64 bytes.

3. Public key -> Address

  1. Start with the public key (128 characters / 64 bytes)

  2. Take the Keccak-256 hash of the public key. You should now have a string that is 64 characters / 32 bytes. (note: SHA3-256 eventually became the standard, but Ethereum uses Keccak)

  3. Take the last 40 characters / 20 bytes of this public key (Keccak-256). Or, in other words, drop the first 24 characters / 12 bytes. These 40 characters / 20 bytes are the address. When prefixed with 0x it becomes 42 characters long.

Definitions

Address: An Ethereum address represents an account. For EOA, the address is derived as the last 20 bytes of the public key controlling the account, e.g., `cd2a3d9f938e13cd947ec05abc7fe734df8dd826. This is a hexadecimal format (base 16 notation), which is often indicated explicitly by appending 0x to the address. Web3.js and console functions accept addresses with or without this prefix but for transparency we encourage their use. Since each byte of the address is represented by 2 hex characters, a prefixed address is 42 characters long. Several apps and APIs are also meant to implement the new checksum-enabled address scheme introduced in the Mist Ethereum wallet as of version 0.5.0. - Homestead Docs

Private Key: A randomly selected positive integer (represented as a byte array of length 32 in big-endian form) in the range [1, secp256k1n − 1]. - Yellow Paper

8
  • 5
    what's the logic behind using only the last 20 bytes of the hash?
    – k26dr
    Sep 6, 2017 at 22:33
  • @k26dr Every crypto seems to do something similar in order to keep addresses unique (so that you don't send Bitcoin to an ETH address, etc.) However, I don't know the specific reason. Would make for a great new question though. It's unlikely you will get a real answer here.
    – tayvano
    Sep 11, 2017 at 8:15
  • 4
    @k26dr it's just to keep the addresses as short as possible without taking the risk of having collisions.
    – Ely
    Oct 15, 2017 at 16:25
  • Good info - But didn't explain final (optional) checksum for capital letters like you did here. ethereum.stackexchange.com/a/2046/22785 This is also part of public addr generation.
    – B. Shea
    Feb 26, 2018 at 15:00
  • 1
    @MartinDawson The answer tells you how the keys are generated (and it's correct). After generating the private/public keys, you do need to store them somewhere securely. That's what the password-encrypted file is for. Jun 25, 2020 at 9:22
16

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:

  1. Generate a 256-bit secure number formated as hex converted to a string with the 0x prefix discarded.
  2. Convert hex string generated in step 1 into a bytes (b"") object.
  3. Calculate the public key as hex using the private key bytes object created in step 2.
  4. Convert the hex public key generated in step 3 into a bytes object.
  5. Compute the hash digest of the bytes object created in step 4 using Keccak_256.
  6. 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.

3
  • In ETH HD-Wallet generation (child key derivation from path m/44'/60/0/x) algorithm, should we use 'compressed' or 'uncompressed' public key?
    – Jemshit
    Apr 12, 2022 at 13:44
  • Looks like 'compressed' 33 byte is used
    – Jemshit
    Apr 13, 2022 at 7:04
  • Re. the comment about the secret key relative to the elliptic curve parameter n, see: eips.ethereum.org/EIPS/eip-1271 : "If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept these malleable signatures as well." Jun 10, 2022 at 1:44
11

Ethereum addresses are hashes of a public key. So to generate one you have to generate a private key first (see: What is the approach to calculate an Ethereum address from a 256 bit private key?) The private key is random but the public key and thus its hash used as the address is not random.

To check an address, and thus know the format, refer to How can I check if an Ethereum address is valid?

6
  • 2
    Okay! A few quick questions: 1) The private and public keys: what encryption algorithm is used? Is it the same as for bitcoin? (secp256k1 ECDSA i believe) 2) The address: It is a hash of the public key. What hashing function is used? SHA256?
    – max
    May 3, 2016 at 14:22
  • Yes and yes but with keccak256. But you should ask this in another question and mark this one as answered if you consider the initial question is answered. May 3, 2016 at 14:47
  • It was actually these questions in the comment that I wanted answered (how the address is generated, what algorithms etc). If you add it to the answer I'll mark it as answered! I will clarify my original question somewhat.
    – max
    May 3, 2016 at 17:19
  • ;) Clarify your question first. It's hard to answer clearly to questions that are not even asked. May 3, 2016 at 17:29
  • Link only questions are not encouraged.
    – niksmac
    May 5, 2016 at 4:22
7

If anyone is looking for a JS solution:

const Web3 = require('web3')
const web3 = new Web3()
const elliptic = require('elliptic')
const ec = new elliptic.ec('secp256k1')


function pubKeyToAddress(compressedPubkey) {
  let keyPair = ec.keyFromPublic(compressedPubkey, 'hex')
  // remove '04' then add prefix '0x'
  let pubkey = '0x' + keyPair.getPublic(false, 'hex').substr(2)
  let address = trimFirst12Bytes(web3.utils.keccak256(pubkey))
  return web3.utils.toChecksumAddress(address)
}
unction trimFirst12Bytes(hexString) {
  return '0x'.concat(hexString.substr(hexString.length - 40))
}

4

Here is a python package hosted in PyPI: eth_address_dump

Dump eth address from private key:

$ echo "0x6ee825aafad19a0d759e1e0ba61d0c523b7b23038998a92d7904458b91667105" | eth_address_dump
private_key = 0x6ee825aafad19a0d759e1e0ba61d0c523b7b23038998a92d7904458b91667105
public_key = 0xaa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9fe85fc162e43d721533736d79c102139d3035d2d9251ccf809bc5bddb81cc6563
compressed_public_key = 0x03aa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9f
address = 0xF7dcf60AebA077461862d51b77d6d804C06E0073

Dump eth address from mnemonic words:

$ echo "olympic wine chicken argue unaware bundle tunnel grid spider slot spell need" | eth_address_dump
mnemonic = olympic wine chicken argue unaware bundle tunnel grid spider slot spell need
private_key = 0x6ee825aafad19a0d759e1e0ba61d0c523b7b23038998a92d7904458b91667105
public_key = 0xaa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9fe85fc162e43d721533736d79c102139d3035d2d9251ccf809bc5bddb81cc6563
compressed_public_key = 0x03aa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9f
address = 0xF7dcf60AebA077461862d51b77d6d804C06E0073

Dump eth address from compressed public key:

$ echo "0x03aa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9f" | eth_address_dump
public_key = 0xaa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9fe85fc162e43d721533736d79c102139d3035d2d9251ccf809bc5bddb81cc6563
compressed_public_key = 0x03aa3e0b3f86053c2aaa08d6f6398e18f76100e0d675680228b000c252e4393e9f
address = 0xF7dcf60AebA077461862d51b77d6d804C06E0073
1
  • A random private key of 64 (hex) characters (256 bits / 32 bytes) is generated first.

  • A 128 (hex) character (512 bits / 64 bytes) public key is then derived from the generated private key using Elliptic Curve Digital Signature Algorithm (ECDSA).

  • The Keccak-256 hash function is then applied to the public key to obtain a 64 character (256 bits / 32 bytes) hash string. The last 40 characters of this string prefixed with 0x becomes the final Ethereum address. (Note: 0x in coding indicates that the number/string is written in hex.)

Source


Note: For a more detailed but simple mathematical explanation of these stepts please read this very useful article. If you want to dive deeper into the concept, read this book.


Simple Python code sample using eth_account module

from eth_account import Account

acc = Account.create(password_string)
prv = acc.key
pub = Account._parsePrivateKey(prv).public_key
addr = acc.address

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.