1 /*
2  * Copyright (c) 2015-2018 Nitrokey UG
3  *
4  * This file is part of libnitrokey.
5  *
6  * libnitrokey is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * any later version.
10  *
11  * libnitrokey is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * SPDX-License-Identifier: LGPL-3.0
20  */
21 
22 #include "NK_C_API.h"
23 #include <iostream>
24 #include <tuple>
25 #include "libnitrokey/NitrokeyManager.h"
26 #include <cstring>
27 #include "libnitrokey/LibraryException.h"
28 #include "libnitrokey/cxx_semantics.h"
29 #include "libnitrokey/stick20_commands.h"
30 #include "libnitrokey/device_proto.h"
31 #include "libnitrokey/version.h"
32 
33 #ifdef _MSC_VER
34 #ifdef _WIN32
35 #pragma message "Using own strndup"
strndup(const char * str,size_t maxlen)36 char * strndup(const char* str, size_t maxlen) {
37 	size_t len = strnlen(str, maxlen);
38 	char* dup = (char *)malloc(len + 1);
39 	memcpy(dup, str, len);
40 	dup[len] = 0;
41 	return dup;
42 }
43 #endif
44 #endif
45 
46 using namespace nitrokey;
47 
48 const uint8_t NK_PWS_SLOT_COUNT = PWS_SLOT_COUNT;
49 static uint8_t NK_last_command_status = 0;
50 static const int max_string_field_length = 100;
51 
52 template <typename T>
duplicate_vector_and_clear(std::vector<T> & v)53 T* duplicate_vector_and_clear(std::vector<T> &v){
54     auto d = new T[v.size()];
55     std::copy(v.begin(), v.end(), d);
56     std::fill(v.begin(), v.end(), 0);
57     return d;
58 }
59 
60 template <typename R, typename T>
get_with_status(T func,R fallback)61 std::tuple<int, R> get_with_status(T func, R fallback) {
62     NK_last_command_status = 0;
63     try {
64         return std::make_tuple(0, func());
65     }
66     catch (CommandFailedException & commandFailedException){
67         NK_last_command_status = commandFailedException.last_command_status;
68     }
69     catch (LibraryException & libraryException){
70         NK_last_command_status = libraryException.exception_id();
71     }
72     catch (const DeviceCommunicationException &deviceException){
73       NK_last_command_status = 256-deviceException.getType();
74     }
75     return std::make_tuple(NK_last_command_status, fallback);
76 }
77 
78 template <typename T>
get_with_array_result(T func)79 uint8_t * get_with_array_result(T func){
80     return std::get<1>(get_with_status<uint8_t*>(func, nullptr));
81 }
82 
83 template <typename T>
get_with_string_result(T func)84 char* get_with_string_result(T func){
85     auto result = std::get<1>(get_with_status<char*>(func, nullptr));
86     if (result == nullptr) {
87         return strndup("", MAXIMUM_STR_REPLY_LENGTH);
88     }
89     return result;
90 }
91 
92 template <typename T>
get_with_result(T func)93 auto get_with_result(T func){
94     return std::get<1>(get_with_status(func, static_cast<decltype(func())>(0)));
95 }
96 
97 template <typename T>
get_without_result(T func)98 uint8_t get_without_result(T func){
99     NK_last_command_status = 0;
100     try {
101         func();
102         return 0;
103     }
104     catch (CommandFailedException & commandFailedException){
105         NK_last_command_status = commandFailedException.last_command_status;
106     }
107     catch (LibraryException & libraryException){
108         NK_last_command_status = libraryException.exception_id();
109     }
110     catch (const InvalidCRCReceived &invalidCRCException){
111       ;
112     }
113     catch (const DeviceCommunicationException &deviceException){
114         NK_last_command_status = 256-deviceException.getType();
115     }
116     return NK_last_command_status;
117 }
118 
119 
120 #ifdef __cplusplus
121 extern "C" {
122 #endif
123 
NK_get_last_command_status()124 	NK_C_API uint8_t NK_get_last_command_status() {
125 		auto _copy = NK_last_command_status;
126 		NK_last_command_status = 0;
127 		return _copy;
128 	}
129 
NK_login(const char * device_model)130 	NK_C_API int NK_login(const char *device_model) {
131 		auto m = NitrokeyManager::instance();
132 		try {
133 			NK_last_command_status = 0;
134 			return m->connect(device_model);
135 		}
136 		catch (CommandFailedException & commandFailedException) {
137 			NK_last_command_status = commandFailedException.last_command_status;
138 			return commandFailedException.last_command_status;
139 		}
140     catch (const DeviceCommunicationException &deviceException){
141       NK_last_command_status = 256-deviceException.getType();
142       cerr << deviceException.what() << endl;
143       return 0;
144     }
145 		catch (std::runtime_error &e) {
146 			cerr << e.what() << endl;
147 			return 0;
148 		}
149 		return 0;
150 	}
151 
NK_login_enum(NK_device_model device_model)152         NK_C_API int NK_login_enum(NK_device_model device_model) {
153                 const char *model_string;
154                 switch (device_model) {
155                     case NK_PRO:
156                         model_string = "P";
157                         break;
158                     case NK_STORAGE:
159                         model_string = "S";
160                         break;
161                     case NK_LIBREM:
162                         model_string = "L";
163                         break;
164                     case NK_DISCONNECTED:
165                     default:
166                         /* no such enum value -- return error code */
167                         return 0;
168                 }
169                 return NK_login(model_string);
170         }
171 
NK_logout()172 	NK_C_API int NK_logout() {
173 		auto m = NitrokeyManager::instance();
174 		return get_without_result([&]() {
175 			m->disconnect();
176 		});
177 	}
178 
NK_first_authenticate(const char * admin_password,const char * admin_temporary_password)179 	NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password) {
180 		auto m = NitrokeyManager::instance();
181 		return get_without_result([&]() {
182 			return m->first_authenticate(admin_password, admin_temporary_password);
183 		});
184 	}
185 
186 
NK_user_authenticate(const char * user_password,const char * user_temporary_password)187 	NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password) {
188 		auto m = NitrokeyManager::instance();
189 		return get_without_result([&]() {
190 			m->user_authenticate(user_password, user_temporary_password);
191 		});
192 	}
193 
NK_factory_reset(const char * admin_password)194 	NK_C_API int NK_factory_reset(const char* admin_password) {
195 		auto m = NitrokeyManager::instance();
196 		return get_without_result([&]() {
197 			m->factory_reset(admin_password);
198 		});
199 	}
NK_build_aes_key(const char * admin_password)200 	NK_C_API int NK_build_aes_key(const char* admin_password) {
201 		auto m = NitrokeyManager::instance();
202 		return get_without_result([&]() {
203 			m->build_aes_key(admin_password);
204 		});
205 	}
206 
NK_unlock_user_password(const char * admin_password,const char * new_user_password)207 	NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) {
208 		auto m = NitrokeyManager::instance();
209 		return get_without_result([&]() {
210 			m->unlock_user_password(admin_password, new_user_password);
211 		});
212 	}
213 
NK_write_config(uint8_t numlock,uint8_t capslock,uint8_t scrolllock,bool enable_user_password,bool delete_user_password,const char * admin_temporary_password)214 	NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
215 		bool delete_user_password,
216 		const char *admin_temporary_password) {
217 		auto m = NitrokeyManager::instance();
218 		return get_without_result([&]() {
219 			return m->write_config(numlock, capslock, scrolllock, enable_user_password, delete_user_password, admin_temporary_password);
220 		});
221 	}
222 
NK_write_config_struct(struct NK_config config,const char * admin_temporary_password)223 	NK_C_API int NK_write_config_struct(struct NK_config config,
224 		const char *admin_temporary_password) {
225                 return NK_write_config(config.numlock, config.capslock, config.scrolllock, config.enable_user_password,
226                     config.disable_user_password, admin_temporary_password);
227         }
228 
229 
NK_read_config()230 	NK_C_API uint8_t* NK_read_config() {
231 		auto m = NitrokeyManager::instance();
232 		return get_with_array_result([&]() {
233 			auto v = m->read_config();
234 			return duplicate_vector_and_clear(v);
235 		});
236 	}
237 
NK_free_config(uint8_t * config)238         NK_C_API void NK_free_config(uint8_t* config) {
239                 delete[] config;
240         }
241 
NK_read_config_struct(struct NK_config * out)242 	NK_C_API int NK_read_config_struct(struct NK_config* out) {
243 		if (out == nullptr) {
244 			return -1;
245 		}
246 		auto m = NitrokeyManager::instance();
247 		return get_without_result([&]() {
248 			auto v = m->read_config();
249                         out->numlock = v[0];
250                         out->capslock = v[1];
251                         out->scrolllock = v[2];
252                         out->enable_user_password = v[3];
253                         out->disable_user_password = v[4];
254 		});
255 	}
256 
257 
NK_get_device_model()258 	NK_C_API enum NK_device_model NK_get_device_model() {
259 		auto m = NitrokeyManager::instance();
260 		try {
261 			auto model = m->get_connected_device_model();
262 			switch (model) {
263 				case DeviceModel::PRO:
264 				    return NK_PRO;
265 				case DeviceModel::STORAGE:
266 				    return NK_STORAGE;
267 				case DeviceModel::LIBREM:
268 				    return NK_LIBREM;
269 				default:
270 				    /* unknown or not connected device */
271 				    return NK_device_model::NK_DISCONNECTED;
272 			}
273 		} catch (const DeviceNotConnected& e) {
274 			return NK_device_model::NK_DISCONNECTED;
275 		}
276 }
277 
278 
clear_string(std::string & s)279 	void clear_string(std::string &s) {
280 		std::fill(s.begin(), s.end(), ' ');
281 	}
282 
283 
NK_status()284 	NK_C_API char * NK_status() {
285 		return NK_get_status_as_string();
286 	}
287 
NK_get_status_as_string()288 	NK_C_API char * NK_get_status_as_string() {
289 		auto m = NitrokeyManager::instance();
290 		return get_with_string_result([&]() {
291 			string && s = m->get_status_as_string();
292 			char * rs = strndup(s.c_str(), MAXIMUM_STR_REPLY_LENGTH);
293 			clear_string(s);
294 			return rs;
295 		});
296 	}
297 
NK_get_status(struct NK_status * out)298 	NK_C_API int NK_get_status(struct NK_status* out) {
299 		if (out == nullptr) {
300 			return -1;
301 		}
302 		auto m = NitrokeyManager::instance();
303 		auto result = get_with_status([&]() {
304 			return m->get_status();
305 		}, proto::stick10::GetStatus::ResponsePayload());
306 		auto error_code = std::get<0>(result);
307 		if (error_code != 0) {
308 			return error_code;
309 		}
310 
311 		auto status = std::get<1>(result);
312 		out->firmware_version_major = status.firmware_version_st.major;
313 		out->firmware_version_minor = status.firmware_version_st.minor;
314 		out->serial_number_smart_card = status.card_serial_u32;
315 		out->config_numlock = status.numlock;
316 		out->config_capslock = status.capslock;
317 		out->config_scrolllock = status.scrolllock;
318 		out->otp_user_password = status.enable_user_password != 0;
319 		return 0;
320 	}
321 
NK_device_serial_number()322 	NK_C_API char * NK_device_serial_number() {
323 		auto m = NitrokeyManager::instance();
324 		return get_with_string_result([&]() {
325 			string && s = m->get_serial_number();
326 			char * rs = strndup(s.c_str(), max_string_field_length);
327 			clear_string(s);
328 			return rs;
329 		});
330 	}
331 
NK_device_serial_number_as_u32()332 	NK_C_API uint32_t NK_device_serial_number_as_u32() {
333 		auto m = NitrokeyManager::instance();
334 		return get_with_result([&]() {
335                         return m->get_serial_number_as_u32();
336 		});
337 	}
338 
NK_get_hotp_code(uint8_t slot_number)339 	NK_C_API char * NK_get_hotp_code(uint8_t slot_number) {
340 		return NK_get_hotp_code_PIN(slot_number, "");
341 	}
342 
NK_get_hotp_code_PIN(uint8_t slot_number,const char * user_temporary_password)343 	NK_C_API char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password) {
344 		auto m = NitrokeyManager::instance();
345 		return get_with_string_result([&]() {
346 			string && s = m->get_HOTP_code(slot_number, user_temporary_password);
347 			char * rs = strndup(s.c_str(), max_string_field_length);
348 			clear_string(s);
349 			return rs;
350 		});
351 	}
352 
NK_get_totp_code(uint8_t slot_number,uint64_t challenge,uint64_t last_totp_time,uint8_t last_interval)353 	NK_C_API char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
354 		uint8_t last_interval) {
355 		return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, "");
356 	}
357 
NK_get_totp_code_PIN(uint8_t slot_number,uint64_t challenge,uint64_t last_totp_time,uint8_t last_interval,const char * user_temporary_password)358 	NK_C_API char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
359 		uint8_t last_interval, const char *user_temporary_password) {
360 		auto m = NitrokeyManager::instance();
361 		return get_with_string_result([&]() {
362 			string && s = m->get_TOTP_code(slot_number, challenge, last_totp_time, last_interval, user_temporary_password);
363 			char * rs = strndup(s.c_str(), max_string_field_length);
364 			clear_string(s);
365 			return rs;
366 		});
367 	}
368 
NK_erase_hotp_slot(uint8_t slot_number,const char * temporary_password)369 	NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) {
370 		auto m = NitrokeyManager::instance();
371 		return get_without_result([&] {
372 			m->erase_hotp_slot(slot_number, temporary_password);
373 		});
374 	}
375 
NK_erase_totp_slot(uint8_t slot_number,const char * temporary_password)376 	NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) {
377 		auto m = NitrokeyManager::instance();
378 		return get_without_result([&] {
379 			m->erase_totp_slot(slot_number, temporary_password);
380 		});
381 	}
382 
NK_write_hotp_slot(uint8_t slot_number,const char * slot_name,const char * secret,uint64_t hotp_counter,bool use_8_digits,bool use_enter,bool use_tokenID,const char * token_ID,const char * temporary_password)383 	NK_C_API int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
384 		bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
385 		const char *temporary_password) {
386 		auto m = NitrokeyManager::instance();
387 		return get_without_result([&] {
388 			m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, token_ID,
389 				temporary_password);
390 		});
391 	}
392 
NK_write_totp_slot(uint8_t slot_number,const char * slot_name,const char * secret,uint16_t time_window,bool use_8_digits,bool use_enter,bool use_tokenID,const char * token_ID,const char * temporary_password)393 	NK_C_API int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
394 		bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
395 		const char *temporary_password) {
396 		auto m = NitrokeyManager::instance();
397 		return get_without_result([&] {
398 			m->write_TOTP_slot(slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, token_ID,
399 				temporary_password);
400 		});
401 	}
402 
NK_get_totp_slot_name(uint8_t slot_number)403 	NK_C_API char* NK_get_totp_slot_name(uint8_t slot_number) {
404 		auto m = NitrokeyManager::instance();
405 		return get_with_string_result([&]() {
406 			const auto slot_name = m->get_totp_slot_name(slot_number);
407 			return slot_name;
408 		});
409 	}
NK_get_hotp_slot_name(uint8_t slot_number)410 	NK_C_API char* NK_get_hotp_slot_name(uint8_t slot_number) {
411 		auto m = NitrokeyManager::instance();
412 		return get_with_string_result([&]() {
413 			const auto slot_name = m->get_hotp_slot_name(slot_number);
414 			return slot_name;
415 		});
416 	}
417 
NK_set_debug(bool state)418 	NK_C_API void NK_set_debug(bool state) {
419 		auto m = NitrokeyManager::instance();
420 		m->set_debug(state);
421 	}
422 
423 
NK_set_debug_level(const int level)424 	NK_C_API void NK_set_debug_level(const int level) {
425 		auto m = NitrokeyManager::instance();
426 		m->set_loglevel(level);
427 	}
428 
NK_get_major_library_version()429 	NK_C_API unsigned int NK_get_major_library_version() {
430 		return get_major_library_version();
431 	}
432 
NK_get_minor_library_version()433 	NK_C_API unsigned int NK_get_minor_library_version() {
434 		return get_minor_library_version();
435 	}
436 
NK_get_library_version()437 	NK_C_API const char* NK_get_library_version() {
438 		return get_library_version();
439 	}
440 
NK_totp_set_time(uint64_t time)441 	NK_C_API int NK_totp_set_time(uint64_t time) {
442 		auto m = NitrokeyManager::instance();
443 		return get_without_result([&]() {
444 			m->set_time(time);
445 		});
446 	}
447 
NK_totp_set_time_soft(uint64_t time)448 	NK_C_API int NK_totp_set_time_soft(uint64_t time) {
449 		auto m = NitrokeyManager::instance();
450 		return get_without_result([&]() {
451 			m->set_time_soft(time);
452 		});
453         }
454 
NK_totp_get_time()455 	NK_C_API int NK_totp_get_time() {
456 	  return 0;
457 	}
458 
NK_change_admin_PIN(const char * current_PIN,const char * new_PIN)459 	NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN) {
460 		auto m = NitrokeyManager::instance();
461 		return get_without_result([&]() {
462 			m->change_admin_PIN(current_PIN, new_PIN);
463 		});
464 	}
465 
NK_change_user_PIN(const char * current_PIN,const char * new_PIN)466 	NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN) {
467 		auto m = NitrokeyManager::instance();
468 		return get_without_result([&]() {
469 			m->change_user_PIN(current_PIN, new_PIN);
470 		});
471 	}
472 
NK_enable_password_safe(const char * user_pin)473 	NK_C_API int NK_enable_password_safe(const char *user_pin) {
474 		auto m = NitrokeyManager::instance();
475 		return get_without_result([&]() {
476 			m->enable_password_safe(user_pin);
477 		});
478 	}
NK_get_password_safe_slot_status()479 	NK_C_API uint8_t * NK_get_password_safe_slot_status() {
480 		auto m = NitrokeyManager::instance();
481 		return get_with_array_result([&]() {
482 			auto slot_status = m->get_password_safe_slot_status();
483 			return duplicate_vector_and_clear(slot_status);
484 		});
485 
486 	}
487 
NK_free_password_safe_slot_status(uint8_t * status)488         NK_C_API void NK_free_password_safe_slot_status(uint8_t* status) {
489                 delete[] status;
490         }
491 
NK_get_user_retry_count()492 	NK_C_API uint8_t NK_get_user_retry_count() {
493 		auto m = NitrokeyManager::instance();
494 		return get_with_result([&]() {
495 			return m->get_user_retry_count();
496 		});
497 	}
498 
NK_get_admin_retry_count()499 	NK_C_API uint8_t NK_get_admin_retry_count() {
500 		auto m = NitrokeyManager::instance();
501 		return get_with_result([&]() {
502 			return m->get_admin_retry_count();
503 		});
504 	}
505 
NK_lock_device()506 	NK_C_API int NK_lock_device() {
507 		auto m = NitrokeyManager::instance();
508 		return get_without_result([&]() {
509 			m->lock_device();
510 		});
511 	}
512 
NK_get_password_safe_slot_name(uint8_t slot_number)513 	NK_C_API char *NK_get_password_safe_slot_name(uint8_t slot_number) {
514 		auto m = NitrokeyManager::instance();
515 		return get_with_string_result([&]() {
516 			return m->get_password_safe_slot_name(slot_number);
517 		});
518 	}
519 
NK_get_password_safe_slot_login(uint8_t slot_number)520 	NK_C_API char *NK_get_password_safe_slot_login(uint8_t slot_number) {
521 		auto m = NitrokeyManager::instance();
522 		return get_with_string_result([&]() {
523 			return m->get_password_safe_slot_login(slot_number);
524 		});
525 	}
NK_get_password_safe_slot_password(uint8_t slot_number)526 	NK_C_API char *NK_get_password_safe_slot_password(uint8_t slot_number) {
527 		auto m = NitrokeyManager::instance();
528 		return get_with_string_result([&]() {
529 			return m->get_password_safe_slot_password(slot_number);
530 		});
531 	}
NK_write_password_safe_slot(uint8_t slot_number,const char * slot_name,const char * slot_login,const char * slot_password)532 	NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
533 		const char *slot_password) {
534 		auto m = NitrokeyManager::instance();
535 		return get_without_result([&]() {
536 			m->write_password_safe_slot(slot_number, slot_name, slot_login, slot_password);
537 		});
538 	}
539 
NK_erase_password_safe_slot(uint8_t slot_number)540 	NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) {
541 		auto m = NitrokeyManager::instance();
542 		return get_without_result([&]() {
543 			m->erase_password_safe_slot(slot_number);
544 		});
545 	}
546 
NK_is_AES_supported(const char * user_password)547 	NK_C_API int NK_is_AES_supported(const char *user_password) {
548 		auto m = NitrokeyManager::instance();
549 		return get_with_result([&]() {
550 			return (uint8_t)m->is_AES_supported(user_password);
551 		});
552 	}
553 
NK_login_auto()554 	NK_C_API int NK_login_auto() {
555 		auto m = NitrokeyManager::instance();
556 		return get_with_result([&]() {
557 			return (uint8_t)m->connect();
558 		});
559 	}
560 
561 	// storage commands
562 
NK_send_startup(uint64_t seconds_from_epoch)563 	NK_C_API int NK_send_startup(uint64_t seconds_from_epoch) {
564 		auto m = NitrokeyManager::instance();
565 		return get_without_result([&]() {
566 			m->send_startup(seconds_from_epoch);
567 		});
568 	}
569 
NK_unlock_encrypted_volume(const char * user_pin)570 	NK_C_API int NK_unlock_encrypted_volume(const char* user_pin) {
571 		auto m = NitrokeyManager::instance();
572 		return get_without_result([&]() {
573 			m->unlock_encrypted_volume(user_pin);
574 		});
575 	}
576 
NK_lock_encrypted_volume()577 	NK_C_API int NK_lock_encrypted_volume() {
578 		auto m = NitrokeyManager::instance();
579 		return get_without_result([&]() {
580 			m->lock_encrypted_volume();
581 		});
582 	}
583 
NK_unlock_hidden_volume(const char * hidden_volume_password)584 	NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password) {
585 		auto m = NitrokeyManager::instance();
586 		return get_without_result([&]() {
587 			m->unlock_hidden_volume(hidden_volume_password);
588 		});
589 	}
590 
NK_lock_hidden_volume()591 	NK_C_API int NK_lock_hidden_volume() {
592 		auto m = NitrokeyManager::instance();
593 		return get_without_result([&]() {
594 			m->lock_hidden_volume();
595 		});
596 	}
597 
NK_create_hidden_volume(uint8_t slot_nr,uint8_t start_percent,uint8_t end_percent,const char * hidden_volume_password)598 	NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
599 		const char *hidden_volume_password) {
600 		auto m = NitrokeyManager::instance();
601 		return get_without_result([&]() {
602 			m->create_hidden_volume(slot_nr, start_percent, end_percent,
603 				hidden_volume_password);
604 		});
605 	}
606 
607   // deprecated, noop on v0.51 and older (excl. v0.49)
608   #pragma GCC diagnostic push
609   #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
NK_set_unencrypted_read_only(const char * user_pin)610 	NK_C_API int NK_set_unencrypted_read_only(const char *user_pin) {
611 		auto m = NitrokeyManager::instance();
612 		return get_without_result([&]() {
613 			m->set_unencrypted_read_only(user_pin);
614 		});
615 	}
616 
617 	// deprecated, noop on v0.51 and older (excl. v0.49)
NK_set_unencrypted_read_write(const char * user_pin)618   NK_C_API int NK_set_unencrypted_read_write(const char *user_pin) {
619 		auto m = NitrokeyManager::instance();
620 		return get_without_result([&]() {
621 			m->set_unencrypted_read_write(user_pin);
622 		});
623 	}
624 #pragma GCC diagnostic pop
625 
NK_set_unencrypted_read_only_admin(const char * admin_pin)626 	NK_C_API int NK_set_unencrypted_read_only_admin(const char *admin_pin) {
627 		auto m = NitrokeyManager::instance();
628 		return get_without_result([&]() {
629 			m->set_unencrypted_read_only_admin(admin_pin);
630 		});
631 	}
632 
NK_set_unencrypted_read_write_admin(const char * admin_pin)633 	NK_C_API int NK_set_unencrypted_read_write_admin(const char *admin_pin) {
634 		auto m = NitrokeyManager::instance();
635 		return get_without_result([&]() {
636 			m->set_unencrypted_read_write_admin(admin_pin);
637 		});
638 	}
639 
NK_set_encrypted_read_only(const char * admin_pin)640 	NK_C_API int NK_set_encrypted_read_only(const char* admin_pin) {
641 		auto m = NitrokeyManager::instance();
642 		return get_without_result([&]() {
643 			m->set_encrypted_volume_read_only(admin_pin);
644 		});
645 	}
646 
NK_set_encrypted_read_write(const char * admin_pin)647 	NK_C_API int NK_set_encrypted_read_write(const char* admin_pin) {
648 		auto m = NitrokeyManager::instance();
649 		return get_without_result([&]() {
650 			m->set_encrypted_volume_read_write(admin_pin);
651 		});
652 	}
653 
NK_export_firmware(const char * admin_pin)654 	NK_C_API int NK_export_firmware(const char* admin_pin) {
655 		auto m = NitrokeyManager::instance();
656 		return get_without_result([&]() {
657 			m->export_firmware(admin_pin);
658 		});
659 	}
660 
NK_clear_new_sd_card_warning(const char * admin_pin)661 	NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) {
662 		auto m = NitrokeyManager::instance();
663 		return get_without_result([&]() {
664 			m->clear_new_sd_card_warning(admin_pin);
665 		});
666 	}
667 
NK_fill_SD_card_with_random_data(const char * admin_pin)668 	NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) {
669 		auto m = NitrokeyManager::instance();
670 		return get_without_result([&]() {
671 			m->fill_SD_card_with_random_data(admin_pin);
672 		});
673 	}
674 
NK_change_update_password(const char * current_update_password,const char * new_update_password)675 	NK_C_API int NK_change_update_password(const char* current_update_password,
676 		const char* new_update_password) {
677 		auto m = NitrokeyManager::instance();
678 		return get_without_result([&]() {
679 			m->change_update_password(current_update_password, new_update_password);
680 		});
681 	}
682 
NK_enable_firmware_update(const char * update_password)683 	NK_C_API int NK_enable_firmware_update(const char* update_password){
684 		auto m = NitrokeyManager::instance();
685 		return get_without_result([&]() {
686 			m->enable_firmware_update(update_password);
687 		});
688 	}
689 
NK_get_status_storage_as_string()690 	NK_C_API char* NK_get_status_storage_as_string() {
691 		auto m = NitrokeyManager::instance();
692 		return get_with_string_result([&]() {
693 			return m->get_status_storage_as_string();
694 		});
695 	}
696 
NK_get_status_storage(NK_storage_status * out)697 	NK_C_API int NK_get_status_storage(NK_storage_status* out) {
698 		if (out == nullptr) {
699 			return -1;
700 		}
701 		auto m = NitrokeyManager::instance();
702 		auto result = get_with_status([&]() {
703 			return m->get_status_storage();
704 		}, proto::stick20::DeviceConfigurationResponsePacket::ResponsePayload());
705 		auto error_code = std::get<0>(result);
706 		if (error_code != 0) {
707 			return error_code;
708 		}
709 
710 		auto status = std::get<1>(result);
711 		out->unencrypted_volume_read_only = status.ReadWriteFlagUncryptedVolume_u8 != 0;
712 		out->unencrypted_volume_active = status.VolumeActiceFlag_st.unencrypted;
713 		out->encrypted_volume_read_only = status.ReadWriteFlagCryptedVolume_u8 != 0;
714 		out->encrypted_volume_active = status.VolumeActiceFlag_st.encrypted;
715 		out->hidden_volume_read_only = status.ReadWriteFlagHiddenVolume_u8 != 0;
716 		out->hidden_volume_active = status.VolumeActiceFlag_st.hidden;
717 		out->firmware_version_major = status.versionInfo.major;
718 		out->firmware_version_minor = status.versionInfo.minor;
719 		out->firmware_locked = status.FirmwareLocked_u8 != 0;
720 		out->serial_number_sd_card = status.ActiveSD_CardID_u32;
721 		out->serial_number_smart_card = status.ActiveSmartCardID_u32;
722 		out->user_retry_count = status.UserPwRetryCount;
723 		out->admin_retry_count = status.AdminPwRetryCount;
724 		out->new_sd_card_found = status.NewSDCardFound_st.NewCard;
725 		out->filled_with_random = (status.SDFillWithRandomChars_u8 & 0x01) != 0;
726 		out->stick_initialized = status.StickKeysNotInitiated == 0;
727 		return 0;
728 	}
729 
NK_get_storage_production_info(NK_storage_ProductionTest * out)730   NK_C_API int NK_get_storage_production_info(NK_storage_ProductionTest * out){
731     if (out == nullptr) {
732       return -1;
733     }
734     auto m = NitrokeyManager::instance();
735     auto result = get_with_status([&]() {
736       return m->production_info();
737     }, proto::stick20::ProductionTest::ResponsePayload());
738 
739 		auto error_code = std::get<0>(result);
740 		if (error_code != 0) {
741 			return error_code;
742 		}
743 
744 		stick20::ProductionTest::ResponsePayload status = std::get<1>(result);
745 		// Cannot use memcpy without declaring C API struct packed
746     // (which is not parsed by Python's CFFI apparently), hence the manual way.
747 #define a(x) out->x = status.x;
748 		 a(FirmwareVersion_au8[0]);
749 		 a(FirmwareVersion_au8[1]);
750 		 a(FirmwareVersionInternal_u8);
751 		 a(SD_Card_Size_u8);
752 		 a(CPU_CardID_u32);
753 		 a(SmartCardID_u32);
754 		 a(SD_CardID_u32);
755 		 a(SC_UserPwRetryCount);
756 		 a(SC_AdminPwRetryCount);
757 		 a(SD_Card_ManufacturingYear_u8);
758 		 a(SD_Card_ManufacturingMonth_u8);
759 		 a(SD_Card_OEM_u16);
760 		 a(SD_WriteSpeed_u16);
761 		 a(SD_Card_Manufacturer_u8);
762 #undef a
763 		return 0;
764   }
765 
NK_get_SD_usage_data(struct NK_SD_usage_data * out)766 	NK_C_API int NK_get_SD_usage_data(struct NK_SD_usage_data* out) {
767 		if (out == nullptr)
768 			return -1;
769 		auto m = NitrokeyManager::instance();
770 		auto result = get_with_status([&]() {
771 			return m->get_SD_usage_data();
772 		}, std::make_pair<uint8_t, uint8_t>(0, 0));
773 		auto error_code = std::get<0>(result);
774 		if (error_code != 0)
775 			return error_code;
776 
777 		auto data = std::get<1>(result);
778 		out->write_level_min = std::get<0>(data);
779 		out->write_level_max = std::get<1>(data);
780 
781 		return 0;
782 	}
783 
NK_get_SD_usage_data_as_string()784 NK_C_API char* NK_get_SD_usage_data_as_string() {
785 		auto m = NitrokeyManager::instance();
786 		return get_with_string_result([&]() {
787 			return m->get_SD_usage_data_as_string();
788 		});
789 	}
790 
NK_get_progress_bar_value()791 	NK_C_API int NK_get_progress_bar_value() {
792 		auto m = NitrokeyManager::instance();
793 		return std::get<1>(get_with_status([&]() {
794 			return m->get_progress_bar_value();
795 		}, -2));
796 	}
797 
NK_get_major_firmware_version()798 	NK_C_API uint8_t NK_get_major_firmware_version() {
799 		auto m = NitrokeyManager::instance();
800 		return get_with_result([&]() {
801 			return m->get_major_firmware_version();
802 		});
803 	}
804 
NK_get_minor_firmware_version()805   NK_C_API uint8_t NK_get_minor_firmware_version() {
806 		auto m = NitrokeyManager::instance();
807 		return get_with_result([&]() {
808 			return m->get_minor_firmware_version();
809 		});
810 	}
811 
NK_set_unencrypted_volume_rorw_pin_type_user()812   NK_C_API int NK_set_unencrypted_volume_rorw_pin_type_user() {
813 		auto m = NitrokeyManager::instance();
814 		return get_with_result([&]() {
815 			return m->set_unencrypted_volume_rorw_pin_type_user() ? 1 : 0;
816 		});
817 	}
818 
NK_list_devices_by_cpuID()819 	NK_C_API char* NK_list_devices_by_cpuID() {
820 		auto nm = NitrokeyManager::instance();
821 		return get_with_string_result([&]() {
822 			auto v = nm->list_devices_by_cpuID();
823 			std::string res;
824 			for (const auto a : v){
825 				res += a+";";
826 			}
827 			if (res.size()>0) res.pop_back(); // remove last delimiter char
828 			return strndup(res.c_str(), MAXIMUM_STR_REPLY_LENGTH);
829 		});
830 	}
831 
copy_device_info(const DeviceInfo & source,NK_device_info * target)832 	bool copy_device_info(const DeviceInfo& source, NK_device_info* target) {
833 		switch (source.m_deviceModel) {
834 		case DeviceModel::PRO:
835 			target->model = NK_PRO;
836 			break;
837 		case DeviceModel::STORAGE:
838 			target->model = NK_STORAGE;
839 			break;
840 		case DeviceModel::LIBREM:
841 			target->model = NK_LIBREM;
842 			break;
843 		default:
844 			return false;
845 		}
846 
847 		target->path = strndup(source.m_path.c_str(), MAXIMUM_STR_REPLY_LENGTH);
848 		target->serial_number = strndup(source.m_serialNumber.c_str(), MAXIMUM_STR_REPLY_LENGTH);
849 		target->next = nullptr;
850 
851 		return target->path && target->serial_number;
852 	}
853 
NK_list_devices()854 	NK_C_API struct NK_device_info* NK_list_devices() {
855 		auto nm = NitrokeyManager::instance();
856 		return get_with_result([&]() -> NK_device_info* {
857 			auto v = nm->list_devices();
858 			if (v.empty())
859 				return nullptr;
860 
861 			auto result = new NK_device_info();
862 			auto ptr = result;
863 			auto first = v.begin();
864 			if (!copy_device_info(*first, ptr)) {
865 				NK_free_device_info(result);
866 				return nullptr;
867 			}
868 			v.erase(first);
869 
870 			for (auto& info : v) {
871 				ptr->next = new NK_device_info();
872 				ptr = ptr->next;
873 
874 				if (!copy_device_info(info, ptr)) {
875 					NK_free_device_info(result);
876 					return nullptr;
877 				}
878 			}
879 			return result;
880 		});
881 	}
882 
NK_free_device_info(struct NK_device_info * device_info)883 	NK_C_API void NK_free_device_info(struct NK_device_info* device_info) {
884 		if (!device_info)
885 			return;
886 
887 		if (device_info->next)
888 			NK_free_device_info(device_info->next);
889 
890 		free(device_info->path);
891 		free(device_info->serial_number);
892 		delete device_info;
893 	}
894 
NK_connect_with_ID(const char * id)895 	NK_C_API int NK_connect_with_ID(const char* id) {
896 		auto m = NitrokeyManager::instance();
897 		return get_with_result([&]() {
898 			return m->connect_with_ID(id) ? 1 : 0;
899 		});
900 	}
901 
NK_connect_with_path(const char * path)902 	NK_C_API int NK_connect_with_path(const char* path) {
903 		auto m = NitrokeyManager::instance();
904 		return get_with_result([&]() {
905 			return m->connect_with_path(path) ? 1 : 0;
906 		});
907 	 }
908 
909 
NK_wink()910 	NK_C_API int NK_wink() {
911 		auto m = NitrokeyManager::instance();
912 		return get_without_result([&]() {
913 			return m->wink();
914 		});
915 	}
916 
NK_enable_firmware_update_pro(const char * update_password)917   NK_C_API int NK_enable_firmware_update_pro(const char* update_password){
918     auto m = NitrokeyManager::instance();
919     return get_without_result([&]() {
920       m->enable_firmware_update_pro(update_password);
921   });
922 }
923 
NK_change_firmware_password_pro(const char * current_firmware_password,const char * new_firmware_password)924   NK_C_API int NK_change_firmware_password_pro(const char *current_firmware_password, const char *new_firmware_password) {
925     auto m = NitrokeyManager::instance();
926     return get_without_result([&]() {
927       m->change_firmware_update_password_pro(current_firmware_password,
928                                              new_firmware_password);
929     });
930   }
931 
932 
NK_read_HOTP_slot(const uint8_t slot_num,struct ReadSlot_t * out)933   NK_C_API int NK_read_HOTP_slot(const uint8_t slot_num, struct ReadSlot_t* out){
934   if (out == nullptr)
935     return -1;
936   auto m = NitrokeyManager::instance();
937   auto result = get_with_status([&]() {
938     return m->get_HOTP_slot_data(slot_num);
939   }, stick10::ReadSlot::ResponsePayload() );
940   auto error_code = std::get<0>(result);
941   if (error_code != 0) {
942     return error_code;
943   }
944 #define a(x) out->x = read_slot.x
945   stick10::ReadSlot::ResponsePayload read_slot = std::get<1>(result);
946   a(_slot_config);
947   a(slot_counter);
948 #undef a
949 #define m(x) memmove(out->x, read_slot.x, sizeof(read_slot.x))
950   m(slot_name);
951   m(slot_token_id);
952 #undef m
953   return 0;
954 }
955 
956 
957 #ifdef __cplusplus
958 }
959 #endif
960