diff --git a/onlykey/__main__.py b/onlykey/__main__.py new file mode 100644 index 0000000..fed5d8e --- /dev/null +++ b/onlykey/__main__.py @@ -0,0 +1,3 @@ +from .cli import main +if __name__ == "__main__": + main() diff --git a/onlykey/cli.py b/onlykey/cli.py index a0080bb..5e5221c 100644 --- a/onlykey/cli.py +++ b/onlykey/cli.py @@ -5,6 +5,7 @@ from builtins import input from builtins import next from builtins import range +import argparse import base64 import binascii import time @@ -13,7 +14,9 @@ import sys import solo import solo.operations -from solo.cli.key import key +from .client import SLOTS_NAME, EDITABLE_SLOT_RANGE, KEYS_SLOT_RANGE +from solo.cli.key import reset, ping, rng, set_pin, change_pin, wink, raw as rngraw, feedkernel as rngfeedkernel, hexbytes as rnghexbytes +import solo.cli.key from solo.cli.monitor import monitor from solo.cli.program import program @@ -24,11 +27,346 @@ from prompt_toolkit.filters import Condition import nacl.signing + + from .client import OnlyKey, Message, MessageField + +AVAILABLE_LAYOUTS = { + 1: "USA_ENGLISH (Default)", + 2: "CANADIAN_FRENCH", + 3: "CANADIAN_MULTILINGUAL", + 4: "DANISH", + 5: "FINNISH", + 6: "FRENCH", + 7: "FRENCH_BELGIAN", + 8: "FRENCH_SWISS", + 9: "GERMAN", + 10: "GERMAN_MAC", + 11: "GERMAN_SWISS", + 12: "ICELANDIC", + 13: "IRISH", + 14: "ITALIAN", + 15: "NORWEGIAN", + 16: "PORTUGUESE", + 17: "PORTUGUESE_BRAZILIAN", + 18: "SPANISH", + 19: "SPANISH_LATIN_AMERICA", + 20: "SWEDISH", + 21: "TURKISH", + 22: "UNITED_KINGDOM", + 23: "US_INTERNATIONAL", + 24: "CZECH", + 25: "SERBIAN_LATIN_ONLY", + 26: "HUNGARIAN", + 27: "DANISH MAC", + 28: "US_DVORAK" +} + only_key = OnlyKey() -def cli(): +def init_onlykey(): + while 1: + if only_key.read_string(timeout_ms=500) != 'UNINITIALIZED': + break + for msg in [Message.OKSETPIN]: + only_key.send_message(msg=msg) + print(only_key.read_string()) + print () + input('Press the Enter key once you are done') + only_key.send_message(msg=msg) + print(only_key.read_string()) + only_key.send_message(msg=msg) + print(only_key.read_string()) + print () + input('Press the Enter key once you are done') + only_key.send_message(msg=msg) + time.sleep(1.5) + print(only_key.read_string()) + print () + for msg in [Message.OKSETPDPIN]: + only_key.send_message(msg=msg) + print(only_key.read_string() + ' for second profile') + print () + input('Press the Enter key once you are done') + only_key.send_message(msg=msg) + print(only_key.read_string() + ' for second profile') + only_key.send_message(msg=msg) + print(only_key.read_string()) + print () + input('Press the Enter key once you are done') + only_key.send_message(msg=msg) + time.sleep(1.5) + print(only_key.read_string()) + print () + for msg in [Message.OKSETSDPIN]: + only_key.send_message(msg=msg) + print(only_key.read_string()) + print () + input('Press the Enter key once you are done') + only_key.send_message(msg=msg) + print(only_key.read_string()) + only_key.send_message(msg=msg) + print(only_key.read_string()) + print () + input('Press the Enter key once you are done') + only_key.send_message(msg=msg) + time.sleep(1.5) + print(only_key.read_string()) + print () + +def command_fwversion(parser, args): + only_key.set_time(time.time()) + okversion = only_key.read_string() + print(okversion[8:]) + +def command_help(parser, args): + parser.print_help() + +def command_getlabels(parser, args): + tmp = {} + for slot in only_key.getlabels(): + tmp[slot.name] = slot + slots = iter(['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']) + for slot_name in slots: + print(tmp[slot_name].to_str().replace('ÿ'," ")) + print(tmp[next(slots)].to_str().replace('ÿ'," ")) + print() + +def command_getkeylabels(parser, args): + tmp = {} + for slot in only_key.getkeylabels(): + tmp[slot.name] = slot + slots = iter(['RSA Key 1', 'RSA Key 2', 'RSA Key 3', 'RSA Key 4', + 'ECC Key 1', 'ECC Key 2', 'ECC Key 3', 'ECC Key 4', + 'ECC Key 5', 'ECC Key 6', 'ECC Key 7', 'ECC Key 8', + 'ECC Key 9', 'ECC Key 10', 'ECC Key 11', 'ECC Key 12', + 'ECC Key 13', 'ECC Key 14', 'ECC Key 15', 'ECC Key 16']) + for slot_name in slots: + print(tmp[slot_name].to_str().replace('ÿ'," ")) + +def slot_name_to_id(name): + for i in SLOTS_NAME: + if SLOTS_NAME[i] == name: + return i + +def parse_slot_id(slot_id_s): + slot_id = slot_name_to_id(slot_id_s) + if slot_id == None: + slot_id = int(slot_id_s) + if slot_id > 25: + return slot_id + else: + return None + else: + return slot_id + +def command_setslot(parser, args): + slot_id = parse_slot_id(args.id) + if args.type == 'label': + only_key.setslot(slot_id, MessageField.LABEL, args.value) + elif args.type == 'ecckeylabel': + only_key.setslot(slot_id+28, MessageField.LABEL, args.value) + elif args.type == 'rsakeylabel': + only_key.setslot(slot_id+24, MessageField.LABEL, args.value) + elif args.type == 'url': + only_key.setslot(slot_id, MessageField.URL, args.value) + elif args.type == 'addchar2': + only_key.setslot(slot_id, MessageField.NEXTKEY1, args.value) + elif args.type == 'delay1': + only_key.setslot(slot_id, MessageField.DELAY1, args.value) + elif args.type == 'username': + only_key.setslot(slot_id, MessageField.USERNAME, args.value) + elif args.type == 'addchar3': + only_key.setslot(slot_id, MessageField.NEXTKEY2, args.value) + elif args.type == 'delay2': + only_key.setslot(slot_id, MessageField.DELAY2, args.value) + elif args.type == 'password': + password = prompt_pass() + only_key.setslot(slot_id, MessageField.PASSWORD, password) + elif args.type == 'addchar5': + only_key.setslot(slot_id, MessageField.NEXTKEY3, args.value) + elif args.type == 'delay3': + only_key.setslot(slot_id, MessageField.DELAY3, args.value) + elif args.type == '2fa': + only_key.setslot(slot_id, MessageField.TFATYPE, args.value) + elif args.type == 'gkey': + totpkey = prompt_key() + totpkey = base64.b32decode("".join(totpkey.split()).upper()) + totpkey = binascii.hexlify(totpkey) + # pad with zeros for even digits + totpkey = totpkey.zfill(len(totpkey) + len(totpkey) % 2) + payload = [int(totpkey[i: i+2], 16) for i in range(0, len(totpkey), 2)] + only_key.setslot(slot_id, MessageField.TOTPKEY, payload) + elif args.type == 'totpkey': + totpkey = prompt_key() + only_key.setslot(slot_id, MessageField.TOTPKEY, totpkey) + elif args.type == 'addchar1': + only_key.setslot(slot_id, MessageField.NEXTKEY4, args.value) + elif args.type == 'addchar4': + only_key.setslot(slot_id, MessageField.NEXTKEY5, args.value) + else: + print("Invalid type") + +def command_idletimeout(parser, args): + only_key.setslot(1, MessageField.IDLETIMEOUT, args.time) + +def command_keylayout(parser, args): + if args.list_all: + for k in AVAILABLE_LAYOUTS: + print(f'{k} {AVAILABLE_LAYOUTS[k]}') + elif args.layout_number == None: + print("layout number missing, use one of") + for k in AVAILABLE_LAYOUTS: + print(f'{k} {AVAILABLE_LAYOUTS[k]}') + else: + print(f"setting keylayout {args.layout_number}: {AVAILABLE_LAYOUTS[args.layout_number]}") + only_key.setslot(1, MessageField.KEYLAYOUT, int(args.layout_number)) + +def command_wipeslot(parser, args): + slot_id = parse_slot_id(args.id) + only_key.wipeslot(slot_id) + +def make_simple_setslot(message): + return lambda parser, args: only_key.setslot(1, message, int(args.value)) + +def command_change_pin(parser, args): + change_pin.callback(args.serial) + +def command_set_pin(parser, args): + set_pin.callback(args.pin) + +def command_ping(parser, args): + ping.callback(args.serial, args.udp, args.ping_data) + +def command_wink(parser, args): + wink.callback(args.serial, args.udp) + +def command_rng(parser, args): + if getattr(args, "output-type") == "raw" and args.count != None: + raise Exception("raw output type doesn't support count flag") + + if getattr(args, "output-type") == "hexbytes": + rnghexbytes.callback(args.count, args.serial) + elif getattr(args, "output-type") == "raw": + rngraw.callback(args.serial) + elif getattr(args, "output-type") == "feedkernel": + rngfeedkernel.callback(args.count, args.serial) + +def command_settime(parser, args): + only_key.set_time(time.time()) + print(only_key.read_string()) + +def command_credential(parser, args): + if args.cred_command == "rm" and args.credential_id == None: + raise Exception("needed: ") + + if args.cred_command == "info": + cred_info.callback(args.pin, args.serial, args.udp) + elif args.cred_command == "ls": + cred_ls.callback(args.pin, args.serial, args.udp) + elif args.cred_command == "rm": + cred_rm.callback(args.pin, args.credential_id, args.serial, args.udp) + + +def setup_argparse(simple_commands): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(title = "commands", dest="sub_command") + version_parser = subparsers.add_parser("version", help="display the version of the app") + fwvesion_parser = subparsers.add_parser("fwversion", help="display the version of the OnlyKey firmware") + help_parser = subparsers.add_parser("help", help="this message") + getlabels_parser = subparsers.add_parser("getlabels", help="return slot's labels") + getkeylabels_parser = subparsers.add_parser("getkeylabels", help="show key's labels") + setslot_parser = subparsers.add_parser("setslot", help="set slot action, label ...") + editable_slot_names = list(SLOTS_NAME.values())[EDITABLE_SLOT_RANGE[0]:EDITABLE_SLOT_RANGE[1]] + setslot_parser.add_argument("id") + setslot_parser.add_argument("type", choices=[ + "label", "ecckeylabel", "rsakeylabel", "url", "addchar1", "delay1", "username", + "addchar2", "delay2", "password", "addchar3", "delay3", "2fa", "totpkey", "addchar4", "addchar5" + ]) + setslot_parser.add_argument("value") + wipeslot_parser = subparsers.add_parser("wipeslot", help="wipe slot") + wipeslot_parser.add_argument("id") + wipekey_parser = subparsers.add_parser("wipekey", help="wipe key") + wipekey_parser.add_argument("key") + idletimeout_parser = subparsers.add_parser("idletimeout", help="OnlyKey locks after idletimeout is reached") + idletimeout_parser.add_argument("time", help="1 – 255 minutes; default = 30; 0 to disable") + keylayout_parser = subparsers.add_parser("keylayout", help="set keyboard layout") + keylayout_parser.add_argument("layout_number", choices=AVAILABLE_LAYOUTS.keys(), nargs="?", type=int) + keylayout_parser.add_argument("--list-all", action="store_true") + + for k in simple_commands: + new_parser = subparsers.add_parser(k, help=f'set {k}') + new_parser.add_argument("value") + + rng_parser = subparsers.add_parser("rng", help="generate rng content") + rng_parser.add_argument("--count", default=8) + rng_parser.add_argument("--serial") + rng_parser.add_argument("output-type", choices=["raw", "hexbytes", "feedkernel"]) + + ping_parser = subparsers.add_parser("ping", help="send ping") + ping_parser.add_argument("--ping-data", default="pong") + ping_parser.add_argument("--serial") + ping_parser.add_argument("--udp", type=bool) + + wink_parser = subparsers.add_parser("wink", help="send wink command to key (blinks LED a few times).") + wink_parser.add_argument("--serial") + wink_parser.add_argument("--udp", type=bool) + + setkey_parser = subparsers.add_parser("setkey", help="set key in a slot") + key_slot_names = list(SLOTS_NAME.values())[KEYS_SLOT_RANGE[0]:KEYS_SLOT_RANGE[1]] + setkey_parser.add_argument("slot", choices=key_slot_names) + setkey_parser.add_argument("keytype") + + init_parser = subparsers.add_parser("init", help="setup onlykey") + + cred_parser = subparsers.add_parser("credential", help="credential actions") + cred_parser.add_argument("--pin") + cred_parser.add_argument("--serial") + cred_parser.add_argument("--udp", type=bool) + cred_parser.add_argument("credential_id", default=argparse.SUPPRESS) + return parser + +def cli2(): + simple_commands = { + "idletimeout": make_simple_setslot(MessageField.IDLETIMEOUT), + "wipemode": make_simple_setslot(MessageField.WIPEMODE), + "keytypespeed": make_simple_setslot(MessageField.KEYTYPESPEED), + "ledbrightness": make_simple_setslot(MessageField.LEDBRIGHTNESS), + "storedkeymode": make_simple_setslot(MessageField.PGPCHALENGEMODE), + "derivedkeymode": make_simple_setslot(MessageField.SSHCHALENGEMODE), + "backupkeymode": make_simple_setslot(MessageField.BACKUPMODE), + "2ndprofilemode": make_simple_setslot(MessageField.SECPROFILEMODE), + "sysadminmode": make_simple_setslot(MessageField.SYSADMINMODE), + "lockbutton": make_simple_setslot(MessageField.LOCKBUTTON), + "hmackeymode": make_simple_setslot(MessageField.HMACMODE), + } + + available_commands = { + "init": lambda parser, args: init_onlykey(), + "version": lambda parser, args: print('OnlyKey CLI v1.2.3'), + "fwversion": command_fwversion, + "help": command_help, + "getlabels": command_getlabels, + "getkeylabels": command_getkeylabels, + "setslot": command_setslot, + "wipeslot": command_wipeslot, + "idletimeout": command_idletimeout, + "keylayout": command_keylayout, + "settime": command_settime, + "rng": command_rng, + "set-pin": command_set_pin, + "change-pin": command_change_pin, + "wink": command_wink, + "ping": command_ping, + "credential": command_credential, + "backupkey": lambda parser, args: only_key.generate_backup_key(), + "wipekey": lambda parser, args: only_key.wipekey(args.key), + "setkey": lambda parser, args: only_key.setkey(args.slot, args.keytype, prompt_pass()) + **simple_commands, + None: command_help, + } + parser = setup_argparse(simple_commands) logging.basicConfig(level=logging.DEBUG) @@ -59,690 +397,23 @@ def prompt_pin(): print('Press any key when finished entering PIN') return - if len(sys.argv) > 1: - if sys.argv[1] == 'settime': - only_key.set_time(time.time()) - print(only_key.read_string()) - elif sys.argv[1] == 'init': - while 1: - if only_key.read_string(timeout_ms=500) != 'UNINITIALIZED': - break - for msg in [Message.OKSETPIN]: - only_key.send_message(msg=msg) - print(only_key.read_string()) - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - print(only_key.read_string()) - only_key.send_message(msg=msg) - print(only_key.read_string()) - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - time.sleep(1.5) - print(only_key.read_string()) - print () - for msg in [Message.OKSETPDPIN]: - only_key.send_message(msg=msg) - print(only_key.read_string() + ' for second profile') - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - print(only_key.read_string() + ' for second profile') - only_key.send_message(msg=msg) - print(only_key.read_string()) - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - time.sleep(1.5) - print(only_key.read_string()) - print () - for msg in [Message.OKSETSDPIN]: - only_key.send_message(msg=msg) - print(only_key.read_string()) - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - print(only_key.read_string()) - only_key.send_message(msg=msg) - print(only_key.read_string()) - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - time.sleep(1.5) - print(only_key.read_string()) - print () - elif sys.argv[1] == 'getlabels': - tmp = {} - for slot in only_key.getlabels(): - tmp[slot.name] = slot - slots = iter(['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']) - for slot_name in slots: - print(tmp[slot_name].to_str().replace('ÿ'," ")) - print(tmp[next(slots)].to_str().replace('ÿ'," ")) - print() - elif sys.argv[1] == 'getkeylabels': - tmp = {} - for slot in only_key.getkeylabels(): - tmp[slot.name] = slot - slots = iter(['RSA Key 1', 'RSA Key 2', 'RSA Key 3', 'RSA Key 4', 'ECC Key 1', 'ECC Key 2', 'ECC Key 3', 'ECC Key 4', 'ECC Key 5', 'ECC Key 6', 'ECC Key 7', 'ECC Key 8', 'ECC Key 9', 'ECC Key 10', 'ECC Key 11', 'ECC Key 12', 'ECC Key 13', 'ECC Key 14', 'ECC Key 15', 'ECC Key 16']) - for slot_name in slots: - print(tmp[slot_name].to_str().replace('ÿ'," ")) - elif sys.argv[1] == 'setslot': - try: - if sys.argv[2] == '1a': - slot_id = 1 - elif sys.argv[2] == '2a': - slot_id = 2 - elif sys.argv[2] == '3a': - slot_id = 3 - elif sys.argv[2] == '4a': - slot_id = 4 - elif sys.argv[2] == '5a': - slot_id = 5 - elif sys.argv[2] == '6a': - slot_id = 6 - elif sys.argv[2] == '1b': - slot_id = 7 - elif sys.argv[2] == '2b': - slot_id = 8 - elif sys.argv[2] == '3b': - slot_id = 9 - elif sys.argv[2] == '4b': - slot_id = 10 - elif sys.argv[2] == '5b': - slot_id = 11 - elif sys.argv[2] == '6b': - slot_id = 12 - elif sys.argv[2] >= int('25'): - slot_id = int(sys.argv[2]) - except: - print("setslot [value]") - print(" must be slot number 1a - 6b") - return - - if sys.argv[3] == 'label': - only_key.setslot(slot_id, MessageField.LABEL, sys.argv[4]) - elif sys.argv[3] == 'ecckeylabel': - only_key.setslot(slot_id+28, MessageField.LABEL, sys.argv[4]) - elif sys.argv[3] == 'rsakeylabel': - only_key.setslot(slot_id+24, MessageField.LABEL, sys.argv[4]) - elif sys.argv[3] == 'url': - only_key.setslot(slot_id, MessageField.URL, sys.argv[4]) - elif sys.argv[3] == 'addchar2': - only_key.setslot(slot_id, MessageField.NEXTKEY1, sys.argv[4]) - elif sys.argv[3] == 'delay1': - only_key.setslot(slot_id, MessageField.DELAY1, sys.argv[4]) - elif sys.argv[3] == 'username': - only_key.setslot(slot_id, MessageField.USERNAME, sys.argv[4]) - elif sys.argv[3] == 'addchar3': - only_key.setslot(slot_id, MessageField.NEXTKEY2, sys.argv[4]) - elif sys.argv[3] == 'delay2': - only_key.setslot(slot_id, MessageField.DELAY2, sys.argv[4]) - elif sys.argv[3] == 'password': - password = prompt_pass() - only_key.setslot(slot_id, MessageField.PASSWORD, password) - elif sys.argv[3] == 'addchar5': - only_key.setslot(slot_id, MessageField.NEXTKEY3, sys.argv[4]) - elif sys.argv[3] == 'delay3': - only_key.setslot(slot_id, MessageField.DELAY3, sys.argv[4]) - elif sys.argv[3] == '2fa': - only_key.setslot(slot_id, MessageField.TFATYPE, sys.argv[4]) - elif sys.argv[3] == 'gkey': - totpkey = prompt_key() - totpkey = base64.b32decode("".join(totpkey.split()).upper()) - totpkey = binascii.hexlify(totpkey) - # pad with zeros for even digits - totpkey = totpkey.zfill(len(totpkey) + len(totpkey) % 2) - payload = [int(totpkey[i: i+2], 16) for i in range(0, len(totpkey), 2)] - only_key.setslot(slot_id, MessageField.TOTPKEY, payload) - elif sys.argv[3] == 'totpkey': - totpkey = prompt_key() - only_key.setslot(slot_id, MessageField.TOTPKEY, totpkey) - elif sys.argv[3] == 'addchar1': - only_key.setslot(slot_id, MessageField.NEXTKEY4, sys.argv[4]) - elif sys.argv[3] == 'addchar4': - only_key.setslot(slot_id, MessageField.NEXTKEY5, sys.argv[4]) - else: - print("setslot [value]") - print(" must be ['label', 'ecckeylabel', 'rsakeylabel', 'url', 'addchar1', 'delay1', 'username', 'addchar2', 'delay2', 'password', 'addchar3', 'delay3', '2fa', 'totpkey', 'addchar4', 'addchar5']") - return - elif sys.argv[1] == 'wipeslot': - try: - if sys.argv[2] == '1a': - slot_id = 1 - elif sys.argv[2] == '2a': - slot_id = 2 - elif sys.argv[2] == '3a': - slot_id = 3 - elif sys.argv[2] == '4a': - slot_id = 4 - elif sys.argv[2] == '5a': - slot_id = 5 - elif sys.argv[2] == '6a': - slot_id = 6 - elif sys.argv[2] == '1b': - slot_id = 7 - elif sys.argv[2] == '2b': - slot_id = 8 - elif sys.argv[2] == '3b': - slot_id = 9 - elif sys.argv[2] == '4b': - slot_id = 10 - elif sys.argv[2] == '5b': - slot_id = 11 - elif sys.argv[2] == '6b': - slot_id = 12 - elif sys.argv[2] >= int('25'): - slot_id = int(sys.argv[2]) + if len(sys.argv) == 1: + print('Control-D to exit.') + while True: + line = prompt('OnlyKey> ') + try: + args = parser.parse_args(line.split()) + available_commands[args.sub_command](parser, args) except: - print("wipeslot ") - print(" must be slot number 1a - 6b") - return - only_key.wipeslot(slot_id) - elif sys.argv[1] == 'backupkey': - only_key.generate_backup_key() - elif sys.argv[1] == 'setkey': - only_key.setkey(sys.argv[2], sys.argv[3], sys.argv[4]) - elif sys.argv[1] == 'wipekey': - only_key.wipekey(sys.argv[2]) - elif sys.argv[1] == 'idletimeout': - only_key.setslot(1, MessageField.IDLETIMEOUT, int(sys.argv[2])) - elif sys.argv[1] == 'wipemode': - only_key.setslot(1, MessageField.WIPEMODE, int(sys.argv[2])) - elif sys.argv[1] == 'keytypespeed': - only_key.setslot(1, MessageField.KEYTYPESPEED, int(sys.argv[2])) - elif sys.argv[1] == 'ledbrightness': - only_key.setslot(1, MessageField.LEDBRIGHTNESS, int(sys.argv[2])) - elif sys.argv[1] == '2ndprofilemode': - only_key.setslot(1, MessageField.SECPROFILEMODE, int(sys.argv[2])) - elif sys.argv[1] == 'storedkeymode': - only_key.setslot(1, MessageField.PGPCHALENGEMODE, int(sys.argv[2])) - elif sys.argv[1] == 'derivedkeymode': - only_key.setslot(1, MessageField.SSHCHALENGEMODE, int(sys.argv[2])) - elif sys.argv[1] == 'backupkeymode': - only_key.setslot(1, MessageField.BACKUPMODE, int(sys.argv[2])) - elif sys.argv[1] == 'keylayout': - only_key.setslot(1, MessageField.KEYLAYOUT, int(sys.argv[2])) - elif sys.argv[1] == 'sysadminmode': - only_key.setslot(1, MessageField.SYSADMINMODE, int(sys.argv[2])) - elif sys.argv[1] == 'lockbutton': - only_key.setslot(1, MessageField.LOCKBUTTON, int(sys.argv[2])) - elif sys.argv[1] == 'hmackeymode': - only_key.setslot(1, MessageField.HMACMODE, int(sys.argv[2])) - elif sys.argv[1] == 'version': - print('OnlyKey CLI v1.2.3') - elif sys.argv[1] == 'fwversion': - only_key.set_time(time.time()) - okversion = only_key.read_string() - print(okversion[8:]) - elif sys.argv[1] == 'change-pin': - if len(sys.argv) > 2: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - elif sys.argv[1] == 'credential': - if len(sys.argv) > 4 or len(sys.argv) < 3: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - if sys.argv[2] == 'info' or sys.argv[2] == 'ls' or sys.argv[2] == 'rm': - if len(sys.argv) == 4 and sys.argv[2] != 'rm': - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - if len(sys.argv) == 4 and sys.argv[3] == '--help': - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - else: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - elif sys.argv[1] == 'ping': - if len(sys.argv) > 2: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - elif sys.argv[1] == 'reset': - if len(sys.argv) > 2: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - elif sys.argv[1] == 'rng': - if len(sys.argv) > 5 or len(sys.argv) < 3: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - if len(sys.argv) > 2: - if sys.argv[2] != 'hexbytes' and sys.argv[2] != 'feedkernel': - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - if len(sys.argv) > 3: - if sys.argv[3] != '--count' or len(sys.argv) != 4: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - if len(sys.argv) == 4 and sys.argv[4] == '--help': - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - elif sys.argv[1] == 'set-pin': - if len(sys.argv) > 2: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - elif sys.argv[1] == 'wink': - if len(sys.argv) > 2: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - return - solo.cli.key() - elif sys.argv[1] == '--help': - print('See available command options here https://docs.crp.to/command-line.html') - return - elif sys.argv[1] == '-h': - print('See available command options here https://docs.crp.to/command-line.html') - return - elif sys.argv[1] == 'help': - print('See available command options here https://docs.crp.to/command-line.html') - return - elif sys.argv[1]: - print('Command not found. See available commands here https://docs.crp.to/command-line.html') - print() - - + print("An error occurred") else: + args = parser.parse_args() + available_commands[args.sub_command](parser, args) - # Print help. - print('OnlyKey CLI v1.2.3') - print('Control-D to exit.') - print() - - def mprompt(): - return prompt('OnlyKey> ') - - nexte = mprompt - - while 1: - sys.argv = [sys.argv[0]] - print() - raw = nexte() - print() - data = raw.split() - if not len(data): - data.append('NULL') - # nexte = prompt_pass - if data[0] == "settime": - only_key.set_time(time.time()) - print(only_key.read_string()) - elif data[0] == "init": - while 1: - if only_key.read_string(timeout_ms=500) != 'UNINITIALIZED': - break - for msg in [Message.OKSETPIN]: - only_key.send_message(msg=msg) - print(only_key.read_string()) - print() - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - print(only_key.read_string()) - only_key.send_message(msg=msg) - print(only_key.read_string()) - print() - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - time.sleep(1.5) - print(only_key.read_string()) - print() - for msg in [Message.OKSETPDPIN]: - only_key.send_message(msg=msg) - print(only_key.read_string() + ' for second profile') - print() - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - print(only_key.read_string() + ' for second profile') - only_key.send_message(msg=msg) - print(only_key.read_string()) - print () - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - time.sleep(1.5) - print(only_key.read_string()) - print() - for msg in [Message.OKSETSDPIN]: - only_key.send_message(msg=msg) - print(only_key.read_string()) - print() - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - print(only_key.read_string()) - only_key.send_message(msg=msg) - print(only_key.read_string()) - print() - input('Press the Enter key once you are done') - only_key.send_message(msg=msg) - time.sleep(1.5) - print(only_key.read_string()) - print() - elif data[0] == 'getlabels': - tmp = {} - for slot in only_key.getlabels(): - tmp[slot.name] = slot - slots = iter(['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']) - for slot_name in slots: - print(tmp[slot_name].to_str().replace('ÿ'," ")) - print(tmp[next(slots)].to_str().replace('ÿ'," ")) - print() - elif data[0] == 'getkeylabels': - tmp = {} - for slot in only_key.getkeylabels(): - tmp[slot.name] = slot - slots = iter(['RSA Key 1', 'RSA Key 2', 'RSA Key 3', 'RSA Key 4', 'ECC Key 1', 'ECC Key 2', 'ECC Key 3', 'ECC Key 4', 'ECC Key 5', 'ECC Key 6', 'ECC Key 7', 'ECC Key 8', 'ECC Key 9', 'ECC Key 10', 'ECC Key 11', 'ECC Key 12', 'ECC Key 13', 'ECC Key 14', 'ECC Key 15', 'ECC Key 16']) - for slot_name in slots: - print(tmp[slot_name].to_str().replace('ÿ'," ")) - elif data[0] == 'setslot': - try: - if data[1] == '1a': - slot_id = 1 - elif data[1] == '2a': - slot_id = 2 - elif data[1] == '3a': - slot_id = 3 - elif data[1] == '4a': - slot_id = 4 - elif data[1] == '5a': - slot_id = 5 - elif data[1] == '6a': - slot_id = 6 - elif data[1] == '1b': - slot_id = 7 - elif data[1] == '2b': - slot_id = 8 - elif data[1] == '3b': - slot_id = 9 - elif data[1] == '4b': - slot_id = 10 - elif data[1] == '5b': - slot_id = 11 - elif data[1] == '6b': - slot_id = 12 - elif data[1] >= int('25'): - slot_id = int(data[1]) - except: - print("setslot [value]") - print(" must be slot number 1a - 6b") - continue - if data[2] == 'label': - only_key.setslot(slot_id, MessageField.LABEL, data[3]) - elif data[2] == 'ecckeylabel': - only_key.setslot(slot_id+28, MessageField.LABEL, data[3]) - elif data[2] == 'rsakeylabel': - only_key.setslot(slot_id+24, MessageField.LABEL, data[3]) - elif data[2] == 'url': - only_key.setslot(slot_id, MessageField.URL, data[3]) - elif data[2] == 'addchar2': - only_key.setslot(slot_id, MessageField.NEXTKEY1, data[3]) - elif data[2] == 'delay1': - only_key.setslot(slot_id, MessageField.DELAY1, data[3]) - elif data[2] == 'username': - only_key.setslot(slot_id, MessageField.USERNAME, data[3]) - elif data[2] == 'addchar3': - only_key.setslot(slot_id, MessageField.NEXTKEY2, data[3]) - elif data[2] == 'delay2': - only_key.setslot(slot_id, MessageField.DELAY2, data[3]) - elif data[2] == 'password': - password = prompt_pass() - only_key.setslot(slot_id, MessageField.PASSWORD, password) - elif data[2] == 'addchar5': - only_key.setslot(slot_id, MessageField.NEXTKEY3, data[3]) - elif data[2] == 'delay3': - only_key.setslot(slot_id, MessageField.DELAY3, data[3]) - elif data[2] == '2fa': - only_key.setslot(slot_id, MessageField.TFATYPE, data[3]) - elif data[2] == 'gkey': - totpkey = prompt_key() - totpkey = base64.b32decode("".join(totpkey.split()).upper()) - totpkey = binascii.hexlify(totpkey) - # pad with zeros for even digits - totpkey = totpkey.zfill(len(totpkey) + len(totpkey) % 2) - payload = [int(totpkey[i: i+2], 16) for i in range(0, len(totpkey), 2)] - only_key.setslot(slot_id, MessageField.TOTPKEY, payload) - elif data[2] == 'totpkey': - totpkey = prompt_key() - only_key.setslot(slot_id, MessageField.TOTPKEY, totpkey) - elif data[2] == 'addchar1': - only_key.setslot(slot_id, MessageField.NEXTKEY3, data[3]) - elif data[2] == 'addchar4': - only_key.setslot(slot_id, MessageField.NEXTKEY3, data[3]) - else: - print("setslot [value]") - print(" must be ['label', 'ecckeylabel', 'rsakeylabel', 'url', 'addchar1', 'delay1', 'username', 'addchar2', 'delay2', 'password', 'addchar3', 'delay3', '2fa', 'totpkey', 'addchar4', 'addchar5']") - continue - elif data[0] == 'wipeslot': - try: - if data[1] == '1a': - slot_id = 1 - elif data[1] == '2a': - slot_id = 2 - elif data[1] == '3a': - slot_id = 3 - elif data[1] == '4a': - slot_id = 4 - elif data[1] == '5a': - slot_id = 5 - elif data[1] == '6a': - slot_id = 6 - elif data[1] == '1b': - slot_id = 7 - elif data[1] == '2b': - slot_id = 8 - elif data[1] == '3b': - slot_id = 9 - elif data[1] == '4b': - slot_id = 10 - elif data[1] == '5b': - slot_id = 11 - elif data[1] == '6b': - slot_id = 12 - elif data[1] >= int('25'): - slot_id = int(data[1]) - except: - print("wipeslot ") - print(" must be slot number 1a - 6b") - continue - only_key.wipeslot(slot_id) - elif data[0] == 'backupkey': - try: - only_key.generate_backup_key() - except: - continue - elif data[0] == 'setkey': - try: - key = prompt_pass() - only_key.setkey(data[1], data[2], key) - except: - continue - elif data[0] == 'wipekey': - try: - only_key.wipekey(data[1]) - except: - continue - elif data[0] == 'idletimeout': - try: - only_key.setslot(1, MessageField.IDLETIMEOUT, int(data[1])) - except: - continue - elif data[0] == 'wipemode': - try: - only_key.setslot(1, MessageField.WIPEMODE, int(data[1])) - except: - continue - elif data[0] == 'keytypespeed': - try: - only_key.setslot(1, MessageField.KEYTYPESPEED, int(data[1])) - except: - continue - elif data[0] == 'ledbrightness': - try: - only_key.setslot(1, MessageField.LEDBRIGHTNESS, int(data[1])) - except: - continue - elif data[0] == 'storedkeymode': - try: - only_key.setslot(1, MessageField.PGPCHALENGEMODE, int(data[1])) - except: - continue - elif data[0] == 'derivedkeymode': - try: - only_key.setslot(1, MessageField.SSHCHALENGEMODE, int(data[1])) - except: - continue - elif data[0] == 'backupkeymode': - try: - only_key.setslot(1, MessageField.BACKUPMODE, int(data[1])) - except: - continue - elif data[0] == '2ndprofilemode': - try: - only_key.setslot(1, MessageField.SECPROFILEMODE, int(data[1])) - except: - continue - elif data[0] == 'keylayout': - try: - only_key.setslot(1, MessageField.KEYLAYOUT, int(data[1])) - except: - continue - elif data[0] == 'sysadminmode': - try: - only_key.setslot(1, MessageField.SYSADMINMODE, int(data[1])) - except: - continue - elif data[0] == 'lockbutton': - try: - only_key.setslot(1, MessageField.LOCKBUTTON, int(data[1])) - except: - continue - elif data[0] == 'hmackeymode': - try: - only_key.setslot(1, MessageField.HMACMODE, int(data[1])) - except: - continue - elif data[0] == 'version': - try: - print('OnlyKey CLI v1.2.3') - except: - continue - elif data[0] == 'fwversion': - try: - only_key.set_time(time.time()) - okversion = only_key.read_string() - print(okversion[8:]) - except: - continue - elif data[0] == 'change-pin': - try: - sys.argv.append(data[0]) - if len(data) > 1: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - continue - solo.cli.key() - except: - continue - elif data[0] == 'credential': - try: - sys.argv.append(data[0]) - if len(data) > 3 or len(data) < 2: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - continue - if data[1] == 'info' or data[1] == 'ls' or data[1] == 'rm': - sys.argv.append(data[1]) - if len(data) == 3 and data[1] == 'rm' and data[2] != '--help': - sys.argv.append(data[2]) - solo.cli.key() - else: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - except: - continue - elif data[0] == 'ping': - try: - sys.argv.append(data[0]) - if len(data) > 1: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - continue - solo.cli.key() - except: - continue - elif data[0] == 'reset': - try: - sys.argv.append(data[0]) - if len(data) > 1: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - continue - solo.cli.key() - except: - continue - elif data[0] == 'rng': - try: - if len(data) > 4 or len(data) < 2: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - continue - sys.argv.append(data[0]) - sys.argv.append(data[1]) - if len(data) > 1: - if data[1] != 'hexbytes' and data[1] != 'feedkernel': - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - continue - if len(data) > 2: - sys.argv.append(data[2]) - if data[2] != '--count': - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - continue - if len(data) > 3: - sys.argv.append(data[3]) - solo.cli.key() - except: - continue - elif data[0] == 'set-pin': - try: - sys.argv.append(data[0]) - if len(data) > 1: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - continue - solo.cli.key() - except: - continue - elif data[0] == 'wink': - try: - sys.argv.append(data[0]) - if len(data) > 1: - print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') - continue - solo.cli.key() - except: - continue - elif data[0] == '--help': - try: - print('See available command options here https://docs.crp.to/command-line.html') - except: - continue - elif data[0] == '-h': - try: - print('See available command options here https://docs.crp.to/command-line.html') - except: - continue - elif data[0] == 'help': - try: - print('See available command options here https://docs.crp.to/command-line.html') - except: - continue - elif data[0] == 'exit': - return - elif data[0] == 'quit': - return - elif data[0]: - try: - print('Option not found. See available command options here https://docs.crp.to/command-line.html') - continue - except: - continue def main(): try: - cli() + cli2() except EOFError: only_key._hid.close() print() diff --git a/onlykey/client.py b/onlykey/client.py index e52b436..6b09d5c 100644 --- a/onlykey/client.py +++ b/onlykey/client.py @@ -85,6 +85,8 @@ 59: 'ECC Key 31', 60: 'ECC Key 32', } +EDITABLE_SLOT_RANGE = (0,12) +KEYS_SLOT_RANGE = (12,60) class Message(Enum):