Python and random keys 21 char max

I am using an api that takes the name 21 char max to represent an internal session, which lasts about two days. I would like the name to not make sense to use some kind of hacking? md5 generates 40 characters, is there anything else I could use?

Now I use 'userid [: 10]' + creation time: ddhhmmss + random 3 characters.

Thanks,

+7
python encryption key
source share
5 answers

If I read your question correctly, you want to create some kind of arbitrary identifier token, which should be 21 characters max. Do I need to be very resistant to guessing? The example you gave is not “critographically strong” in the sense that it can be guessed by looking for less than 1/2 of the total possible key space.

You do not say whether all characters can be 256 ASCII characters, or if they should be limited to, say, printable ASCII (33-127, inclusive) or a smaller range.

There is a Python module designed for UUIDs (universal unique identifiers). You probably want uuid4 to generate a random UUID and use OS support if available (on Linux, Mac, FreeBSD, and possibly others).

>>> import uuid >>> u = uuid.uuid4() >>> u UUID('d94303e7-1be4-49ef-92f2-472bc4b4286d') >>> u.bytes '\xd9C\x03\xe7\x1b\xe4I\xef\x92\xf2G+\xc4\xb4(m' >>> len(u.bytes) 16 >>> 

16 random bytes are very unobvious, and there is no need to use the full 21 bytes that your API allows if all you want to have an indescribable opaque identifier.

If you cannot use such raw bytes, this is probably a bad idea, because it’s harder to use debugging in logs and other messages, and it’s harder to compare by eye and then convert the bytes to something more readable, for example using base-64 encoding, with the result reduced to 21 (or any other) bytes:

 >>> u.bytes.encode("base64") '2UMD5xvkSe+S8kcrxLQobQ==\n' >>> len(u.bytes.encode("base64")) 25 >>> u.bytes.encode("base64")[:21] '2UMD5xvkSe+S8kcrxLQob' >>> 

This gives you an extremely high-quality random string of length 21.

You may not like the “+” or “/” character, which can be in the base-64 string, because without proper escaping, which can interfere with working with URLs. Since you already thought of using “random 3 characters”, I don’t think it bothers you. If so, you can replace these characters with something else ("-" and "." May work) or delete them, if any.

As others have pointed out, you can use .encode ("hex") and get the hexadecimal equivalent, but only 4 bits of randomness / character * 21 max characters gives you 84 bits of randomness, not twice. Each bit doubles your key space, making the theoretical search space much, much smaller. 2E24 less.

Your keyspace is still 2E24 in size, even with hexadecimal encoding, so I think this is a more theoretical problem. I would not worry about people making brutal attacks against your system.

Edit

PS: The uuid.uuid4 function uses libuuid, if available. This gets its entropy from os.urandom (if available) otherwise from the current time and the local ethernet MAC address. If libuuid is not available, the uuid.uuid4 function receives bytes directly from os.urandom (if available), otherwise it uses a random module. The random module uses an initial default value based on os.urandom (if available), otherwise a value based on the current time. Breakdown occurs for each function call, so if you do not have os.urandom, then the overhead is a little more than you might expect.

Take a home message? If you know that you have os.urandom, you can do

 os.urandom(16).encode("base64")[:21] 

but if you do not want to worry about its availability, use the uuid module.

+23
source share

The hexadecimal representation of MD5 has a very low randomness: you get only 4 bits of entropy per character.

Use random characters, for example:

 import random import string "".join([random.choice(string.ascii_letters + string.digits + ".-") for i in xrange(21)]) 

In the selection, put all valid characters.

When using a real hash function such as SHA1, you will get good results when used properly, the added complexity and CPU consumption seem not to be justified for your needs. You only need a random string.

+4
source share

Why not take the first 21 characters from the md5 or SHA1 hash?

+2
source share

The base64 module can use URL-safe encoding. Therefore, if necessary, instead of

 u.bytes.encode("base64") 

you could do

 import base64 token = base64.urlsafe_b64encode(u.bytes) 

and, conveniently, convert back

 u = uuid.UUID(bytes=base64.urlsafe_b64decode(token)) 
+2
source share

Characters or bytes? If it accepts arbitrary strings, you can just use bytes and not worry about expanding to readable characters (for which base64 would be better than hexadecimal).

MD5 generates 16 characters if you do not use its hexadecimal extension. SHA1 generates 20 under the same conditions.

 >>> import hashlib >>> len(hashlib.md5('foobar').digest()) 16 >>> len(hashlib.sha1('foobar').digest()) 20 

After that, a few extra bytes will be required.

0
source share

All Articles