This is a program that generates a random hex value.
# How this works:
# uses random to get a random number
# checks what hash method to use
# uses that method to hash the random number
# returns the value
import hashlib
import random as rand
def hexrandom(minint, maxint, shamode="sha1"):
x = str(rand.randint(int(minint), int(maxint)))
reval = None
if shamode == "sha1":
reval = hashlib.sha1(x.encode()).hexdigest()
elif shamode == "sha224":
reval = hashlib.sha224(x.encode()).hexdigest()
elif shamode == "sha256":
reval = hashlib.sha256(x.encode()).hexdigest()
elif shamode == "sha384":
reval = hashlib.sha384(x.encode()).hexdigest()
elif shamode == "sha512":
reval = hashlib.sha512(x.encode()).hexdigest()
return reval
How it works is simple.
It creates a random number.
It then hashes that number.
Then, it .hexdigest()
s that.
And then returns that.
By the way, I tried being descriptive with explaining it, but it kept saying that there was unformated code. Sorry about that. The big description will be in a .md
file on the repository. MarkDown file here
Development of the module this will be here.
2 Answers 2
Anytime you have a stacked if
with basically all of the same comparisions, you should consider another method.
Use a dict:
The most common way to do this sort of thing is a dict
. The dict makes it easy to map a string to a method like:
sha_methods = dict(
sha1=hashlib.sha1,
sha224=hashlib.sha224,
sha256=hashlib.sha256,
sha384=hashlib.sha384,
sha512=hashlib.sha512,
)
def hexrandom(minint, maxint, shamode="sha1"):
if shamode not in sha_mthods:
raise ValueError("Unknown hashlib method {}".format(shamode))
x = str(rand.randint(int(minint), int(maxint)))
return sha_methods[shamode](x.encode()).hexdigest()
Use a direct method lookup:
In this case, since the shamode
string matches the method name hashlib, we can use getattr
to directly lookup this method like:
def hexrandom(minint, maxint, shamode="sha1"):
method = getattr(hashlib, shamode, None)
if method is None:
raise ValueError("Unknown hashlib method {}".format(shamode))
x = str(rand.randint(int(minint), int(maxint)))
return method(x.encode()).hexdigest()
-
\$\begingroup\$ Thanks for the advice. Any other advice? \$\endgroup\$Person– Person2018年01月24日 04:37:17 +00:00Commented Jan 24, 2018 at 4:37
-
\$\begingroup\$ @Person Add docstring to the function :) \$\endgroup\$hjpotter92– hjpotter922018年01月24日 05:05:18 +00:00Commented Jan 24, 2018 at 5:05
-
1\$\begingroup\$ Ah, yes. READIBILITY!. Also I think I followed PEP-8 \$\endgroup\$Person– Person2018年01月24日 05:09:10 +00:00Commented Jan 24, 2018 at 5:09
-
2\$\begingroup\$ @Person depending on your needs - it's not obvious why you need to use a hashing algorithm as you don't appear to care that much about being able to reproduce things - since you appear to just be hashing a random integer - you may just want to do something like:
binascii.hexlify(os.urandom(16))
and make a string from random bytes to start with... \$\endgroup\$Jon Clements– Jon Clements2018年01月24日 11:12:56 +00:00Commented Jan 24, 2018 at 11:12
There are a few strange things about your code. You are using SHA like a KDF, which is a function that generates randomness from a limited source of entropy. The strange thing is, you have a (virtually) unlimited source of randomness in either random.getrandombits()
or os.urandom()
. And you are asking for a random hex in a way that to me distinguishes it from random bytes. So let's clear up a few points:
- Random bytes are random bytes. Hex is a different way of representing bytes (you can use hex to represent any string of bytes). What you are looking to do is generate random bytes (in your code, that number depends on which SHA you use--SHA1 for example is 20 bytes) and then encode that in hex (20 bytes is 40 hex digits)
- I hope you are not using your code for cryptographic purposes.
random
is not cryptographically secure! And even if you were using a cryptographically secure source (such asos.urandom
), using a hex-encoded hash of a cryptographic source of randomness is bad (every byte in your hex is limited to one of0123456789abcedf
, severely limiting the space)! - Instead of trying to stretch the randomness from a small source, you should instead just be getting random bytes of your desired length and then encoding them in hex (if needed)
If you're on Python 3.6, you can use secrets.token_bytes
to get cryptographically secure random bytes or secrets.token_hex
to get hex-encoded cryptographically secure random bytes. If you aren't on Python 3.6, you can achieve what secrets
does with the following:
import binascii
import os
def token_bytes(nbytes):
"""Return a random byte string containing *nbytes* bytes.
If *nbytes* is ``None`` or not supplied, a reasonable
default is used.
>>> token_bytes(16) #doctest:+SKIP
b'\\xebr\\x17D*t\\xae\\xd4\\xe3S\\xb6\\xe2\\xebP1\\x8b'
"""
return os.urandom(nbytes)
def token_hex(nbytes):
"""Return a random text string, in hexadecimal.
The string has *nbytes* random bytes, each byte converted to two
hex digits. If *nbytes* is ``None`` or not supplied, a reasonable
default is used.
>>> token_hex(16) #doctest:+SKIP
'f9bf78b9a18ce6d46a0cd2b0b86df9da'
"""
return binascii.hexlify(token_bytes(nbytes)).decode('ascii')
This is the exact code from the secrets
module, so I'd contend it's python-endorsed way to generate the randomness you're looking for.
randint
internally gets 4 bytes to make an integer out of. Why not just get however many bytes you need (stackoverflow.com/questions/5495492/…) and then encode as hex if necessary? Additionally Python 3.6 hassecrets.token_hex
: docs.python.org/3/library/secrets.html#secrets.token_hex \$\endgroup\$if
chain has already been given, but the much more helpful advice to use a different algorithm has not. \$\endgroup\$