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)