Home > Software design >  "binascii.Error: decoding with 'hex' codec failed (Error: Odd-length string)" er
"binascii.Error: decoding with 'hex' codec failed (Error: Odd-length string)" er

Time:03-30

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)
  • Related