From 5f0d4ace04ec1c99e5e851cf79559c80c5f7f379 Mon Sep 17 00:00:00 2001 From: Sven Ebenfeld Date: Tue, 28 Apr 2020 15:03:33 +0200 Subject: add python_bindings_example for python3 Signed-off-by: Sven Ebenfeld --- python3_bindings_example.py | 157 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 python3_bindings_example.py diff --git a/python3_bindings_example.py b/python3_bindings_example.py new file mode 100644 index 0000000..fb24eff --- /dev/null +++ b/python3_bindings_example.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +""" +Copyright (c) 2015-2018 Nitrokey UG + +This file is part of libnitrokey. + +libnitrokey is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +any later version. + +libnitrokey is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libnitrokey. If not, see . + +SPDX-License-Identifier: LGPL-3.0 +""" + +import cffi +from enum import Enum + +""" +This example will print 10 HOTP codes from just written HOTP#2 slot. +For more examples of use please refer to unittest/test_*.py files. +""" + +ffi = cffi.FFI() +get_string = ffi.string + +class DeviceErrorCode(Enum): + STATUS_OK = 0 + NOT_PROGRAMMED = 3 + WRONG_PASSWORD = 4 + STATUS_NOT_AUTHORIZED = 5 + STATUS_AES_DEC_FAILED = 0xa + + +def get_library(): + fp = 'NK_C_API.h' # path to C API header + + declarations = [] + with open(fp, 'r') as f: + declarations = f.readlines() + + cnt = 0 + a = iter(declarations) + for declaration in a: + if declaration.strip().startswith('NK_C_API'): + declaration = declaration.replace('NK_C_API', '').strip() + while ';' not in declaration: + declaration += (next(a)).strip() + # print(declaration) + ffi.cdef(declaration, override=True) + cnt +=1 + print('Imported {} declarations'.format(cnt)) + + + C = None + import os, sys + path_build = os.path.join(".", "build") + paths = [ + os.environ.get('LIBNK_PATH', None), + os.path.join(path_build,"libnitrokey.so"), + os.path.join(path_build,"libnitrokey.dylib"), + os.path.join(path_build,"libnitrokey.dll"), + os.path.join(path_build,"nitrokey.dll"), + ] + for p in paths: + if not p: continue + print("Trying " +p) + p = os.path.abspath(p) + if os.path.exists(p): + print("Found: "+p) + C = ffi.dlopen(p) + break + else: + print("File does not exist: " + p) + if not C: + print("No library file found") + print("Please set the path using LIBNK_PATH environment variable to existing library or compile it (see " + "README.md for details)") + sys.exit(1) + + return C + + +def get_hotp_code(lib, i): + return get_string(lib.NK_get_hotp_code(i)) + +def to_hex(ss): + return ''.join([ format(ord(s),'02x') for s in ss ]) + +print('Warning!') +print('This example will change your configuration on inserted stick and overwrite your HOTP#2 slot.') +print('Please write "continue" to continue or any other string to quit') +a = input() + +if not a == 'continue': + exit() + +ADMIN = input('Please enter your admin PIN (empty string uses 12345678) ') +ADMIN = ADMIN or '12345678' # use default if empty string + +show_log = input('Should log messages be shown (please write "yes" to enable; this will make harder reading script output) ') == 'yes' +libnitrokey = get_library() + +if show_log: + log_level = input('Please select verbosity level (0-5, 2 is library default, 3 will be selected on empty input) ') + log_level = log_level or '3' + log_level = int(log_level) + libnitrokey.NK_set_debug_level(log_level) +else: + libnitrokey.NK_set_debug_level(2) + + +ADMIN_TEMP = '123123123' +RFC_SECRET = to_hex('12345678901234567890') + +# libnitrokey.NK_login('S') # connect only to Nitrokey Storage device +# libnitrokey.NK_login('P') # connect only to Nitrokey Pro device +device_connected = libnitrokey.NK_login_auto() # connect to any Nitrokey Stick +if device_connected: + print('Connected to Nitrokey device!') +else: + print('Could not connect to Nitrokey device!') + exit() + +use_8_digits = True +pin_correct = libnitrokey.NK_first_authenticate(ADMIN.encode('ascii'), ADMIN_TEMP.encode('ascii')) == DeviceErrorCode.STATUS_OK.value +if pin_correct: + print('Your PIN is correct!') +else: + print('Your PIN is not correct! Please try again. Please be careful to not lock your stick!') + retry_count_left = libnitrokey.NK_get_admin_retry_count() + print('Retry count left: %d' % retry_count_left ) + exit() + +# For function parameters documentation please check NK_C_API.h +assert libnitrokey.NK_write_config(255, 255, 255, False, True, ADMIN_TEMP.encode('ascii')) == DeviceErrorCode.STATUS_OK.value +libnitrokey.NK_first_authenticate(ADMIN.encode('ascii'), ADMIN_TEMP.encode('ascii')) +libnitrokey.NK_write_hotp_slot(1, 'python_test'.encode('ascii'), RFC_SECRET.encode('ascii'), 0, use_8_digits, False, False, "".encode('ascii'), + ADMIN_TEMP.encode('ascii')) +# RFC test according to: https://tools.ietf.org/html/rfc4226#page-32 +test_data = [ + 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871, + 645520489, +] +print('Getting HOTP code from Nitrokey Stick (RFC test, 8 digits): ') +for i in range(10): + hotp_slot_1_code = get_hotp_code(libnitrokey, 1) + correct_str = "correct!" if hotp_slot_1_code.decode('ascii') == str(test_data[i])[-8:] else "not correct" + print('%d: %s, should be %s -> %s' % (i, hotp_slot_1_code.decode('ascii'), str(test_data[i])[-8:], correct_str)) +libnitrokey.NK_logout() # disconnect device -- cgit v1.2.1 From df952bba8dac9b88f1c62efeb0bf7af9139e59bf Mon Sep 17 00:00:00 2001 From: Sven Ebenfeld Date: Tue, 28 Apr 2020 15:13:25 +0200 Subject: Add Python3 Example in README.md Signed-off-by: Sven Ebenfeld --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/README.md b/README.md index a3683c0..aa5ca1a 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ To use libnitrokey with Python a [CFFI](http://cffi.readthedocs.io/en/latest/ove pip install --user cffi # for python 2.x pip3 install cffi # for python 3.x ``` +## Python2 Just import it, read the C API header and it is done! You have access to the library. Here is an example (in Python 2) printing HOTP code for Pro or Storage device, assuming it is run in root directory [(full example)](python_bindings_example.py): ```python #!/usr/bin/env python2 @@ -158,6 +159,88 @@ print('Getting HOTP code from Nitrokey device: ') print(hotp_slot_code) libnitrokey.NK_logout() # disconnect device ``` +In case no devices are connected, a friendly message will be printed. +All available functions for C and Python are listed in [NK_C_API.h](NK_C_API.h). Please check `Documentation` section below. + +## Python3 +Just import it, read the C API header and it is done! You have access to the library. Here is an example (in Python 3) printing HOTP code for Pro or Storage device, assuming it is run in root directory [(full example)](python3_bindings_example.py): +```python +#!/usr/bin/env python3 +import cffi + +ffi = cffi.FFI() +get_string = ffi.string + +def get_library(): + fp = 'NK_C_API.h' # path to C API header + + declarations = [] + with open(fp, 'r') as f: + declarations = f.readlines() + + cnt = 0 + a = iter(declarations) + for declaration in a: + if declaration.strip().startswith('NK_C_API'): + declaration = declaration.replace('NK_C_API', '').strip() + while ';' not in declaration: + declaration += (next(a)).strip() + # print(declaration) + ffi.cdef(declaration, override=True) + cnt +=1 + print('Imported {} declarations'.format(cnt)) + + + C = None + import os, sys + path_build = os.path.join(".", "build") + paths = [ + os.environ.get('LIBNK_PATH', None), + os.path.join(path_build,"libnitrokey.so"), + os.path.join(path_build,"libnitrokey.dylib"), + os.path.join(path_build,"libnitrokey.dll"), + os.path.join(path_build,"nitrokey.dll"), + ] + for p in paths: + if not p: continue + print("Trying " +p) + p = os.path.abspath(p) + if os.path.exists(p): + print("Found: "+p) + C = ffi.dlopen(p) + break + else: + print("File does not exist: " + p) + if not C: + print("No library file found") + sys.exit(1) + + return C + + +def get_hotp_code(lib, i): + return lib.NK_get_hotp_code(i) + +def connect_device(lib): + # lib.NK_login('S'.encode('ascii')) # connect only to Nitrokey Storage device + # lib.NK_login('P'.encode('ascii')) # connect only to Nitrokey Pro device + device_connected = lib.NK_login_auto() # connect to any Nitrokey Stick + if device_connected: + print('Connected to Nitrokey device!') + else: + print('Could not connect to Nitrokey device!') + exit() + +libnitrokey = get_library() +libnitrokey.NK_set_debug(False) # do not show debug messages (log library only) + +connect_device(libnitrokey) + +hotp_slot_code = get_hotp_code(libnitrokey, 1) +print('Getting HOTP code from Nitrokey device: ') +print(ffi.string(hotp_slot_code).decode('ascii')) +libnitrokey.NK_logout() # disconnect device +``` In case no devices are connected, a friendly message will be printed. All available functions for C and Python are listed in [NK_C_API.h](NK_C_API.h). Please check `Documentation` section below. -- cgit v1.2.1