#!/usr/bin/env python3 from base64 import b32decode from os import environ, listdir, path, uname from pathlib import Path from sshync import get_profile from sshyp import decrypt, shm_gen, whitelist_verify from subprocess import PIPE, Popen, run from sys import argv, exit as s_exit from time import sleep, strftime, time def totp(_secret, _algo, _digits, _period): # uses provided information to generate a standard totp key from hmac import new as new_mac from struct import pack, unpack _secret = b32decode(_secret.upper() + '=' * ((8 - len(_secret)) % 8)) _counter = pack('>Q', int(time() / _period)) _mac = new_mac(_secret, _counter, _algo).digest() _offset = _mac[-1] & 0x0f _binary = unpack('>L', _mac[_offset:_offset + 4])[0] & 0x7fffffff return str(_binary)[-_digits:].zfill(_digits) def mfa_read_shortcut(): # extracts MFA info from the user-specified sshyp entry from shutil import rmtree if not Path(f"{directory}{arguments[0]}.gpg").exists(): print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not exist\u001b[0m\n") s_exit(1) _shm_folder, _shm_entry = shm_gen() if quick_unlock_enabled == 'y': decrypt(directory + arguments[0], _shm_folder, _shm_entry, whitelist_verify(port, username_ssh, ip, device_id)) else: decrypt(directory + arguments[0], _shm_folder, _shm_entry, False) try: _mfa_data = open(f"{path.expanduser('~/.config/sshyp/tmp/')}{_shm_folder}/{_shm_entry}", 'r').readlines() _type = _mfa_data[4].split('otpauth://')[1].split('/')[0] _secret = _mfa_data[4].split('?secret=')[1].split('&issuer=')[0] _algo = _mfa_data[4].split('&algorithm=')[1].split('&digits=')[0] _digits = int(_mfa_data[4].split('&digits=')[1].split('&period=')[0]) _period = int(_mfa_data[4].split('&period=')[1]) rmtree(f"{path.expanduser('~/.config/sshyp/tmp/')}{_shm_folder}") return _type, _secret, _algo, _digits, _period except IndexError: print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not contain valid mfa data\u001b[0m\n") rmtree(f"{path.expanduser('~/.config/sshyp/tmp/')}{_shm_folder}") s_exit(1) if __name__ == '__main__': # argument fetcher arguments = argv[1:] if len(arguments) < 1 or not arguments[0].startswith('/'): print("\nsshyp-mfa extension usage: sshyp copy -m\n\nrun 'man sshyp-mfa' for more information\n") s_exit(1) # user data fetcher device_id = listdir(path.expanduser('~/.config/sshyp/devices'))[0] quick_unlock_enabled = open(path.expanduser('~/.config/sshyp/sshyp-data')).readlines()[3].rstrip() ssh_info = get_profile(path.expanduser('~/.config/sshyp/sshyp.sshync')) username_ssh = str(ssh_info[0].rstrip()) ip = str(ssh_info[1].rstrip()) port = str(ssh_info[2].rstrip()) directory = str(ssh_info[3].rstrip()) # main process: runs functions to generate MFA key, then continuously copies up-to-date MFA key to clipboard try: mfa_data, copied = mfa_read_shortcut(), None print('\nmfa key copied to clipboard\n\nuntil this process is closed, your clipboard will be automatically ' 'updated with the newest mfa key') while True: if str(int(strftime('%S'))/mfa_data[4]).endswith('.0') or copied is None: if copied is None: copied = 1 if mfa_data[0] == 'steam': from steam.guard import generate_twofactor_code as steam_totp _mfa_key = steam_totp(b32decode(mfa_data[1])) else: _mfa_key = totp(mfa_data[1], mfa_data[2], mfa_data[3], mfa_data[4]) if 'WSL_DISTRO_NAME' in environ: # WSL clipboard detection run(('powershell.exe', '-c', 'Set-Clipboard', _mfa_key)) elif 'WAYLAND_DISPLAY' in environ: # Wayland clipboard detection run(('wl-copy', _mfa_key)) elif uname()[0] == 'Haiku': # Haiku clipboard detection run(('clipboard', '-c', _mfa_key)) elif Path("/data/data/com.termux").exists(): # Termux (Android) clipboard detection run(('termux-clipboard-set', _mfa_key)) else: # X11 clipboard detection run(('xclip', '-sel', 'c'), stdin=Popen(('echo', '-n', _mfa_key), stdout=PIPE).stdout) sleep(1) except KeyboardInterrupt: print('\n') s_exit(0)