summaryrefslogtreecommitdiff
path: root/python3_bindings_example.py
blob: fb24eff11fe785f9e8408c3af13586e823d7086f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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 <http://www.gnu.org/licenses/>.

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