[Python][Crypto] Simplest possible password manager, part II

Previously. As I remarked at that post, base64 is inefficient. You would use more characters, including space, maybe.

How would you print a number?

#!/usr/bin/env python3

# slice one bit at each iteration:
def as_base2(n):
    rt=""
    while True:
        # is the last bit set?
        if n&1:
            rt=rt+"1"
        else:
            rt=rt+"0"
        # discard last bit.
        n=n>>1
        # no more bits?
        if n==0:
            # reverse string:
            return rt[::-1]

# slice 4 bits at each iteration:
def as_base16(n):
    rt=""
    while True:
        rt=rt+"0123456789abcdef"[n&0xf]
        # discard last 4 bits.
        n=n>>4
        # no more bits?
        if n==0:
            # reverse string:
            return rt[::-1]

# slice one decimal digit at each iteration:
def as_base10(n):
    rt=""
    while True:
        rt=rt+"0123456789"[int(n%10)]
        # discard last decimal number
        n=int(n/10)
        # no more bits?
        if n==0:
            # reverse string:
            return rt[::-1]

# as an exercise, you can write a function for base64 - just slice 6 bits at each iteration!

# slice a digit of some base, at each iteration:
def as_base_X(n, charset):
    base=len(charset)
    rt=""
    while True:
        rt=rt+charset[int(n%base)]
        # discard last decimal number
        n=int(n/base)
        # no more bits?
        if n==0:
            # reverse string:
            return rt[::-1]

# For more info about modular arithmetics, read: https://math.recipes/Math-recipes.pdf

print (as_base2(0b111000))
print (as_base16(0x1234567890abcdef))
print (as_base10(1234567890))

charset="".join(list(map(chr, list(range(32, 127)))))
# 95 chars, space included:
# ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
print ("["+as_base_X(1234567890, charset)+"]") # will print: [/.y5M]

Now I use the following Python script to encode a MD5 hash as a bignumber and than print it as a base95 number:

#!/usr/bin/env python3
import hashlib

print ("Enter the master password:")
MASTER=input()
ACCOUNT="dennis.yurichev"
HOST="gmail.com"
# To be incremented if you have to change this password
# For example, if compromised:
SALT="1"

m=hashlib.md5()
m.update(MASTER.encode("utf-8"))
m.update(ACCOUNT.encode("utf-8"))
m.update(HOST.encode("utf-8"))
m.update(SALT.encode("utf-8"))
hashed=m.digest()
# https://stackoverflow.com/questions/444591/how-to-convert-a-string-of-bytes-into-an-int
hashed_bn=int.from_bytes(hashed, byteorder='big')
#print (hashed_bn)

# slice a digit of some base, at each iteration:
def as_base_X(n, charset):
    base=len(charset)
    rt=""
    while True:
        rt=rt+charset[int(n%base)]
        # discard last decimal number
        n=int(n/base)
        # no more bits?
        if n==0:
            # reverse string:
            return rt[::-1]

charset="".join(list(map(chr, list(range(32, 127)))))
# 95 chars, space included:
# ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
print (as_base_X(hashed_bn, charset))

For example:

Enter the master password:
asd
$Pvnp-Kw%yj1}S5)gZW@

This is shorter than base64.

(the post first published at 20220511.)


List of my other blog posts. My company.

Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.