6
votes

For a given private key generated by MyEtherWallet, I would like to see I have the tools to independently arrive at similar results.

Wrote a wrapper around Keccak to accept both hexadecimal and ASCII inputs to be sha3-224 or sha3-256 or sha3-384 or sha3-512 hashed. Hashes are consistent with Test Vectors. Would like to apply it with bitcoin-explorer (bx) version3 commands to see if I can synthesize ETH addresses. If successful at synthesizing addresses for ETH, XMR, and MAX altcoins, I go through the effort of submitting a pull request to add sha3 hashing capabilities to bx version3, and update this Wiki concerning the application of bx to altcoins.

5
  • To calculate a public address using bx's secp256k1 capabilities is simply: % echo fc8fba997174132184998ab82b28e441e80c73236ccaf8b6a1efadc33febecfc | bx ec-to-public -u 04cf90dc2b34937fff7cf1eb4b260f1e5610231134b864761e508505938bbef8fc00aeb265797336b74146e018da7b78070f1f1072540a2c3fe83637ca5d605b3c
    – skaht
    Commented Dec 30, 2015 at 3:38
  • Issue I'm having is with Keccak sha3-256. Dropping the 04 from the address above into a sha3-256 and taking the lower 20 bytes to the right isn't working.
    – skaht
    Commented Dec 30, 2015 at 3:48
  • % ./keccak -x -256 cf90dc2b34937fff7cf1eb4b260f1e5610231134b864761e508505938bbef8fc00aeb265797336b74146e018da7b78070f1f1072540a2c3fe83637ca5d605b3c b992c6ced76bf40cfe875feb99d0dfbbb6eb831dc0605a82d084e5d930bc6631
    – skaht
    Commented Dec 30, 2015 at 3:52
  • How is it guaranteed that addresses are unique within the network then? What if I happen to have generated the same private key as someone else, then I will have the same address? Or, a more likely scenario, what if my private key is different to someone else, but it maps to the same address?
    – Graham P
    Commented May 26, 2016 at 7:14
  • Q1) There is no guarantee that two different private keys can't have the same public ETH public address, just very improbable. Q2) If there is a collision between the private keys, s/he that spends first gets to reap booty at the loss of another. More likely to loose your funds at a bank for whatever reason...
    – skaht
    Commented Jun 24, 2016 at 0:27

3 Answers 3

6
votes
  1. Start with the public key bytes (a bytestring of length 64)
  2. Of that public key, take the Keccak-256 hash used ubiquitously by Ethereum (make sure you get that right, as the ultimately standardized SHA3-256 hash differs). You should now have a bytestring of length 32.
  3. Drop the first 12 bytes. You should now have a bytestring of length 20, the Ethereum address associated with your public key.

Update: Oops! I see you want it from the private key, not the public key. That's harder. You have to first derive the public key from the private key, which is best with the help of an EC crypto library. I can show you some example code in Scala, but the EC math is mostly a black box to me. First, interpret the 256 bit private key as an unsigned big integer. Then, see e.g. here. Curve represents the named eliptic curve secp256k1. The details of the math are, alas, beyond me, but hopefully in whatever environment you are coding you have access to a high quality crypto library.

Update 2: In the comment thread, I speculated about where Ethereum's hash function diverged from the SHA3 standard. My speculation was mistaken, the version I thought was the incompatible change is the version Ethereum in fact uses. Thanks to work by Eric McCarthy, who chased this down in great detail. Please see this comment below for more details.

