hex-base64-translator.py (3562B) - raw
1 #!/usr/bin/env python3 2 # Copyright (C) 2020 Oscar Benedito <oscar@oscarbenedito.com> 3 # License: Affero General Public License version 3 or later 4 5 # This script simply translates hexadecimal strings into Base64 strings (by 6 # converting them into bits and then reading the bits as Base64) and the inverse 7 # process. 8 # 9 # The goal of this program is make a password containing only characters in the 10 # Base64 alphabet and then being able to split the secret between different 11 # parties using ssss (<http://point-at-infinity.org/ssss/>) with the hexadecimal 12 # option. With this, an attacker can't get any advantage by discarding unvalid 13 # answers, since they are all valid (when running the program normally, you can 14 # get "binary" secrets or "ASCII" secrets). 15 # 16 # All this trouble is due to the fact that I am not sure if there is a way for 17 # an attacker with some shares of the secret to avoid making a brute-force 18 # attack by knowing the implementation of ssss and anticipate binary (and 19 # therefore invalid) results. 20 21 import sys 22 from getpass import getpass 23 24 25 def char_to_bits(c): 26 n = ord(c) 27 if n >= 65 and n <= 90: 28 return bin(n - 65)[2:].zfill(6) 29 elif n >= 97 and n <= 122: 30 return bin(n - 71)[2:].zfill(6) 31 elif n >= 48 and n <= 57: 32 return bin(n + 4)[2:].zfill(6) 33 elif (c == '+'): 34 return bin(62)[2:].zfill(6) 35 elif (c == '/'): 36 return bin(63)[2:].zfill(6) 37 else: 38 sys.exit('Error, ' + c + ' is not a Base64 character.', file=sys.stderr) 39 40 41 def bits_to_char(s): 42 n = int(s, 2) 43 if n < 26: 44 return chr(n + 65) 45 elif n < 52: 46 return chr(n + 71) 47 elif n < 62: 48 return chr(n - 4) 49 elif n == 62: 50 return '+' 51 elif n == 63: 52 return '/' 53 else: 54 sys.exit('Error, ' + s + ' (' + str(n) + ') is not a binary number lower than 64.', file=sys.stderr) 55 56 57 def base64_to_hex(s): 58 if len(s) % 2: 59 print('WARNING: Number of Base64 characters is not multiple of 2. Adding zeros to string.', file=sys.stderr) 60 s = 'A' + s 61 62 ret = '' 63 carry = '' 64 while len(s) > 0: 65 cs = s[:2] 66 bs = char_to_bits(s[0]) + char_to_bits(s[1]) 67 ret += hex(int(bs, 2))[2:].zfill(3) 68 s = s[2:] 69 70 return ret 71 72 73 def hex_to_base64(s): 74 if len(s) % 3: 75 print('WARNING: Number of hexadecimal values is not a multiple of 3. Adding zeros to string.', file=sys.stderr) 76 s = '0'*(3 - (len(s) % 3)) + s 77 78 ret = '' 79 while len(s) > 0: 80 bs = bin(int(s[:3], 16))[2:].zfill(12) 81 ret += bits_to_char(bs[:6]) 82 ret += bits_to_char(bs[6:]) 83 s = s[3:] 84 85 return ret 86 87 88 if __name__ == '__main__': 89 if len(sys.argv) != 2 or (sys.argv[1] != 'base64-to-hex' and sys.argv[1] != 'hex-to-base64'): 90 sys.exit('Usage: ' + sys.argv[0] + ' base64-to-hex | hex-to-base64.') 91 92 if sys.argv[1] == 'base64-to-hex': 93 inp = getpass(prompt = 'Base64 secret: ') 94 print('') 95 out = base64_to_hex(inp) 96 print('-'*80) 97 print('Secret in hexadecimal:', out) 98 print('-'*80) 99 100 elif sys.argv[1] == 'hex-to-base64': 101 inp = getpass(prompt = 'Hexadecimal secret: ') 102 print('') 103 out = hex_to_base64(inp) 104 print('-'*80) 105 print('Secret in Base64:', out) 106 print('-'*80) 107 if inp[0] == '0' and len(inp) % 2 == 0: 108 out = hex_to_base64(inp[1:]) 109 print('-'*80) 110 print('Due to SSSS having an output with an even number of characters, your secret could be:', out) 111 print('-'*80)