From b964508cd1a8e3ec61b5f5eddfa79cf6c99bc8db Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Thu, 1 Jun 2017 19:28:36 +0200 Subject: First CLI implementation Signed-off-by: Szczepan Zalega --- unittest/libnitrokey.py | 232 ++++++++++++++++++++++++++++++++++++++++++++++++ unittest/setup.py | 15 ++++ 2 files changed, 247 insertions(+) create mode 100755 unittest/libnitrokey.py create mode 100644 unittest/setup.py diff --git a/unittest/libnitrokey.py b/unittest/libnitrokey.py new file mode 100755 index 0000000..f5a7f70 --- /dev/null +++ b/unittest/libnitrokey.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python + +""" +Use the following to run locally: + +$ sudo pip install virtualenv +$ virtualenv venv +$ . venv/bin/activate +$ pip install --editable . +$ libnitrokey --help +""" + +import click +import itertools +import cffi + +ffi = cffi.FFI() + +gs = ffi.string +device_type = None + + +def connect(): + fp = '../NK_C_API.h' + + declarations = [] + with open(fp, 'r') as f: + declarations = f.readlines() + + a = iter(declarations) + for declaration in a: + if declaration.startswith('NK_C_API'): + declaration = declaration.replace('NK_C_API', '').strip() + while not ';' in declaration: + declaration += (next(a)).strip() + ffi.cdef(declaration, override=True) + + C = None + import os, sys + + path_build = [".", os.path.join(".", "build"), os.path.join("..", "build")] + names = ["libnitrokey-log.so", "libnitrokey.so"] + paths = itertools.product(path_build, names) + tested = [] + for p in paths: + p = os.path.join(p[0], p[1]) + tested.append(p) + if os.path.exists(p): + C = ffi.dlopen(p) + break + if not C: + print("No library file found") + print("Tested paths:" + repr(tested)) + sys.exit(1) + + C.NK_set_debug(False) + + nk_login = C.NK_login_auto() + if nk_login != 1: + print('No devices detected!') + assert nk_login != 0 # returns 0 if not connected or wrong model or 1 when connected + global device_type + firmware_version = C.NK_get_major_firmware_version() + model = 'P' if firmware_version in [7, 8] else 'S' + device_type = (model, firmware_version) + + # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK + + # C.NK_status() + + C.NK_set_debug(True) + return C + + +C = connect() + + +@click.group() +@click.option('--debug', default=0, help='debug level') +def cli(debug): + C.NK_set_debug(debug != 0) + pass + + +@click.command() +def status(): + """Show device's status""" + print (gs(C.NK_status())) + if device_type[0] == 'S': + print (gs(C.NK_get_status_storage_as_string())) + return C.NK_get_last_command_status() + + +@click.command() +def device_serial_number(): + """Show device's serial number""" + print (gs(C.NK_device_serial_number())) + return C.NK_get_last_command_status() + + +def abort_callback(ctx, param, value): + if not value: + ctx.abort() + + +@click.command() +@click.option('--yes', is_flag=True, callback=abort_callback, + expose_value=False, prompt='Do you want to continue?') +def lock_device(): + """Lock the device - lock volumes and password safe""" + print C.NK_lock_device() or "Locked" + return C.NK_get_last_command_status() + + +# +# @click.command() +# def NK_first_authenticate(): +# """Show device status""" +# print C.NK_first_authenticate() +# return C.NK_get_last_command_status() +# +# +# @click.command() +# def NK_user_authenticate(): +# """Show device status""" +# print C.NK_user_authenticate() +# return C.NK_get_last_command_status() + + +# @click.command() +# def NK_factory_reset(): +# """Show device status""" +# print C.NK_factory_reset() +# return C.NK_get_last_command_status() +# +# +# @click.command() +# def NK_build_aes_key(): +# """Show device status""" +# print C.NK_build_aes_key() +# return C.NK_get_last_command_status() +# +# +# @click.command() +# @click.argument('slot_number') +# def NK_unlock_user_password(slot_number): +# """Show device status""" +# print C.NK_unlock_user_password() +# return C.NK_get_last_command_status() + + +@click.command() +@click.argument('slot_number') +def get_totp_slot_name(slot_number): + """Show TOTP slot name""" + print gs(C.NK_get_totp_slot_name(int(slot_number))) or "OTP slot not programmed" + return C.NK_get_last_command_status() + + +@click.command() +@click.argument('slot_number') +def get_hotp_slot_name(slot_number): + """Show HOTP slot name""" + print gs(C.NK_get_hotp_slot_name(int(slot_number))) or "OTP slot not programmed" + return C.NK_get_last_command_status() + + +# +# @click.command() +# def NK_erase_hotp_slot(): +# """Show device status""" +# print C.NK_erase_hotp_slot() +# return C.NK_get_last_command_status() +# +# +# @click.command() +# def NK_erase_totp_slot(): +# """NK_erase_totp_slot""" +# print C.NK_erase_totp_slot() +# return C.NK_get_last_command_status() + + +@click.command() +@click.argument('slot_number') +def get_hotp_code(slot_number): + """Get HOTP code""" + print gs(C.NK_get_hotp_code(int(slot_number))) or "OTP slot not programmed" + return C.NK_get_last_command_status() + + +@click.command() +@click.argument('slot_number') +def get_totp_code(slot_number): + """Get TOTP code""" + print gs(C.NK_get_totp_code(int(slot_number), 0, 0, 0)) or "OTP slot not programmed" + return C.NK_get_last_command_status() + + +@click.command() +@click.option('--user_pin', prompt=True, confirmation_prompt=True, + hide_input=True) +def unlock_encrypted_volume(user_pin): + """Unlock encrypted volume""" + print C.NK_unlock_encrypted_volume(str(user_pin)) or "Unlocked" + return C.NK_get_last_command_status() + + +@click.command() +@click.option('--hidden_volume_password', prompt=True, confirmation_prompt=True, + hide_input=True) +def unlock_hidden_volume(hidden_volume_password): + """Unlock hidden volume""" + print C.NK_unlock_hidden_volume(str(hidden_volume_password)) or "Unlocked" + return C.NK_get_last_command_status() + + +cli.add_command(status) +cli.add_command(device_serial_number) +cli.add_command(lock_device) +cli.add_command(unlock_encrypted_volume) +cli.add_command(unlock_hidden_volume) + +cli.add_command(get_hotp_code) +cli.add_command(get_totp_code) +cli.add_command(get_hotp_slot_name) +cli.add_command(get_totp_slot_name) + +if __name__ == '__main__': + cli() + C.NK_logout() + diff --git a/unittest/setup.py b/unittest/setup.py new file mode 100644 index 0000000..8d2fe9d --- /dev/null +++ b/unittest/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup + +setup( + name='libnitrokey', + version='0.1', + py_modules=['libnitrokey'], + install_requires=[ + 'Click', + 'cffi', + ], + entry_points=''' + [console_scripts] + libnitrokey=libnitrokey:cli + ''', +) \ No newline at end of file -- cgit v1.2.3