See also PgenKivy, PgenQt and PgenCs ## Pgen version 1 Equivalent of the following on a standard Gnu command line ```bash echo "$input$secret" | md5sum | cut -c1-32 | base64 | cut -c1-16 | tr "+/" "@#" ``` ```py #!/usr/bin/env python3 import hashlib import base64 import sys args = sys.argv[1:] import os env = os.environ def hash(x): 'Takes bytes x and returns md5 digest as bytes' x = x + b"\n" m = hashlib.md5() m.update(x) return m.hexdigest().encode("utf8") def b64(x): 'Takes bytes x and returns base64 encoding as string' return base64.b64encode(x).decode("utf8") def tr(x): 'equivalent of tr "+/" "@#"' return x.replace("/","@").replace("+","#") def comb(i,s,schema=b"$I$S"): return schema.replace(b"$I",i).replace(b"$S",s) def j(i,s,schema="$I$S"): i = i.encode("utf8") s = s.encode("utf8") schema = schema.encode("utf8") return tr(b64(hash(comb(i,s,schema)))) s = os.getenv("S","") for x in args: print(j(x,s)[:16]) ``` This version has a few flaws, in particular omitting the `xxd -r -p` to convert the hex string output by md5sum to binary before feeding it through base64. Thus the 16 character string output only captures half the entropy it should. Another is a relic of the bash version, and in particular the missing '-n' on the echo command. The Javascript implementation of this (versions 1 and 2) keeps these flaws when in Md5n mode. ### Pgen version 2 Equivalent of the following on a standard Gnu command line. ```bash echo "$input$secret" | sha256sum | cut -c1-64 | xxd -r -p | base64 | cut -c1-16 | tr "+/" "@#" ``` ```python #!/usr/bin/env python3 import hashlib import base64 import sys args = sys.argv[1:] import os env = os.environ def hash(x): 'Takes bytes x and returns sha256 digest as bytes' m = hashlib.sha256() m.update(x) return m.digest() def b64(x): 'Takes bytes x and returns base64 encoding as string' return base64.b64encode(x).decode("utf8") def tr(x): 'equivalent of tr "+/" "@#"' return x.replace("+","@").replace("/","#") def comb(i,s,schema=b"$I$S"): return schema.replace(b"$I",i).replace(b"$S",s) def jj(i,s,schema="$I$S"): i = i.encode("utf8") s = s.encode("utf8") schema = schema.encode("utf8") return tr(b64(hash(comb(i,s,schema)))) s = os.getenv("S","") for x in args: print(jj(x,s)[:16]) ``` ### Pgen version 3 No bash command line equivalent, uses sha256 function iteratively 16384*n times where n is the length of the sequence (here the sequence is "in principio erat verbum et verbum erat apud deum et deus erat verbum") ```python #!/usr/bin/env python3 import hashlib import base64 import sys args = sys.argv[1:] import os env = os.environ john1 = "in principio erat verbum et verbum erat apud deum et deus erat verbum".encode("utf8").split(b" ") def hash(x): 'Takes bytes x and returns sha256 digest as bytes' m = hashlib.sha256() m.update(x) return m.digest() def jjjs(inp,sec,seq,n=16384): "inp cur seq are bytes() objects" x =inp for i in range(n): for t in seq: x = hash(b"".join((t,sec,inp,x,sec,t))) return x def jjjt(inp,sec): return jjjs(inp,sec,john1) def b64(x): 'Takes bytes x and returns base64 encoding as string' return base64.b64encode(x).decode("utf8") def tr(x): 'equivalent of tr "+/" "@#"' return x.replace("+","@").replace("/","#") def jjj(inp,sec,n=16): return tr(b64(jjjt(inp,sec)))[:n] s = os.getenv("S","").encode("utf8") for x in args: print(jjj(x.encode("utf8"),s)) ``` And if one wishes to have multiple 'secret' phrases, it is simple enough to append them to the sequence. Basically the idea here is to make a more processor intensive hashing function out of sha256. In addition, one can change the hash function to anything else. The key is that the process is *repeatable*. The versions 1 and 2 have the nice property that they can be expressed as a 1-line bash command (indeed, it works with /bin/dash too). In practice, version 2 is fine for most things, and version 3 is perhaps overkill. ### Pgen version 3.1 I figure it is easier to iterate the `x -> sha256 -> base64 -> x` loop rather than something fancy like a sequence of words. ```python #!/usr/bin/env python3 import hashlib import base64 import sys args = sys.argv[1:] import os env = os.environ # helpers for iterating sha def sha256(x, coding='utf8'): return sha256_real(x.encode(coding)).decode(coding) def sha256_real(x): sha = hashlib.sha256() sha.update(x) return base64.b64encode(sha.digest()) def sha_multi_round(x,n, coding='utf8'): return sha_multi_round_real(x.encode(coding),n).decode(coding) def sha_multi_round_real(x,n): for j in range(n): x = sha256_real(x) return x def b64(x): 'Takes bytes x and returns base64 encoding as string' return base64.b64encode(x).decode("utf8") def tr(x): 'equivalent of tr "+/" "@#"' return x.replace("+","@").replace("/","#") def jjj(inp,sec,n=16384,l=16): return tr(sha_multi_round(inp+sec,n))[:l] s = os.getenv("S","") for x in args: print(jjj(x,s)) ```