0

I'm implementing a function in order to replace os.urandom(n). The code below is what I have so far:

def random_entropy(n):
    """
    Returns a random entropy, which is a sequence of numbers between 0, 255

    :n: a integer number
    """
    if n <= 0:
        return ""
    buffer = []
    for i in range(0, n):
        number = random.randint(0, 255)
        buffer.append(hex(number))
    
    return ''.join(buffer)

if I go to the terminal, I get the following output:

>>> import os
>>> os.urandom(10)
'm\xd4\x94\x00x7\xbe\x04\xa2R'
>>> type(os.urandom(10))
<type 'str'>
>>> map(ord, os.urandom(10))
[65, 120, 218, 135, 66, 134, 141, 140, 178, 25]

in the end, I want my function to return a similar output as os.urandom: 'm\xd4\x94\x00x7\xbe\x04\xa2R' (I mean, the binary output). How can I achieve that? I'm implementing this for an ecdsa module for SGX, so I can't use the os or secrets packages.

From the Python documentation:

Return a string of size random bytes suitable for cryptographic use.

cybertextron
  • 10,547
  • 28
  • 104
  • 208
  • Does this answer your question? [What does secrets module do to make perfect random sequences in Python](https://stackoverflow.com/questions/65311182/what-does-secrets-module-do-to-make-perfect-random-sequences-in-python) – Peter O. May 06 '21 at 15:14
  • You're using Python 2 aren't you? – Mark Ransom May 06 '21 at 15:24
  • @MarkRansom I'm... I will update the question – cybertextron May 06 '21 at 15:26
  • You may want to read [Whats the difference between os.urandom() and random?](https://stackoverflow.com/q/47514695/5987) - you can't substitute `random` for `urandom`. – Mark Ransom May 06 '21 at 15:28
  • @MarkRansom I'm implementing a SGX module for ecdsa, but it relies on `os.urandom` for entropy. However, we can't use `os` in SGX, that's why I have to implement this module. Could you please help me with that? – cybertextron May 06 '21 at 15:29
  • @PeterO. it does not ... `secrets` is not a library in SGX, so it can't be used. – cybertextron May 06 '21 at 15:29
  • 1
    I highlighted that question because its answer shows how cryptographic generators generally work: they ultimately rely on nondeterministic sources which are hard to guess. Another relevant question is: https://stackoverflow.com/questions/62375129/how-to-get-truly-random-data-not-random-data-fed-into-a-prng-seed-like-csrngs And in any case, the `random` module (except for `random.SystemRandom`, which may or may not work for you) is not a cryptographic generator. – Peter O. May 06 '21 at 15:33

1 Answers1

2

First, before you embark on this you should know that random is not an adequate substitute for urandom in many applications. Read the links given in the question comments. You're abusing the word "entropy" in the name of your function.

By using hex on each of your numbers, you're converting it to a readable multi-character string representation. You don't want that, you want chr instead.

Here's a quick fix of your code for Python 2:

def random_entropy(n):
    """
    Returns a random entropy, which is a sequence of numbers between 0, 255

    :n: a integer number
    """
    if n <= 0:
        return ""
    buffer = []
    for i in range(0, n):
        number = random.randint(0, 255)
        buffer.append(chr(number))
    
    return ''.join(buffer)

There's no need to generate a list first, you can put a loop directly into the join and simplify things greatly:

def random_entropy(n):
    """
    Returns a random entropy, which is a sequence of numbers between 0, 255

    :n: a integer number
    """
    return ''.join(chr(random.randint(0, 255)) for _ in range(n))

Python 3 does things a little differently. Here's the equivalent:

def random_entropy(n):
    """
    Returns a random entropy, which is a sequence of numbers between 0, 255

    :n: a integer number
    """
    return b''.join(random.randint(0, 255).to_bytes(1, 'little') for _ in range(n))

Edit: finally in Python 3.9 there is a function in random that delivers exactly what you ask: random.randbytes. Note the warning given there:

This method should not be used for generating security tokens. Use secrets.token_bytes() instead.

def random_bytes(n):
    """
    Returns a random byte string, which is a sequence of numbers between 0, 255.
    This should not be used in any application requiring cryptographic security.

    :n: a integer number for the number of bytes requested.
    """
    return random.randbytes(n)
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622