PRBHA-10: A hashing algorithm in Python
Don’t roll your own encryption.  It takes a team of experts to develop new secure encryption methods, and even they can get it wrong from time to time.

Huge hole in your DIY encryption:

If I use a 2-character password, I might naïvely expect I’d have \$62^2\$ possible passwords that I can encrypt the data with.  I’d be really shocked when it turns out there are only 4.

random.getrandbits(len(password))


generates 2 random bits, for \$2^2\$ possible values to multiply to_num(data) by.  Only 4 possibilities is a lot easier to attack than \$62^2\$ different possibilities!

And one of those possibilities ... all bits zero ... destroys all the data you want to encode.  So we’re down to actually only 3 possible numbers to test to reverse the encryption.



Any encryption mechanism worth its salt uses a salt (initial random data) to prevent the same message with the same password from being encrypted as the same text.



Code improvements: use generator expressions.  Eg)

chunk = []
for digit in str(unique_id):
    chunk.append(LETTERS[int(digit)])
return "".join(chunk)


would be more efficient rewritten as:

return "".join(LETTERS[int(digit)] for digit in str(unique_id))


The chunk list is never created in memory; instead, the join method pulls items one at a time from the generator expression.



Finally, don’t roll your own encryption.