Although the random module in Python is very practical, for cryptographic purposes the recommendation is to use the os.urandom()
call, which has more randomness guaranteed by the operating system.
On the other hand, it is even more convenient because os.urandom
already accepts a parameter with the number of random bytes and returns an object of type bytes - in this case, for 4096 bits, you use os.urandom(512)
- in general for whatever use you are going to make the object bytes will be more practical than an integer with 4096 bits.
However, the language supports integers of this size - so if you want the numeric value you can do: int.from_bytes(os.urandom(512), "big")
. If you prefer as a text string with 2 hexadecimal digits per byte, you can do os.urandom(512).hex()
(convertible back to bytes with bytes.fromhex(minha_string)
).
Now let's assume you have another function of your choice that generates random, fixed-length 32-bit numbers (for example, a call to a hardware device driver) - in which case you you can use the Python "struct" module to join 16 calls to your function in a continuous bytes object with 512 bytes (and can then be converted to an integer, or to a hexadecimal string as above):
struct.pack("=16I", *(random.randrange(0, 2**32) for _ in range(16)))
(In case I used random.randrange as an example, but the recommendation is to actually use os.urandom, as I explained above). Understanding this expression:
is a call to the struct.pack
function where the first parameter describes the next: plus 16 32-bit integers that will be interpreted using the native endiannes of the machine - little endian in x86_64 - indicated by the " ". Then a generator expression that is deployed by the *
operator - each generated element is passed to the function as a positional parameter. The expression in turn simply repeats 16 times, indicated in for _ in range(16)
to call the desired function.