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.
% echo fc8fba997174132184998ab82b28e441e80c73236ccaf8b6a1efadc33febecfc | bx ec-to-public -u
04cf90dc2b34937fff7cf1eb4b260f1e5610231134b864761e508505938bbef8fc00aeb265797336b74146e018da7b78070f1f1072540a2c3fe83637ca5d605b3c% ./keccak -x -256 cf90dc2b34937fff7cf1eb4b260f1e5610231134b864761e508505938bbef8fc00aeb265797336b74146e018da7b78070f1f1072540a2c3fe83637ca5d605b3c
b992c6ced76bf40cfe875feb99d0dfbbb6eb831dc0605a82d084e5d930bc6631