diff --git a/Hatnotation-Password-Generator.py b/Hatnotation-Password-Generator.py new file mode 100644 index 0000000..ccfaea9 --- /dev/null +++ b/Hatnotation-Password-Generator.py @@ -0,0 +1,114 @@ +import string +import secrets +import sys +import math + +#Author: Steven Hatzakis @ 2019 + +#A cryptographically-secure password generator that uses Python's built-in "secrets" module and that is compatible with the Hatnotation library. + +alphabet = ["0", +"1", +"2", +"3", +"4", +"5", +"6", +"7", +"8", +"9", +"A", +"B", +"C", +"D", +"E", +"F", +"G", +"H", +"I", +"J", +"K", +"L", +"M", +"N", +"O", +"P", +"Q", +"R", +"S", +"T", +"U", +"V", +"W", +"X", +"Y", +"Z", +"!", +'"', +"#", +"$", +"%", +"&", +"'", +"(", +")", +"*", +'+', +",", +"-", +".", +"{", +":", +";", +"<", +"=", +">", +"?", +"@", +"[", +"}", +"]", +"^", +"_", +"`"] +joinedalphabet="".join(alphabet) +print('this is the alphabet we will be using:',joinedalphabet, +'which has', len(alphabet), 'characters (total 64), and where the length of passwords generated randomly from this alphabet will determine their strength in terms of bits of security (entropy).') +print(len(string.ascii_letters + string.digits+ string.punctuation)) + +password22 = ''.join(secrets.choice(alphabet) for i in range(22)) +password44 = ''.join(secrets.choice(alphabet) for i in range(44)) +password64 = ''.join(secrets.choice(alphabet) for i in range(64)) +password176 = ''.join(secrets.choice(alphabet) for i in range(176)) + + +library=64 #used to calculate entropy + +numberchars22=len(password22) +entropy22=library**numberchars22 +logentropy22=math.log2(entropy22) +print(password22) +print("your above",len(password22), "character-long password has entropy of security in this many bits",int(logentropy22)) #sys.getsizeof(password20),"bits long") + +numberchars44=len(password44) +entropy44=library**numberchars44 +logentropy44=math.log2(entropy44) +print(password44) +print("your above",len(password44), "character-long password has entropy of security in this many bits",int(logentropy44))#sys.getsizeof(password79),"bits long") + + +numberchars64=len(password64) +entropy64=library**numberchars64 +logentropy64=math.log2(entropy64) +print(password64) +print("your above",len(password64), "character-long password has entropy of security in this many bits" ,int(logentropy64))#,sys.getsizeof(password207),"bits long") + +numberchars176=len(password176) +entropy176=library**numberchars176 +logentropy176=math.log2(entropy176) +print(password176) +print("your above",len(password176), "character-long password has entropy of security in this many bits" ,int(logentropy176))#,sys.getsizeof(password207),"bits long") + + +print("START Alphabet",alphabet,"END Alphabet") + diff --git a/Password_Generator_python-hatnotation_v3.py b/Password_Generator_python-hatnotation_v3.py new file mode 100644 index 0000000..1e54b6b --- /dev/null +++ b/Password_Generator_python-hatnotation_v3.py @@ -0,0 +1,92 @@ +import string +import secrets +import sys +import math + +#Author: © Steven J Hatzakis, 2020 + +alphabet = ["0", +"1", +"2", +"3", +"4", +"5", +"6", +"7", +"8", +"9", +"A", +"B", +"C", +"D", +"E", +"F", +"G", +"H", +"I", +"J", +"K", +"L", +"M", +"N", +"O", +"P", +"Q", +"R", +"S", +"T", +"U", +"V", +"W", +"X", +"Y", +"Z", +"!", +'"', +"#", +"$", +"%", +"&", +"'", +"(", +")", +"*", +'+', +",", +"-", +".", +"{", +":", +";", +"<", +"=", +">", +"?", +"@", +"[", +"}", +"]", +"^", +"_", +"`"] +joinedalphabet="".join(alphabet) +print('This is the alphabet we will be using:',joinedalphabet, 'which has', len(alphabet), 'characters (total 64), and where the length of passwords generated randomly from this alphabet will determine their strength in terms of bits of security (entropy).') +print(len(string.ascii_letters + string.digits+ string.punctuation)) + +# total printable is 95 with whitespace: +#'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ' +char=int(input('Please enter a number (i.e. enter ''22'' for a 22-character password with 128 bits of security):')) +password = ''.join(secrets.choice(alphabet) for i in range(char)) + +library=64 #used to calculate entropy + +numberchars=len(password) +entropy=library**numberchars +logentropy=math.log2(entropy) +print(password) +print("your above",len(password), + "character-long password has entropy of security in this many bits", + int(logentropy)) + + + diff --git a/README.md b/README.md index 858106a..614e93d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Hatnotation +# :tophat: Hatnotation > Hatnotation is short for the *Hatzakis Base 64 notation system* which is a method to encode/decode arbitrary binary strings of data, invented by Steven Hatzakis and open-sourced here under [Apache License 2.0](https://github.com/hatgit/hatnotation/blob/master/LICENSE). @@ -8,12 +8,12 @@ ## Purpose -An encoding/decoding method that allows users to compress their human-readable data into fewer human-readable characters than other popular notation systems, for any arbitrary underlying machine-readable binary string. +An encoding/decoding method that allows users to compress their human-readable data into fewer human-readable characters than other popular notation systems, for any arbitrary underlying machine-readable binary string, using a 6-bit values from the range of 2^6 values. ## Warning: -This software is still in its experimental phase and should not be relied upon for production. +This software is still in its experimental phase (including debugging, redesign and error-checking/testing) and should not be relied upon for production. For example, as of April 30 before the conclusion of the Ethereal Hackathon our Javascript versions of the Decoder were still being debugged. ## Background on Mnemonics (private keys) and Human vs Machine-readable code @@ -21,7 +21,7 @@ Mnemonics (aka recovery phrases) are used in many popular crypto wallet applciat For example, instead of a user having to backup a string of 128 bits or their private key, they can simply store the encoded mnemonic which represents those bits or a private key. -> Note: While the word "private key" is usually associated with public/private key-pairs in cryptogrpahy, for the purpose of this Readme.md file, the use of private key refers to the master private key (initial entropy) for a crypto vault (within which accounts and private keys are dervived) which can also be considered a pre-image of the mnemonic. +> Note: While the word "private key" is usually associated with public/private key-pairs in cryptogrpahy, for the purpose of this Readme.md file, the use of private key refers to the master private key (initial entropy) for a crypto vault (within which accounts and private keys are derived) which can also be considered a pre-image of the mnemonic. | |12-word mnemonic |24-word mnemonic | @@ -38,22 +38,33 @@ In terms of actual pre-image resistance, the initial entropy should be generated ### Example of various notation methods for a given binary (base 2) string: -- Binary (base-2) format: ->`00001001100111001011111110101111000100110000001100100111011101101011100000111110011000110100110000101100001011101010000000010111` -- Hexidecimal(base-16) format: -> `99cbfaf13032776b83e634c2c2ea017` -- Decimal (base-10) integer format: ->`12776938083042441757844264502598475799` -- Mnemonic format (BIP39): ->`another tourist type champion crash robust thought small equip gesture pool cool` (note: this mnemonic conveys 132 bits as the extra 4-bit checksum is deterministic based on the initial 128 bits). +- 132-bit Binary (base-2) format with leading 0b): +>`0b000010011001110010111111101011110001001100000011001001110111011010111000001111100110001101001100001011000010111010100000000101111110` -- >Hatnotation format: - > `9$B_,4-C$T(W_O;-)B'0N` +- 33-character Hexidecimal(base-16) format (with leading 0x): +> `0x099cbfaf13032776b83e634c2c2ea017e` + +- 16-bytes totalling 35 characters excluding backslahes: +> b'\x99\xcb\xfa\xf102wk\x83\xe64\xc2\xc2\xea\x01~' + +- 24-character Base64: +>`b'mcv68TAyd2uD5jTCwuoBfg=='` + +- 39-decimal (base-10) integer format: +>`204431009328679068125508232041575612798` + +- 12-word Mnemonic format (BIP39): +>`another tourist type champion crash robust thought small equip gesture pool cool` (note: this mnemonic conveys 132 bits as the extra 4-bit checksum '1110' from the above binary string is deterministic based on the initial 128 bits). + +- 22-character Hatnotation format: +> 2P{`(.C39>Q?F#DCB2[W5_ + + ## Important -The Hatnotation system is *not intended to be an alternative to human-readable mnemonics*, but rather a complement and simply another representation of the machine-readable code, with the benefit of a reduction in the number of characters needed to notate and backup/store the data, using common and special characters from a library of 64 total possible characters (in range of 2^6). +The Hatnotation system is *not intended to be an alternative to human-readable mnemonics*, but rather a complement and simply another representation of the machine-readable code, with the benefit of a reduction in the number of characters needed to notate and backup/store the data, using common and special characters from a library of 64 total possible characters (in the zero-indexed range of 2^6-1). @@ -138,7 +149,7 @@ Using the binascii libary in python which contains the string library, we source 43 | "101011", | "(", 44 | "101100", | ")", 45 | "101101", | "*", -46 | "101110", | "+", +46 | "101110", | '+', 47 | "101111", | ",", 48 | "110000", | "-", 49 | "110001", | ".", @@ -169,7 +180,7 @@ In Python version 3.7 using the strings library, the following steps can be take - Note, the backslash `\` and forwardslash `/`characters were swapped with opening `{` and closing `}` curly brackets in the following issue: https://github.com/hatgit/hatnotation/issues/3. - The list of valid Hatnotation library characters are thus as follows: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-.{:;<=>?@[}]^_` -(and the following four remain excluded/reserved ~~```"\/|~"```~~). +- And the following four remain excluded/reserved ~~```"\/|~"```~~). ## Requirements @@ -180,9 +191,9 @@ Python 3 or higher ## Tests: -> Example Test strings: +> Example Test strings (note: these are not ASCII notations): -- Decode Target: `HELLOWORLD +- Decode Target: `HELLOWORLD` - Each letter decodes to respective 6-bit group: `"010001","001110","010101","010101","011000"," ", "100000","011000","011011","010101","001101", @@ -190,11 +201,36 @@ Python 3 or higher - Concatenation of both words into one string: `"010001001110010101010101011000100000011000011011010101001101" -- Converted binary string to hex (can be used as starting point to encode to "helloworld": `0x44e55562061b54d` +- Converted binary string to hex (can be used as starting point to encode to "HELLOWORLD": `0x44e55562061b54d` + +>The following Hex string can be fed to the encoder to print all characters in their linear order except for the first which is "0" (zero) and gets omitted: + +0x108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31ef3d35db7e39eb2f3dfbf + +The easiest of this example can be seen using the Hatnotation library of 64 characters as the input to the decoder: + +0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-.{:;<=>?@[}]^_` + +The library in binary format as a continous string: +000000000001000010000011000100000101000110000111001000001001001010001011001100001101001110001111010000010001010010010011010100010101010110010111011000011001011010011011011100011101011110011111100000100001100010100011100100100101100110100111101000101001101010101011101100101101101110101111110000110001111011110011110100110101110110110111111000111001111010110010111100111101111110111111 -Second Test: Decoder should be able to take a single backslash `\` and return index value 59, as well as multiple ones '`\\\\\`' and not be affected by any formatting/rendering issues of the app or interface (i.e. python or javascript/html). +The above 384-bit binary string (based on 64*6 bits) in hex is: 0x108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31ef3d35db7e39eb2f3dfbf +When the above hex string is encoded back to hatnotation it loses the leading zero (or first 6 zeroes of the above binary string) resulting in it missing from the start of the resulting encoded characters: "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-.{:;<=>?@[}]^_`" + +The loss of leading zeroes has been discussed in the following issue and is common across other popular notation systems when converting from left-padded binary data: https://github.com/hatgit/hatnotation/issues/6 + +## On the subject of leading zeroes (different note): +It should be noted that if the Decoder happens to output a leading zero at the start of the string, any such continous run of zeroes (up to 5) can be discarded and means that the program appended the zero(s) because the length of the encoded binary data modulo 6 was greater than 0, unless the length of data modulo 6 is equal to zero and there are still leading zeroes. + +**When leading zeroes CAN be discarded:** If the following 64-bit string `1111111101110110000010000001010100110110010101010010100100100000` was pasted into the Hatnotation encoder (where 64 mod 6 = 4), there would be two zeroes automatically appended by the program to the left when decoding back into the string as follows `001111111101110110000010000001010100110110010101010010100100100000` (so that `66 mod 6 = 0`) which would only be noticeable when decoding the Hatnotation `F^=21K=LI!W` back to binary using the decoder, where two leading zeroes would appear and which should be discarded to obtain the original binary data (There is however, an exception to this rule noted below in the next example). + +As noted above, the **exception when leading zeroes should NOT be discarded** is when the length of the string being encoded is already equal to zero when modulo 6, and thus leading zeroes are already present such as in the following string, `001111111101110110000010000001010100110110010101010010100100100000` which when encoded would appear also as `F^=21K=LI!W` and indistinguishable from an identical string that lacked such two leading zeroes prior to encoding, and thus the user must retain the initial length of the string prior to encoding, in order to decide whether any leading zeroes should be discarded or not when decoding back to the initial data. + +This approach is necessary as appending the zeroes instead at the right end would increase the size of the number versus at the front ( `['111111', '110111', '011000', '001000', '000101', '010011', '011001', '010101', '001010', '010010', '0000']`) which could lead to errors, compared to the way the program works now where the output is read as a big-endian number where the most significant bit starts at the left and thus any leading zeroes should be dropped : [`'001111', '111101', '110110', '000010', '000001', '010100', '110110', '010101', '010010', '100100', '100000'`] (especially as the encoder cannot encode the value `0` at the start position in hatnotation which in binary is "`000000`"). + +** There can be some formatting issues in Python which affect how data is printed as noted in this committ: https://github.com/hatgit/hatnotation/commit/66727918cef8a5bdfad21051d52b9c1e483c7fbc ## Resources: @@ -208,3 +244,10 @@ Second Test: Decoder should be able to take a single backslash `\` and return in - potentially propose a request for comments (RFC) for consideration as a standard. - add error message for invalid characters (i.e. lowercase and reserved characters `\|/~` +# About Hatnotation-Password-Generator.py + +Author: Steven Hatzakis @ 2019 + +A cryptographcially-secure [password generator](https://github.com/hatgit/hatnotation/blob/master/Hatnotation-Password-Generator.py) that uses Python's built-in "secrets" module and that is compatible with the Hatnotation library (i.e. Generates passwords that only use the 64 Hatnotation characters). + + diff --git a/base64Decode_v1_01.js b/base64Decode_v1_01.js new file mode 100644 index 0000000..95b8fc0 --- /dev/null +++ b/base64Decode_v1_01.js @@ -0,0 +1,125 @@ +//Author: Andrea Zuccarini @2019 + +//Version 1.01 + +// Example Test string target to decode: HELLOWORLD +// Each letter decodes to respective 6-bit group: "010001","001110","010101","010101","011000"," ", "100000","011000","011011","010101","001101", +// Each Word as Continous string "010001001110010101010101011000" "100000011000011011010101001101" +// Concatenation of both words into one string: "010001001110010101010101011000100000011000011011010101001101" +// Converted binary string to hex: "0x44e55562061b54d" + + +b64dict= {}; + +base64library=['0', +'1', +'2', +'3', +'4', +'5', +'6', +'7', +'8', +'9', +'A', +'B', +'C', +'D', +"E", +'F', +'G', +'H', +'I', +'J', +'K', +'L', +'M', +'N', +'O', +'P', +'Q', +'R', +'S', +'T', +'U', +'V', +'W', +'X', +'Y', +'Z', +'!', +'"', +'#', +'$', +'%', +'&', +"'", +'(', +')', +'*', +'+', +',', +'-', +'.', +'}', +':', +';', +'<', +'=', +'>', +'?', +'@', +'[', +'{', +']', +'^', +'_', +'`']; + +for(var i=0;i?@[}]^_`:'); +console.log(decodeAsBin('HELLOWORLD')); +console.log('0x' + decodeAsHex('HELLOWORLD')); + + +// test decode string "HELLOWORLD" should return the binary equivalent of hex: 0x44e55562061b54d +// test decode string "INVENTEDBYSTEVENHATZAKIS@2018" should return this hex 0x125df39774e34b89c74e7ce5d129d8ca512739080048 as equivalent in binary to this output: 010010010111011111001110010111011101001110001101001011100010011100011101001110011111001110010111010001001010011101100011001010010100010010011100111001000010000000000001001000 + +// Note/CONSIDERATION: In version 1.01 to resolve a rendering conflict, the Forward slash "/" and Backslash "\" characters have been swapped out and replaced by the closing curly bracket "}" and opening curly bracket "{" on purpose. diff --git a/base64Decode_v1_01.py b/base64Decode_v1_02.py similarity index 80% rename from base64Decode_v1_01.py rename to base64Decode_v1_02.py index 16ee090..fb5a4d8 100644 --- a/base64Decode_v1_01.py +++ b/base64Decode_v1_02.py @@ -63,11 +63,11 @@ "(", ")", "*", -"+", +'+', ",", "-", ".", -"}", +"{", ":", ";", "<", @@ -76,7 +76,7 @@ "?", "@", "[", -"{", +"}", "]", "^", "_", @@ -101,13 +101,13 @@ def decodeAsHex(string): return hex(int(decodeAsBin(string), 2)) -hatdata2decode=input('Enter any combination of the following characters to decode without spaces: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&''()*+,-.{:;<=>?@[}]^_`":') +hatdata2decode=input('Enter any combination of the following characters to decode without spaces: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!"'"#$%&'"'()*+,-.{:;<=>?@[}]^_`:') print(decodeAsBin(hatdata2decode)) ## test decode string "HELLOWORLD" should return the binary equivalent of hex: 0x44e55562061b54d ## test decode string "INVENTEDBYSTEVENHATZAKIS@2018' should return this hex 0x125df39774e34b89c74e7ce5d129d8ca512739080048 as equivalent in binary to this output: 010010010111011111001110010111011101001110001101001011100010011100011101001110011111001110010111010001001010011101100011001010010100010010011100111001000010000000000001001000 - +## Note/ WARNING: leading zeroes are not retained in the Hatnotated string, therefore, when decoding the test string `9$B_,4-C$T(W_O;-)B'0N` found in the Readme file, two leading zeroes will not appear. Software implementers should pad enough zeroes to math the Hatnotated character lenght (i.e. 22 pasted characters should compute 132 bits, but if only 130 bits show then 2 leading zeroes shoud be padded. ## Note/CONSIDERATION: In version 1.01 to resolve a rendering conflict, the Forward slash `/` and Backslash `\` characters have been swapped out and replaced by the closing curly bracket `}` and opening curly bracket `{` on purpose. diff --git a/base64encode_v1-01.js b/base64encode_v1-01.js new file mode 100644 index 0000000..5b9add6 --- /dev/null +++ b/base64encode_v1-01.js @@ -0,0 +1,96 @@ +//Author: Andrea Zuccarini @2019 +// Version 1.01 + +//test binary data string: 0b010010010111011111001110010111011101001110001101001011100010011100011101001110011111001110010111010001001010011101100011001010010100010010011100111001000010000000000001001000 + +//test string as hex: '0x125df39774e34b89c74e7ce5d129d8ca512739080048' + +//test string result :'INVENTEDBYSTEVENHATZAKIS@2018' + +// legacy notes: #FIX: LEADING ZEROS LOST (i.e.w/ test string 0x064ce8c835f4d374e04e33244beccad0) +// also, even when no leading zero in string, the leading zeroes of the actual base64 character are discarded, +// as the encoding appears to happen from right to left, see this test string where leading zeroes of +// first char 2 are discarded, and last char is N 0xb13ae7e331ce9dfa59799e95ee8dc117 + +function base64en(address_hex) { // important never to rename base64en to "base64" which can corrupt the Python installation. + // (renamed alphabet to base64library below) alphabet = string.digits+string.ascii_uppercase+string.punctuation + base64library=["0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "!", + '"', + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "*", + "+", + ",", + "-", + ".", + "{", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "[", + "}", + "]", + "^", + "_", + "`"] + b64_string = ''; + // Convert hex to decimal + address_int = parseInt(address_hex,16); + // Append digits to the start of string + while(address_int > 0) { + digit = address_int % 64; + digit_char = base64library[digit]; + b64_string = digit_char + b64_string; + address_int = parseInt(address_int/64); + } + return b64_string; +} +//hexer=input('enter hex with pad'); +hexer = '0x125df39774e34'; +console.log(base64en(hexer)); +console.log("The above is this many char's long: ", base64en(hexer).length); diff --git a/base64encode_v1_01.py b/base64encode_v1_02.py similarity index 96% rename from base64encode_v1_01.py rename to base64encode_v1_02.py index 3363b34..7c6562d 100644 --- a/base64encode_v1_01.py +++ b/base64encode_v1_02.py @@ -4,7 +4,7 @@ import binascii # required #Author: Steven Hatzakis @2018, Licensed under Apache 2.0 -# Version 1.01 +# Version 1.02 #test binary data string: 0b010010010111011111001110010111011101001110001101001011100010011100011101001110011111001110010111010001001010011101100011001010010100010010011100111001000010000000000001001000 @@ -65,7 +65,7 @@ def base64en(address_hex): # important never to rename base64en to "base64" wh "(", ")", "*", -"+", +'+', ",", "-", ".", @@ -99,6 +99,6 @@ def base64en(address_hex): # important never to rename base64en to "base64" wh #for one in range(ones): # b64_string = '1' + b64_string return b64_string -hexer=int(input('enter hex with pad'),16) +hexer=int(input('enter binary string with '0b' pad'),2) print(base64en(hexer)) print("The above is this many char's long: ",len(str((base64en(hexer))))) diff --git a/index.html b/index.html new file mode 100644 index 0000000..3d5be54 --- /dev/null +++ b/index.html @@ -0,0 +1,5518 @@ + + + BIP39 Mnemonic + + + // + + + + + +

REGULAR: BIP39 Mnemonic Tool

+

STEP 1: Select word count:

+ +

OPTIONAL: Insert entropy in the form of a 0x-padded hex string of length 32 (for 12 words) or 64 (for 24 words):

+ +
+
+ + +
+
+
+ +
+
+ +

Decode Base64 Entropy

+ + +
+

+
+ +