11
  • (sorry! my initial response misread the post as starting from the public key. i've added an update with imperfect hints on deriving the public from the private key.) Commented Dec 29, 2015 at 5:47
  • I'm very confused, per ETHEREUM: A SECURE DECENTRALISED GENERALISED TRANSACTION LEDGER I may format as typewriter text, e.g. the Keccak-256 hash function (as per the winning entry to the SHA-3 contest) is denoted KEC (and generally referred to as plain Keccak). FIPS202_SHA3_256 amounts to being Keccak(1088, 512, input, inputByteLen, 0x06, output, 32). What are the Keccak parameters for Keccak-256?
    – skaht
    Commented Dec 29, 2015 at 22:52
  • Request for comment / opinions barely mentions sha3.
    – skaht
    Commented Dec 29, 2015 at 23:02
  • Per Instead of addresses being the RIPEMD160 hash of the SHA256 hash of the public key prefixed with 04, addresses are simply the last 20 bytes of the SHA3 hash of the public key.
    – skaht
    Commented Dec 29, 2015 at 23:37
  • So, it's ugly. But the padding strategy of Keccak changed as the function was stewarded through the SHA3 standardization process. The relevant change I think happens at version 3.0 here, see especially padding.h and padding.cpp. Commented Dec 30, 2015 at 9:25
2
votes

For computing ETH addresses, the last 20 bytes of a Keccak-256 hash are used. Must Not use the NIST FIPS 202 flavors of Keccak such as KeccakCodePackage.

However, a hack to main.c and genKATShortMsg.cpp from KeccakTools will yield proper ETH addresses, detailed in a comment above.

For the following ETH private key: b205a1e03ddf50247d8483435cd91f9c732bad281ad420061ab4310c33166276. The associated public key results from using bx.

% echo b205a1e03ddf50247d8483435cd91f9c732bad281ad420061ab4310c33166276 | bx ec-to-public -u
04**6cb84859e85b1d9a27e060fdede38bb818c93850fb6e42d9c7e4bd879f8b9153fd94ed48e1f63312dce58f4d778ff45a2e5abb08a39c1bc0241139f5e54de7df**

The following KeccakTools/Example\ trails configuration file is needed to to execute
../bin/KeccakTools from the Example\ trails directory.

% cat ShortMsgKAT.txt

\# Algorithm Name: NOT_FIPS202_SHA3_256
\# Principal Submitter: SKAHT
Len = 512
Msg = 6cb84859e85b1d9a27e060fdede38bb818c93850fb6e42d9c7e4bd879f8b9153fd94ed48e1f63312dce58f4d778ff45a2e5abb08a39c1bc0241139f5e54de7df

A file called ShortMsgKAT_keccak-256.txt is created from executing the KeccakTools that will contain:

MD = 787EC5A5313A976F7BDF9EED**AFDEFC1937AE294C3BD55386A8B9775539D81653** 

when the hack documented above is performed to build KeccakTools.

1
  • The skinny for calculating Ethereum addresses is also found at this location. Deduced that Ethereum does not natively support compressed private keys.
    – skaht
    Commented Jun 24, 2016 at 0:31
0
votes

Answering the main headline question about 256-bit private keys, and header:

"For a given private key generated by MyEtherWallet, I would like to see I have the tools to independently arrive at similar results."

Firstly, it's important to check the private key size (whether generated randomly or deterministically) to make sure it is valid in terms of its use within curve secp256k1 curve (beyond the length in bits of the key), before generating the public key and address (although many applications such as the one below will check for you).

Note: This applies to Bitcoin as well, not just ethereum, as they use the same curves, despite the address formatting being different due to the different steps and different hashing algorithms and encodings used (and although the same private key can be used across both, that is a different discussion).

While the curve itself is equal to the finite field p equal to: ((((((((2**256)-2**32)-2**9)-2**8)-2**7)-2**6)-2**4)-1) and which in hexadecimal is: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' and 256 binary bits long, the private key cannot be larger than the order n which is slighlty smaller even though it is also 256-bits: '0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'

That is, the private key cannot be larger than n as per the secp256k1 curve parameters.

After making that check to be sure the key is valid, the next steps would be to use something like the ECDSA library along with eth-keys library in Python.

Here below is an example such program in Python that accepts a private key or can generate keys randomly (see comment in code after # for private_key_hex=hex(int(bin(secrets.randbits(256)),base=2))):

import secrets
import math
import sha3
import ecdsa
from ecdsa import SigningKey, SECP256k1
from eth_keys import keys 

private_key_hex=hex(int(input('paste a 0x-padded 64 character hex string, total 66 with 0x'),base=16))
scep256k1_curvelimit= int(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141) #Curve limit is 
if int(private_key_hex,base=16) > scep256k1_curvelimit:
    print('private key is out of the range of the curve and invalid, do not use!')
else:
    print('private key is within range of curve')

#private_key_hex=hex(int(bin(secrets.randbits(256)),base=2)) Uncomment this to use RANDOM
private_key_hex_nopad=private_key_hex[2:]
private_key=bytearray.fromhex(private_key_hex_nopad)

def address_formatted(to_hash_str): 
    keccak = sha3.keccak_256()
    out = ''
    str_hash = to_hash_str.lower().replace('0x', '')
    keccak.update(str_hash.encode('ascii'))
    create_hash_digit = keccak.hexdigest()

    for i, c in enumerate(str_hash):
        if int(create_hash_digit[i], 16) >= 8:
            out += c.upper()
        else:
            out += c
    return '0x' + out        

keccak = sha3.keccak_256()
f=private_key
private_key = ecdsa.SigningKey.from_string(f, curve=ecdsa.SECP256k1)
public_key = private_key.get_verifying_key().to_string()
keccak.update(public_key)
print(public_key)
address = keccak.hexdigest()[24:]
address_full = keccak.hexdigest()

print("Private key:", private_key.to_string().hex())
print("Public key: ", public_key.hex())
print("Ethereum Address, based on last 40 hex of Keccak Hash digest:    ", address_formatted(address))
print('Full Hash Digest (address is last 40 characters): ',address_full,)

Note: the following code snippet from the above program is not necessary and I just added there for informational purposes, as the depended libraries have inherent checks to tell whether the private key (secret exponent) is less than n :

scep256k1_curvelimit= int(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141) #Curve limit is 
if int(private_key_hex,base=16) > scep256k1_curvelimit:
    print('private key is out of the range of the curve and invalid, do not use!')
else:
    print('private key is within range of curve')

The output of the above program using the cryptographically-secure randomly-generated 256-bit key would look something like this (don't use these values on main net):

b'\xa2%\xbfV_\xf4\xea\x03\x9b\xcc\xba>&En\x91\x0c\xd7NF\x16\xd6~\xe0\xa1f\xe2m\xa6\xe5\xe5Z\x08\xd0\xfa\x16Y\xb4\xb5G\xbaq9\xcaS\x1fb\x90{\x9c.r\xb8\x07\x12\xf1\xc8\x1e\xceC\xc3?K\x8b'
Private key: c2c72dfbff11dfb4e9d5b0a20c620c58b15bb7552753601f043db91331b0db15
Public key:  a225bf565ff4ea039bccba3e26456e910cd74e4616d67ee0a166e26da6e5e55a08d0fa1659b4b547ba7139ca531f62907b9c2e72b80712f1c81ece43c33f4b8b
Ethereum Address, based on last 40 hex of Keccak Hash digest:     0x6eA27154616a29708dce7650b475Dd6b82eBa6a3
Full Hash Digest (address is last 40 characters):  f616607efea8e1a5c2f0f8576ea27154616a29708dce7650b475dd6b82eba6a3

Note: Keccak can also be accessed as part of the Hashlib library or installed from the pysha3 library, depending on the Python version installed.

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