import codecs
import random
private_key = random.randrange(1,115792089237316195423570985008687907853269984665640564039457584007913129639936)
private_key = hex(private_key)[2:]
private_key_bytes = codecs.decode(private_key, 'hex')
I'm assuming 2256 is the upper bound for possible Bitcoin private key. Right? Even though when I actually manually go past that upper limit it still works. But that's unrelated to my issue.
This is a small piece of the code. About 90% of the time it works and I'm able to proceed with the final code, but then at times it gives me the error "binascii.Error: Odd-length string". What's causing this and how do I fix it?
CodePudding user response:
The private key can be an odd-length string, as hex()
will exclude leading zeros from its output (if they exist).
To resolve, pad the private key with enough zeros to make it 64 characters.
import codecs
import random
private_key = random.randrange(1, 2**256)
private_key = '0' * (len(hex(private_key)) % 2) hex(private_key)[2:]
private_key_bytes = codecs.decode(private_key, 'hex')
CodePudding user response:
It is because hex()
does not include leading zeros.
Either way, you shouldn't use a PRNG for a private key, please use Python's secrets module:
>>> import secrets
>>> secrets.token_hex(256)
'55e20c2f9e53a9629db4bb6fa5bc5c6874300993a2358823bd30610674eb3e6763e51c1d778b1982285806c53a205744e07ec5cf92ce5204b75f91cb3b59011ed664cdcee5498d52c219efee3b1df7b44800d8537c0a440c0562bac8dd49aed767b3df9091520fb7e3bd8fa3c1fa866d1b8693339c6cf80e3318967bf0f28a6dbd8faf90d1621104c5435fbbd991f7b88497860166a216127cd619c746340b40f8f920276c5ae23c5e637e44ffb30f74896a004325b7f0b6b499bb69d928443949d1683c6e0f3d0621c1782a54bd0d0f314fae4af1ca93578c2660827b44dfb68c8dc0ca685a62eb27e8ab66d441babda2c24efda75850d52e22468bb7b4f069'
From random:
The pseudo-random generators of this module should not be used for security purposes. For security or cryptographic uses, see the secrets module.
CodePudding user response:
Many integers will produce an odd number of hex bytes. For a smaller example, consider generating numbers in range(256)
. If you randomly generate 16-255 (0x10
to 0xff
) you'll trim it to two digits, corresponding to one real byte of data. But if you generate 15, hex
will return '0xf'
, and 'f'
that can't decode unambiguously to a single byte (did you want b'\x0f'
or b'\xf0'
?).
Really, if you want to generate the bytes of a private key, the easy solution is to just generate the bytes directly, and use a secure means of generating the bytes (Python's PRNG is not suitable for cryptographically secure random numbers):
import os
private_key_bytes = os.urandom(32) # Directly pulls 32 bytes/256 bits of cryptographically secure
# random bytes from OS source
private_key = int.from_bytes(private_key_bytes, 'big') # If needed
Or equivalently using the random
module as a wrapper for os.urandom
:
import random
private_key_bytes = random.SystemRandom().randbytes(32) # SystemRandom directly wraps os.urandom
# Conversion to int unchanged
The secrets
module provides similar wrapper functionality with cryptographically secure random number generation and while it was only introduced in 3.6, you should be able to rely on that in most contexts, making for a third option:
import secrets
private_key_bytes = secrets.token_bytes(32)