From 47ea358ddc1bc37b809f9f5b893a6b099cbb262b Mon Sep 17 00:00:00 2001 From: Daniel Mueller Date: Mon, 12 Aug 2019 22:23:33 -0700 Subject: Update nitrokey crate to 0.4.0-alpha.3 This change updates the version of the nitrokey crate that we use to 0.4.0-alpha.3. This version is the supposedly last pre-release before 0.4.0, with no further major anticipated changes. In order to integrate with this new version we have to adjust the way we connect to a Nitrokey device by funneling those connection requests through a global manager object. The rationale behind that step being that the underlying libnitrokey actually cannot handle access of multiple devices at the same time, and so the manager object is used to prevent accidental wrong concurrent usage. Because a device object now effectively keeps a reference to the manager, we need to provide an additional lifetime to that and derived objects. Lastly, the use of a manager is also the reason why the tests had to be adjusted to no longer accept device objects in their signatures, but only the respective model for which to invoke the test. That is required because, as elaborated earlier on, having a device object implies having taken a reference to a manager (in that case owned by nitrokey-test), and that reference clashes with the nitrocli code itself attempting to take the manager. We side step this problem by merely accepting a Model object, which can be passed around independently of the manager itself, meaning that nitrokey-test does not need to hold such a reference while the test is run. Import subrepo nitrokey/:nitrokey at f150d59410eefdec2ae69b2422906a3d1d88aa07 Import subrepo nitrokey-sys/:nitrokey-sys at 8695e2c762807e033a86c8d03974b686d20cdd72 Import subrepo lazy-static/:lazy-static at b4b2b16aaa79dd7548e288455a0dbe4065bf4e1a --- lazy-static/.gitignore | 4 + lazy-static/.travis.yml | 34 + lazy-static/Cargo.toml | 34 + lazy-static/LICENSE-APACHE | 201 ++++ lazy-static/LICENSE-MIT | 25 + lazy-static/README.md | 79 ++ lazy-static/appveyor.yml | 61 + lazy-static/compiletest/Cargo.toml | 11 + lazy-static/compiletest/src/lib.rs | 11 + .../compiletest/tests/compile-fail/README.md | 22 + .../incorrect_visibility_restriction.rs | 10 + .../tests/compile-fail/static_is_private.rs | 14 + .../tests/compile-fail/static_is_sized.rs | 11 + lazy-static/compiletest/tests/compile_tests.rs | 19 + lazy-static/src/core_lazy.rs | 31 + lazy-static/src/inline_lazy.rs | 65 ++ lazy-static/src/lib.rs | 212 ++++ lazy-static/tests/no_std.rs | 20 + lazy-static/tests/test.rs | 162 +++ nitrocli/CHANGELOG.md | 4 +- nitrocli/Cargo.lock | 19 +- nitrocli/Cargo.toml | 5 +- nitrocli/src/commands.rs | 45 +- nitrocli/src/pinentry.rs | 8 +- nitrocli/src/tests/config.rs | 12 +- nitrocli/src/tests/encrypted.rs | 37 +- nitrocli/src/tests/hidden.rs | 25 +- nitrocli/src/tests/lock.rs | 15 +- nitrocli/src/tests/mod.rs | 13 +- nitrocli/src/tests/otp.rs | 28 +- nitrocli/src/tests/pin.rs | 61 +- nitrocli/src/tests/pws.rs | 20 +- nitrocli/src/tests/reset.rs | 34 +- nitrocli/src/tests/status.rs | 16 +- nitrocli/src/tests/unencrypted.rs | 25 +- nitrokey-sys/CHANGELOG.md | 26 + nitrokey-sys/Cargo.toml | 2 +- nitrokey-sys/build.rs | 4 +- .../DeviceCommunicationExceptions.cpp | 24 - nitrokey-sys/libnitrokey-v3.4.1/LICENSE | 166 --- nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.cc | 755 ------------ nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.h | 792 ------------- nitrokey-sys/libnitrokey-v3.4.1/NitrokeyManager.cc | 1149 ------------------- nitrokey-sys/libnitrokey-v3.4.1/README.md | 210 ---- nitrokey-sys/libnitrokey-v3.4.1/command_id.cc | 184 --- nitrokey-sys/libnitrokey-v3.4.1/device.cc | 295 ----- .../libnitrokey/CommandFailedException.h | 76 -- .../libnitrokey/DeviceCommunicationExceptions.h | 73 -- .../libnitrokey/LibraryException.h | 118 -- .../libnitrokey/LongOperationInProgressException.h | 45 - .../libnitrokey/NitrokeyManager.h | 303 ----- .../libnitrokey-v3.4.1/libnitrokey/command.h | 112 -- .../libnitrokey-v3.4.1/libnitrokey/command_id.h | 154 --- .../libnitrokey-v3.4.1/libnitrokey/cxx_semantics.h | 44 - .../libnitrokey-v3.4.1/libnitrokey/deprecated.h | 35 - .../libnitrokey-v3.4.1/libnitrokey/device.h | 169 --- .../libnitrokey-v3.4.1/libnitrokey/device_proto.h | 491 -------- .../libnitrokey-v3.4.1/libnitrokey/dissect.h | 145 --- .../libnitrokey-v3.4.1/libnitrokey/hidapi/hidapi.h | 391 ------- nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/log.h | 108 -- nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/misc.h | 94 -- .../libnitrokey/stick10_commands.h | 889 --------------- .../libnitrokey/stick10_commands_0.8.h | 348 ------ .../libnitrokey/stick20_commands.h | 394 ------- .../libnitrokey-v3.4.1/libnitrokey/version.h | 33 - nitrokey-sys/libnitrokey-v3.4.1/log.cc | 103 -- nitrokey-sys/libnitrokey-v3.4.1/misc.cc | 123 -- nitrokey-sys/libnitrokey-v3.4.1/version.cc | 37 - nitrokey-sys/libnitrokey-v3.4.1/version.cc.in | 37 - .../DeviceCommunicationExceptions.cpp | 24 + nitrokey-sys/libnitrokey-v3.5/LICENSE | 166 +++ nitrokey-sys/libnitrokey-v3.5/NK_C_API.cc | 910 +++++++++++++++ nitrokey-sys/libnitrokey-v3.5/NK_C_API.h | 1014 +++++++++++++++++ nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc | 1199 ++++++++++++++++++++ nitrokey-sys/libnitrokey-v3.5/README.md | 291 +++++ nitrokey-sys/libnitrokey-v3.5/command_id.cc | 188 +++ nitrokey-sys/libnitrokey-v3.5/device.cc | 345 ++++++ .../libnitrokey/CommandFailedException.h | 76 ++ .../libnitrokey/DeviceCommunicationExceptions.h | 73 ++ .../libnitrokey/LibraryException.h | 118 ++ .../libnitrokey/LongOperationInProgressException.h | 45 + .../libnitrokey-v3.5/libnitrokey/NitrokeyManager.h | 308 +++++ .../libnitrokey-v3.5/libnitrokey/command.h | 112 ++ .../libnitrokey-v3.5/libnitrokey/command_id.h | 156 +++ .../libnitrokey-v3.5/libnitrokey/cxx_semantics.h | 44 + .../libnitrokey-v3.5/libnitrokey/deprecated.h | 35 + nitrokey-sys/libnitrokey-v3.5/libnitrokey/device.h | 224 ++++ .../libnitrokey-v3.5/libnitrokey/device_proto.h | 491 ++++++++ .../libnitrokey-v3.5/libnitrokey/dissect.h | 145 +++ .../libnitrokey-v3.5/libnitrokey/hidapi/hidapi.h | 391 +++++++ nitrokey-sys/libnitrokey-v3.5/libnitrokey/log.h | 109 ++ nitrokey-sys/libnitrokey-v3.5/libnitrokey/misc.h | 121 ++ .../libnitrokey/stick10_commands.h | 929 +++++++++++++++ .../libnitrokey/stick10_commands_0.8.h | 348 ++++++ .../libnitrokey/stick20_commands.h | 394 +++++++ .../libnitrokey-v3.5/libnitrokey/version.h | 33 + nitrokey-sys/libnitrokey-v3.5/log.cc | 103 ++ nitrokey-sys/libnitrokey-v3.5/misc.cc | 123 ++ nitrokey-sys/libnitrokey-v3.5/version.cc | 37 + nitrokey-sys/libnitrokey-v3.5/version.cc.in | 37 + nitrokey-sys/src/ffi.rs | 377 +++++- nitrokey/CHANGELOG.md | 10 + nitrokey/Cargo.toml | 8 +- nitrokey/README.md | 13 +- nitrokey/TODO.md | 8 +- nitrokey/src/auth.rs | 90 +- nitrokey/src/device.rs | 368 +++--- nitrokey/src/error.rs | 30 +- nitrokey/src/lib.rs | 301 ++++- nitrokey/src/otp.rs | 27 +- nitrokey/src/pws.rs | 54 +- nitrokey/tests/device.rs | 48 +- nitrokey/tests/lib.rs | 16 + nitrokey/tests/otp.rs | 4 +- nitrokey/tests/pws.rs | 4 +- 115 files changed, 10865 insertions(+), 8404 deletions(-) create mode 100644 lazy-static/.gitignore create mode 100644 lazy-static/.travis.yml create mode 100644 lazy-static/Cargo.toml create mode 100644 lazy-static/LICENSE-APACHE create mode 100644 lazy-static/LICENSE-MIT create mode 100644 lazy-static/README.md create mode 100644 lazy-static/appveyor.yml create mode 100644 lazy-static/compiletest/Cargo.toml create mode 100644 lazy-static/compiletest/src/lib.rs create mode 100644 lazy-static/compiletest/tests/compile-fail/README.md create mode 100644 lazy-static/compiletest/tests/compile-fail/incorrect_visibility_restriction.rs create mode 100644 lazy-static/compiletest/tests/compile-fail/static_is_private.rs create mode 100644 lazy-static/compiletest/tests/compile-fail/static_is_sized.rs create mode 100644 lazy-static/compiletest/tests/compile_tests.rs create mode 100644 lazy-static/src/core_lazy.rs create mode 100644 lazy-static/src/inline_lazy.rs create mode 100644 lazy-static/src/lib.rs create mode 100644 lazy-static/tests/no_std.rs create mode 100644 lazy-static/tests/test.rs delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/DeviceCommunicationExceptions.cpp delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/LICENSE delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/NitrokeyManager.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/README.md delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/command_id.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/device.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/CommandFailedException.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/DeviceCommunicationExceptions.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LibraryException.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LongOperationInProgressException.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/NitrokeyManager.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command_id.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/cxx_semantics.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/deprecated.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device_proto.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/dissect.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/hidapi/hidapi.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/log.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/misc.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands_0.8.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick20_commands.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/version.h delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/log.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/misc.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/version.cc delete mode 100644 nitrokey-sys/libnitrokey-v3.4.1/version.cc.in create mode 100644 nitrokey-sys/libnitrokey-v3.5/DeviceCommunicationExceptions.cpp create mode 100644 nitrokey-sys/libnitrokey-v3.5/LICENSE create mode 100644 nitrokey-sys/libnitrokey-v3.5/NK_C_API.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/NK_C_API.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/README.md create mode 100644 nitrokey-sys/libnitrokey-v3.5/command_id.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/device.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/CommandFailedException.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/DeviceCommunicationExceptions.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/LibraryException.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/LongOperationInProgressException.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/NitrokeyManager.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/command.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/command_id.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/cxx_semantics.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/deprecated.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/device.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/device_proto.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/dissect.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/hidapi/hidapi.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/log.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/misc.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands_0.8.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick20_commands.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/libnitrokey/version.h create mode 100644 nitrokey-sys/libnitrokey-v3.5/log.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/misc.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/version.cc create mode 100644 nitrokey-sys/libnitrokey-v3.5/version.cc.in diff --git a/lazy-static/.gitignore b/lazy-static/.gitignore new file mode 100644 index 0000000..bde55ce --- /dev/null +++ b/lazy-static/.gitignore @@ -0,0 +1,4 @@ +target +doc +Cargo.lock +.cargo diff --git a/lazy-static/.travis.yml b/lazy-static/.travis.yml new file mode 100644 index 0000000..f4c3c74 --- /dev/null +++ b/lazy-static/.travis.yml @@ -0,0 +1,34 @@ +language: rust +matrix: + include: + - rust: 1.24.1 + - rust: stable + script: + - cargo test + - cargo test --features spin_no_std + - os: osx + - rust: beta + - rust: nightly + script: + - cargo test + - cargo bench + - cargo test --features spin_no_std + - cargo bench --features spin_no_std + - cd compiletest + - cargo clean + - cargo test + - cd ../ + + - rust: nightly + before_script: + - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH + script: + - cargo doc --no-deps --all-features + after_success: + - travis-cargo --only nightly doc-upload +script: + - cargo test + +env: + global: + - secure: YXu24LptjeYirjWYjWGsMT2m3mB7LvQATE6TVo7VEUXv8GYoy2ORIHD83PeImxC93MmZ01QeUezRzuCW51ZcK92VnNSBttlF60SvIX18VsJrV92tsAhievFstqYQ+fB8DIuQ8noU0jPz7GpI+R9dlTRSImAqWOnVIghA+Wzz7Js= diff --git a/lazy-static/Cargo.toml b/lazy-static/Cargo.toml new file mode 100644 index 0000000..679f5cd --- /dev/null +++ b/lazy-static/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "lazy_static" +# NB: When modifying, also modify html_root_url in lib.rs +version = "1.2.0" +authors = ["Marvin Löbel "] +license = "MIT/Apache-2.0" + +description = "A macro for declaring lazily evaluated statics in Rust." +readme = "README.md" +documentation = "https://docs.rs/lazy_static" + +repository = "https://github.com/rust-lang-nursery/lazy-static.rs" +keywords = ["macro", "lazy", "static"] +categories = [ "no-std", "rust-patterns", "memory-management" ] +exclude = ["/.travis.yml", "/appveyor.yml"] + +[dependencies.spin] +version = "0.4.10" +optional = true +default-features = false +features = ["once"] + +[features] +nightly = [] +spin_no_std = ["spin"] + +[badges] +appveyor = { repository = "rust-lang-nursery/lazy-static.rs" } +travis-ci = { repository = "rust-lang-nursery/lazy-static.rs" } + +is-it-maintained-issue-resolution = { repository = "rust-lang-nursery/lazy-static.rs" } +is-it-maintained-open-issues = { repository = "rust-lang-nursery/lazy-static.rs" } + +maintenance = { status = "passively-maintained" } diff --git a/lazy-static/LICENSE-APACHE b/lazy-static/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/lazy-static/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lazy-static/LICENSE-MIT b/lazy-static/LICENSE-MIT new file mode 100644 index 0000000..25597d5 --- /dev/null +++ b/lazy-static/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2010 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lazy-static/README.md b/lazy-static/README.md new file mode 100644 index 0000000..d96cdf8 --- /dev/null +++ b/lazy-static/README.md @@ -0,0 +1,79 @@ +lazy-static.rs +============== + +A macro for declaring lazily evaluated statics in Rust. + +Using this macro, it is possible to have `static`s that require code to be +executed at runtime in order to be initialized. +This includes anything requiring heap allocations, like vectors or hash maps, +as well as anything that requires non-const function calls to be computed. + +[![Travis-CI Status](https://travis-ci.org/rust-lang-nursery/lazy-static.rs.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/lazy-static.rs) +[![Latest version](https://img.shields.io/crates/v/lazy_static.svg)](https://crates.io/crates/lazy_static) +[![Documentation](https://docs.rs/lazy_static/badge.svg)](https://docs.rs/lazy_static) +[![License](https://img.shields.io/crates/l/lazy_static.svg)](https://github.com/rust-lang-nursery/lazy-static.rs#license) + +## Minimum supported `rustc` + +`1.24.1+` + +This version is explicitly tested in CI and may only be bumped in new minor versions. Any changes to the supported minimum version will be called out in the release notes. + + +# Getting Started + +[lazy-static.rs is available on crates.io](https://crates.io/crates/lazy_static). +It is recommended to look there for the newest released version, as well as links to the newest builds of the docs. + +At the point of the last update of this README, the latest published version could be used like this: + +Add the following dependency to your Cargo manifest... + +```toml +[dependencies] +lazy_static = "1.2.0" +``` + +...and see the [docs](https://docs.rs/lazy_static) for how to use it. + +# Example + +```rust +#[macro_use] +extern crate lazy_static; + +use std::collections::HashMap; + +lazy_static! { + static ref HASHMAP: HashMap = { + let mut m = HashMap::new(); + m.insert(0, "foo"); + m.insert(1, "bar"); + m.insert(2, "baz"); + m + }; +} + +fn main() { + // First access to `HASHMAP` initializes it + println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap()); + + // Any further access to `HASHMAP` just returns the computed value + println!("The entry for `1` is \"{}\".", HASHMAP.get(&1).unwrap()); +} +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/lazy-static/appveyor.yml b/lazy-static/appveyor.yml new file mode 100644 index 0000000..b138452 --- /dev/null +++ b/lazy-static/appveyor.yml @@ -0,0 +1,61 @@ +environment: + global: + PROJECT_NAME: lazy_static + # When this was added there were revocation check failures when using the + # libcurl backend as libcurl checks by default, but rustup doesn't provide the + # switch to turn this off. Switch to Hyper which looks to not check for + # revocation by default like libcurl does. + RUSTUP_USE_REQWEST: 1 + CARGO_HTTP_CHECK_REVOKE: false + matrix: + # Stable channel + - TARGET: i686-pc-windows-gnu + CHANNEL: stable + - TARGET: i686-pc-windows-msvc + CHANNEL: stable + - TARGET: x86_64-pc-windows-gnu + CHANNEL: stable + - TARGET: x86_64-pc-windows-msvc + CHANNEL: stable + # Beta channel + - TARGET: i686-pc-windows-gnu + CHANNEL: beta + - TARGET: i686-pc-windows-msvc + CHANNEL: beta + - TARGET: x86_64-pc-windows-gnu + CHANNEL: beta + - TARGET: x86_64-pc-windows-msvc + CHANNEL: beta + # Nightly channel + - TARGET: i686-pc-windows-gnu + CHANNEL: nightly + - TARGET: i686-pc-windows-msvc + CHANNEL: nightly + - TARGET: x86_64-pc-windows-gnu + CHANNEL: nightly + - TARGET: x86_64-pc-windows-msvc + CHANNEL: nightly + +# Install Rust and Cargo +# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init.exe -y --default-toolchain %CHANNEL% --default-host %TARGET% + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin + - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo build --verbose + - cargo test + - if [%CHANNEL%]==[nightly] ( + cd compiletest && + cargo clean && + cargo build --verbose && + cargo test && + cd ../ + ) diff --git a/lazy-static/compiletest/Cargo.toml b/lazy-static/compiletest/Cargo.toml new file mode 100644 index 0000000..e25d5a7 --- /dev/null +++ b/lazy-static/compiletest/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lazy_static_compiletest" +version = "0.0.1" +publish = false +authors = ["lazy_static contributors"] + +[dependencies.lazy_static] +path = "../" + +[dependencies.compiletest_rs] +version = "0.3" diff --git a/lazy-static/compiletest/src/lib.rs b/lazy-static/compiletest/src/lib.rs new file mode 100644 index 0000000..0452765 --- /dev/null +++ b/lazy-static/compiletest/src/lib.rs @@ -0,0 +1,11 @@ +/* +This library is a shim around `lazy_static` that disambiguates it with the `lazy_static` +that's shipped with the Rust toolchain. We re-export the entire public API of `lazy_static` +under a different crate name so that can be imported in the compile tests. + +This currently appears to use the right local build of `lazy_static`. +*/ + +extern crate lazy_static; + +pub use self::lazy_static::*; diff --git a/lazy-static/compiletest/tests/compile-fail/README.md b/lazy-static/compiletest/tests/compile-fail/README.md new file mode 100644 index 0000000..58dbc3b --- /dev/null +++ b/lazy-static/compiletest/tests/compile-fail/README.md @@ -0,0 +1,22 @@ +This directory contains snippets of code that should yield a +warning/note/help/error at compilation. Syntax of annotations is described in +[rust documentation](https://github.com/rust-lang/rust/blob/master/src/test/COMPILER_TESTS.md). +For more information check out [`compiletest` crate](https://github.com/laumann/compiletest-rs). + +To run compile tests issue `cargo +nightly --test`. + +## Notes on working with `compiletest` crate + +* Currently code that is inside macro should not be annotated, as `compiletest` + crate cannot deal with the fact that macro invocations effectively changes + line numbering. To prevent this add a `// error-pattern:` + on the top of the file and make sure that you set `deny` lint level + if you want to test compiler message different than error. +* `compiletest` crate by default sets `allow(dead_code)` lint level so make sure + that you change it to something suiting your needs even if the warning is + issued prior to any macro invocation. +* If you get a message `error: 0 unexpected errors found, 1 expected errors not found` + despite the fact that some error was bound to occur don't worry - it's a known + issue in the `compiletest` crate and your error was probably not registered - + make sure that your annotations are correct and that you are setting correct + lint levels. diff --git a/lazy-static/compiletest/tests/compile-fail/incorrect_visibility_restriction.rs b/lazy-static/compiletest/tests/compile-fail/incorrect_visibility_restriction.rs new file mode 100644 index 0000000..360e23d --- /dev/null +++ b/lazy-static/compiletest/tests/compile-fail/incorrect_visibility_restriction.rs @@ -0,0 +1,10 @@ +// incorrect visibility restriction +#[macro_use] +extern crate lazy_static_compiletest as lazy_static; + +lazy_static! { + pub(nonsense) static ref WRONG: () = (); + //~^ ERROR incorrect visibility restriction +} + +fn main() { } diff --git a/lazy-static/compiletest/tests/compile-fail/static_is_private.rs b/lazy-static/compiletest/tests/compile-fail/static_is_private.rs new file mode 100644 index 0000000..6ebc8f5 --- /dev/null +++ b/lazy-static/compiletest/tests/compile-fail/static_is_private.rs @@ -0,0 +1,14 @@ +#[macro_use] +extern crate lazy_static_compiletest as lazy_static; + +mod outer { + pub mod inner { + lazy_static! { + pub(in outer) static ref FOO: () = (); + } + } +} + +fn main() { + assert_eq!(*outer::inner::FOO, ()); //~ ERROR static `FOO` is private +} diff --git a/lazy-static/compiletest/tests/compile-fail/static_is_sized.rs b/lazy-static/compiletest/tests/compile-fail/static_is_sized.rs new file mode 100644 index 0000000..ac1cad4 --- /dev/null +++ b/lazy-static/compiletest/tests/compile-fail/static_is_sized.rs @@ -0,0 +1,11 @@ +// error-pattern:the size for values of type `str` cannot be known at compilation time +#[macro_use] +extern crate lazy_static_compiletest as lazy_static; + +lazy_static! { + pub static ref FOO: str = panic!(); +} + + +fn main() { +} diff --git a/lazy-static/compiletest/tests/compile_tests.rs b/lazy-static/compiletest/tests/compile_tests.rs new file mode 100644 index 0000000..d908077 --- /dev/null +++ b/lazy-static/compiletest/tests/compile_tests.rs @@ -0,0 +1,19 @@ +extern crate compiletest_rs as compiletest; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::Config::default(); + config.mode = mode.parse().expect("Invalid mode"); + config.src_base = ["tests", mode].iter().collect(); + + config.verbose = true; + + config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps/".to_owned()); + config.clean_rmeta(); + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail"); +} diff --git a/lazy-static/src/core_lazy.rs b/lazy-static/src/core_lazy.rs new file mode 100644 index 0000000..b66c3e0 --- /dev/null +++ b/lazy-static/src/core_lazy.rs @@ -0,0 +1,31 @@ +// Copyright 2016 lazy-static.rs Developers +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +extern crate spin; + +use self::spin::Once; + +pub struct Lazy(Once); + +impl Lazy { + pub const INIT: Self = Lazy(Once::INIT); + + #[inline(always)] + pub fn get(&'static self, builder: F) -> &T + where F: FnOnce() -> T + { + self.0.call_once(builder) + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __lazy_static_create { + ($NAME:ident, $T:ty) => { + static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT; + } +} diff --git a/lazy-static/src/inline_lazy.rs b/lazy-static/src/inline_lazy.rs new file mode 100644 index 0000000..268dd45 --- /dev/null +++ b/lazy-static/src/inline_lazy.rs @@ -0,0 +1,65 @@ +// Copyright 2016 lazy-static.rs Developers +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +extern crate core; +extern crate std; + +use self::std::prelude::v1::*; +use self::std::cell::Cell; +use self::std::sync::Once; +pub use self::std::sync::ONCE_INIT; + +// FIXME: Replace Option with MaybeInitialized +pub struct Lazy(Cell>, Once); + +impl Lazy { + pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT); + + #[inline(always)] + pub fn get(&'static self, f: F) -> &T + where + F: FnOnce() -> T, + { + self.1.call_once(|| { + self.0.set(Some(f())); + }); + + // `self.0` is guaranteed to be `Some` by this point + // The `Once` will catch and propegate panics + unsafe { + match *self.0.as_ptr() { + Some(ref x) => x, + None => { + debug_assert!(false, "attempted to derefence an uninitialized lazy static. This is a bug"); + + unreachable_unchecked() + }, + } + } + } +} + +unsafe impl Sync for Lazy {} + +#[macro_export] +#[doc(hidden)] +macro_rules! __lazy_static_create { + ($NAME:ident, $T:ty) => { + static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT; + }; +} + +/// Polyfill for std::hint::unreachable_unchecked. There currently exists a +/// [crate](https://docs.rs/unreachable) for an equivalent to std::hint::unreachable_unchecked, but +/// lazy_static currently doesn't include any runtime dependencies and we've chosen to include this +/// short polyfill rather than include a new crate in every consumer's build. +/// +/// This should be replaced by std's version when lazy_static starts to require at least Rust 1.27. +unsafe fn unreachable_unchecked() -> ! { + enum Void {} + match std::mem::uninitialized::() {} +} diff --git a/lazy-static/src/lib.rs b/lazy-static/src/lib.rs new file mode 100644 index 0000000..42dc405 --- /dev/null +++ b/lazy-static/src/lib.rs @@ -0,0 +1,212 @@ +// Copyright 2016 lazy-static.rs Developers +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +/*! +A macro for declaring lazily evaluated statics. + +Using this macro, it is possible to have `static`s that require code to be +executed at runtime in order to be initialized. +This includes anything requiring heap allocations, like vectors or hash maps, +as well as anything that requires function calls to be computed. + +# Syntax + +```ignore +lazy_static! { + [pub] static ref NAME_1: TYPE_1 = EXPR_1; + [pub] static ref NAME_2: TYPE_2 = EXPR_2; + ... + [pub] static ref NAME_N: TYPE_N = EXPR_N; +} +``` + +Attributes (including doc comments) are supported as well: + +```rust +# #[macro_use] +# extern crate lazy_static; +# fn main() { +lazy_static! { + /// This is an example for using doc comment attributes + static ref EXAMPLE: u8 = 42; +} +# } +``` + +# Semantics + +For a given `static ref NAME: TYPE = EXPR;`, the macro generates a unique type that +implements `Deref` and stores it in a static with name `NAME`. (Attributes end up +attaching to this type.) + +On first deref, `EXPR` gets evaluated and stored internally, such that all further derefs +can return a reference to the same object. Note that this can lead to deadlocks +if you have multiple lazy statics that depend on each other in their initialization. + +Apart from the lazy initialization, the resulting "static ref" variables +have generally the same properties as regular "static" variables: + +- Any type in them needs to fulfill the `Sync` trait. +- If the type has a destructor, then it will not run when the process exits. + +# Example + +Using the macro: + +```rust +#[macro_use] +extern crate lazy_static; + +use std::collections::HashMap; + +lazy_static! { + static ref HASHMAP: HashMap = { + let mut m = HashMap::new(); + m.insert(0, "foo"); + m.insert(1, "bar"); + m.insert(2, "baz"); + m + }; + static ref COUNT: usize = HASHMAP.len(); + static ref NUMBER: u32 = times_two(21); +} + +fn times_two(n: u32) -> u32 { n * 2 } + +fn main() { + println!("The map has {} entries.", *COUNT); + println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap()); + println!("A expensive calculation on a static results in: {}.", *NUMBER); +} +``` + +# Implementation details + +The `Deref` implementation uses a hidden static variable that is guarded by an atomic check on each access. + +# Cargo features + +This crate provides two cargo features: + +- `nightly`: This uses unstable language features only available on the nightly release channel for a more optimal implementation. In practice this currently means avoiding a heap allocation per static. This feature might get deprecated at a later point once all relevant optimizations are usable from stable. +- `spin_no_std` (implies `nightly`): This allows using this crate in a no-std environment, by depending on the standalone `spin` crate. + +Both features depend on unstable language features, which means +no guarantees can be made about them in regard to SemVer stability. + +*/ + +#![doc(html_root_url = "https://docs.rs/lazy_static/1.2.0")] +#![no_std] + +#[cfg(not(feature = "spin_no_std"))] +#[path="inline_lazy.rs"] +#[doc(hidden)] +pub mod lazy; + +#[cfg(feature = "spin_no_std")] +#[path="core_lazy.rs"] +#[doc(hidden)] +pub mod lazy; + +#[doc(hidden)] +pub use core::ops::Deref as __Deref; + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! __lazy_static_internal { + // optional visibility restrictions are wrapped in `()` to allow for + // explicitly passing otherwise implicit information about private items + ($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + __lazy_static_internal!(@MAKE TY, $(#[$attr])*, ($($vis)*), $N); + __lazy_static_internal!(@TAIL, $N : $T = $e); + lazy_static!($($t)*); + }; + (@TAIL, $N:ident : $T:ty = $e:expr) => { + impl $crate::__Deref for $N { + type Target = $T; + fn deref(&self) -> &$T { + #[inline(always)] + fn __static_ref_initialize() -> $T { $e } + + #[inline(always)] + fn __stability() -> &'static $T { + __lazy_static_create!(LAZY, $T); + LAZY.get(__static_ref_initialize) + } + __stability() + } + } + impl $crate::LazyStatic for $N { + fn initialize(lazy: &Self) { + let _ = &**lazy; + } + } + }; + // `vis` is wrapped in `()` to prevent parsing ambiguity + (@MAKE TY, $(#[$attr:meta])*, ($($vis:tt)*), $N:ident) => { + #[allow(missing_copy_implementations)] + #[allow(non_camel_case_types)] + #[allow(dead_code)] + $(#[$attr])* + $($vis)* struct $N {__private_field: ()} + #[doc(hidden)] + $($vis)* static $N: $N = $N {__private_field: ()}; + }; + () => () +} + +#[macro_export(local_inner_macros)] +macro_rules! lazy_static { + ($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + // use `()` to explicitly forward the information about private items + __lazy_static_internal!($(#[$attr])* () static ref $N : $T = $e; $($t)*); + }; + ($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + __lazy_static_internal!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*); + }; + ($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + __lazy_static_internal!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $e; $($t)*); + }; + () => () +} + +/// Support trait for enabling a few common operation on lazy static values. +/// +/// This is implemented by each defined lazy static, and +/// used by the free functions in this crate. +pub trait LazyStatic { + #[doc(hidden)] + fn initialize(lazy: &Self); +} + +/// Takes a shared reference to a lazy static and initializes +/// it if it has not been already. +/// +/// This can be used to control the initialization point of a lazy static. +/// +/// Example: +/// +/// ```rust +/// #[macro_use] +/// extern crate lazy_static; +/// +/// lazy_static! { +/// static ref BUFFER: Vec = (0..65537).collect(); +/// } +/// +/// fn main() { +/// lazy_static::initialize(&BUFFER); +/// +/// // ... +/// work_with_initialized_data(&BUFFER); +/// } +/// # fn work_with_initialized_data(_: &[u8]) {} +/// ``` +pub fn initialize(lazy: &T) { + LazyStatic::initialize(lazy); +} diff --git a/lazy-static/tests/no_std.rs b/lazy-static/tests/no_std.rs new file mode 100644 index 0000000..f94a1aa --- /dev/null +++ b/lazy-static/tests/no_std.rs @@ -0,0 +1,20 @@ +#![cfg(feature="spin_no_std")] + +#![no_std] + +#[macro_use] +extern crate lazy_static; + +lazy_static! { + /// Documentation! + pub static ref NUMBER: u32 = times_two(3); +} + +fn times_two(n: u32) -> u32 { + n * 2 +} + +#[test] +fn test_basic() { + assert_eq!(*NUMBER, 6); +} diff --git a/lazy-static/tests/test.rs b/lazy-static/tests/test.rs new file mode 100644 index 0000000..654abc5 --- /dev/null +++ b/lazy-static/tests/test.rs @@ -0,0 +1,162 @@ +#[macro_use] +extern crate lazy_static; +use std::collections::HashMap; + +lazy_static! { + /// Documentation! + pub static ref NUMBER: u32 = times_two(3); + + static ref ARRAY_BOXES: [Box; 3] = [Box::new(1), Box::new(2), Box::new(3)]; + + /// More documentation! + #[allow(unused_variables)] + #[derive(Copy, Clone, Debug)] + pub static ref STRING: String = "hello".to_string(); + + static ref HASHMAP: HashMap = { + let mut m = HashMap::new(); + m.insert(0, "abc"); + m.insert(1, "def"); + m.insert(2, "ghi"); + m + }; + + // This should not compile if the unsafe is removed. + static ref UNSAFE: u32 = unsafe { + std::mem::transmute::(-1) + }; +} + +lazy_static! { + static ref S1: &'static str = "a"; + static ref S2: &'static str = "b"; +} +lazy_static! { + static ref S3: String = [*S1, *S2].join(""); +} + +#[test] +fn s3() { + assert_eq!(&*S3, "ab"); +} + +fn times_two(n: u32) -> u32 { + n * 2 +} + +#[test] +fn test_basic() { + assert_eq!(&**STRING, "hello"); + assert_eq!(*NUMBER, 6); + assert!(HASHMAP.get(&1).is_some()); + assert!(HASHMAP.get(&3).is_none()); + assert_eq!(&*ARRAY_BOXES, &[Box::new(1), Box::new(2), Box::new(3)]); + assert_eq!(*UNSAFE, std::u32::MAX); +} + +#[test] +fn test_repeat() { + assert_eq!(*NUMBER, 6); + assert_eq!(*NUMBER, 6); + assert_eq!(*NUMBER, 6); +} + +#[test] +fn test_meta() { + // this would not compile if STRING were not marked #[derive(Copy, Clone)] + let copy_of_string = STRING; + // just to make sure it was copied + assert!(&STRING as *const _ != ©_of_string as *const _); + + // this would not compile if STRING were not marked #[derive(Debug)] + assert_eq!(format!("{:?}", STRING), "STRING { __private_field: () }".to_string()); +} + +mod visibility { + lazy_static! { + pub static ref FOO: Box = Box::new(0); + static ref BAR: Box = Box::new(98); + } + + pub mod inner { + lazy_static! { + pub(in visibility) static ref BAZ: Box = Box::new(42); + pub(crate) static ref BAG: Box = Box::new(37); + } + } + + #[test] + fn sub_test() { + assert_eq!(**FOO, 0); + assert_eq!(**BAR, 98); + assert_eq!(**inner::BAZ, 42); + assert_eq!(**inner::BAG, 37); + } +} + +#[test] +fn test_visibility() { + assert_eq!(*visibility::FOO, Box::new(0)); + assert_eq!(*visibility::inner::BAG, Box::new(37)); +} + +// This should not cause a warning about a missing Copy implementation +lazy_static! { + pub static ref VAR: i32 = { 0 }; +} + +#[derive(Copy, Clone, Debug, PartialEq)] +struct X; +struct Once(X); +const ONCE_INIT: Once = Once(X); +static DATA: X = X; +static ONCE: X = X; +fn require_sync() -> X { X } +fn transmute() -> X { X } +fn __static_ref_initialize() -> X { X } +fn test(_: Vec) -> X { X } + +// All these names should not be shadowed +lazy_static! { + static ref ITEM_NAME_TEST: X = { + test(vec![X, Once(X).0, ONCE_INIT.0, DATA, ONCE, + require_sync(), transmute(), + // Except this, which will sadly be shadowed by internals: + // __static_ref_initialize() + ]) + }; +} + +#[test] +fn item_name_shadowing() { + assert_eq!(*ITEM_NAME_TEST, X); +} + +use std::sync::atomic::AtomicBool; +use std::sync::atomic::ATOMIC_BOOL_INIT; +use std::sync::atomic::Ordering::SeqCst; + +static PRE_INIT_FLAG: AtomicBool = ATOMIC_BOOL_INIT; + +lazy_static! { + static ref PRE_INIT: () = { + PRE_INIT_FLAG.store(true, SeqCst); + () + }; +} + +#[test] +fn pre_init() { + assert_eq!(PRE_INIT_FLAG.load(SeqCst), false); + lazy_static::initialize(&PRE_INIT); + assert_eq!(PRE_INIT_FLAG.load(SeqCst), true); +} + +lazy_static! { + static ref LIFETIME_NAME: for<'a> fn(&'a u8) = { fn f(_: &u8) {} f }; +} + +#[test] +fn lifetime_name() { + let _ = LIFETIME_NAME; +} diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index dd40acd..7104c20 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -1,6 +1,8 @@ Unreleased ---------- -- Bumped `nitrokey` dependency to `0.4.0-alpha.2` +- Bumped `nitrokey` dependency to `0.4.0-alpha.3` + - Bumped `nitrokey-sys` dependency to `3.5.0` + - Added `lazy_static` dependency in version `1.2.0` 0.3.0 diff --git a/nitrocli/Cargo.lock b/nitrocli/Cargo.lock index 168be50..cd90994 100644 --- a/nitrocli/Cargo.lock +++ b/nitrocli/Cargo.lock @@ -55,7 +55,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" @@ -77,32 +76,33 @@ dependencies = [ "argparse 0.2.2", "base32 0.4.0", "libc 0.2.66", - "nitrokey 0.4.0-alpha.2", - "nitrokey-test 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nitrokey 0.4.0-alpha.3", + "nitrokey-test 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "nitrokey-test-state 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nitrokey" -version = "0.4.0-alpha.2" +version = "0.4.0-alpha.3" dependencies = [ + "lazy_static 1.2.0", "libc 0.2.66", - "nitrokey-sys 3.4.3", + "nitrokey-sys 3.5.0", "rand_core 0.3.0", "rand_os 0.1.1", ] [[package]] name = "nitrokey-sys" -version = "3.4.3" +version = "3.5.0" dependencies = [ "cc 1.0.48", ] [[package]] name = "nitrokey-test" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -190,7 +190,7 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0", ] [[package]] @@ -234,9 +234,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" "checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8" -"checksum nitrokey-test 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f2195b25114e38da93d24169b074e7d1007238a0e33916e01b353099c09df379" +"checksum nitrokey-test 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e7e81b55db51769209e88a63cdbb4f2dc7ee9cd20ccaf32fbb940a3b0c50259" "checksum nitrokey-test-state 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a59b732ed6d5212424ed31ec9649f05652bcbc38f45f2292b27a6044e7098803" "checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" diff --git a/nitrocli/Cargo.toml b/nitrocli/Cargo.toml index 1eb301e..23395c2 100644 --- a/nitrocli/Cargo.toml +++ b/nitrocli/Cargo.toml @@ -53,10 +53,10 @@ path = "../base32" version = "0.2" [dependencies.nitrokey] -version = "0.4.0-alpha.2" +version = "0.4.0-alpha.3" [dev-dependencies.nitrokey-test] -version = "0.2.1" +version = "0.3.1" [dev-dependencies.nitrokey-test-state] version = "0.1" @@ -71,5 +71,6 @@ cc = { path = "../cc" } libc = { path = "../libc" } nitrokey = { path = "../nitrokey" } nitrokey-sys = { path = "../nitrokey-sys" } +lazy_static = { path = "../lazy-static" } rand_core = { path = "../rand/rand_core" } rand_os = { path = "../rand/rand_os" } diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index ee410e5..f0e0f6e 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -60,16 +60,19 @@ fn set_log_level(ctx: &mut args::ExecCtx<'_>) { /// Connect to any Nitrokey device and do something with it. fn with_device(ctx: &mut args::ExecCtx<'_>, op: F) -> Result<()> where - F: FnOnce(&mut args::ExecCtx<'_>, nitrokey::DeviceWrapper) -> Result<()>, + F: FnOnce(&mut args::ExecCtx<'_>, nitrokey::DeviceWrapper<'_>) -> Result<()>, { + let mut manager = nitrokey::take()?; set_log_level(ctx); let device = match ctx.model { - Some(model) => nitrokey::connect_model(model.into()).map_err(|_| { + Some(model) => manager.connect_model(model.into()).map_err(|_| { let error = format!("Nitrokey {} device not found", model.as_user_facing_str()); Error::Error(error) })?, - None => nitrokey::connect().map_err(|_| Error::from("Nitrokey device not found"))?, + None => manager + .connect() + .map_err(|_| Error::from("Nitrokey device not found"))?, }; op(ctx, device) @@ -78,8 +81,9 @@ where /// Connect to a Nitrokey Storage device and do something with it. fn with_storage_device(ctx: &mut args::ExecCtx<'_>, op: F) -> Result<()> where - F: FnOnce(&mut args::ExecCtx<'_>, nitrokey::Storage) -> Result<()>, + F: FnOnce(&mut args::ExecCtx<'_>, nitrokey::Storage<'_>) -> Result<()>, { + let mut manager = nitrokey::take()?; set_log_level(ctx); if let Some(model) = ctx.model { @@ -90,8 +94,9 @@ where } } - let device = - nitrokey::Storage::connect().map_err(|_| Error::from("Nitrokey Storage device not found"))?; + let device = manager + .connect_storage() + .map_err(|_| Error::from("Nitrokey Storage device not found"))?; op(ctx, device) } @@ -99,7 +104,7 @@ where /// do something with it. fn with_password_safe(ctx: &mut args::ExecCtx<'_>, mut op: F) -> Result<()> where - F: FnMut(&mut args::ExecCtx<'_>, nitrokey::PasswordSafe<'_>) -> Result<()>, + F: FnMut(&mut args::ExecCtx<'_>, nitrokey::PasswordSafe<'_, '_>) -> Result<()>, { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pinentry::PinType::User, &device)?; @@ -123,7 +128,7 @@ where /// Authenticate the given device using the given PIN type and operation. /// /// If an error occurs, the error message `msg` is used. -fn authenticate( +fn authenticate<'mgr, D, A, F>( ctx: &mut args::ExecCtx<'_>, device: D, pin_type: pinentry::PinType, @@ -131,7 +136,7 @@ fn authenticate( op: F, ) -> Result where - D: Device, + D: Device<'mgr>, F: FnMut(&mut args::ExecCtx<'_>, D, &str) -> result::Result, { let pin_entry = pinentry::PinEntry::from(pin_type, &device)?; @@ -140,9 +145,12 @@ where } /// Authenticate the given device with the user PIN. -fn authenticate_user(ctx: &mut args::ExecCtx<'_>, device: T) -> Result> +fn authenticate_user<'mgr, T>( + ctx: &mut args::ExecCtx<'_>, + device: T, +) -> Result> where - T: Device, + T: Device<'mgr>, { authenticate( ctx, @@ -154,9 +162,12 @@ where } /// Authenticate the given device with the admin PIN. -fn authenticate_admin(ctx: &mut args::ExecCtx<'_>, device: T) -> Result> +fn authenticate_admin<'mgr, T>( + ctx: &mut args::ExecCtx<'_>, + device: T, +) -> Result> where - T: Device, + T: Device<'mgr>, { authenticate( ctx, @@ -322,7 +333,7 @@ fn print_storage_status( fn print_status( ctx: &mut args::ExecCtx<'_>, model: &'static str, - device: &nitrokey::DeviceWrapper, + device: &nitrokey::DeviceWrapper<'_>, ) -> Result<()> { let serial_number = device .get_serial_number() @@ -700,7 +711,7 @@ pub fn otp_clear( fn print_otp_status( ctx: &mut args::ExecCtx<'_>, algorithm: args::OtpAlgorithm, - device: &nitrokey::DeviceWrapper, + device: &nitrokey::DeviceWrapper<'_>, all: bool, ) -> Result<()> { let mut slot: u8 = 0; @@ -843,7 +854,7 @@ fn print_pws_data( Ok(()) } -fn check_slot(pws: &nitrokey::PasswordSafe<'_>, slot: u8) -> Result<()> { +fn check_slot(pws: &nitrokey::PasswordSafe<'_, '_>, slot: u8) -> Result<()> { if slot >= nitrokey::SLOT_COUNT { return Err(nitrokey::Error::from(nitrokey::LibraryError::InvalidSlot).into()); } @@ -912,7 +923,7 @@ pub fn pws_clear(ctx: &mut args::ExecCtx<'_>, slot: u8) -> Result<()> { fn print_pws_slot( ctx: &mut args::ExecCtx<'_>, - pws: &nitrokey::PasswordSafe<'_>, + pws: &nitrokey::PasswordSafe<'_, '_>, slot: usize, programmed: bool, ) -> Result<()> { diff --git a/nitrocli/src/pinentry.rs b/nitrocli/src/pinentry.rs index 0733cd1..af28e2d 100644 --- a/nitrocli/src/pinentry.rs +++ b/nitrocli/src/pinentry.rs @@ -58,9 +58,9 @@ pub struct PinEntry { } impl PinEntry { - pub fn from(pin_type: PinType, device: &D) -> crate::Result + pub fn from<'mgr, D>(pin_type: PinType, device: &D) -> crate::Result where - D: nitrokey::Device, + D: nitrokey::Device<'mgr>, { let model = device.get_model(); let serial = device.get_serial_number()?; @@ -131,9 +131,9 @@ pub struct PwdEntry { } impl PwdEntry { - pub fn from(device: &D) -> crate::Result + pub fn from<'mgr, D>(device: &D) -> crate::Result where - D: nitrokey::Device, + D: nitrokey::Device<'mgr>, { let model = device.get_model(); let serial = device.get_serial_number()?; diff --git a/nitrocli/src/tests/config.rs b/nitrocli/src/tests/config.rs index 8983cb8..ea3a0e8 100644 --- a/nitrocli/src/tests/config.rs +++ b/nitrocli/src/tests/config.rs @@ -20,7 +20,7 @@ use super::*; #[test_device] -fn get(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn get(model: nitrokey::Model) -> crate::Result<()> { let re = regex::Regex::new( r#"^Config: numlock binding: (not set|\d+) @@ -31,14 +31,14 @@ $"#, ) .unwrap(); - let out = Nitrocli::with_dev(device).handle(&["config", "get"])?; + let out = Nitrocli::with_model(model).handle(&["config", "get"])?; assert!(re.is_match(&out), out); Ok(()) } #[test_device] -fn set_wrong_usage(device: nitrokey::DeviceWrapper) { - let res = Nitrocli::with_dev(device).handle(&["config", "set", "--numlock", "2", "-N"]); +fn set_wrong_usage(model: nitrokey::Model) { + let res = Nitrocli::with_model(model).handle(&["config", "set", "--numlock", "2", "-N"]); assert_eq!( res.unwrap_str_err(), "--numlock and --no-numlock are mutually exclusive" @@ -46,8 +46,8 @@ fn set_wrong_usage(device: nitrokey::DeviceWrapper) { } #[test_device] -fn set_get(device: nitrokey::DeviceWrapper) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +fn set_get(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["config", "set", "-s", "1", "-c", "0", "-N"])?; let re = regex::Regex::new( diff --git a/nitrocli/src/tests/encrypted.rs b/nitrocli/src/tests/encrypted.rs index 8aef864..75b84c3 100644 --- a/nitrocli/src/tests/encrypted.rs +++ b/nitrocli/src/tests/encrypted.rs @@ -19,8 +19,8 @@ use super::*; -#[test_device] -fn status_open_close(device: nitrokey::Storage) -> crate::Result<()> { +#[test_device(storage)] +fn status_open_close(model: nitrokey::Model) -> crate::Result<()> { fn make_re(open: Option) -> regex::Regex { let encrypted = match open { Some(open) => { @@ -44,7 +44,7 @@ $"#, regex::Regex::new(&re).unwrap() } - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); let out = ncli.handle(&["status"])?; assert!(make_re(None).is_match(&out), out); @@ -59,32 +59,37 @@ $"#, Ok(()) } -#[test_device] -fn encrypted_open_on_pro(device: nitrokey::Pro) { - let res = Nitrocli::with_dev(device).handle(&["encrypted", "open"]); +#[test_device(pro)] +fn encrypted_open_on_pro(model: nitrokey::Model) { + let res = Nitrocli::with_model(model).handle(&["encrypted", "open"]); assert_eq!( res.unwrap_str_err(), "This command is only available on the Nitrokey Storage", ); } -#[test_device] -fn encrypted_open_close(device: nitrokey::Storage) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +#[test_device(storage)] +fn encrypted_open_close(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let out = ncli.handle(&["encrypted", "open"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; - assert!(device.get_status()?.encrypted_volume.active); - assert!(!device.get_status()?.hidden_volume.active); - drop(device); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; + assert!(device.get_status()?.encrypted_volume.active); + assert!(!device.get_status()?.hidden_volume.active); + } let out = ncli.handle(&["encrypted", "close"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; - assert!(!device.get_status()?.encrypted_volume.active); - assert!(!device.get_status()?.hidden_volume.active); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; + assert!(!device.get_status()?.encrypted_volume.active); + assert!(!device.get_status()?.hidden_volume.active); + } Ok(()) } diff --git a/nitrocli/src/tests/hidden.rs b/nitrocli/src/tests/hidden.rs index 483a801..28a5d23 100644 --- a/nitrocli/src/tests/hidden.rs +++ b/nitrocli/src/tests/hidden.rs @@ -19,26 +19,31 @@ use super::*; -#[test_device] -fn hidden_create_open_close(device: nitrokey::Storage) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +#[test_device(storage)] +fn hidden_create_open_close(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let out = ncli.handle(&["hidden", "create", "0", "50", "100"])?; assert!(out.is_empty()); let out = ncli.handle(&["hidden", "open"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; - assert!(!device.get_status()?.encrypted_volume.active); - assert!(device.get_status()?.hidden_volume.active); - drop(device); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; + assert!(!device.get_status()?.encrypted_volume.active); + assert!(device.get_status()?.hidden_volume.active); + } let out = ncli.handle(&["hidden", "close"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; - assert!(!device.get_status()?.encrypted_volume.active); - assert!(!device.get_status()?.hidden_volume.active); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; + assert!(!device.get_status()?.encrypted_volume.active); + assert!(!device.get_status()?.hidden_volume.active); + } Ok(()) } diff --git a/nitrocli/src/tests/lock.rs b/nitrocli/src/tests/lock.rs index d23d2ae..5140152 100644 --- a/nitrocli/src/tests/lock.rs +++ b/nitrocli/src/tests/lock.rs @@ -19,24 +19,25 @@ use super::*; -#[test_device] -fn lock_pro(device: nitrokey::Pro) -> crate::Result<()> { +#[test_device(pro)] +fn lock_pro(model: nitrokey::Model) -> crate::Result<()> { // We can't really test much more here than just success of the command. - let out = Nitrocli::with_dev(device).handle(&["lock"])?; + let out = Nitrocli::with_model(model).handle(&["lock"])?; assert!(out.is_empty()); Ok(()) } -#[test_device] -fn lock_storage(device: nitrokey::Storage) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +#[test_device(storage)] +fn lock_storage(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["encrypted", "open"])?; let out = ncli.handle(&["lock"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; assert!(!device.get_status()?.encrypted_volume.active); Ok(()) diff --git a/nitrocli/src/tests/mod.rs b/nitrocli/src/tests/mod.rs index e8af624..1e2fe26 100644 --- a/nitrocli/src/tests/mod.rs +++ b/nitrocli/src/tests/mod.rs @@ -97,21 +97,18 @@ impl Nitrocli { } } - pub fn with_dev(device: D) -> Self + pub fn with_model(model: M) -> Self where - D: nitrokey::Device, + M: Into, { - let result = Self { - model: Some(device.get_model()), + Self { + model: Some(model.into()), admin_pin: Some(nitrokey::DEFAULT_ADMIN_PIN.into()), user_pin: Some(nitrokey::DEFAULT_USER_PIN.into()), new_admin_pin: None, new_user_pin: None, password: Some("1234567".into()), - }; - - drop(device); - result + } } pub fn admin_pin(&mut self, pin: impl Into) { diff --git a/nitrocli/src/tests/otp.rs b/nitrocli/src/tests/otp.rs index 2893e80..0ccecf9 100644 --- a/nitrocli/src/tests/otp.rs +++ b/nitrocli/src/tests/otp.rs @@ -22,8 +22,8 @@ use super::*; use crate::args; #[test_device] -fn set_invalid_slot_raw(device: nitrokey::DeviceWrapper) { - let (rc, out, err) = Nitrocli::with_dev(device).run(&["otp", "set", "100", "name", "1234"]); +fn set_invalid_slot_raw(model: nitrokey::Model) { + let (rc, out, err) = Nitrocli::with_model(model).run(&["otp", "set", "100", "name", "1234"]); assert_ne!(rc, 0); assert_eq!(out, b""); @@ -31,8 +31,8 @@ fn set_invalid_slot_raw(device: nitrokey::DeviceWrapper) { } #[test_device] -fn set_invalid_slot(device: nitrokey::DeviceWrapper) { - let res = Nitrocli::with_dev(device).handle(&["otp", "set", "100", "name", "1234"]); +fn set_invalid_slot(model: nitrokey::Model) { + let res = Nitrocli::with_model(model).handle(&["otp", "set", "100", "name", "1234"]); assert_eq!( res.unwrap_lib_err(), @@ -44,14 +44,14 @@ fn set_invalid_slot(device: nitrokey::DeviceWrapper) { } #[test_device] -fn status(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn status(model: nitrokey::Model) -> crate::Result<()> { let re = regex::Regex::new( r#"^alg\tslot\tname ((totp|hotp)\t\d+\t.+\n)+$"#, ) .unwrap(); - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); // Make sure that we have at least something to display by ensuring // that there is one slot programmed. let _ = ncli.handle(&["otp", "set", "0", "the-name", "123456"])?; @@ -62,14 +62,14 @@ fn status(device: nitrokey::DeviceWrapper) -> crate::Result<()> { } #[test_device] -fn set_get_hotp(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn set_get_hotp(model: nitrokey::Model) -> crate::Result<()> { // Secret and expected HOTP values as per RFC 4226: Appendix D -- HOTP // Algorithm: Test Values. const SECRET: &str = "12345678901234567890"; const OTP1: &str = concat!(755224, "\n"); const OTP2: &str = concat!(287082, "\n"); - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&[ "otp", "set", "-a", "hotp", "-f", "ascii", "1", "name", &SECRET, ])?; @@ -83,14 +83,14 @@ fn set_get_hotp(device: nitrokey::DeviceWrapper) -> crate::Result<()> { } #[test_device] -fn set_get_totp(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn set_get_totp(model: nitrokey::Model) -> crate::Result<()> { // Secret and expected TOTP values as per RFC 6238: Appendix B -- // Test Vectors. const SECRET: &str = "12345678901234567890"; const TIME: &str = stringify!(1111111111); const OTP: &str = concat!(14050471, "\n"); - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["otp", "set", "-d", "8", "-f", "ascii", "2", "name", &SECRET])?; let out = ncli.handle(&["otp", "get", "-t", TIME, "2"])?; @@ -99,22 +99,22 @@ fn set_get_totp(device: nitrokey::DeviceWrapper) -> crate::Result<()> { } #[test_device] -fn set_totp_uneven_chars(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn set_totp_uneven_chars(model: nitrokey::Model) -> crate::Result<()> { let secrets = [ (args::OtpSecretFormat::Hex, "123"), (args::OtpSecretFormat::Base32, "FBILDWWGA2"), ]; - let mut ncli = Nitrocli::with_dev(device); for (format, secret) in &secrets { + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["otp", "set", "-f", format.as_ref(), "3", "foobar", &secret])?; } Ok(()) } #[test_device] -fn clear(device: nitrokey::DeviceWrapper) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +fn clear(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["otp", "set", "3", "hotp-test", "abcdef"])?; let _ = ncli.handle(&["otp", "clear", "3"])?; let res = ncli.handle(&["otp", "get", "3"]); diff --git a/nitrocli/src/tests/pin.rs b/nitrocli/src/tests/pin.rs index e4cd316..958a36d 100644 --- a/nitrocli/src/tests/pin.rs +++ b/nitrocli/src/tests/pin.rs @@ -23,40 +23,48 @@ use nitrokey::Device; use super::*; #[test_device] -fn unblock(device: nitrokey::DeviceWrapper) -> crate::Result<()> { - let (device, err) = device.authenticate_user("wrong-pin").unwrap_err(); - match err { - nitrokey::Error::CommandError(err) if err == nitrokey::CommandError::WrongPassword => (), - _ => panic!("Unexpected error variant found: {:?}", err), +fn unblock(model: nitrokey::Model) -> crate::Result<()> { + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_model(model)?; + let (device, err) = device.authenticate_user("wrong-pin").unwrap_err(); + match err { + nitrokey::Error::CommandError(err) if err == nitrokey::CommandError::WrongPassword => (), + _ => panic!("Unexpected error variant found: {:?}", err), + } + assert!(device.get_user_retry_count()? < 3); } - assert!(device.get_user_retry_count()? < 3); - let model = device.get_model(); - let _ = Nitrocli::with_dev(device).handle(&["pin", "unblock"])?; - let device = nitrokey::connect_model(model)?; - assert_eq!(device.get_user_retry_count()?, 3); + let _ = Nitrocli::with_model(model).handle(&["pin", "unblock"])?; + + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_model(model)?; + assert_eq!(device.get_user_retry_count()?, 3); + } Ok(()) } #[test_device] -fn set_user(device: nitrokey::DeviceWrapper) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); - +fn set_user(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); // Set a new user PIN. ncli.new_user_pin("new-pin"); let out = ncli.handle(&["pin", "set", "user"])?; assert!(out.is_empty()); - let device = nitrokey::connect_model(ncli.model().unwrap())?; - let (device, err) = device - .authenticate_user(nitrokey::DEFAULT_USER_PIN) - .unwrap_err(); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_model(model)?; + let (_, err) = device + .authenticate_user(nitrokey::DEFAULT_USER_PIN) + .unwrap_err(); - match err { - nitrokey::Error::CommandError(err) if err == nitrokey::CommandError::WrongPassword => (), - _ => panic!("Unexpected error variant found: {:?}", err), + match err { + nitrokey::Error::CommandError(err) if err == nitrokey::CommandError::WrongPassword => (), + _ => panic!("Unexpected error variant found: {:?}", err), + } } - drop(device); // Revert to the default user PIN. ncli.user_pin("new-pin"); @@ -65,9 +73,12 @@ fn set_user(device: nitrokey::DeviceWrapper) -> crate::Result<()> { let out = ncli.handle(&["pin", "set", "user"])?; assert!(out.is_empty()); - let device = nitrokey::connect_model(ncli.model().unwrap())?; - let _ = device - .authenticate_user(nitrokey::DEFAULT_USER_PIN) - .unwrap(); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_model(ncli.model().unwrap())?; + let _ = device + .authenticate_user(nitrokey::DEFAULT_USER_PIN) + .unwrap(); + } Ok(()) } diff --git a/nitrocli/src/tests/pws.rs b/nitrocli/src/tests/pws.rs index 9468dcf..651b2d5 100644 --- a/nitrocli/src/tests/pws.rs +++ b/nitrocli/src/tests/pws.rs @@ -20,8 +20,8 @@ use super::*; #[test_device] -fn set_invalid_slot(device: nitrokey::DeviceWrapper) { - let res = Nitrocli::with_dev(device).handle(&["pws", "set", "100", "name", "login", "1234"]); +fn set_invalid_slot(model: nitrokey::Model) { + let res = Nitrocli::with_model(model).handle(&["pws", "set", "100", "name", "login", "1234"]); assert_eq!( res.unwrap_lib_err(), @@ -33,14 +33,14 @@ fn set_invalid_slot(device: nitrokey::DeviceWrapper) { } #[test_device] -fn status(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn status(model: nitrokey::Model) -> crate::Result<()> { let re = regex::Regex::new( r#"^slot\tname (\d+\t.+\n)+$"#, ) .unwrap(); - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); // Make sure that we have at least something to display by ensuring // that there are there is one slot programmed. let _ = ncli.handle(&["pws", "set", "0", "the-name", "the-login", "123456"])?; @@ -51,12 +51,12 @@ fn status(device: nitrokey::DeviceWrapper) -> crate::Result<()> { } #[test_device] -fn set_get(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn set_get(model: nitrokey::Model) -> crate::Result<()> { const NAME: &str = "dropbox"; const LOGIN: &str = "d-e-s-o"; const PASSWORD: &str = "my-secret-password"; - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["pws", "set", "1", &NAME, &LOGIN, &PASSWORD])?; let out = ncli.handle(&["pws", "get", "1", "--quiet", "--name"])?; @@ -83,12 +83,12 @@ fn set_get(device: nitrokey::DeviceWrapper) -> crate::Result<()> { } #[test_device] -fn set_reset_get(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn set_reset_get(model: nitrokey::Model) -> crate::Result<()> { const NAME: &str = "some/svc"; const LOGIN: &str = "a\\user"; const PASSWORD: &str = "!@&-)*(&+%^@"; - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["pws", "set", "2", &NAME, &LOGIN, &PASSWORD])?; let out = ncli.handle(&["reset"])?; @@ -106,8 +106,8 @@ fn set_reset_get(device: nitrokey::DeviceWrapper) -> crate::Result<()> { } #[test_device] -fn clear(device: nitrokey::DeviceWrapper) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +fn clear(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let _ = ncli.handle(&["pws", "set", "10", "clear-test", "some-login", "abcdef"])?; let _ = ncli.handle(&["pws", "clear", "10"])?; let res = ncli.handle(&["pws", "get", "10"]); diff --git a/nitrocli/src/tests/reset.rs b/nitrocli/src/tests/reset.rs index f9452ec..e197970 100644 --- a/nitrocli/src/tests/reset.rs +++ b/nitrocli/src/tests/reset.rs @@ -23,32 +23,38 @@ use nitrokey::GetPasswordSafe; use super::*; #[test_device] -fn reset(device: nitrokey::DeviceWrapper) -> crate::Result<()> { +fn reset(model: nitrokey::Model) -> crate::Result<()> { let new_admin_pin = "87654321"; - let mut ncli = Nitrocli::with_dev(device); + let mut ncli = Nitrocli::with_model(model); // Change the admin PIN. ncli.new_admin_pin(new_admin_pin); let _ = ncli.handle(&["pin", "set", "admin"])?; - // Check that the admin PIN has been changed. - let device = nitrokey::connect_model(ncli.model().unwrap())?; - let _ = device.authenticate_admin(new_admin_pin).unwrap(); + { + let mut manager = nitrokey::force_take()?; + // Check that the admin PIN has been changed. + let device = manager.connect_model(ncli.model().unwrap())?; + let _ = device.authenticate_admin(new_admin_pin).unwrap(); + } // Perform factory reset ncli.admin_pin(new_admin_pin); let out = ncli.handle(&["reset"])?; assert!(out.is_empty()); - // Check that the admin PIN has been reset. - let device = nitrokey::connect_model(ncli.model().unwrap())?; - let mut device = device - .authenticate_admin(nitrokey::DEFAULT_ADMIN_PIN) - .unwrap(); - - // Check that the password store works, i.e., the AES key has been - // built. - let _ = device.get_password_safe(nitrokey::DEFAULT_USER_PIN)?; + { + let mut manager = nitrokey::force_take()?; + // Check that the admin PIN has been reset. + let device = manager.connect_model(ncli.model().unwrap())?; + let mut device = device + .authenticate_admin(nitrokey::DEFAULT_ADMIN_PIN) + .unwrap(); + + // Check that the password store works, i.e., the AES key has been + // built. + let _ = device.get_password_safe(nitrokey::DEFAULT_USER_PIN)?; + } Ok(()) } diff --git a/nitrocli/src/tests/status.rs b/nitrocli/src/tests/status.rs index 7aac5ad..c9f4976 100644 --- a/nitrocli/src/tests/status.rs +++ b/nitrocli/src/tests/status.rs @@ -36,31 +36,31 @@ fn not_found() { assert_eq!(res.unwrap_str_err(), "Nitrokey device not found"); } -#[test_device] -fn output_pro(device: nitrokey::Pro) -> crate::Result<()> { +#[test_device(pro)] +fn output_pro(model: nitrokey::Model) -> crate::Result<()> { let re = regex::Regex::new( r#"^Status: model: Pro serial number: 0x[[:xdigit:]]{8} - firmware version: \d+\.\d+ + firmware version: v\d+\.\d+ user retry count: [0-3] admin retry count: [0-3] $"#, ) .unwrap(); - let out = Nitrocli::with_dev(device).handle(&["status"])?; + let out = Nitrocli::with_model(model).handle(&["status"])?; assert!(re.is_match(&out), out); Ok(()) } -#[test_device] -fn output_storage(device: nitrokey::Storage) -> crate::Result<()> { +#[test_device(storage)] +fn output_storage(model: nitrokey::Model) -> crate::Result<()> { let re = regex::Regex::new( r#"^Status: model: Storage serial number: 0x[[:xdigit:]]{8} - firmware version: \d+\.\d+ + firmware version: v\d+\.\d+ user retry count: [0-3] admin retry count: [0-3] Storage: @@ -75,7 +75,7 @@ $"#, ) .unwrap(); - let out = Nitrocli::with_dev(device).handle(&["status"])?; + let out = Nitrocli::with_model(model).handle(&["status"])?; assert!(re.is_match(&out), out); Ok(()) } diff --git a/nitrocli/src/tests/unencrypted.rs b/nitrocli/src/tests/unencrypted.rs index c976f50..547dcaf 100644 --- a/nitrocli/src/tests/unencrypted.rs +++ b/nitrocli/src/tests/unencrypted.rs @@ -19,23 +19,28 @@ use super::*; -#[test_device] -fn unencrypted_set_read_write(device: nitrokey::Storage) -> crate::Result<()> { - let mut ncli = Nitrocli::with_dev(device); +#[test_device(storage)] +fn unencrypted_set_read_write(model: nitrokey::Model) -> crate::Result<()> { + let mut ncli = Nitrocli::with_model(model); let out = ncli.handle(&["unencrypted", "set", "read-write"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; - assert!(device.get_status()?.unencrypted_volume.active); - assert!(!device.get_status()?.unencrypted_volume.read_only); - drop(device); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; + assert!(device.get_status()?.unencrypted_volume.active); + assert!(!device.get_status()?.unencrypted_volume.read_only); + } let out = ncli.handle(&["unencrypted", "set", "read-only"])?; assert!(out.is_empty()); - let device = nitrokey::Storage::connect()?; - assert!(device.get_status()?.unencrypted_volume.active); - assert!(device.get_status()?.unencrypted_volume.read_only); + { + let mut manager = nitrokey::force_take()?; + let device = manager.connect_storage()?; + assert!(device.get_status()?.unencrypted_volume.active); + assert!(device.get_status()?.unencrypted_volume.read_only); + } Ok(()) } diff --git a/nitrokey-sys/CHANGELOG.md b/nitrokey-sys/CHANGELOG.md index a11d168..a440ca1 100644 --- a/nitrokey-sys/CHANGELOG.md +++ b/nitrokey-sys/CHANGELOG.md @@ -1,3 +1,29 @@ +# v3.5.0 (2019-07-04) +- Mark deprecated functions using the `deprecated` attribute. +- Update to libnitrokey 3.5, causing all following changes. +- New constant `NK_PWS_SLOT_COUNT`. +- New structures: + - `NK_device_info` + - `NK_status` + - `NK_SD_usage_data` + - `ReadSlot_t` +- New functions: + - `NK_get_SD_usage_data` + - `NK_get_status` + - `NK_get_status_as_string` + - `NK_list_devices` + - `NK_free_device_info` + - `NK_connect_with_path` + - `NK_enable_firmware_update_pro` + - `NK_change_firmware_password_pro` + - `NK_read_HOTP_slot` +- Deprecated functions: + - `NK_status` +- Changed the return type for `NK_get_major_firmware_version` and + `NK_get_minor_firmware_version` to `u8`. +- Changed `NK_get_progress_bar_value` to return -2 instead of 0 if an error + occurs. + # v3.4.3 (2019-10-12) - Link directly against `libnitrokey` if the `USE_SYSTEM_LIBNITROKEY` environment variable is set. diff --git a/nitrokey-sys/Cargo.toml b/nitrokey-sys/Cargo.toml index f9d304b..068670b 100644 --- a/nitrokey-sys/Cargo.toml +++ b/nitrokey-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nitrokey-sys" -version = "3.4.3" +version = "3.5.0" authors = ["Robin Krahl "] edition = "2018" homepage = "https://code.ireas.org/nitrokey-rs/" diff --git a/nitrokey-sys/build.rs b/nitrokey-sys/build.rs index defce72..a9d0778 100644 --- a/nitrokey-sys/build.rs +++ b/nitrokey-sys/build.rs @@ -25,8 +25,8 @@ impl string::ToString for Version { const LIBNITROKEY_VERSION: Version = Version { major: 3, - minor: 4, - patch: Some(1), + minor: 5, + patch: None, }; fn prepare_version_source( diff --git a/nitrokey-sys/libnitrokey-v3.4.1/DeviceCommunicationExceptions.cpp b/nitrokey-sys/libnitrokey-v3.4.1/DeviceCommunicationExceptions.cpp deleted file mode 100644 index 4d62aad..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/DeviceCommunicationExceptions.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 - */ - -#include "DeviceCommunicationExceptions.h" - -std::atomic_int DeviceCommunicationException::occurred {0}; diff --git a/nitrokey-sys/libnitrokey-v3.4.1/LICENSE b/nitrokey-sys/libnitrokey-v3.4.1/LICENSE deleted file mode 100644 index 341c30b..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/LICENSE +++ /dev/null @@ -1,166 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. - diff --git a/nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.cc b/nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.cc deleted file mode 100644 index 7d0a10e..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.cc +++ /dev/null @@ -1,755 +0,0 @@ -/* - * 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 - */ - -#include "NK_C_API.h" -#include -#include -#include "libnitrokey/NitrokeyManager.h" -#include -#include "libnitrokey/LibraryException.h" -#include "libnitrokey/cxx_semantics.h" -#include "libnitrokey/stick20_commands.h" -#include "version.h" - -#ifdef _MSC_VER -#ifdef _WIN32 -#pragma message "Using own strndup" -char * strndup(const char* str, size_t maxlen) { - size_t len = strnlen(str, maxlen); - char* dup = (char *)malloc(len + 1); - memcpy(dup, str, len); - dup[len] = 0; - return dup; -} -#endif -#endif - -using namespace nitrokey; - -static uint8_t NK_last_command_status = 0; -static const int max_string_field_length = 100; - -template -T* duplicate_vector_and_clear(std::vector &v){ - auto d = new T[v.size()]; - std::copy(v.begin(), v.end(), d); - std::fill(v.begin(), v.end(), 0); - return d; -} - -template -std::tuple get_with_status(T func, R fallback) { - NK_last_command_status = 0; - try { - return std::make_tuple(0, func()); - } - catch (CommandFailedException & commandFailedException){ - NK_last_command_status = commandFailedException.last_command_status; - } - catch (LibraryException & libraryException){ - NK_last_command_status = libraryException.exception_id(); - } - catch (const DeviceCommunicationException &deviceException){ - NK_last_command_status = 256-deviceException.getType(); - } - return std::make_tuple(NK_last_command_status, fallback); -} - -template -uint8_t * get_with_array_result(T func){ - return std::get<1>(get_with_status(func, nullptr)); -} - -template -char* get_with_string_result(T func){ - auto result = std::get<1>(get_with_status(func, nullptr)); - if (result == nullptr) { - return strndup("", MAXIMUM_STR_REPLY_LENGTH); - } - return result; -} - -template -auto get_with_result(T func){ - return std::get<1>(get_with_status(func, static_cast(0))); -} - -template -uint8_t get_without_result(T func){ - NK_last_command_status = 0; - try { - func(); - return 0; - } - catch (CommandFailedException & commandFailedException){ - NK_last_command_status = commandFailedException.last_command_status; - } - catch (LibraryException & libraryException){ - NK_last_command_status = libraryException.exception_id(); - } - catch (const InvalidCRCReceived &invalidCRCException){ - ; - } - catch (const DeviceCommunicationException &deviceException){ - NK_last_command_status = 256-deviceException.getType(); - } - return NK_last_command_status; -} - - -#ifdef __cplusplus -extern "C" { -#endif - - NK_C_API uint8_t NK_get_last_command_status() { - auto _copy = NK_last_command_status; - NK_last_command_status = 0; - return _copy; - } - - NK_C_API int NK_login(const char *device_model) { - auto m = NitrokeyManager::instance(); - try { - NK_last_command_status = 0; - return m->connect(device_model); - } - catch (CommandFailedException & commandFailedException) { - NK_last_command_status = commandFailedException.last_command_status; - return commandFailedException.last_command_status; - } - catch (const DeviceCommunicationException &deviceException){ - NK_last_command_status = 256-deviceException.getType(); - cerr << deviceException.what() << endl; - return 0; - } - catch (std::runtime_error &e) { - cerr << e.what() << endl; - return 0; - } - return 0; - } - - NK_C_API int NK_login_enum(NK_device_model device_model) { - const char *model_string; - switch (device_model) { - case NK_PRO: - model_string = "P"; - break; - case NK_STORAGE: - model_string = "S"; - break; - case NK_DISCONNECTED: - default: - /* no such enum value -- return error code */ - return 0; - } - return NK_login(model_string); - } - - NK_C_API int NK_logout() { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->disconnect(); - }); - } - - NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - return m->first_authenticate(admin_password, admin_temporary_password); - }); - } - - - NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->user_authenticate(user_password, user_temporary_password); - }); - } - - NK_C_API int NK_factory_reset(const char* admin_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->factory_reset(admin_password); - }); - } - NK_C_API int NK_build_aes_key(const char* admin_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->build_aes_key(admin_password); - }); - } - - NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->unlock_user_password(admin_password, new_user_password); - }); - } - - NK_C_API int 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) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - return m->write_config(numlock, capslock, scrolllock, enable_user_password, delete_user_password, admin_temporary_password); - }); - } - - - NK_C_API uint8_t* NK_read_config() { - auto m = NitrokeyManager::instance(); - return get_with_array_result([&]() { - auto v = m->read_config(); - return duplicate_vector_and_clear(v); - }); - } - - - NK_C_API enum NK_device_model NK_get_device_model() { - auto m = NitrokeyManager::instance(); - try { - auto model = m->get_connected_device_model(); - switch (model) { - case DeviceModel::PRO: - return NK_PRO; - case DeviceModel::STORAGE: - return NK_STORAGE; - default: - /* unknown or not connected device */ - return NK_device_model::NK_DISCONNECTED; - } - } catch (const DeviceNotConnected& e) { - return NK_device_model::NK_DISCONNECTED; - } -} - - - void clear_string(std::string &s) { - std::fill(s.begin(), s.end(), ' '); - } - - - NK_C_API char * NK_status() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - string && s = m->get_status_as_string(); - char * rs = strndup(s.c_str(), MAXIMUM_STR_REPLY_LENGTH); - clear_string(s); - return rs; - }); - } - - NK_C_API char * NK_device_serial_number() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - string && s = m->get_serial_number(); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); - } - - NK_C_API char * NK_get_hotp_code(uint8_t slot_number) { - return NK_get_hotp_code_PIN(slot_number, ""); - } - - NK_C_API char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - string && s = m->get_HOTP_code(slot_number, user_temporary_password); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); - } - - NK_C_API char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval) { - return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, ""); - } - - NK_C_API char * 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) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - string && s = m->get_TOTP_code(slot_number, challenge, last_totp_time, last_interval, user_temporary_password); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); - } - - NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&] { - m->erase_hotp_slot(slot_number, temporary_password); - }); - } - - NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&] { - m->erase_totp_slot(slot_number, temporary_password); - }); - } - - NK_C_API int 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) { - auto m = NitrokeyManager::instance(); - return get_without_result([&] { - m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, token_ID, - temporary_password); - }); - } - - NK_C_API int 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) { - auto m = NitrokeyManager::instance(); - return get_without_result([&] { - m->write_TOTP_slot(slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, token_ID, - temporary_password); - }); - } - - NK_C_API char* NK_get_totp_slot_name(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - const auto slot_name = m->get_totp_slot_name(slot_number); - return slot_name; - }); - } - NK_C_API char* NK_get_hotp_slot_name(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - const auto slot_name = m->get_hotp_slot_name(slot_number); - return slot_name; - }); - } - - NK_C_API void NK_set_debug(bool state) { - auto m = NitrokeyManager::instance(); - m->set_debug(state); - } - - - NK_C_API void NK_set_debug_level(const int level) { - auto m = NitrokeyManager::instance(); - m->set_loglevel(level); - } - - NK_C_API unsigned int NK_get_major_library_version() { - return get_major_library_version(); - } - - NK_C_API unsigned int NK_get_minor_library_version() { - return get_minor_library_version(); - } - - NK_C_API const char* NK_get_library_version() { - return get_library_version(); - } - - NK_C_API int NK_totp_set_time(uint64_t time) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_time(time); - }); - } - - NK_C_API int NK_totp_set_time_soft(uint64_t time) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_time_soft(time); - }); - } - - NK_C_API int NK_totp_get_time() { - return 0; - } - - NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->change_admin_PIN(current_PIN, new_PIN); - }); - } - - NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->change_user_PIN(current_PIN, new_PIN); - }); - } - - NK_C_API int NK_enable_password_safe(const char *user_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->enable_password_safe(user_pin); - }); - } - NK_C_API uint8_t * NK_get_password_safe_slot_status() { - auto m = NitrokeyManager::instance(); - return get_with_array_result([&]() { - auto slot_status = m->get_password_safe_slot_status(); - return duplicate_vector_and_clear(slot_status); - }); - - } - - NK_C_API uint8_t NK_get_user_retry_count() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->get_user_retry_count(); - }); - } - - NK_C_API uint8_t NK_get_admin_retry_count() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->get_admin_retry_count(); - }); - } - - NK_C_API int NK_lock_device() { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->lock_device(); - }); - } - - NK_C_API char *NK_get_password_safe_slot_name(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - return m->get_password_safe_slot_name(slot_number); - }); - } - - NK_C_API char *NK_get_password_safe_slot_login(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - return m->get_password_safe_slot_login(slot_number); - }); - } - NK_C_API char *NK_get_password_safe_slot_password(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - return m->get_password_safe_slot_password(slot_number); - }); - } - NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, - const char *slot_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->write_password_safe_slot(slot_number, slot_name, slot_login, slot_password); - }); - } - - NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->erase_password_safe_slot(slot_number); - }); - } - - NK_C_API int NK_is_AES_supported(const char *user_password) { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return (uint8_t)m->is_AES_supported(user_password); - }); - } - - NK_C_API int NK_login_auto() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return (uint8_t)m->connect(); - }); - } - - // storage commands - - NK_C_API int NK_send_startup(uint64_t seconds_from_epoch) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->send_startup(seconds_from_epoch); - }); - } - - NK_C_API int NK_unlock_encrypted_volume(const char* user_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->unlock_encrypted_volume(user_pin); - }); - } - - NK_C_API int NK_lock_encrypted_volume() { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->lock_encrypted_volume(); - }); - } - - NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->unlock_hidden_volume(hidden_volume_password); - }); - } - - NK_C_API int NK_lock_hidden_volume() { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->lock_hidden_volume(); - }); - } - - NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, - const char *hidden_volume_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->create_hidden_volume(slot_nr, start_percent, end_percent, - hidden_volume_password); - }); - } - - NK_C_API int NK_set_unencrypted_read_only(const char *user_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_unencrypted_read_only(user_pin); - }); - } - - NK_C_API int NK_set_unencrypted_read_write(const char *user_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_unencrypted_read_write(user_pin); - }); - } - - NK_C_API int NK_set_unencrypted_read_only_admin(const char *admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_unencrypted_read_only_admin(admin_pin); - }); - } - - NK_C_API int NK_set_unencrypted_read_write_admin(const char *admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_unencrypted_read_write_admin(admin_pin); - }); - } - - NK_C_API int NK_set_encrypted_read_only(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_encrypted_volume_read_only(admin_pin); - }); - } - - NK_C_API int NK_set_encrypted_read_write(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->set_encrypted_volume_read_write(admin_pin); - }); - } - - NK_C_API int NK_export_firmware(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->export_firmware(admin_pin); - }); - } - - NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->clear_new_sd_card_warning(admin_pin); - }); - } - - NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->fill_SD_card_with_random_data(admin_pin); - }); - } - - NK_C_API int NK_change_update_password(const char* current_update_password, - const char* new_update_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->change_update_password(current_update_password, new_update_password); - }); - } - - NK_C_API int NK_enable_firmware_update(const char* update_password){ - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - m->enable_firmware_update(update_password); - }); - } - - NK_C_API char* NK_get_status_storage_as_string() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - return m->get_status_storage_as_string(); - }); - } - - NK_C_API int NK_get_status_storage(NK_storage_status* out) { - if (out == nullptr) { - return -1; - } - auto m = NitrokeyManager::instance(); - auto result = get_with_status([&]() { - return m->get_status_storage(); - }, proto::stick20::DeviceConfigurationResponsePacket::ResponsePayload()); - auto error_code = std::get<0>(result); - if (error_code != 0) { - return error_code; - } - - auto status = std::get<1>(result); - out->unencrypted_volume_read_only = status.ReadWriteFlagUncryptedVolume_u8 != 0; - out->unencrypted_volume_active = status.VolumeActiceFlag_st.unencrypted; - out->encrypted_volume_read_only = status.ReadWriteFlagCryptedVolume_u8 != 0; - out->encrypted_volume_active = status.VolumeActiceFlag_st.encrypted; - out->hidden_volume_read_only = status.ReadWriteFlagHiddenVolume_u8 != 0; - out->hidden_volume_active = status.VolumeActiceFlag_st.hidden; - out->firmware_version_major = status.versionInfo.major; - out->firmware_version_minor = status.versionInfo.minor; - out->firmware_locked = status.FirmwareLocked_u8 != 0; - out->serial_number_sd_card = status.ActiveSD_CardID_u32; - out->serial_number_smart_card = status.ActiveSmartCardID_u32; - out->user_retry_count = status.UserPwRetryCount; - out->admin_retry_count = status.AdminPwRetryCount; - out->new_sd_card_found = status.NewSDCardFound_st.NewCard; - out->filled_with_random = (status.SDFillWithRandomChars_u8 & 0x01) != 0; - out->stick_initialized = status.StickKeysNotInitiated == 0; - return 0; - } - - NK_C_API int NK_get_storage_production_info(NK_storage_ProductionTest * out){ - if (out == nullptr) { - return -1; - } - auto m = NitrokeyManager::instance(); - auto result = get_with_status([&]() { - return m->production_info(); - }, proto::stick20::ProductionTest::ResponsePayload()); - - auto error_code = std::get<0>(result); - if (error_code != 0) { - return error_code; - } - - stick20::ProductionTest::ResponsePayload status = std::get<1>(result); - // Cannot use memcpy without declaring C API struct packed - // (which is not parsed by Python's CFFI apparently), hence the manual way. -#define a(x) out->x = status.x; - a(FirmwareVersion_au8[0]); - a(FirmwareVersion_au8[1]); - a(FirmwareVersionInternal_u8); - a(SD_Card_Size_u8); - a(CPU_CardID_u32); - a(SmartCardID_u32); - a(SD_CardID_u32); - a(SC_UserPwRetryCount); - a(SC_AdminPwRetryCount); - a(SD_Card_ManufacturingYear_u8); - a(SD_Card_ManufacturingMonth_u8); - a(SD_Card_OEM_u16); - a(SD_WriteSpeed_u16); - a(SD_Card_Manufacturer_u8); -#undef a - return 0; - } - - -NK_C_API char* NK_get_SD_usage_data_as_string() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - return m->get_SD_usage_data_as_string(); - }); - } - - NK_C_API int NK_get_progress_bar_value() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->get_progress_bar_value(); - }); - } - - NK_C_API int NK_get_major_firmware_version() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->get_major_firmware_version(); - }); - } - - NK_C_API int NK_get_minor_firmware_version() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->get_minor_firmware_version(); - }); - } - - NK_C_API int NK_set_unencrypted_volume_rorw_pin_type_user() { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->set_unencrypted_volume_rorw_pin_type_user() ? 1 : 0; - }); - } - - NK_C_API char* NK_list_devices_by_cpuID() { - auto nm = NitrokeyManager::instance(); - return get_with_string_result([&]() { - auto v = nm->list_devices_by_cpuID(); - std::string res; - for (const auto a : v){ - res += a+";"; - } - if (res.size()>0) res.pop_back(); // remove last delimiter char - return strndup(res.c_str(), MAXIMUM_STR_REPLY_LENGTH); - }); - } - - NK_C_API int NK_connect_with_ID(const char* id) { - auto m = NitrokeyManager::instance(); - return get_with_result([&]() { - return m->connect_with_ID(id) ? 1 : 0; - }); - } - - NK_C_API int NK_wink() { - auto m = NitrokeyManager::instance(); - return get_without_result([&]() { - return m->wink(); - }); - } - -#ifdef __cplusplus -} -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.h b/nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.h deleted file mode 100644 index b1bdf1e..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/NK_C_API.h +++ /dev/null @@ -1,792 +0,0 @@ -/* - * 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 - */ - -#ifndef LIBNITROKEY_NK_C_API_H -#define LIBNITROKEY_NK_C_API_H - -#include -#include - -#include "deprecated.h" - -#ifdef _MSC_VER -#define NK_C_API __declspec(dllexport) -#else -#define NK_C_API -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - static const int MAXIMUM_STR_REPLY_LENGTH = 8192; - - /** - * The Nitrokey device models supported by the API. - */ - enum NK_device_model { - /** - * Use, if no supported device is connected - */ - NK_DISCONNECTED = 0, - /** - * Nitrokey Pro. - */ - NK_PRO = 1, - /** - * Nitrokey Storage. - */ - NK_STORAGE = 2 - }; - - /** - * Stores the status of a Storage device. - */ - struct NK_storage_status { - /** - * Indicates whether the unencrypted volume is read-only. - */ - bool unencrypted_volume_read_only; - /** - * Indicates whether the unencrypted volume is active. - */ - bool unencrypted_volume_active; - /** - * Indicates whether the encrypted volume is read-only. - */ - bool encrypted_volume_read_only; - /** - * Indicates whether the encrypted volume is active. - */ - bool encrypted_volume_active; - /** - * Indicates whether the hidden volume is read-only. - */ - bool hidden_volume_read_only; - /** - * Indicates whether the hidden volume is active. - */ - bool hidden_volume_active; - /** - * The major firmware version, e. g. 0 in v0.40. - */ - uint8_t firmware_version_major; - /** - * The minor firmware version, e. g. 40 in v0.40. - */ - uint8_t firmware_version_minor; - /** - * Indicates whether the firmware is locked. - */ - bool firmware_locked; - /** - * The serial number of the SD card in the Storage stick. - */ - uint32_t serial_number_sd_card; - /** - * The serial number of the smart card in the Storage stick. - */ - uint32_t serial_number_smart_card; - /** - * The number of remaining login attempts for the user PIN. - */ - uint8_t user_retry_count; - /** - * The number of remaining login attempts for the admin PIN. - */ - uint8_t admin_retry_count; - /** - * Indicates whether a new SD card was found. - */ - bool new_sd_card_found; - /** - * Indicates whether the SD card is filled with random characters. - */ - bool filled_with_random; - /** - * Indicates whether the stick has been initialized by generating - * the AES keys. - */ - bool stick_initialized; - }; - - struct NK_storage_ProductionTest{ - uint8_t FirmwareVersion_au8[2]; - uint8_t FirmwareVersionInternal_u8; - uint8_t SD_Card_Size_u8; - uint32_t CPU_CardID_u32; - uint32_t SmartCardID_u32; - uint32_t SD_CardID_u32; - uint8_t SC_UserPwRetryCount; - uint8_t SC_AdminPwRetryCount; - uint8_t SD_Card_ManufacturingYear_u8; - uint8_t SD_Card_ManufacturingMonth_u8; - uint16_t SD_Card_OEM_u16; - uint16_t SD_WriteSpeed_u16; - uint8_t SD_Card_Manufacturer_u8; - }; - - NK_C_API int NK_get_storage_production_info(struct NK_storage_ProductionTest * out); - - -/** - * Set debug level of messages written on stderr - * @param state state=True - most messages, state=False - only errors level - */ - NK_C_API void NK_set_debug(bool state); - - /** - * Set debug level of messages written on stderr - * @param level (int) 0-lowest verbosity, 5-highest verbosity - */ - NK_C_API void NK_set_debug_level(const int level); - - /** - * Get the major library version, e. g. the 3 in v3.2. - * @return the major library version - */ - NK_C_API unsigned int NK_get_major_library_version(); - - /** - * Get the minor library version, e. g. the 2 in v3.2. - * @return the minor library version - */ - NK_C_API unsigned int NK_get_minor_library_version(); - - /** - * Get the library version as a string. This is the output of - * `git describe --always` at compile time, for example "v3.3" or - * "v3.3-19-gaee920b". - * The return value is a string literal and must not be freed. - * @return the library version as a string - */ - NK_C_API const char* NK_get_library_version(); - - /** - * Connect to device of given model. Currently library can be connected only to one device at once. - * @param device_model char 'S': Nitrokey Storage, 'P': Nitrokey Pro - * @return 1 if connected, 0 if wrong model or cannot connect - */ - NK_C_API int NK_login(const char *device_model); - - /** - * Connect to device of given model. Currently library can be connected only to one device at once. - * @param device_model NK_device_model: NK_PRO: Nitrokey Pro, NK_STORAGE: Nitrokey Storage - * @return 1 if connected, 0 if wrong model or cannot connect - */ - NK_C_API int NK_login_enum(enum NK_device_model device_model); - - /** - * Connect to first available device, starting checking from Pro 1st to Storage 2nd. - * @return 1 if connected, 0 if wrong model or cannot connect - */ - NK_C_API int NK_login_auto(); - - /** - * Disconnect from the device. - * @return command processing error code - */ - NK_C_API int NK_logout(); - - /** - * Query the model of the connected device. - * Returns the model of the connected device or NK_DISCONNECTED. - * - * @return true if a device is connected and the out argument has been set - */ - NK_C_API enum NK_device_model NK_get_device_model(); - - /** - * Return the debug status string. Debug purposes. - * @return command processing error code - */ - NK_C_API char * NK_status(); - - /** - * Return the device's serial number string in hex. - * @return string device's serial number in hex - */ - NK_C_API char * NK_device_serial_number(); - - /** - * Get last command processing status. Useful for commands which returns the results of their own and could not return - * an error code. - * @return previous command processing error code - */ - NK_C_API uint8_t NK_get_last_command_status(); - - /** - * Lock device - cancel any user device unlocking. - * @return command processing error code - */ - NK_C_API int NK_lock_device(); - - /** - * Authenticates the user on USER privilages with user_password and sets user's temporary password on device to user_temporary_password. - * @param user_password char[25] current user password - * @param user_temporary_password char[25] user temporary password to be set on device for further communication (authentication command) - * @return command processing error code - */ - NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password); - - /** - * Authenticates the user on ADMIN privilages with admin_password and sets user's temporary password on device to admin_temporary_password. - * @param admin_password char[25] current administrator PIN - * @param admin_temporary_password char[25] admin temporary password to be set on device for further communication (authentication command) - * @return command processing error code - */ - NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password); - - /** - * Execute a factory reset. - * @param admin_password char[20] current administrator PIN - * @return command processing error code - */ - NK_C_API int NK_factory_reset(const char* admin_password); - - /** - * Generates AES key on the device - * @param admin_password char[20] current administrator PIN - * @return command processing error code - */ - NK_C_API int NK_build_aes_key(const char* admin_password); - - /** - * Unlock user PIN locked after 3 incorrect codes tries. - * @param admin_password char[20] current administrator PIN - * @return command processing error code - */ - NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password); - - /** - * Write general config to the device - * @param numlock set value in range [0-1] to send HOTP code from slot 'numlock' after double pressing numlock - * or outside the range to disable this function - * @param capslock similar to numlock but with capslock - * @param scrolllock similar to numlock but with scrolllock - * @param enable_user_password set True to enable OTP PIN protection (require PIN each OTP code request) - * @param delete_user_password (unused) - * @param admin_temporary_password current admin temporary password - * @return command processing error code - */ - NK_C_API int 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); - - /** - * Get currently set config - status of function Numlock/Capslock/Scrollock OTP sending and is enabled PIN protected OTP - * @see NK_write_config - * @return uint8_t general_config[5]: - * uint8_t numlock; - uint8_t capslock; - uint8_t scrolllock; - uint8_t enable_user_password; - uint8_t delete_user_password; - - */ - NK_C_API uint8_t* NK_read_config(); - - //OTP - - /** - * Get name of given TOTP slot - * @param slot_number TOTP slot number, slot_number<15 - * @return char[20] the name of the slot - */ - NK_C_API char * NK_get_totp_slot_name(uint8_t slot_number); - - /** - * - * @param slot_number HOTP slot number, slot_number<3 - * @return char[20] the name of the slot - */ - NK_C_API char * NK_get_hotp_slot_name(uint8_t slot_number); - - /** - * Erase HOTP slot data from the device - * @param slot_number HOTP slot number, slot_number<3 - * @param temporary_password admin temporary password - * @return command processing error code - */ - NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password); - - /** - * Erase TOTP slot data from the device - * @param slot_number TOTP slot number, slot_number<15 - * @param temporary_password admin temporary password - * @return command processing error code - */ - NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password); - - /** - * Write HOTP slot data to the device - * @param slot_number HOTP slot number, slot_number<3, 0-numbered - * @param slot_name char[15] desired slot name. C string (requires ending '\0'; 16 bytes). - * @param secret char[40] 160-bit or 320-bit (currently Pro v0.8 only) secret as a hex string. C string (requires ending '\0'; 41 bytes). - * See NitrokeyManager::is_320_OTP_secret_supported. - * @param hotp_counter uint32_t starting value of HOTP counter - * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) - * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock - * @param use_tokenID @see token_ID - * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section - * @param temporary_password char[25] admin temporary password - * @return command processing error code - */ - NK_C_API int 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); - - /** - * Write TOTP slot data to the device - * @param slot_number TOTP slot number, slot_number<15, 0-numbered - * @param slot_name char[15] desired slot name. C string (requires ending '\0'; 16 bytes). - * @param secret char[40] 160-bit or 320-bit (currently Pro v0.8 only) secret as a hex string. C string (requires ending '\0'; 41 bytes). - * See NitrokeyManager::is_320_OTP_secret_supported. - * @param time_window uint16_t time window for this TOTP - * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) - * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock - * @param use_tokenID @see token_ID - * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section - * @param temporary_password char[20] admin temporary password - * @return command processing error code - */ - NK_C_API int 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); - - /** - * Get HOTP code from the device - * @param slot_number HOTP slot number, slot_number<3 - * @return HOTP code - */ - NK_C_API char * NK_get_hotp_code(uint8_t slot_number); - - /** - * Get HOTP code from the device (PIN protected) - * @param slot_number HOTP slot number, slot_number<3 - * @param user_temporary_password char[25] user temporary password if PIN protected OTP codes are enabled, - * otherwise should be set to empty string - '' - * @return HOTP code - */ - NK_C_API char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password); - - /** - * Get TOTP code from the device - * @param slot_number TOTP slot number, slot_number<15 - * @param challenge TOTP challenge -- unused - * @param last_totp_time last time -- unused - * @param last_interval last interval --unused - * @return TOTP code - */ - NK_C_API char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval); - - /** - * Get TOTP code from the device (PIN protected) - * @param slot_number TOTP slot number, slot_number<15 - * @param challenge TOTP challenge -- unused - * @param last_totp_time last time -- unused - * @param last_interval last interval -- unused - * @param user_temporary_password char[25] user temporary password if PIN protected OTP codes are enabled, - * otherwise should be set to empty string - '' - * @return TOTP code - */ - NK_C_API char * 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); - - /** - * Set time on the device (for TOTP requests) - * @param time seconds in unix epoch (from 01.01.1970) - * @return command processing error code - */ - NK_C_API int NK_totp_set_time(uint64_t time); - - /** - * Set the device time used for TOTP to the given time. Contrary to - * {@code set_time(uint64_t)}, this command fails if {@code old_time} - * > {@code time} or if {@code old_time} is zero (where {@code - * old_time} is the current time on the device). - * - * @param time new device time as Unix timestamp (seconds since - * 1970-01-01) - * @return command processing error code - */ - NK_C_API int NK_totp_set_time_soft(uint64_t time); - - // NK_totp_get_time is deprecated -- use NK_totp_set_time_soft instead - DEPRECATED - NK_C_API int NK_totp_get_time(); - - //passwords - /** - * Change administrator PIN - * @param current_PIN char[25] current PIN - * @param new_PIN char[25] new PIN - * @return command processing error code - */ - NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN); - - /** - * Change user PIN - * @param current_PIN char[25] current PIN - * @param new_PIN char[25] new PIN - * @return command processing error code - */ - NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN); - - - /** - * Get retry count of user PIN - * @return user PIN retry count - */ - NK_C_API uint8_t NK_get_user_retry_count(); - - /** - * Get retry count of admin PIN - * @return admin PIN retry count - */ - NK_C_API uint8_t NK_get_admin_retry_count(); - //password safe - - /** - * Enable password safe access - * @param user_pin char[30] current user PIN - * @return command processing error code - */ - NK_C_API int NK_enable_password_safe(const char *user_pin); - - /** - * Get password safe slots' status - * @return uint8_t[16] slot statuses - each byte represents one slot with 0 (not programmed) and 1 (programmed) - */ - NK_C_API uint8_t * NK_get_password_safe_slot_status(); - - /** - * Get password safe slot name - * @param slot_number password safe slot number, slot_number<16 - * @return slot name - */ - NK_C_API char *NK_get_password_safe_slot_name(uint8_t slot_number); - - /** - * Get password safe slot login - * @param slot_number password safe slot number, slot_number<16 - * @return login from the PWS slot - */ - NK_C_API char *NK_get_password_safe_slot_login(uint8_t slot_number); - - /** - * Get the password safe slot password - * @param slot_number password safe slot number, slot_number<16 - * @return password from the PWS slot - */ - NK_C_API char *NK_get_password_safe_slot_password(uint8_t slot_number); - - /** - * Write password safe data to the slot - * @param slot_number password safe slot number, slot_number<16 - * @param slot_name char[11] name of the slot - * @param slot_login char[32] login string - * @param slot_password char[20] password string - * @return command processing error code - */ - NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, - const char *slot_login, const char *slot_password); - - /** - * Erase the password safe slot from the device - * @param slot_number password safe slot number, slot_number<16 - * @return command processing error code - */ - NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number); - - /** - * Check whether AES is supported by the device - * @return 0 for no and 1 for yes - */ - NK_C_API int NK_is_AES_supported(const char *user_password); - - /** - * Get device's major firmware version - * @return major part of the version number (e.g. 0 from 0.48, 0 from 0.7 etc.) - */ - NK_C_API int NK_get_major_firmware_version(); - - /** - * Get device's minor firmware version - * @return minor part of the version number (e.g. 7 from 0.7, 48 from 0.48 etc.) - */ - NK_C_API int NK_get_minor_firmware_version(); - - /** - * Function to determine unencrypted volume PIN type - * @param minor_firmware_version - * @return Returns 1, if set unencrypted volume ro/rw pin type is User, 0 otherwise. - */ - NK_C_API int NK_set_unencrypted_volume_rorw_pin_type_user(); - - - /** - * This command is typically run to initiate - * communication with the device (altough not required). - * It sets time on device and returns its current status - * - a combination of set_time and get_status_storage commands - * Storage only - * @param seconds_from_epoch date and time expressed in seconds - */ - NK_C_API int NK_send_startup(uint64_t seconds_from_epoch); - - /** - * Unlock encrypted volume. - * Storage only - * @param user_pin user pin 20 characters - * @return command processing error code - */ - NK_C_API int NK_unlock_encrypted_volume(const char* user_pin); - - /** - * Locks encrypted volume - * @return command processing error code - */ - NK_C_API int NK_lock_encrypted_volume(); - - /** - * Unlock hidden volume and lock encrypted volume. - * Requires encrypted volume to be unlocked. - * Storage only - * @param hidden_volume_password 20 characters - * @return command processing error code - */ - NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password); - - /** - * Locks hidden volume - * @return command processing error code - */ - NK_C_API int NK_lock_hidden_volume(); - - /** - * Create hidden volume. - * Requires encrypted volume to be unlocked. - * Storage only - * @param slot_nr slot number in range 0-3 - * @param start_percent volume begin expressed in percent of total available storage, int in range 0-99 - * @param end_percent volume end expressed in percent of total available storage, int in range 1-100 - * @param hidden_volume_password 20 characters - * @return command processing error code - */ - NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, - const char *hidden_volume_password); - - /** - * Make unencrypted volume read-only. - * Device hides unencrypted volume for a second therefore make sure - * buffers are flushed before running. - * Does nothing if firmware version is not matched - * Firmware range: Storage v0.50, v0.48 and below - * Storage only - * @param user_pin 20 characters User PIN - * @return command processing error code - */ - NK_C_API int NK_set_unencrypted_read_only(const char *user_pin); - - /** - * Make unencrypted volume read-write. - * Device hides unencrypted volume for a second therefore make sure - * buffers are flushed before running. - * Does nothing if firmware version is not matched - * Firmware range: Storage v0.50, v0.48 and below - * Storage only - * @param user_pin 20 characters User PIN - * @return command processing error code - */ - NK_C_API int NK_set_unencrypted_read_write(const char *user_pin); - - /** - * Make unencrypted volume read-only. - * Device hides unencrypted volume for a second therefore make sure - * buffers are flushed before running. - * Does nothing if firmware version is not matched - * Firmware range: Storage v0.49, v0.51+ - * Storage only - * @param admin_pin 20 characters Admin PIN - * @return command processing error code - */ - NK_C_API int NK_set_unencrypted_read_only_admin(const char* admin_pin); - - /** - * Make unencrypted volume read-write. - * Device hides unencrypted volume for a second therefore make sure - * buffers are flushed before running. - * Does nothing if firmware version is not matched - * Firmware range: Storage v0.49, v0.51+ - * Storage only - * @param admin_pin 20 characters Admin PIN - * @return command processing error code - */ - NK_C_API int NK_set_unencrypted_read_write_admin(const char* admin_pin); - - /** - * Make encrypted volume read-only. - * Device hides encrypted volume for a second therefore make sure - * buffers are flushed before running. - * Firmware range: v0.49 only, future (see firmware release notes) - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ - NK_C_API int NK_set_encrypted_read_only(const char* admin_pin); - - /** - * Make encrypted volume read-write. - * Device hides encrypted volume for a second therefore make sure - * buffers are flushed before running. - * Firmware range: v0.49 only, future (see firmware release notes) - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ - NK_C_API int NK_set_encrypted_read_write(const char* admin_pin); - - /** - * Exports device's firmware to unencrypted volume. - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ - NK_C_API int NK_export_firmware(const char* admin_pin); - - /** - * Clear new SD card notification. It is set after factory reset. - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ - NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin); - - /** - * Fill SD card with random data. - * Should be done on first stick initialization after creating keys. - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ - NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin); - - /** - * Change update password. - * Update password is used for entering update mode, where firmware - * could be uploaded using dfu-programmer or other means. - * Storage only - * @param current_update_password 20 characters - * @param new_update_password 20 characters - * @return command processing error code - */ - NK_C_API int NK_change_update_password(const char* current_update_password, - const char* new_update_password); - - /** - * Enter update mode. Needs update password. - * When device is in update mode it no longer accepts any HID commands until - * firmware is launched (regardless of being updated or not). - * Smartcard (through CCID interface) and its all volumes are not visible as well. - * Its VID and PID are changed to factory-default (03eb:2ff1 Atmel Corp.) - * to be detected by flashing software. Result of this command can be reversed - * by using 'launch' command. - * For dfu-programmer it would be: 'dfu-programmer at32uc3a3256s launch'. - * Storage only - * @param update_password 20 characters - * @return command processing error code - */ - NK_C_API int NK_enable_firmware_update(const char* update_password); - - /** - * Get Storage stick status as string. - * Storage only - * @return string with devices attributes - */ - NK_C_API char* NK_get_status_storage_as_string(); - - /** - * Get the Storage stick status and return the command processing - * error code. If the code is zero, i. e. the command was successful, - * the storage status is written to the output pointer's target. - * The output pointer must not be null. - * - * @param out the output pointer for the storage status - * @return command processing error code - */ - NK_C_API int NK_get_status_storage(struct NK_storage_status* out); - - /** - * Get SD card usage attributes as string. - * Usable during hidden volumes creation. - * Storage only - * @return string with SD card usage attributes - */ - NK_C_API char* NK_get_SD_usage_data_as_string(); - - /** - * Get progress value of current long operation. - * Storage only - * @return int in range 0-100 or -1 if device is not busy - */ - NK_C_API int NK_get_progress_bar_value(); - -/** - * Returns a list of connected devices' id's, delimited by ';' character. Empty string is returned on no device found. - * Each ID could consist of: - * 1. SC_id:SD_id_p_path (about 40 bytes) - * 2. path (about 10 bytes) - * where 'path' is USB path (bus:num), 'SC_id' is smartcard ID, 'SD_id' is storage card ID and - * '_p_' and ':' are field delimiters. - * Case 2 (USB path only) is used, when the device cannot be asked about its status data (e.g. during a long operation, - * like clearing SD card. - * Internally connects to all available devices and creates a map between ids and connection objects. - * Side effects: changes active device to last detected Storage device. - * Storage only - * @example Example of returned data: '00005d19:dacc2cb4_p_0001:0010:02;000037c7:4cf12445_p_0001:000f:02;0001:000c:02' - * @return string delimited id's of connected devices - */ - NK_C_API char* NK_list_devices_by_cpuID(); - - -/** - * Connects to the device with given ID. ID's list could be created with NK_list_devices_by_cpuID. - * Requires calling to NK_list_devices_by_cpuID first. Connecting to arbitrary ID/USB path is not handled. - * On connection requests status from device and disconnects it / removes from map on connection failure. - * Storage only - * @param id Target device ID (example: '00005d19:dacc2cb4_p_0001:0010:02') - * @return 1 on successful connection, 0 otherwise - */ - NK_C_API int NK_connect_with_ID(const char* id); - - /** - * Blink red and green LED alternatively and infinitely (until device is reconnected). - * @return command processing error code - */ - NK_C_API int NK_wink(); - -#ifdef __cplusplus -} -#endif - -#endif //LIBNITROKEY_NK_C_API_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/NitrokeyManager.cc b/nitrokey-sys/libnitrokey-v3.4.1/NitrokeyManager.cc deleted file mode 100644 index a950e4b..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/NitrokeyManager.cc +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * 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 - */ - -#include -#include -#include "libnitrokey/NitrokeyManager.h" -#include "libnitrokey/LibraryException.h" -#include -#include -#include -#include "libnitrokey/misc.h" -#include -#include "libnitrokey/cxx_semantics.h" -#include -#include - -std::mutex nitrokey::proto::send_receive_mtx; - -namespace nitrokey{ - - std::mutex mex_dev_com_manager; - -#ifndef strndup -#ifdef _WIN32 -#pragma message "Using own strndup" -char * strndup(const char* str, size_t maxlen){ - size_t len = strnlen(str, maxlen); - char* dup = (char *) malloc(len + 1); - memcpy(dup, str, len); - dup[len] = 0; - return dup; -} -#endif -#endif - -using nitrokey::misc::strcpyT; - - template - typename T::CommandPayload get_payload(){ - //Create, initialize and return by value command payload - typename T::CommandPayload st; - bzero(&st, sizeof(st)); - return st; - } - - - // package type to auth, auth type [Authorize,UserAuthorize] - template - void NitrokeyManager::authorize_packet(T &package, const char *admin_temporary_password, shared_ptr device){ - if (!is_authorization_command_supported()){ - LOG("Authorization command not supported, skipping", Loglevel::WARNING); - } - auto auth = get_payload(); - strcpyT(auth.temporary_password, admin_temporary_password); - auth.crc_to_authorize = S::CommandTransaction::getCRC(package); - A::CommandTransaction::run(device, auth); - } - - shared_ptr NitrokeyManager::_instance = nullptr; - - NitrokeyManager::NitrokeyManager() : device(nullptr) - { - set_debug(false); - } - NitrokeyManager::~NitrokeyManager() { - std::lock_guard lock(mex_dev_com_manager); - - for (auto d : connected_devices){ - if (d.second == nullptr) continue; - d.second->disconnect(); - connected_devices[d.first] = nullptr; - } - } - - bool NitrokeyManager::set_current_device_speed(int retry_delay, int send_receive_delay){ - if (retry_delay < 20 || send_receive_delay < 20){ - LOG("Delay set too low: " + to_string(retry_delay) +" "+ to_string(send_receive_delay), Loglevel::WARNING); - return false; - } - - std::lock_guard lock(mex_dev_com_manager); - if(device == nullptr) { - return false; - } - device->set_receiving_delay(std::chrono::duration(send_receive_delay)); - device->set_retry_delay(std::chrono::duration(retry_delay)); - return true; - } - - std::vector NitrokeyManager::list_devices(){ - std::lock_guard lock(mex_dev_com_manager); - - auto p = make_shared(); - return p->enumerate(); // make static - } - - std::vector NitrokeyManager::list_devices_by_cpuID(){ - using misc::toHex; - //disconnect default device - disconnect(); - - std::lock_guard lock(mex_dev_com_manager); - LOGD1("Disconnecting registered devices"); - for (auto & kv : connected_devices_byID){ - if (kv.second != nullptr) - kv.second->disconnect(); - } - connected_devices_byID.clear(); - - LOGD1("Enumerating devices"); - std::vector res; - auto d = make_shared(); - const auto v = d->enumerate(); - LOGD1("Discovering IDs"); - for (auto & p: v){ - d = make_shared(); - LOGD1( std::string("Found: ") + p ); - d->set_path(p); - try{ - if (d->connect()){ - device = d; - std::string id; - try { - const auto status = get_status_storage(); - const auto sc_id = toHex(status.ActiveSmartCardID_u32); - const auto sd_id = toHex(status.ActiveSD_CardID_u32); - id += sc_id + ":" + sd_id; - id += "_p_" + p; - } - catch (const LongOperationInProgressException &e) { - LOGD1(std::string("Long operation in progress, setting ID to: ") + p); - id = p; - } - - connected_devices_byID[id] = d; - res.push_back(id); - LOGD1( std::string("Found: ") + p + " => " + id); - } else{ - LOGD1( std::string("Could not connect to: ") + p); - } - } - catch (const DeviceCommunicationException &e){ - LOGD1( std::string("Exception encountered: ") + p); - } - } - return res; - } - - bool NitrokeyManager::connect_with_ID(const std::string id) { - std::lock_guard lock(mex_dev_com_manager); - - auto position = connected_devices_byID.find(id); - if (position == connected_devices_byID.end()) { - LOGD1(std::string("Could not find device ")+id + ". Refresh devices list with list_devices_by_cpuID()."); - return false; - } - - auto d = connected_devices_byID[id]; - device = d; - current_device_id = id; - - //validate connection - try{ - get_status(); - } - catch (const LongOperationInProgressException &){ - //ignore - } - catch (const DeviceCommunicationException &){ - d->disconnect(); - current_device_id = ""; - connected_devices_byID[id] = nullptr; - connected_devices_byID.erase(position); - return false; - } - nitrokey::log::Log::setPrefix(id); - LOGD1("Device successfully changed"); - return true; - } - - /** - * Connects device to path. - * Assumes devices are not being disconnected and caches connections (param cache_connections). - * @param path os-dependent device path - * @return false, when could not connect, true otherwise - */ - bool NitrokeyManager::connect_with_path(std::string path) { - const bool cache_connections = false; - - std::lock_guard lock(mex_dev_com_manager); - - if (cache_connections){ - if(connected_devices.find(path) != connected_devices.end() - && connected_devices[path] != nullptr) { - device = connected_devices[path]; - return true; - } - } - - auto p = make_shared(); - p->set_path(path); - - if(!p->connect()) return false; - - if(cache_connections){ - connected_devices [path] = p; - } - - device = p; //previous device will be disconnected automatically - current_device_id = path; - nitrokey::log::Log::setPrefix(path); - LOGD1("Device successfully changed"); - return true; - } - - bool NitrokeyManager::connect() { - std::lock_guard lock(mex_dev_com_manager); - vector< shared_ptr > devices = { make_shared(), make_shared() }; - bool connected = false; - for( auto & d : devices ){ - if (d->connect()){ - device = std::shared_ptr(d); - connected = true; - } - } - return connected; - } - - - void NitrokeyManager::set_log_function(std::function log_function){ - static nitrokey::log::FunctionalLogHandler handler(log_function); - nitrokey::log::Log::instance().set_handler(&handler); - } - - bool NitrokeyManager::set_default_commands_delay(int delay){ - if (delay < 20){ - LOG("Delay set too low: " + to_string(delay), Loglevel::WARNING); - return false; - } - Device::set_default_device_speed(delay); - return true; - } - - bool NitrokeyManager::connect(const char *device_model) { - std::lock_guard lock(mex_dev_com_manager); - LOG(__FUNCTION__, nitrokey::log::Loglevel::DEBUG_L2); - switch (device_model[0]){ - case 'P': - device = make_shared(); - break; - case 'S': - device = make_shared(); - break; - default: - throw std::runtime_error("Unknown model"); - } - return device->connect(); - } - - bool NitrokeyManager::connect(device::DeviceModel device_model) { - const char *model_string; - switch (device_model) { - case device::DeviceModel::PRO: - model_string = "P"; - break; - case device::DeviceModel::STORAGE: - model_string = "S"; - break; - default: - throw std::runtime_error("Unknown model"); - } - return connect(model_string); - } - - shared_ptr NitrokeyManager::instance() { - static std::mutex mutex; - std::lock_guard lock(mutex); - if (_instance == nullptr){ - _instance = make_shared(); - } - return _instance; - } - - - - bool NitrokeyManager::disconnect() { - std::lock_guard lock(mex_dev_com_manager); - return _disconnect_no_lock(); - } - - bool NitrokeyManager::_disconnect_no_lock() { - //do not use directly without locked mutex, - //used by could_be_enumerated, disconnect - if (device == nullptr){ - return false; - } - const auto res = device->disconnect(); - device = nullptr; - return res; - } - - bool NitrokeyManager::is_connected() throw(){ - std::lock_guard lock(mex_dev_com_manager); - if(device != nullptr){ - auto connected = device->could_be_enumerated(); - if(connected){ - return true; - } else { - _disconnect_no_lock(); - return false; - } - } - return false; - } - - bool NitrokeyManager::could_current_device_be_enumerated() { - std::lock_guard lock(mex_dev_com_manager); - if (device != nullptr) { - return device->could_be_enumerated(); - } - return false; - } - - void NitrokeyManager::set_loglevel(int loglevel) { - loglevel = max(loglevel, static_cast(Loglevel::ERROR)); - loglevel = min(loglevel, static_cast(Loglevel::DEBUG_L2)); - Log::instance().set_loglevel(static_cast(loglevel)); - } - - void NitrokeyManager::set_loglevel(Loglevel loglevel) { - Log::instance().set_loglevel(loglevel); - } - - void NitrokeyManager::set_debug(bool state) { - if (state){ - Log::instance().set_loglevel(Loglevel::DEBUG); - } else { - Log::instance().set_loglevel(Loglevel::ERROR); - } - } - - - string NitrokeyManager::get_serial_number() { - if (device == nullptr) { return ""; }; - switch (device->get_device_model()) { - case DeviceModel::PRO: { - auto response = GetStatus::CommandTransaction::run(device); - return nitrokey::misc::toHex(response.data().card_serial_u32); - } - break; - - case DeviceModel::STORAGE: - { - auto response = stick20::GetDeviceStatus::CommandTransaction::run(device); - return nitrokey::misc::toHex(response.data().ActiveSmartCardID_u32); - } - break; - } - return "NA"; - } - - stick10::GetStatus::ResponsePayload NitrokeyManager::get_status(){ - try{ - auto response = GetStatus::CommandTransaction::run(device); - return response.data(); - } - catch (DeviceSendingFailure &e){ -// disconnect(); - throw; - } - } - - string NitrokeyManager::get_status_as_string() { - auto response = GetStatus::CommandTransaction::run(device); - return response.data().dissect(); - } - - string getFilledOTPCode(uint32_t code, bool use_8_digits){ - stringstream s; - s << std::right << std::setw(use_8_digits ? 8 : 6) << std::setfill('0') << code; - return s.str(); - } - - string NitrokeyManager::get_HOTP_code(uint8_t slot_number, const char *user_temporary_password) { - if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - - if (is_authorization_command_supported()){ - auto gh = get_payload(); - gh.slot_number = get_internal_slot_number_for_hotp(slot_number); - if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen - authorize_packet(gh, user_temporary_password, device); - } - auto resp = GetHOTP::CommandTransaction::run(device, gh); - return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); - } else { - auto gh = get_payload(); - gh.slot_number = get_internal_slot_number_for_hotp(slot_number); - if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0) { - strcpyT(gh.temporary_user_password, user_temporary_password); - } - auto resp = stick10_08::GetHOTP::CommandTransaction::run(device, gh); - return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); - } - return ""; - } - - bool NitrokeyManager::is_valid_hotp_slot_number(uint8_t slot_number) const { return slot_number < 3; } - bool NitrokeyManager::is_valid_totp_slot_number(uint8_t slot_number) const { return slot_number < 0x10-1; } //15 - uint8_t NitrokeyManager::get_internal_slot_number_for_totp(uint8_t slot_number) const { return (uint8_t) (0x20 + slot_number); } - uint8_t NitrokeyManager::get_internal_slot_number_for_hotp(uint8_t slot_number) const { return (uint8_t) (0x10 + slot_number); } - - - - string NitrokeyManager::get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval, - const char *user_temporary_password) { - if(!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - slot_number = get_internal_slot_number_for_totp(slot_number); - - if (is_authorization_command_supported()){ - auto gt = get_payload(); - gt.slot_number = slot_number; - gt.challenge = challenge; - gt.last_interval = last_interval; - gt.last_totp_time = last_totp_time; - - if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen - authorize_packet(gt, user_temporary_password, device); - } - auto resp = GetTOTP::CommandTransaction::run(device, gt); - return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); - } else { - auto gt = get_payload(); - strcpyT(gt.temporary_user_password, user_temporary_password); - gt.slot_number = slot_number; - auto resp = stick10_08::GetTOTP::CommandTransaction::run(device, gt); - return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); - } - return ""; - } - - bool NitrokeyManager::erase_slot(uint8_t slot_number, const char *temporary_password) { - if (is_authorization_command_supported()){ - auto p = get_payload(); - p.slot_number = slot_number; - authorize_packet(p, temporary_password, device); - auto resp = EraseSlot::CommandTransaction::run(device,p); - } else { - auto p = get_payload(); - p.slot_number = slot_number; - strcpyT(p.temporary_admin_password, temporary_password); - auto resp = stick10_08::EraseSlot::CommandTransaction::run(device,p); - } - return true; - } - - bool NitrokeyManager::erase_hotp_slot(uint8_t slot_number, const char *temporary_password) { - if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - slot_number = get_internal_slot_number_for_hotp(slot_number); - return erase_slot(slot_number, temporary_password); - } - - bool NitrokeyManager::erase_totp_slot(uint8_t slot_number, const char *temporary_password) { - if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - slot_number = get_internal_slot_number_for_totp(slot_number); - return erase_slot(slot_number, temporary_password); - } - - template - void vector_copy_ranged(T& dest, std::vector &vec, size_t begin, size_t elements_to_copy){ - const size_t d_size = sizeof(dest); - if(d_size < elements_to_copy){ - throw TargetBufferSmallerThanSource(elements_to_copy, d_size); - } - std::fill(dest, dest+d_size, 0); - std::copy(vec.begin() + begin, vec.begin() +begin + elements_to_copy, dest); - } - - template - void vector_copy(T& dest, std::vector &vec){ - const size_t d_size = sizeof(dest); - if(d_size < vec.size()){ - throw TargetBufferSmallerThanSource(vec.size(), d_size); - } - std::fill(dest, dest+d_size, 0); - std::copy(vec.begin(), vec.end(), dest); - } - - bool NitrokeyManager::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) { - if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - - int internal_slot_number = get_internal_slot_number_for_hotp(slot_number); - if (is_authorization_command_supported()){ - write_HOTP_slot_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, - token_ID, temporary_password); - } else { - write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, - token_ID, temporary_password); - } - return true; - } - - void NitrokeyManager::write_HOTP_slot_authorize(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) { - auto payload = get_payload(); - payload.slot_number = slot_number; - auto secret_bin = misc::hex_string_to_byte(secret); - vector_copy(payload.slot_secret, secret_bin); - strcpyT(payload.slot_name, slot_name); - strcpyT(payload.slot_token_id, token_ID); - switch (device->get_device_model() ){ - case DeviceModel::PRO: { - payload.slot_counter = hotp_counter; - break; - } - case DeviceModel::STORAGE: { - string counter = to_string(hotp_counter); - strcpyT(payload.slot_counter_s, counter.c_str()); - break; - } - default: - LOG(string(__FILE__) + to_string(__LINE__) + - string(__FUNCTION__) + string(" Unhandled device model for HOTP") - , Loglevel::DEBUG); - break; - } - payload.use_8_digits = use_8_digits; - payload.use_enter = use_enter; - payload.use_tokenID = use_tokenID; - - authorize_packet(payload, temporary_password, device); - - auto resp = WriteToHOTPSlot::CommandTransaction::run(device, payload); - } - - bool NitrokeyManager::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) { - if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - int internal_slot_number = get_internal_slot_number_for_totp(slot_number); - - if (is_authorization_command_supported()){ - write_TOTP_slot_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, - token_ID, temporary_password); - } else { - write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, - token_ID, temporary_password); - } - - return true; - } - - void NitrokeyManager::write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name, - const char *secret, - uint64_t counter_or_interval, bool use_8_digits, bool use_enter, - bool use_tokenID, const char *token_ID, - const char *temporary_password) const { - - auto payload2 = get_payload(); - strcpyT(payload2.temporary_admin_password, temporary_password); - strcpyT(payload2.data, slot_name); - payload2.setTypeName(); - stick10_08::SendOTPData::CommandTransaction::run(device, payload2); - - payload2.setTypeSecret(); - payload2.id = 0; - auto secret_bin = misc::hex_string_to_byte(secret); - auto remaining_secret_length = secret_bin.size(); - const auto maximum_OTP_secret_size = 40; - if(remaining_secret_length > maximum_OTP_secret_size){ - throw TargetBufferSmallerThanSource(remaining_secret_length, maximum_OTP_secret_size); - } - - while (remaining_secret_length>0){ - const auto bytesToCopy = std::min(sizeof(payload2.data), remaining_secret_length); - const auto start = secret_bin.size() - remaining_secret_length; - memset(payload2.data, 0, sizeof(payload2.data)); - vector_copy_ranged(payload2.data, secret_bin, start, bytesToCopy); - stick10_08::SendOTPData::CommandTransaction::run(device, payload2); - remaining_secret_length -= bytesToCopy; - payload2.id++; - } - - auto payload = get_payload(); - strcpyT(payload.temporary_admin_password, temporary_password); - strcpyT(payload.slot_token_id, token_ID); - payload.use_8_digits = use_8_digits; - payload.use_enter = use_enter; - payload.use_tokenID = use_tokenID; - payload.slot_counter_or_interval = counter_or_interval; - payload.slot_number = internal_slot_number; - stick10_08::WriteToOTPSlot::CommandTransaction::run(device, payload); - } - - void NitrokeyManager::write_TOTP_slot_authorize(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) { - auto payload = get_payload(); - payload.slot_number = slot_number; - auto secret_bin = misc::hex_string_to_byte(secret); - vector_copy(payload.slot_secret, secret_bin); - strcpyT(payload.slot_name, slot_name); - strcpyT(payload.slot_token_id, token_ID); - payload.slot_interval = time_window; //FIXME naming - payload.use_8_digits = use_8_digits; - payload.use_enter = use_enter; - payload.use_tokenID = use_tokenID; - - authorize_packet(payload, temporary_password, device); - - auto resp = WriteToTOTPSlot::CommandTransaction::run(device, payload); - } - - char * NitrokeyManager::get_totp_slot_name(uint8_t slot_number) { - if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - slot_number = get_internal_slot_number_for_totp(slot_number); - return get_slot_name(slot_number); - } - char * NitrokeyManager::get_hotp_slot_name(uint8_t slot_number) { - if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); - slot_number = get_internal_slot_number_for_hotp(slot_number); - return get_slot_name(slot_number); - } - - static const int max_string_field_length = 2*1024; //storage's status string is ~1k - - char * NitrokeyManager::get_slot_name(uint8_t slot_number) { - auto payload = get_payload(); - payload.slot_number = slot_number; - auto resp = GetSlotName::CommandTransaction::run(device, payload); - return strndup((const char *) resp.data().slot_name, max_string_field_length); - } - - bool NitrokeyManager::first_authenticate(const char *pin, const char *temporary_password) { - auto authreq = get_payload(); - strcpyT(authreq.card_password, pin); - strcpyT(authreq.temporary_password, temporary_password); - FirstAuthenticate::CommandTransaction::run(device, authreq); - return true; - } - - bool NitrokeyManager::set_time(uint64_t time) { - auto p = get_payload(); - p.reset = 1; - p.time = time; - SetTime::CommandTransaction::run(device, p); - return false; - } - - void NitrokeyManager::set_time_soft(uint64_t time) { - auto p = get_payload(); - p.reset = 0; - p.time = time; - SetTime::CommandTransaction::run(device, p); - } - - bool NitrokeyManager::get_time(uint64_t time) { - set_time_soft(time); - return true; - } - - void NitrokeyManager::change_user_PIN(const char *current_PIN, const char *new_PIN) { - change_PIN_general(current_PIN, new_PIN); - } - - void NitrokeyManager::change_admin_PIN(const char *current_PIN, const char *new_PIN) { - change_PIN_general(current_PIN, new_PIN); - } - - template - void NitrokeyManager::change_PIN_general(const char *current_PIN, const char *new_PIN) { - switch (device->get_device_model()){ - case DeviceModel::PRO: - { - auto p = get_payload(); - strcpyT(p.old_pin, current_PIN); - strcpyT(p.new_pin, new_PIN); - ProCommand::CommandTransaction::run(device, p); - } - break; - //in Storage change admin/user pin is divided to two commands with 20 chars field len - case DeviceModel::STORAGE: - { - auto p = get_payload(); - strcpyT(p.password, current_PIN); - p.set_kind(StoKind); - auto p2 = get_payload(); - strcpyT(p2.password, new_PIN); - p2.set_kind(StoKind); - ChangeAdminUserPin20Current::CommandTransaction::run(device, p); - ChangeAdminUserPin20New::CommandTransaction::run(device, p2); - } - break; - } - - } - - void NitrokeyManager::enable_password_safe(const char *user_pin) { - //The following command will cancel enabling PWS if it is not supported - auto a = get_payload(); - strcpyT(a.user_password, user_pin); - IsAESSupported::CommandTransaction::run(device, a); - - auto p = get_payload(); - strcpyT(p.user_password, user_pin); - EnablePasswordSafe::CommandTransaction::run(device, p); - } - - vector NitrokeyManager::get_password_safe_slot_status() { - auto responsePayload = GetPasswordSafeSlotStatus::CommandTransaction::run(device); - vector v = vector(responsePayload.data().password_safe_status, - responsePayload.data().password_safe_status - + sizeof(responsePayload.data().password_safe_status)); - return v; - } - - uint8_t NitrokeyManager::get_user_retry_count() { - if(device->get_device_model() == DeviceModel::STORAGE){ - stick20::GetDeviceStatus::CommandTransaction::run(device); - } - auto response = GetUserPasswordRetryCount::CommandTransaction::run(device); - return response.data().password_retry_count; - } - - uint8_t NitrokeyManager::get_admin_retry_count() { - if(device->get_device_model() == DeviceModel::STORAGE){ - stick20::GetDeviceStatus::CommandTransaction::run(device); - } - auto response = GetPasswordRetryCount::CommandTransaction::run(device); - return response.data().password_retry_count; - } - - void NitrokeyManager::lock_device() { - LockDevice::CommandTransaction::run(device); - } - - char * NitrokeyManager::get_password_safe_slot_name(uint8_t slot_number) { - if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); - auto p = get_payload(); - p.slot_number = slot_number; - auto response = GetPasswordSafeSlotName::CommandTransaction::run(device, p); - return strndup((const char *) response.data().slot_name, max_string_field_length); - } - - bool NitrokeyManager::is_valid_password_safe_slot_number(uint8_t slot_number) const { return slot_number < 16; } - - char * NitrokeyManager::get_password_safe_slot_login(uint8_t slot_number) { - if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); - auto p = get_payload(); - p.slot_number = slot_number; - auto response = GetPasswordSafeSlotLogin::CommandTransaction::run(device, p); - return strndup((const char *) response.data().slot_login, max_string_field_length); - } - - char * NitrokeyManager::get_password_safe_slot_password(uint8_t slot_number) { - if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); - auto p = get_payload(); - p.slot_number = slot_number; - auto response = GetPasswordSafeSlotPassword::CommandTransaction::run(device, p); - return strndup((const char *) response.data().slot_password, max_string_field_length); //FIXME use secure way - } - - void NitrokeyManager::write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, - const char *slot_password) { - if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); - auto p = get_payload(); - p.slot_number = slot_number; - strcpyT(p.slot_name, slot_name); - strcpyT(p.slot_password, slot_password); - SetPasswordSafeSlotData::CommandTransaction::run(device, p); - - auto p2 = get_payload(); - p2.slot_number = slot_number; - strcpyT(p2.slot_login_name, slot_login); - SetPasswordSafeSlotData2::CommandTransaction::run(device, p2); - } - - void NitrokeyManager::erase_password_safe_slot(uint8_t slot_number) { - if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); - auto p = get_payload(); - p.slot_number = slot_number; - ErasePasswordSafeSlot::CommandTransaction::run(device, p); - } - - void NitrokeyManager::user_authenticate(const char *user_password, const char *temporary_password) { - auto p = get_payload(); - strcpyT(p.card_password, user_password); - strcpyT(p.temporary_password, temporary_password); - UserAuthenticate::CommandTransaction::run(device, p); - } - - void NitrokeyManager::build_aes_key(const char *admin_password) { - switch (device->get_device_model()) { - case DeviceModel::PRO: { - auto p = get_payload(); - strcpyT(p.admin_password, admin_password); - BuildAESKey::CommandTransaction::run(device, p); - break; - } - case DeviceModel::STORAGE : { - auto p = get_payload(); - strcpyT(p.password, admin_password); - p.set_defaults(); - stick20::CreateNewKeys::CommandTransaction::run(device, p); - break; - } - } - } - - void NitrokeyManager::factory_reset(const char *admin_password) { - auto p = get_payload(); - strcpyT(p.admin_password, admin_password); - FactoryReset::CommandTransaction::run(device, p); - } - - void NitrokeyManager::unlock_user_password(const char *admin_password, const char *new_user_password) { - switch (device->get_device_model()){ - case DeviceModel::PRO: { - auto p = get_payload(); - strcpyT(p.admin_password, admin_password); - strcpyT(p.user_new_password, new_user_password); - stick10::UnlockUserPassword::CommandTransaction::run(device, p); - break; - } - case DeviceModel::STORAGE : { - auto p2 = get_payload(); - p2.set_defaults(); - strcpyT(p2.password, admin_password); - ChangeAdminUserPin20Current::CommandTransaction::run(device, p2); - auto p3 = get_payload(); - p3.set_defaults(); - strcpyT(p3.password, new_user_password); - stick20::UnlockUserPin::CommandTransaction::run(device, p3); - break; - } - } - } - - - void NitrokeyManager::write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password, - bool delete_user_password, const char *admin_temporary_password) { - auto p = get_payload(); - p.numlock = numlock; - p.capslock = capslock; - p.scrolllock = scrolllock; - p.enable_user_password = static_cast(enable_user_password ? 1 : 0); - p.delete_user_password = static_cast(delete_user_password ? 1 : 0); - if (is_authorization_command_supported()){ - authorize_packet(p, admin_temporary_password, device); - } else { - strcpyT(p.temporary_admin_password, admin_temporary_password); - } - stick10_08::WriteGeneralConfig::CommandTransaction::run(device, p); - } - - vector NitrokeyManager::read_config() { - auto responsePayload = GetStatus::CommandTransaction::run(device); - vector v = vector(responsePayload.data().general_config, - responsePayload.data().general_config+sizeof(responsePayload.data().general_config)); - return v; - } - - bool NitrokeyManager::is_authorization_command_supported(){ - //authorization command is supported for versions equal or below: - auto m = std::unordered_map({ - {DeviceModel::PRO, 7}, - {DeviceModel::STORAGE, 999}, - }); - return get_minor_firmware_version() <= m[device->get_device_model()]; - } - - bool NitrokeyManager::is_320_OTP_secret_supported(){ - //authorization command is supported for versions equal or below: - auto m = std::unordered_map({ - {DeviceModel::PRO, 8}, - {DeviceModel::STORAGE, 999}, - }); - return get_minor_firmware_version() >= m[device->get_device_model()]; - } - - DeviceModel NitrokeyManager::get_connected_device_model() const{ - if (device == nullptr){ - throw DeviceNotConnected("device not connected"); - } - return device->get_device_model(); - } - - bool NitrokeyManager::is_smartcard_in_use(){ - try{ - stick20::CheckSmartcardUsage::CommandTransaction::run(device); - } - catch(const CommandFailedException & e){ - return e.reason_smartcard_busy(); - } - return false; - } - - int NitrokeyManager::get_minor_firmware_version(){ - switch(device->get_device_model()){ - case DeviceModel::PRO:{ - auto status_p = GetStatus::CommandTransaction::run(device); - return status_p.data().firmware_version_st.minor; //7 or 8 - } - case DeviceModel::STORAGE:{ - auto status = stick20::GetDeviceStatus::CommandTransaction::run(device); - auto test_firmware = status.data().versionInfo.build_iteration != 0; - if (test_firmware) - LOG("Development firmware detected. Increasing minor version number.", nitrokey::log::Loglevel::WARNING); - return status.data().versionInfo.minor + (test_firmware? 1 : 0); - } - } - return 0; - } - int NitrokeyManager::get_major_firmware_version(){ - switch(device->get_device_model()){ - case DeviceModel::PRO:{ - auto status_p = GetStatus::CommandTransaction::run(device); - return status_p.data().firmware_version_st.major; //0 - } - case DeviceModel::STORAGE:{ - auto status = stick20::GetDeviceStatus::CommandTransaction::run(device); - return status.data().versionInfo.major; - } - } - return 0; - } - - bool NitrokeyManager::is_AES_supported(const char *user_password) { - auto a = get_payload(); - strcpyT(a.user_password, user_password); - IsAESSupported::CommandTransaction::run(device, a); - return true; - } - - //storage commands - - void NitrokeyManager::send_startup(uint64_t seconds_from_epoch){ - auto p = get_payload(); -// p.set_defaults(); //set current time - p.localtime = seconds_from_epoch; - stick20::SendStartup::CommandTransaction::run(device, p); - } - - void NitrokeyManager::unlock_encrypted_volume(const char* user_pin){ - misc::execute_password_command(device, user_pin); - } - - void NitrokeyManager::unlock_hidden_volume(const char* hidden_volume_password) { - misc::execute_password_command(device, hidden_volume_password); - } - - void NitrokeyManager::set_encrypted_volume_read_only(const char* admin_pin) { - misc::execute_password_command(device, admin_pin); - } - - void NitrokeyManager::set_encrypted_volume_read_write(const char* admin_pin) { - misc::execute_password_command(device, admin_pin); - } - - //TODO check is encrypted volume unlocked before execution - //if not return library exception - void NitrokeyManager::create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, - const char *hidden_volume_password) { - auto p = get_payload(); - p.SlotNr_u8 = slot_nr; - p.StartBlockPercent_u8 = start_percent; - p.EndBlockPercent_u8 = end_percent; - strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password); - stick20::SetupHiddenVolume::CommandTransaction::run(device, p); - } - - void NitrokeyManager::set_unencrypted_read_only_admin(const char* admin_pin) { - //from v0.49, v0.52+ it needs Admin PIN - if (set_unencrypted_volume_rorw_pin_type_user()){ - LOG("set_unencrypted_read_only_admin is not supported for this version of Storage device. " - "Please update firmware to v0.52+. Doing nothing.", nitrokey::log::Loglevel::WARNING); - return; - } - misc::execute_password_command(device, admin_pin); - } - - void NitrokeyManager::set_unencrypted_read_only(const char *user_pin) { - //until v0.48 (incl. v0.50 and v0.51) User PIN was sufficient - LOG("set_unencrypted_read_only is deprecated. Use set_unencrypted_read_only_admin instead.", - nitrokey::log::Loglevel::WARNING); - if (!set_unencrypted_volume_rorw_pin_type_user()){ - LOG("set_unencrypted_read_only is not supported for this version of Storage device. Doing nothing.", - nitrokey::log::Loglevel::WARNING); - return; - } - misc::execute_password_command(device, user_pin); - } - - void NitrokeyManager::set_unencrypted_read_write_admin(const char* admin_pin) { - //from v0.49, v0.52+ it needs Admin PIN - if (set_unencrypted_volume_rorw_pin_type_user()){ - LOG("set_unencrypted_read_write_admin is not supported for this version of Storage device. " - "Please update firmware to v0.52+. Doing nothing.", nitrokey::log::Loglevel::WARNING); - return; - } - misc::execute_password_command(device, admin_pin); - } - - void NitrokeyManager::set_unencrypted_read_write(const char *user_pin) { - //until v0.48 (incl. v0.50 and v0.51) User PIN was sufficient - LOG("set_unencrypted_read_write is deprecated. Use set_unencrypted_read_write_admin instead.", - nitrokey::log::Loglevel::WARNING); - if (!set_unencrypted_volume_rorw_pin_type_user()){ - LOG("set_unencrypted_read_write is not supported for this version of Storage device. Doing nothing.", - nitrokey::log::Loglevel::WARNING); - return; - } - misc::execute_password_command(device, user_pin); - } - - bool NitrokeyManager::set_unencrypted_volume_rorw_pin_type_user(){ - auto minor_firmware_version = get_minor_firmware_version(); - return minor_firmware_version <= 48 || minor_firmware_version == 50 || minor_firmware_version == 51; - } - - void NitrokeyManager::export_firmware(const char* admin_pin) { - misc::execute_password_command(device, admin_pin); - } - - void NitrokeyManager::enable_firmware_update(const char* firmware_pin) { - misc::execute_password_command(device, firmware_pin); - } - - void NitrokeyManager::clear_new_sd_card_warning(const char* admin_pin) { - misc::execute_password_command(device, admin_pin); - } - - void NitrokeyManager::fill_SD_card_with_random_data(const char* admin_pin) { - auto p = get_payload(); - p.set_defaults(); - strcpyT(p.admin_pin, admin_pin); - stick20::FillSDCardWithRandomChars::CommandTransaction::run(device, p); - } - - void NitrokeyManager::change_update_password(const char* current_update_password, const char* new_update_password) { - auto p = get_payload(); - strcpyT(p.current_update_password, current_update_password); - strcpyT(p.new_update_password, new_update_password); - stick20::ChangeUpdatePassword::CommandTransaction::run(device, p); - } - - char * NitrokeyManager::get_status_storage_as_string(){ - auto p = stick20::GetDeviceStatus::CommandTransaction::run(device); - return strndup(p.data().dissect().c_str(), max_string_field_length); - } - - stick20::DeviceConfigurationResponsePacket::ResponsePayload NitrokeyManager::get_status_storage(){ - auto p = stick20::GetDeviceStatus::CommandTransaction::run(device); - return p.data(); - } - - char * NitrokeyManager::get_SD_usage_data_as_string(){ - auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device); - return strndup(p.data().dissect().c_str(), max_string_field_length); - } - - std::pair NitrokeyManager::get_SD_usage_data(){ - auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device); - return std::make_pair(p.data().WriteLevelMin, p.data().WriteLevelMax); - } - - int NitrokeyManager::get_progress_bar_value(){ - try{ - stick20::GetDeviceStatus::CommandTransaction::run(device); - return -1; - } - catch (LongOperationInProgressException &e){ - return e.progress_bar_value; - } - } - - string NitrokeyManager::get_TOTP_code(uint8_t slot_number, const char *user_temporary_password) { - return get_TOTP_code(slot_number, 0, 0, 0, user_temporary_password); - } - - stick10::ReadSlot::ResponsePayload NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) { - auto p = get_payload(); - p.slot_number = slot_number; - auto data = stick10::ReadSlot::CommandTransaction::run(device, p); - return data.data(); - } - - stick10::ReadSlot::ResponsePayload NitrokeyManager::get_TOTP_slot_data(const uint8_t slot_number) { - return get_OTP_slot_data(get_internal_slot_number_for_totp(slot_number)); - } - - stick10::ReadSlot::ResponsePayload NitrokeyManager::get_HOTP_slot_data(const uint8_t slot_number) { - auto slot_data = get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number)); - if (device->get_device_model() == DeviceModel::STORAGE){ - //convert counter from string to ull - auto counter_s = std::string(slot_data.slot_counter_s, slot_data.slot_counter_s+sizeof(slot_data.slot_counter_s)); - slot_data.slot_counter = std::stoull(counter_s); - } - return slot_data; - } - - void NitrokeyManager::lock_encrypted_volume() { - misc::execute_password_command(device, ""); - } - - void NitrokeyManager::lock_hidden_volume() { - misc::execute_password_command(device, ""); - } - - uint8_t NitrokeyManager::get_SD_card_size() { - auto data = stick20::ProductionTest::CommandTransaction::run(device); - return data.data().SD_Card_Size_u8; - } - - const string NitrokeyManager::get_current_device_id() const { - return current_device_id; - } - - void NitrokeyManager::wink(){ - stick20::Wink::CommandTransaction::run(device); - }; - - stick20::ProductionTest::ResponsePayload NitrokeyManager::production_info(){ - auto data = stick20::ProductionTest::CommandTransaction::run(device); - return data.data(); - }; - -} diff --git a/nitrokey-sys/libnitrokey-v3.4.1/README.md b/nitrokey-sys/libnitrokey-v3.4.1/README.md deleted file mode 100644 index 81b367a..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/README.md +++ /dev/null @@ -1,210 +0,0 @@ -[![Build Status](https://travis-ci.org/Nitrokey/libnitrokey.svg?branch=master)](https://travis-ci.org/Nitrokey/libnitrokey) -[![Waffle.io - Columns and their card count](https://badge.waffle.io/Nitrokey/libnitrokey.svg?columns=ready,in%20progress,test,waiting%20for%20feedback)](https://waffle.io/Nitrokey/libnitrokey) - -# libnitrokey -libnitrokey is a project to communicate with Nitrokey Pro and Storage devices in a clean and easy manner. Written in C++14, testable with `py.test` and `Catch` frameworks, with C API, Python access (through CFFI and C API, in future with Pybind11). - -The development of this project is aimed to make it itself a living documentation of communication protocol between host and the Nitrokey stick devices. The command packets' format is described here: [Pro v0.7](include/stick10_commands.h), [Pro v0.8](include/stick10_commands_0.8.h), [Storage](include/stick20_commands.h). Handling and additional operations are described here: [NitrokeyManager.cc](NitrokeyManager.cc). - -A C++14 complying compiler is required due to heavy use of variable templates. For feature support tables please check [table 1](https://gcc.gnu.org/projects/cxx-status.html#cxx14) or [table 2](http://en.cppreference.com/w/cpp/compiler_support). - -libnitrokey is developed and tested with the latest compilers: g++ 6.2, clang 3.8. We use Travis CI to test builds also on g++ 5.4 and under OSX compilers starting up from xcode 8.2 environment. - -## Getting sources -This repository uses `git submodules`. -To clone please use git's `--recursive` option like in: -```bash -git clone --recursive https://github.com/Nitrokey/libnitrokey.git -``` -or for already cloned repository: -```bash -git clone https://github.com/Nitrokey/libnitrokey.git -cd libnitrokey -git submodule update --init --recursive -``` - -## Dependencies -Following libraries are needed to use libnitrokey on Linux (names of the packages on Ubuntu): -- libhidapi-dev [(HID API)](http://www.signal11.us/oss/hidapi/) -- libusb-1.0-0-dev - - -## Compilation -libnitrokey uses CMake as its main build system. As a secondary option it offers building through Qt's qMake. -### Qt -A Qt's .pro project file is provided for direct compilation and for inclusion to other projects. -Using it directly is not recommended due to lack of dependencies check and not implemented library versioning. -Compilation is tested with Qt 5.6 and greater. - -Quick start example: -```bash -mkdir -p build -cd build -qmake .. -make -j2 -``` - -### Windows MS Visual Studio 2017 -Lately Visual Studio has started handling CMake files directly. After opening the project's directory it should recognize it and initialize build system. Afterwards please run: -1. `CMake -> Cache -> View Cache CMakeLists.txt -> CMakeLists.txt` to edit settings -2. `CMake -> Build All` to build - -It is possible too to use CMake GUI directly with its settings editor. - -### CMake -To compile please run following sequence of commands: -```bash -# assuming current dir is ./libnitrokey/ -mkdir -p build -cd build -cmake .. -make -j2 -``` - -By default (with empty `` string) this will create in `build/` directory a shared library (.so, .dll or .dynlib). If you wish to build static version you can use as `` string `-DBUILD_SHARED_LIBS=OFF`. - -All options could be listed with `cmake .. -L` or instead `cmake` a `ccmake ..` tool could be used for configuration (where `..` is the path to directory with `CMakeLists.txt` file). `ccmake` shows also description of the build parameters. - -If you have trouble compiling or running the library you can check [.travis.yml](.travis.yml) file for configuration details. This file is used by Travis CI service to make test builds on OSX and Ubuntu 14.04. - -Other build options (all take either `ON` or `OFF`): -* ADD_ASAN - add tests for memory leaks and out-of-bounds access -* ADD_TSAN - add tests for threads race, needs USE_CLANG -* COMPILE_TESTS - compile C++ tests -* COMPILE_OFFLINE_TESTS - compile C++ tests, that do not require any device to be connected -* LOG_VOLATILE_DATA (default: OFF) - include secrets in log (PWS passwords, PINs etc) -* NO_LOG (default: OFF) - do not compile LOG statements - will make library smaller, but without any diagnostic messages - - - -# Using libnitrokey with Python -To use libnitrokey with Python a [CFFI](http://cffi.readthedocs.io/en/latest/overview.html) library is required (either 2.7+ or 3.0+). It can be installed with: -```bash -pip install --user cffi # for python 2.x -pip3 install cffi # for python 3.x -``` -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 -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) - - -libnitrokey = get_library() -libnitrokey.NK_set_debug(False) # do not show debug messages (log library only) - -hotp_slot_code = get_hotp_code(libnitrokey, 1) -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. - -## Documentation -The documentation of C API is included in the sources (could be generated with doxygen if requested). -Please check [NK_C_API.h](NK_C_API.h) (C API) for high level commands and [include/NitrokeyManager.h](include/NitrokeyManager.h) (C++ API). All devices' commands are listed along with packet format in [include/stick10_commands.h](include/stick10_commands.h) and [include/stick20_commands.h](include/stick20_commands.h) respectively for Nitrokey Pro and Nitrokey Storage products. - -# Tests -Warning! Before you run unittests please either change both your Admin and User PINs on your Nitrostick to defaults (`12345678` and `123456` respectively) or change the values in tests source code. If you do not change them the tests might lock your device and lose your data. If it's too late, you can reset your Nitrokey using instructions from [homepage](https://www.nitrokey.com/de/documentation/how-reset-nitrokey). - -## Python tests -libnitrokey has a great suite of tests written in Python 3 under the path: `unittest/test_*.py`: -* `test_pro.py` - contains tests of OTP, Password Safe and PIN control functionality. Could be run on both Pro and Storage devices. -* `test_storage.py` - contains tests of Encrypted Volumes functionality. Could be run only on Storage. -The tests themselves show how to handle common requests to device. -Before running please install all required libraries with: -```bash -cd unittest -pip install --user -r requirements.txt -``` -To run them please execute: -```bash -# substitute with either pro or storage -py.test -v test_.py -# more specific use - run tests containing in name 5 times: -py.test -v test_.py -k --count 5 - -``` -For additional documentation please check the following for [py.test installation](http://doc.pytest.org/en/latest/getting-started.html). For better coverage [randomly plugin](https://pypi.python.org/pypi/pytest-randomly) is installed - it randomizes the test order allowing to detect unseen dependencies between the tests. - -## C++ tests -There are also some unit tests implemented in C++, placed in unittest directory. They are not written as extensively as Python tests and are rather more a C++ low level interface check, often not using C++ API from `NitrokeyManager.cc`. Some of them are: [test_HOTP.cc](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test_HOTP.cc), -[test.cc](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test.cc). -Unit tests were written and tested on Ubuntu 16.04/16.10/17.04. To run them just execute binaries built in ./libnitrokey/build dir after enabling them by passing `-DCOMPILE_TESTS=ON` option like in `cmake .. -DCOMPILE_TESTS=ON && make`. - - -The documentation of how it works could be found in nitrokey-app project's README on Github: -[Nitrokey-app - internals](https://github.com/Nitrokey/nitrokey-app/blob/master/README.md#internals). - -To peek/debug communication with device running nitrokey-app (0.x branch) in debug mode (`-d` switch) and checking the logs -(right click on tray icon and then 'Debug') might be helpful. Latest Nitrokey App (1.x branch) uses libnitrokey to communicate with device. Once run with `--dl 3` (3 or higher; range 0-5) it will print all communication to the console. Additionally crosschecking with -firmware code should show how things works: -[report_protocol.c](https://github.com/Nitrokey/nitrokey-pro-firmware/blob/master/src/keyboard/report_protocol.c) -(for Nitrokey Pro, for Storage similarly). - -# Known issues / tasks -* Currently only one device can be connected at a time (experimental work could be found in `wip-multiple_devices` branch), -* C++ API needs some reorganization to C++ objects (instead of pointers to arrays). This will be also preparing for integration with Pybind11, -* Fix compilation warnings. - -Other tasks might be listed either in [TODO](TODO) file or on project's issues page. - -# License -This project is licensed under LGPL version 3. License text could be found under [LICENSE](LICENSE) file. - -# Roadmap -To check what issues will be fixed and when please check [milestones](https://github.com/Nitrokey/libnitrokey/milestones) page. diff --git a/nitrokey-sys/libnitrokey-v3.4.1/command_id.cc b/nitrokey-sys/libnitrokey-v3.4.1/command_id.cc deleted file mode 100644 index a6c2a28..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/command_id.cc +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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 - */ - -#include -#include "command_id.h" - -namespace nitrokey { -namespace proto { - -const char *commandid_to_string(CommandID id) { - switch (id) { - case CommandID::GET_STATUS: - return "GET_STATUS"; - case CommandID::WRITE_TO_SLOT: - return "WRITE_TO_SLOT"; - case CommandID::READ_SLOT_NAME: - return "READ_SLOT_NAME"; - case CommandID::READ_SLOT: - return "READ_SLOT"; - case CommandID::GET_CODE: - return "GET_CODE"; - case CommandID::WRITE_CONFIG: - return "WRITE_CONFIG"; - case CommandID::ERASE_SLOT: - return "ERASE_SLOT"; - case CommandID::FIRST_AUTHENTICATE: - return "FIRST_AUTHENTICATE"; - case CommandID::AUTHORIZE: - return "AUTHORIZE"; - case CommandID::GET_PASSWORD_RETRY_COUNT: - return "GET_PASSWORD_RETRY_COUNT"; - case CommandID::CLEAR_WARNING: - return "CLEAR_WARNING"; - case CommandID::SET_TIME: - return "SET_TIME"; - case CommandID::TEST_COUNTER: - return "TEST_COUNTER"; - case CommandID::TEST_TIME: - return "TEST_TIME"; - case CommandID::USER_AUTHENTICATE: - return "USER_AUTHENTICATE"; - case CommandID::GET_USER_PASSWORD_RETRY_COUNT: - return "GET_USER_PASSWORD_RETRY_COUNT"; - case CommandID::USER_AUTHORIZE: - return "USER_AUTHORIZE"; - case CommandID::UNLOCK_USER_PASSWORD: - return "UNLOCK_USER_PASSWORD"; - case CommandID::LOCK_DEVICE: - return "LOCK_DEVICE"; - case CommandID::FACTORY_RESET: - return "FACTORY_RESET"; - case CommandID::CHANGE_USER_PIN: - return "CHANGE_USER_PIN"; - case CommandID::CHANGE_ADMIN_PIN: - return "CHANGE_ADMIN_PIN"; - - case CommandID::ENABLE_CRYPTED_PARI: - return "ENABLE_CRYPTED_PARI"; - case CommandID::DISABLE_CRYPTED_PARI: - return "DISABLE_CRYPTED_PARI"; - case CommandID::ENABLE_HIDDEN_CRYPTED_PARI: - return "ENABLE_HIDDEN_CRYPTED_PARI"; - case CommandID::DISABLE_HIDDEN_CRYPTED_PARI: - return "DISABLE_HIDDEN_CRYPTED_PARI"; - case CommandID::ENABLE_FIRMWARE_UPDATE: - return "ENABLE_FIRMWARE_UPDATE"; - case CommandID::EXPORT_FIRMWARE_TO_FILE: - return "EXPORT_FIRMWARE_TO_FILE"; - case CommandID::GENERATE_NEW_KEYS: - return "GENERATE_NEW_KEYS"; - case CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS: - return "FILL_SD_CARD_WITH_RANDOM_CHARS"; - - case CommandID::WRITE_STATUS_DATA: - return "WRITE_STATUS_DATA"; - case CommandID::ENABLE_READONLY_UNCRYPTED_LUN: - return "ENABLE_READONLY_UNCRYPTED_LUN"; - case CommandID::ENABLE_READWRITE_UNCRYPTED_LUN: - return "ENABLE_READWRITE_UNCRYPTED_LUN"; - - case CommandID::SEND_PASSWORD_MATRIX: - return "SEND_PASSWORD_MATRIX"; - case CommandID::SEND_PASSWORD_MATRIX_PINDATA: - return "SEND_PASSWORD_MATRIX_PINDATA"; - case CommandID::SEND_PASSWORD_MATRIX_SETUP: - return "SEND_PASSWORD_MATRIX_SETUP"; - - case CommandID::GET_DEVICE_STATUS: - return "GET_DEVICE_STATUS"; - case CommandID::SEND_DEVICE_STATUS: - return "SEND_DEVICE_STATUS"; - - case CommandID::SEND_HIDDEN_VOLUME_PASSWORD: - return "SEND_HIDDEN_VOLUME_PASSWORD"; - case CommandID::SEND_HIDDEN_VOLUME_SETUP: - return "SEND_HIDDEN_VOLUME_SETUP"; - case CommandID::SEND_PASSWORD: - return "SEND_PASSWORD"; - case CommandID::SEND_NEW_PASSWORD: - return "SEND_NEW_PASSWORD"; - case CommandID::CLEAR_NEW_SD_CARD_FOUND: - return "CLEAR_NEW_SD_CARD_FOUND"; - - case CommandID::SEND_STARTUP: - return "SEND_STARTUP"; - case CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED: - return "SEND_CLEAR_STICK_KEYS_NOT_INITIATED"; - case CommandID::SEND_LOCK_STICK_HARDWARE: - return "SEND_LOCK_STICK_HARDWARE"; - - case CommandID::PRODUCTION_TEST: - return "PRODUCTION_TEST"; - case CommandID::SEND_DEBUG_DATA: - return "SEND_DEBUG_DATA"; - - case CommandID::CHANGE_UPDATE_PIN: - return "CHANGE_UPDATE_PIN"; - - case CommandID::ENABLE_ADMIN_READONLY_UNCRYPTED_LUN: - return "ENABLE_ADMIN_READONLY_UNCRYPTED_LUN"; - case CommandID::ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN: - return "ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN"; - case CommandID::ENABLE_ADMIN_READONLY_ENCRYPTED_LUN: - return "ENABLE_ADMIN_READONLY_ENCRYPTED_LUN"; - case CommandID::ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN: - return "ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN"; - case CommandID::CHECK_SMARTCARD_USAGE: - return "CHECK_SMARTCARD_USAGE"; - - case CommandID::GET_PW_SAFE_SLOT_STATUS: - return "GET_PW_SAFE_SLOT_STATUS"; - case CommandID::GET_PW_SAFE_SLOT_NAME: - return "GET_PW_SAFE_SLOT_NAME"; - case CommandID::GET_PW_SAFE_SLOT_PASSWORD: - return "GET_PW_SAFE_SLOT_PASSWORD"; - case CommandID::GET_PW_SAFE_SLOT_LOGINNAME: - return "GET_PW_SAFE_SLOT_LOGINNAME"; - case CommandID::SET_PW_SAFE_SLOT_DATA_1: - return "SET_PW_SAFE_SLOT_DATA_1"; - case CommandID::SET_PW_SAFE_SLOT_DATA_2: - return "SET_PW_SAFE_SLOT_DATA_2"; - case CommandID::PW_SAFE_ERASE_SLOT: - return "PW_SAFE_ERASE_SLOT"; - case CommandID::PW_SAFE_ENABLE: - return "PW_SAFE_ENABLE"; - case CommandID::PW_SAFE_INIT_KEY: - return "PW_SAFE_INIT_KEY"; - case CommandID::PW_SAFE_SEND_DATA: - return "PW_SAFE_SEND_DATA"; - case CommandID::SD_CARD_HIGH_WATERMARK: - return "SD_CARD_HIGH_WATERMARK"; - case CommandID::DETECT_SC_AES: - return "DETECT_SC_AES"; - case CommandID::NEW_AES_KEY: - return "NEW_AES_KEY"; - case CommandID::WRITE_TO_SLOT_2: - return "WRITE_TO_SLOT_2"; - case CommandID::SEND_OTP_DATA: - return "SEND_OTP_DATA"; - case CommandID::WINK: - return "WINK"; - } - return "UNKNOWN"; -} -} -} diff --git a/nitrokey-sys/libnitrokey-v3.4.1/device.cc b/nitrokey-sys/libnitrokey-v3.4.1/device.cc deleted file mode 100644 index 80e4b38..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/device.cc +++ /dev/null @@ -1,295 +0,0 @@ -/* - * 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 - */ - -#include -#include -#include -#include -#include -#include "hidapi/hidapi.h" -#include "libnitrokey/misc.h" -#include "libnitrokey/device.h" -#include "libnitrokey/log.h" -#include -#include "DeviceCommunicationExceptions.h" -#include "device.h" - -std::mutex mex_dev_com; - -using namespace nitrokey::device; -using namespace nitrokey::log; -using namespace std::chrono; - -std::atomic_int Device::instances_count{0}; -std::chrono::milliseconds Device::default_delay {0} ; - -Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, - const milliseconds send_receive_delay, const int retry_receiving_count, - const milliseconds retry_timeout) - : - last_command_status(0), - m_vid(vid), - m_pid(pid), - m_model(model), - m_retry_sending_count(1), - m_retry_receiving_count(retry_receiving_count), - m_retry_timeout(retry_timeout), - m_send_receive_delay(send_receive_delay), - mp_devhandle(nullptr) -{ - instances_count++; -} - -bool Device::disconnect() { - //called in object's destructor - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - std::lock_guard lock(mex_dev_com); - return _disconnect(); -} - -bool Device::_disconnect() { - LOG(std::string(__FUNCTION__) + std::string(m_model == DeviceModel::PRO ? "PRO" : "STORAGE"), Loglevel::DEBUG_L2); - LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); - - if(mp_devhandle == nullptr) { - LOG(std::string("Disconnection: handle already freed: ") + std::to_string(mp_devhandle == nullptr) + " ("+m_path+")", Loglevel::DEBUG_L1); - return false; - } - - hid_close(mp_devhandle); - mp_devhandle = nullptr; -#ifndef __APPLE__ - if (instances_count == 1){ - LOG(std::string("Calling hid_exit"), Loglevel::DEBUG_L2); - hid_exit(); - } -#endif - return true; -} - -bool Device::connect() { - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - std::lock_guard lock(mex_dev_com); - return _connect(); -} - -bool Device::_connect() { - LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); - -// hid_init(); // done automatically on hid_open - if (m_path.empty()){ - mp_devhandle = hid_open(m_vid, m_pid, nullptr); - } else { - mp_devhandle = hid_open_path(m_path.c_str()); - } - const bool success = mp_devhandle != nullptr; - LOG(std::string("Connection success: ") + std::to_string(success) + " ("+m_path+")", Loglevel::DEBUG_L1); - return success; -} - -void Device::set_path(const std::string path){ - m_path = path; -} - -int Device::send(const void *packet) { - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - std::lock_guard lock(mex_dev_com); - LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); - - int send_feature_report = -1; - - for (int i = 0; i < 3 && send_feature_report < 0; ++i) { - if (mp_devhandle == nullptr) { - LOG(std::string("Connection fail") , Loglevel::DEBUG_L2); - throw DeviceNotConnected("Attempted HID send on an invalid descriptor."); - } - send_feature_report = hid_send_feature_report( - mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE); - if (send_feature_report < 0) _reconnect(); - //add thread sleep? - LOG(std::string("Sending attempt: ")+std::to_string(i+1) + " / 3" , Loglevel::DEBUG_L2); - } - return send_feature_report; -} - -int Device::recv(void *packet) { - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - std::lock_guard lock(mex_dev_com); - LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); - int status; - int retry_count = 0; - - for (;;) { - if (mp_devhandle == nullptr){ - LOG(std::string("Connection fail") , Loglevel::DEBUG_L2); - throw DeviceNotConnected("Attempted HID receive on an invalid descriptor."); - } - - status = (hid_get_feature_report(mp_devhandle, (unsigned char *)(packet), - HID_REPORT_SIZE)); - - auto pwherr = hid_error(mp_devhandle); - std::wstring wherr = (pwherr != nullptr) ? pwherr : L"No error message"; - std::string herr(wherr.begin(), wherr.end()); - LOG(std::string("libhid error message: ") + herr, - Loglevel::DEBUG_L2); - - if (status > 0) break; // success - if (retry_count++ >= m_retry_receiving_count) { - LOG( - "Maximum retry count reached: " + std::to_string(retry_count), - Loglevel::WARNING); - LOG( - std::string("Counter stats: ") + m_counters.get_as_string(), - Loglevel::DEBUG); - break; - } - _reconnect(); - LOG("Retrying... " + std::to_string(retry_count), - Loglevel::DEBUG); - std::this_thread::sleep_for(m_retry_timeout); - } - - return status; -} - -std::vector Device::enumerate(){ - //TODO make static - auto pInfo = hid_enumerate(m_vid, m_pid); - auto pInfo_ = pInfo; - std::vector res; - while (pInfo != nullptr){ - std::string a (pInfo->path); - res.push_back(a); - pInfo = pInfo->next; - } - - if (pInfo_ != nullptr){ - hid_free_enumeration(pInfo_); - } - - return res; -} - -bool Device::could_be_enumerated() { - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - std::lock_guard lock(mex_dev_com); - if (mp_devhandle==nullptr){ - return false; - } -#ifndef __APPLE__ - auto pInfo = hid_enumerate(m_vid, m_pid); - if (pInfo != nullptr){ - hid_free_enumeration(pInfo); - return true; - } - return false; -#else -// alternative for OSX - unsigned char buf[1]; - return hid_read_timeout(mp_devhandle, buf, sizeof(buf), 20) != -1; -#endif -} - -void Device::show_stats() { - auto s = m_counters.get_as_string(); - LOG(s, Loglevel::DEBUG_L2); -} - -void Device::_reconnect() { - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - ++m_counters.low_level_reconnect; - _disconnect(); - _connect(); -} - -Device::~Device() { - show_stats(); - disconnect(); - instances_count--; -} - -void Device::set_default_device_speed(int delay) { - default_delay = std::chrono::duration(delay); -} - - -void Device::set_receiving_delay(const std::chrono::milliseconds delay){ - std::lock_guard lock(mex_dev_com); - m_send_receive_delay = delay; -} - -void Device::set_retry_delay(const std::chrono::milliseconds delay){ - std::lock_guard lock(mex_dev_com); - m_retry_timeout = delay; -} - -Stick10::Stick10(): - Device(0x20a0, 0x4108, DeviceModel::PRO, 100ms, 5, 100ms) - { - setDefaultDelay(); - } - - -Stick20::Stick20(): - Device(0x20a0, 0x4109, DeviceModel::STORAGE, 40ms, 55, 40ms) - { - setDefaultDelay(); - } - -#include -#define p(x) ss << #x << " " << x << ", "; -std::string Device::ErrorCounters::get_as_string() { - std::stringstream ss; - p(total_comm_runs); - p(communication_successful); - ss << "("; - p(command_successful_recv); - p(command_result_not_equal_0_recv); - ss << "), "; - p(sends_executed); - p(recv_executed); - p(successful_storage_commands); - p(total_retries); - ss << "("; - p(busy); - p(busy_progressbar); - p(CRC_other_than_awaited); - p(wrong_CRC); - ss << "), "; - p(low_level_reconnect); - p(sending_error); - p(receiving_error); - return ss.str(); -} - -void Device::setDefaultDelay() { - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - - auto count = default_delay.count(); - if (count != 0){ - LOG("Setting default delay to " + std::to_string(count), Loglevel::DEBUG_L2); - m_retry_timeout = default_delay; - m_send_receive_delay = default_delay; - } -} - -#undef p diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/CommandFailedException.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/CommandFailedException.h deleted file mode 100644 index 32bd6b7..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/CommandFailedException.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 - */ - -#ifndef LIBNITROKEY_COMMANDFAILEDEXCEPTION_H -#define LIBNITROKEY_COMMANDFAILEDEXCEPTION_H - -#include -#include -#include "log.h" -#include "command_id.h" - -using cs = nitrokey::proto::stick10::command_status; -using cs2 = nitrokey::proto::stick20::device_status; - -class CommandFailedException : public std::exception { -public: - const uint8_t last_command_id; - const uint8_t last_command_status; - - CommandFailedException(uint8_t last_command_id, uint8_t last_command_status) : - last_command_id(last_command_id), - last_command_status(last_command_status){ - LOG(std::string("CommandFailedException, status: ")+ std::to_string(last_command_status), nitrokey::log::Loglevel::DEBUG); - } - - virtual const char *what() const throw() { - return "Command execution has failed on device"; - } - - - bool reason_timestamp_warning() const throw(){ - return last_command_status == static_cast(cs::timestamp_warning); - } - - bool reason_AES_not_initialized() const throw(){ - return last_command_status == static_cast(cs::AES_dec_failed); - } - - bool reason_not_authorized() const throw(){ - return last_command_status == static_cast(cs::not_authorized); - } - - bool reason_slot_not_programmed() const throw(){ - return last_command_status == static_cast(cs::slot_not_programmed); - } - - bool reason_wrong_password() const throw(){ - return last_command_status == static_cast(cs::wrong_password); - } - - bool reason_smartcard_busy() const throw(){ - return last_command_status == static_cast(cs2::smartcard_error); - } - -}; - - -#endif //LIBNITROKEY_COMMANDFAILEDEXCEPTION_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/DeviceCommunicationExceptions.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/DeviceCommunicationExceptions.h deleted file mode 100644 index f710d0b..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/DeviceCommunicationExceptions.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 - */ - - -#ifndef LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H -#define LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H - -#include -#include -#include -#include - - -class DeviceCommunicationException: public std::runtime_error -{ - std::string message; - static std::atomic_int occurred; -public: - DeviceCommunicationException(std::string _msg): std::runtime_error(_msg), message(_msg){ - ++occurred; - } - uint8_t getType() const {return 1;}; -// virtual const char* what() const throw() override { -// return message.c_str(); -// } - static bool has_occurred(){ return occurred > 0; }; - static void reset_occurred_flag(){ occurred = 0; }; -}; - -class DeviceNotConnected: public DeviceCommunicationException { -public: - DeviceNotConnected(std::string msg) : DeviceCommunicationException(msg){} - uint8_t getType() const {return 2;}; -}; - -class DeviceSendingFailure: public DeviceCommunicationException { -public: - DeviceSendingFailure(std::string msg) : DeviceCommunicationException(msg){} - uint8_t getType() const {return 3;}; -}; - -class DeviceReceivingFailure: public DeviceCommunicationException { -public: - DeviceReceivingFailure(std::string msg) : DeviceCommunicationException(msg){} - uint8_t getType() const {return 4;}; -}; - -class InvalidCRCReceived: public DeviceReceivingFailure { -public: - InvalidCRCReceived(std::string msg) : DeviceReceivingFailure(msg){} - uint8_t getType() const {return 5;}; -}; - - -#endif //LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LibraryException.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LibraryException.h deleted file mode 100644 index 3b9d177..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LibraryException.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 - */ - -#ifndef LIBNITROKEY_LIBRARYEXCEPTION_H -#define LIBNITROKEY_LIBRARYEXCEPTION_H - -#include -#include -#include -#include "log.h" - -class LibraryException: std::exception { -public: - virtual uint8_t exception_id()= 0; -}; - -class TargetBufferSmallerThanSource: public LibraryException { -public: - virtual uint8_t exception_id() override { - return 203; - } - -public: - size_t source_size; - size_t target_size; - - TargetBufferSmallerThanSource( - size_t source_size, size_t target_size - ) : source_size(source_size), target_size(target_size) {} - - virtual const char *what() const throw() override { - std::string s = " "; - auto ts = [](size_t x){ return std::to_string(x); }; - std::string msg = std::string("Target buffer size is smaller than source: [source size, buffer size]") - +s+ ts(source_size) +s+ ts(target_size); - return msg.c_str(); - } - -}; - -class InvalidHexString : public LibraryException { -public: - virtual uint8_t exception_id() override { - return 202; - } - -public: - uint8_t invalid_char; - - InvalidHexString (uint8_t invalid_char) : invalid_char( invalid_char) {} - - virtual const char *what() const throw() override { - return "Invalid character in hex string"; - } - -}; - -class InvalidSlotException : public LibraryException { -public: - virtual uint8_t exception_id() override { - return 201; - } - -public: - uint8_t slot_selected; - - InvalidSlotException(uint8_t slot_selected) : slot_selected(slot_selected) {} - - virtual const char *what() const throw() override { - return "Wrong slot selected"; - } - -}; - - - -class TooLongStringException : public LibraryException { -public: - virtual uint8_t exception_id() override { - return 200; - } - - std::size_t size_source; - std::size_t size_destination; - std::string message; - - TooLongStringException(size_t size_source, size_t size_destination, const std::string &message = "") : size_source( - size_source), size_destination(size_destination), message(message) { - LOG(std::string("TooLongStringException, size diff: ")+ std::to_string(size_source-size_destination), nitrokey::log::Loglevel::DEBUG); - - } - - virtual const char *what() const throw() override { - //TODO add sizes and message data to final message - return "Too long string has been supplied as an argument"; - } - -}; - -#endif //LIBNITROKEY_LIBRARYEXCEPTION_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LongOperationInProgressException.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LongOperationInProgressException.h deleted file mode 100644 index 865d6b5..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/LongOperationInProgressException.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - */ - -#ifndef LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H -#define LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H - -#include "CommandFailedException.h" - -class LongOperationInProgressException : public CommandFailedException { - -public: - unsigned char progress_bar_value; - - LongOperationInProgressException( - unsigned char _command_id, uint8_t last_command_status, unsigned char _progress_bar_value) - : CommandFailedException(_command_id, last_command_status), progress_bar_value(_progress_bar_value){ - LOG( - std::string("LongOperationInProgressException, progress bar status: ")+ - std::to_string(progress_bar_value), nitrokey::log::Loglevel::DEBUG); - } - virtual const char *what() const throw() { - return "Device returned busy status with long operation in progress"; - } -}; - - -#endif //LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/NitrokeyManager.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/NitrokeyManager.h deleted file mode 100644 index d6e5df4..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/NitrokeyManager.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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 - */ - -#ifndef LIBNITROKEY_NITROKEYMANAGER_H -#define LIBNITROKEY_NITROKEYMANAGER_H - -#include "device.h" -#include "log.h" -#include "device_proto.h" -#include "stick10_commands.h" -#include "stick10_commands_0.8.h" -#include "stick20_commands.h" -#include -#include -#include - -namespace nitrokey { - using namespace nitrokey::device; - using namespace std; - using namespace nitrokey::proto::stick10; - using namespace nitrokey::proto::stick20; - using namespace nitrokey::proto; - using namespace nitrokey::log; - - -#ifdef __WIN32 -char * strndup(const char* str, size_t maxlen); -#endif - - class NitrokeyManager { - public: - static shared_ptr instance(); - - bool first_authenticate(const char *pin, const char *temporary_password); - bool 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); - bool 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); - string get_HOTP_code(uint8_t slot_number, const char *user_temporary_password); - string get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval, - const char *user_temporary_password); - string get_TOTP_code(uint8_t slot_number, const char *user_temporary_password); - stick10::ReadSlot::ResponsePayload get_TOTP_slot_data(const uint8_t slot_number); - stick10::ReadSlot::ResponsePayload get_HOTP_slot_data(const uint8_t slot_number); - - bool set_time(uint64_t time); - /** - * Set the device time used for TOTP to the given time. Contrary to - * {@code set_time(uint64_t)}, this command fails if {@code old_time} - * > {@code time} or if {@code old_time} is zero (where {@code - * old_time} is the current time on the device). - * - * @param time new device time as Unix timestamp (seconds since - * 1970-01-01) - */ - void set_time_soft(uint64_t time); - - [[deprecated("get_time is deprecated -- use set_time_soft instead")]] - bool get_time(uint64_t time = 0); - bool erase_totp_slot(uint8_t slot_number, const char *temporary_password); - bool erase_hotp_slot(uint8_t slot_number, const char *temporary_password); - std::vector list_devices(); - std::vector list_devices_by_cpuID(); - - /** - * Connect to the device using unique smartcard:datacard id. - * Needs list_device_by_cpuID() run first - * @param id Current ID of the target device - * @return true on success, false on failure - */ - bool connect_with_ID(const std::string id); - bool connect_with_path (std::string path); - bool connect(const char *device_model); - bool connect(device::DeviceModel device_model); - bool connect(); - bool disconnect(); - bool is_connected() throw() ; - bool could_current_device_be_enumerated(); - bool set_default_commands_delay(int delay); - - DeviceModel get_connected_device_model() const; - void set_debug(bool state); - stick10::GetStatus::ResponsePayload get_status(); - string get_status_as_string(); - string get_serial_number(); - - char * get_totp_slot_name(uint8_t slot_number); - char * get_hotp_slot_name(uint8_t slot_number); - - void change_user_PIN(const char *current_PIN, const char *new_PIN); - void change_admin_PIN(const char *current_PIN, const char *new_PIN); - - void enable_password_safe(const char *user_pin); - - vector get_password_safe_slot_status(); - - uint8_t get_admin_retry_count(); - uint8_t get_user_retry_count(); - - void lock_device(); - - char * get_password_safe_slot_name(uint8_t slot_number); - char * get_password_safe_slot_password(uint8_t slot_number); - char * get_password_safe_slot_login(uint8_t slot_number); - - void - write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, - const char *slot_password); - - void erase_password_safe_slot(uint8_t slot_number); - - void user_authenticate(const char *user_password, const char *temporary_password); - - void factory_reset(const char *admin_password); - - void build_aes_key(const char *admin_password); - - void unlock_user_password(const char *admin_password, const char *new_user_password); - - void write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password, - bool delete_user_password, const char *admin_temporary_password); - - vector read_config(); - - bool is_AES_supported(const char *user_password); - - void unlock_encrypted_volume(const char *user_password); - void lock_encrypted_volume(); - - void unlock_hidden_volume(const char *hidden_volume_password); - void lock_hidden_volume(); - - /** - * Sets unencrypted volume read-only. - * Works until v0.48 (incl. v0.50), where User PIN was sufficient - * Does nothing otherwise. - * @param user_pin User PIN - */ - void set_unencrypted_read_only(const char *user_pin); - - /** - * Sets unencrypted volume read-only. - * Works from v0.49 (except v0.50) accepts Admin PIN - * Does nothing otherwise. - * @param admin_pin Admin PIN - */ - void set_unencrypted_read_only_admin(const char *admin_pin); - - /** - * Sets unencrypted volume read-write. - * Works until v0.48 (incl. v0.50), where User PIN was sufficient - * Does nothing otherwise. - * @param user_pin User PIN - */ - void set_unencrypted_read_write(const char *user_pin); - - /** - * Sets unencrypted volume read-write. - * Works from v0.49 (except v0.50) accepts Admin PIN - * Does nothing otherwise. - * @param admin_pin Admin PIN - */ - void set_unencrypted_read_write_admin(const char *admin_pin); - - void export_firmware(const char *admin_pin); - void enable_firmware_update(const char *firmware_pin); - - void clear_new_sd_card_warning(const char *admin_pin); - - void fill_SD_card_with_random_data(const char *admin_pin); - - uint8_t get_SD_card_size(); - - void change_update_password(const char *current_update_password, const char *new_update_password); - - void create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, - const char *hidden_volume_password); - - void send_startup(uint64_t seconds_from_epoch); - - char * get_status_storage_as_string(); - stick20::DeviceConfigurationResponsePacket::ResponsePayload get_status_storage(); - - char * get_SD_usage_data_as_string(); - std::pair get_SD_usage_data(); - - - int get_progress_bar_value(); - - ~NitrokeyManager(); - bool is_authorization_command_supported(); - bool is_320_OTP_secret_supported(); - - - template - void authorize_packet(T &package, const char *admin_temporary_password, shared_ptr device); - int get_minor_firmware_version(); - - explicit NitrokeyManager(); - void set_log_function(std::function log_function); - private: - - static shared_ptr _instance; - std::shared_ptr device; - std::string current_device_id; - public: - const string get_current_device_id() const; - - private: - std::unordered_map > connected_devices; - std::unordered_map > connected_devices_byID; - - - stick10::ReadSlot::ResponsePayload get_OTP_slot_data(const uint8_t slot_number); - bool is_valid_hotp_slot_number(uint8_t slot_number) const; - bool is_valid_totp_slot_number(uint8_t slot_number) const; - bool is_valid_password_safe_slot_number(uint8_t slot_number) const; - uint8_t get_internal_slot_number_for_hotp(uint8_t slot_number) const; - uint8_t get_internal_slot_number_for_totp(uint8_t slot_number) const; - bool erase_slot(uint8_t slot_number, const char *temporary_password); - char * get_slot_name(uint8_t slot_number); - - template - void change_PIN_general(const char *current_PIN, const char *new_PIN); - - void write_HOTP_slot_authorize(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); - - void write_TOTP_slot_authorize(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); - - void write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name, const char *secret, - uint64_t counter_or_interval, - bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID, - const char *temporary_password) const; - bool _disconnect_no_lock(); - - public: - bool set_current_device_speed(int retry_delay, int send_receive_delay); - void set_loglevel(Loglevel loglevel); - - void set_loglevel(int loglevel); - - /** - * Sets encrypted volume read-only. - * Supported from future versions of Storage. - * @param admin_pin Admin PIN - */ - void set_encrypted_volume_read_only(const char *admin_pin); - - /** - * Sets encrypted volume read-write. - * Supported from future versions of Storage. - * @param admin_pin Admin PIN - */ - void set_encrypted_volume_read_write(const char *admin_pin); - - int get_major_firmware_version(); - - bool is_smartcard_in_use(); - - /** - * Function to determine unencrypted volume PIN type - * @param minor_firmware_version - * @return Returns true, if set unencrypted volume ro/rw pin type is User, false otherwise. - */ - bool set_unencrypted_volume_rorw_pin_type_user(); - - /** - * Blink red and green LED alternatively and infinitely (until device is reconnected). - */ - void wink(); - - stick20::ProductionTest::ResponsePayload production_info(); - }; -} - - - -#endif //LIBNITROKEY_NITROKEYMANAGER_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command.h deleted file mode 100644 index 6852bf0..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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 - */ - -#ifndef COMMAND_H -#define COMMAND_H -#include -#include "command_id.h" -#include "cxx_semantics.h" - -#define print_to_ss(x) ( ss << " " << (#x) <<":\t" << (x) << std::endl ); -#ifdef LOG_VOLATILE_DATA -#define print_to_ss_volatile(x) print_to_ss(x); -#else -#define print_to_ss_volatile(x) ( ss << " " << (#x) <<":\t" << "***********" << std::endl ); -#endif -#define hexdump_to_ss(x) (ss << #x":\n"\ - << ::nitrokey::misc::hexdump((const uint8_t *) (&x), sizeof x, false)); - -namespace nitrokey { - namespace proto { - - template - class Command : semantics::non_constructible { - public: - constexpr static CommandID command_id() { return cmd_id; } - - template - std::string dissect(const T &) { - return std::string("Payload dissection is unavailable"); - } - }; - -namespace stick20{ - enum class PasswordKind : uint8_t { - User = 'P', - Admin = 'A', - AdminPrefixed - }; - - template - class PasswordCommand : public Command { - constexpr static CommandID _command_id() { return cmd_id; } - public: - struct CommandPayload { - uint8_t kind; - uint8_t password[password_length]; - - std::string dissect() const { - std::stringstream ss; - print_to_ss( kind ); - print_to_ss_volatile(password); - return ss.str(); - } - void set_kind_admin() { - kind = (uint8_t) 'A'; - } - void set_kind_admin_prefixed() { - kind = (uint8_t) 'P'; - } - void set_kind_user() { - kind = (uint8_t) 'P'; - } - - void set_defaults(){ - set_kind(Tpassword_kind); - } - - void set_kind(PasswordKind password_kind){ - switch (password_kind){ - case PasswordKind::Admin: - set_kind_admin(); - break; - case PasswordKind::User: - set_kind_user(); - break; - case PasswordKind::AdminPrefixed: - set_kind_admin_prefixed(); - break; - } - }; - - } __packed; - - //typedef Transaction::command_id(), struct CommandPayload, struct EmptyPayload> - // CommandTransaction; - using CommandTransaction = Transaction; - //using CommandTransaction = Transaction<_command_id(), CommandPayload, EmptyPayload>; - - }; - } - } -} - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command_id.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command_id.h deleted file mode 100644 index eb0d450..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/command_id.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 - */ - -#ifndef COMMAND_ID_H -#define COMMAND_ID_H -#include - -namespace nitrokey { -namespace proto { - namespace stick20 { - enum class device_status : uint8_t { - idle = 0, - ok, - busy, - wrong_password, - busy_progressbar, - password_matrix_ready, - no_user_password_unlock, // FIXME: translate on receive to command status error (fix in firmware?) - smartcard_error, - security_bit_active - }; - const int CMD_START_VALUE = 0x20; - const int CMD_END_VALUE = 0x60; - } - namespace stick10 { - enum class command_status : uint8_t { - ok = 0, - wrong_CRC, - wrong_slot, - slot_not_programmed, - wrong_password = 4, - not_authorized, - timestamp_warning, - no_name_error, - not_supported, - unknown_command, - AES_dec_failed - }; - enum class device_status : uint8_t { - ok = 0, - busy = 1, - error, - received_report, - }; - } - - -enum class CommandID : uint8_t { - GET_STATUS = 0x00, - WRITE_TO_SLOT = 0x01, - READ_SLOT_NAME = 0x02, - READ_SLOT = 0x03, - GET_CODE = 0x04, - WRITE_CONFIG = 0x05, - ERASE_SLOT = 0x06, - FIRST_AUTHENTICATE = 0x07, - AUTHORIZE = 0x08, - GET_PASSWORD_RETRY_COUNT = 0x09, - CLEAR_WARNING = 0x0A, - SET_TIME = 0x0B, - TEST_COUNTER = 0x0C, - TEST_TIME = 0x0D, - USER_AUTHENTICATE = 0x0E, - GET_USER_PASSWORD_RETRY_COUNT = 0x0F, - USER_AUTHORIZE = 0x10, - UNLOCK_USER_PASSWORD = 0x11, - LOCK_DEVICE = 0x12, - FACTORY_RESET = 0x13, - CHANGE_USER_PIN = 0x14, - CHANGE_ADMIN_PIN = 0x15, - WRITE_TO_SLOT_2 = 0x16, - SEND_OTP_DATA = 0x17, - - ENABLE_CRYPTED_PARI = 0x20, - DISABLE_CRYPTED_PARI = 0x20 + 1, - ENABLE_HIDDEN_CRYPTED_PARI = 0x20 + 2, - DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3, - ENABLE_FIRMWARE_UPDATE = 0x20 + 4, //enables update mode - EXPORT_FIRMWARE_TO_FILE = 0x20 + 5, - GENERATE_NEW_KEYS = 0x20 + 6, - FILL_SD_CARD_WITH_RANDOM_CHARS = 0x20 + 7, - - WRITE_STATUS_DATA = 0x20 + 8, //@unused - ENABLE_READONLY_UNCRYPTED_LUN = 0x20 + 9, - ENABLE_READWRITE_UNCRYPTED_LUN = 0x20 + 10, - - SEND_PASSWORD_MATRIX = 0x20 + 11, //@unused - SEND_PASSWORD_MATRIX_PINDATA = 0x20 + 12, //@unused - SEND_PASSWORD_MATRIX_SETUP = 0x20 + 13, //@unused - - GET_DEVICE_STATUS = 0x20 + 14, - SEND_DEVICE_STATUS = 0x20 + 15, - - SEND_HIDDEN_VOLUME_PASSWORD = 0x20 + 16, //@unused - SEND_HIDDEN_VOLUME_SETUP = 0x20 + 17, - SEND_PASSWORD = 0x20 + 18, - SEND_NEW_PASSWORD = 0x20 + 19, - CLEAR_NEW_SD_CARD_FOUND = 0x20 + 20, - - SEND_STARTUP = 0x20 + 21, - SEND_CLEAR_STICK_KEYS_NOT_INITIATED = 0x20 + 22, - SEND_LOCK_STICK_HARDWARE = 0x20 + 23, //locks firmware upgrade - - PRODUCTION_TEST = 0x20 + 24, - SEND_DEBUG_DATA = 0x20 + 25, //@unused - - CHANGE_UPDATE_PIN = 0x20 + 26, - - //added in v0.48.5 - ENABLE_ADMIN_READONLY_UNCRYPTED_LUN = 0x20 + 28, - ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN = 0x20 + 29, - ENABLE_ADMIN_READONLY_ENCRYPTED_LUN = 0x20 + 30, - ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN = 0x20 + 31, - CHECK_SMARTCARD_USAGE = 0x20 + 32, - //v0.52+ - WINK = 0x20 + 33, - - GET_PW_SAFE_SLOT_STATUS = 0x60, - GET_PW_SAFE_SLOT_NAME = 0x61, - GET_PW_SAFE_SLOT_PASSWORD = 0x62, - GET_PW_SAFE_SLOT_LOGINNAME = 0x63, - SET_PW_SAFE_SLOT_DATA_1 = 0x64, - SET_PW_SAFE_SLOT_DATA_2 = 0x65, - PW_SAFE_ERASE_SLOT = 0x66, - PW_SAFE_ENABLE = 0x67, - PW_SAFE_INIT_KEY = 0x68, //@unused - PW_SAFE_SEND_DATA = 0x69, //@unused - SD_CARD_HIGH_WATERMARK = 0x70, - DETECT_SC_AES = 0x6a, - NEW_AES_KEY = 0x6b -}; - -const char *commandid_to_string(CommandID id); -} -} -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/cxx_semantics.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/cxx_semantics.h deleted file mode 100644 index 36ed142..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/cxx_semantics.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 - */ - -#ifndef CXX_SEMANTICS_H -#define CXX_SEMANTICS_H - -#ifndef _MSC_VER -#define __packed __attribute__((__packed__)) -#else -#define __packed -#endif - -#ifdef _MSC_VER -#define strdup _strdup -#endif - -/* - * There's no need to include Boost for a simple subset this project needs. - */ -namespace semantics { -class non_constructible { - non_constructible() {} -}; -} - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/deprecated.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/deprecated.h deleted file mode 100644 index 5a83288..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/deprecated.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 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 - */ - - -#ifndef LIBNITROKEY_DEPRECATED_H -#define LIBNITROKEY_DEPRECATED_H - -#if defined(__GNUC__) || defined(__clang__) -#define DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define DEPRECATED __declspec(deprecated) -#else -#pragma message("WARNING: DEPRECATED macro is not defined for this compiler") -#define DEPRECATED -#endif - -#endif //LIBNITROKEY_DEPRECATED_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device.h deleted file mode 100644 index f6d2380..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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 - */ - -#ifndef DEVICE_H -#define DEVICE_H -#include -#include "hidapi/hidapi.h" -#include -#include -#include - -#define HID_REPORT_SIZE 65 - -#include - -namespace nitrokey { -namespace device { - using namespace std::chrono_literals; - using std::chrono::milliseconds; - - struct EnumClassHash - { - template - std::size_t operator()(T t) const - { - return static_cast(t); - } - }; - -enum class DeviceModel{ - PRO, - STORAGE -}; - -#include - -class Device { - -public: - - struct ErrorCounters{ - using cnt = std::atomic_int; - cnt wrong_CRC; - cnt CRC_other_than_awaited; - cnt busy; - cnt total_retries; - cnt sending_error; - cnt receiving_error; - cnt total_comm_runs; - cnt successful_storage_commands; - cnt command_successful_recv; - cnt recv_executed; - cnt sends_executed; - cnt busy_progressbar; - cnt command_result_not_equal_0_recv; - cnt communication_successful; - cnt low_level_reconnect; - std::string get_as_string(); - - } m_counters = {}; - - - Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, - const milliseconds send_receive_delay, const int retry_receiving_count, - const milliseconds retry_timeout); - - virtual ~Device(); - - // lack of device is not actually an error, - // so it doesn't throw - virtual bool connect(); - virtual bool disconnect(); - - /* - * Sends packet of HID_REPORT_SIZE. - */ - virtual int send(const void *packet); - - /* - * Gets packet of HID_REPORT_SIZE. - * Can sleep. See below. - */ - virtual int recv(void *packet); - - /*** - * Returns true if some device is visible by OS with given VID and PID - * whether the device is connected through HID API or not. - * @return true if visible by OS - */ - bool could_be_enumerated(); - std::vector enumerate(); - - - void show_stats(); -// ErrorCounters get_stats(){ return m_counters; } - int get_retry_receiving_count() const { return m_retry_receiving_count; }; - int get_retry_sending_count() const { return m_retry_sending_count; }; - std::chrono::milliseconds get_retry_timeout() const { return m_retry_timeout; }; - std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;} - - int get_last_command_status() {int a = std::atomic_exchange(&last_command_status, static_cast(0)); return a;}; - void set_last_command_status(uint8_t _err) { last_command_status = _err;} ; - bool last_command_sucessfull() const {return last_command_status == 0;}; - DeviceModel get_device_model() const {return m_model;} - void set_receiving_delay(std::chrono::milliseconds delay); - void set_retry_delay(std::chrono::milliseconds delay); - static void set_default_device_speed(int delay); - void setDefaultDelay(); - void set_path(const std::string path); - - - private: - std::atomic last_command_status; - void _reconnect(); - bool _connect(); - bool _disconnect(); - -protected: - const uint16_t m_vid; - const uint16_t m_pid; - const DeviceModel m_model; - - /* - * While the project uses Signal11 portable HIDAPI - * library, there's no way of doing it asynchronously, - * hence polling. - */ - const int m_retry_sending_count; - const int m_retry_receiving_count; - std::chrono::milliseconds m_retry_timeout; - std::chrono::milliseconds m_send_receive_delay; - std::atomicmp_devhandle; - std::string m_path; - - static std::atomic_int instances_count; - static std::chrono::milliseconds default_delay ; -}; - -class Stick10 : public Device { - public: - Stick10(); - -}; - -class Stick20 : public Device { - public: - Stick20(); -}; -} -} -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device_proto.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device_proto.h deleted file mode 100644 index 45a6c16..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/device_proto.h +++ /dev/null @@ -1,491 +0,0 @@ -/* - * 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 - */ - -#ifndef DEVICE_PROTO_H -#define DEVICE_PROTO_H - -#include -#include -#include -#include -#include -// a local version for compatibility with Windows -#include -#include "cxx_semantics.h" -#include "device.h" -#include "misc.h" -#include "log.h" -#include "command_id.h" -#include "dissect.h" -#include "CommandFailedException.h" -#include "LongOperationInProgressException.h" - -#define STICK20_UPDATE_MODE_VID 0x03EB -#define STICK20_UPDATE_MODE_PID 0x2FF1 - -#define PAYLOAD_SIZE 53 -#define PWS_SLOT_COUNT 16 -#define PWS_SLOTNAME_LENGTH 11 -#define PWS_PASSWORD_LENGTH 20 -#define PWS_LOGINNAME_LENGTH 32 - -#define PWS_SEND_PASSWORD 0 -#define PWS_SEND_LOGINNAME 1 -#define PWS_SEND_TAB 2 -#define PWS_SEND_CR 3 - -#include -#include "DeviceCommunicationExceptions.h" -#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) - -namespace nitrokey { - namespace proto { - extern std::mutex send_receive_mtx; - - -/* - * POD types for HID proto commands - * Instances are meant to be __packed. - * - * TODO (future) support for Big Endian - */ -#pragma pack (push,1) -/* - * Every packet is a USB HID report (check USB spec) - */ - template - struct HIDReport { - uint8_t _zero; - CommandID command_id; // uint8_t - union { - uint8_t _padding[HID_REPORT_SIZE - 6]; - Payload payload; - } __packed; - uint32_t crc; - - // POD types can't have non-default constructors - // used in Transaction<>::run() - void initialize() { - bzero(this, sizeof *this); - command_id = cmd_id; - } - - uint32_t calculate_CRC() const { - // w/o leading zero, a part of each HID packet - // w/o 4-byte crc - return misc::stm_crc32((const uint8_t *) (this) + 1, - (size_t) (HID_REPORT_SIZE - 5)); - } - - void update_CRC() { crc = calculate_CRC(); } - - bool isCRCcorrect() const { return crc == calculate_CRC(); } - - bool isValid() const { - return true; - // return !_zero && payload.isValid() && isCRCcorrect(); - } - - operator std::string() const { - // Packet type is known upfront in normal operation. - // Can't be used to dissect random packets. - return QueryDissector::dissect(*this); - } - } __packed; - -/* - * Response payload (the parametrized type inside struct HIDReport) - * - * command_id member in incoming HIDReport structure carries the command - * type last used. - */ - namespace DeviceResponseConstants{ - //magic numbers from firmware - static constexpr auto storage_status_absolute_address = 21; - static constexpr auto storage_data_absolute_address = storage_status_absolute_address + 5; - static constexpr auto header_size = 8; //from _zero to last_command_status inclusive - static constexpr auto footer_size = 4; //crc - static constexpr auto wrapping_size = header_size + footer_size; - } - - template - struct DeviceResponse { - static constexpr auto storage_status_padding_size = - DeviceResponseConstants::storage_status_absolute_address - DeviceResponseConstants::header_size; - - uint8_t _zero; - uint8_t device_status; - uint8_t command_id; // originally last_command_type - uint32_t last_command_crc; - uint8_t last_command_status; - - union { - uint8_t _padding[HID_REPORT_SIZE - DeviceResponseConstants::wrapping_size]; - ResponsePayload payload; - struct { - uint8_t _storage_status_padding[storage_status_padding_size]; - uint8_t command_counter; - uint8_t command_id; - uint8_t device_status; //@see stick20::device_status - uint8_t progress_bar_value; - } __packed storage_status; - } __packed; - - uint32_t crc; - - void initialize() { bzero(this, sizeof *this); } - - uint32_t calculate_CRC() const { - // w/o leading zero, a part of each HID packet - // w/o 4-byte crc - return misc::stm_crc32((const uint8_t *) (this) + 1, - (size_t) (HID_REPORT_SIZE - 5)); - } - - void update_CRC() { crc = calculate_CRC(); } - bool isCRCcorrect() const { return crc == calculate_CRC(); } - bool isValid() const { - // return !_zero && payload.isValid() && isCRCcorrect() && - // command_id == (uint8_t)(cmd_id); - return crc != 0; - } - - operator std::string() const { - return ResponseDissector::dissect(*this); - } - } __packed; - - struct EmptyPayload { - bool isValid() const { return true; } - - std::string dissect() const { return std::string("Empty Payload."); } - } __packed; - - template - class ClearingProxy { - public: - ClearingProxy(command_packet &p) { - packet = p; - bzero(&p, sizeof(p)); - } - - ~ClearingProxy() { - bzero(&packet, sizeof(packet)); - } - - response_payload &data() { - return packet.payload; - } - - command_packet packet; - }; - - template - class Transaction : semantics::non_constructible { - public: - // Types declared in command class scope can't be reached from there. - typedef command_payload CommandPayload; - typedef response_payload ResponsePayload; - - - typedef struct HIDReport OutgoingPacket; - typedef struct DeviceResponse ResponsePacket; -#pragma pack (pop) - - static_assert(std::is_pod::value, - "outgoingpacket must be a pod type"); - static_assert(std::is_pod::value, - "ResponsePacket must be a POD type"); - static_assert(sizeof(OutgoingPacket) == HID_REPORT_SIZE, - "OutgoingPacket type is not the right size"); - static_assert(sizeof(ResponsePacket) == HID_REPORT_SIZE, - "ResponsePacket type is not the right size"); - - static uint32_t getCRC( - const command_payload &payload) { - OutgoingPacket outp; - outp.initialize(); - outp.payload = payload; - outp.update_CRC(); - return outp.crc; - } - - template - static void clear_packet(T &st) { - bzero(&st, sizeof(st)); - } - - static ClearingProxy run(std::shared_ptr dev, - const command_payload &payload) { - using namespace ::nitrokey::device; - using namespace ::nitrokey::log; - using namespace std::chrono_literals; - - std::lock_guard guard(send_receive_mtx); - - LOG(__FUNCTION__, Loglevel::DEBUG_L2); - - if (dev == nullptr){ - LOG(std::string("Throw: Device not initialized"), Loglevel::DEBUG_L1); - throw DeviceNotConnected("Device not initialized"); - } - dev->m_counters.total_comm_runs++; - - int status; - OutgoingPacket outp; - ResponsePacket resp; - - // POD types can't have non-default constructors - outp.initialize(); - resp.initialize(); - - outp.payload = payload; - outp.update_CRC(); - - LOG("-------------------", Loglevel::DEBUG); - LOG("Outgoing HID packet:", Loglevel::DEBUG); - LOG(static_cast(outp), Loglevel::DEBUG); - LOG(std::string("=> ") + std::string(commandid_to_string(static_cast(outp.command_id))), Loglevel::DEBUG_L1); - - - if (!outp.isValid()) { - LOG(std::string("Throw: Invalid outgoing packet"), Loglevel::DEBUG_L1); - throw DeviceSendingFailure("Invalid outgoing packet"); - } - - bool successful_communication = false; - int receiving_retry_counter = 0; - int sending_retry_counter = dev->get_retry_sending_count(); - while (sending_retry_counter-- > 0) { - dev->m_counters.sends_executed++; - status = dev->send(&outp); - if (status <= 0){ - //FIXME early disconnection not yet working properly -// LOG("Encountered communication error, disconnecting device", Loglevel::DEBUG_L2); -// dev->disconnect(); - dev->m_counters.sending_error++; - LOG(std::string("Throw: Device error while sending command "), Loglevel::DEBUG_L1); - throw DeviceSendingFailure( - std::string("Device error while sending command ") + - std::to_string(status)); - } - - std::this_thread::sleep_for(dev->get_send_receive_delay()); - - // FIXME make checks done in device:recv here - receiving_retry_counter = dev->get_retry_receiving_count(); - int busy_counter = 0; - auto retry_timeout = dev->get_retry_timeout(); - while (receiving_retry_counter-- > 0) { - dev->m_counters.recv_executed++; - status = dev->recv(&resp); - - if (dev->get_device_model() == DeviceModel::STORAGE && - resp.command_id >= stick20::CMD_START_VALUE && - resp.command_id < stick20::CMD_END_VALUE ) { - LOG(std::string("Detected storage device cmd, status: ") + - std::to_string(resp.storage_status.device_status), Loglevel::DEBUG_L2); - - resp.last_command_status = static_cast(stick10::command_status::ok); - switch (static_cast(resp.storage_status.device_status)) { - case stick20::device_status::idle : - case stick20::device_status::ok: - resp.device_status = static_cast(stick10::device_status::ok); - break; - case stick20::device_status::busy: - case stick20::device_status::busy_progressbar: //TODO this will be modified later for getting progressbar status - resp.device_status = static_cast(stick10::device_status::busy); - break; - case stick20::device_status::wrong_password: - resp.last_command_status = static_cast(stick10::command_status::wrong_password); - resp.device_status = static_cast(stick10::device_status::ok); - break; - case stick20::device_status::no_user_password_unlock: - resp.last_command_status = static_cast(stick10::command_status::AES_dec_failed); - resp.device_status = static_cast(stick10::device_status::ok); - break; - default: - LOG(std::string("Unknown storage device status, cannot translate: ") + - std::to_string(resp.storage_status.device_status), Loglevel::DEBUG); - resp.device_status = resp.storage_status.device_status; - break; - }; - } - - //Some of the commands return wrong CRC, for now skip checking it (TODO list and report) - //if (resp.device_status == 0 && resp.last_command_crc == outp.crc && resp.isCRCcorrect()) break; - auto CRC_equal_awaited = true; // resp.last_command_crc == outp.crc; - if (resp.device_status == static_cast(stick10::device_status::ok) && - CRC_equal_awaited && resp.isValid()){ - successful_communication = true; - break; - } - if (resp.device_status == static_cast(stick10::device_status::busy)) { - dev->m_counters.busy++; - - if (busy_counter++<10) { - receiving_retry_counter++; - LOG("Status busy, not decreasing receiving_retry_counter counter: " + - std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2); - } else { - retry_timeout *= 2; - retry_timeout = std::min(retry_timeout, 300ms); - busy_counter = 0; - LOG("Status busy, decreasing receiving_retry_counter counter: " + - std::to_string(receiving_retry_counter) + ", current delay:" - + std::to_string(retry_timeout.count()), Loglevel::DEBUG); - LOG(std::string("Busy retry: status ") - + std::to_string(resp.storage_status.device_status) - + ", " - + std::to_string(retry_timeout.count()) - + "ms, counter " - + std::to_string(receiving_retry_counter) - + ", progress: " - + std::to_string(resp.storage_status.progress_bar_value) - , Loglevel::DEBUG_L1); - } - } - if (resp.device_status == static_cast(stick10::device_status::busy) && - static_cast(resp.storage_status.device_status) - == stick20::device_status::busy_progressbar){ - successful_communication = true; - break; - } - LOG(std::string("Retry status - dev status, awaited cmd crc, correct packet CRC: ") - + std::to_string(resp.device_status) + - " " + std::to_string(CRC_equal_awaited) + - " " + std::to_string(resp.isCRCcorrect()), Loglevel::DEBUG_L2); - - if (!resp.isCRCcorrect()) dev->m_counters.wrong_CRC++; - if (!CRC_equal_awaited) dev->m_counters.CRC_other_than_awaited++; - - - LOG( - "Device is not ready or received packet's last CRC is not equal to sent CRC packet, retrying...", - Loglevel::DEBUG_L2); - LOG("Invalid incoming HID packet:", Loglevel::DEBUG_L2); - LOG(static_cast(resp), Loglevel::DEBUG_L2); - dev->m_counters.total_retries++; - LOG(".", Loglevel::DEBUG_L1); - std::this_thread::sleep_for(retry_timeout); - continue; - } - if (successful_communication) break; - LOG(std::string("Resending (outer loop) "), Loglevel::DEBUG_L2); - LOG(std::string("sending_retry_counter count: ") + std::to_string(sending_retry_counter), - Loglevel::DEBUG); - } - - if(resp.last_command_crc != outp.crc){ - LOG(std::string("Accepting response with CRC other than expected ") - + "Command ID: " + std::to_string(resp.command_id) + " " + - commandid_to_string(static_cast(resp.command_id)) + " " - + "Reported by response and expected: " + std::to_string(resp.last_command_crc) + "!=" + std::to_string(outp.crc), - Loglevel::WARNING - ); - } - - dev->set_last_command_status(resp.last_command_status); // FIXME should be handled on device.recv - - clear_packet(outp); - - - if (status <= 0) { - dev->m_counters.receiving_error++; - LOG(std::string("Throw: Device error while executing command "), Loglevel::DEBUG_L1); - throw DeviceReceivingFailure( //FIXME replace with CriticalErrorException - std::string("Device error while executing command ") + - std::to_string(status)); - } - - LOG(std::string("<= ") + - std::string( - commandid_to_string(static_cast(resp.command_id)) - + std::string(" ") - + std::to_string(resp.device_status) - + std::string(" ") - + std::to_string(resp.storage_status.device_status) -// + std::to_string( status_translate_command(resp.storage_status.device_status)) - ), Loglevel::DEBUG_L1); - - LOG("Incoming HID packet:", Loglevel::DEBUG); - LOG(static_cast(resp), Loglevel::DEBUG); - if (dev->get_retry_receiving_count() - receiving_retry_counter > 2) { - LOG(std::string("Packet received with receiving_retry_counter count: ") + - std::to_string(receiving_retry_counter), - Loglevel::DEBUG_L1); - } - - if (resp.device_status == static_cast(stick10::device_status::busy) && - static_cast(resp.storage_status.device_status) - == stick20::device_status::busy_progressbar){ - dev->m_counters.busy_progressbar++; - LOG(std::string("Throw: Long operation in progress exception"), Loglevel::DEBUG_L1); - throw LongOperationInProgressException( - resp.command_id, resp.device_status, resp.storage_status.progress_bar_value); - } - - if (!resp.isValid()) { - LOG(std::string("Throw: Invalid incoming packet"), Loglevel::DEBUG_L1); - throw InvalidCRCReceived("Invalid incoming packet"); - } - if (receiving_retry_counter <= 0){ - LOG(std::string("Throw: \"Maximum receiving_retry_counter count reached for receiving response from the device!\"" - + std::to_string(receiving_retry_counter)), Loglevel::DEBUG_L1); - throw DeviceReceivingFailure( - "Maximum receiving_retry_counter count reached for receiving response from the device!"); - } - dev->m_counters.communication_successful++; - - if (resp.last_command_status != static_cast(stick10::command_status::ok)){ - dev->m_counters.command_result_not_equal_0_recv++; - LOG(std::string("Throw: CommandFailedException ") + std::to_string(resp.last_command_status), Loglevel::DEBUG_L1); - throw CommandFailedException(resp.command_id, resp.last_command_status); - } - - dev->m_counters.command_successful_recv++; - - if (dev->get_device_model() == DeviceModel::STORAGE && - resp.command_id >= stick20::CMD_START_VALUE && - resp.command_id < stick20::CMD_END_VALUE ) { - dev->m_counters.successful_storage_commands++; - } - - if (!resp.isCRCcorrect()) - LOG(std::string("Accepting response from device with invalid CRC. ") - + "Command ID: " + std::to_string(resp.command_id) + " " + - commandid_to_string(static_cast(resp.command_id)) + " " - + "Reported and calculated: " + std::to_string(resp.crc) + "!=" + std::to_string(resp.calculate_CRC()), - Loglevel::WARNING - ); - - // See: DeviceResponse - return resp; - } - - static ClearingProxy run(std::shared_ptr dev) { - command_payload empty_payload; - return run(dev, empty_payload); - } - }; - } -} -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/dissect.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/dissect.h deleted file mode 100644 index 690b5b7..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/dissect.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 - */ - -/* - * Protocol packet dissection - */ -#ifndef DISSECT_H -#define DISSECT_H -#include -#include -#include -#include "misc.h" -#include "cxx_semantics.h" -#include "command_id.h" -#include "device_proto.h" - -namespace nitrokey { -namespace proto { - -template -class QueryDissector : semantics::non_constructible { - public: - static std::string dissect(const HIDPacket &pod) { - std::stringstream out; - -#ifdef LOG_VOLATILE_DATA - out << "Raw HID packet:" << std::endl; - out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod); -#endif - - out << "Contents:" << std::endl; - out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) - << std::endl; - out << "CRC:\t" - << std::hex << std::setw(2) << std::setfill('0') - << pod.crc << std::endl; - - out << "Payload:" << std::endl; - out << pod.payload.dissect(); - return out.str(); - } -}; - - - - -template -class ResponseDissector : semantics::non_constructible { - public: - static std::string status_translate_device(int status){ - auto enum_status = static_cast(status); - switch (enum_status){ - case stick10::device_status::ok: return "OK"; - case stick10::device_status::busy: return "BUSY"; - case stick10::device_status::error: return "ERROR"; - case stick10::device_status::received_report: return "RECEIVED_REPORT"; - } - return std::string("UNKNOWN: ") + std::to_string(status); - } - - static std::string to_upper(std::string str){ - for (auto & c: str) c = toupper(c); - return str; - } - static std::string status_translate_command(int status){ - auto enum_status = static_cast(status); - switch (enum_status) { -#define p(X) case X: return to_upper(std::string(#X)); - p(stick10::command_status::ok) - p(stick10::command_status::wrong_CRC) - p(stick10::command_status::wrong_slot) - p(stick10::command_status::slot_not_programmed) - p(stick10::command_status::wrong_password) - p(stick10::command_status::not_authorized) - p(stick10::command_status::timestamp_warning) - p(stick10::command_status::no_name_error) - p(stick10::command_status::not_supported) - p(stick10::command_status::unknown_command) - p(stick10::command_status::AES_dec_failed) -#undef p - } - return std::string("UNKNOWN: ") + std::to_string(status); - } - - static std::string dissect(const HIDPacket &pod) { - std::stringstream out; - - // FIXME use values from firmware (possibly generate separate - // header automatically) - -#ifdef LOG_VOLATILE_DATA - out << "Raw HID packet:" << std::endl; - out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod); -#endif - - out << "Device status:\t" << pod.device_status + 0 << " " - << status_translate_device(pod.device_status) << std::endl; - out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) << " hex: " << std::hex << (int)pod.command_id - << std::endl; - out << "Last command CRC:\t" - << std::hex << std::setw(2) << std::setfill('0') - << pod.last_command_crc << std::endl; - out << "Last command status:\t" << pod.last_command_status + 0 << " " - << status_translate_command(pod.last_command_status) << std::endl; - out << "CRC:\t" - << std::hex << std::setw(2) << std::setfill('0') - << pod.crc << std::endl; - if((int)pod.command_id == pod.storage_status.command_id){ - out << "Storage stick status (where applicable):" << std::endl; -#define d(x) out << " "#x": \t"<< std::hex << std::setw(2) \ - << std::setfill('0')<< static_cast(x) << std::endl; - d(pod.storage_status.command_counter); - d(pod.storage_status.command_id); - d(pod.storage_status.device_status); - d(pod.storage_status.progress_bar_value); -#undef d - } - - out << "Payload:" << std::endl; - out << pod.payload.dissect(); - return out.str(); - } -}; -} -} - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/hidapi/hidapi.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/hidapi/hidapi.h deleted file mode 100644 index e5bc2dc..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/hidapi/hidapi.h +++ /dev/null @@ -1,391 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - Alan Ott - Signal 11 Software - - 8/22/2009 - - Copyright 2009, All Rights Reserved. - - At the discretion of the user of this library, - this software may be licensed under the terms of the - GNU General Public License v3, a BSD-Style license, or the - original HIDAPI license as outlined in the LICENSE.txt, - LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt - files located at the root of the source distribution. - These files may also be found in the public source - code repository located at: - http://github.com/signal11/hidapi . -********************************************************/ - -/** @file - * @defgroup API hidapi API - */ - -#ifndef HIDAPI_H__ -#define HIDAPI_H__ - -#include - -#ifdef _WIN32 - #define HID_API_EXPORT __declspec(dllexport) - #define HID_API_CALL -#else - #define HID_API_EXPORT /**< API export macro */ - #define HID_API_CALL /**< API call macro */ -#endif - -#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ - -#ifdef __cplusplus -extern "C" { -#endif - struct hid_device_; - typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ - - /** hidapi info structure */ - struct hid_device_info { - /** Platform-specific device path */ - char *path; - /** Device Vendor ID */ - unsigned short vendor_id; - /** Device Product ID */ - unsigned short product_id; - /** Serial Number */ - wchar_t *serial_number; - /** Device Release Number in binary-coded decimal, - also known as Device Version Number */ - unsigned short release_number; - /** Manufacturer String */ - wchar_t *manufacturer_string; - /** Product string */ - wchar_t *product_string; - /** Usage Page for this Device/Interface - (Windows/Mac only). */ - unsigned short usage_page; - /** Usage for this Device/Interface - (Windows/Mac only).*/ - unsigned short usage; - /** The USB interface which this logical device - represents. Valid on both Linux implementations - in all cases, and valid on the Windows implementation - only if the device contains more than one interface. */ - int interface_number; - - /** Pointer to the next device */ - struct hid_device_info *next; - }; - - - /** @brief Initialize the HIDAPI library. - - This function initializes the HIDAPI library. Calling it is not - strictly necessary, as it will be called automatically by - hid_enumerate() and any of the hid_open_*() functions if it is - needed. This function should be called at the beginning of - execution however, if there is a chance of HIDAPI handles - being opened by different threads simultaneously. - - @ingroup API - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_init(void); - - /** @brief Finalize the HIDAPI library. - - This function frees all of the static data associated with - HIDAPI. It should be called at the end of execution to avoid - memory leaks. - - @ingroup API - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_exit(void); - - /** @brief Enumerate the HID Devices. - - This function returns a linked list of all the HID devices - attached to the system which match vendor_id and product_id. - If @p vendor_id is set to 0 then any vendor matches. - If @p product_id is set to 0 then any product matches. - If @p vendor_id and @p product_id are both set to 0, then - all HID devices will be returned. - - @ingroup API - @param vendor_id The Vendor ID (VID) of the types of device - to open. - @param product_id The Product ID (PID) of the types of - device to open. - - @returns - This function returns a pointer to a linked list of type - struct #hid_device, containing information about the HID devices - attached to the system, or NULL in the case of failure. Free - this linked list by calling hid_free_enumeration(). - */ - struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); - - /** @brief Free an enumeration Linked List - - This function frees a linked list created by hid_enumerate(). - - @ingroup API - @param devs Pointer to a list of struct_device returned from - hid_enumerate(). - */ - void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); - - /** @brief Open a HID device using a Vendor ID (VID), Product ID - (PID) and optionally a serial number. - - If @p serial_number is NULL, the first device with the - specified VID and PID is opened. - - @ingroup API - @param vendor_id The Vendor ID (VID) of the device to open. - @param product_id The Product ID (PID) of the device to open. - @param serial_number The Serial Number of the device to open - (Optionally NULL). - - @returns - This function returns a pointer to a #hid_device object on - success or NULL on failure. - */ - HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); - - /** @brief Open a HID device by its path name. - - The path name be determined by calling hid_enumerate(), or a - platform-specific path name can be used (eg: /dev/hidraw0 on - Linux). - - @ingroup API - @param path The path name of the device to open - - @returns - This function returns a pointer to a #hid_device object on - success or NULL on failure. - */ - HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); - - /** @brief Write an Output report to a HID device. - - The first byte of @p data[] must contain the Report ID. For - devices which only support a single report, this must be set - to 0x0. The remaining bytes contain the report data. Since - the Report ID is mandatory, calls to hid_write() will always - contain one more byte than the report contains. For example, - if a hid report is 16 bytes long, 17 bytes must be passed to - hid_write(), the Report ID (or 0x0, for devices with a - single report), followed by the report data (16 bytes). In - this example, the length passed in would be 17. - - hid_write() will send the data on the first OUT endpoint, if - one exists. If it does not, it will send the data through - the Control Endpoint (Endpoint 0). - - @ingroup API - @param device A device handle returned from hid_open(). - @param data The data to send, including the report number as - the first byte. - @param length The length in bytes of the data to send. - - @returns - This function returns the actual number of bytes written and - -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); - - /** @brief Read an Input report from a HID device with timeout. - - Input reports are returned - to the host through the INTERRUPT IN endpoint. The first byte will - contain the Report number if the device uses numbered reports. - - @ingroup API - @param device A device handle returned from hid_open(). - @param data A buffer to put the read data into. - @param length The number of bytes to read. For devices with - multiple reports, make sure to read an extra byte for - the report number. - @param milliseconds timeout in milliseconds or -1 for blocking wait. - - @returns - This function returns the actual number of bytes read and - -1 on error. If no packet was available to be read within - the timeout period, this function returns 0. - */ - int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); - - /** @brief Read an Input report from a HID device. - - Input reports are returned - to the host through the INTERRUPT IN endpoint. The first byte will - contain the Report number if the device uses numbered reports. - - @ingroup API - @param device A device handle returned from hid_open(). - @param data A buffer to put the read data into. - @param length The number of bytes to read. For devices with - multiple reports, make sure to read an extra byte for - the report number. - - @returns - This function returns the actual number of bytes read and - -1 on error. If no packet was available to be read and - the handle is in non-blocking mode, this function returns 0. - */ - int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); - - /** @brief Set the device handle to be non-blocking. - - In non-blocking mode calls to hid_read() will return - immediately with a value of 0 if there is no data to be - read. In blocking mode, hid_read() will wait (block) until - there is data to read before returning. - - Nonblocking can be turned on and off at any time. - - @ingroup API - @param device A device handle returned from hid_open(). - @param nonblock enable or not the nonblocking reads - - 1 to enable nonblocking - - 0 to disable nonblocking. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); - - /** @brief Send a Feature report to the device. - - Feature reports are sent over the Control endpoint as a - Set_Report transfer. The first byte of @p data[] must - contain the Report ID. For devices which only support a - single report, this must be set to 0x0. The remaining bytes - contain the report data. Since the Report ID is mandatory, - calls to hid_send_feature_report() will always contain one - more byte than the report contains. For example, if a hid - report is 16 bytes long, 17 bytes must be passed to - hid_send_feature_report(): the Report ID (or 0x0, for - devices which do not use numbered reports), followed by the - report data (16 bytes). In this example, the length passed - in would be 17. - - @ingroup API - @param device A device handle returned from hid_open(). - @param data The data to send, including the report number as - the first byte. - @param length The length in bytes of the data to send, including - the report number. - - @returns - This function returns the actual number of bytes written and - -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); - - /** @brief Get a feature report from a HID device. - - Set the first byte of @p data[] to the Report ID of the - report to be read. Make sure to allow space for this - extra byte in @p data[]. Upon return, the first byte will - still contain the Report ID, and the report data will - start in data[1]. - - @ingroup API - @param device A device handle returned from hid_open(). - @param data A buffer to put the read data into, including - the Report ID. Set the first byte of @p data[] to the - Report ID of the report to be read, or set it to zero - if your device does not use numbered reports. - @param length The number of bytes to read, including an - extra byte for the report ID. The buffer can be longer - than the actual report. - - @returns - This function returns the number of bytes read plus - one for the report ID (which is still in the first - byte), or -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); - - /** @brief Close a HID device. - - @ingroup API - @param device A device handle returned from hid_open(). - */ - void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); - - /** @brief Get The Manufacturer String from a HID device. - - @ingroup API - @param device A device handle returned from hid_open(). - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); - - /** @brief Get The Product String from a HID device. - - @ingroup API - @param device A device handle returned from hid_open(). - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); - - /** @brief Get The Serial Number String from a HID device. - - @ingroup API - @param device A device handle returned from hid_open(). - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); - - /** @brief Get a string from a HID device, based on its string index. - - @ingroup API - @param device A device handle returned from hid_open(). - @param string_index The index of the string to get. - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); - - /** @brief Get a string describing the last error which occurred. - - @ingroup API - @param device A device handle returned from hid_open(). - - @returns - This function returns a string containing the last error - which occurred or NULL if none has occurred. - */ - HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/log.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/log.h deleted file mode 100644 index 2a64bef..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/log.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 - */ - -#ifndef LOG_H -#define LOG_H - -#include -#include - -namespace nitrokey { - namespace log { - -//for MSVC -#ifdef ERROR -#undef ERROR -#endif - - - enum class Loglevel : int { - ERROR, - WARNING, - INFO, - DEBUG_L1, - DEBUG, - DEBUG_L2 - }; - - class LogHandler { - public: - virtual void print(const std::string &, Loglevel lvl) = 0; - protected: - std::string loglevel_to_str(Loglevel); - std::string format_message_to_string(const std::string &str, const Loglevel &lvl); - - }; - - class StdlogHandler : public LogHandler { - public: - virtual void print(const std::string &, Loglevel lvl); - }; - - class FunctionalLogHandler : public LogHandler { - using log_function_type = std::function; - log_function_type log_function; - public: - FunctionalLogHandler(log_function_type _log_function); - virtual void print(const std::string &, Loglevel lvl); - - }; - - extern StdlogHandler stdlog_handler; - - class Log { - public: - Log() : mp_loghandler(&stdlog_handler), m_loglevel(Loglevel::WARNING) {} - - static Log &instance() { - if (mp_instance == nullptr) mp_instance = new Log; - return *mp_instance; - } - - void operator()(const std::string &, Loglevel); - void set_loglevel(Loglevel lvl) { m_loglevel = lvl; } - void set_handler(LogHandler *handler) { mp_loghandler = handler; } - - private: - LogHandler *mp_loghandler; - Loglevel m_loglevel; - static std::string prefix; - public: - static void setPrefix(std::string prefix = std::string()); - - private: - - static Log *mp_instance; - }; - } -} - - -#ifdef NO_LOG -#define LOG(string, level) while(false){} -#define LOGD(string) while(false){} -#else -#define LOG(string, level) nitrokey::log::Log::instance()((string), (level)) -#define LOGD1(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L1)) -#define LOGD(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L2)) -#endif - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/misc.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/misc.h deleted file mode 100644 index 88254dd..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/misc.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 - */ - - -#ifndef MISC_H -#define MISC_H -#include -#include -#include -#include -#include "log.h" -#include "LibraryException.h" -#include -#include - - -namespace nitrokey { -namespace misc { - - template - std::string toHex(T value){ - using namespace std; - std::ostringstream oss; - oss << std::hex << std::setw(sizeof(value)*2) << std::setfill('0') << value; - return oss.str(); - } - - /** - * Copies string from pointer to fixed size C-style array. Src needs to be a valid C-string - eg. ended with '\0'. - * Throws when source is bigger than destination. - * @tparam T type of destination array - * @param dest fixed size destination array - * @param src pointer to source c-style valid string - */ - template - void strcpyT(T& dest, const char* src){ - - if (src == nullptr) -// throw EmptySourceStringException(slot_number); - return; - const size_t s_dest = sizeof dest; - LOG(std::string("strcpyT sizes dest src ") - +std::to_string(s_dest)+ " " - +std::to_string(strlen(src))+ " " - ,nitrokey::log::Loglevel::DEBUG_L2); - if (strlen(src) > s_dest){ - throw TooLongStringException(strlen(src), s_dest, src); - } - strncpy((char*) &dest, src, s_dest); - } - -#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) - template -typename T::CommandPayload get_payload(){ - //Create, initialize and return by value command payload - typename T::CommandPayload st; - bzero(&st, sizeof(st)); - return st; -} - - template - void execute_password_command(Tdev &stick, const char *password) { - auto p = get_payload(); - p.set_defaults(); - strcpyT(p.password, password); - CMDTYPE::CommandTransaction::run(stick, p); - } - - std::string hexdump(const uint8_t *p, size_t size, bool print_header=true, bool print_ascii=true, - bool print_empty=true); - uint32_t stm_crc32(const uint8_t *data, size_t size); - std::vector hex_string_to_byte(const char* hexString); -} -} - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands.h deleted file mode 100644 index f2ffba2..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands.h +++ /dev/null @@ -1,889 +0,0 @@ -/* - * 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 - */ - -#ifndef STICK10_COMMANDS_H -#define STICK10_COMMANDS_H - -#include -#include -#include -#include -#include -#include "device_proto.h" -#include "command.h" - -#pragma pack (push,1) - -namespace nitrokey { -namespace proto { - - - -/* - * Stick10 protocol definition - */ -namespace stick10 { -class GetSlotName : public Command { - public: - // reachable as a typedef in Transaction - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return slot_number<0x10+3; } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_name[15]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(slot_name); - return ss.str(); - } - } __packed; - - typedef Transaction CommandTransaction; -}; - -class EraseSlot : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class SetTime : Command { - public: - struct CommandPayload { - uint8_t reset; // 0 - get time, 1 - set time - uint64_t time; // posix time - - bool isValid() const { return reset && reset != 1; } - std::string dissect() const { - std::stringstream ss; - ss << "reset:\t" << (int)(reset) << std::endl; - ss << "time:\t" << (time) << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - - -class WriteToHOTPSlot : Command { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[15]; - uint8_t slot_secret[20]; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - union{ - uint8_t slot_token_id[13]; /** OATH Token Identifier */ - struct{ /** @see https://openauthentication.org/token-specs/ */ - uint8_t omp[2]; - uint8_t tt[2]; - uint8_t mui[8]; - uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 - } slot_token_fields; - }; - union{ - uint64_t slot_counter; - uint8_t slot_counter_s[8]; - } __packed; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - print_to_ss_volatile(slot_name); - print_to_ss_volatile(slot_secret); - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - - ss << "slot_token_id:\t"; - for (auto i : slot_token_id) - ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; - ss << std::endl; - ss << "slot_counter:\t[" << (int)slot_counter << "]\t" - << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false); - - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class WriteToTOTPSlot : Command { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[15]; - uint8_t slot_secret[20]; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - union{ - uint8_t slot_token_id[13]; /** OATH Token Identifier */ - struct{ /** @see https://openauthentication.org/token-specs/ */ - uint8_t omp[2]; - uint8_t tt[2]; - uint8_t mui[8]; - uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 - } slot_token_fields; - }; - uint16_t slot_interval; - - bool isValid() const { return !(slot_number & 0xF0); } //TODO check - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - print_to_ss_volatile(slot_name); - print_to_ss_volatile(slot_secret); - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "slot_token_id:\t"; - for (auto i : slot_token_id) - ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; - ss << std::endl; - ss << "slot_interval:\t" << (int)slot_interval << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class GetTOTP : Command { - public: - struct CommandPayload { - uint8_t slot_number; - uint64_t challenge; - uint64_t last_totp_time; - uint8_t last_interval; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - ss << "challenge:\t" << (challenge) << std::endl; - ss << "last_totp_time:\t" << (last_totp_time) << std::endl; - ss << "last_interval:\t" << (int)(last_interval) << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - union { - uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 - struct { - uint32_t code; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - } __packed ; - } __packed ; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << "code:\t" << (code) << std::endl; - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class GetHOTP : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return (slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - union { - uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 - struct { - uint32_t code; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - } __packed; - } __packed; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << "code:\t" << (code) << std::endl; - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class ReadSlot : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return !(slot_number & 0xF0); } - - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_name[15]; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - union{ - uint8_t slot_token_id[13]; /** OATH Token Identifier */ - struct{ /** @see https://openauthentication.org/token-specs/ */ - uint8_t omp[2]; - uint8_t tt[2]; - uint8_t mui[8]; - uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 - } slot_token_fields; - }; - union{ - uint64_t slot_counter; - uint8_t slot_counter_s[8]; - } __packed; - - bool isValid() const { return true; } - - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(slot_name); - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - - ss << "slot_token_id:\t"; - for (auto i : slot_token_id) - ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; - ss << std::endl; - ss << "slot_counter:\t[" << (int)slot_counter << "]\t" - << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false); - return ss.str(); - } - } __packed; - - typedef Transaction CommandTransaction; -}; - -class GetStatus : Command { - public: - struct ResponsePayload { - union { - uint16_t firmware_version; - struct { - uint8_t minor; - uint8_t major; - } firmware_version_st; - }; - union{ - uint8_t card_serial[4]; - uint32_t card_serial_u32; - } __packed; - union { - uint8_t general_config[5]; - struct{ - uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ - uint8_t capslock; /** same as numlock */ - uint8_t scrolllock; /** same as numlock */ - uint8_t enable_user_password; - uint8_t delete_user_password; /* unused */ - } __packed; - } __packed; - - static constexpr uint8_t special_HOTP_slots = 2; - bool isValid() const { return numlock < special_HOTP_slots && capslock < special_HOTP_slots - && scrolllock < special_HOTP_slots && enable_user_password < 2; } - - std::string get_card_serial_hex() const { - return nitrokey::misc::toHex(card_serial_u32); - } - - std::string dissect() const { - std::stringstream ss; - ss << "firmware_version:\t" - << "[" << firmware_version << "]" << "\t" - << ::nitrokey::misc::hexdump( - (const uint8_t *)(&firmware_version), sizeof firmware_version, false); - ss << "card_serial_u32:\t" << std::hex << card_serial_u32 << std::endl; - ss << "card_serial:\t" - << ::nitrokey::misc::hexdump((const uint8_t *)(card_serial), - sizeof card_serial, false); - ss << "general_config:\t" - << ::nitrokey::misc::hexdump((const uint8_t *)(general_config), - sizeof general_config, false); - ss << "numlock:\t" << (int)numlock << std::endl; - ss << "capslock:\t" << (int)capslock << std::endl; - ss << "scrolllock:\t" << (int)scrolllock << std::endl; - ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; - ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; - - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class GetPasswordRetryCount : Command { - public: - struct ResponsePayload { - uint8_t password_retry_count; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << " password_retry_count\t" << (int)password_retry_count << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class GetUserPasswordRetryCount - : Command { - public: - struct ResponsePayload { - uint8_t password_retry_count; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << " password_retry_count\t" << (int)password_retry_count << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - - template - void write_array(T &ss, Q (&arr)[N]){ - for (int i=0; i { - public: - struct ResponsePayload { - uint8_t password_safe_status[PWS_SLOT_COUNT]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << "password_safe_status\t"; - write_array(ss, password_safe_status); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class GetPasswordSafeSlotName : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number\t" << (int)slot_number << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_name[PWS_SLOTNAME_LENGTH]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(slot_name); - return ss.str(); - } - } __packed; - - typedef Transaction CommandTransaction; -}; - -class GetPasswordSafeSlotPassword - : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << " slot_number\t" << (int)slot_number << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_password[PWS_PASSWORD_LENGTH]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(slot_password); - return ss.str(); - } - } __packed; - - typedef Transaction CommandTransaction; -}; - -class GetPasswordSafeSlotLogin - : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << " slot_number\t" << (int)slot_number << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_login[PWS_LOGINNAME_LENGTH]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(slot_login); - return ss.str(); - } - } __packed; - - typedef Transaction CommandTransaction; -}; - -class SetPasswordSafeSlotData : Command { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[PWS_SLOTNAME_LENGTH]; - uint8_t slot_password[PWS_PASSWORD_LENGTH]; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << " slot_number\t" << (int)slot_number << std::endl; - print_to_ss_volatile(slot_name); - print_to_ss_volatile(slot_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class SetPasswordSafeSlotData2 : Command { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_login_name[PWS_LOGINNAME_LENGTH]; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << " slot_number\t" << (int)slot_number << std::endl; - print_to_ss_volatile(slot_login_name); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class ErasePasswordSafeSlot : Command { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << " slot_number\t" << (int)slot_number << std::endl; - return ss.str(); - } - - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class EnablePasswordSafe : Command { - public: - struct CommandPayload { - uint8_t user_password[30]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(user_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class PasswordSafeInitKey : Command { - /** - * never used in Nitrokey App - */ - public: - typedef Transaction - CommandTransaction; -}; - -class PasswordSafeSendSlotViaHID : Command { - /** - * never used in Nitrokey App - */ - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_kind; - - bool isValid() const { return !(slot_number & 0xF0); } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -// TODO "Device::passwordSafeSendSlotDataViaHID" - -class WriteGeneralConfig : Command { - public: - struct CommandPayload { - union{ - uint8_t config[5]; - struct{ - uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ - uint8_t capslock; /** same as numlock */ - uint8_t scrolllock; /** same as numlock */ - uint8_t enable_user_password; - uint8_t delete_user_password; - }; - }; - bool isValid() const { return numlock < 2 && capslock < 2 && scrolllock < 2 && enable_user_password < 2; } - - std::string dissect() const { - std::stringstream ss; - ss << "numlock:\t" << (int)numlock << std::endl; - ss << "capslock:\t" << (int)capslock << std::endl; - ss << "scrolllock:\t" << (int)scrolllock << std::endl; - ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; - ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class FirstAuthenticate : Command { - public: - struct CommandPayload { - uint8_t card_password[25]; - uint8_t temporary_password[25]; - - bool isValid() const { return true; } - - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(card_password); - hexdump_to_ss(temporary_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class UserAuthenticate : Command { - public: - struct CommandPayload { - uint8_t card_password[25]; - uint8_t temporary_password[25]; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(card_password); - hexdump_to_ss(temporary_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class Authorize : Command { - public: - struct CommandPayload { - uint32_t crc_to_authorize; - uint8_t temporary_password[25]; - - std::string dissect() const { - std::stringstream ss; - ss << " crc_to_authorize:\t" << std::hex << std::setw(2) << std::setfill('0') << crc_to_authorize<< std::endl; - hexdump_to_ss(temporary_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class UserAuthorize : Command { - public: - struct CommandPayload { - uint32_t crc_to_authorize; - uint8_t temporary_password[25]; - std::string dissect() const { - std::stringstream ss; - ss << " crc_to_authorize:\t" << crc_to_authorize<< std::endl; - hexdump_to_ss(temporary_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class UnlockUserPassword : Command { - public: - struct CommandPayload { - uint8_t admin_password[25]; - uint8_t user_new_password[25]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(admin_password); - print_to_ss_volatile(user_new_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class ChangeUserPin : Command { - public: - struct CommandPayload { - uint8_t old_pin[25]; - uint8_t new_pin[25]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(old_pin); - print_to_ss_volatile(new_pin); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class IsAESSupported : Command { - public: - struct CommandPayload { - uint8_t user_password[20]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(user_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - - -class ChangeAdminPin : Command { - public: - struct CommandPayload { - uint8_t old_pin[25]; - uint8_t new_pin[25]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(old_pin); - print_to_ss_volatile(new_pin); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class LockDevice : Command { - public: - typedef Transaction - CommandTransaction; -}; - -class FactoryReset : Command { - public: - struct CommandPayload { - uint8_t admin_password[20]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(admin_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; -}; - -class BuildAESKey : Command { - public: - struct CommandPayload { - uint8_t admin_password[20]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile(admin_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - -}; - -} -} -} -#pragma pack (pop) -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands_0.8.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands_0.8.h deleted file mode 100644 index 9477890..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick10_commands_0.8.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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 - */ - - -#ifndef LIBNITROKEY_STICK10_COMMANDS_0_8_H -#define LIBNITROKEY_STICK10_COMMANDS_0_8_H - -#include -#include -#include -#include -#include -#include "command.h" -#include "device_proto.h" -#include "stick10_commands.h" - -#pragma pack (push,1) - - -namespace nitrokey { - namespace proto { - -/* - * Stick10 protocol definition - */ - namespace stick10_08 { - using stick10::FirstAuthenticate; - using stick10::UserAuthenticate; - using stick10::SetTime; - using stick10::GetStatus; - using stick10::BuildAESKey; - using stick10::ChangeAdminPin; - using stick10::ChangeUserPin; - using stick10::EnablePasswordSafe; - using stick10::ErasePasswordSafeSlot; - using stick10::FactoryReset; - using stick10::GetPasswordRetryCount; - using stick10::GetUserPasswordRetryCount; - using stick10::GetPasswordSafeSlotLogin; - using stick10::GetPasswordSafeSlotName; - using stick10::GetPasswordSafeSlotPassword; - using stick10::GetPasswordSafeSlotStatus; - using stick10::GetSlotName; - using stick10::IsAESSupported; - using stick10::LockDevice; - using stick10::PasswordSafeInitKey; - using stick10::PasswordSafeSendSlotViaHID; - using stick10::SetPasswordSafeSlotData; - using stick10::SetPasswordSafeSlotData2; - using stick10::UnlockUserPassword; - using stick10::ReadSlot; - - class EraseSlot : Command { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t temporary_admin_password[25]; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - hexdump_to_ss(temporary_admin_password); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - class SendOTPData : Command { - //admin auth - public: - struct CommandPayload { - uint8_t temporary_admin_password[25]; - uint8_t type; //S-secret, N-name - uint8_t id; //multiple reports for values longer than 30 bytes - uint8_t data[30]; //data, does not need null termination - - bool isValid() const { return true; } - - void setTypeName(){ - type = 'N'; - } - void setTypeSecret(){ - type = 'S'; - } - - std::string dissect() const { - std::stringstream ss; - hexdump_to_ss(temporary_admin_password); - ss << "type:\t" << type << std::endl; - ss << "id:\t" << (int)id << std::endl; -#ifdef LOG_VOLATILE_DATA - ss << "data:" << std::endl - << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data); -#else - ss << " Volatile data not logged" << std::endl; -#endif - return ss.str(); - } - } __packed; - - - struct ResponsePayload { - union { - uint8_t data[40]; - } __packed; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; -#ifdef LOG_VOLATILE_DATA - ss << "data:" << std::endl - << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data); -#else - ss << " Volatile data not logged" << std::endl; -#endif - return ss.str(); - } - } __packed; - - - typedef Transaction - CommandTransaction; - }; - - class WriteToOTPSlot : Command { - //admin auth - public: - struct CommandPayload { - uint8_t temporary_admin_password[25]; - uint8_t slot_number; - union { - uint64_t slot_counter_or_interval; - uint8_t slot_counter_s[8]; - } __packed; - union { - uint8_t _slot_config; - struct { - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - union { - uint8_t slot_token_id[13]; /** OATH Token Identifier */ - struct { /** @see https://openauthentication.org/token-specs/ */ - uint8_t omp[2]; - uint8_t tt[2]; - uint8_t mui[8]; - uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 - } slot_token_fields; - }; - - bool isValid() const { return true; } - - std::string dissect() const { - std::stringstream ss; - hexdump_to_ss(temporary_admin_password); - ss << "slot_config:\t" << std::bitset<8>((int) _slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - ss << "slot_number:\t" << (int) (slot_number) << std::endl; - ss << "slot_counter_or_interval:\t[" << (int) slot_counter_or_interval << "]\t" - << ::nitrokey::misc::hexdump((const uint8_t *) (&slot_counter_or_interval), sizeof slot_counter_or_interval, false); - - ss << "slot_token_id:\t"; - for (auto i : slot_token_id) - ss << std::hex << std::setw(2) << std::setfill('0') << (int) i << " "; - ss << std::endl; - - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - class GetHOTP : Command { - public: - struct CommandPayload { - uint8_t slot_number; - struct { - uint64_t challenge; //@unused - uint64_t last_totp_time; //@unused - uint8_t last_interval; //@unused - } __packed _unused; - uint8_t temporary_user_password[25]; - - bool isValid() const { return (slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - hexdump_to_ss(temporary_user_password); - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - union { - uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 - struct { - uint32_t code; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - } __packed; - } __packed; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << "code:\t" << (code) << std::endl; - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - - class GetTOTP : Command { - //user auth - public: - struct CommandPayload { - uint8_t slot_number; - uint64_t challenge; //@unused - uint64_t last_totp_time; //@unused - uint8_t last_interval; //@unused - uint8_t temporary_user_password[25]; - - bool isValid() const { return !(slot_number & 0xF0); } - std::string dissect() const { - std::stringstream ss; - hexdump_to_ss(temporary_user_password); - ss << "slot_number:\t" << (int)(slot_number) << std::endl; - ss << "challenge:\t" << (challenge) << std::endl; - ss << "last_totp_time:\t" << (last_totp_time) << std::endl; - ss << "last_interval:\t" << (int)(last_interval) << std::endl; - return ss.str(); - } - } __packed; - - struct ResponsePayload { - union { - uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 - struct { - uint32_t code; - union{ - uint8_t _slot_config; - struct{ - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - } __packed ; - } __packed ; - - bool isValid() const { return true; } - std::string dissect() const { - std::stringstream ss; - ss << "code:\t" << (code) << std::endl; - ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - - class WriteGeneralConfig : Command { - //admin auth - public: - struct CommandPayload { - union{ - uint8_t config[5]; - struct{ - uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ - uint8_t capslock; /** same as numlock */ - uint8_t scrolllock; /** same as numlock */ - uint8_t enable_user_password; - uint8_t delete_user_password; - }; - }; - uint8_t temporary_admin_password[25]; - - static constexpr uint8_t special_HOTP_slots = 3; - bool isValid() const { return numlock < special_HOTP_slots && capslock < special_HOTP_slots - && scrolllock < special_HOTP_slots && enable_user_password < 2; } - - std::string dissect() const { - std::stringstream ss; - ss << "numlock:\t" << (int)numlock << std::endl; - ss << "capslock:\t" << (int)capslock << std::endl; - ss << "scrolllock:\t" << (int)scrolllock << std::endl; - ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; - ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - } - } -} -#pragma pack (pop) - -#endif //LIBNITROKEY_STICK10_COMMANDS_0_8_H diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick20_commands.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick20_commands.h deleted file mode 100644 index 7efa1b6..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/stick20_commands.h +++ /dev/null @@ -1,394 +0,0 @@ -/* - * 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 - */ - -#ifndef STICK20_COMMANDS_H -#define STICK20_COMMANDS_H - - - -#include -#include "command.h" -#include -#include -#include "device_proto.h" - -#pragma pack (push,1) - -namespace nitrokey { - namespace proto { - -/* -* STICK20 protocol command ids -* a superset (almost) of STICK10 -*/ - - namespace stick20 { - - class ChangeAdminUserPin20Current : - public PasswordCommand {}; - class ChangeAdminUserPin20New : - public PasswordCommand {}; - class UnlockUserPin : - public PasswordCommand {}; - - class EnableEncryptedPartition : public PasswordCommand {}; - class EnableHiddenEncryptedPartition : public PasswordCommand {}; - - class SetUnencryptedVolumeReadOnlyAdmin : - public PasswordCommand {}; - class SetUnencryptedVolumeReadWriteAdmin : - public PasswordCommand {}; - class SetEncryptedVolumeReadOnly : - public PasswordCommand {}; - class SetEncryptedVolumeReadWrite : - public PasswordCommand {}; - - //FIXME the volume disabling commands do not need password - class DisableEncryptedPartition : public PasswordCommand {}; - class DisableHiddenEncryptedPartition : public PasswordCommand {}; - - class EnableFirmwareUpdate : public PasswordCommand {}; - - class ChangeUpdatePassword : Command { - public: - struct CommandPayload { - uint8_t __gap; - uint8_t current_update_password[20]; - uint8_t __gap2; - uint8_t new_update_password[20]; - std::string dissect() const { - std::stringstream ss; - print_to_ss_volatile( current_update_password ); - print_to_ss_volatile( new_update_password ); - return ss.str(); - } - }; - - typedef Transaction - CommandTransaction; - }; - - class ExportFirmware : public PasswordCommand {}; - - class CreateNewKeys : - public PasswordCommand {}; - - - class FillSDCardWithRandomChars : Command { - public: - enum class ChosenVolumes : uint8_t { - all_volumes = 0, - encrypted_volume = 1 - }; - - struct CommandPayload { - uint8_t volume_flag; - uint8_t kind; - uint8_t admin_pin[20]; - - std::string dissect() const { - std::stringstream ss; - print_to_ss( (int) volume_flag ); - print_to_ss( kind ); - print_to_ss_volatile(admin_pin); - return ss.str(); - } - void set_kind_user() { - kind = (uint8_t) 'P'; - } - void set_defaults(){ - set_kind_user(); - volume_flag = static_cast(ChosenVolumes::encrypted_volume); - } - - } __packed; - - typedef Transaction::command_id(), - struct CommandPayload, struct EmptyPayload> - CommandTransaction; - }; - - namespace StorageCommandResponsePayload{ - using namespace DeviceResponseConstants; - static constexpr auto padding_size = - storage_data_absolute_address - header_size; - struct TransmissionData{ - uint8_t _padding[padding_size]; - - uint8_t SendCounter_u8; - uint8_t SendDataType_u8; - uint8_t FollowBytesFlag_u8; - uint8_t SendSize_u8; - - std::string dissect() const { - std::stringstream ss; - ss << "_padding:" << std::endl - << ::nitrokey::misc::hexdump((const uint8_t *) (_padding), - sizeof _padding); - print_to_ss((int) SendCounter_u8); - print_to_ss((int) SendDataType_u8); - print_to_ss((int) FollowBytesFlag_u8); - print_to_ss((int) SendSize_u8); - return ss.str(); - } - - } __packed; - } - - namespace DeviceConfigurationResponsePacket{ - - struct ResponsePayload { - StorageCommandResponsePayload::TransmissionData transmission_data; - - uint16_t MagicNumber_StickConfig_u16; - /** - * READ_WRITE_ACTIVE = ReadWriteFlagUncryptedVolume_u8 == 0; - */ - uint8_t ReadWriteFlagUncryptedVolume_u8; - uint8_t ReadWriteFlagCryptedVolume_u8; - - union{ - uint8_t VersionInfo_au8[4]; - struct { - uint8_t major; - uint8_t minor; - uint8_t _reserved2; - uint8_t build_iteration; - } __packed versionInfo; - } __packed; - - uint8_t ReadWriteFlagHiddenVolume_u8; - uint8_t FirmwareLocked_u8; - - union{ - uint8_t NewSDCardFound_u8; - struct { - bool NewCard :1; - uint8_t Counter :7; - } __packed NewSDCardFound_st; - } __packed; - - /** - * SD card FILLED with random chars - */ - uint8_t SDFillWithRandomChars_u8; - uint32_t ActiveSD_CardID_u32; - union{ - uint8_t VolumeActiceFlag_u8; - struct { - bool unencrypted :1; - bool encrypted :1; - bool hidden :1; - } __packed VolumeActiceFlag_st; - } __packed; - uint8_t NewSmartCardFound_u8; - uint8_t UserPwRetryCount; - uint8_t AdminPwRetryCount; - uint32_t ActiveSmartCardID_u32; - uint8_t StickKeysNotInitiated; - - bool isValid() const { return true; } - - std::string dissect() const { - std::stringstream ss; - - print_to_ss(transmission_data.dissect()); - print_to_ss( MagicNumber_StickConfig_u16 ); - print_to_ss((int) ReadWriteFlagUncryptedVolume_u8 ); - print_to_ss((int) ReadWriteFlagCryptedVolume_u8 ); - print_to_ss((int) ReadWriteFlagHiddenVolume_u8 ); - print_to_ss((int) versionInfo.major ); - print_to_ss((int) versionInfo.minor ); - print_to_ss((int) versionInfo.build_iteration ); - print_to_ss((int) FirmwareLocked_u8 ); - print_to_ss((int) NewSDCardFound_u8 ); - print_to_ss((int) NewSDCardFound_st.NewCard ); - print_to_ss((int) NewSDCardFound_st.Counter ); - print_to_ss((int) SDFillWithRandomChars_u8 ); - print_to_ss( ActiveSD_CardID_u32 ); - print_to_ss((int) VolumeActiceFlag_u8 ); - print_to_ss((int) VolumeActiceFlag_st.unencrypted ); - print_to_ss((int) VolumeActiceFlag_st.encrypted ); - print_to_ss((int) VolumeActiceFlag_st.hidden); - print_to_ss((int) NewSmartCardFound_u8 ); - print_to_ss((int) UserPwRetryCount ); - print_to_ss((int) AdminPwRetryCount ); - print_to_ss( ActiveSmartCardID_u32 ); - print_to_ss((int) StickKeysNotInitiated ); - - return ss.str(); - } - } __packed; - } - - class SendStartup : Command { - public: - struct CommandPayload { - uint64_t localtime; // POSIX seconds from epoch start, supports until year 2106 - std::string dissect() const { - std::stringstream ss; - print_to_ss( localtime ); - return ss.str(); - } - void set_defaults(){ - localtime = - std::chrono::duration_cast ( - std::chrono::system_clock::now().time_since_epoch()).count(); - } - }__packed; - - using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload; - - typedef Transaction - CommandTransaction; - }; - - -// TODO fix original nomenclature - class SendSetReadonlyToUncryptedVolume : public PasswordCommand {}; - class SendSetReadwriteToUncryptedVolume : public PasswordCommand {}; - class SendClearNewSdCardFound : public PasswordCommand {}; - - class GetDeviceStatus : Command { - public: - using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload; - - typedef Transaction - CommandTransaction; - }; - - class Wink : Command { - public: - typedef Transaction - CommandTransaction; - }; - - class CheckSmartcardUsage : Command { - public: - typedef Transaction - CommandTransaction; - }; - - class GetSDCardOccupancy : Command { - public: - struct ResponsePayload { - uint8_t WriteLevelMin; - uint8_t WriteLevelMax; - uint8_t ReadLevelMin; - uint8_t ReadLevelMax; - std::string dissect() const { - std::stringstream ss; - print_to_ss((int) WriteLevelMin); - print_to_ss((int) WriteLevelMax); - print_to_ss((int) ReadLevelMin); - print_to_ss((int) ReadLevelMax); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - - class SetupHiddenVolume : Command { - public: - constexpr static int MAX_HIDDEN_VOLUME_PASSWORD_SIZE = 20; - struct CommandPayload { - uint8_t SlotNr_u8; - uint8_t StartBlockPercent_u8; - uint8_t EndBlockPercent_u8; - uint8_t HiddenVolumePassword_au8[MAX_HIDDEN_VOLUME_PASSWORD_SIZE]; - std::string dissect() const { - std::stringstream ss; - print_to_ss((int) SlotNr_u8); - print_to_ss((int) StartBlockPercent_u8); - print_to_ss((int) EndBlockPercent_u8); - print_to_ss_volatile(HiddenVolumePassword_au8); - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - -//disable this command for now -// class LockFirmware : public PasswordCommand {}; - - class ProductionTest : Command { - public: - struct ResponsePayload { - - StorageCommandResponsePayload::TransmissionData transmission_data; - - uint8_t FirmwareVersion_au8[2]; // 2 byte // 2 - uint8_t FirmwareVersionInternal_u8; // 1 byte // 3 - uint8_t SD_Card_Size_u8; // 1 byte // 4 - uint32_t CPU_CardID_u32; // 4 byte // 8 - uint32_t SmartCardID_u32; // 4 byte // 12 - uint32_t SD_CardID_u32; // 4 byte // 16 - uint8_t SC_UserPwRetryCount; // User PIN retry count 1 byte // 17 - uint8_t SC_AdminPwRetryCount; // Admin PIN retry count 1 byte // 18 - uint8_t SD_Card_ManufacturingYear_u8; // 1 byte // 19 - uint8_t SD_Card_ManufacturingMonth_u8; // 1 byte // 20 - uint16_t SD_Card_OEM_u16; // 2 byte // 22 - uint16_t SD_WriteSpeed_u16; // in kbyte / sec 2 byte // 24 - uint8_t SD_Card_Manufacturer_u8; // 1 byte // 25 - - bool isValid() const { return true; } - - std::string dissect() const { - std::stringstream ss; - - print_to_ss(transmission_data.dissect()); - print_to_ss((int) FirmwareVersion_au8[0]); - print_to_ss((int) FirmwareVersion_au8[1]); - print_to_ss((int) FirmwareVersionInternal_u8); - print_to_ss((int) SD_Card_Size_u8); - print_to_ss( CPU_CardID_u32); - print_to_ss( SmartCardID_u32); - print_to_ss( SD_CardID_u32); - print_to_ss((int) SC_UserPwRetryCount); - print_to_ss((int) SC_AdminPwRetryCount); - print_to_ss((int) SD_Card_ManufacturingYear_u8); - print_to_ss((int) SD_Card_ManufacturingMonth_u8); - print_to_ss( SD_Card_OEM_u16); - print_to_ss( SD_WriteSpeed_u16); - print_to_ss((int) SD_Card_Manufacturer_u8); - return ss.str(); - } - - } __packed; - - typedef Transaction - CommandTransaction; - }; - - } - } -} - -#undef print_to_ss -#pragma pack (pop) - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/version.h b/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/version.h deleted file mode 100644 index 6547af0..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/libnitrokey/version.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 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 - */ - -#ifndef LIBNITROKEY_VERSION_H -#define LIBNITROKEY_VERSION_H - -namespace nitrokey { - unsigned int get_major_library_version(); - - unsigned int get_minor_library_version(); - - const char* get_library_version(); -} - -#endif diff --git a/nitrokey-sys/libnitrokey-v3.4.1/log.cc b/nitrokey-sys/libnitrokey-v3.4.1/log.cc deleted file mode 100644 index 06acee7..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/log.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 - */ - -#include "log.h" -#include -#include -#include - -#include - -namespace nitrokey { - namespace log { - - Log *Log::mp_instance = nullptr; - StdlogHandler stdlog_handler; - - std::string Log::prefix = ""; - - - std::string LogHandler::loglevel_to_str(Loglevel lvl) { - switch (lvl) { - case Loglevel::DEBUG_L1: - return std::string("DEBUG_L1"); - case Loglevel::DEBUG_L2: - return std::string("DEBUG_L2"); - case Loglevel::DEBUG: - return std::string("DEBUG"); - case Loglevel::INFO: - return std::string("INFO"); - case Loglevel::WARNING: - return std::string("WARNING"); - case Loglevel::ERROR: - return std::string("ERROR"); - } - return std::string(""); - } - - void Log::operator()(const std::string &logstr, Loglevel lvl) { - if (mp_loghandler != nullptr){ - if ((int) lvl <= (int) m_loglevel) mp_loghandler->print(prefix+logstr, lvl); - } - } - - void Log::setPrefix(const std::string prefix) { - if (!prefix.empty()){ - Log::prefix = "["+prefix+"]"; - } else { - Log::prefix = ""; - } - } - - void StdlogHandler::print(const std::string &str, Loglevel lvl) { - std::string s = format_message_to_string(str, lvl); - std::clog << s; - } - - void FunctionalLogHandler::print(const std::string &str, Loglevel lvl) { - std::string s = format_message_to_string(str, lvl); - log_function(s); - } - - std::string LogHandler::format_message_to_string(const std::string &str, const Loglevel &lvl) { - static bool last_short = false; - if (str.length() == 1){ - last_short = true; - return str; - } - time_t t = time(nullptr); - tm tm = *localtime(&t); - - std::stringstream s; - s - << (last_short? "\n" : "") - << "[" << std::put_time(&tm, "%c") << "]" - << "[" << loglevel_to_str(lvl) << "]\t" - << str << std::endl; - last_short = false; - return s.str(); - } - - FunctionalLogHandler::FunctionalLogHandler(log_function_type _log_function) { - log_function = _log_function; - } - } -} diff --git a/nitrokey-sys/libnitrokey-v3.4.1/misc.cc b/nitrokey-sys/libnitrokey-v3.4.1/misc.cc deleted file mode 100644 index 59185f3..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/misc.cc +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 - */ - -#include -#include -#include "misc.h" -#include "inttypes.h" -#include -#include -#include "LibraryException.h" -#include - -namespace nitrokey { -namespace misc { - - - -::std::vector hex_string_to_byte(const char* hexString){ - const size_t big_string_size = 257; //arbitrary 'big' number - const size_t s_size = strnlen(hexString, big_string_size); - const size_t d_size = s_size/2; - if (s_size%2!=0 || s_size>=big_string_size){ - throw InvalidHexString(0); - } - auto data = ::std::vector(); - data.reserve(d_size); - - char buf[3]; - buf[2] = '\0'; - for(size_t i=0; i -::std::string hexdump(const uint8_t *p, size_t size, bool print_header, - bool print_ascii, bool print_empty) { - ::std::stringstream out; - char formatbuf[128]; - const uint8_t *pstart = p; - - for (const uint8_t *pend = p + size; p < pend;) { - if (print_header){ - snprintf(formatbuf, 128, "%04x\t", static_cast (p - pstart)); - out << formatbuf; - } - - const uint8_t* pp = p; - for (const uint8_t *le = p + 16; p < le; p++) { - if (p < pend){ - snprintf(formatbuf, 128, "%02x ", uint8_t(*p)); - out << formatbuf; - } else { - if(print_empty) - out << "-- "; - } - - } - if(print_ascii){ - out << " "; - for (const uint8_t *le = pp + 16; pp < le && pp < pend; pp++) { - if (std::isgraph(*pp)) - out << uint8_t(*pp); - else - out << '.'; - } - } - out << ::std::endl; - } - return out.str(); -} - -static uint32_t _crc32(uint32_t crc, uint32_t data) { - int i; - crc = crc ^ data; - - for (i = 0; i < 32; i++) { - if (crc & 0x80000000) - crc = (crc << 1) ^ 0x04C11DB7; // polynomial used in STM32 - else - crc = (crc << 1); - } - - return crc; -} - -uint32_t stm_crc32(const uint8_t *data, size_t size) { - uint32_t crc = 0xffffffff; - const uint32_t *pend = (const uint32_t *)(data + size); - for (const uint32_t *p = (const uint32_t *)(data); p < pend; p++) - crc = _crc32(crc, *p); - return crc; -} -} -} diff --git a/nitrokey-sys/libnitrokey-v3.4.1/version.cc b/nitrokey-sys/libnitrokey-v3.4.1/version.cc deleted file mode 100644 index dfdc802..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/version.cc +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 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 - */ - -#include "version.h" - -namespace nitrokey { - unsigned int get_major_library_version() { - return 3; - } - - unsigned int get_minor_library_version() { - return 0; - } - - const char* get_library_version() { - return "unknown"; - } -} - diff --git a/nitrokey-sys/libnitrokey-v3.4.1/version.cc.in b/nitrokey-sys/libnitrokey-v3.4.1/version.cc.in deleted file mode 100644 index 0eae647..0000000 --- a/nitrokey-sys/libnitrokey-v3.4.1/version.cc.in +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 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 - */ - -#include "version.h" - -namespace nitrokey { - unsigned int get_major_library_version() { - return @PROJECT_VERSION_MAJOR@; - } - - unsigned int get_minor_library_version() { - return @PROJECT_VERSION_MINOR@; - } - - const char* get_library_version() { - return "@PROJECT_VERSION_GIT@"; - } -} - diff --git a/nitrokey-sys/libnitrokey-v3.5/DeviceCommunicationExceptions.cpp b/nitrokey-sys/libnitrokey-v3.5/DeviceCommunicationExceptions.cpp new file mode 100644 index 0000000..4d62aad --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/DeviceCommunicationExceptions.cpp @@ -0,0 +1,24 @@ +/* + * 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 + */ + +#include "DeviceCommunicationExceptions.h" + +std::atomic_int DeviceCommunicationException::occurred {0}; diff --git a/nitrokey-sys/libnitrokey-v3.5/LICENSE b/nitrokey-sys/libnitrokey-v3.5/LICENSE new file mode 100644 index 0000000..341c30b --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/LICENSE @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/nitrokey-sys/libnitrokey-v3.5/NK_C_API.cc b/nitrokey-sys/libnitrokey-v3.5/NK_C_API.cc new file mode 100644 index 0000000..1d3fa3a --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/NK_C_API.cc @@ -0,0 +1,910 @@ +/* + * 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 + */ + +#include "NK_C_API.h" +#include +#include +#include "libnitrokey/NitrokeyManager.h" +#include +#include "libnitrokey/LibraryException.h" +#include "libnitrokey/cxx_semantics.h" +#include "libnitrokey/stick20_commands.h" +#include "libnitrokey/device_proto.h" +#include "libnitrokey/version.h" + +#ifdef _MSC_VER +#ifdef _WIN32 +#pragma message "Using own strndup" +char * strndup(const char* str, size_t maxlen) { + size_t len = strnlen(str, maxlen); + char* dup = (char *)malloc(len + 1); + memcpy(dup, str, len); + dup[len] = 0; + return dup; +} +#endif +#endif + +using namespace nitrokey; + +const uint8_t NK_PWS_SLOT_COUNT = PWS_SLOT_COUNT; +static uint8_t NK_last_command_status = 0; +static const int max_string_field_length = 100; + +template +T* duplicate_vector_and_clear(std::vector &v){ + auto d = new T[v.size()]; + std::copy(v.begin(), v.end(), d); + std::fill(v.begin(), v.end(), 0); + return d; +} + +template +std::tuple get_with_status(T func, R fallback) { + NK_last_command_status = 0; + try { + return std::make_tuple(0, func()); + } + catch (CommandFailedException & commandFailedException){ + NK_last_command_status = commandFailedException.last_command_status; + } + catch (LibraryException & libraryException){ + NK_last_command_status = libraryException.exception_id(); + } + catch (const DeviceCommunicationException &deviceException){ + NK_last_command_status = 256-deviceException.getType(); + } + return std::make_tuple(NK_last_command_status, fallback); +} + +template +uint8_t * get_with_array_result(T func){ + return std::get<1>(get_with_status(func, nullptr)); +} + +template +char* get_with_string_result(T func){ + auto result = std::get<1>(get_with_status(func, nullptr)); + if (result == nullptr) { + return strndup("", MAXIMUM_STR_REPLY_LENGTH); + } + return result; +} + +template +auto get_with_result(T func){ + return std::get<1>(get_with_status(func, static_cast(0))); +} + +template +uint8_t get_without_result(T func){ + NK_last_command_status = 0; + try { + func(); + return 0; + } + catch (CommandFailedException & commandFailedException){ + NK_last_command_status = commandFailedException.last_command_status; + } + catch (LibraryException & libraryException){ + NK_last_command_status = libraryException.exception_id(); + } + catch (const InvalidCRCReceived &invalidCRCException){ + ; + } + catch (const DeviceCommunicationException &deviceException){ + NK_last_command_status = 256-deviceException.getType(); + } + return NK_last_command_status; +} + + +#ifdef __cplusplus +extern "C" { +#endif + + NK_C_API uint8_t NK_get_last_command_status() { + auto _copy = NK_last_command_status; + NK_last_command_status = 0; + return _copy; + } + + NK_C_API int NK_login(const char *device_model) { + auto m = NitrokeyManager::instance(); + try { + NK_last_command_status = 0; + return m->connect(device_model); + } + catch (CommandFailedException & commandFailedException) { + NK_last_command_status = commandFailedException.last_command_status; + return commandFailedException.last_command_status; + } + catch (const DeviceCommunicationException &deviceException){ + NK_last_command_status = 256-deviceException.getType(); + cerr << deviceException.what() << endl; + return 0; + } + catch (std::runtime_error &e) { + cerr << e.what() << endl; + return 0; + } + return 0; + } + + NK_C_API int NK_login_enum(NK_device_model device_model) { + const char *model_string; + switch (device_model) { + case NK_PRO: + model_string = "P"; + break; + case NK_STORAGE: + model_string = "S"; + break; + case NK_DISCONNECTED: + default: + /* no such enum value -- return error code */ + return 0; + } + return NK_login(model_string); + } + + NK_C_API int NK_logout() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->disconnect(); + }); + } + + NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + return m->first_authenticate(admin_password, admin_temporary_password); + }); + } + + + NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->user_authenticate(user_password, user_temporary_password); + }); + } + + NK_C_API int NK_factory_reset(const char* admin_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->factory_reset(admin_password); + }); + } + NK_C_API int NK_build_aes_key(const char* admin_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->build_aes_key(admin_password); + }); + } + + NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->unlock_user_password(admin_password, new_user_password); + }); + } + + NK_C_API int 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) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + return m->write_config(numlock, capslock, scrolllock, enable_user_password, delete_user_password, admin_temporary_password); + }); + } + + + NK_C_API uint8_t* NK_read_config() { + auto m = NitrokeyManager::instance(); + return get_with_array_result([&]() { + auto v = m->read_config(); + return duplicate_vector_and_clear(v); + }); + } + + + NK_C_API enum NK_device_model NK_get_device_model() { + auto m = NitrokeyManager::instance(); + try { + auto model = m->get_connected_device_model(); + switch (model) { + case DeviceModel::PRO: + return NK_PRO; + case DeviceModel::STORAGE: + return NK_STORAGE; + default: + /* unknown or not connected device */ + return NK_device_model::NK_DISCONNECTED; + } + } catch (const DeviceNotConnected& e) { + return NK_device_model::NK_DISCONNECTED; + } +} + + + void clear_string(std::string &s) { + std::fill(s.begin(), s.end(), ' '); + } + + + NK_C_API char * NK_status() { + return NK_get_status_as_string(); + } + + NK_C_API char * NK_get_status_as_string() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_status_as_string(); + char * rs = strndup(s.c_str(), MAXIMUM_STR_REPLY_LENGTH); + clear_string(s); + return rs; + }); + } + + NK_C_API int NK_get_status(struct NK_status* out) { + if (out == nullptr) { + return -1; + } + auto m = NitrokeyManager::instance(); + auto result = get_with_status([&]() { + return m->get_status(); + }, proto::stick10::GetStatus::ResponsePayload()); + auto error_code = std::get<0>(result); + if (error_code != 0) { + return error_code; + } + + auto status = std::get<1>(result); + out->firmware_version_major = status.firmware_version_st.major; + out->firmware_version_minor = status.firmware_version_st.minor; + out->serial_number_smart_card = status.card_serial_u32; + out->config_numlock = status.numlock; + out->config_capslock = status.capslock; + out->config_scrolllock = status.scrolllock; + out->otp_user_password = status.enable_user_password != 0; + return 0; + } + + NK_C_API char * NK_device_serial_number() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_serial_number(); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API char * NK_get_hotp_code(uint8_t slot_number) { + return NK_get_hotp_code_PIN(slot_number, ""); + } + + NK_C_API char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_HOTP_code(slot_number, user_temporary_password); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval) { + return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, ""); + } + + NK_C_API char * 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) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_TOTP_code(slot_number, challenge, last_totp_time, last_interval, user_temporary_password); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->erase_hotp_slot(slot_number, temporary_password); + }); + } + + NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->erase_totp_slot(slot_number, temporary_password); + }); + } + + NK_C_API int 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) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, token_ID, + temporary_password); + }); + } + + NK_C_API int 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) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->write_TOTP_slot(slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, token_ID, + temporary_password); + }); + } + + NK_C_API char* NK_get_totp_slot_name(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + const auto slot_name = m->get_totp_slot_name(slot_number); + return slot_name; + }); + } + NK_C_API char* NK_get_hotp_slot_name(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + const auto slot_name = m->get_hotp_slot_name(slot_number); + return slot_name; + }); + } + + NK_C_API void NK_set_debug(bool state) { + auto m = NitrokeyManager::instance(); + m->set_debug(state); + } + + + NK_C_API void NK_set_debug_level(const int level) { + auto m = NitrokeyManager::instance(); + m->set_loglevel(level); + } + + NK_C_API unsigned int NK_get_major_library_version() { + return get_major_library_version(); + } + + NK_C_API unsigned int NK_get_minor_library_version() { + return get_minor_library_version(); + } + + NK_C_API const char* NK_get_library_version() { + return get_library_version(); + } + + NK_C_API int NK_totp_set_time(uint64_t time) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_time(time); + }); + } + + NK_C_API int NK_totp_set_time_soft(uint64_t time) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_time_soft(time); + }); + } + + NK_C_API int NK_totp_get_time() { + return 0; + } + + NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_admin_PIN(current_PIN, new_PIN); + }); + } + + NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_user_PIN(current_PIN, new_PIN); + }); + } + + NK_C_API int NK_enable_password_safe(const char *user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->enable_password_safe(user_pin); + }); + } + NK_C_API uint8_t * NK_get_password_safe_slot_status() { + auto m = NitrokeyManager::instance(); + return get_with_array_result([&]() { + auto slot_status = m->get_password_safe_slot_status(); + return duplicate_vector_and_clear(slot_status); + }); + + } + + NK_C_API uint8_t NK_get_user_retry_count() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_user_retry_count(); + }); + } + + NK_C_API uint8_t NK_get_admin_retry_count() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_admin_retry_count(); + }); + } + + NK_C_API int NK_lock_device() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->lock_device(); + }); + } + + NK_C_API char *NK_get_password_safe_slot_name(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_password_safe_slot_name(slot_number); + }); + } + + NK_C_API char *NK_get_password_safe_slot_login(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_password_safe_slot_login(slot_number); + }); + } + NK_C_API char *NK_get_password_safe_slot_password(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_password_safe_slot_password(slot_number); + }); + } + NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, + const char *slot_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->write_password_safe_slot(slot_number, slot_name, slot_login, slot_password); + }); + } + + NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->erase_password_safe_slot(slot_number); + }); + } + + NK_C_API int NK_is_AES_supported(const char *user_password) { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return (uint8_t)m->is_AES_supported(user_password); + }); + } + + NK_C_API int NK_login_auto() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return (uint8_t)m->connect(); + }); + } + + // storage commands + + NK_C_API int NK_send_startup(uint64_t seconds_from_epoch) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->send_startup(seconds_from_epoch); + }); + } + + NK_C_API int NK_unlock_encrypted_volume(const char* user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->unlock_encrypted_volume(user_pin); + }); + } + + NK_C_API int NK_lock_encrypted_volume() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->lock_encrypted_volume(); + }); + } + + NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->unlock_hidden_volume(hidden_volume_password); + }); + } + + NK_C_API int NK_lock_hidden_volume() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->lock_hidden_volume(); + }); + } + + NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, + const char *hidden_volume_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->create_hidden_volume(slot_nr, start_percent, end_percent, + hidden_volume_password); + }); + } + + NK_C_API int NK_set_unencrypted_read_only(const char *user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_unencrypted_read_only(user_pin); + }); + } + + NK_C_API int NK_set_unencrypted_read_write(const char *user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_unencrypted_read_write(user_pin); + }); + } + + NK_C_API int NK_set_unencrypted_read_only_admin(const char *admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_unencrypted_read_only_admin(admin_pin); + }); + } + + NK_C_API int NK_set_unencrypted_read_write_admin(const char *admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_unencrypted_read_write_admin(admin_pin); + }); + } + + NK_C_API int NK_set_encrypted_read_only(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_encrypted_volume_read_only(admin_pin); + }); + } + + NK_C_API int NK_set_encrypted_read_write(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_encrypted_volume_read_write(admin_pin); + }); + } + + NK_C_API int NK_export_firmware(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->export_firmware(admin_pin); + }); + } + + NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->clear_new_sd_card_warning(admin_pin); + }); + } + + NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->fill_SD_card_with_random_data(admin_pin); + }); + } + + NK_C_API int NK_change_update_password(const char* current_update_password, + const char* new_update_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_update_password(current_update_password, new_update_password); + }); + } + + NK_C_API int NK_enable_firmware_update(const char* update_password){ + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->enable_firmware_update(update_password); + }); + } + + NK_C_API char* NK_get_status_storage_as_string() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_status_storage_as_string(); + }); + } + + NK_C_API int NK_get_status_storage(NK_storage_status* out) { + if (out == nullptr) { + return -1; + } + auto m = NitrokeyManager::instance(); + auto result = get_with_status([&]() { + return m->get_status_storage(); + }, proto::stick20::DeviceConfigurationResponsePacket::ResponsePayload()); + auto error_code = std::get<0>(result); + if (error_code != 0) { + return error_code; + } + + auto status = std::get<1>(result); + out->unencrypted_volume_read_only = status.ReadWriteFlagUncryptedVolume_u8 != 0; + out->unencrypted_volume_active = status.VolumeActiceFlag_st.unencrypted; + out->encrypted_volume_read_only = status.ReadWriteFlagCryptedVolume_u8 != 0; + out->encrypted_volume_active = status.VolumeActiceFlag_st.encrypted; + out->hidden_volume_read_only = status.ReadWriteFlagHiddenVolume_u8 != 0; + out->hidden_volume_active = status.VolumeActiceFlag_st.hidden; + out->firmware_version_major = status.versionInfo.major; + out->firmware_version_minor = status.versionInfo.minor; + out->firmware_locked = status.FirmwareLocked_u8 != 0; + out->serial_number_sd_card = status.ActiveSD_CardID_u32; + out->serial_number_smart_card = status.ActiveSmartCardID_u32; + out->user_retry_count = status.UserPwRetryCount; + out->admin_retry_count = status.AdminPwRetryCount; + out->new_sd_card_found = status.NewSDCardFound_st.NewCard; + out->filled_with_random = (status.SDFillWithRandomChars_u8 & 0x01) != 0; + out->stick_initialized = status.StickKeysNotInitiated == 0; + return 0; + } + + NK_C_API int NK_get_storage_production_info(NK_storage_ProductionTest * out){ + if (out == nullptr) { + return -1; + } + auto m = NitrokeyManager::instance(); + auto result = get_with_status([&]() { + return m->production_info(); + }, proto::stick20::ProductionTest::ResponsePayload()); + + auto error_code = std::get<0>(result); + if (error_code != 0) { + return error_code; + } + + stick20::ProductionTest::ResponsePayload status = std::get<1>(result); + // Cannot use memcpy without declaring C API struct packed + // (which is not parsed by Python's CFFI apparently), hence the manual way. +#define a(x) out->x = status.x; + a(FirmwareVersion_au8[0]); + a(FirmwareVersion_au8[1]); + a(FirmwareVersionInternal_u8); + a(SD_Card_Size_u8); + a(CPU_CardID_u32); + a(SmartCardID_u32); + a(SD_CardID_u32); + a(SC_UserPwRetryCount); + a(SC_AdminPwRetryCount); + a(SD_Card_ManufacturingYear_u8); + a(SD_Card_ManufacturingMonth_u8); + a(SD_Card_OEM_u16); + a(SD_WriteSpeed_u16); + a(SD_Card_Manufacturer_u8); +#undef a + return 0; + } + + NK_C_API int NK_get_SD_usage_data(struct NK_SD_usage_data* out) { + if (out == nullptr) + return -1; + auto m = NitrokeyManager::instance(); + auto result = get_with_status([&]() { + return m->get_SD_usage_data(); + }, std::make_pair(0, 0)); + auto error_code = std::get<0>(result); + if (error_code != 0) + return error_code; + + auto data = std::get<1>(result); + out->write_level_min = std::get<0>(data); + out->write_level_max = std::get<1>(data); + + return 0; + } + +NK_C_API char* NK_get_SD_usage_data_as_string() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_SD_usage_data_as_string(); + }); + } + + NK_C_API int NK_get_progress_bar_value() { + auto m = NitrokeyManager::instance(); + return std::get<1>(get_with_status([&]() { + return m->get_progress_bar_value(); + }, -2)); + } + + NK_C_API uint8_t NK_get_major_firmware_version() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_major_firmware_version(); + }); + } + + NK_C_API uint8_t NK_get_minor_firmware_version() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_minor_firmware_version(); + }); + } + + NK_C_API int NK_set_unencrypted_volume_rorw_pin_type_user() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->set_unencrypted_volume_rorw_pin_type_user() ? 1 : 0; + }); + } + + NK_C_API char* NK_list_devices_by_cpuID() { + auto nm = NitrokeyManager::instance(); + return get_with_string_result([&]() { + auto v = nm->list_devices_by_cpuID(); + std::string res; + for (const auto a : v){ + res += a+";"; + } + if (res.size()>0) res.pop_back(); // remove last delimiter char + return strndup(res.c_str(), MAXIMUM_STR_REPLY_LENGTH); + }); + } + + bool copy_device_info(const DeviceInfo& source, NK_device_info* target) { + switch (source.m_deviceModel) { + case DeviceModel::PRO: + target->model = NK_PRO; + break; + case DeviceModel::STORAGE: + target->model = NK_STORAGE; + break; + default: + return false; + } + + target->path = strndup(source.m_path.c_str(), MAXIMUM_STR_REPLY_LENGTH); + target->serial_number = strndup(source.m_serialNumber.c_str(), MAXIMUM_STR_REPLY_LENGTH); + target->next = nullptr; + + return target->path && target->serial_number; + } + + NK_C_API struct NK_device_info* NK_list_devices() { + auto nm = NitrokeyManager::instance(); + return get_with_result([&]() -> NK_device_info* { + auto v = nm->list_devices(); + if (v.empty()) + return nullptr; + + auto result = new NK_device_info(); + auto ptr = result; + auto first = v.begin(); + if (!copy_device_info(*first, ptr)) { + NK_free_device_info(result); + return nullptr; + } + v.erase(first); + + for (auto& info : v) { + ptr->next = new NK_device_info(); + ptr = ptr->next; + + if (!copy_device_info(info, ptr)) { + NK_free_device_info(result); + return nullptr; + } + } + return result; + }); + } + + NK_C_API void NK_free_device_info(struct NK_device_info* device_info) { + if (!device_info) + return; + + if (device_info->next) + NK_free_device_info(device_info->next); + + free(device_info->path); + free(device_info->serial_number); + delete device_info; + } + + NK_C_API int NK_connect_with_ID(const char* id) { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->connect_with_ID(id) ? 1 : 0; + }); + } + + NK_C_API int NK_connect_with_path(const char* path) { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->connect_with_path(path) ? 1 : 0; + }); + } + + + NK_C_API int NK_wink() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + return m->wink(); + }); + } + + NK_C_API int NK_enable_firmware_update_pro(const char* update_password){ + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->enable_firmware_update_pro(update_password); + }); +} + + NK_C_API int NK_change_firmware_password_pro(const char *current_firmware_password, const char *new_firmware_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_firmware_update_password_pro(current_firmware_password, + new_firmware_password); + }); + } + + + NK_C_API int NK_read_HOTP_slot(const uint8_t slot_num, struct ReadSlot_t* out){ + if (out == nullptr) + return -1; + auto m = NitrokeyManager::instance(); + auto result = get_with_status([&]() { + return m->get_HOTP_slot_data(slot_num); + }, stick10::ReadSlot::ResponsePayload() ); + auto error_code = std::get<0>(result); + if (error_code != 0) { + return error_code; + } +#define a(x) out->x = read_slot.x + stick10::ReadSlot::ResponsePayload read_slot = std::get<1>(result); + a(_slot_config); + a(slot_counter); +#undef a +#define m(x) memmove(out->x, read_slot.x, sizeof(read_slot.x)) + m(slot_name); + m(slot_token_id); +#undef m + return 0; +} + + +#ifdef __cplusplus +} +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/NK_C_API.h b/nitrokey-sys/libnitrokey-v3.5/NK_C_API.h new file mode 100644 index 0000000..d5c54a3 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/NK_C_API.h @@ -0,0 +1,1014 @@ +/* + * 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 + */ + +#ifndef LIBNITROKEY_NK_C_API_H +#define LIBNITROKEY_NK_C_API_H + +#include +#include + +#include "deprecated.h" + +#ifdef _MSC_VER +#define NK_C_API __declspec(dllexport) +#else +#define NK_C_API +#endif + +/** + * \file + * + * C API for libnitrokey + * + * \mainpage + * + * **libnitrokey** provides access to Nitrokey Pro and Nitrokey Storage devices. + * This documentation describes libnitrokey’s C API. For a list of the + * available functions, see the NK_C_API.h file. + * + * \section getting_started Example + * + * \code{.c} + * #include + * #include + * #include + * + * int main(void) + * { + * if (NK_login_auto() != 1) { + * fprintf(stderr, "No Nitrokey found.\n"); + * return 1; + * } + * + * NK_device_model model = NK_get_device_model(); + * printf("Connected to "); + * switch (model) { + * case NK_PRO: + * printf("a Nitrokey Pro"); + * break; + * case NK_STORAGE: + * printf("a Nitrokey Storage"); + * break; + * default: + * printf("an unsupported Nitrokey"); + * break; + * } + * + * char* serial_number = NK_device_serial_number(); + * if (serial_number) + * printf(" with serial number %s\n", serial_number); + * else + * printf(" -- could not query serial number!\n"); + * free(serial_number); + * + * NK_logout(); + * return 0; + * } + * \endcode + */ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * The number of slots in the password safe. + */ + extern const uint8_t NK_PWS_SLOT_COUNT; + + static const int MAXIMUM_STR_REPLY_LENGTH = 8192; + + /** + * The Nitrokey device models supported by the API. + */ + enum NK_device_model { + /** + * Use, if no supported device is connected + */ + NK_DISCONNECTED = 0, + /** + * Nitrokey Pro. + */ + NK_PRO = 1, + /** + * Nitrokey Storage. + */ + NK_STORAGE = 2 + }; + + /** + * The connection info for a Nitrokey device as a linked list. + */ + struct NK_device_info { + /** + * The model of the Nitrokey device. + */ + enum NK_device_model model; + /** + * The USB device path for NK_connect_with_path. + */ + char* path; + /** + * The serial number. + */ + char* serial_number; + /** + * The pointer to the next element of the linked list or null + * if this is the last element in the list. + */ + struct NK_device_info* next; + }; + + /** + * Stores the common device status for all Nitrokey devices. + */ + struct NK_status { + /** + * The major firmware version, e. g. 0 in v0.40. + */ + uint8_t firmware_version_major; + /** + * The minor firmware version, e. g. 40 in v0.40. + */ + uint8_t firmware_version_minor; + /** + * The serial number of the smart card. + */ + uint32_t serial_number_smart_card; + /** + * The HOTP slot to generate a password from if the numlock + * key is pressed twice (slot 0-1, or any other value to + * disable the function). + */ + uint8_t config_numlock; + /** + * The HOTP slot to generate a password from if the capslock + * key is pressed twice (slot 0-1, or any other value to + * disable the function). + */ + uint8_t config_capslock; + /** + * The HOTP slot to generate a password from if the scrolllock + * key is pressed twice (slot 0-1, or any other value to + * disable the function). + */ + uint8_t config_scrolllock; + /** + * Indicates whether the user password is required to generate + * an OTP value. + */ + bool otp_user_password; + }; + + /** + * Stores the status of a Storage device. + */ + struct NK_storage_status { + /** + * Indicates whether the unencrypted volume is read-only. + */ + bool unencrypted_volume_read_only; + /** + * Indicates whether the unencrypted volume is active. + */ + bool unencrypted_volume_active; + /** + * Indicates whether the encrypted volume is read-only. + */ + bool encrypted_volume_read_only; + /** + * Indicates whether the encrypted volume is active. + */ + bool encrypted_volume_active; + /** + * Indicates whether the hidden volume is read-only. + */ + bool hidden_volume_read_only; + /** + * Indicates whether the hidden volume is active. + */ + bool hidden_volume_active; + /** + * The major firmware version, e. g. 0 in v0.40. + */ + uint8_t firmware_version_major; + /** + * The minor firmware version, e. g. 40 in v0.40. + */ + uint8_t firmware_version_minor; + /** + * Indicates whether the firmware is locked. + */ + bool firmware_locked; + /** + * The serial number of the SD card in the Storage stick. + */ + uint32_t serial_number_sd_card; + /** + * The serial number of the smart card in the Storage stick. + */ + uint32_t serial_number_smart_card; + /** + * The number of remaining login attempts for the user PIN. + */ + uint8_t user_retry_count; + /** + * The number of remaining login attempts for the admin PIN. + */ + uint8_t admin_retry_count; + /** + * Indicates whether a new SD card was found. + */ + bool new_sd_card_found; + /** + * Indicates whether the SD card is filled with random characters. + */ + bool filled_with_random; + /** + * Indicates whether the stick has been initialized by generating + * the AES keys. + */ + bool stick_initialized; + }; + + /** + * Data about the usage of the SD card. + */ + struct NK_SD_usage_data { + /** + * The minimum write level, as a percentage of the total card + * size. + */ + uint8_t write_level_min; + /** + * The maximum write level, as a percentage of the total card + * size. + */ + uint8_t write_level_max; + }; + + + struct NK_storage_ProductionTest{ + uint8_t FirmwareVersion_au8[2]; + uint8_t FirmwareVersionInternal_u8; + uint8_t SD_Card_Size_u8; + uint32_t CPU_CardID_u32; + uint32_t SmartCardID_u32; + uint32_t SD_CardID_u32; + uint8_t SC_UserPwRetryCount; + uint8_t SC_AdminPwRetryCount; + uint8_t SD_Card_ManufacturingYear_u8; + uint8_t SD_Card_ManufacturingMonth_u8; + uint16_t SD_Card_OEM_u16; + uint16_t SD_WriteSpeed_u16; + uint8_t SD_Card_Manufacturer_u8; + }; + + NK_C_API int NK_get_storage_production_info(struct NK_storage_ProductionTest * out); + + +/** + * Set debug level of messages written on stderr + * @param state state=True - most messages, state=False - only errors level + */ + NK_C_API void NK_set_debug(bool state); + + /** + * Set debug level of messages written on stderr + * @param level (int) 0-lowest verbosity, 5-highest verbosity + */ + NK_C_API void NK_set_debug_level(const int level); + + /** + * Get the major library version, e. g. the 3 in v3.2. + * @return the major library version + */ + NK_C_API unsigned int NK_get_major_library_version(); + + /** + * Get the minor library version, e. g. the 2 in v3.2. + * @return the minor library version + */ + NK_C_API unsigned int NK_get_minor_library_version(); + + /** + * Get the library version as a string. This is the output of + * `git describe --always` at compile time, for example "v3.3" or + * "v3.3-19-gaee920b". + * The return value is a string literal and must not be freed. + * @return the library version as a string + */ + NK_C_API const char* NK_get_library_version(); + + /** + * Connect to device of given model. Currently library can be connected only to one device at once. + * @param device_model char 'S': Nitrokey Storage, 'P': Nitrokey Pro + * @return 1 if connected, 0 if wrong model or cannot connect + */ + NK_C_API int NK_login(const char *device_model); + + /** + * Connect to device of given model. Currently library can be connected only to one device at once. + * @param device_model NK_device_model: NK_PRO: Nitrokey Pro, NK_STORAGE: Nitrokey Storage + * @return 1 if connected, 0 if wrong model or cannot connect + */ + NK_C_API int NK_login_enum(enum NK_device_model device_model); + + /** + * Connect to first available device, starting checking from Pro 1st to Storage 2nd. + * @return 1 if connected, 0 if wrong model or cannot connect + */ + NK_C_API int NK_login_auto(); + + /** + * Disconnect from the device. + * @return command processing error code + */ + NK_C_API int NK_logout(); + + /** + * Query the model of the connected device. + * Returns the model of the connected device or NK_DISCONNECTED. + * + * @return true if a device is connected and the out argument has been set + */ + NK_C_API enum NK_device_model NK_get_device_model(); + + /** + * Return the debug status string. Debug purposes. This function is + * deprecated in favor of NK_get_status_as_string. + * @return string representation of the status or an empty string + * if the command failed + */ + DEPRECATED + NK_C_API char * NK_status(); + + /** + * Return the debug status string. Debug purposes. + * @return string representation of the status or an empty string + * if the command failed + */ + NK_C_API char * NK_get_status_as_string(); + + /** + * Get the stick status common to all Nitrokey devices and return the + * command processing error code. If the code is zero, i. e. the + * command was successful, the storage status is written to the output + * pointer's target. The output pointer must not be null. + * + * @param out the output pointer for the status + * @return command processing error code + */ + NK_C_API int NK_get_status(struct NK_status* out); + + /** + * Return the device's serial number string in hex. + * @return string device's serial number in hex + */ + NK_C_API char * NK_device_serial_number(); + + /** + * Get last command processing status. Useful for commands which returns the results of their own and could not return + * an error code. + * @return previous command processing error code + */ + NK_C_API uint8_t NK_get_last_command_status(); + + /** + * Lock device - cancel any user device unlocking. + * @return command processing error code + */ + NK_C_API int NK_lock_device(); + + /** + * Authenticates the user on USER privilages with user_password and sets user's temporary password on device to user_temporary_password. + * @param user_password char[25] current user password + * @param user_temporary_password char[25] user temporary password to be set on device for further communication (authentication command) + * @return command processing error code + */ + NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password); + + /** + * Authenticates the user on ADMIN privilages with admin_password and sets user's temporary password on device to admin_temporary_password. + * @param admin_password char[25] current administrator PIN + * @param admin_temporary_password char[25] admin temporary password to be set on device for further communication (authentication command) + * @return command processing error code + */ + NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password); + + /** + * Execute a factory reset. + * @param admin_password char[20] current administrator PIN + * @return command processing error code + */ + NK_C_API int NK_factory_reset(const char* admin_password); + + /** + * Generates AES key on the device + * @param admin_password char[20] current administrator PIN + * @return command processing error code + */ + NK_C_API int NK_build_aes_key(const char* admin_password); + + /** + * Unlock user PIN locked after 3 incorrect codes tries. + * @param admin_password char[20] current administrator PIN + * @return command processing error code + */ + NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password); + + /** + * Write general config to the device + * @param numlock set value in range [0-1] to send HOTP code from slot 'numlock' after double pressing numlock + * or outside the range to disable this function + * @param capslock similar to numlock but with capslock + * @param scrolllock similar to numlock but with scrolllock + * @param enable_user_password set True to enable OTP PIN protection (require PIN each OTP code request) + * @param delete_user_password (unused) + * @param admin_temporary_password current admin temporary password + * @return command processing error code + */ + NK_C_API int 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); + + /** + * Get currently set config - status of function Numlock/Capslock/Scrollock OTP sending and is enabled PIN protected OTP + * @see NK_write_config + * @return uint8_t general_config[5]: + * uint8_t numlock; + uint8_t capslock; + uint8_t scrolllock; + uint8_t enable_user_password; + uint8_t delete_user_password; + + */ + NK_C_API uint8_t* NK_read_config(); + + //OTP + + /** + * Get name of given TOTP slot + * @param slot_number TOTP slot number, slot_number<15 + * @return char[20] the name of the slot + */ + NK_C_API char * NK_get_totp_slot_name(uint8_t slot_number); + + /** + * + * @param slot_number HOTP slot number, slot_number<3 + * @return char[20] the name of the slot + */ + NK_C_API char * NK_get_hotp_slot_name(uint8_t slot_number); + + /** + * Erase HOTP slot data from the device + * @param slot_number HOTP slot number, slot_number<3 + * @param temporary_password admin temporary password + * @return command processing error code + */ + NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password); + + /** + * Erase TOTP slot data from the device + * @param slot_number TOTP slot number, slot_number<15 + * @param temporary_password admin temporary password + * @return command processing error code + */ + NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password); + + /** + * Write HOTP slot data to the device + * @param slot_number HOTP slot number, slot_number<3, 0-numbered + * @param slot_name char[15] desired slot name. C string (requires ending '\0'; 16 bytes). + * @param secret char[40] 160-bit or 320-bit (currently Pro v0.8 only) secret as a hex string. C string (requires ending '\0'; 41 bytes). + * See NitrokeyManager::is_320_OTP_secret_supported. + * @param hotp_counter uint32_t starting value of HOTP counter + * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) + * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock + * @param use_tokenID @see token_ID + * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section + * @param temporary_password char[25] admin temporary password + * @return command processing error code + */ + NK_C_API int 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); + + /** + * Write TOTP slot data to the device + * @param slot_number TOTP slot number, slot_number<15, 0-numbered + * @param slot_name char[15] desired slot name. C string (requires ending '\0'; 16 bytes). + * @param secret char[40] 160-bit or 320-bit (currently Pro v0.8 only) secret as a hex string. C string (requires ending '\0'; 41 bytes). + * See NitrokeyManager::is_320_OTP_secret_supported. + * @param time_window uint16_t time window for this TOTP + * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) + * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock + * @param use_tokenID @see token_ID + * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section + * @param temporary_password char[20] admin temporary password + * @return command processing error code + */ + NK_C_API int 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); + + /** + * Get HOTP code from the device + * @param slot_number HOTP slot number, slot_number<3 + * @return HOTP code + */ + NK_C_API char * NK_get_hotp_code(uint8_t slot_number); + + /** + * Get HOTP code from the device (PIN protected) + * @param slot_number HOTP slot number, slot_number<3 + * @param user_temporary_password char[25] user temporary password if PIN protected OTP codes are enabled, + * otherwise should be set to empty string - '' + * @return HOTP code + */ + NK_C_API char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password); + + /** + * Get TOTP code from the device + * @param slot_number TOTP slot number, slot_number<15 + * @param challenge TOTP challenge -- unused + * @param last_totp_time last time -- unused + * @param last_interval last interval --unused + * @return TOTP code + */ + NK_C_API char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval); + + /** + * Get TOTP code from the device (PIN protected) + * @param slot_number TOTP slot number, slot_number<15 + * @param challenge TOTP challenge -- unused + * @param last_totp_time last time -- unused + * @param last_interval last interval -- unused + * @param user_temporary_password char[25] user temporary password if PIN protected OTP codes are enabled, + * otherwise should be set to empty string - '' + * @return TOTP code + */ + NK_C_API char * 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); + + /** + * Set time on the device (for TOTP requests) + * @param time seconds in unix epoch (from 01.01.1970) + * @return command processing error code + */ + NK_C_API int NK_totp_set_time(uint64_t time); + + /** + * Set the device time used for TOTP to the given time. Contrary to + * {@code set_time(uint64_t)}, this command fails if {@code old_time} + * > {@code time} or if {@code old_time} is zero (where {@code + * old_time} is the current time on the device). + * + * @param time new device time as Unix timestamp (seconds since + * 1970-01-01) + * @return command processing error code + */ + NK_C_API int NK_totp_set_time_soft(uint64_t time); + + // NK_totp_get_time is deprecated -- use NK_totp_set_time_soft instead + DEPRECATED + NK_C_API int NK_totp_get_time(); + + //passwords + /** + * Change administrator PIN + * @param current_PIN char[25] current PIN + * @param new_PIN char[25] new PIN + * @return command processing error code + */ + NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN); + + /** + * Change user PIN + * @param current_PIN char[25] current PIN + * @param new_PIN char[25] new PIN + * @return command processing error code + */ + NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN); + + + /** + * Get retry count of user PIN + * @return user PIN retry count + */ + NK_C_API uint8_t NK_get_user_retry_count(); + + /** + * Get retry count of admin PIN + * @return admin PIN retry count + */ + NK_C_API uint8_t NK_get_admin_retry_count(); + //password safe + + /** + * Enable password safe access + * @param user_pin char[30] current user PIN + * @return command processing error code + */ + NK_C_API int NK_enable_password_safe(const char *user_pin); + + /** + * Get password safe slots' status + * @return uint8_t[16] slot statuses - each byte represents one slot with 0 (not programmed) and 1 (programmed) + */ + NK_C_API uint8_t * NK_get_password_safe_slot_status(); + + /** + * Get password safe slot name + * @param slot_number password safe slot number, slot_number<16 + * @return slot name + */ + NK_C_API char *NK_get_password_safe_slot_name(uint8_t slot_number); + + /** + * Get password safe slot login + * @param slot_number password safe slot number, slot_number<16 + * @return login from the PWS slot + */ + NK_C_API char *NK_get_password_safe_slot_login(uint8_t slot_number); + + /** + * Get the password safe slot password + * @param slot_number password safe slot number, slot_number<16 + * @return password from the PWS slot + */ + NK_C_API char *NK_get_password_safe_slot_password(uint8_t slot_number); + + /** + * Write password safe data to the slot + * @param slot_number password safe slot number, slot_number<16 + * @param slot_name char[11] name of the slot + * @param slot_login char[32] login string + * @param slot_password char[20] password string + * @return command processing error code + */ + NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, + const char *slot_login, const char *slot_password); + + /** + * Erase the password safe slot from the device + * @param slot_number password safe slot number, slot_number<16 + * @return command processing error code + */ + NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number); + + /** + * Check whether AES is supported by the device + * @return 0 for no and 1 for yes + */ + NK_C_API int NK_is_AES_supported(const char *user_password); + + /** + * Get device's major firmware version + * @return major part of the version number (e.g. 0 from 0.48, 0 from 0.7 etc.) + */ + NK_C_API uint8_t NK_get_major_firmware_version(); + + /** + * Get device's minor firmware version + * @return minor part of the version number (e.g. 7 from 0.7, 48 from 0.48 etc.) + */ + NK_C_API uint8_t NK_get_minor_firmware_version(); + + /** + * Function to determine unencrypted volume PIN type + * @param minor_firmware_version + * @return Returns 1, if set unencrypted volume ro/rw pin type is User, 0 otherwise. + */ + NK_C_API int NK_set_unencrypted_volume_rorw_pin_type_user(); + + + /** + * This command is typically run to initiate + * communication with the device (altough not required). + * It sets time on device and returns its current status + * - a combination of set_time and get_status_storage commands + * Storage only + * @param seconds_from_epoch date and time expressed in seconds + */ + NK_C_API int NK_send_startup(uint64_t seconds_from_epoch); + + /** + * Unlock encrypted volume. + * Storage only + * @param user_pin user pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_unlock_encrypted_volume(const char* user_pin); + + /** + * Locks encrypted volume + * @return command processing error code + */ + NK_C_API int NK_lock_encrypted_volume(); + + /** + * Unlock hidden volume and lock encrypted volume. + * Requires encrypted volume to be unlocked. + * Storage only + * @param hidden_volume_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password); + + /** + * Locks hidden volume + * @return command processing error code + */ + NK_C_API int NK_lock_hidden_volume(); + + /** + * Create hidden volume. + * Requires encrypted volume to be unlocked. + * Storage only + * @param slot_nr slot number in range 0-3 + * @param start_percent volume begin expressed in percent of total available storage, int in range 0-99 + * @param end_percent volume end expressed in percent of total available storage, int in range 1-100 + * @param hidden_volume_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, + const char *hidden_volume_password); + + /** + * Make unencrypted volume read-only. + * Device hides unencrypted volume for a second therefore make sure + * buffers are flushed before running. + * Does nothing if firmware version is not matched + * Firmware range: Storage v0.50, v0.48 and below + * Storage only + * @param user_pin 20 characters User PIN + * @return command processing error code + */ + NK_C_API int NK_set_unencrypted_read_only(const char *user_pin); + + /** + * Make unencrypted volume read-write. + * Device hides unencrypted volume for a second therefore make sure + * buffers are flushed before running. + * Does nothing if firmware version is not matched + * Firmware range: Storage v0.50, v0.48 and below + * Storage only + * @param user_pin 20 characters User PIN + * @return command processing error code + */ + NK_C_API int NK_set_unencrypted_read_write(const char *user_pin); + + /** + * Make unencrypted volume read-only. + * Device hides unencrypted volume for a second therefore make sure + * buffers are flushed before running. + * Does nothing if firmware version is not matched + * Firmware range: Storage v0.49, v0.51+ + * Storage only + * @param admin_pin 20 characters Admin PIN + * @return command processing error code + */ + NK_C_API int NK_set_unencrypted_read_only_admin(const char* admin_pin); + + /** + * Make unencrypted volume read-write. + * Device hides unencrypted volume for a second therefore make sure + * buffers are flushed before running. + * Does nothing if firmware version is not matched + * Firmware range: Storage v0.49, v0.51+ + * Storage only + * @param admin_pin 20 characters Admin PIN + * @return command processing error code + */ + NK_C_API int NK_set_unencrypted_read_write_admin(const char* admin_pin); + + /** + * Make encrypted volume read-only. + * Device hides encrypted volume for a second therefore make sure + * buffers are flushed before running. + * Firmware range: v0.49 only, future (see firmware release notes) + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_set_encrypted_read_only(const char* admin_pin); + + /** + * Make encrypted volume read-write. + * Device hides encrypted volume for a second therefore make sure + * buffers are flushed before running. + * Firmware range: v0.49 only, future (see firmware release notes) + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_set_encrypted_read_write(const char* admin_pin); + + /** + * Exports device's firmware to unencrypted volume. + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_export_firmware(const char* admin_pin); + + /** + * Clear new SD card notification. It is set after factory reset. + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin); + + /** + * Fill SD card with random data. + * Should be done on first stick initialization after creating keys. + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin); + + /** + * Change update password. + * Update password is used for entering update mode, where firmware + * could be uploaded using dfu-programmer or other means. + * Storage only + * @param current_update_password 20 characters + * @param new_update_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_change_update_password(const char* current_update_password, + const char* new_update_password); + + /** + * Enter update mode. Needs update password. + * When device is in update mode it no longer accepts any HID commands until + * firmware is launched (regardless of being updated or not). + * Smartcard (through CCID interface) and its all volumes are not visible as well. + * Its VID and PID are changed to factory-default (03eb:2ff1 Atmel Corp.) + * to be detected by flashing software. Result of this command can be reversed + * by using 'launch' command. + * For dfu-programmer it would be: 'dfu-programmer at32uc3a3256s launch'. + * Storage only + * @param update_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_enable_firmware_update(const char* update_password); + + /** + * Get Storage stick status as string. + * Storage only + * @return string with devices attributes + */ + NK_C_API char* NK_get_status_storage_as_string(); + + /** + * Get the Storage stick status and return the command processing + * error code. If the code is zero, i. e. the command was successful, + * the storage status is written to the output pointer's target. + * The output pointer must not be null. + * + * @param out the output pointer for the storage status + * @return command processing error code + */ + NK_C_API int NK_get_status_storage(struct NK_storage_status* out); + + /** + * Get SD card usage attributes. Usable during hidden volumes creation. + * If the command was successful (return value 0), the usage data is + * written to the output pointer’s target. The output pointer must + * not be null. + * Storage only + * @param out the output pointer for the usage data + * @return command processing error code + */ + NK_C_API int NK_get_SD_usage_data(struct NK_SD_usage_data* out); + + /** + * Get SD card usage attributes as string. + * Usable during hidden volumes creation. + * Storage only + * @return string with SD card usage attributes + */ + NK_C_API char* NK_get_SD_usage_data_as_string(); + + /** + * Get progress value of current long operation. + * Storage only + * @return int in range 0-100 or -1 if device is not busy or -2 if an + * error occured + */ + NK_C_API int NK_get_progress_bar_value(); + +/** + * Returns a list of connected devices' id's, delimited by ';' character. Empty string is returned on no device found. + * Each ID could consist of: + * 1. SC_id:SD_id_p_path (about 40 bytes) + * 2. path (about 10 bytes) + * where 'path' is USB path (bus:num), 'SC_id' is smartcard ID, 'SD_id' is storage card ID and + * '_p_' and ':' are field delimiters. + * Case 2 (USB path only) is used, when the device cannot be asked about its status data (e.g. during a long operation, + * like clearing SD card. + * Internally connects to all available devices and creates a map between ids and connection objects. + * Side effects: changes active device to last detected Storage device. + * Storage only + * @example Example of returned data: '00005d19:dacc2cb4_p_0001:0010:02;000037c7:4cf12445_p_0001:000f:02;0001:000c:02' + * @return string delimited id's of connected devices + */ + NK_C_API char* NK_list_devices_by_cpuID(); + + /** + * Returns a linked list of all connected devices, or null if no devices + * are connected or an error occured. The linked list must be freed by + * calling NK_free_device_info. + * @return a linked list of all connected devices + */ + NK_C_API struct NK_device_info* NK_list_devices(); + + /** + * Free a linked list returned by NK_list_devices. + * @param the linked list to free or null + */ + NK_C_API void NK_free_device_info(struct NK_device_info* device_info); + +/** + * Connects to the device with given ID. ID's list could be created with NK_list_devices_by_cpuID. + * Requires calling to NK_list_devices_by_cpuID first. Connecting to arbitrary ID/USB path is not handled. + * On connection requests status from device and disconnects it / removes from map on connection failure. + * Storage only + * @param id Target device ID (example: '00005d19:dacc2cb4_p_0001:0010:02') + * @return 1 on successful connection, 0 otherwise + */ + NK_C_API int NK_connect_with_ID(const char* id); + + /** + * Connects to a device with the given path. The path is a USB device + * path as returned by hidapi. + * @param path the device path + * @return 1 on successful connection, 0 otherwise + */ + NK_C_API int NK_connect_with_path(const char* path); + + /** + * Blink red and green LED alternatively and infinitely (until device is reconnected). + * @return command processing error code + */ + NK_C_API int NK_wink(); + + + /** + * Enable update mode on Nitrokey Pro. + * Supported from v0.11. + * @param update_password 20 bytes update password + * @return command processing error code + */ + NK_C_API int NK_enable_firmware_update_pro(const char* update_password); + + /** + * Change update-mode password on Nitrokey Pro. + * Supported from v0.11. + * @param current_firmware_password 20 bytes update password + * @param new_firmware_password 20 bytes update password + * @return command processing error code + */ + NK_C_API int NK_change_firmware_password_pro(const char *current_firmware_password, const char *new_firmware_password); + + +// as in ReadSlot::ResponsePayload +struct ReadSlot_t { + uint8_t slot_name[15]; + uint8_t _slot_config; + uint8_t slot_token_id[13]; + uint64_t slot_counter; +}; + + +NK_C_API int NK_read_HOTP_slot(const uint8_t slot_num, struct ReadSlot_t* out); + +#ifdef __cplusplus +} +#endif + +#endif //LIBNITROKEY_NK_C_API_H diff --git a/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc b/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc new file mode 100644 index 0000000..6c26a43 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc @@ -0,0 +1,1199 @@ +/* + * 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 + */ + +#include +#include +#include "libnitrokey/NitrokeyManager.h" +#include "libnitrokey/LibraryException.h" +#include +#include +#include +#include "libnitrokey/misc.h" +#include +#include "libnitrokey/cxx_semantics.h" +#include "libnitrokey/misc.h" +#include +#include + +std::mutex nitrokey::proto::send_receive_mtx; + +namespace nitrokey{ + + std::mutex mex_dev_com_manager; + +#ifndef strndup +#ifdef _WIN32 +#pragma message "Using own strndup" +char * strndup(const char* str, size_t maxlen){ + size_t len = strnlen(str, maxlen); + char* dup = (char *) malloc(len + 1); + memcpy(dup, str, len); + dup[len] = 0; + return dup; +} +#endif +#endif + +using nitrokey::misc::strcpyT; + + template + typename T::CommandPayload get_payload(){ + //Create, initialize and return by value command payload + typename T::CommandPayload st; + bzero(&st, sizeof(st)); + return st; + } + + + // package type to auth, auth type [Authorize,UserAuthorize] + template + void NitrokeyManager::authorize_packet(T &package, const char *admin_temporary_password, shared_ptr device){ + if (!is_authorization_command_supported()){ + LOG("Authorization command not supported, skipping", Loglevel::WARNING); + } + auto auth = get_payload(); + strcpyT(auth.temporary_password, admin_temporary_password); + auth.crc_to_authorize = S::CommandTransaction::getCRC(package); + A::CommandTransaction::run(device, auth); + } + + shared_ptr NitrokeyManager::_instance = nullptr; + + NitrokeyManager::NitrokeyManager() : device(nullptr) + { + set_debug(false); + } + NitrokeyManager::~NitrokeyManager() { + std::lock_guard lock(mex_dev_com_manager); + + for (auto d : connected_devices){ + if (d.second == nullptr) continue; + d.second->disconnect(); + connected_devices[d.first] = nullptr; + } + } + + bool NitrokeyManager::set_current_device_speed(int retry_delay, int send_receive_delay){ + if (retry_delay < 20 || send_receive_delay < 20){ + LOG("Delay set too low: " + to_string(retry_delay) +" "+ to_string(send_receive_delay), Loglevel::WARNING); + return false; + } + + std::lock_guard lock(mex_dev_com_manager); + if(device == nullptr) { + return false; + } + device->set_receiving_delay(std::chrono::duration(send_receive_delay)); + device->set_retry_delay(std::chrono::duration(retry_delay)); + return true; + } + + std::vector NitrokeyManager::list_devices(){ + std::lock_guard lock(mex_dev_com_manager); + + return Device::enumerate(); + } + + std::vector NitrokeyManager::list_devices_by_cpuID(){ + using misc::toHex; + //disconnect default device + disconnect(); + + std::lock_guard lock(mex_dev_com_manager); + LOGD1("Disconnecting registered devices"); + for (auto & kv : connected_devices_byID){ + if (kv.second != nullptr) + kv.second->disconnect(); + } + connected_devices_byID.clear(); + + LOGD1("Enumerating devices"); + std::vector res; + const auto v = Device::enumerate(); + LOGD1("Discovering IDs"); + for (auto & i: v){ + if (i.m_deviceModel != DeviceModel::STORAGE) + continue; + auto p = i.m_path; + auto d = make_shared(); + LOGD1( std::string("Found: ") + p ); + d->set_path(p); + try{ + if (d->connect()){ + device = d; + std::string id; + try { + const auto status = get_status_storage(); + const auto sc_id = toHex(status.ActiveSmartCardID_u32); + const auto sd_id = toHex(status.ActiveSD_CardID_u32); + id += sc_id + ":" + sd_id; + id += "_p_" + p; + } + catch (const LongOperationInProgressException &e) { + LOGD1(std::string("Long operation in progress, setting ID to: ") + p); + id = p; + } + + connected_devices_byID[id] = d; + res.push_back(id); + LOGD1( std::string("Found: ") + p + " => " + id); + } else{ + LOGD1( std::string("Could not connect to: ") + p); + } + } + catch (const DeviceCommunicationException &e){ + LOGD1( std::string("Exception encountered: ") + p); + } + } + return res; + } + + bool NitrokeyManager::connect_with_ID(const std::string id) { + std::lock_guard lock(mex_dev_com_manager); + + auto position = connected_devices_byID.find(id); + if (position == connected_devices_byID.end()) { + LOGD1(std::string("Could not find device ")+id + ". Refresh devices list with list_devices_by_cpuID()."); + return false; + } + + auto d = connected_devices_byID[id]; + device = d; + current_device_id = id; + + //validate connection + try{ + get_status(); + } + catch (const LongOperationInProgressException &){ + //ignore + } + catch (const DeviceCommunicationException &){ + d->disconnect(); + current_device_id = ""; + connected_devices_byID[id] = nullptr; + connected_devices_byID.erase(position); + return false; + } + nitrokey::log::Log::setPrefix(id); + LOGD1("Device successfully changed"); + return true; + } + + /** + * Connects device to path. + * Assumes devices are not being disconnected and caches connections (param cache_connections). + * @param path os-dependent device path + * @return false, when could not connect, true otherwise + */ + bool NitrokeyManager::connect_with_path(std::string path) { + const bool cache_connections = false; + + std::lock_guard lock(mex_dev_com_manager); + + if (cache_connections){ + if(connected_devices.find(path) != connected_devices.end() + && connected_devices[path] != nullptr) { + device = connected_devices[path]; + return true; + } + } + + auto info_ptr = hid_enumerate(NITROKEY_VID, 0); + auto first_info_ptr = info_ptr; + if (!info_ptr) + return false; + + misc::Option model; + while (info_ptr && !model.has_value()) { + if (path == std::string(info_ptr->path)) { + model = product_id_to_model(info_ptr->product_id); + } + info_ptr = info_ptr->next; + } + hid_free_enumeration(first_info_ptr); + + if (!model.has_value()) + return false; + + auto p = Device::create(model.value()); + if (!p) + return false; + p->set_path(path); + + if(!p->connect()) return false; + + if(cache_connections){ + connected_devices [path] = p; + } + + device = p; //previous device will be disconnected automatically + current_device_id = path; + nitrokey::log::Log::setPrefix(path); + LOGD1("Device successfully changed"); + return true; + } + + bool NitrokeyManager::connect() { + std::lock_guard lock(mex_dev_com_manager); + vector< shared_ptr > devices = { make_shared(), make_shared() }; + bool connected = false; + for( auto & d : devices ){ + if (d->connect()){ + device = std::shared_ptr(d); + connected = true; + } + } + return connected; + } + + + void NitrokeyManager::set_log_function(std::function log_function){ + static nitrokey::log::FunctionalLogHandler handler(log_function); + nitrokey::log::Log::instance().set_handler(&handler); + } + + bool NitrokeyManager::set_default_commands_delay(int delay){ + if (delay < 20){ + LOG("Delay set too low: " + to_string(delay), Loglevel::WARNING); + return false; + } + Device::set_default_device_speed(delay); + return true; + } + + bool NitrokeyManager::connect(const char *device_model) { + std::lock_guard lock(mex_dev_com_manager); + LOG(__FUNCTION__, nitrokey::log::Loglevel::DEBUG_L2); + switch (device_model[0]){ + case 'P': + device = make_shared(); + break; + case 'S': + device = make_shared(); + break; + default: + throw std::runtime_error("Unknown model"); + } + return device->connect(); + } + + bool NitrokeyManager::connect(device::DeviceModel device_model) { + const char *model_string; + switch (device_model) { + case device::DeviceModel::PRO: + model_string = "P"; + break; + case device::DeviceModel::STORAGE: + model_string = "S"; + break; + default: + throw std::runtime_error("Unknown model"); + } + return connect(model_string); + } + + shared_ptr NitrokeyManager::instance() { + static std::mutex mutex; + std::lock_guard lock(mutex); + if (_instance == nullptr){ + _instance = make_shared(); + } + return _instance; + } + + + + bool NitrokeyManager::disconnect() { + std::lock_guard lock(mex_dev_com_manager); + return _disconnect_no_lock(); + } + + bool NitrokeyManager::_disconnect_no_lock() { + //do not use directly without locked mutex, + //used by could_be_enumerated, disconnect + if (device == nullptr){ + return false; + } + const auto res = device->disconnect(); + device = nullptr; + return res; + } + + bool NitrokeyManager::is_connected() throw(){ + std::lock_guard lock(mex_dev_com_manager); + if(device != nullptr){ + auto connected = device->could_be_enumerated(); + if(connected){ + return true; + } else { + _disconnect_no_lock(); + return false; + } + } + return false; + } + + bool NitrokeyManager::could_current_device_be_enumerated() { + std::lock_guard lock(mex_dev_com_manager); + if (device != nullptr) { + return device->could_be_enumerated(); + } + return false; + } + + void NitrokeyManager::set_loglevel(int loglevel) { + loglevel = max(loglevel, static_cast(Loglevel::ERROR)); + loglevel = min(loglevel, static_cast(Loglevel::DEBUG_L2)); + Log::instance().set_loglevel(static_cast(loglevel)); + } + + void NitrokeyManager::set_loglevel(Loglevel loglevel) { + Log::instance().set_loglevel(loglevel); + } + + void NitrokeyManager::set_debug(bool state) { + if (state){ + Log::instance().set_loglevel(Loglevel::DEBUG); + } else { + Log::instance().set_loglevel(Loglevel::ERROR); + } + } + + + string NitrokeyManager::get_serial_number() { + if (device == nullptr) { return ""; }; + switch (device->get_device_model()) { + case DeviceModel::PRO: { + auto response = GetStatus::CommandTransaction::run(device); + return nitrokey::misc::toHex(response.data().card_serial_u32); + } + break; + + case DeviceModel::STORAGE: + { + auto response = stick20::GetDeviceStatus::CommandTransaction::run(device); + return nitrokey::misc::toHex(response.data().ActiveSmartCardID_u32); + } + break; + } + return "NA"; + } + + stick10::GetStatus::ResponsePayload NitrokeyManager::get_status(){ + try{ + auto response = GetStatus::CommandTransaction::run(device); + return response.data(); + } + catch (DeviceSendingFailure &e){ +// disconnect(); + throw; + } + } + + string NitrokeyManager::get_status_as_string() { + auto response = GetStatus::CommandTransaction::run(device); + return response.data().dissect(); + } + + string getFilledOTPCode(uint32_t code, bool use_8_digits){ + stringstream s; + s << std::right << std::setw(use_8_digits ? 8 : 6) << std::setfill('0') << code; + return s.str(); + } + + string NitrokeyManager::get_HOTP_code(uint8_t slot_number, const char *user_temporary_password) { + if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + + if (is_authorization_command_supported()){ + auto gh = get_payload(); + gh.slot_number = get_internal_slot_number_for_hotp(slot_number); + if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen + authorize_packet(gh, user_temporary_password, device); + } + auto resp = GetHOTP::CommandTransaction::run(device, gh); + return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); + } else { + auto gh = get_payload(); + gh.slot_number = get_internal_slot_number_for_hotp(slot_number); + if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0) { + strcpyT(gh.temporary_user_password, user_temporary_password); + } + auto resp = stick10_08::GetHOTP::CommandTransaction::run(device, gh); + return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); + } + return ""; + } + + bool NitrokeyManager::is_internal_hotp_slot_number(uint8_t slot_number) const { return slot_number < 0x20; } + bool NitrokeyManager::is_valid_hotp_slot_number(uint8_t slot_number) const { return slot_number < 3; } + bool NitrokeyManager::is_valid_totp_slot_number(uint8_t slot_number) const { return slot_number < 0x10-1; } //15 + uint8_t NitrokeyManager::get_internal_slot_number_for_totp(uint8_t slot_number) const { return (uint8_t) (0x20 + slot_number); } + uint8_t NitrokeyManager::get_internal_slot_number_for_hotp(uint8_t slot_number) const { return (uint8_t) (0x10 + slot_number); } + + + + string NitrokeyManager::get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval, + const char *user_temporary_password) { + if(!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + slot_number = get_internal_slot_number_for_totp(slot_number); + + if (is_authorization_command_supported()){ + auto gt = get_payload(); + gt.slot_number = slot_number; + gt.challenge = challenge; + gt.last_interval = last_interval; + gt.last_totp_time = last_totp_time; + + if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen + authorize_packet(gt, user_temporary_password, device); + } + auto resp = GetTOTP::CommandTransaction::run(device, gt); + return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); + } else { + auto gt = get_payload(); + strcpyT(gt.temporary_user_password, user_temporary_password); + gt.slot_number = slot_number; + auto resp = stick10_08::GetTOTP::CommandTransaction::run(device, gt); + return getFilledOTPCode(resp.data().code, resp.data().use_8_digits); + } + return ""; + } + + bool NitrokeyManager::erase_slot(uint8_t slot_number, const char *temporary_password) { + if (is_authorization_command_supported()){ + auto p = get_payload(); + p.slot_number = slot_number; + authorize_packet(p, temporary_password, device); + auto resp = EraseSlot::CommandTransaction::run(device,p); + } else { + auto p = get_payload(); + p.slot_number = slot_number; + strcpyT(p.temporary_admin_password, temporary_password); + auto resp = stick10_08::EraseSlot::CommandTransaction::run(device,p); + } + return true; + } + + bool NitrokeyManager::erase_hotp_slot(uint8_t slot_number, const char *temporary_password) { + if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + slot_number = get_internal_slot_number_for_hotp(slot_number); + return erase_slot(slot_number, temporary_password); + } + + bool NitrokeyManager::erase_totp_slot(uint8_t slot_number, const char *temporary_password) { + if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + slot_number = get_internal_slot_number_for_totp(slot_number); + return erase_slot(slot_number, temporary_password); + } + + template + void vector_copy_ranged(T& dest, std::vector &vec, size_t begin, size_t elements_to_copy){ + const size_t d_size = sizeof(dest); + if(d_size < elements_to_copy){ + throw TargetBufferSmallerThanSource(elements_to_copy, d_size); + } + std::fill(dest, dest+d_size, 0); + std::copy(vec.begin() + begin, vec.begin() +begin + elements_to_copy, dest); + } + + template + void vector_copy(T& dest, std::vector &vec){ + const size_t d_size = sizeof(dest); + if(d_size < vec.size()){ + throw TargetBufferSmallerThanSource(vec.size(), d_size); + } + std::fill(dest, dest+d_size, 0); + std::copy(vec.begin(), vec.end(), dest); + } + + bool NitrokeyManager::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) { + if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + + int internal_slot_number = get_internal_slot_number_for_hotp(slot_number); + if (is_authorization_command_supported()){ + write_HOTP_slot_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, + token_ID, temporary_password); + } else { + write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, + token_ID, temporary_password); + } + return true; + } + + void NitrokeyManager::write_HOTP_slot_authorize(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) { + auto payload = get_payload(); + payload.slot_number = slot_number; + auto secret_bin = misc::hex_string_to_byte(secret); + vector_copy(payload.slot_secret, secret_bin); + strcpyT(payload.slot_name, slot_name); + strcpyT(payload.slot_token_id, token_ID); + switch (device->get_device_model() ){ + case DeviceModel::PRO: { + payload.slot_counter = hotp_counter; + break; + } + case DeviceModel::STORAGE: { + string counter = to_string(hotp_counter); + strcpyT(payload.slot_counter_s, counter.c_str()); + break; + } + default: + LOG(string(__FILE__) + to_string(__LINE__) + + string(__FUNCTION__) + string(" Unhandled device model for HOTP") + , Loglevel::DEBUG); + break; + } + payload.use_8_digits = use_8_digits; + payload.use_enter = use_enter; + payload.use_tokenID = use_tokenID; + + authorize_packet(payload, temporary_password, device); + + auto resp = WriteToHOTPSlot::CommandTransaction::run(device, payload); + } + + bool NitrokeyManager::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) { + if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + int internal_slot_number = get_internal_slot_number_for_totp(slot_number); + + if (is_authorization_command_supported()){ + write_TOTP_slot_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, + token_ID, temporary_password); + } else { + write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, + token_ID, temporary_password); + } + + return true; + } + + void NitrokeyManager::write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name, + const char *secret, + uint64_t counter_or_interval, bool use_8_digits, bool use_enter, + bool use_tokenID, const char *token_ID, + const char *temporary_password) const { + + auto payload2 = get_payload(); + strcpyT(payload2.temporary_admin_password, temporary_password); + strcpyT(payload2.data, slot_name); + payload2.setTypeName(); + stick10_08::SendOTPData::CommandTransaction::run(device, payload2); + + payload2.setTypeSecret(); + payload2.id = 0; + auto secret_bin = misc::hex_string_to_byte(secret); + auto remaining_secret_length = secret_bin.size(); + const auto maximum_OTP_secret_size = 40; + if(remaining_secret_length > maximum_OTP_secret_size){ + throw TargetBufferSmallerThanSource(remaining_secret_length, maximum_OTP_secret_size); + } + + while (remaining_secret_length>0){ + const auto bytesToCopy = std::min(sizeof(payload2.data), remaining_secret_length); + const auto start = secret_bin.size() - remaining_secret_length; + memset(payload2.data, 0, sizeof(payload2.data)); + vector_copy_ranged(payload2.data, secret_bin, start, bytesToCopy); + stick10_08::SendOTPData::CommandTransaction::run(device, payload2); + remaining_secret_length -= bytesToCopy; + payload2.id++; + } + + auto payload = get_payload(); + strcpyT(payload.temporary_admin_password, temporary_password); + strcpyT(payload.slot_token_id, token_ID); + payload.use_8_digits = use_8_digits; + payload.use_enter = use_enter; + payload.use_tokenID = use_tokenID; + payload.slot_counter_or_interval = counter_or_interval; + payload.slot_number = internal_slot_number; + stick10_08::WriteToOTPSlot::CommandTransaction::run(device, payload); + } + + void NitrokeyManager::write_TOTP_slot_authorize(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) { + auto payload = get_payload(); + payload.slot_number = slot_number; + auto secret_bin = misc::hex_string_to_byte(secret); + vector_copy(payload.slot_secret, secret_bin); + strcpyT(payload.slot_name, slot_name); + strcpyT(payload.slot_token_id, token_ID); + payload.slot_interval = time_window; //FIXME naming + payload.use_8_digits = use_8_digits; + payload.use_enter = use_enter; + payload.use_tokenID = use_tokenID; + + authorize_packet(payload, temporary_password, device); + + auto resp = WriteToTOTPSlot::CommandTransaction::run(device, payload); + } + + char * NitrokeyManager::get_totp_slot_name(uint8_t slot_number) { + if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + slot_number = get_internal_slot_number_for_totp(slot_number); + return get_slot_name(slot_number); + } + char * NitrokeyManager::get_hotp_slot_name(uint8_t slot_number) { + if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number); + slot_number = get_internal_slot_number_for_hotp(slot_number); + return get_slot_name(slot_number); + } + + static const int max_string_field_length = 2*1024; //storage's status string is ~1k + + char * NitrokeyManager::get_slot_name(uint8_t slot_number) { + auto payload = get_payload(); + payload.slot_number = slot_number; + auto resp = GetSlotName::CommandTransaction::run(device, payload); + return strndup((const char *) resp.data().slot_name, max_string_field_length); + } + + bool NitrokeyManager::first_authenticate(const char *pin, const char *temporary_password) { + auto authreq = get_payload(); + strcpyT(authreq.card_password, pin); + strcpyT(authreq.temporary_password, temporary_password); + FirstAuthenticate::CommandTransaction::run(device, authreq); + return true; + } + + bool NitrokeyManager::set_time(uint64_t time) { + auto p = get_payload(); + p.reset = 1; + p.time = time; + SetTime::CommandTransaction::run(device, p); + return false; + } + + void NitrokeyManager::set_time_soft(uint64_t time) { + auto p = get_payload(); + p.reset = 0; + p.time = time; + SetTime::CommandTransaction::run(device, p); + } + + bool NitrokeyManager::get_time(uint64_t time) { + set_time_soft(time); + return true; + } + + void NitrokeyManager::change_user_PIN(const char *current_PIN, const char *new_PIN) { + change_PIN_general(current_PIN, new_PIN); + } + + void NitrokeyManager::change_admin_PIN(const char *current_PIN, const char *new_PIN) { + change_PIN_general(current_PIN, new_PIN); + } + + template + void NitrokeyManager::change_PIN_general(const char *current_PIN, const char *new_PIN) { + switch (device->get_device_model()){ + case DeviceModel::PRO: + { + auto p = get_payload(); + strcpyT(p.old_pin, current_PIN); + strcpyT(p.new_pin, new_PIN); + ProCommand::CommandTransaction::run(device, p); + } + break; + //in Storage change admin/user pin is divided to two commands with 20 chars field len + case DeviceModel::STORAGE: + { + auto p = get_payload(); + strcpyT(p.password, current_PIN); + p.set_kind(StoKind); + auto p2 = get_payload(); + strcpyT(p2.password, new_PIN); + p2.set_kind(StoKind); + ChangeAdminUserPin20Current::CommandTransaction::run(device, p); + ChangeAdminUserPin20New::CommandTransaction::run(device, p2); + } + break; + } + + } + + void NitrokeyManager::enable_password_safe(const char *user_pin) { + //The following command will cancel enabling PWS if it is not supported + auto a = get_payload(); + strcpyT(a.user_password, user_pin); + IsAESSupported::CommandTransaction::run(device, a); + + auto p = get_payload(); + strcpyT(p.user_password, user_pin); + EnablePasswordSafe::CommandTransaction::run(device, p); + } + + vector NitrokeyManager::get_password_safe_slot_status() { + auto responsePayload = GetPasswordSafeSlotStatus::CommandTransaction::run(device); + vector v = vector(responsePayload.data().password_safe_status, + responsePayload.data().password_safe_status + + sizeof(responsePayload.data().password_safe_status)); + return v; + } + + uint8_t NitrokeyManager::get_user_retry_count() { + if(device->get_device_model() == DeviceModel::STORAGE){ + stick20::GetDeviceStatus::CommandTransaction::run(device); + } + auto response = GetUserPasswordRetryCount::CommandTransaction::run(device); + return response.data().password_retry_count; + } + + uint8_t NitrokeyManager::get_admin_retry_count() { + if(device->get_device_model() == DeviceModel::STORAGE){ + stick20::GetDeviceStatus::CommandTransaction::run(device); + } + auto response = GetPasswordRetryCount::CommandTransaction::run(device); + return response.data().password_retry_count; + } + + void NitrokeyManager::lock_device() { + LockDevice::CommandTransaction::run(device); + } + + char * NitrokeyManager::get_password_safe_slot_name(uint8_t slot_number) { + if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); + auto p = get_payload(); + p.slot_number = slot_number; + auto response = GetPasswordSafeSlotName::CommandTransaction::run(device, p); + return strndup((const char *) response.data().slot_name, max_string_field_length); + } + + bool NitrokeyManager::is_valid_password_safe_slot_number(uint8_t slot_number) const { return slot_number < 16; } + + char * NitrokeyManager::get_password_safe_slot_login(uint8_t slot_number) { + if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); + auto p = get_payload(); + p.slot_number = slot_number; + auto response = GetPasswordSafeSlotLogin::CommandTransaction::run(device, p); + return strndup((const char *) response.data().slot_login, max_string_field_length); + } + + char * NitrokeyManager::get_password_safe_slot_password(uint8_t slot_number) { + if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); + auto p = get_payload(); + p.slot_number = slot_number; + auto response = GetPasswordSafeSlotPassword::CommandTransaction::run(device, p); + return strndup((const char *) response.data().slot_password, max_string_field_length); //FIXME use secure way + } + + void NitrokeyManager::write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, + const char *slot_password) { + if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); + auto p = get_payload(); + p.slot_number = slot_number; + strcpyT(p.slot_name, slot_name); + strcpyT(p.slot_password, slot_password); + SetPasswordSafeSlotData::CommandTransaction::run(device, p); + + auto p2 = get_payload(); + p2.slot_number = slot_number; + strcpyT(p2.slot_login_name, slot_login); + SetPasswordSafeSlotData2::CommandTransaction::run(device, p2); + } + + void NitrokeyManager::erase_password_safe_slot(uint8_t slot_number) { + if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number); + auto p = get_payload(); + p.slot_number = slot_number; + ErasePasswordSafeSlot::CommandTransaction::run(device, p); + } + + void NitrokeyManager::user_authenticate(const char *user_password, const char *temporary_password) { + auto p = get_payload(); + strcpyT(p.card_password, user_password); + strcpyT(p.temporary_password, temporary_password); + UserAuthenticate::CommandTransaction::run(device, p); + } + + void NitrokeyManager::build_aes_key(const char *admin_password) { + switch (device->get_device_model()) { + case DeviceModel::PRO: { + auto p = get_payload(); + strcpyT(p.admin_password, admin_password); + BuildAESKey::CommandTransaction::run(device, p); + break; + } + case DeviceModel::STORAGE : { + auto p = get_payload(); + strcpyT(p.password, admin_password); + p.set_defaults(); + stick20::CreateNewKeys::CommandTransaction::run(device, p); + break; + } + } + } + + void NitrokeyManager::factory_reset(const char *admin_password) { + auto p = get_payload(); + strcpyT(p.admin_password, admin_password); + FactoryReset::CommandTransaction::run(device, p); + } + + void NitrokeyManager::unlock_user_password(const char *admin_password, const char *new_user_password) { + switch (device->get_device_model()){ + case DeviceModel::PRO: { + auto p = get_payload(); + strcpyT(p.admin_password, admin_password); + strcpyT(p.user_new_password, new_user_password); + stick10::UnlockUserPassword::CommandTransaction::run(device, p); + break; + } + case DeviceModel::STORAGE : { + auto p2 = get_payload(); + p2.set_defaults(); + strcpyT(p2.password, admin_password); + ChangeAdminUserPin20Current::CommandTransaction::run(device, p2); + auto p3 = get_payload(); + p3.set_defaults(); + strcpyT(p3.password, new_user_password); + stick20::UnlockUserPin::CommandTransaction::run(device, p3); + break; + } + } + } + + + void NitrokeyManager::write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password, + bool delete_user_password, const char *admin_temporary_password) { + auto p = get_payload(); + p.numlock = numlock; + p.capslock = capslock; + p.scrolllock = scrolllock; + p.enable_user_password = static_cast(enable_user_password ? 1 : 0); + p.delete_user_password = static_cast(delete_user_password ? 1 : 0); + if (is_authorization_command_supported()){ + authorize_packet(p, admin_temporary_password, device); + } else { + strcpyT(p.temporary_admin_password, admin_temporary_password); + } + stick10_08::WriteGeneralConfig::CommandTransaction::run(device, p); + } + + vector NitrokeyManager::read_config() { + auto responsePayload = GetStatus::CommandTransaction::run(device); + vector v = vector(responsePayload.data().general_config, + responsePayload.data().general_config+sizeof(responsePayload.data().general_config)); + return v; + } + + bool NitrokeyManager::is_authorization_command_supported(){ + //authorization command is supported for versions equal or below: + auto m = std::unordered_map({ + {DeviceModel::PRO, 7}, + {DeviceModel::STORAGE, 53}, + }); + return get_minor_firmware_version() <= m[device->get_device_model()]; + } + + bool NitrokeyManager::is_320_OTP_secret_supported(){ + // 320 bit OTP secret is supported by version bigger or equal to: + auto m = std::unordered_map({ + {DeviceModel::PRO, 8}, + {DeviceModel::STORAGE, 54}, + }); + return get_minor_firmware_version() >= m[device->get_device_model()]; + } + + DeviceModel NitrokeyManager::get_connected_device_model() const{ + if (device == nullptr){ + throw DeviceNotConnected("device not connected"); + } + return device->get_device_model(); + } + + bool NitrokeyManager::is_smartcard_in_use(){ + try{ + stick20::CheckSmartcardUsage::CommandTransaction::run(device); + } + catch(const CommandFailedException & e){ + return e.reason_smartcard_busy(); + } + return false; + } + + uint8_t NitrokeyManager::get_minor_firmware_version(){ + switch(device->get_device_model()){ + case DeviceModel::PRO:{ + auto status_p = GetStatus::CommandTransaction::run(device); + return status_p.data().firmware_version_st.minor; //7 or 8 + } + case DeviceModel::STORAGE:{ + auto status = stick20::GetDeviceStatus::CommandTransaction::run(device); + auto test_firmware = status.data().versionInfo.build_iteration != 0; + if (test_firmware) + LOG("Development firmware detected. Increasing minor version number.", nitrokey::log::Loglevel::WARNING); + return status.data().versionInfo.minor + (test_firmware? 1 : 0); + } + } + return 0; + } + uint8_t NitrokeyManager::get_major_firmware_version(){ + switch(device->get_device_model()){ + case DeviceModel::PRO:{ + auto status_p = GetStatus::CommandTransaction::run(device); + return status_p.data().firmware_version_st.major; //0 + } + case DeviceModel::STORAGE:{ + auto status = stick20::GetDeviceStatus::CommandTransaction::run(device); + return status.data().versionInfo.major; + } + } + return 0; + } + + bool NitrokeyManager::is_AES_supported(const char *user_password) { + auto a = get_payload(); + strcpyT(a.user_password, user_password); + IsAESSupported::CommandTransaction::run(device, a); + return true; + } + + //storage commands + + void NitrokeyManager::send_startup(uint64_t seconds_from_epoch){ + auto p = get_payload(); +// p.set_defaults(); //set current time + p.localtime = seconds_from_epoch; + stick20::SendStartup::CommandTransaction::run(device, p); + } + + void NitrokeyManager::unlock_encrypted_volume(const char* user_pin){ + misc::execute_password_command(device, user_pin); + } + + void NitrokeyManager::unlock_hidden_volume(const char* hidden_volume_password) { + misc::execute_password_command(device, hidden_volume_password); + } + + void NitrokeyManager::set_encrypted_volume_read_only(const char* admin_pin) { + misc::execute_password_command(device, admin_pin); + } + + void NitrokeyManager::set_encrypted_volume_read_write(const char* admin_pin) { + misc::execute_password_command(device, admin_pin); + } + + //TODO check is encrypted volume unlocked before execution + //if not return library exception + void NitrokeyManager::create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, + const char *hidden_volume_password) { + auto p = get_payload(); + p.SlotNr_u8 = slot_nr; + p.StartBlockPercent_u8 = start_percent; + p.EndBlockPercent_u8 = end_percent; + strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password); + stick20::SetupHiddenVolume::CommandTransaction::run(device, p); + } + + void NitrokeyManager::set_unencrypted_read_only_admin(const char* admin_pin) { + //from v0.49, v0.52+ it needs Admin PIN + if (set_unencrypted_volume_rorw_pin_type_user()){ + LOG("set_unencrypted_read_only_admin is not supported for this version of Storage device. " + "Please update firmware to v0.52+. Doing nothing.", nitrokey::log::Loglevel::WARNING); + return; + } + misc::execute_password_command(device, admin_pin); + } + + void NitrokeyManager::set_unencrypted_read_only(const char *user_pin) { + //until v0.48 (incl. v0.50 and v0.51) User PIN was sufficient + LOG("set_unencrypted_read_only is deprecated. Use set_unencrypted_read_only_admin instead.", + nitrokey::log::Loglevel::WARNING); + if (!set_unencrypted_volume_rorw_pin_type_user()){ + LOG("set_unencrypted_read_only is not supported for this version of Storage device. Doing nothing.", + nitrokey::log::Loglevel::WARNING); + return; + } + misc::execute_password_command(device, user_pin); + } + + void NitrokeyManager::set_unencrypted_read_write_admin(const char* admin_pin) { + //from v0.49, v0.52+ it needs Admin PIN + if (set_unencrypted_volume_rorw_pin_type_user()){ + LOG("set_unencrypted_read_write_admin is not supported for this version of Storage device. " + "Please update firmware to v0.52+. Doing nothing.", nitrokey::log::Loglevel::WARNING); + return; + } + misc::execute_password_command(device, admin_pin); + } + + void NitrokeyManager::set_unencrypted_read_write(const char *user_pin) { + //until v0.48 (incl. v0.50 and v0.51) User PIN was sufficient + LOG("set_unencrypted_read_write is deprecated. Use set_unencrypted_read_write_admin instead.", + nitrokey::log::Loglevel::WARNING); + if (!set_unencrypted_volume_rorw_pin_type_user()){ + LOG("set_unencrypted_read_write is not supported for this version of Storage device. Doing nothing.", + nitrokey::log::Loglevel::WARNING); + return; + } + misc::execute_password_command(device, user_pin); + } + + bool NitrokeyManager::set_unencrypted_volume_rorw_pin_type_user(){ + auto minor_firmware_version = get_minor_firmware_version(); + return minor_firmware_version <= 48 || minor_firmware_version == 50 || minor_firmware_version == 51; + } + + void NitrokeyManager::export_firmware(const char* admin_pin) { + misc::execute_password_command(device, admin_pin); + } + + void NitrokeyManager::enable_firmware_update(const char* firmware_pin) { + misc::execute_password_command(device, firmware_pin); + } + + void NitrokeyManager::clear_new_sd_card_warning(const char* admin_pin) { + misc::execute_password_command(device, admin_pin); + } + + void NitrokeyManager::fill_SD_card_with_random_data(const char* admin_pin) { + auto p = get_payload(); + p.set_defaults(); + strcpyT(p.admin_pin, admin_pin); + stick20::FillSDCardWithRandomChars::CommandTransaction::run(device, p); + } + + void NitrokeyManager::change_update_password(const char* current_update_password, const char* new_update_password) { + auto p = get_payload(); + strcpyT(p.current_update_password, current_update_password); + strcpyT(p.new_update_password, new_update_password); + stick20::ChangeUpdatePassword::CommandTransaction::run(device, p); + } + + char * NitrokeyManager::get_status_storage_as_string(){ + auto p = stick20::GetDeviceStatus::CommandTransaction::run(device); + return strndup(p.data().dissect().c_str(), max_string_field_length); + } + + stick20::DeviceConfigurationResponsePacket::ResponsePayload NitrokeyManager::get_status_storage(){ + auto p = stick20::GetDeviceStatus::CommandTransaction::run(device); + return p.data(); + } + + char * NitrokeyManager::get_SD_usage_data_as_string(){ + auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device); + return strndup(p.data().dissect().c_str(), max_string_field_length); + } + + std::pair NitrokeyManager::get_SD_usage_data(){ + auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device); + return std::make_pair(p.data().WriteLevelMin, p.data().WriteLevelMax); + } + + int NitrokeyManager::get_progress_bar_value(){ + try{ + stick20::GetDeviceStatus::CommandTransaction::run(device); + return -1; + } + catch (LongOperationInProgressException &e){ + return e.progress_bar_value; + } + } + + string NitrokeyManager::get_TOTP_code(uint8_t slot_number, const char *user_temporary_password) { + return get_TOTP_code(slot_number, 0, 0, 0, user_temporary_password); + } + + /** + * Returns ReadSlot structure, describing OTP slot configuration. Always return binary counter - + * does the necessary conversion, if needed, to unify the behavior across Pro and Storage. + * @private For internal use only + * @param slot_number which OTP slot to use (usual format) + * @return ReadSlot structure + */ + stick10::ReadSlot::ResponsePayload NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) { + auto p = get_payload(); + p.slot_number = slot_number; + p.data_format = stick10::ReadSlot::CounterFormat::BINARY; // ignored for devices other than Storage v0.54+ + auto data = stick10::ReadSlot::CommandTransaction::run(device, p); + + auto &payload = data.data(); + + // if fw <=v0.53 and asked binary - do the conversion from ASCII + if (device->get_device_model() == DeviceModel::STORAGE && get_minor_firmware_version() <= 53 + && is_internal_hotp_slot_number(slot_number)) + { + //convert counter from string to ull + auto counter_s = std::string(payload.slot_counter_s, payload.slot_counter_s + sizeof(payload.slot_counter_s)); + payload.slot_counter = std::stoull(counter_s); + } + + return payload; + } + + stick10::ReadSlot::ResponsePayload NitrokeyManager::get_TOTP_slot_data(const uint8_t slot_number) { + return get_OTP_slot_data(get_internal_slot_number_for_totp(slot_number)); + } + + stick10::ReadSlot::ResponsePayload NitrokeyManager::get_HOTP_slot_data(const uint8_t slot_number) { + return get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number)); + } + + void NitrokeyManager::lock_encrypted_volume() { + misc::execute_password_command(device, ""); + } + + void NitrokeyManager::lock_hidden_volume() { + misc::execute_password_command(device, ""); + } + + uint8_t NitrokeyManager::get_SD_card_size() { + auto data = stick20::ProductionTest::CommandTransaction::run(device); + return data.data().SD_Card_Size_u8; + } + + const string NitrokeyManager::get_current_device_id() const { + return current_device_id; + } + + void NitrokeyManager::wink(){ + stick20::Wink::CommandTransaction::run(device); + }; + + stick20::ProductionTest::ResponsePayload NitrokeyManager::production_info(){ + auto data = stick20::ProductionTest::CommandTransaction::run(device); + return data.data(); + }; + + void NitrokeyManager::enable_firmware_update_pro(const char *firmware_pin) { + auto p = get_payload(); + strcpyT(p.firmware_password, firmware_pin); + FirmwareUpdate::CommandTransaction::run(device, p); + } + + void + NitrokeyManager::change_firmware_update_password_pro(const char *firmware_pin_current, const char *firmware_pin_new) { + auto p = get_payload(); + strcpyT(p.firmware_password_current, firmware_pin_current); + strcpyT(p.firmware_password_new, firmware_pin_new); + FirmwarePasswordChange::CommandTransaction::run(device, p); + } + +} diff --git a/nitrokey-sys/libnitrokey-v3.5/README.md b/nitrokey-sys/libnitrokey-v3.5/README.md new file mode 100644 index 0000000..a3683c0 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/README.md @@ -0,0 +1,291 @@ +[![Build Status](https://travis-ci.org/Nitrokey/libnitrokey.svg?branch=master)](https://travis-ci.org/Nitrokey/libnitrokey) +[![Waffle.io - Columns and their card count](https://badge.waffle.io/Nitrokey/libnitrokey.svg?columns=ready,in%20progress,test,waiting%20for%20feedback)](https://waffle.io/Nitrokey/libnitrokey) + +# libnitrokey +libnitrokey is a project to communicate with Nitrokey Pro and Storage devices in a clean and easy manner. Written in C++14, testable with `py.test` and `Catch` frameworks, with C API, Python access (through CFFI and C API, in future with Pybind11). + +The development of this project is aimed to make it itself a living documentation of communication protocol between host and the Nitrokey stick devices. The command packets' format is described here: [Pro v0.7](libnitrokey/stick10_commands.h), [Pro v0.8](libnitrokey/stick10_commands_0.8.h), [Storage](libnitrokey/stick20_commands.h). Handling and additional operations are described here: [NitrokeyManager.cc](NitrokeyManager.cc). + +A C++14 complying compiler is required due to heavy use of variable templates. For feature support tables please check [table 1](https://gcc.gnu.org/projects/cxx-status.html#cxx14) or [table 2](http://en.cppreference.com/w/cpp/compiler_support). + +libnitrokey is developed and tested with the latest compilers: g++ 6.2, clang 3.8. We use Travis CI to test builds also on g++ 5.4 and under OSX compilers starting up from xcode 8.2 environment. + +## Getting sources +This repository uses `git submodules`. +To clone please use git's `--recursive` option like in: +```bash +git clone --recursive https://github.com/Nitrokey/libnitrokey.git +``` +or for already cloned repository: +```bash +git clone https://github.com/Nitrokey/libnitrokey.git +cd libnitrokey +git submodule update --init --recursive +``` + +## Dependencies +Following libraries are needed to use libnitrokey on Linux (names of the packages on Ubuntu): +- libhidapi-dev [(HID API)](http://www.signal11.us/oss/hidapi/) +- libusb-1.0-0-dev + + +## Compilation +libnitrokey uses CMake as its main build system. As a secondary option it offers building through Qt's qMake. +### Qt +A Qt's .pro project file is provided for direct compilation and for inclusion to other projects. +Using it directly is not recommended due to lack of dependencies check and not implemented library versioning. +Compilation is tested with Qt 5.6 and greater. + +Quick start example: +```bash +mkdir -p build +cd build +qmake .. +make -j2 +``` + +### Windows and Visual Studio 2017 +Lately Visual Studio has started handling CMake files directly. After opening the project's directory it should recognize it and initialize build system. Afterwards please run: +1. `CMake -> Cache -> View Cache CMakeLists.txt -> CMakeLists.txt` to edit settings +2. `CMake -> Build All` to build + +It is possible too to use CMake GUI directly with its settings editor. + +### CMake +To compile please run following sequence of commands: +```bash +# assuming current dir is ./libnitrokey/ +mkdir -p build +cd build +cmake .. +make -j2 +``` + +By default (with empty `` string) this will create in `build/` directory a shared library (.so, .dll or .dynlib). If you wish to build static version you can use as `` string `-DBUILD_SHARED_LIBS=OFF`. + +All options could be listed with `cmake .. -L` or instead `cmake` a `ccmake ..` tool could be used for configuration (where `..` is the path to directory with `CMakeLists.txt` file). `ccmake` shows also description of the build parameters. + +If you have trouble compiling or running the library you can check [.travis.yml](.travis.yml) file for configuration details. This file is used by Travis CI service to make test builds on OSX and Ubuntu 14.04. + +Other build options (all take either `ON` or `OFF`): +* ADD_ASAN - add tests for memory leaks and out-of-bounds access +* ADD_TSAN - add tests for threads race, needs USE_CLANG +* COMPILE_TESTS - compile C++ tests +* COMPILE_OFFLINE_TESTS - compile C++ tests, that do not require any device to be connected +* LOG_VOLATILE_DATA (default: OFF) - include secrets in log (PWS passwords, PINs etc) +* NO_LOG (default: OFF) - do not compile LOG statements - will make library smaller, but without any diagnostic messages + + +### Meson +It is possible to use Meson and Ninja to build the project as well (currently available only `master` branch). +Please run: +``` +meson builddir +meson configure builddir # to show available build flags +ninja -C builddir +``` + +# Using libnitrokey with Python +To use libnitrokey with Python a [CFFI](http://cffi.readthedocs.io/en/latest/overview.html) library is required (either 2.7+ or 3.0+). It can be installed with: +```bash +pip install --user cffi # for python 2.x +pip3 install cffi # for python 3.x +``` +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 +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) + + +libnitrokey = get_library() +libnitrokey.NK_set_debug(False) # do not show debug messages (log library only) + +hotp_slot_code = get_hotp_code(libnitrokey, 1) +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. + +## Documentation +The documentation of C API is included in the sources (can be generated with `make doc` if Doxygen is installed). +Please check [NK_C_API.h](NK_C_API.h) (C API) for high level commands and [libnitrokey/NitrokeyManager.h](libnitrokey/NitrokeyManager.h) (C++ API). All devices' commands are listed along with packet format in [libnitrokey/stick10_commands.h](libnitrokey/stick10_commands.h) and [libnitrokey/stick20_commands.h](libnitrokey/stick20_commands.h) respectively for Nitrokey Pro and Nitrokey Storage products. + +# Tests +**Warning!** Most of the tests will overwrite user data. The only user-data safe tests are specified in `unittest/test_safe.cpp` (see *C++ tests* chapter). + +**Warning!** Before you run unittests please change both your Admin and User PINs on your Nitrostick to defaults (`12345678` and `123456` respectively), or change the values in tests source code. If you do not change them, the tests might lock your device temporarily. If it's too late already, you can reset your Nitrokey using instructions from [homepage](https://www.nitrokey.com/de/documentation/how-reset-nitrokey). + +## Python tests +libnitrokey has a great suite of tests written in Python 3 under the path: [unittest/test_*.py](https://github.com/Nitrokey/libnitrokey/tree/master/unittest): +* `test_pro.py` - contains tests of OTP, Password Safe and PIN control functionality. Could be run on both Pro and Storage devices. +* `test_storage.py` - contains tests of Encrypted Volumes functionality. Could be run only on Storage. + +The tests themselves show how to handle common requests to device. +Before running please install all required libraries with: +```bash +cd unittest +pip install --user -r requirements.txt +``` +or use Python's environment managing tool like [pipenv](https://pipenv.readthedocs.io/en/latest/) or `virtualenv`. + + +To run them please execute: +```bash +# substitute with either 'pro' or 'storage' +py.test -v test_.py +# more specific use - run tests containing in name 5 times: +py.test -v test_.py -k --count 5 + +``` +For additional documentation please check the following for [py.test installation](http://doc.pytest.org/en/latest/getting-started.html). For better coverage [randomly plugin](https://pypi.python.org/pypi/pytest-randomly) is installed - it randomizes the test order allowing to detect unseen dependencies between the tests. + +## C++ tests +There are also some unit tests implemented in C++, placed in unittest directory. The only user-data safe online test set here is [test_safe.cpp](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test_safe.cpp), which tries to connect to the device, and collect its status data. Example run for Storage: +```text +# Storage device inserted, firmware version v0.53 +$ ./test_safe +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_DEVICE_STATUS +.. +[Wed Jan 2 13:31:17 2019][DEBUG_L1] <= GET_DEVICE_STATUS 0 1 +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_PASSWORD_RETRY_COUNT +[Wed Jan 2 13:31:17 2019][DEBUG_L1] <= GET_PASSWORD_RETRY_COUNT 0 0 +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_DEVICE_STATUS +.. +[Wed Jan 2 13:31:17 2019][DEBUG_L1] <= GET_DEVICE_STATUS 0 1 +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_USER_PASSWORD_RETRY_COUNT +[Wed Jan 2 13:31:17 2019][DEBUG_L1] <= GET_USER_PASSWORD_RETRY_COUNT 0 0 +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_DEVICE_STATUS +... +[Wed Jan 2 13:31:17 2019][DEBUG_L1] <= GET_DEVICE_STATUS 0 1 + transmission_data.dissect(): _padding: +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 05 2e 01 ................ +0010 00 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- .. + (int) SendCounter_u8: 0 + (int) SendDataType_u8: 3 + (int) FollowBytesFlag_u8: 0 + (int) SendSize_u8: 28 + + MagicNumber_StickConfig_u16: 13080 + (int) ReadWriteFlagUncryptedVolume_u8: 1 + (int) ReadWriteFlagCryptedVolume_u8: 0 + (int) ReadWriteFlagHiddenVolume_u8: 0 + (int) versionInfo.major: 0 + (int) versionInfo.minor: 53 + (int) versionInfo.build_iteration: 0 + (int) FirmwareLocked_u8: 0 + (int) NewSDCardFound_u8: 1 + (int) NewSDCardFound_st.NewCard: 1 + (int) NewSDCardFound_st.Counter: 0 + (int) SDFillWithRandomChars_u8: 1 + ActiveSD_CardID_u32: 3670817656 + (int) VolumeActiceFlag_u8: 1 + (int) VolumeActiceFlag_st.unencrypted: 1 + (int) VolumeActiceFlag_st.encrypted: 0 + (int) VolumeActiceFlag_st.hidden: 0 + (int) NewSmartCardFound_u8: 0 + (int) UserPwRetryCount: 3 + (int) AdminPwRetryCount: 3 + ActiveSmartCardID_u32: 24122 + (int) StickKeysNotInitiated: 0 + +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_DEVICE_STATUS +.. +[Wed Jan 2 13:31:17 2019][DEBUG_L1] <= GET_DEVICE_STATUS 0 1 +00005e3a +[Wed Jan 2 13:31:17 2019][DEBUG_L1] => GET_DEVICE_STATUS +.... +[Wed Jan 2 13:31:18 2019][DEBUG_L1] <= GET_DEVICE_STATUS 0 1 +[Wed Jan 2 13:31:18 2019][DEBUG_L1] => GET_DEVICE_STATUS +... +[Wed Jan 2 13:31:18 2019][DEBUG_L1] <= GET_DEVICE_STATUS 0 1 +=============================================================================== +All tests passed (18 assertions in 6 test cases) +``` +Test's execution configuration and verbosity could be manipulated - please see `./test_safe --help` for details. + +The other tests sets are not written as extensively as Python tests and are rather more a C++ low level interface check used during the library development, using either low-level components, C API from `NK_C_API.cc`, or C++ API from `NitrokeyManager.cc`. Some of them are: [test_HOTP.cc](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test_HOTP.cc), +[test1.cc](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test1.cc). See more in [unittest](https://github.com/Nitrokey/libnitrokey/tree/master/unittest) directory. + +**Note: these are not device model agnostic, and will most probably destroy your data on the device.** + + +Unit tests were checked on Ubuntu 16.04/16.10/17.04. To run them just execute binaries built in `./libnitrokey/build` dir, after enabling them by passing `-DCOMPILE_TESTS=ON` option to `cmake` - e.g.: `cmake .. -DCOMPILE_TESTS=ON && make`. + + +The documentation of how it works could be found in nitrokey-app project's README on Github: +[Nitrokey-app - internals](https://github.com/Nitrokey/nitrokey-app/blob/master/README.md#internals). + +To peek/debug communication with device running nitrokey-app (0.x branch) in debug mode (`-d` switch) and checking the logs +(right click on tray icon and then 'Debug') might be helpful. Latest Nitrokey App (1.x branch) uses libnitrokey to communicate with device. Once run with `--dl 3` (3 or higher; range 0-5) it will print all communication to the console. Additionally crosschecking with +firmware code should show how things works: +[report_protocol.c](https://github.com/Nitrokey/nitrokey-pro-firmware/blob/master/src/keyboard/report_protocol.c) +(for Nitrokey Pro, for Storage similarly). + +# Known issues / tasks +* Currently only one device can be connected at a time (experimental work could be found in `wip-multiple_devices` branch), +* C++ API needs some reorganization to C++ objects (instead of pointers to byte arrays). This will be also preparing for integration with Pybind11, +* Fix compilation warnings. + +Other tasks might be listed either in [TODO](TODO) file or on project's issues page. + +# License +This project is licensed under LGPL version 3. License text could be found under [LICENSE](LICENSE) file. + +# Roadmap +To check what issues will be fixed and when please check [milestones](https://github.com/Nitrokey/libnitrokey/milestones) page. diff --git a/nitrokey-sys/libnitrokey-v3.5/command_id.cc b/nitrokey-sys/libnitrokey-v3.5/command_id.cc new file mode 100644 index 0000000..9a329bc --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/command_id.cc @@ -0,0 +1,188 @@ +/* + * 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 + */ + +#include +#include "command_id.h" + +namespace nitrokey { +namespace proto { + +const char *commandid_to_string(CommandID id) { + switch (id) { + case CommandID::GET_STATUS: + return "GET_STATUS"; + case CommandID::WRITE_TO_SLOT: + return "WRITE_TO_SLOT"; + case CommandID::READ_SLOT_NAME: + return "READ_SLOT_NAME"; + case CommandID::READ_SLOT: + return "READ_SLOT"; + case CommandID::GET_CODE: + return "GET_CODE"; + case CommandID::WRITE_CONFIG: + return "WRITE_CONFIG"; + case CommandID::ERASE_SLOT: + return "ERASE_SLOT"; + case CommandID::FIRST_AUTHENTICATE: + return "FIRST_AUTHENTICATE"; + case CommandID::AUTHORIZE: + return "AUTHORIZE"; + case CommandID::GET_PASSWORD_RETRY_COUNT: + return "GET_PASSWORD_RETRY_COUNT"; + case CommandID::CLEAR_WARNING: + return "CLEAR_WARNING"; + case CommandID::SET_TIME: + return "SET_TIME"; + case CommandID::TEST_COUNTER: + return "TEST_COUNTER"; + case CommandID::TEST_TIME: + return "TEST_TIME"; + case CommandID::USER_AUTHENTICATE: + return "USER_AUTHENTICATE"; + case CommandID::GET_USER_PASSWORD_RETRY_COUNT: + return "GET_USER_PASSWORD_RETRY_COUNT"; + case CommandID::USER_AUTHORIZE: + return "USER_AUTHORIZE"; + case CommandID::UNLOCK_USER_PASSWORD: + return "UNLOCK_USER_PASSWORD"; + case CommandID::LOCK_DEVICE: + return "LOCK_DEVICE"; + case CommandID::FACTORY_RESET: + return "FACTORY_RESET"; + case CommandID::CHANGE_USER_PIN: + return "CHANGE_USER_PIN"; + case CommandID::CHANGE_ADMIN_PIN: + return "CHANGE_ADMIN_PIN"; + case CommandID::FIRMWARE_UPDATE: + return "FIRMWARE_UPDATE"; + case CommandID::FIRMWARE_PASSWORD_CHANGE: + return "FIRMWARE_PASSWORD_CHANGE"; + + case CommandID::ENABLE_CRYPTED_PARI: + return "ENABLE_CRYPTED_PARI"; + case CommandID::DISABLE_CRYPTED_PARI: + return "DISABLE_CRYPTED_PARI"; + case CommandID::ENABLE_HIDDEN_CRYPTED_PARI: + return "ENABLE_HIDDEN_CRYPTED_PARI"; + case CommandID::DISABLE_HIDDEN_CRYPTED_PARI: + return "DISABLE_HIDDEN_CRYPTED_PARI"; + case CommandID::ENABLE_FIRMWARE_UPDATE: + return "ENABLE_FIRMWARE_UPDATE"; + case CommandID::EXPORT_FIRMWARE_TO_FILE: + return "EXPORT_FIRMWARE_TO_FILE"; + case CommandID::GENERATE_NEW_KEYS: + return "GENERATE_NEW_KEYS"; + case CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS: + return "FILL_SD_CARD_WITH_RANDOM_CHARS"; + + case CommandID::WRITE_STATUS_DATA: + return "WRITE_STATUS_DATA"; + case CommandID::ENABLE_READONLY_UNCRYPTED_LUN: + return "ENABLE_READONLY_UNCRYPTED_LUN"; + case CommandID::ENABLE_READWRITE_UNCRYPTED_LUN: + return "ENABLE_READWRITE_UNCRYPTED_LUN"; + + case CommandID::SEND_PASSWORD_MATRIX: + return "SEND_PASSWORD_MATRIX"; + case CommandID::SEND_PASSWORD_MATRIX_PINDATA: + return "SEND_PASSWORD_MATRIX_PINDATA"; + case CommandID::SEND_PASSWORD_MATRIX_SETUP: + return "SEND_PASSWORD_MATRIX_SETUP"; + + case CommandID::GET_DEVICE_STATUS: + return "GET_DEVICE_STATUS"; + case CommandID::SEND_DEVICE_STATUS: + return "SEND_DEVICE_STATUS"; + + case CommandID::SEND_HIDDEN_VOLUME_PASSWORD: + return "SEND_HIDDEN_VOLUME_PASSWORD"; + case CommandID::SEND_HIDDEN_VOLUME_SETUP: + return "SEND_HIDDEN_VOLUME_SETUP"; + case CommandID::SEND_PASSWORD: + return "SEND_PASSWORD"; + case CommandID::SEND_NEW_PASSWORD: + return "SEND_NEW_PASSWORD"; + case CommandID::CLEAR_NEW_SD_CARD_FOUND: + return "CLEAR_NEW_SD_CARD_FOUND"; + + case CommandID::SEND_STARTUP: + return "SEND_STARTUP"; + case CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED: + return "SEND_CLEAR_STICK_KEYS_NOT_INITIATED"; + case CommandID::SEND_LOCK_STICK_HARDWARE: + return "SEND_LOCK_STICK_HARDWARE"; + + case CommandID::PRODUCTION_TEST: + return "PRODUCTION_TEST"; + case CommandID::SEND_DEBUG_DATA: + return "SEND_DEBUG_DATA"; + + case CommandID::CHANGE_UPDATE_PIN: + return "CHANGE_UPDATE_PIN"; + + case CommandID::ENABLE_ADMIN_READONLY_UNCRYPTED_LUN: + return "ENABLE_ADMIN_READONLY_UNCRYPTED_LUN"; + case CommandID::ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN: + return "ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN"; + case CommandID::ENABLE_ADMIN_READONLY_ENCRYPTED_LUN: + return "ENABLE_ADMIN_READONLY_ENCRYPTED_LUN"; + case CommandID::ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN: + return "ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN"; + case CommandID::CHECK_SMARTCARD_USAGE: + return "CHECK_SMARTCARD_USAGE"; + + case CommandID::GET_PW_SAFE_SLOT_STATUS: + return "GET_PW_SAFE_SLOT_STATUS"; + case CommandID::GET_PW_SAFE_SLOT_NAME: + return "GET_PW_SAFE_SLOT_NAME"; + case CommandID::GET_PW_SAFE_SLOT_PASSWORD: + return "GET_PW_SAFE_SLOT_PASSWORD"; + case CommandID::GET_PW_SAFE_SLOT_LOGINNAME: + return "GET_PW_SAFE_SLOT_LOGINNAME"; + case CommandID::SET_PW_SAFE_SLOT_DATA_1: + return "SET_PW_SAFE_SLOT_DATA_1"; + case CommandID::SET_PW_SAFE_SLOT_DATA_2: + return "SET_PW_SAFE_SLOT_DATA_2"; + case CommandID::PW_SAFE_ERASE_SLOT: + return "PW_SAFE_ERASE_SLOT"; + case CommandID::PW_SAFE_ENABLE: + return "PW_SAFE_ENABLE"; + case CommandID::PW_SAFE_INIT_KEY: + return "PW_SAFE_INIT_KEY"; + case CommandID::PW_SAFE_SEND_DATA: + return "PW_SAFE_SEND_DATA"; + case CommandID::SD_CARD_HIGH_WATERMARK: + return "SD_CARD_HIGH_WATERMARK"; + case CommandID::DETECT_SC_AES: + return "DETECT_SC_AES"; + case CommandID::NEW_AES_KEY: + return "NEW_AES_KEY"; + case CommandID::WRITE_TO_SLOT_2: + return "WRITE_TO_SLOT_2"; + case CommandID::SEND_OTP_DATA: + return "SEND_OTP_DATA"; + case CommandID::WINK: + return "WINK"; + } + return "UNKNOWN"; +} +} +} diff --git a/nitrokey-sys/libnitrokey-v3.5/device.cc b/nitrokey-sys/libnitrokey-v3.5/device.cc new file mode 100644 index 0000000..bc42965 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/device.cc @@ -0,0 +1,345 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hidapi/hidapi.h" +#include "libnitrokey/misc.h" +#include "libnitrokey/device.h" +#include "libnitrokey/log.h" +#include +#include "DeviceCommunicationExceptions.h" +#include "device.h" + +std::mutex mex_dev_com; + +using namespace nitrokey::device; +using namespace nitrokey::log; +using namespace nitrokey::misc; +using namespace std::chrono; + +const uint16_t nitrokey::device::NITROKEY_VID = 0x20a0; +const uint16_t nitrokey::device::NITROKEY_PRO_PID = 0x4108; +const uint16_t nitrokey::device::NITROKEY_STORAGE_PID = 0x4109; + +Option nitrokey::device::product_id_to_model(uint16_t product_id) { + switch (product_id) { + case NITROKEY_PRO_PID: + return DeviceModel::PRO; + case NITROKEY_STORAGE_PID: + return DeviceModel::STORAGE; + default: + return {}; + } +} + +std::atomic_int Device::instances_count{0}; +std::chrono::milliseconds Device::default_delay {0} ; + +std::ostream& nitrokey::device::operator<<(std::ostream& stream, DeviceModel model) { + switch (model) { + case DeviceModel::PRO: + stream << "Pro"; + break; + case DeviceModel::STORAGE: + stream << "Storage"; + break; + default: + stream << "Unknown"; + break; + } + return stream; +} + +Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, + const milliseconds send_receive_delay, const int retry_receiving_count, + const milliseconds retry_timeout) + : + last_command_status(0), + m_vid(vid), + m_pid(pid), + m_model(model), + m_retry_sending_count(1), + m_retry_receiving_count(retry_receiving_count), + m_retry_timeout(retry_timeout), + m_send_receive_delay(send_receive_delay), + mp_devhandle(nullptr) +{ + instances_count++; +} + +bool Device::disconnect() { + //called in object's destructor + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + std::lock_guard lock(mex_dev_com); + return _disconnect(); +} + +bool Device::_disconnect() { + LOG(std::string(__FUNCTION__) + std::string(m_model == DeviceModel::PRO ? "PRO" : "STORAGE"), Loglevel::DEBUG_L2); + LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); + + if(mp_devhandle == nullptr) { + LOG(std::string("Disconnection: handle already freed: ") + std::to_string(mp_devhandle == nullptr) + " ("+m_path+")", Loglevel::DEBUG_L1); + return false; + } + + hid_close(mp_devhandle); + mp_devhandle = nullptr; +#ifndef __APPLE__ + if (instances_count == 1){ + LOG(std::string("Calling hid_exit"), Loglevel::DEBUG_L2); + hid_exit(); + } +#endif + return true; +} + +bool Device::connect() { + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + std::lock_guard lock(mex_dev_com); + return _connect(); +} + +bool Device::_connect() { + LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); + +// hid_init(); // done automatically on hid_open + if (m_path.empty()){ + mp_devhandle = hid_open(m_vid, m_pid, nullptr); + } else { + mp_devhandle = hid_open_path(m_path.c_str()); + } + const bool success = mp_devhandle != nullptr; + LOG(std::string("Connection success: ") + std::to_string(success) + " ("+m_path+")", Loglevel::DEBUG_L1); + return success; +} + +void Device::set_path(const std::string path){ + m_path = path; +} + +int Device::send(const void *packet) { + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + std::lock_guard lock(mex_dev_com); + LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); + + int send_feature_report = -1; + + for (int i = 0; i < 3 && send_feature_report < 0; ++i) { + if (mp_devhandle == nullptr) { + LOG(std::string("Connection fail") , Loglevel::DEBUG_L2); + throw DeviceNotConnected("Attempted HID send on an invalid descriptor."); + } + send_feature_report = hid_send_feature_report( + mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE); + if (send_feature_report < 0) _reconnect(); + //add thread sleep? + LOG(std::string("Sending attempt: ")+std::to_string(i+1) + " / 3" , Loglevel::DEBUG_L2); + } + return send_feature_report; +} + +int Device::recv(void *packet) { + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + std::lock_guard lock(mex_dev_com); + LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); + int status; + int retry_count = 0; + + for (;;) { + if (mp_devhandle == nullptr){ + LOG(std::string("Connection fail") , Loglevel::DEBUG_L2); + throw DeviceNotConnected("Attempted HID receive on an invalid descriptor."); + } + + status = (hid_get_feature_report(mp_devhandle, (unsigned char *)(packet), + HID_REPORT_SIZE)); + + auto pwherr = hid_error(mp_devhandle); + std::wstring wherr = (pwherr != nullptr) ? pwherr : L"No error message"; + std::string herr(wherr.begin(), wherr.end()); + LOG(std::string("libhid error message: ") + herr, + Loglevel::DEBUG_L2); + + if (status > 0) break; // success + if (retry_count++ >= m_retry_receiving_count) { + LOG( + "Maximum retry count reached: " + std::to_string(retry_count), + Loglevel::WARNING); + LOG( + std::string("Counter stats: ") + m_counters.get_as_string(), + Loglevel::DEBUG); + break; + } + _reconnect(); + LOG("Retrying... " + std::to_string(retry_count), + Loglevel::DEBUG); + std::this_thread::sleep_for(m_retry_timeout); + } + + return status; +} + +std::vector Device::enumerate(){ + auto pInfo = hid_enumerate(NITROKEY_VID, 0); + auto pInfo_ = pInfo; + std::vector res; + while (pInfo != nullptr){ + auto deviceModel = product_id_to_model(pInfo->product_id); + if (deviceModel.has_value()) { + std::string path(pInfo->path); + std::wstring serialNumberW(pInfo->serial_number); + std::wstring_convert> converter; + std::string serialNumber = converter.to_bytes(serialNumberW); + DeviceInfo info = { deviceModel.value(), path, serialNumber }; + res.push_back(info); + } + pInfo = pInfo->next; + } + + if (pInfo_ != nullptr){ + hid_free_enumeration(pInfo_); + } + + return res; +} + +std::shared_ptr Device::create(DeviceModel model) { + switch (model) { + case DeviceModel::PRO: + return std::make_shared(); + case DeviceModel::STORAGE: + return std::make_shared(); + default: + return {}; + } +} + +bool Device::could_be_enumerated() { + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + std::lock_guard lock(mex_dev_com); + if (mp_devhandle==nullptr){ + return false; + } +#ifndef __APPLE__ + auto pInfo = hid_enumerate(m_vid, m_pid); + if (pInfo != nullptr){ + hid_free_enumeration(pInfo); + return true; + } + return false; +#else +// alternative for OSX + unsigned char buf[1]; + return hid_read_timeout(mp_devhandle, buf, sizeof(buf), 20) != -1; +#endif +} + +void Device::show_stats() { + auto s = m_counters.get_as_string(); + LOG(s, Loglevel::DEBUG_L2); +} + +void Device::_reconnect() { + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + ++m_counters.low_level_reconnect; + _disconnect(); + _connect(); +} + +Device::~Device() { + show_stats(); + disconnect(); + instances_count--; +} + +void Device::set_default_device_speed(int delay) { + default_delay = std::chrono::duration(delay); +} + + +void Device::set_receiving_delay(const std::chrono::milliseconds delay){ + std::lock_guard lock(mex_dev_com); + m_send_receive_delay = delay; +} + +void Device::set_retry_delay(const std::chrono::milliseconds delay){ + std::lock_guard lock(mex_dev_com); + m_retry_timeout = delay; +} + +Stick10::Stick10(): + Device(NITROKEY_VID, NITROKEY_PRO_PID, DeviceModel::PRO, 100ms, 5, 100ms) + { + setDefaultDelay(); + } + + +Stick20::Stick20(): + Device(NITROKEY_VID, NITROKEY_STORAGE_PID, DeviceModel::STORAGE, 40ms, 55, 40ms) + { + setDefaultDelay(); + } + +#include +#define p(x) ss << #x << " " << x << ", "; +std::string Device::ErrorCounters::get_as_string() { + std::stringstream ss; + p(total_comm_runs); + p(communication_successful); + ss << "("; + p(command_successful_recv); + p(command_result_not_equal_0_recv); + ss << "), "; + p(sends_executed); + p(recv_executed); + p(successful_storage_commands); + p(total_retries); + ss << "("; + p(busy); + p(busy_progressbar); + p(CRC_other_than_awaited); + p(wrong_CRC); + ss << "), "; + p(low_level_reconnect); + p(sending_error); + p(receiving_error); + return ss.str(); +} + +void Device::setDefaultDelay() { + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + + auto count = default_delay.count(); + if (count != 0){ + LOG("Setting default delay to " + std::to_string(count), Loglevel::DEBUG_L2); + m_retry_timeout = default_delay; + m_send_receive_delay = default_delay; + } +} + +#undef p diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/CommandFailedException.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/CommandFailedException.h new file mode 100644 index 0000000..32bd6b7 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/CommandFailedException.h @@ -0,0 +1,76 @@ +/* + * 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 + */ + +#ifndef LIBNITROKEY_COMMANDFAILEDEXCEPTION_H +#define LIBNITROKEY_COMMANDFAILEDEXCEPTION_H + +#include +#include +#include "log.h" +#include "command_id.h" + +using cs = nitrokey::proto::stick10::command_status; +using cs2 = nitrokey::proto::stick20::device_status; + +class CommandFailedException : public std::exception { +public: + const uint8_t last_command_id; + const uint8_t last_command_status; + + CommandFailedException(uint8_t last_command_id, uint8_t last_command_status) : + last_command_id(last_command_id), + last_command_status(last_command_status){ + LOG(std::string("CommandFailedException, status: ")+ std::to_string(last_command_status), nitrokey::log::Loglevel::DEBUG); + } + + virtual const char *what() const throw() { + return "Command execution has failed on device"; + } + + + bool reason_timestamp_warning() const throw(){ + return last_command_status == static_cast(cs::timestamp_warning); + } + + bool reason_AES_not_initialized() const throw(){ + return last_command_status == static_cast(cs::AES_dec_failed); + } + + bool reason_not_authorized() const throw(){ + return last_command_status == static_cast(cs::not_authorized); + } + + bool reason_slot_not_programmed() const throw(){ + return last_command_status == static_cast(cs::slot_not_programmed); + } + + bool reason_wrong_password() const throw(){ + return last_command_status == static_cast(cs::wrong_password); + } + + bool reason_smartcard_busy() const throw(){ + return last_command_status == static_cast(cs2::smartcard_error); + } + +}; + + +#endif //LIBNITROKEY_COMMANDFAILEDEXCEPTION_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/DeviceCommunicationExceptions.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/DeviceCommunicationExceptions.h new file mode 100644 index 0000000..f710d0b --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/DeviceCommunicationExceptions.h @@ -0,0 +1,73 @@ +/* + * 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 + */ + + +#ifndef LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H +#define LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H + +#include +#include +#include +#include + + +class DeviceCommunicationException: public std::runtime_error +{ + std::string message; + static std::atomic_int occurred; +public: + DeviceCommunicationException(std::string _msg): std::runtime_error(_msg), message(_msg){ + ++occurred; + } + uint8_t getType() const {return 1;}; +// virtual const char* what() const throw() override { +// return message.c_str(); +// } + static bool has_occurred(){ return occurred > 0; }; + static void reset_occurred_flag(){ occurred = 0; }; +}; + +class DeviceNotConnected: public DeviceCommunicationException { +public: + DeviceNotConnected(std::string msg) : DeviceCommunicationException(msg){} + uint8_t getType() const {return 2;}; +}; + +class DeviceSendingFailure: public DeviceCommunicationException { +public: + DeviceSendingFailure(std::string msg) : DeviceCommunicationException(msg){} + uint8_t getType() const {return 3;}; +}; + +class DeviceReceivingFailure: public DeviceCommunicationException { +public: + DeviceReceivingFailure(std::string msg) : DeviceCommunicationException(msg){} + uint8_t getType() const {return 4;}; +}; + +class InvalidCRCReceived: public DeviceReceivingFailure { +public: + InvalidCRCReceived(std::string msg) : DeviceReceivingFailure(msg){} + uint8_t getType() const {return 5;}; +}; + + +#endif //LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/LibraryException.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/LibraryException.h new file mode 100644 index 0000000..3b9d177 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/LibraryException.h @@ -0,0 +1,118 @@ +/* + * 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 + */ + +#ifndef LIBNITROKEY_LIBRARYEXCEPTION_H +#define LIBNITROKEY_LIBRARYEXCEPTION_H + +#include +#include +#include +#include "log.h" + +class LibraryException: std::exception { +public: + virtual uint8_t exception_id()= 0; +}; + +class TargetBufferSmallerThanSource: public LibraryException { +public: + virtual uint8_t exception_id() override { + return 203; + } + +public: + size_t source_size; + size_t target_size; + + TargetBufferSmallerThanSource( + size_t source_size, size_t target_size + ) : source_size(source_size), target_size(target_size) {} + + virtual const char *what() const throw() override { + std::string s = " "; + auto ts = [](size_t x){ return std::to_string(x); }; + std::string msg = std::string("Target buffer size is smaller than source: [source size, buffer size]") + +s+ ts(source_size) +s+ ts(target_size); + return msg.c_str(); + } + +}; + +class InvalidHexString : public LibraryException { +public: + virtual uint8_t exception_id() override { + return 202; + } + +public: + uint8_t invalid_char; + + InvalidHexString (uint8_t invalid_char) : invalid_char( invalid_char) {} + + virtual const char *what() const throw() override { + return "Invalid character in hex string"; + } + +}; + +class InvalidSlotException : public LibraryException { +public: + virtual uint8_t exception_id() override { + return 201; + } + +public: + uint8_t slot_selected; + + InvalidSlotException(uint8_t slot_selected) : slot_selected(slot_selected) {} + + virtual const char *what() const throw() override { + return "Wrong slot selected"; + } + +}; + + + +class TooLongStringException : public LibraryException { +public: + virtual uint8_t exception_id() override { + return 200; + } + + std::size_t size_source; + std::size_t size_destination; + std::string message; + + TooLongStringException(size_t size_source, size_t size_destination, const std::string &message = "") : size_source( + size_source), size_destination(size_destination), message(message) { + LOG(std::string("TooLongStringException, size diff: ")+ std::to_string(size_source-size_destination), nitrokey::log::Loglevel::DEBUG); + + } + + virtual const char *what() const throw() override { + //TODO add sizes and message data to final message + return "Too long string has been supplied as an argument"; + } + +}; + +#endif //LIBNITROKEY_LIBRARYEXCEPTION_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/LongOperationInProgressException.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/LongOperationInProgressException.h new file mode 100644 index 0000000..865d6b5 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/LongOperationInProgressException.h @@ -0,0 +1,45 @@ +/* + * 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 + */ + +#ifndef LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H +#define LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H + +#include "CommandFailedException.h" + +class LongOperationInProgressException : public CommandFailedException { + +public: + unsigned char progress_bar_value; + + LongOperationInProgressException( + unsigned char _command_id, uint8_t last_command_status, unsigned char _progress_bar_value) + : CommandFailedException(_command_id, last_command_status), progress_bar_value(_progress_bar_value){ + LOG( + std::string("LongOperationInProgressException, progress bar status: ")+ + std::to_string(progress_bar_value), nitrokey::log::Loglevel::DEBUG); + } + virtual const char *what() const throw() { + return "Device returned busy status with long operation in progress"; + } +}; + + +#endif //LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/NitrokeyManager.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/NitrokeyManager.h new file mode 100644 index 0000000..33ede1b --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/NitrokeyManager.h @@ -0,0 +1,308 @@ +/* + * 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 + */ + +#ifndef LIBNITROKEY_NITROKEYMANAGER_H +#define LIBNITROKEY_NITROKEYMANAGER_H + +#include "device.h" +#include "log.h" +#include "device_proto.h" +#include "stick10_commands.h" +#include "stick10_commands_0.8.h" +#include "stick20_commands.h" +#include +#include +#include + +namespace nitrokey { + using namespace nitrokey::device; + using namespace std; + using namespace nitrokey::proto::stick10; + using namespace nitrokey::proto::stick20; + using namespace nitrokey::proto; + using namespace nitrokey::log; + + +#ifdef __WIN32 +char * strndup(const char* str, size_t maxlen); +#endif + + class NitrokeyManager { + public: + static shared_ptr instance(); + + bool first_authenticate(const char *pin, const char *temporary_password); + bool 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); + bool 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); + string get_HOTP_code(uint8_t slot_number, const char *user_temporary_password); + string get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval, + const char *user_temporary_password); + string get_TOTP_code(uint8_t slot_number, const char *user_temporary_password); + stick10::ReadSlot::ResponsePayload get_TOTP_slot_data(const uint8_t slot_number); + stick10::ReadSlot::ResponsePayload get_HOTP_slot_data(const uint8_t slot_number); + + bool set_time(uint64_t time); + /** + * Set the device time used for TOTP to the given time. Contrary to + * {@code set_time(uint64_t)}, this command fails if {@code old_time} + * > {@code time} or if {@code old_time} is zero (where {@code + * old_time} is the current time on the device). + * + * @param time new device time as Unix timestamp (seconds since + * 1970-01-01) + */ + void set_time_soft(uint64_t time); + + [[deprecated("get_time is deprecated -- use set_time_soft instead")]] + bool get_time(uint64_t time = 0); + bool erase_totp_slot(uint8_t slot_number, const char *temporary_password); + bool erase_hotp_slot(uint8_t slot_number, const char *temporary_password); + std::vector list_devices(); + std::vector list_devices_by_cpuID(); + + /** + * Connect to the device using unique smartcard:datacard id. + * Needs list_device_by_cpuID() run first + * @param id Current ID of the target device + * @return true on success, false on failure + */ + bool connect_with_ID(const std::string id); + bool connect_with_path (std::string path); + bool connect(const char *device_model); + bool connect(device::DeviceModel device_model); + bool connect(); + bool disconnect(); + bool is_connected() throw() ; + bool could_current_device_be_enumerated(); + bool set_default_commands_delay(int delay); + + DeviceModel get_connected_device_model() const; + void set_debug(bool state); + stick10::GetStatus::ResponsePayload get_status(); + string get_status_as_string(); + string get_serial_number(); + + char * get_totp_slot_name(uint8_t slot_number); + char * get_hotp_slot_name(uint8_t slot_number); + + void change_user_PIN(const char *current_PIN, const char *new_PIN); + void change_admin_PIN(const char *current_PIN, const char *new_PIN); + + void enable_password_safe(const char *user_pin); + + vector get_password_safe_slot_status(); + + uint8_t get_admin_retry_count(); + uint8_t get_user_retry_count(); + + void lock_device(); + + char * get_password_safe_slot_name(uint8_t slot_number); + char * get_password_safe_slot_password(uint8_t slot_number); + char * get_password_safe_slot_login(uint8_t slot_number); + + void + write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, + const char *slot_password); + + void erase_password_safe_slot(uint8_t slot_number); + + void user_authenticate(const char *user_password, const char *temporary_password); + + void factory_reset(const char *admin_password); + + void build_aes_key(const char *admin_password); + + void unlock_user_password(const char *admin_password, const char *new_user_password); + + void write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password, + bool delete_user_password, const char *admin_temporary_password); + + vector read_config(); + + bool is_AES_supported(const char *user_password); + + void unlock_encrypted_volume(const char *user_password); + void lock_encrypted_volume(); + + void unlock_hidden_volume(const char *hidden_volume_password); + void lock_hidden_volume(); + + /** + * Sets unencrypted volume read-only. + * Works until v0.48 (incl. v0.50), where User PIN was sufficient + * Does nothing otherwise. + * @param user_pin User PIN + */ + void set_unencrypted_read_only(const char *user_pin); + + /** + * Sets unencrypted volume read-only. + * Works from v0.49 (except v0.50) accepts Admin PIN + * Does nothing otherwise. + * @param admin_pin Admin PIN + */ + void set_unencrypted_read_only_admin(const char *admin_pin); + + /** + * Sets unencrypted volume read-write. + * Works until v0.48 (incl. v0.50), where User PIN was sufficient + * Does nothing otherwise. + * @param user_pin User PIN + */ + void set_unencrypted_read_write(const char *user_pin); + + /** + * Sets unencrypted volume read-write. + * Works from v0.49 (except v0.50) accepts Admin PIN + * Does nothing otherwise. + * @param admin_pin Admin PIN + */ + void set_unencrypted_read_write_admin(const char *admin_pin); + + void export_firmware(const char *admin_pin); + void enable_firmware_update(const char *firmware_pin); + + void clear_new_sd_card_warning(const char *admin_pin); + + void fill_SD_card_with_random_data(const char *admin_pin); + + uint8_t get_SD_card_size(); + + void change_update_password(const char *current_update_password, const char *new_update_password); + + void create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, + const char *hidden_volume_password); + + void send_startup(uint64_t seconds_from_epoch); + + char * get_status_storage_as_string(); + stick20::DeviceConfigurationResponsePacket::ResponsePayload get_status_storage(); + + char * get_SD_usage_data_as_string(); + std::pair get_SD_usage_data(); + + + int get_progress_bar_value(); + + ~NitrokeyManager(); + bool is_authorization_command_supported(); + bool is_320_OTP_secret_supported(); + + + template + void authorize_packet(T &package, const char *admin_temporary_password, shared_ptr device); + uint8_t get_minor_firmware_version(); + + explicit NitrokeyManager(); + void set_log_function(std::function log_function); + private: + + static shared_ptr _instance; + std::shared_ptr device; + std::string current_device_id; + public: + const string get_current_device_id() const; + + private: + std::unordered_map > connected_devices; + std::unordered_map > connected_devices_byID; + + + stick10::ReadSlot::ResponsePayload get_OTP_slot_data(const uint8_t slot_number); + bool is_valid_hotp_slot_number(uint8_t slot_number) const; + bool is_valid_totp_slot_number(uint8_t slot_number) const; + bool is_valid_password_safe_slot_number(uint8_t slot_number) const; + uint8_t get_internal_slot_number_for_hotp(uint8_t slot_number) const; + uint8_t get_internal_slot_number_for_totp(uint8_t slot_number) const; + bool erase_slot(uint8_t slot_number, const char *temporary_password); + char * get_slot_name(uint8_t slot_number); + + template + void change_PIN_general(const char *current_PIN, const char *new_PIN); + + void write_HOTP_slot_authorize(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); + + void write_TOTP_slot_authorize(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); + + void write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name, const char *secret, + uint64_t counter_or_interval, + bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID, + const char *temporary_password) const; + bool _disconnect_no_lock(); + + public: + bool set_current_device_speed(int retry_delay, int send_receive_delay); + void set_loglevel(Loglevel loglevel); + + void set_loglevel(int loglevel); + + /** + * Sets encrypted volume read-only. + * Supported from future versions of Storage. + * @param admin_pin Admin PIN + */ + void set_encrypted_volume_read_only(const char *admin_pin); + + /** + * Sets encrypted volume read-write. + * Supported from future versions of Storage. + * @param admin_pin Admin PIN + */ + void set_encrypted_volume_read_write(const char *admin_pin); + + uint8_t get_major_firmware_version(); + + bool is_smartcard_in_use(); + + /** + * Function to determine unencrypted volume PIN type + * @param minor_firmware_version + * @return Returns true, if set unencrypted volume ro/rw pin type is User, false otherwise. + */ + bool set_unencrypted_volume_rorw_pin_type_user(); + + /** + * Blink red and green LED alternatively and infinitely (until device is reconnected). + */ + void wink(); + + stick20::ProductionTest::ResponsePayload production_info(); + + void enable_firmware_update_pro(const char *firmware_pin); + + void change_firmware_update_password_pro(const char *firmware_pin_current, const char *firmware_pin_new); + bool is_internal_hotp_slot_number(uint8_t slot_number) const; + }; +} + + + +#endif //LIBNITROKEY_NITROKEYMANAGER_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/command.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/command.h new file mode 100644 index 0000000..6852bf0 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/command.h @@ -0,0 +1,112 @@ +/* + * 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 + */ + +#ifndef COMMAND_H +#define COMMAND_H +#include +#include "command_id.h" +#include "cxx_semantics.h" + +#define print_to_ss(x) ( ss << " " << (#x) <<":\t" << (x) << std::endl ); +#ifdef LOG_VOLATILE_DATA +#define print_to_ss_volatile(x) print_to_ss(x); +#else +#define print_to_ss_volatile(x) ( ss << " " << (#x) <<":\t" << "***********" << std::endl ); +#endif +#define hexdump_to_ss(x) (ss << #x":\n"\ + << ::nitrokey::misc::hexdump((const uint8_t *) (&x), sizeof x, false)); + +namespace nitrokey { + namespace proto { + + template + class Command : semantics::non_constructible { + public: + constexpr static CommandID command_id() { return cmd_id; } + + template + std::string dissect(const T &) { + return std::string("Payload dissection is unavailable"); + } + }; + +namespace stick20{ + enum class PasswordKind : uint8_t { + User = 'P', + Admin = 'A', + AdminPrefixed + }; + + template + class PasswordCommand : public Command { + constexpr static CommandID _command_id() { return cmd_id; } + public: + struct CommandPayload { + uint8_t kind; + uint8_t password[password_length]; + + std::string dissect() const { + std::stringstream ss; + print_to_ss( kind ); + print_to_ss_volatile(password); + return ss.str(); + } + void set_kind_admin() { + kind = (uint8_t) 'A'; + } + void set_kind_admin_prefixed() { + kind = (uint8_t) 'P'; + } + void set_kind_user() { + kind = (uint8_t) 'P'; + } + + void set_defaults(){ + set_kind(Tpassword_kind); + } + + void set_kind(PasswordKind password_kind){ + switch (password_kind){ + case PasswordKind::Admin: + set_kind_admin(); + break; + case PasswordKind::User: + set_kind_user(); + break; + case PasswordKind::AdminPrefixed: + set_kind_admin_prefixed(); + break; + } + }; + + } __packed; + + //typedef Transaction::command_id(), struct CommandPayload, struct EmptyPayload> + // CommandTransaction; + using CommandTransaction = Transaction; + //using CommandTransaction = Transaction<_command_id(), CommandPayload, EmptyPayload>; + + }; + } + } +} + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/command_id.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/command_id.h new file mode 100644 index 0000000..ee6726c --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/command_id.h @@ -0,0 +1,156 @@ +/* + * 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 + */ + +#ifndef COMMAND_ID_H +#define COMMAND_ID_H +#include + +namespace nitrokey { +namespace proto { + namespace stick20 { + enum class device_status : uint8_t { + idle = 0, + ok, + busy, + wrong_password, + busy_progressbar, + password_matrix_ready, + no_user_password_unlock, // FIXME: translate on receive to command status error (fix in firmware?) + smartcard_error, + security_bit_active + }; + const int CMD_START_VALUE = 0x20; + const int CMD_END_VALUE = 0x60; + } + namespace stick10 { + enum class command_status : uint8_t { + ok = 0, + wrong_CRC, + wrong_slot, + slot_not_programmed, + wrong_password = 4, + not_authorized, + timestamp_warning, + no_name_error, + not_supported, + unknown_command, + AES_dec_failed + }; + enum class device_status : uint8_t { + ok = 0, + busy = 1, + error, + received_report, + }; + } + + +enum class CommandID : uint8_t { + GET_STATUS = 0x00, + WRITE_TO_SLOT = 0x01, + READ_SLOT_NAME = 0x02, + READ_SLOT = 0x03, + GET_CODE = 0x04, + WRITE_CONFIG = 0x05, + ERASE_SLOT = 0x06, + FIRST_AUTHENTICATE = 0x07, + AUTHORIZE = 0x08, + GET_PASSWORD_RETRY_COUNT = 0x09, + CLEAR_WARNING = 0x0A, + SET_TIME = 0x0B, + TEST_COUNTER = 0x0C, + TEST_TIME = 0x0D, + USER_AUTHENTICATE = 0x0E, + GET_USER_PASSWORD_RETRY_COUNT = 0x0F, + USER_AUTHORIZE = 0x10, + UNLOCK_USER_PASSWORD = 0x11, + LOCK_DEVICE = 0x12, + FACTORY_RESET = 0x13, + CHANGE_USER_PIN = 0x14, + CHANGE_ADMIN_PIN = 0x15, + WRITE_TO_SLOT_2 = 0x16, + SEND_OTP_DATA = 0x17, + FIRMWARE_UPDATE = 0x19, + FIRMWARE_PASSWORD_CHANGE = 0x1A, + + ENABLE_CRYPTED_PARI = 0x20, + DISABLE_CRYPTED_PARI = 0x20 + 1, + ENABLE_HIDDEN_CRYPTED_PARI = 0x20 + 2, + DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3, + ENABLE_FIRMWARE_UPDATE = 0x20 + 4, //enables update mode + EXPORT_FIRMWARE_TO_FILE = 0x20 + 5, + GENERATE_NEW_KEYS = 0x20 + 6, + FILL_SD_CARD_WITH_RANDOM_CHARS = 0x20 + 7, + + WRITE_STATUS_DATA = 0x20 + 8, //@unused + ENABLE_READONLY_UNCRYPTED_LUN = 0x20 + 9, + ENABLE_READWRITE_UNCRYPTED_LUN = 0x20 + 10, + + SEND_PASSWORD_MATRIX = 0x20 + 11, //@unused + SEND_PASSWORD_MATRIX_PINDATA = 0x20 + 12, //@unused + SEND_PASSWORD_MATRIX_SETUP = 0x20 + 13, //@unused + + GET_DEVICE_STATUS = 0x20 + 14, + SEND_DEVICE_STATUS = 0x20 + 15, + + SEND_HIDDEN_VOLUME_PASSWORD = 0x20 + 16, //@unused + SEND_HIDDEN_VOLUME_SETUP = 0x20 + 17, + SEND_PASSWORD = 0x20 + 18, + SEND_NEW_PASSWORD = 0x20 + 19, + CLEAR_NEW_SD_CARD_FOUND = 0x20 + 20, + + SEND_STARTUP = 0x20 + 21, + SEND_CLEAR_STICK_KEYS_NOT_INITIATED = 0x20 + 22, + SEND_LOCK_STICK_HARDWARE = 0x20 + 23, //locks firmware upgrade + + PRODUCTION_TEST = 0x20 + 24, + SEND_DEBUG_DATA = 0x20 + 25, //@unused + + CHANGE_UPDATE_PIN = 0x20 + 26, + + //added in v0.48.5 + ENABLE_ADMIN_READONLY_UNCRYPTED_LUN = 0x20 + 28, + ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN = 0x20 + 29, + ENABLE_ADMIN_READONLY_ENCRYPTED_LUN = 0x20 + 30, + ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN = 0x20 + 31, + CHECK_SMARTCARD_USAGE = 0x20 + 32, + //v0.52+ + WINK = 0x20 + 33, + + GET_PW_SAFE_SLOT_STATUS = 0x60, + GET_PW_SAFE_SLOT_NAME = 0x61, + GET_PW_SAFE_SLOT_PASSWORD = 0x62, + GET_PW_SAFE_SLOT_LOGINNAME = 0x63, + SET_PW_SAFE_SLOT_DATA_1 = 0x64, + SET_PW_SAFE_SLOT_DATA_2 = 0x65, + PW_SAFE_ERASE_SLOT = 0x66, + PW_SAFE_ENABLE = 0x67, + PW_SAFE_INIT_KEY = 0x68, //@unused + PW_SAFE_SEND_DATA = 0x69, //@unused + SD_CARD_HIGH_WATERMARK = 0x70, + DETECT_SC_AES = 0x6a, + NEW_AES_KEY = 0x6b +}; + +const char *commandid_to_string(CommandID id); +} +} +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/cxx_semantics.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/cxx_semantics.h new file mode 100644 index 0000000..36ed142 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/cxx_semantics.h @@ -0,0 +1,44 @@ +/* + * 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 + */ + +#ifndef CXX_SEMANTICS_H +#define CXX_SEMANTICS_H + +#ifndef _MSC_VER +#define __packed __attribute__((__packed__)) +#else +#define __packed +#endif + +#ifdef _MSC_VER +#define strdup _strdup +#endif + +/* + * There's no need to include Boost for a simple subset this project needs. + */ +namespace semantics { +class non_constructible { + non_constructible() {} +}; +} + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/deprecated.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/deprecated.h new file mode 100644 index 0000000..5a83288 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/deprecated.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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 + */ + + +#ifndef LIBNITROKEY_DEPRECATED_H +#define LIBNITROKEY_DEPRECATED_H + +#if defined(__GNUC__) || defined(__clang__) +#define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: DEPRECATED macro is not defined for this compiler") +#define DEPRECATED +#endif + +#endif //LIBNITROKEY_DEPRECATED_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/device.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/device.h new file mode 100644 index 0000000..d50080d --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/device.h @@ -0,0 +1,224 @@ +/* + * 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 + */ + +#ifndef DEVICE_H +#define DEVICE_H +#include +#include "hidapi/hidapi.h" +#include +#include +#include +#include +#include +#include "misc.h" + +#define HID_REPORT_SIZE 65 + +#include + +namespace nitrokey { +namespace device { + using namespace std::chrono_literals; + using std::chrono::milliseconds; + + struct EnumClassHash + { + template + std::size_t operator()(T t) const + { + return static_cast(t); + } + }; + +enum class DeviceModel{ + PRO, + STORAGE +}; + +std::ostream& operator<<(std::ostream& stream, DeviceModel model); + +/** + * The USB vendor ID for Nitrokey devices. + */ +extern const uint16_t NITROKEY_VID; +/** + * The USB product ID for the Nitrokey Pro. + */ +extern const uint16_t NITROKEY_PRO_PID; +/** + * The USB product ID for the Nitrokey Storage. + */ +extern const uint16_t NITROKEY_STORAGE_PID; + +/** + * Convert the given USB product ID to a Nitrokey model. If there is no model + * with that ID, return an absent value. + */ +misc::Option product_id_to_model(uint16_t product_id); + +/** + * Information about a connected device. + * + * This struct contains the information about a connected device returned by + * hidapi when enumerating the connected devices. + */ +struct DeviceInfo { + /** + * The model of the connected device. + */ + DeviceModel m_deviceModel; + /** + * The USB connection path for the device. + */ + std::string m_path; + /** + * The serial number of the device. + */ + std::string m_serialNumber; +}; + +#include + +class Device { + +public: + + struct ErrorCounters{ + using cnt = std::atomic_int; + cnt wrong_CRC; + cnt CRC_other_than_awaited; + cnt busy; + cnt total_retries; + cnt sending_error; + cnt receiving_error; + cnt total_comm_runs; + cnt successful_storage_commands; + cnt command_successful_recv; + cnt recv_executed; + cnt sends_executed; + cnt busy_progressbar; + cnt command_result_not_equal_0_recv; + cnt communication_successful; + cnt low_level_reconnect; + std::string get_as_string(); + + } m_counters = {}; + + + Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, + const milliseconds send_receive_delay, const int retry_receiving_count, + const milliseconds retry_timeout); + + virtual ~Device(); + + // lack of device is not actually an error, + // so it doesn't throw + virtual bool connect(); + virtual bool disconnect(); + + /* + * Sends packet of HID_REPORT_SIZE. + */ + virtual int send(const void *packet); + + /* + * Gets packet of HID_REPORT_SIZE. + * Can sleep. See below. + */ + virtual int recv(void *packet); + + /*** + * Returns true if some device is visible by OS with given VID and PID + * whether the device is connected through HID API or not. + * @return true if visible by OS + */ + bool could_be_enumerated(); + /** + * Returns a vector with all connected Nitrokey devices. + * + * @return information about all connected devices + */ + static std::vector enumerate(); + + /** + * Create a Device of the given model. + */ + static std::shared_ptr create(DeviceModel model); + + + void show_stats(); +// ErrorCounters get_stats(){ return m_counters; } + int get_retry_receiving_count() const { return m_retry_receiving_count; }; + int get_retry_sending_count() const { return m_retry_sending_count; }; + std::chrono::milliseconds get_retry_timeout() const { return m_retry_timeout; }; + std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;} + + int get_last_command_status() {int a = std::atomic_exchange(&last_command_status, static_cast(0)); return a;}; + void set_last_command_status(uint8_t _err) { last_command_status = _err;} ; + bool last_command_sucessfull() const {return last_command_status == 0;}; + DeviceModel get_device_model() const {return m_model;} + void set_receiving_delay(std::chrono::milliseconds delay); + void set_retry_delay(std::chrono::milliseconds delay); + static void set_default_device_speed(int delay); + void setDefaultDelay(); + void set_path(const std::string path); + + + private: + std::atomic last_command_status; + void _reconnect(); + bool _connect(); + bool _disconnect(); + +protected: + const uint16_t m_vid; + const uint16_t m_pid; + const DeviceModel m_model; + + /* + * While the project uses Signal11 portable HIDAPI + * library, there's no way of doing it asynchronously, + * hence polling. + */ + const int m_retry_sending_count; + const int m_retry_receiving_count; + std::chrono::milliseconds m_retry_timeout; + std::chrono::milliseconds m_send_receive_delay; + std::atomicmp_devhandle; + std::string m_path; + + static std::atomic_int instances_count; + static std::chrono::milliseconds default_delay ; +}; + +class Stick10 : public Device { + public: + Stick10(); + +}; + +class Stick20 : public Device { + public: + Stick20(); +}; +} +} +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/device_proto.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/device_proto.h new file mode 100644 index 0000000..45a6c16 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/device_proto.h @@ -0,0 +1,491 @@ +/* + * 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 + */ + +#ifndef DEVICE_PROTO_H +#define DEVICE_PROTO_H + +#include +#include +#include +#include +#include +// a local version for compatibility with Windows +#include +#include "cxx_semantics.h" +#include "device.h" +#include "misc.h" +#include "log.h" +#include "command_id.h" +#include "dissect.h" +#include "CommandFailedException.h" +#include "LongOperationInProgressException.h" + +#define STICK20_UPDATE_MODE_VID 0x03EB +#define STICK20_UPDATE_MODE_PID 0x2FF1 + +#define PAYLOAD_SIZE 53 +#define PWS_SLOT_COUNT 16 +#define PWS_SLOTNAME_LENGTH 11 +#define PWS_PASSWORD_LENGTH 20 +#define PWS_LOGINNAME_LENGTH 32 + +#define PWS_SEND_PASSWORD 0 +#define PWS_SEND_LOGINNAME 1 +#define PWS_SEND_TAB 2 +#define PWS_SEND_CR 3 + +#include +#include "DeviceCommunicationExceptions.h" +#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) + +namespace nitrokey { + namespace proto { + extern std::mutex send_receive_mtx; + + +/* + * POD types for HID proto commands + * Instances are meant to be __packed. + * + * TODO (future) support for Big Endian + */ +#pragma pack (push,1) +/* + * Every packet is a USB HID report (check USB spec) + */ + template + struct HIDReport { + uint8_t _zero; + CommandID command_id; // uint8_t + union { + uint8_t _padding[HID_REPORT_SIZE - 6]; + Payload payload; + } __packed; + uint32_t crc; + + // POD types can't have non-default constructors + // used in Transaction<>::run() + void initialize() { + bzero(this, sizeof *this); + command_id = cmd_id; + } + + uint32_t calculate_CRC() const { + // w/o leading zero, a part of each HID packet + // w/o 4-byte crc + return misc::stm_crc32((const uint8_t *) (this) + 1, + (size_t) (HID_REPORT_SIZE - 5)); + } + + void update_CRC() { crc = calculate_CRC(); } + + bool isCRCcorrect() const { return crc == calculate_CRC(); } + + bool isValid() const { + return true; + // return !_zero && payload.isValid() && isCRCcorrect(); + } + + operator std::string() const { + // Packet type is known upfront in normal operation. + // Can't be used to dissect random packets. + return QueryDissector::dissect(*this); + } + } __packed; + +/* + * Response payload (the parametrized type inside struct HIDReport) + * + * command_id member in incoming HIDReport structure carries the command + * type last used. + */ + namespace DeviceResponseConstants{ + //magic numbers from firmware + static constexpr auto storage_status_absolute_address = 21; + static constexpr auto storage_data_absolute_address = storage_status_absolute_address + 5; + static constexpr auto header_size = 8; //from _zero to last_command_status inclusive + static constexpr auto footer_size = 4; //crc + static constexpr auto wrapping_size = header_size + footer_size; + } + + template + struct DeviceResponse { + static constexpr auto storage_status_padding_size = + DeviceResponseConstants::storage_status_absolute_address - DeviceResponseConstants::header_size; + + uint8_t _zero; + uint8_t device_status; + uint8_t command_id; // originally last_command_type + uint32_t last_command_crc; + uint8_t last_command_status; + + union { + uint8_t _padding[HID_REPORT_SIZE - DeviceResponseConstants::wrapping_size]; + ResponsePayload payload; + struct { + uint8_t _storage_status_padding[storage_status_padding_size]; + uint8_t command_counter; + uint8_t command_id; + uint8_t device_status; //@see stick20::device_status + uint8_t progress_bar_value; + } __packed storage_status; + } __packed; + + uint32_t crc; + + void initialize() { bzero(this, sizeof *this); } + + uint32_t calculate_CRC() const { + // w/o leading zero, a part of each HID packet + // w/o 4-byte crc + return misc::stm_crc32((const uint8_t *) (this) + 1, + (size_t) (HID_REPORT_SIZE - 5)); + } + + void update_CRC() { crc = calculate_CRC(); } + bool isCRCcorrect() const { return crc == calculate_CRC(); } + bool isValid() const { + // return !_zero && payload.isValid() && isCRCcorrect() && + // command_id == (uint8_t)(cmd_id); + return crc != 0; + } + + operator std::string() const { + return ResponseDissector::dissect(*this); + } + } __packed; + + struct EmptyPayload { + bool isValid() const { return true; } + + std::string dissect() const { return std::string("Empty Payload."); } + } __packed; + + template + class ClearingProxy { + public: + ClearingProxy(command_packet &p) { + packet = p; + bzero(&p, sizeof(p)); + } + + ~ClearingProxy() { + bzero(&packet, sizeof(packet)); + } + + response_payload &data() { + return packet.payload; + } + + command_packet packet; + }; + + template + class Transaction : semantics::non_constructible { + public: + // Types declared in command class scope can't be reached from there. + typedef command_payload CommandPayload; + typedef response_payload ResponsePayload; + + + typedef struct HIDReport OutgoingPacket; + typedef struct DeviceResponse ResponsePacket; +#pragma pack (pop) + + static_assert(std::is_pod::value, + "outgoingpacket must be a pod type"); + static_assert(std::is_pod::value, + "ResponsePacket must be a POD type"); + static_assert(sizeof(OutgoingPacket) == HID_REPORT_SIZE, + "OutgoingPacket type is not the right size"); + static_assert(sizeof(ResponsePacket) == HID_REPORT_SIZE, + "ResponsePacket type is not the right size"); + + static uint32_t getCRC( + const command_payload &payload) { + OutgoingPacket outp; + outp.initialize(); + outp.payload = payload; + outp.update_CRC(); + return outp.crc; + } + + template + static void clear_packet(T &st) { + bzero(&st, sizeof(st)); + } + + static ClearingProxy run(std::shared_ptr dev, + const command_payload &payload) { + using namespace ::nitrokey::device; + using namespace ::nitrokey::log; + using namespace std::chrono_literals; + + std::lock_guard guard(send_receive_mtx); + + LOG(__FUNCTION__, Loglevel::DEBUG_L2); + + if (dev == nullptr){ + LOG(std::string("Throw: Device not initialized"), Loglevel::DEBUG_L1); + throw DeviceNotConnected("Device not initialized"); + } + dev->m_counters.total_comm_runs++; + + int status; + OutgoingPacket outp; + ResponsePacket resp; + + // POD types can't have non-default constructors + outp.initialize(); + resp.initialize(); + + outp.payload = payload; + outp.update_CRC(); + + LOG("-------------------", Loglevel::DEBUG); + LOG("Outgoing HID packet:", Loglevel::DEBUG); + LOG(static_cast(outp), Loglevel::DEBUG); + LOG(std::string("=> ") + std::string(commandid_to_string(static_cast(outp.command_id))), Loglevel::DEBUG_L1); + + + if (!outp.isValid()) { + LOG(std::string("Throw: Invalid outgoing packet"), Loglevel::DEBUG_L1); + throw DeviceSendingFailure("Invalid outgoing packet"); + } + + bool successful_communication = false; + int receiving_retry_counter = 0; + int sending_retry_counter = dev->get_retry_sending_count(); + while (sending_retry_counter-- > 0) { + dev->m_counters.sends_executed++; + status = dev->send(&outp); + if (status <= 0){ + //FIXME early disconnection not yet working properly +// LOG("Encountered communication error, disconnecting device", Loglevel::DEBUG_L2); +// dev->disconnect(); + dev->m_counters.sending_error++; + LOG(std::string("Throw: Device error while sending command "), Loglevel::DEBUG_L1); + throw DeviceSendingFailure( + std::string("Device error while sending command ") + + std::to_string(status)); + } + + std::this_thread::sleep_for(dev->get_send_receive_delay()); + + // FIXME make checks done in device:recv here + receiving_retry_counter = dev->get_retry_receiving_count(); + int busy_counter = 0; + auto retry_timeout = dev->get_retry_timeout(); + while (receiving_retry_counter-- > 0) { + dev->m_counters.recv_executed++; + status = dev->recv(&resp); + + if (dev->get_device_model() == DeviceModel::STORAGE && + resp.command_id >= stick20::CMD_START_VALUE && + resp.command_id < stick20::CMD_END_VALUE ) { + LOG(std::string("Detected storage device cmd, status: ") + + std::to_string(resp.storage_status.device_status), Loglevel::DEBUG_L2); + + resp.last_command_status = static_cast(stick10::command_status::ok); + switch (static_cast(resp.storage_status.device_status)) { + case stick20::device_status::idle : + case stick20::device_status::ok: + resp.device_status = static_cast(stick10::device_status::ok); + break; + case stick20::device_status::busy: + case stick20::device_status::busy_progressbar: //TODO this will be modified later for getting progressbar status + resp.device_status = static_cast(stick10::device_status::busy); + break; + case stick20::device_status::wrong_password: + resp.last_command_status = static_cast(stick10::command_status::wrong_password); + resp.device_status = static_cast(stick10::device_status::ok); + break; + case stick20::device_status::no_user_password_unlock: + resp.last_command_status = static_cast(stick10::command_status::AES_dec_failed); + resp.device_status = static_cast(stick10::device_status::ok); + break; + default: + LOG(std::string("Unknown storage device status, cannot translate: ") + + std::to_string(resp.storage_status.device_status), Loglevel::DEBUG); + resp.device_status = resp.storage_status.device_status; + break; + }; + } + + //Some of the commands return wrong CRC, for now skip checking it (TODO list and report) + //if (resp.device_status == 0 && resp.last_command_crc == outp.crc && resp.isCRCcorrect()) break; + auto CRC_equal_awaited = true; // resp.last_command_crc == outp.crc; + if (resp.device_status == static_cast(stick10::device_status::ok) && + CRC_equal_awaited && resp.isValid()){ + successful_communication = true; + break; + } + if (resp.device_status == static_cast(stick10::device_status::busy)) { + dev->m_counters.busy++; + + if (busy_counter++<10) { + receiving_retry_counter++; + LOG("Status busy, not decreasing receiving_retry_counter counter: " + + std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2); + } else { + retry_timeout *= 2; + retry_timeout = std::min(retry_timeout, 300ms); + busy_counter = 0; + LOG("Status busy, decreasing receiving_retry_counter counter: " + + std::to_string(receiving_retry_counter) + ", current delay:" + + std::to_string(retry_timeout.count()), Loglevel::DEBUG); + LOG(std::string("Busy retry: status ") + + std::to_string(resp.storage_status.device_status) + + ", " + + std::to_string(retry_timeout.count()) + + "ms, counter " + + std::to_string(receiving_retry_counter) + + ", progress: " + + std::to_string(resp.storage_status.progress_bar_value) + , Loglevel::DEBUG_L1); + } + } + if (resp.device_status == static_cast(stick10::device_status::busy) && + static_cast(resp.storage_status.device_status) + == stick20::device_status::busy_progressbar){ + successful_communication = true; + break; + } + LOG(std::string("Retry status - dev status, awaited cmd crc, correct packet CRC: ") + + std::to_string(resp.device_status) + + " " + std::to_string(CRC_equal_awaited) + + " " + std::to_string(resp.isCRCcorrect()), Loglevel::DEBUG_L2); + + if (!resp.isCRCcorrect()) dev->m_counters.wrong_CRC++; + if (!CRC_equal_awaited) dev->m_counters.CRC_other_than_awaited++; + + + LOG( + "Device is not ready or received packet's last CRC is not equal to sent CRC packet, retrying...", + Loglevel::DEBUG_L2); + LOG("Invalid incoming HID packet:", Loglevel::DEBUG_L2); + LOG(static_cast(resp), Loglevel::DEBUG_L2); + dev->m_counters.total_retries++; + LOG(".", Loglevel::DEBUG_L1); + std::this_thread::sleep_for(retry_timeout); + continue; + } + if (successful_communication) break; + LOG(std::string("Resending (outer loop) "), Loglevel::DEBUG_L2); + LOG(std::string("sending_retry_counter count: ") + std::to_string(sending_retry_counter), + Loglevel::DEBUG); + } + + if(resp.last_command_crc != outp.crc){ + LOG(std::string("Accepting response with CRC other than expected ") + + "Command ID: " + std::to_string(resp.command_id) + " " + + commandid_to_string(static_cast(resp.command_id)) + " " + + "Reported by response and expected: " + std::to_string(resp.last_command_crc) + "!=" + std::to_string(outp.crc), + Loglevel::WARNING + ); + } + + dev->set_last_command_status(resp.last_command_status); // FIXME should be handled on device.recv + + clear_packet(outp); + + + if (status <= 0) { + dev->m_counters.receiving_error++; + LOG(std::string("Throw: Device error while executing command "), Loglevel::DEBUG_L1); + throw DeviceReceivingFailure( //FIXME replace with CriticalErrorException + std::string("Device error while executing command ") + + std::to_string(status)); + } + + LOG(std::string("<= ") + + std::string( + commandid_to_string(static_cast(resp.command_id)) + + std::string(" ") + + std::to_string(resp.device_status) + + std::string(" ") + + std::to_string(resp.storage_status.device_status) +// + std::to_string( status_translate_command(resp.storage_status.device_status)) + ), Loglevel::DEBUG_L1); + + LOG("Incoming HID packet:", Loglevel::DEBUG); + LOG(static_cast(resp), Loglevel::DEBUG); + if (dev->get_retry_receiving_count() - receiving_retry_counter > 2) { + LOG(std::string("Packet received with receiving_retry_counter count: ") + + std::to_string(receiving_retry_counter), + Loglevel::DEBUG_L1); + } + + if (resp.device_status == static_cast(stick10::device_status::busy) && + static_cast(resp.storage_status.device_status) + == stick20::device_status::busy_progressbar){ + dev->m_counters.busy_progressbar++; + LOG(std::string("Throw: Long operation in progress exception"), Loglevel::DEBUG_L1); + throw LongOperationInProgressException( + resp.command_id, resp.device_status, resp.storage_status.progress_bar_value); + } + + if (!resp.isValid()) { + LOG(std::string("Throw: Invalid incoming packet"), Loglevel::DEBUG_L1); + throw InvalidCRCReceived("Invalid incoming packet"); + } + if (receiving_retry_counter <= 0){ + LOG(std::string("Throw: \"Maximum receiving_retry_counter count reached for receiving response from the device!\"" + + std::to_string(receiving_retry_counter)), Loglevel::DEBUG_L1); + throw DeviceReceivingFailure( + "Maximum receiving_retry_counter count reached for receiving response from the device!"); + } + dev->m_counters.communication_successful++; + + if (resp.last_command_status != static_cast(stick10::command_status::ok)){ + dev->m_counters.command_result_not_equal_0_recv++; + LOG(std::string("Throw: CommandFailedException ") + std::to_string(resp.last_command_status), Loglevel::DEBUG_L1); + throw CommandFailedException(resp.command_id, resp.last_command_status); + } + + dev->m_counters.command_successful_recv++; + + if (dev->get_device_model() == DeviceModel::STORAGE && + resp.command_id >= stick20::CMD_START_VALUE && + resp.command_id < stick20::CMD_END_VALUE ) { + dev->m_counters.successful_storage_commands++; + } + + if (!resp.isCRCcorrect()) + LOG(std::string("Accepting response from device with invalid CRC. ") + + "Command ID: " + std::to_string(resp.command_id) + " " + + commandid_to_string(static_cast(resp.command_id)) + " " + + "Reported and calculated: " + std::to_string(resp.crc) + "!=" + std::to_string(resp.calculate_CRC()), + Loglevel::WARNING + ); + + // See: DeviceResponse + return resp; + } + + static ClearingProxy run(std::shared_ptr dev) { + command_payload empty_payload; + return run(dev, empty_payload); + } + }; + } +} +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/dissect.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/dissect.h new file mode 100644 index 0000000..690b5b7 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/dissect.h @@ -0,0 +1,145 @@ +/* + * 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 + */ + +/* + * Protocol packet dissection + */ +#ifndef DISSECT_H +#define DISSECT_H +#include +#include +#include +#include "misc.h" +#include "cxx_semantics.h" +#include "command_id.h" +#include "device_proto.h" + +namespace nitrokey { +namespace proto { + +template +class QueryDissector : semantics::non_constructible { + public: + static std::string dissect(const HIDPacket &pod) { + std::stringstream out; + +#ifdef LOG_VOLATILE_DATA + out << "Raw HID packet:" << std::endl; + out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod); +#endif + + out << "Contents:" << std::endl; + out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) + << std::endl; + out << "CRC:\t" + << std::hex << std::setw(2) << std::setfill('0') + << pod.crc << std::endl; + + out << "Payload:" << std::endl; + out << pod.payload.dissect(); + return out.str(); + } +}; + + + + +template +class ResponseDissector : semantics::non_constructible { + public: + static std::string status_translate_device(int status){ + auto enum_status = static_cast(status); + switch (enum_status){ + case stick10::device_status::ok: return "OK"; + case stick10::device_status::busy: return "BUSY"; + case stick10::device_status::error: return "ERROR"; + case stick10::device_status::received_report: return "RECEIVED_REPORT"; + } + return std::string("UNKNOWN: ") + std::to_string(status); + } + + static std::string to_upper(std::string str){ + for (auto & c: str) c = toupper(c); + return str; + } + static std::string status_translate_command(int status){ + auto enum_status = static_cast(status); + switch (enum_status) { +#define p(X) case X: return to_upper(std::string(#X)); + p(stick10::command_status::ok) + p(stick10::command_status::wrong_CRC) + p(stick10::command_status::wrong_slot) + p(stick10::command_status::slot_not_programmed) + p(stick10::command_status::wrong_password) + p(stick10::command_status::not_authorized) + p(stick10::command_status::timestamp_warning) + p(stick10::command_status::no_name_error) + p(stick10::command_status::not_supported) + p(stick10::command_status::unknown_command) + p(stick10::command_status::AES_dec_failed) +#undef p + } + return std::string("UNKNOWN: ") + std::to_string(status); + } + + static std::string dissect(const HIDPacket &pod) { + std::stringstream out; + + // FIXME use values from firmware (possibly generate separate + // header automatically) + +#ifdef LOG_VOLATILE_DATA + out << "Raw HID packet:" << std::endl; + out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod); +#endif + + out << "Device status:\t" << pod.device_status + 0 << " " + << status_translate_device(pod.device_status) << std::endl; + out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) << " hex: " << std::hex << (int)pod.command_id + << std::endl; + out << "Last command CRC:\t" + << std::hex << std::setw(2) << std::setfill('0') + << pod.last_command_crc << std::endl; + out << "Last command status:\t" << pod.last_command_status + 0 << " " + << status_translate_command(pod.last_command_status) << std::endl; + out << "CRC:\t" + << std::hex << std::setw(2) << std::setfill('0') + << pod.crc << std::endl; + if((int)pod.command_id == pod.storage_status.command_id){ + out << "Storage stick status (where applicable):" << std::endl; +#define d(x) out << " "#x": \t"<< std::hex << std::setw(2) \ + << std::setfill('0')<< static_cast(x) << std::endl; + d(pod.storage_status.command_counter); + d(pod.storage_status.command_id); + d(pod.storage_status.device_status); + d(pod.storage_status.progress_bar_value); +#undef d + } + + out << "Payload:" << std::endl; + out << pod.payload.dissect(); + return out.str(); + } +}; +} +} + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/hidapi/hidapi.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/hidapi/hidapi.h new file mode 100644 index 0000000..e5bc2dc --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/hidapi/hidapi.h @@ -0,0 +1,391 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/log.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/log.h new file mode 100644 index 0000000..278b49c --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/log.h @@ -0,0 +1,109 @@ +/* + * 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 + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include + +namespace nitrokey { + namespace log { + +//for MSVC +#ifdef ERROR +#undef ERROR +#endif + + + enum class Loglevel : int { + ERROR, + WARNING, + INFO, + DEBUG_L1, + DEBUG, + DEBUG_L2 + }; + + class LogHandler { + public: + virtual void print(const std::string &, Loglevel lvl) = 0; + virtual ~LogHandler() = default; + protected: + std::string loglevel_to_str(Loglevel); + std::string format_message_to_string(const std::string &str, const Loglevel &lvl); + + }; + + class StdlogHandler : public LogHandler { + public: + virtual void print(const std::string &, Loglevel lvl); + }; + + class FunctionalLogHandler : public LogHandler { + using log_function_type = std::function; + log_function_type log_function; + public: + FunctionalLogHandler(log_function_type _log_function); + virtual void print(const std::string &, Loglevel lvl); + + }; + + extern StdlogHandler stdlog_handler; + + class Log { + public: + Log() : mp_loghandler(&stdlog_handler), m_loglevel(Loglevel::WARNING) {} + + static Log &instance() { + if (mp_instance == nullptr) mp_instance = new Log; + return *mp_instance; + } + + void operator()(const std::string &, Loglevel); + void set_loglevel(Loglevel lvl) { m_loglevel = lvl; } + void set_handler(LogHandler *handler) { mp_loghandler = handler; } + + private: + LogHandler *mp_loghandler; + Loglevel m_loglevel; + static std::string prefix; + public: + static void setPrefix(std::string prefix = std::string()); + + private: + + static Log *mp_instance; + }; + } +} + + +#ifdef NO_LOG +#define LOG(string, level) while(false){} +#define LOGD(string) while(false){} +#else +#define LOG(string, level) nitrokey::log::Log::instance()((string), (level)) +#define LOGD1(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L1)) +#define LOGD(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L2)) +#endif + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/misc.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/misc.h new file mode 100644 index 0000000..a9c4672 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/misc.h @@ -0,0 +1,121 @@ +/* + * 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 + */ + + +#ifndef MISC_H +#define MISC_H +#include +#include +#include +#include +#include "log.h" +#include "LibraryException.h" +#include +#include +#include + + +namespace nitrokey { +namespace misc { + +/** + * Simple replacement for std::optional (C++17). + */ +template +class Option { +public: + Option() : m_hasValue(false), m_value() {} + Option(T value) : m_hasValue(true), m_value(value) {} + + bool has_value() const { + return m_hasValue; + } + T value() const { + if (!m_hasValue) { + throw std::logic_error("Called Option::value without value"); + } + return m_value; + } + +private: + bool m_hasValue; + T m_value; +}; + + template + std::string toHex(T value){ + using namespace std; + std::ostringstream oss; + oss << std::hex << std::setw(sizeof(value)*2) << std::setfill('0') << value; + return oss.str(); + } + +#define FIELD_WIDTH_MAX (100) + /** + * Copies string from pointer to fixed size C-style array. Src needs to be a valid C-string - eg. ended with '\0'. + * Throws when source is bigger than destination. + * @tparam T type of destination array + * @param dest fixed size destination array + * @param src pointer to source c-style valid string + */ + template + void strcpyT(T& dest, const char* src){ + + if (src == nullptr) +// throw EmptySourceStringException(slot_number); + return; + const size_t s_dest = sizeof dest; + const size_t src_strlen = strnlen(src, FIELD_WIDTH_MAX); + LOG(std::string("strcpyT sizes dest src ") + + std::to_string(s_dest) + " " + + std::to_string(src_strlen) + " " + , nitrokey::log::Loglevel::DEBUG_L2); + if (src_strlen > s_dest){ + throw TooLongStringException(src_strlen, s_dest, src); + } + strncpy((char*) &dest, src, s_dest); + } + +#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) + template +typename T::CommandPayload get_payload(){ + //Create, initialize and return by value command payload + typename T::CommandPayload st; + bzero(&st, sizeof(st)); + return st; +} + + template + void execute_password_command(Tdev &stick, const char *password) { + auto p = get_payload(); + p.set_defaults(); + strcpyT(p.password, password); + CMDTYPE::CommandTransaction::run(stick, p); + } + + std::string hexdump(const uint8_t *p, size_t size, bool print_header=true, bool print_ascii=true, + bool print_empty=true); + uint32_t stm_crc32(const uint8_t *data, size_t size); + std::vector hex_string_to_byte(const char* hexString); +} +} + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands.h new file mode 100644 index 0000000..5e8a5aa --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands.h @@ -0,0 +1,929 @@ +/* + * 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 + */ + +#ifndef STICK10_COMMANDS_H +#define STICK10_COMMANDS_H + +#include +#include +#include +#include +#include +#include "device_proto.h" +#include "command.h" + +#pragma pack (push,1) + +namespace nitrokey { +namespace proto { + + + +/* + * Stick10 protocol definition + */ +namespace stick10 { +class GetSlotName : public Command { + public: + // reachable as a typedef in Transaction + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return slot_number<0x10+3; } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_name[15]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(slot_name); + return ss.str(); + } + } __packed; + + typedef Transaction CommandTransaction; +}; + +class EraseSlot : Command { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class SetTime : Command { + public: + struct CommandPayload { + uint8_t reset; // 0 - get time, 1 - set time + uint64_t time; // posix time + + bool isValid() const { return reset && reset != 1; } + std::string dissect() const { + std::stringstream ss; + ss << "reset:\t" << (int)(reset) << std::endl; + ss << "time:\t" << (time) << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + + +class WriteToHOTPSlot : Command { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[15]; + uint8_t slot_secret[20]; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + union{ + uint8_t slot_token_id[13]; /** OATH Token Identifier */ + struct{ /** @see https://openauthentication.org/token-specs/ */ + uint8_t omp[2]; + uint8_t tt[2]; + uint8_t mui[8]; + uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 + } slot_token_fields; + }; + union{ + uint64_t slot_counter; + uint8_t slot_counter_s[8]; + } __packed; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + print_to_ss_volatile(slot_name); + print_to_ss_volatile(slot_secret); + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + + ss << "slot_token_id:\t"; + for (auto i : slot_token_id) + ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; + ss << std::endl; + ss << "slot_counter:\t[" << (int)slot_counter << "]\t" + << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false); + + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class WriteToTOTPSlot : Command { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[15]; + uint8_t slot_secret[20]; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + union{ + uint8_t slot_token_id[13]; /** OATH Token Identifier */ + struct{ /** @see https://openauthentication.org/token-specs/ */ + uint8_t omp[2]; + uint8_t tt[2]; + uint8_t mui[8]; + uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 + } slot_token_fields; + }; + uint16_t slot_interval; + + bool isValid() const { return !(slot_number & 0xF0); } //TODO check + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + print_to_ss_volatile(slot_name); + print_to_ss_volatile(slot_secret); + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "slot_token_id:\t"; + for (auto i : slot_token_id) + ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; + ss << std::endl; + ss << "slot_interval:\t" << (int)slot_interval << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class GetTOTP : Command { + public: + struct CommandPayload { + uint8_t slot_number; + uint64_t challenge; + uint64_t last_totp_time; + uint8_t last_interval; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + ss << "challenge:\t" << (challenge) << std::endl; + ss << "last_totp_time:\t" << (last_totp_time) << std::endl; + ss << "last_interval:\t" << (int)(last_interval) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + union { + uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 + struct { + uint32_t code; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + } __packed ; + } __packed ; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "code:\t" << (code) << std::endl; + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class GetHOTP : Command { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return (slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + union { + uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 + struct { + uint32_t code; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + } __packed; + } __packed; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "code:\t" << (code) << std::endl; + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class ReadSlot : Command { + public: + enum class CounterFormat { + ASCII = 0, + BINARY = 1, + }; + struct CommandPayload { + uint8_t slot_number; + CounterFormat data_format; //Storage v0.54+ only: slot_counter value format: 0 - in ascii, 1 - binary + + bool isValid() const { return !(slot_number & 0xF0); } + + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_name[15]; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + union{ + uint8_t slot_token_id[13]; /** OATH Token Identifier */ + struct{ /** @see https://openauthentication.org/token-specs/ */ + uint8_t omp[2]; + uint8_t tt[2]; + uint8_t mui[8]; + uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 + } slot_token_fields; + }; + union{ + uint64_t slot_counter; + uint8_t slot_counter_s[8]; + } __packed; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(slot_name); + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + + ss << "slot_token_id:\t"; + for (auto i : slot_token_id) + ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; + ss << std::endl; + ss << "slot_counter:\t[" << (int)slot_counter << "]\t" + << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false); + return ss.str(); + } + } __packed; + + typedef Transaction CommandTransaction; +}; + +class GetStatus : Command { + public: + struct ResponsePayload { + union { + uint16_t firmware_version; + struct { + uint8_t minor; + uint8_t major; + } firmware_version_st; + }; + union{ + uint8_t card_serial[4]; + uint32_t card_serial_u32; + } __packed; + union { + uint8_t general_config[5]; + struct{ + uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ + uint8_t capslock; /** same as numlock */ + uint8_t scrolllock; /** same as numlock */ + uint8_t enable_user_password; + uint8_t delete_user_password; /* unused */ + } __packed; + } __packed; + + static constexpr uint8_t special_HOTP_slots = 2; + bool isValid() const { return numlock < special_HOTP_slots && capslock < special_HOTP_slots + && scrolllock < special_HOTP_slots && enable_user_password < 2; } + + std::string get_card_serial_hex() const { + return nitrokey::misc::toHex(card_serial_u32); + } + + std::string dissect() const { + std::stringstream ss; + ss << "firmware_version:\t" + << "[" << firmware_version << "]" << "\t" + << ::nitrokey::misc::hexdump( + (const uint8_t *)(&firmware_version), sizeof firmware_version, false); + ss << "card_serial_u32:\t" << std::hex << card_serial_u32 << std::endl; + ss << "card_serial:\t" + << ::nitrokey::misc::hexdump((const uint8_t *)(card_serial), + sizeof card_serial, false); + ss << "general_config:\t" + << ::nitrokey::misc::hexdump((const uint8_t *)(general_config), + sizeof general_config, false); + ss << "numlock:\t" << (int)numlock << std::endl; + ss << "capslock:\t" << (int)capslock << std::endl; + ss << "scrolllock:\t" << (int)scrolllock << std::endl; + ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; + ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; + + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class GetPasswordRetryCount : Command { + public: + struct ResponsePayload { + uint8_t password_retry_count; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " password_retry_count\t" << (int)password_retry_count << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class GetUserPasswordRetryCount + : Command { + public: + struct ResponsePayload { + uint8_t password_retry_count; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " password_retry_count\t" << (int)password_retry_count << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + + template + void write_array(T &ss, Q (&arr)[N]){ + for (int i=0; i { + public: + struct ResponsePayload { + uint8_t password_safe_status[PWS_SLOT_COUNT]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "password_safe_status\t"; + write_array(ss, password_safe_status); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class GetPasswordSafeSlotName : Command { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number\t" << (int)slot_number << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_name[PWS_SLOTNAME_LENGTH]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(slot_name); + return ss.str(); + } + } __packed; + + typedef Transaction CommandTransaction; +}; + +class GetPasswordSafeSlotPassword + : Command { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << (int)slot_number << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_password[PWS_PASSWORD_LENGTH]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(slot_password); + return ss.str(); + } + } __packed; + + typedef Transaction CommandTransaction; +}; + +class GetPasswordSafeSlotLogin + : Command { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << (int)slot_number << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_login[PWS_LOGINNAME_LENGTH]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(slot_login); + return ss.str(); + } + } __packed; + + typedef Transaction CommandTransaction; +}; + +class SetPasswordSafeSlotData : Command { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[PWS_SLOTNAME_LENGTH]; + uint8_t slot_password[PWS_PASSWORD_LENGTH]; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << (int)slot_number << std::endl; + print_to_ss_volatile(slot_name); + print_to_ss_volatile(slot_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class SetPasswordSafeSlotData2 : Command { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_login_name[PWS_LOGINNAME_LENGTH]; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << (int)slot_number << std::endl; + print_to_ss_volatile(slot_login_name); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class ErasePasswordSafeSlot : Command { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << (int)slot_number << std::endl; + return ss.str(); + } + + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class EnablePasswordSafe : Command { + public: + struct CommandPayload { + uint8_t user_password[30]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(user_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class PasswordSafeInitKey : Command { + /** + * never used in Nitrokey App + */ + public: + typedef Transaction + CommandTransaction; +}; + +class PasswordSafeSendSlotViaHID : Command { + /** + * never used in Nitrokey App + */ + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_kind; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +// TODO "Device::passwordSafeSendSlotDataViaHID" + +class WriteGeneralConfig : Command { + public: + struct CommandPayload { + union{ + uint8_t config[5]; + struct{ + uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ + uint8_t capslock; /** same as numlock */ + uint8_t scrolllock; /** same as numlock */ + uint8_t enable_user_password; + uint8_t delete_user_password; + }; + }; + bool isValid() const { return numlock < 2 && capslock < 2 && scrolllock < 2 && enable_user_password < 2; } + + std::string dissect() const { + std::stringstream ss; + ss << "numlock:\t" << (int)numlock << std::endl; + ss << "capslock:\t" << (int)capslock << std::endl; + ss << "scrolllock:\t" << (int)scrolllock << std::endl; + ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; + ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class FirstAuthenticate : Command { + public: + struct CommandPayload { + uint8_t card_password[25]; + uint8_t temporary_password[25]; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(card_password); + hexdump_to_ss(temporary_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class UserAuthenticate : Command { + public: + struct CommandPayload { + uint8_t card_password[25]; + uint8_t temporary_password[25]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(card_password); + hexdump_to_ss(temporary_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class Authorize : Command { + public: + struct CommandPayload { + uint32_t crc_to_authorize; + uint8_t temporary_password[25]; + + std::string dissect() const { + std::stringstream ss; + ss << " crc_to_authorize:\t" << std::hex << std::setw(2) << std::setfill('0') << crc_to_authorize<< std::endl; + hexdump_to_ss(temporary_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class UserAuthorize : Command { + public: + struct CommandPayload { + uint32_t crc_to_authorize; + uint8_t temporary_password[25]; + std::string dissect() const { + std::stringstream ss; + ss << " crc_to_authorize:\t" << crc_to_authorize<< std::endl; + hexdump_to_ss(temporary_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class UnlockUserPassword : Command { + public: + struct CommandPayload { + uint8_t admin_password[25]; + uint8_t user_new_password[25]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(admin_password); + print_to_ss_volatile(user_new_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class ChangeUserPin : Command { + public: + struct CommandPayload { + uint8_t old_pin[25]; + uint8_t new_pin[25]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(old_pin); + print_to_ss_volatile(new_pin); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class IsAESSupported : Command { + public: + struct CommandPayload { + uint8_t user_password[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(user_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + + +class ChangeAdminPin : Command { + public: + struct CommandPayload { + uint8_t old_pin[25]; + uint8_t new_pin[25]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(old_pin); + print_to_ss_volatile(new_pin); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class LockDevice : Command { + public: + typedef Transaction + CommandTransaction; +}; + +class FactoryReset : Command { + public: + struct CommandPayload { + uint8_t admin_password[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(admin_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; +}; + +class BuildAESKey : Command { + public: + struct CommandPayload { + uint8_t admin_password[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(admin_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + +}; + +class FirmwareUpdate : Command { +public: + struct CommandPayload { + uint8_t firmware_password[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(firmware_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + +}; + +class FirmwarePasswordChange : Command { +public: + struct CommandPayload { + uint8_t firmware_password_current[20]; + uint8_t firmware_password_new[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(firmware_password_current); + print_to_ss_volatile(firmware_password_new); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + +}; + + +} +} +} +#pragma pack (pop) +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands_0.8.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands_0.8.h new file mode 100644 index 0000000..9477890 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick10_commands_0.8.h @@ -0,0 +1,348 @@ +/* + * 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 + */ + + +#ifndef LIBNITROKEY_STICK10_COMMANDS_0_8_H +#define LIBNITROKEY_STICK10_COMMANDS_0_8_H + +#include +#include +#include +#include +#include +#include "command.h" +#include "device_proto.h" +#include "stick10_commands.h" + +#pragma pack (push,1) + + +namespace nitrokey { + namespace proto { + +/* + * Stick10 protocol definition + */ + namespace stick10_08 { + using stick10::FirstAuthenticate; + using stick10::UserAuthenticate; + using stick10::SetTime; + using stick10::GetStatus; + using stick10::BuildAESKey; + using stick10::ChangeAdminPin; + using stick10::ChangeUserPin; + using stick10::EnablePasswordSafe; + using stick10::ErasePasswordSafeSlot; + using stick10::FactoryReset; + using stick10::GetPasswordRetryCount; + using stick10::GetUserPasswordRetryCount; + using stick10::GetPasswordSafeSlotLogin; + using stick10::GetPasswordSafeSlotName; + using stick10::GetPasswordSafeSlotPassword; + using stick10::GetPasswordSafeSlotStatus; + using stick10::GetSlotName; + using stick10::IsAESSupported; + using stick10::LockDevice; + using stick10::PasswordSafeInitKey; + using stick10::PasswordSafeSendSlotViaHID; + using stick10::SetPasswordSafeSlotData; + using stick10::SetPasswordSafeSlotData2; + using stick10::UnlockUserPassword; + using stick10::ReadSlot; + + class EraseSlot : Command { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t temporary_admin_password[25]; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + hexdump_to_ss(temporary_admin_password); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + class SendOTPData : Command { + //admin auth + public: + struct CommandPayload { + uint8_t temporary_admin_password[25]; + uint8_t type; //S-secret, N-name + uint8_t id; //multiple reports for values longer than 30 bytes + uint8_t data[30]; //data, does not need null termination + + bool isValid() const { return true; } + + void setTypeName(){ + type = 'N'; + } + void setTypeSecret(){ + type = 'S'; + } + + std::string dissect() const { + std::stringstream ss; + hexdump_to_ss(temporary_admin_password); + ss << "type:\t" << type << std::endl; + ss << "id:\t" << (int)id << std::endl; +#ifdef LOG_VOLATILE_DATA + ss << "data:" << std::endl + << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data); +#else + ss << " Volatile data not logged" << std::endl; +#endif + return ss.str(); + } + } __packed; + + + struct ResponsePayload { + union { + uint8_t data[40]; + } __packed; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; +#ifdef LOG_VOLATILE_DATA + ss << "data:" << std::endl + << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data); +#else + ss << " Volatile data not logged" << std::endl; +#endif + return ss.str(); + } + } __packed; + + + typedef Transaction + CommandTransaction; + }; + + class WriteToOTPSlot : Command { + //admin auth + public: + struct CommandPayload { + uint8_t temporary_admin_password[25]; + uint8_t slot_number; + union { + uint64_t slot_counter_or_interval; + uint8_t slot_counter_s[8]; + } __packed; + union { + uint8_t _slot_config; + struct { + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + union { + uint8_t slot_token_id[13]; /** OATH Token Identifier */ + struct { /** @see https://openauthentication.org/token-specs/ */ + uint8_t omp[2]; + uint8_t tt[2]; + uint8_t mui[8]; + uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 + } slot_token_fields; + }; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + hexdump_to_ss(temporary_admin_password); + ss << "slot_config:\t" << std::bitset<8>((int) _slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + ss << "slot_number:\t" << (int) (slot_number) << std::endl; + ss << "slot_counter_or_interval:\t[" << (int) slot_counter_or_interval << "]\t" + << ::nitrokey::misc::hexdump((const uint8_t *) (&slot_counter_or_interval), sizeof slot_counter_or_interval, false); + + ss << "slot_token_id:\t"; + for (auto i : slot_token_id) + ss << std::hex << std::setw(2) << std::setfill('0') << (int) i << " "; + ss << std::endl; + + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + class GetHOTP : Command { + public: + struct CommandPayload { + uint8_t slot_number; + struct { + uint64_t challenge; //@unused + uint64_t last_totp_time; //@unused + uint8_t last_interval; //@unused + } __packed _unused; + uint8_t temporary_user_password[25]; + + bool isValid() const { return (slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + hexdump_to_ss(temporary_user_password); + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + union { + uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 + struct { + uint32_t code; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + } __packed; + } __packed; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "code:\t" << (code) << std::endl; + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + + class GetTOTP : Command { + //user auth + public: + struct CommandPayload { + uint8_t slot_number; + uint64_t challenge; //@unused + uint64_t last_totp_time; //@unused + uint8_t last_interval; //@unused + uint8_t temporary_user_password[25]; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + hexdump_to_ss(temporary_user_password); + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + ss << "challenge:\t" << (challenge) << std::endl; + ss << "last_totp_time:\t" << (last_totp_time) << std::endl; + ss << "last_interval:\t" << (int)(last_interval) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + union { + uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 + struct { + uint32_t code; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + } __packed ; + } __packed ; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "code:\t" << (code) << std::endl; + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + + class WriteGeneralConfig : Command { + //admin auth + public: + struct CommandPayload { + union{ + uint8_t config[5]; + struct{ + uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ + uint8_t capslock; /** same as numlock */ + uint8_t scrolllock; /** same as numlock */ + uint8_t enable_user_password; + uint8_t delete_user_password; + }; + }; + uint8_t temporary_admin_password[25]; + + static constexpr uint8_t special_HOTP_slots = 3; + bool isValid() const { return numlock < special_HOTP_slots && capslock < special_HOTP_slots + && scrolllock < special_HOTP_slots && enable_user_password < 2; } + + std::string dissect() const { + std::stringstream ss; + ss << "numlock:\t" << (int)numlock << std::endl; + ss << "capslock:\t" << (int)capslock << std::endl; + ss << "scrolllock:\t" << (int)scrolllock << std::endl; + ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; + ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + } + } +} +#pragma pack (pop) + +#endif //LIBNITROKEY_STICK10_COMMANDS_0_8_H diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick20_commands.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick20_commands.h new file mode 100644 index 0000000..7efa1b6 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/stick20_commands.h @@ -0,0 +1,394 @@ +/* + * 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 + */ + +#ifndef STICK20_COMMANDS_H +#define STICK20_COMMANDS_H + + + +#include +#include "command.h" +#include +#include +#include "device_proto.h" + +#pragma pack (push,1) + +namespace nitrokey { + namespace proto { + +/* +* STICK20 protocol command ids +* a superset (almost) of STICK10 +*/ + + namespace stick20 { + + class ChangeAdminUserPin20Current : + public PasswordCommand {}; + class ChangeAdminUserPin20New : + public PasswordCommand {}; + class UnlockUserPin : + public PasswordCommand {}; + + class EnableEncryptedPartition : public PasswordCommand {}; + class EnableHiddenEncryptedPartition : public PasswordCommand {}; + + class SetUnencryptedVolumeReadOnlyAdmin : + public PasswordCommand {}; + class SetUnencryptedVolumeReadWriteAdmin : + public PasswordCommand {}; + class SetEncryptedVolumeReadOnly : + public PasswordCommand {}; + class SetEncryptedVolumeReadWrite : + public PasswordCommand {}; + + //FIXME the volume disabling commands do not need password + class DisableEncryptedPartition : public PasswordCommand {}; + class DisableHiddenEncryptedPartition : public PasswordCommand {}; + + class EnableFirmwareUpdate : public PasswordCommand {}; + + class ChangeUpdatePassword : Command { + public: + struct CommandPayload { + uint8_t __gap; + uint8_t current_update_password[20]; + uint8_t __gap2; + uint8_t new_update_password[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile( current_update_password ); + print_to_ss_volatile( new_update_password ); + return ss.str(); + } + }; + + typedef Transaction + CommandTransaction; + }; + + class ExportFirmware : public PasswordCommand {}; + + class CreateNewKeys : + public PasswordCommand {}; + + + class FillSDCardWithRandomChars : Command { + public: + enum class ChosenVolumes : uint8_t { + all_volumes = 0, + encrypted_volume = 1 + }; + + struct CommandPayload { + uint8_t volume_flag; + uint8_t kind; + uint8_t admin_pin[20]; + + std::string dissect() const { + std::stringstream ss; + print_to_ss( (int) volume_flag ); + print_to_ss( kind ); + print_to_ss_volatile(admin_pin); + return ss.str(); + } + void set_kind_user() { + kind = (uint8_t) 'P'; + } + void set_defaults(){ + set_kind_user(); + volume_flag = static_cast(ChosenVolumes::encrypted_volume); + } + + } __packed; + + typedef Transaction::command_id(), + struct CommandPayload, struct EmptyPayload> + CommandTransaction; + }; + + namespace StorageCommandResponsePayload{ + using namespace DeviceResponseConstants; + static constexpr auto padding_size = + storage_data_absolute_address - header_size; + struct TransmissionData{ + uint8_t _padding[padding_size]; + + uint8_t SendCounter_u8; + uint8_t SendDataType_u8; + uint8_t FollowBytesFlag_u8; + uint8_t SendSize_u8; + + std::string dissect() const { + std::stringstream ss; + ss << "_padding:" << std::endl + << ::nitrokey::misc::hexdump((const uint8_t *) (_padding), + sizeof _padding); + print_to_ss((int) SendCounter_u8); + print_to_ss((int) SendDataType_u8); + print_to_ss((int) FollowBytesFlag_u8); + print_to_ss((int) SendSize_u8); + return ss.str(); + } + + } __packed; + } + + namespace DeviceConfigurationResponsePacket{ + + struct ResponsePayload { + StorageCommandResponsePayload::TransmissionData transmission_data; + + uint16_t MagicNumber_StickConfig_u16; + /** + * READ_WRITE_ACTIVE = ReadWriteFlagUncryptedVolume_u8 == 0; + */ + uint8_t ReadWriteFlagUncryptedVolume_u8; + uint8_t ReadWriteFlagCryptedVolume_u8; + + union{ + uint8_t VersionInfo_au8[4]; + struct { + uint8_t major; + uint8_t minor; + uint8_t _reserved2; + uint8_t build_iteration; + } __packed versionInfo; + } __packed; + + uint8_t ReadWriteFlagHiddenVolume_u8; + uint8_t FirmwareLocked_u8; + + union{ + uint8_t NewSDCardFound_u8; + struct { + bool NewCard :1; + uint8_t Counter :7; + } __packed NewSDCardFound_st; + } __packed; + + /** + * SD card FILLED with random chars + */ + uint8_t SDFillWithRandomChars_u8; + uint32_t ActiveSD_CardID_u32; + union{ + uint8_t VolumeActiceFlag_u8; + struct { + bool unencrypted :1; + bool encrypted :1; + bool hidden :1; + } __packed VolumeActiceFlag_st; + } __packed; + uint8_t NewSmartCardFound_u8; + uint8_t UserPwRetryCount; + uint8_t AdminPwRetryCount; + uint32_t ActiveSmartCardID_u32; + uint8_t StickKeysNotInitiated; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + + print_to_ss(transmission_data.dissect()); + print_to_ss( MagicNumber_StickConfig_u16 ); + print_to_ss((int) ReadWriteFlagUncryptedVolume_u8 ); + print_to_ss((int) ReadWriteFlagCryptedVolume_u8 ); + print_to_ss((int) ReadWriteFlagHiddenVolume_u8 ); + print_to_ss((int) versionInfo.major ); + print_to_ss((int) versionInfo.minor ); + print_to_ss((int) versionInfo.build_iteration ); + print_to_ss((int) FirmwareLocked_u8 ); + print_to_ss((int) NewSDCardFound_u8 ); + print_to_ss((int) NewSDCardFound_st.NewCard ); + print_to_ss((int) NewSDCardFound_st.Counter ); + print_to_ss((int) SDFillWithRandomChars_u8 ); + print_to_ss( ActiveSD_CardID_u32 ); + print_to_ss((int) VolumeActiceFlag_u8 ); + print_to_ss((int) VolumeActiceFlag_st.unencrypted ); + print_to_ss((int) VolumeActiceFlag_st.encrypted ); + print_to_ss((int) VolumeActiceFlag_st.hidden); + print_to_ss((int) NewSmartCardFound_u8 ); + print_to_ss((int) UserPwRetryCount ); + print_to_ss((int) AdminPwRetryCount ); + print_to_ss( ActiveSmartCardID_u32 ); + print_to_ss((int) StickKeysNotInitiated ); + + return ss.str(); + } + } __packed; + } + + class SendStartup : Command { + public: + struct CommandPayload { + uint64_t localtime; // POSIX seconds from epoch start, supports until year 2106 + std::string dissect() const { + std::stringstream ss; + print_to_ss( localtime ); + return ss.str(); + } + void set_defaults(){ + localtime = + std::chrono::duration_cast ( + std::chrono::system_clock::now().time_since_epoch()).count(); + } + }__packed; + + using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload; + + typedef Transaction + CommandTransaction; + }; + + +// TODO fix original nomenclature + class SendSetReadonlyToUncryptedVolume : public PasswordCommand {}; + class SendSetReadwriteToUncryptedVolume : public PasswordCommand {}; + class SendClearNewSdCardFound : public PasswordCommand {}; + + class GetDeviceStatus : Command { + public: + using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload; + + typedef Transaction + CommandTransaction; + }; + + class Wink : Command { + public: + typedef Transaction + CommandTransaction; + }; + + class CheckSmartcardUsage : Command { + public: + typedef Transaction + CommandTransaction; + }; + + class GetSDCardOccupancy : Command { + public: + struct ResponsePayload { + uint8_t WriteLevelMin; + uint8_t WriteLevelMax; + uint8_t ReadLevelMin; + uint8_t ReadLevelMax; + std::string dissect() const { + std::stringstream ss; + print_to_ss((int) WriteLevelMin); + print_to_ss((int) WriteLevelMax); + print_to_ss((int) ReadLevelMin); + print_to_ss((int) ReadLevelMax); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + + class SetupHiddenVolume : Command { + public: + constexpr static int MAX_HIDDEN_VOLUME_PASSWORD_SIZE = 20; + struct CommandPayload { + uint8_t SlotNr_u8; + uint8_t StartBlockPercent_u8; + uint8_t EndBlockPercent_u8; + uint8_t HiddenVolumePassword_au8[MAX_HIDDEN_VOLUME_PASSWORD_SIZE]; + std::string dissect() const { + std::stringstream ss; + print_to_ss((int) SlotNr_u8); + print_to_ss((int) StartBlockPercent_u8); + print_to_ss((int) EndBlockPercent_u8); + print_to_ss_volatile(HiddenVolumePassword_au8); + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + +//disable this command for now +// class LockFirmware : public PasswordCommand {}; + + class ProductionTest : Command { + public: + struct ResponsePayload { + + StorageCommandResponsePayload::TransmissionData transmission_data; + + uint8_t FirmwareVersion_au8[2]; // 2 byte // 2 + uint8_t FirmwareVersionInternal_u8; // 1 byte // 3 + uint8_t SD_Card_Size_u8; // 1 byte // 4 + uint32_t CPU_CardID_u32; // 4 byte // 8 + uint32_t SmartCardID_u32; // 4 byte // 12 + uint32_t SD_CardID_u32; // 4 byte // 16 + uint8_t SC_UserPwRetryCount; // User PIN retry count 1 byte // 17 + uint8_t SC_AdminPwRetryCount; // Admin PIN retry count 1 byte // 18 + uint8_t SD_Card_ManufacturingYear_u8; // 1 byte // 19 + uint8_t SD_Card_ManufacturingMonth_u8; // 1 byte // 20 + uint16_t SD_Card_OEM_u16; // 2 byte // 22 + uint16_t SD_WriteSpeed_u16; // in kbyte / sec 2 byte // 24 + uint8_t SD_Card_Manufacturer_u8; // 1 byte // 25 + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + + print_to_ss(transmission_data.dissect()); + print_to_ss((int) FirmwareVersion_au8[0]); + print_to_ss((int) FirmwareVersion_au8[1]); + print_to_ss((int) FirmwareVersionInternal_u8); + print_to_ss((int) SD_Card_Size_u8); + print_to_ss( CPU_CardID_u32); + print_to_ss( SmartCardID_u32); + print_to_ss( SD_CardID_u32); + print_to_ss((int) SC_UserPwRetryCount); + print_to_ss((int) SC_AdminPwRetryCount); + print_to_ss((int) SD_Card_ManufacturingYear_u8); + print_to_ss((int) SD_Card_ManufacturingMonth_u8); + print_to_ss( SD_Card_OEM_u16); + print_to_ss( SD_WriteSpeed_u16); + print_to_ss((int) SD_Card_Manufacturer_u8); + return ss.str(); + } + + } __packed; + + typedef Transaction + CommandTransaction; + }; + + } + } +} + +#undef print_to_ss +#pragma pack (pop) + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/libnitrokey/version.h b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/version.h new file mode 100644 index 0000000..6547af0 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/libnitrokey/version.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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 + */ + +#ifndef LIBNITROKEY_VERSION_H +#define LIBNITROKEY_VERSION_H + +namespace nitrokey { + unsigned int get_major_library_version(); + + unsigned int get_minor_library_version(); + + const char* get_library_version(); +} + +#endif diff --git a/nitrokey-sys/libnitrokey-v3.5/log.cc b/nitrokey-sys/libnitrokey-v3.5/log.cc new file mode 100644 index 0000000..06acee7 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/log.cc @@ -0,0 +1,103 @@ +/* + * 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 + */ + +#include "log.h" +#include +#include +#include + +#include + +namespace nitrokey { + namespace log { + + Log *Log::mp_instance = nullptr; + StdlogHandler stdlog_handler; + + std::string Log::prefix = ""; + + + std::string LogHandler::loglevel_to_str(Loglevel lvl) { + switch (lvl) { + case Loglevel::DEBUG_L1: + return std::string("DEBUG_L1"); + case Loglevel::DEBUG_L2: + return std::string("DEBUG_L2"); + case Loglevel::DEBUG: + return std::string("DEBUG"); + case Loglevel::INFO: + return std::string("INFO"); + case Loglevel::WARNING: + return std::string("WARNING"); + case Loglevel::ERROR: + return std::string("ERROR"); + } + return std::string(""); + } + + void Log::operator()(const std::string &logstr, Loglevel lvl) { + if (mp_loghandler != nullptr){ + if ((int) lvl <= (int) m_loglevel) mp_loghandler->print(prefix+logstr, lvl); + } + } + + void Log::setPrefix(const std::string prefix) { + if (!prefix.empty()){ + Log::prefix = "["+prefix+"]"; + } else { + Log::prefix = ""; + } + } + + void StdlogHandler::print(const std::string &str, Loglevel lvl) { + std::string s = format_message_to_string(str, lvl); + std::clog << s; + } + + void FunctionalLogHandler::print(const std::string &str, Loglevel lvl) { + std::string s = format_message_to_string(str, lvl); + log_function(s); + } + + std::string LogHandler::format_message_to_string(const std::string &str, const Loglevel &lvl) { + static bool last_short = false; + if (str.length() == 1){ + last_short = true; + return str; + } + time_t t = time(nullptr); + tm tm = *localtime(&t); + + std::stringstream s; + s + << (last_short? "\n" : "") + << "[" << std::put_time(&tm, "%c") << "]" + << "[" << loglevel_to_str(lvl) << "]\t" + << str << std::endl; + last_short = false; + return s.str(); + } + + FunctionalLogHandler::FunctionalLogHandler(log_function_type _log_function) { + log_function = _log_function; + } + } +} diff --git a/nitrokey-sys/libnitrokey-v3.5/misc.cc b/nitrokey-sys/libnitrokey-v3.5/misc.cc new file mode 100644 index 0000000..59185f3 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/misc.cc @@ -0,0 +1,123 @@ +/* + * 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 + */ + +#include +#include +#include "misc.h" +#include "inttypes.h" +#include +#include +#include "LibraryException.h" +#include + +namespace nitrokey { +namespace misc { + + + +::std::vector hex_string_to_byte(const char* hexString){ + const size_t big_string_size = 257; //arbitrary 'big' number + const size_t s_size = strnlen(hexString, big_string_size); + const size_t d_size = s_size/2; + if (s_size%2!=0 || s_size>=big_string_size){ + throw InvalidHexString(0); + } + auto data = ::std::vector(); + data.reserve(d_size); + + char buf[3]; + buf[2] = '\0'; + for(size_t i=0; i +::std::string hexdump(const uint8_t *p, size_t size, bool print_header, + bool print_ascii, bool print_empty) { + ::std::stringstream out; + char formatbuf[128]; + const uint8_t *pstart = p; + + for (const uint8_t *pend = p + size; p < pend;) { + if (print_header){ + snprintf(formatbuf, 128, "%04x\t", static_cast (p - pstart)); + out << formatbuf; + } + + const uint8_t* pp = p; + for (const uint8_t *le = p + 16; p < le; p++) { + if (p < pend){ + snprintf(formatbuf, 128, "%02x ", uint8_t(*p)); + out << formatbuf; + } else { + if(print_empty) + out << "-- "; + } + + } + if(print_ascii){ + out << " "; + for (const uint8_t *le = pp + 16; pp < le && pp < pend; pp++) { + if (std::isgraph(*pp)) + out << uint8_t(*pp); + else + out << '.'; + } + } + out << ::std::endl; + } + return out.str(); +} + +static uint32_t _crc32(uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04C11DB7; // polynomial used in STM32 + else + crc = (crc << 1); + } + + return crc; +} + +uint32_t stm_crc32(const uint8_t *data, size_t size) { + uint32_t crc = 0xffffffff; + const uint32_t *pend = (const uint32_t *)(data + size); + for (const uint32_t *p = (const uint32_t *)(data); p < pend; p++) + crc = _crc32(crc, *p); + return crc; +} +} +} diff --git a/nitrokey-sys/libnitrokey-v3.5/version.cc b/nitrokey-sys/libnitrokey-v3.5/version.cc new file mode 100644 index 0000000..dfdc802 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/version.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 + */ + +#include "version.h" + +namespace nitrokey { + unsigned int get_major_library_version() { + return 3; + } + + unsigned int get_minor_library_version() { + return 0; + } + + const char* get_library_version() { + return "unknown"; + } +} + diff --git a/nitrokey-sys/libnitrokey-v3.5/version.cc.in b/nitrokey-sys/libnitrokey-v3.5/version.cc.in new file mode 100644 index 0000000..0eae647 --- /dev/null +++ b/nitrokey-sys/libnitrokey-v3.5/version.cc.in @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 + */ + +#include "version.h" + +namespace nitrokey { + unsigned int get_major_library_version() { + return @PROJECT_VERSION_MAJOR@; + } + + unsigned int get_minor_library_version() { + return @PROJECT_VERSION_MINOR@; + } + + const char* get_library_version() { + return "@PROJECT_VERSION_GIT@"; + } +} + diff --git a/nitrokey-sys/src/ffi.rs b/nitrokey-sys/src/ffi.rs index 58879ad..a01eae8 100644 --- a/nitrokey-sys/src/ffi.rs +++ b/nitrokey-sys/src/ffi.rs @@ -1,5 +1,10 @@ /* automatically generated by rust-bindgen, manually modified */ +extern "C" { + #[link_name = "\u{1}NK_PWS_SLOT_COUNT"] + pub static mut NK_PWS_SLOT_COUNT: u8; +} +pub const MAXIMUM_STR_REPLY_LENGTH: ::std::os::raw::c_int = 8192; /// Use, if no supported device is connected pub const NK_device_model_NK_DISCONNECTED: NK_device_model = 0; /// Nitrokey Pro. @@ -8,6 +13,188 @@ pub const NK_device_model_NK_PRO: NK_device_model = 1; pub const NK_device_model_NK_STORAGE: NK_device_model = 2; /// The Nitrokey device models supported by the API. pub type NK_device_model = u32; +/// The connection info for a Nitrokey device as a linked list. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct NK_device_info { + /// The model of the Nitrokey device. + pub model: NK_device_model, + /// The USB device path for NK_connect_with_path. + pub path: *mut ::std::os::raw::c_char, + /// The serial number. + pub serial_number: *mut ::std::os::raw::c_char, + /// The pointer to the next element of the linked list or null + /// if this is the last element in the list. + pub next: *mut NK_device_info, +} +#[test] +fn bindgen_test_layout_NK_device_info() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(NK_device_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(NK_device_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).model as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(NK_device_info), + "::", + stringify!(model) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).path as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(NK_device_info), + "::", + stringify!(path) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).serial_number as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(NK_device_info), + "::", + stringify!(serial_number) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).next as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(NK_device_info), + "::", + stringify!(next) + ) + ); +} +/// Stores the common device status for all Nitrokey devices. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct NK_status { + /// The major firmware version, e. g. 0 in v0.40. + pub firmware_version_major: u8, + /// The minor firmware version, e. g. 40 in v0.40. + pub firmware_version_minor: u8, + /// The serial number of the smart card. + pub serial_number_smart_card: u32, + /// The HOTP slot to generate a password from if the numlock + /// key is pressed twice (slot 0-1, or any other value to + /// disable the function). + pub config_numlock: u8, + /// The HOTP slot to generate a password from if the capslock + /// key is pressed twice (slot 0-1, or any other value to + /// disable the function). + pub config_capslock: u8, + /// The HOTP slot to generate a password from if the scrolllock + /// key is pressed twice (slot 0-1, or any other value to + /// disable the function). + pub config_scrolllock: u8, + /// Indicates whether the user password is required to generate + /// an OTP value. + pub otp_user_password: bool, +} +#[test] +fn bindgen_test_layout_NK_status() { + assert_eq!( + ::std::mem::size_of::(), + 12usize, + concat!("Size of: ", stringify!(NK_status)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(NK_status)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).firmware_version_major as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(firmware_version_major) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).firmware_version_minor as *const _ as usize + }, + 1usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(firmware_version_minor) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).serial_number_smart_card as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(serial_number_smart_card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).config_numlock as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(config_numlock) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).config_capslock as *const _ as usize }, + 9usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(config_capslock) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).config_scrolllock as *const _ as usize }, + 10usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(config_scrolllock) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).otp_user_password as *const _ as usize }, + 11usize, + concat!( + "Offset of field: ", + stringify!(NK_status), + "::", + stringify!(otp_user_password) + ) + ); +} /// Stores the status of a Storage device. #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -259,6 +446,54 @@ fn bindgen_test_layout_NK_storage_status() { ) ); } +/// Data about the usage of the SD card. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct NK_SD_usage_data { + /// The minimum write level, as a percentage of the total card + /// size. + pub write_level_min: u8, + /// The maximum write level, as a percentage of the total card + /// size. + pub write_level_max: u8, +} +#[test] +fn bindgen_test_layout_NK_SD_usage_data() { + assert_eq!( + ::std::mem::size_of::(), + 2usize, + concat!("Size of: ", stringify!(NK_SD_usage_data)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(NK_SD_usage_data)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).write_level_min as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(NK_SD_usage_data), + "::", + stringify!(write_level_min) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).write_level_max as *const _ as usize + }, + 1usize, + concat!( + "Offset of field: ", + stringify!(NK_SD_usage_data), + "::", + stringify!(write_level_max) + ) + ); +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct NK_storage_ProductionTest { @@ -519,10 +754,29 @@ extern "C" { /// @return true if a device is connected and the out argument has been set pub fn NK_get_device_model() -> NK_device_model; } +extern "C" { + /// Return the debug status string. Debug purposes. This function is + /// deprecated in favor of NK_get_status_as_string. + /// @return string representation of the status or an empty string + /// if the command failed + #[deprecated(since = "3.5.0", note="use `NK_get_status_as_string` instead")] + pub fn NK_status() -> *mut ::std::os::raw::c_char; +} extern "C" { /// Return the debug status string. Debug purposes. + /// @return string representation of the status or an empty string + /// if the command failed + pub fn NK_get_status_as_string() -> *mut ::std::os::raw::c_char; +} +extern "C" { + /// Get the stick status common to all Nitrokey devices and return the + /// command processing error code. If the code is zero, i. e. the + /// command was successful, the storage status is written to the output + /// pointer's target. The output pointer must not be null. + /// + /// @param out the output pointer for the status /// @return command processing error code - pub fn NK_status() -> *mut ::std::os::raw::c_char; + pub fn NK_get_status(out: *mut NK_status) -> ::std::os::raw::c_int; } extern "C" { /// Return the device's serial number string in hex. @@ -760,6 +1014,7 @@ extern "C" { pub fn NK_totp_set_time_soft(time: u64) -> ::std::os::raw::c_int; } extern "C" { + #[deprecated(since = "3.4.0", note="use `NK_totp_set_time_soft` instead")] pub fn NK_totp_get_time() -> ::std::os::raw::c_int; } extern "C" { @@ -853,12 +1108,12 @@ extern "C" { extern "C" { /// Get device's major firmware version /// @return major part of the version number (e.g. 0 from 0.48, 0 from 0.7 etc.) - pub fn NK_get_major_firmware_version() -> ::std::os::raw::c_int; + pub fn NK_get_major_firmware_version() -> u8; } extern "C" { /// Get device's minor firmware version /// @return minor part of the version number (e.g. 7 from 0.7, 48 from 0.48 etc.) - pub fn NK_get_minor_firmware_version() -> ::std::os::raw::c_int; + pub fn NK_get_minor_firmware_version() -> u8; } extern "C" { /// Function to determine unencrypted volume PIN type @@ -1067,6 +1322,16 @@ extern "C" { /// @return command processing error code pub fn NK_get_status_storage(out: *mut NK_storage_status) -> ::std::os::raw::c_int; } +extern "C" { + /// Get SD card usage attributes. Usable during hidden volumes creation. + /// If the command was successful (return value 0), the usage data is + /// written to the output pointer’s target. The output pointer must + /// not be null. + /// Storage only + /// @param out the output pointer for the usage data + /// @return command processing error code + pub fn NK_get_SD_usage_data(out: *mut NK_SD_usage_data) -> ::std::os::raw::c_int; +} extern "C" { /// Get SD card usage attributes as string. /// Usable during hidden volumes creation. @@ -1077,7 +1342,8 @@ extern "C" { extern "C" { /// Get progress value of current long operation. /// Storage only - /// @return int in range 0-100 or -1 if device is not busy + /// @return int in range 0-100 or -1 if device is not busy or -2 if an + /// error occured pub fn NK_get_progress_bar_value() -> ::std::os::raw::c_int; } extern "C" { @@ -1096,6 +1362,18 @@ extern "C" { /// @return string delimited id's of connected devices pub fn NK_list_devices_by_cpuID() -> *mut ::std::os::raw::c_char; } +extern "C" { + /// Returns a linked list of all connected devices, or null if no devices + /// are connected or an error occured. The linked list must be freed by + /// calling NK_free_device_info. + /// @return a linked list of all connected devices + pub fn NK_list_devices() -> *mut NK_device_info; +} +extern "C" { + /// Free a linked list returned by NK_list_devices. + /// @param the linked list to free or null + pub fn NK_free_device_info(device_info: *mut NK_device_info); +} extern "C" { /// Connects to the device with given ID. ID's list could be created with NK_list_devices_by_cpuID. /// Requires calling to NK_list_devices_by_cpuID first. Connecting to arbitrary ID/USB path is not handled. @@ -1105,8 +1383,99 @@ extern "C" { /// @return 1 on successful connection, 0 otherwise pub fn NK_connect_with_ID(id: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; } +extern "C" { + /// Connects to a device with the given path. The path is a USB device + /// path as returned by hidapi. + /// @param path the device path + /// @return 1 on successful connection, 0 otherwise + pub fn NK_connect_with_path(path: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} extern "C" { /// Blink red and green LED alternatively and infinitely (until device is reconnected). /// @return command processing error code pub fn NK_wink() -> ::std::os::raw::c_int; } +extern "C" { + /// Enable update mode on Nitrokey Pro. + /// Supported from v0.11. + /// @param update_password 20 bytes update password + /// @return command processing error code + pub fn NK_enable_firmware_update_pro( + update_password: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + /// Change update-mode password on Nitrokey Pro. + /// Supported from v0.11. + /// @param current_firmware_password 20 bytes update password + /// @param new_firmware_password 20 bytes update password + /// @return command processing error code + pub fn NK_change_firmware_password_pro( + current_firmware_password: *const ::std::os::raw::c_char, + new_firmware_password: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ReadSlot_t { + pub slot_name: [u8; 15usize], + pub _slot_config: u8, + pub slot_token_id: [u8; 13usize], + pub slot_counter: u64, +} +#[test] +fn bindgen_test_layout_ReadSlot_t() { + assert_eq!( + ::std::mem::size_of::(), + 40usize, + concat!("Size of: ", stringify!(ReadSlot_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(ReadSlot_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).slot_name as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(ReadSlot_t), + "::", + stringify!(slot_name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::()))._slot_config as *const _ as usize }, + 15usize, + concat!( + "Offset of field: ", + stringify!(ReadSlot_t), + "::", + stringify!(_slot_config) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).slot_token_id as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(ReadSlot_t), + "::", + stringify!(slot_token_id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).slot_counter as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(ReadSlot_t), + "::", + stringify!(slot_counter) + ) + ); +} +extern "C" { + pub fn NK_read_HOTP_slot(slot_num: u8, out: *mut ReadSlot_t) -> ::std::os::raw::c_int; +} diff --git a/nitrokey/CHANGELOG.md b/nitrokey/CHANGELOG.md index e98e857..3051d0f 100644 --- a/nitrokey/CHANGELOG.md +++ b/nitrokey/CHANGELOG.md @@ -38,6 +38,16 @@ SPDX-License-Identifier: MIT - Implement `DerefMut` for `User` and `Admin`. - Add `device_mut` method to `DeviceWrapper`. - Require a mutable `Device` reference if a method changes the device state. +- Update the `nitrokey-sys` dependency to version 3.5.0. +- Update the `nitrokey-test` dependency to version 0.3 and add the + `nitrokey-test-state` dependency in version 0.1.0. +- Refactor connection management: + - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. + - Add the `Manager` struct that manages connections to Nitrokey devices. + - Remove `connect`, `connect_model`, `Pro::connect` and `Storage::connect`. + - Add the `into_manager` function to the `Device` trait. + - Add the `force_take` function that ignores a `PoisonError` when accessing + the manager instance. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/nitrokey/Cargo.toml b/nitrokey/Cargo.toml index fd6fef7..62eea02 100644 --- a/nitrokey/Cargo.toml +++ b/nitrokey/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nitrokey" -version = "0.4.0-alpha.2" +version = "0.4.0-alpha.3" authors = ["Robin Krahl "] edition = "2018" homepage = "https://code.ireas.org/nitrokey-rs/" @@ -17,10 +17,12 @@ license = "MIT" exclude = [".builds/*"] [dependencies] +lazy_static = "1.2.0" libc = "0.2" -nitrokey-sys = "~3.4" +nitrokey-sys = "3.5" rand_core = {version = "0.3", default-features = false, features = ["std"] } rand_os = {version = "0.1"} [dev-dependencies] -nitrokey-test = {version = "=0.2.0"} +nitrokey-test = "0.3" +nitrokey-test-state = "0.1.0" diff --git a/nitrokey/README.md b/nitrokey/README.md index 8c596eb..a29ac6f 100644 --- a/nitrokey/README.md +++ b/nitrokey/README.md @@ -28,13 +28,6 @@ supported by `nitrokey-rs`: - `NK_get_device_model`. We know which model we connected to, so we can provide this information without calling `libnitrokey`. -- `NK_get_time`. This method is useless as it will always cause a timestamp - error on the device (see [pull request #114][] for `libnitrokey` for details). -- `NK_get_status`. This method only provides a string representation of - data that can be accessed by other methods (firmware version, serial number, - configuration). -- `NK_get_status_storage_as_string`. This method only provides an incomplete - string representation of the data returned by `NK_get_status_storage`. - `NK_is_AES_supported`. This method is no longer needed for Nitrokey devices with a recent firmware version. - `NK_set_unencrypted_volume_rorw_pin_type_user`, @@ -42,6 +35,11 @@ supported by `nitrokey-rs`: methods are only relevant for older firmware versions (pre-v0.51). As the Nitrokey Storage firmware can be updated easily, we do not support these outdated versions. +- `NK_totp_get_time`, `NK_status`. These functions are deprecated. +- `NK_read_HOTP_slot`. This function is only available for HOTP slots, not for + TOTP. We will support it once both types are supported by `libnitrokey`. +- All `*_as_string` functions that return string representations of data + returned by other functions. ## Tests @@ -82,7 +80,6 @@ under the [LGPL-3.0][]. [`libnitrokey`]: https://github.com/nitrokey/libnitrokey [`nitrokey-test`]: https://github.com/d-e-s-o/nitrokey-test [nitrokey-rs-dev@ireas.org]: mailto:nitrokey-rs-dev@ireas.org -[pull request #114]: https://github.com/Nitrokey/libnitrokey/pull/114 [MIT license]: https://opensource.org/licenses/MIT [LGPL-3.0]: https://opensource.org/licenses/lgpl-3.0.html [reuse]: https://reuse.software/practices/2.0/ diff --git a/nitrokey/TODO.md b/nitrokey/TODO.md index 1ff723d..efa66d3 100644 --- a/nitrokey/TODO.md +++ b/nitrokey/TODO.md @@ -6,10 +6,16 @@ SPDX-License-Identifier: MIT - Add support for the currently unsupported commands: - `NK_send_startup` - `NK_fill_SD_card_with_random_data` - - `NK_get_SD_usage_data_as_string` + - `NK_get_SD_usage_data` - `NK_get_progress_bar_value` - `NK_list_devices_by_cpuID` - `NK_connect_with_ID` + - `NK_get_status` + - `NK_list_devices` + - `NK_free_device_info` + - `NK_connect_with_path` + - `NK_enable_firmware_update_pro` + - `NK_change_firmware_password_pro` - Clear passwords from memory. - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware issue 65][]). diff --git a/nitrokey/src/auth.rs b/nitrokey/src/auth.rs index f9f50fa..0b000f7 100644 --- a/nitrokey/src/auth.rs +++ b/nitrokey/src/auth.rs @@ -1,6 +1,7 @@ // Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT +use std::marker; use std::ops; use std::os::raw::c_char; use std::os::raw::c_int; @@ -18,7 +19,7 @@ static TEMPORARY_PASSWORD_LENGTH: usize = 25; /// Provides methods to authenticate as a user or as an admin using a PIN. The authenticated /// methods will consume the current device instance. On success, they return the authenticated /// device. Otherwise, they return the current unauthenticated device and the error code. -pub trait Authenticate { +pub trait Authenticate<'a> { /// Performs user authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the /// error are returned. @@ -38,11 +39,12 @@ pub trait Authenticate { /// use nitrokey::{Authenticate, DeviceWrapper, User}; /// # use nitrokey::Error; /// - /// fn perform_user_task(device: &User) {} + /// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -61,9 +63,9 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> where - Self: Device + Sized; + Self: Device<'a> + Sized; /// Performs admin authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the @@ -84,11 +86,12 @@ pub trait Authenticate { /// use nitrokey::{Authenticate, Admin, DeviceWrapper}; /// # use nitrokey::Error; /// - /// fn perform_admin_task(device: &Admin) {} + /// fn perform_admin_task<'a>(device: &Admin<'a, DeviceWrapper<'a>>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let device = match device.authenticate_admin("123456") { /// Ok(admin) => { /// perform_admin_task(&admin); @@ -107,9 +110,9 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> where - Self: Device + Sized; + Self: Device<'a> + Sized; } trait AuthenticatedDevice { @@ -128,9 +131,10 @@ trait AuthenticatedDevice { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct User { +pub struct User<'a, T: Device<'a>> { device: T, temp_password: Vec, + marker: marker::PhantomData<&'a T>, } /// A Nitrokey device with admin authentication. @@ -143,14 +147,15 @@ pub struct User { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct Admin { +pub struct Admin<'a, T: Device<'a>> { device: T, temp_password: Vec, + marker: marker::PhantomData<&'a T>, } -fn authenticate(device: D, password: &str, callback: T) -> Result +fn authenticate<'a, D, A, T>(device: D, password: &str, callback: T) -> Result where - D: Device, + D: Device<'a>, A: AuthenticatedDevice, T: Fn(*const c_char, *const c_char) -> c_int, { @@ -170,14 +175,14 @@ where } } -fn authenticate_user_wrapper( +fn authenticate_user_wrapper<'a, T, C>( device: T, constructor: C, password: &str, -) -> Result, (DeviceWrapper, Error)> +) -> Result>, (DeviceWrapper<'a>, Error)> where - T: Device, - C: Fn(T) -> DeviceWrapper, + T: Device<'a> + 'a, + C: Fn(T) -> DeviceWrapper<'a>, { let result = device.authenticate_user(password); match result { @@ -186,14 +191,14 @@ where } } -fn authenticate_admin_wrapper( +fn authenticate_admin_wrapper<'a, T, C>( device: T, constructor: C, password: &str, -) -> Result, (DeviceWrapper, Error)> +) -> Result>, (DeviceWrapper<'a>, Error)> where - T: Device, - C: Fn(T) -> DeviceWrapper, + T: Device<'a> + 'a, + C: Fn(T) -> DeviceWrapper<'a>, { let result = device.authenticate_admin(password); match result { @@ -202,7 +207,7 @@ where } } -impl User { +impl<'a, T: Device<'a>> User<'a, T> { /// Forgets the user authentication and returns an unauthenticated device. This method /// consumes the authenticated device. It does not perform any actual commands on the /// Nitrokey. @@ -211,7 +216,7 @@ impl User { } } -impl ops::Deref for User { +impl<'a, T: Device<'a>> ops::Deref for User<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -219,13 +224,13 @@ impl ops::Deref for User { } } -impl ops::DerefMut for User { +impl<'a, T: Device<'a>> ops::DerefMut for User<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.device } } -impl GenerateOtp for User { +impl<'a, T: Device<'a>> GenerateOtp for User<'a, T> { fn get_hotp_code(&mut self, slot: u8) -> Result { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) @@ -239,11 +244,12 @@ impl GenerateOtp for User { } } -impl AuthenticatedDevice for User { +impl<'a, T: Device<'a>> AuthenticatedDevice for User<'a, T> { fn new(device: T, temp_password: Vec) -> Self { User { device, temp_password, + marker: marker::PhantomData, } } @@ -252,7 +258,7 @@ impl AuthenticatedDevice for User { } } -impl ops::Deref for Admin { +impl<'a, T: Device<'a>> ops::Deref for Admin<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -260,13 +266,13 @@ impl ops::Deref for Admin { } } -impl ops::DerefMut for Admin { +impl<'a, T: Device<'a>> ops::DerefMut for Admin<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.device } } -impl Admin { +impl<'a, T: Device<'a>> Admin<'a, T> { /// Forgets the user authentication and returns an unauthenticated device. This method /// consumes the authenticated device. It does not perform any actual commands on the /// Nitrokey. @@ -287,7 +293,8 @@ impl Admin { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let config = Config::new(None, None, None, false); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -316,7 +323,7 @@ impl Admin { } } -impl ConfigureOtp for Admin { +impl<'a, T: Device<'a>> ConfigureOtp for Admin<'a, T> { fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { @@ -364,11 +371,12 @@ impl ConfigureOtp for Admin { } } -impl AuthenticatedDevice for Admin { +impl<'a, T: Device<'a>> AuthenticatedDevice for Admin<'a, T> { fn new(device: T, temp_password: Vec) -> Self { Admin { device, temp_password, + marker: marker::PhantomData, } } @@ -377,8 +385,8 @@ impl AuthenticatedDevice for Admin { } } -impl Authenticate for DeviceWrapper { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for DeviceWrapper<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -387,7 +395,7 @@ impl Authenticate for DeviceWrapper { } } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -399,28 +407,28 @@ impl Authenticate for DeviceWrapper { } } -impl Authenticate for Pro { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for Pro<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) } } -impl Authenticate for Storage { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for Storage<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) diff --git a/nitrokey/src/device.rs b/nitrokey/src/device.rs index f6492cd..758d4c1 100644 --- a/nitrokey/src/device.rs +++ b/nitrokey/src/device.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT use std::fmt; -use std::marker; use libc; use nitrokey_sys; @@ -54,7 +53,7 @@ impl fmt::Display for VolumeMode { /// A wrapper for a Nitrokey device of unknown type. /// -/// Use the function [`connect`][] to obtain a wrapped instance. The wrapper implements all traits +/// Use the [`connect`][] method to obtain a wrapped instance. The wrapper implements all traits /// that are shared between all Nitrokey devices so that the shared functionality can be used /// without knowing the type of the underlying device. If you want to use functionality that is /// not available for all devices, you have to extract the device. @@ -67,11 +66,12 @@ impl fmt::Display for VolumeMode { /// use nitrokey::{Authenticate, DeviceWrapper, User}; /// # use nitrokey::Error; /// -/// fn perform_user_task(device: &User) {} +/// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -97,7 +97,8 @@ impl fmt::Display for VolumeMode { /// fn perform_storage_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; /// perform_common_task(&device); /// match device { /// DeviceWrapper::Storage(storage) => perform_storage_task(&storage), @@ -107,21 +108,20 @@ impl fmt::Display for VolumeMode { /// # } /// ``` /// -/// [`connect`]: fn.connect.html +/// [`connect`]: struct.Manager.html#method.connect #[derive(Debug)] -pub enum DeviceWrapper { +pub enum DeviceWrapper<'a> { /// A Nitrokey Storage device. - Storage(Storage), + Storage(Storage<'a>), /// A Nitrokey Pro device. - Pro(Pro), + Pro(Pro<'a>), } /// A Nitrokey Pro device without user or admin authentication. /// -/// Use the global function [`connect`][] to obtain an instance wrapper or the method -/// [`connect`][`Pro::connect`] to directly obtain an instance. If you want to execute a command -/// that requires user or admin authentication, use [`authenticate_admin`][] or -/// [`authenticate_user`][]. +/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_pro`] method to +/// directly obtain an instance. If you want to execute a command that requires user or admin +/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][]. /// /// # Examples /// @@ -131,11 +131,12 @@ pub enum DeviceWrapper { /// use nitrokey::{Authenticate, User, Pro}; /// # use nitrokey::Error; /// -/// fn perform_user_task(device: &User) {} +/// fn perform_user_task<'a>(device: &User<'a, Pro<'a>>) {} /// fn perform_other_task(device: &Pro) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Pro::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect_pro()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -153,21 +154,18 @@ pub enum DeviceWrapper { /// /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user -/// [`connect`]: fn.connect.html -/// [`Pro::connect`]: #method.connect +/// [`connect`]: struct.Manager.html#method.connect +/// [`connect_pro`]: struct.Manager.html#method.connect_pro #[derive(Debug)] -pub struct Pro { - // make sure that users cannot directly instantiate this type - #[doc(hidden)] - marker: marker::PhantomData<()>, +pub struct Pro<'a> { + manager: Option<&'a mut crate::Manager>, } /// A Nitrokey Storage device without user or admin authentication. /// -/// Use the global function [`connect`][] to obtain an instance wrapper or the method -/// [`connect`][`Storage::connect`] to directly obtain an instance. If you want to execute a -/// command that requires user or admin authentication, use [`authenticate_admin`][] or -/// [`authenticate_user`][]. +/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_storage`] method to +/// directly obtain an instance. If you want to execute a command that requires user or admin +/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][]. /// /// # Examples /// @@ -177,11 +175,12 @@ pub struct Pro { /// use nitrokey::{Authenticate, User, Storage}; /// # use nitrokey::Error; /// -/// fn perform_user_task(device: &User) {} +/// fn perform_user_task<'a>(device: &User<'a, Storage<'a>>) {} /// fn perform_other_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Storage::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect_storage()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -199,13 +198,11 @@ pub struct Pro { /// /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user -/// [`connect`]: fn.connect.html -/// [`Storage::connect`]: #method.connect +/// [`connect`]: struct.Manager.html#method.connect +/// [`connect_storage`]: struct.Manager.html#method.connect_storage #[derive(Debug)] -pub struct Storage { - // make sure that users cannot directly instantiate this type - #[doc(hidden)] - marker: marker::PhantomData<()>, +pub struct Storage<'a> { + manager: Option<&'a mut crate::Manager>, } /// The status of a volume on a Nitrokey Storage device. @@ -296,7 +293,32 @@ pub struct StorageStatus { /// /// This trait provides the commands that can be executed without authentication and that are /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { +pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { + /// Returns the [`Manager`][] instance that has been used to connect to this device. + /// + /// # Example + /// + /// ``` + /// use nitrokey::{Device, DeviceWrapper}; + /// + /// fn do_something(device: DeviceWrapper) { + /// // reconnect to any device + /// let manager = device.into_manager(); + /// let device = manager.connect(); + /// // do something with the device + /// // ... + /// } + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect() { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + fn into_manager(self) -> &'a mut crate::Manager; + /// Returns the model of the connected Nitrokey device. /// /// # Example @@ -306,7 +328,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// println!("Connected to a Nitrokey {}", device.get_model()); /// # Ok(()) /// # } @@ -322,7 +345,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// match device.get_serial_number() { /// Ok(number) => println!("serial no: {}", number), /// Err(err) => eprintln!("Could not get serial number: {}", err), @@ -344,7 +368,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// let count = device.get_user_retry_count(); /// match device.get_user_retry_count() { /// Ok(count) => println!("{} remaining authentication attempts (user)", count), /// Err(err) => eprintln!("Could not get user retry count: {}", err), @@ -366,7 +392,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let count = device.get_admin_retry_count(); /// match device.get_admin_retry_count() { /// Ok(count) => println!("{} remaining authentication attempts (admin)", count), @@ -388,7 +415,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// match device.get_firmware_version() { /// Ok(version) => println!("Firmware version: {}", version), /// Err(err) => eprintln!("Could not access firmware version: {}", err), @@ -399,14 +427,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { fn get_firmware_version(&self) -> Result { let major = result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() })?; let minor = result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() })?; - let max = i32::from(u8::max_value()); - if major < 0 || minor < 0 || major > max || minor > max { - return Err(Error::UnexpectedError); - } - Ok(FirmwareVersion { - major: major as u8, - minor: minor as u8, - }) + Ok(FirmwareVersion { major, minor }) } /// Returns the current configuration of the Nitrokey device. @@ -418,7 +439,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let config = device.get_config()?; /// println!("numlock binding: {:?}", config.numlock); /// println!("capslock binding: {:?}", config.capslock); @@ -452,7 +474,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.change_admin_pin("12345678", "12345679") { /// Ok(()) => println!("Updated admin PIN."), /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), @@ -485,7 +508,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.change_user_pin("123456", "123457") { /// Ok(()) => println!("Updated admin PIN."), /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), @@ -518,7 +542,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.unlock_user_pin("12345678", "123456") { /// Ok(()) => println!("Unlocked user PIN."), /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err), @@ -552,7 +577,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.lock() { /// Ok(()) => println!("Locked the Nitrokey device."), /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err), @@ -583,7 +609,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.factory_reset("12345678") { /// Ok(()) => println!("Performed a factory reset."), /// Err(err) => eprintln!("Could not perform a factory reset: {}", err), @@ -617,7 +644,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.build_aes_key("12345678") { /// Ok(()) => println!("New AES keys have been built."), /// Err(err) => eprintln!("Could not build new AES keys: {}", err), @@ -633,67 +661,6 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { } } -/// Connects to a Nitrokey device. This method can be used to connect to any connected device, -/// both a Nitrokey Pro and a Nitrokey Storage. -/// -/// # Errors -/// -/// - [`NotConnected`][] if no Nitrokey device is connected -/// -/// # Example -/// -/// ``` -/// use nitrokey::DeviceWrapper; -/// -/// fn do_something(device: DeviceWrapper) {} -/// -/// match nitrokey::connect() { -/// Ok(device) => do_something(device), -/// Err(err) => eprintln!("Could not connect to a Nitrokey: {}", err), -/// } -/// ``` -/// -/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected -pub fn connect() -> Result { - if unsafe { nitrokey_sys::NK_login_auto() } == 1 { - match get_connected_device() { - Some(wrapper) => Ok(wrapper), - None => Err(CommunicationError::NotConnected.into()), - } - } else { - Err(CommunicationError::NotConnected.into()) - } -} - -/// Connects to a Nitrokey device of the given model. -/// -/// # Errors -/// -/// - [`NotConnected`][] if no Nitrokey device of the given model is connected -/// -/// # Example -/// -/// ``` -/// use nitrokey::DeviceWrapper; -/// use nitrokey::Model; -/// -/// fn do_something(device: DeviceWrapper) {} -/// -/// match nitrokey::connect_model(Model::Pro) { -/// Ok(device) => do_something(device), -/// Err(err) => eprintln!("Could not connect to a Nitrokey Pro: {}", err), -/// } -/// ``` -/// -/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected -pub fn connect_model(model: Model) -> Result { - if connect_enum(model) { - Ok(create_device_wrapper(model)) - } else { - Err(CommunicationError::NotConnected.into()) - } -} - fn get_connected_model() -> Option { match unsafe { nitrokey_sys::NK_get_device_model() } { nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), @@ -702,18 +669,26 @@ fn get_connected_model() -> Option { } } -fn create_device_wrapper(model: Model) -> DeviceWrapper { +pub(crate) fn create_device_wrapper( + manager: &mut crate::Manager, + model: Model, +) -> DeviceWrapper<'_> { match model { - Model::Pro => Pro::new().into(), - Model::Storage => Storage::new().into(), + Model::Pro => Pro::new(manager).into(), + Model::Storage => Storage::new(manager).into(), } } -fn get_connected_device() -> Option { - get_connected_model().map(create_device_wrapper) +pub(crate) fn get_connected_device( + manager: &mut crate::Manager, +) -> Result, Error> { + match get_connected_model() { + Some(model) => Ok(create_device_wrapper(manager, model)), + None => Err(CommunicationError::NotConnected.into()), + } } -fn connect_enum(model: Model) -> bool { +pub(crate) fn connect_enum(model: Model) -> bool { let model = match model { Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE, Model::Pro => nitrokey_sys::NK_device_model_NK_PRO, @@ -721,15 +696,15 @@ fn connect_enum(model: Model) -> bool { unsafe { nitrokey_sys::NK_login_enum(model) == 1 } } -impl DeviceWrapper { - fn device(&self) -> &dyn Device { +impl<'a> DeviceWrapper<'a> { + fn device(&self) -> &dyn Device<'a> { match *self { DeviceWrapper::Storage(ref storage) => storage, DeviceWrapper::Pro(ref pro) => pro, } } - fn device_mut(&mut self) -> &mut dyn Device { + fn device_mut(&mut self) -> &mut dyn Device<'a> { match *self { DeviceWrapper::Storage(ref mut storage) => storage, DeviceWrapper::Pro(ref mut pro) => pro, @@ -737,19 +712,19 @@ impl DeviceWrapper { } } -impl From for DeviceWrapper { - fn from(device: Pro) -> Self { +impl<'a> From> for DeviceWrapper<'a> { + fn from(device: Pro<'a>) -> Self { DeviceWrapper::Pro(device) } } -impl From for DeviceWrapper { - fn from(device: Storage) -> Self { +impl<'a> From> for DeviceWrapper<'a> { + fn from(device: Storage<'a>) -> Self { DeviceWrapper::Storage(device) } } -impl GenerateOtp for DeviceWrapper { +impl<'a> GenerateOtp for DeviceWrapper<'a> { fn get_hotp_slot_name(&self, slot: u8) -> Result { self.device().get_hotp_slot_name(slot) } @@ -767,7 +742,14 @@ impl GenerateOtp for DeviceWrapper { } } -impl Device for DeviceWrapper { +impl<'a> Device<'a> for DeviceWrapper<'a> { + fn into_manager(self) -> &'a mut crate::Manager { + match self { + DeviceWrapper::Pro(dev) => dev.into_manager(), + DeviceWrapper::Storage(dev) => dev.into_manager(), + } + } + fn get_model(&self) -> Model { match *self { DeviceWrapper::Pro(_) => Model::Pro, @@ -776,44 +758,15 @@ impl Device for DeviceWrapper { } } -impl Pro { - /// Connects to a Nitrokey Pro. - /// - /// # Errors - /// - /// - [`NotConnected`][] if no Nitrokey device of the given model is connected - /// - /// # Example - /// - /// ``` - /// use nitrokey::Pro; - /// - /// fn use_pro(device: Pro) {} - /// - /// match nitrokey::Pro::connect() { - /// Ok(device) => use_pro(device), - /// Err(err) => eprintln!("Could not connect to the Nitrokey Pro: {}", err), - /// } - /// ``` - /// - /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected - pub fn connect() -> Result { - // TODO: maybe Option instead of Result? - if connect_enum(Model::Pro) { - Ok(Pro::new()) - } else { - Err(CommunicationError::NotConnected.into()) - } - } - - fn new() -> Pro { +impl<'a> Pro<'a> { + pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { Pro { - marker: marker::PhantomData, + manager: Some(manager), } } } -impl Drop for Pro { +impl<'a> Drop for Pro<'a> { fn drop(&mut self) { unsafe { nitrokey_sys::NK_logout(); @@ -821,47 +774,22 @@ impl Drop for Pro { } } -impl Device for Pro { +impl<'a> Device<'a> for Pro<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Pro } } -impl GenerateOtp for Pro {} +impl<'a> GenerateOtp for Pro<'a> {} -impl Storage { - /// Connects to a Nitrokey Storage. - /// - /// # Errors - /// - /// - [`NotConnected`][] if no Nitrokey device of the given model is connected - /// - /// # Example - /// - /// ``` - /// use nitrokey::Storage; - /// - /// fn use_storage(device: Storage) {} - /// - /// match nitrokey::Storage::connect() { - /// Ok(device) => use_storage(device), - /// Err(err) => eprintln!("Could not connect to the Nitrokey Storage: {}", err), - /// } - /// ``` - /// - /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected - pub fn connect() -> Result { - // TODO: maybe Option instead of Result? - if connect_enum(Model::Storage) { - Ok(Storage::new()) - } else { - Err(CommunicationError::NotConnected.into()) - } - } - - fn new() -> Storage { +impl<'a> Storage<'a> { + pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { Storage { - marker: marker::PhantomData, + manager: Some(manager), } } @@ -882,7 +810,8 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), /// Err(err) => eprintln!("Failed to update update PIN: {}", err), @@ -919,7 +848,8 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), /// Err(err) => eprintln!("Could not enter update mode: {}", err), @@ -953,7 +883,8 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), @@ -982,7 +913,8 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => { /// println!("Enabled the encrypted volume."); @@ -1028,7 +960,8 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => println!("Enabled a hidden volume."), @@ -1061,7 +994,8 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => { @@ -1108,7 +1042,8 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// device.enable_encrypted_volume("123445")?; /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; /// # Ok(()) @@ -1148,7 +1083,8 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), @@ -1193,7 +1129,8 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.set_encrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the encrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the encrypted volume to read-write mode: {}", err), @@ -1231,7 +1168,8 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect_storage()?; /// match device.get_status() { /// Ok(status) => { /// println!("SD card ID: {:#x}", status.serial_number_sd_card); @@ -1274,7 +1212,8 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect_storage()?; /// match device.get_production_info() { /// Ok(data) => { /// println!("SD card ID: {:#x}", data.sd_card.serial_number); @@ -1322,7 +1261,8 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), /// Err(err) => eprintln!("Could not set the clear the new SD card warning: {}", err), @@ -1367,7 +1307,7 @@ impl Storage { } } -impl Drop for Storage { +impl<'a> Drop for Storage<'a> { fn drop(&mut self) { unsafe { nitrokey_sys::NK_logout(); @@ -1375,13 +1315,17 @@ impl Drop for Storage { } } -impl Device for Storage { +impl<'a> Device<'a> for Storage<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Storage } } -impl GenerateOtp for Storage {} +impl<'a> GenerateOtp for Storage<'a> {} impl From for StorageProductionInfo { fn from(data: nitrokey_sys::NK_storage_ProductionTest) -> Self { diff --git a/nitrokey/src/error.rs b/nitrokey/src/error.rs index 1730171..9e6adc0 100644 --- a/nitrokey/src/error.rs +++ b/nitrokey/src/error.rs @@ -5,6 +5,7 @@ use std::error; use std::fmt; use std::os::raw; use std::str; +use std::sync; use crate::device; @@ -13,11 +14,15 @@ use crate::device; pub enum Error { /// An error reported by the Nitrokey device in the response packet. CommandError(CommandError), - /// A device communication. + /// A device communication error. CommunicationError(CommunicationError), + /// An error occurred due to concurrent access to the Nitrokey device. + ConcurrentAccessError, /// A library usage error. LibraryError(LibraryError), - /// An error that occured during random number generation. + /// An error that occurred due to a poisoned lock. + PoisonError(sync::PoisonError>), + /// An error that occurred during random number generation. RandError(Box), /// An error that is caused by an unexpected value returned by libnitrokey. UnexpectedError, @@ -65,7 +70,22 @@ impl From for Error { } } -impl From<(T, Error)> for Error { +impl From>> for Error { + fn from(error: sync::PoisonError>) -> Self { + Error::PoisonError(error) + } +} + +impl From>> for Error { + fn from(error: sync::TryLockError>) -> Self { + match error { + sync::TryLockError::Poisoned(err) => err.into(), + sync::TryLockError::WouldBlock => Error::ConcurrentAccessError, + } + } +} + +impl<'a, T: device::Device<'a>> From<(T, Error)> for Error { fn from((_, err): (T, Error)) -> Self { err } @@ -76,7 +96,9 @@ impl error::Error for Error { match *self { Error::CommandError(ref err) => Some(err), Error::CommunicationError(ref err) => Some(err), + Error::ConcurrentAccessError => None, Error::LibraryError(ref err) => Some(err), + Error::PoisonError(ref err) => Some(err), Error::RandError(ref err) => Some(err.as_ref()), Error::UnexpectedError => None, Error::UnknownError(_) => None, @@ -90,7 +112,9 @@ impl fmt::Display for Error { match *self { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err), + Error::ConcurrentAccessError => write!(f, "Internal error: concurrent access"), Error::LibraryError(ref err) => write!(f, "Library error: {}", err), + Error::PoisonError(_) => write!(f, "Internal error: poisoned lock"), Error::RandError(ref err) => write!(f, "RNG error: {}", err), Error::UnexpectedError => write!(f, "An unexpected error occurred"), Error::UnknownError(ref err) => write!(f, "Unknown error: {}", err), diff --git a/nitrokey/src/lib.rs b/nitrokey/src/lib.rs index c35829c..a4402c5 100644 --- a/nitrokey/src/lib.rs +++ b/nitrokey/src/lib.rs @@ -9,13 +9,16 @@ //! performed without authentication, some require user access, and some require admin access. //! This is modelled using the types [`User`][] and [`Admin`][]. //! -//! Use [`connect`][] to connect to any Nitrokey device. The method will return a +//! You can only connect to one Nitrokey at a time. Use the global [`take`][] function to obtain +//! an reference to the [`Manager`][] singleton that keeps track of the connections. Then use the +//! [`connect`][] method to connect to any Nitrokey device. The method will return a //! [`DeviceWrapper`][] that abstracts over the supported Nitrokey devices. You can also use -//! [`Pro::connect`][] or [`Storage::connect`][] to connect to a specific device. +//! [`connect_model`][], [`connect_pro`][] or [`connect_storage`][] to connect to a specific +//! device. //! -//! You can then use [`authenticate_user`][] or [`authenticate_admin`][] to get an authenticated -//! device that can perform operations that require authentication. You can use [`device`][] to go -//! back to the unauthenticated device. +//! You can call [`authenticate_user`][] or [`authenticate_admin`][] to get an authenticated device +//! that can perform operations that require authentication. You can use [`device`][] to go back +//! to the unauthenticated device. //! //! This makes sure that you can only execute a command if you have the required access rights. //! Otherwise, your code will not compile. The only exception are the methods to generate one-time @@ -31,7 +34,8 @@ //! # use nitrokey::Error; //! //! # fn try_main() -> Result<(), Error> { -//! let device = nitrokey::connect()?; +//! let mut manager = nitrokey::take()?; +//! let device = manager.connect()?; //! println!("{}", device.get_serial_number()?); //! # Ok(()) //! # } @@ -44,7 +48,8 @@ //! # use nitrokey::Error; //! //! # fn try_main() -> Result<(), Error> { -//! let device = nitrokey::connect()?; +//! let mut manager = nitrokey::take()?; +//! let device = manager.connect()?; //! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); //! match device.authenticate_admin("12345678") { //! Ok(mut admin) => { @@ -66,7 +71,8 @@ //! # use nitrokey::Error; //! //! # fn try_main() -> Result<(), Error> { -//! let mut device = nitrokey::connect()?; +//! let mut manager = nitrokey::take()?; +//! let mut device = manager.connect()?; //! match device.get_hotp_code(1) { //! Ok(code) => println!("Generated HOTP code: {}", code), //! Err(err) => eprintln!("Could not generate HOTP code: {}", err), @@ -77,9 +83,12 @@ //! //! [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin //! [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user -//! [`connect`]: fn.connect.html -//! [`Pro::connect`]: struct.Pro.html#fn.connect.html -//! [`Storage::connect`]: struct.Storage.html#fn.connect.html +//! [`take`]: fn.take.html +//! [`connect`]: struct.Manager.html#method.connect +//! [`connect_model`]: struct.Manager.html#method.connect_model +//! [`connect_pro`]: struct.Manager.html#method.connect_pro +//! [`connect_storage`]: struct.Manager.html#method.connect_storage +//! [`manager`]: trait.Device.html#method.manager //! [`device`]: struct.User.html#method.device //! [`get_hotp_code`]: trait.GenerateOtp.html#method.get_hotp_code //! [`get_totp_code`]: trait.GenerateOtp.html#method.get_totp_code @@ -89,6 +98,9 @@ #![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)] +#[macro_use(lazy_static)] +extern crate lazy_static; + mod auth; mod config; mod device; @@ -98,14 +110,16 @@ mod pws; mod util; use std::fmt; +use std::marker; +use std::sync; use nitrokey_sys; pub use crate::auth::{Admin, Authenticate, User}; pub use crate::config::Config; pub use crate::device::{ - connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, - StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, + Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, StorageStatus, + VolumeMode, VolumeStatus, }; pub use crate::error::{CommandError, CommunicationError, Error, LibraryError}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; @@ -117,6 +131,10 @@ pub const DEFAULT_ADMIN_PIN: &str = "12345678"; /// The default user PIN for all Nitrokey devices. pub const DEFAULT_USER_PIN: &str = "123456"; +lazy_static! { + static ref MANAGER: sync::Mutex = sync::Mutex::new(Manager::new()); +} + /// A version of the libnitrokey library. /// /// Use the [`get_library_version`](fn.get_library_version.html) function to query the library @@ -147,6 +165,263 @@ impl fmt::Display for Version { } } +/// A manager for connections to Nitrokey devices. +/// +/// Currently, libnitrokey only provides access to one Nitrokey device at the same time. This +/// manager struct makes sure that `nitrokey-rs` does not try to connect to two devices at the same +/// time. +/// +/// To obtain a reference to an instance of this manager, use the [`take`][] function. Use one of +/// the connect methods – [`connect`][], [`connect_model`][], [`connect_pro`][] or +/// [`connect_storage`][] – to retrieve a [`Device`][] instance. +/// +/// # Examples +/// +/// Connect to a single device: +/// +/// ```no_run +/// use nitrokey::Device; +/// # use nitrokey::Error; +/// +/// # fn try_main() -> Result<(), Error> { +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; +/// println!("{}", device.get_serial_number()?); +/// # Ok(()) +/// # } +/// ``` +/// +/// Connect to a Pro and a Storage device: +/// +/// ```no_run +/// use nitrokey::{Device, Model}; +/// # use nitrokey::Error; +/// +/// # fn try_main() -> Result<(), Error> { +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect_model(Model::Pro)?; +/// println!("Pro: {}", device.get_serial_number()?); +/// drop(device); +/// let device = manager.connect_model(Model::Storage)?; +/// println!("Storage: {}", device.get_serial_number()?); +/// # Ok(()) +/// # } +/// ``` +/// +/// [`connect`]: #method.connect +/// [`connect_model`]: #method.connect_model +/// [`connect_pro`]: #method.connect_pro +/// [`connect_storage`]: #method.connect_storage +/// [`manager`]: trait.Device.html#method.manager +/// [`take`]: fn.take.html +/// [`Device`]: trait.Device.html +#[derive(Debug)] +pub struct Manager { + marker: marker::PhantomData<()>, +} + +impl Manager { + fn new() -> Self { + Manager { + marker: marker::PhantomData, + } + } + + /// Connects to a Nitrokey device. + /// + /// This method can be used to connect to any connected device, both a Nitrokey Pro and a + /// Nitrokey Storage. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::DeviceWrapper; + /// + /// fn do_something(device: DeviceWrapper) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// let mut manager = nitrokey::take()?; + /// match manager.connect() { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect(&mut self) -> Result, Error> { + if unsafe { nitrokey_sys::NK_login_auto() } == 1 { + device::get_connected_device(self) + } else { + Err(CommunicationError::NotConnected.into()) + } + } + + /// Connects to a Nitrokey device of the given model. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::DeviceWrapper; + /// use nitrokey::Model; + /// + /// fn do_something(device: DeviceWrapper) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect_model(Model::Pro) { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey Pro: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect_model(&mut self, model: Model) -> Result, Error> { + if device::connect_enum(model) { + Ok(device::create_device_wrapper(self, model)) + } else { + Err(CommunicationError::NotConnected.into()) + } + } + + /// Connects to a Nitrokey Pro. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::Pro; + /// + /// fn use_pro(device: Pro) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect_pro() { + /// Ok(device) => use_pro(device), + /// Err(err) => println!("Could not connect to the Nitrokey Pro: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect_pro(&mut self) -> Result, Error> { + if device::connect_enum(device::Model::Pro) { + Ok(device::Pro::new(self)) + } else { + Err(CommunicationError::NotConnected.into()) + } + } + + /// Connects to a Nitrokey Storage. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::Storage; + /// + /// fn use_storage(device: Storage) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect_storage() { + /// Ok(device) => use_storage(device), + /// Err(err) => println!("Could not connect to the Nitrokey Storage: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect_storage(&mut self) -> Result, Error> { + if device::connect_enum(Model::Storage) { + Ok(Storage::new(self)) + } else { + Err(CommunicationError::NotConnected.into()) + } + } +} + +/// Take an instance of the connection manager, blocking until an instance is available. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, this method blocks. If you want a non-blocking version, use [`take`][]. +/// +/// # Errors +/// +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take`]: fn.take.html +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take_blocking() -> Result, Error> { + MANAGER.lock().map_err(Into::into) +} + +/// Try to take an instance of the connection manager. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, a [`ConcurrentAccessError`][] is returned. If you want a blocking version, use +/// [`take_blocking`][]. If you want to access the manager instance even if the cache is poisoned, +/// use [`force_take`][]. +/// +/// # Errors +/// +/// - [`ConcurrentAccessError`][] if the token for the `Manager` instance cannot be locked +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take_blocking`]: fn.take_blocking.html +/// [`force_take`]: fn.force_take.html +/// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take() -> Result, Error> { + MANAGER.try_lock().map_err(Into::into) +} + +/// Try to take an instance of the connection manager, ignoring a poisoned cache. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, a [`ConcurrentAccessError`][] is returned. If you want a blocking version, use +/// [`take_blocking`][]. +/// +/// If a thread has previously panicked while accessing the manager instance, the cache is +/// poisoned. The default implementation, [`take`][], returns a [`PoisonError`][] on subsequent +/// calls. This implementation ignores the poisoned cache and returns the manager instance. +/// +/// # Errors +/// +/// - [`ConcurrentAccessError`][] if the token for the `Manager` instance cannot be locked +/// +/// [`take`]: fn.take.html +/// [`take_blocking`]: fn.take_blocking.html +/// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError +/// [`Manager`]: struct.Manager.html +pub fn force_take() -> Result, Error> { + match take() { + Ok(guard) => Ok(guard), + Err(err) => match err { + Error::PoisonError(err) => Ok(err.into_inner()), + err => Err(err), + }, + } +} + /// Enables or disables debug output. Calling this method with `true` is equivalent to setting the /// log level to `Debug`; calling it with `false` is equivalent to the log level `Error` (see /// [`set_log_level`][]). diff --git a/nitrokey/src/otp.rs b/nitrokey/src/otp.rs index ee142c7..4667aff 100644 --- a/nitrokey/src/otp.rs +++ b/nitrokey/src/otp.rs @@ -35,7 +35,8 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -71,7 +72,8 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -104,7 +106,8 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { /// match admin.erase_hotp_slot(1) { @@ -134,7 +137,8 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { /// match admin.erase_totp_slot(1) { @@ -171,7 +175,8 @@ pub trait GenerateOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH); /// match time { /// Ok(time) => device.set_time(time.as_secs(), false)?, @@ -209,7 +214,8 @@ pub trait GenerateOtp { /// use nitrokey::{CommandError, Error, GenerateOtp}; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// match device.get_hotp_slot_name(1) { /// Ok(name) => println!("HOTP slot 1: {}", name), /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => eprintln!("HOTP slot 1 not programmed"), @@ -238,7 +244,8 @@ pub trait GenerateOtp { /// use nitrokey::{CommandError, Error, GenerateOtp}; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// match device.get_totp_slot_name(1) { /// Ok(name) => println!("TOTP slot 1: {}", name), /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => eprintln!("TOTP slot 1 not programmed"), @@ -270,7 +277,8 @@ pub trait GenerateOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let code = device.get_hotp_code(1)?; /// println!("Generated HOTP code on slot 1: {}", code); /// # Ok(()) @@ -305,7 +313,8 @@ pub trait GenerateOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH); /// match time { /// Ok(time) => { diff --git a/nitrokey/src/pws.rs b/nitrokey/src/pws.rs index 371de6e..3398deb 100644 --- a/nitrokey/src/pws.rs +++ b/nitrokey/src/pws.rs @@ -43,7 +43,8 @@ pub const SLOT_COUNT: u8 = 16; /// } /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::connect()?; +/// let mut manager = nitrokey::take()?; +/// let mut device = manager.connect()?; /// let pws = device.get_password_safe("123456")?; /// use_password_safe(&pws); /// drop(pws); @@ -57,8 +58,8 @@ pub const SLOT_COUNT: u8 = 16; /// [`lock`]: trait.Device.html#method.lock /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html #[derive(Debug)] -pub struct PasswordSafe<'a> { - _device: &'a dyn Device, +pub struct PasswordSafe<'a, 'b> { + _device: &'a dyn Device<'b>, } /// Provides access to a [`PasswordSafe`][]. @@ -67,7 +68,7 @@ pub struct PasswordSafe<'a> { /// retrieved from it. /// /// [`PasswordSafe`]: struct.PasswordSafe.html -pub trait GetPasswordSafe { +pub trait GetPasswordSafe<'a> { /// Enables and returns the password safe. /// /// The underlying device must always live at least as long as a password safe retrieved from @@ -98,7 +99,8 @@ pub trait GetPasswordSafe { /// fn use_password_safe(pws: &PasswordSafe) {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { /// use_password_safe(&pws); @@ -117,13 +119,13 @@ pub trait GetPasswordSafe { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error>; + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error>; } -fn get_password_safe<'a>( - device: &'a dyn Device, +fn get_password_safe<'a, 'b>( + device: &'a dyn Device<'b>, user_pin: &str, -) -> Result, Error> { +) -> Result, Error> { let user_pin_string = get_cstring(user_pin)?; get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) }) .map(|_| PasswordSafe { _device: device }) @@ -137,7 +139,7 @@ fn get_pws_result(s: String) -> Result { } } -impl<'a> PasswordSafe<'a> { +impl<'a, 'b> PasswordSafe<'a, 'b> { /// Returns the status of all password slots. /// /// The status indicates whether a slot is programmed or not. @@ -149,7 +151,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let pws = device.get_password_safe("123456")?; /// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| { /// let status = match *programmed { @@ -194,7 +197,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { /// let name = pws.get_slot_name(0)?; @@ -231,7 +235,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -264,7 +269,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -295,7 +301,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -341,7 +348,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; /// let mut pws = device.get_password_safe("123456")?; /// match pws.erase_slot(0) { /// Ok(()) => println!("Erased slot 0."), @@ -357,27 +365,27 @@ impl<'a> PasswordSafe<'a> { } } -impl<'a> Drop for PasswordSafe<'a> { +impl<'a, 'b> Drop for PasswordSafe<'a, 'b> { fn drop(&mut self) { // TODO: disable the password safe -- NK_lock_device has side effects on the Nitrokey // Storage, see https://github.com/Nitrokey/nitrokey-storage-firmware/issues/65 } } -impl GetPasswordSafe for Pro { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for Pro<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -impl GetPasswordSafe for Storage { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for Storage<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -impl GetPasswordSafe for DeviceWrapper { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for DeviceWrapper<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } diff --git a/nitrokey/tests/device.rs b/nitrokey/tests/device.rs index 5c52024..e367558 100644 --- a/nitrokey/tests/device.rs +++ b/nitrokey/tests/device.rs @@ -33,40 +33,39 @@ fn count_nitrokey_block_devices() -> usize { #[test_device] fn connect_no_device() { - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect()); - assert_cmu_err!( - CommunicationError::NotConnected, - nitrokey::connect_model(nitrokey::Model::Pro) - ); + let mut manager = unwrap_ok!(nitrokey::take()); + + assert_cmu_err!(CommunicationError::NotConnected, manager.connect()); assert_cmu_err!( CommunicationError::NotConnected, - nitrokey::connect_model(nitrokey::Model::Storage) + manager.connect_model(nitrokey::Model::Pro) ); - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Pro::connect()); assert_cmu_err!( CommunicationError::NotConnected, - nitrokey::Storage::connect() + manager.connect_model(nitrokey::Model::Storage) ); + assert_cmu_err!(CommunicationError::NotConnected, manager.connect_pro()); + assert_cmu_err!(CommunicationError::NotConnected, manager.connect_storage()); } #[test_device] fn connect_pro(device: Pro) { assert_eq!(device.get_model(), nitrokey::Model::Pro); - drop(device); - assert_any_ok!(nitrokey::connect()); - assert_any_ok!(nitrokey::connect_model(nitrokey::Model::Pro)); - assert_any_ok!(nitrokey::Pro::connect()); + let manager = device.into_manager(); + assert_any_ok!(manager.connect()); + assert_any_ok!(manager.connect_model(nitrokey::Model::Pro)); + assert_any_ok!(manager.connect_pro()); } #[test_device] fn connect_storage(device: Storage) { assert_eq!(device.get_model(), nitrokey::Model::Storage); - drop(device); - assert_any_ok!(nitrokey::connect()); - assert_any_ok!(nitrokey::connect_model(nitrokey::Model::Storage)); - assert_any_ok!(nitrokey::Storage::connect()); + let manager = device.into_manager(); + assert_any_ok!(manager.connect()); + assert_any_ok!(manager.connect_model(nitrokey::Model::Storage)); + assert_any_ok!(manager.connect_storage()); } fn assert_empty_serial_number() { @@ -97,7 +96,10 @@ fn get_firmware_version(device: Pro) { assert!(version.minor > 0); } -fn admin_retry(device: T, suffix: &str, count: u8) -> T { +fn admin_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where + T: Authenticate<'a> + Device<'a> + 'a, +{ let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), @@ -107,7 +109,10 @@ fn admin_retry(device: T, suffix: &str, count: u8) -> return device; } -fn user_retry(device: T, suffix: &str, count: u8) -> T { +fn user_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where + T: Authenticate<'a> + Device<'a> + 'a, +{ let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), @@ -216,10 +221,10 @@ fn change_admin_pin(device: DeviceWrapper) { device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); } -fn require_failed_user_login(device: D, password: &str, error: CommandError) -> D +fn require_failed_user_login<'a, D>(device: D, password: &str, error: CommandError) -> D where - D: Device + Authenticate, - nitrokey::User: std::fmt::Debug, + D: Device<'a> + Authenticate<'a> + 'a, + nitrokey::User<'a, D>: std::fmt::Debug, { let result = device.authenticate_user(password); assert!(result.is_err()); @@ -339,6 +344,7 @@ fn factory_reset(device: DeviceWrapper) { assert_utf8_err_or_ne("testpw", pws.get_slot_password(0)); drop(pws); + assert_ok!(3, device.get_user_retry_count()); assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); } diff --git a/nitrokey/tests/lib.rs b/nitrokey/tests/lib.rs index 8ab75f6..25aae0f 100644 --- a/nitrokey/tests/lib.rs +++ b/nitrokey/tests/lib.rs @@ -10,3 +10,19 @@ fn get_library_version() { assert!(version.git.is_empty() || version.git.starts_with("v")); assert!(version.major > 0); } + +#[test] +fn take_manager() { + assert!(nitrokey::take().is_ok()); + + let result = nitrokey::take(); + assert!(result.is_ok()); + let result2 = nitrokey::take(); + match result2 { + Ok(_) => panic!("Expected error, got Ok(_)!"), + Err(nitrokey::Error::ConcurrentAccessError) => {} + Err(err) => panic!("Expected ConcurrentAccessError, got {}", err), + } + drop(result); + assert!(nitrokey::take().is_ok()); +} diff --git a/nitrokey/tests/otp.rs b/nitrokey/tests/otp.rs index c0bbecf..aafda59 100644 --- a/nitrokey/tests/otp.rs +++ b/nitrokey/tests/otp.rs @@ -36,9 +36,9 @@ enum TotpTimestampSize { U64, } -fn make_admin_test_device(device: T) -> Admin +fn make_admin_test_device<'a, T>(device: T) -> Admin<'a, T> where - T: Device, + T: Device<'a>, (T, nitrokey::Error): Debug, { unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) diff --git a/nitrokey/tests/pws.rs b/nitrokey/tests/pws.rs index b0e5abe..7169695 100644 --- a/nitrokey/tests/pws.rs +++ b/nitrokey/tests/pws.rs @@ -32,9 +32,9 @@ fn get_slot_name_direct(slot: u8) -> Result { } } -fn get_pws(device: &mut T) -> PasswordSafe +fn get_pws<'a, T>(device: &mut T) -> PasswordSafe<'_, 'a> where - T: Device, + T: Device<'a>, { unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)) } -- cgit v1.2.3