Compare commits
63 Commits
release/1.
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7249775c17 | ||
|
|
980cff0a2f | ||
|
|
8f44f5d3c6 | ||
|
|
37a84ed218 | ||
|
|
6c72147248 | ||
|
|
3d37003d8c | ||
|
|
becf8fabc5 | ||
|
|
17492287f3 | ||
|
|
c800dee95e | ||
|
|
2c390dc763 | ||
|
|
5aee75fd4b | ||
|
|
69e8c0f334 | ||
|
|
317cc2036d | ||
|
|
312d15c3fa | ||
|
|
d356f1b1e0 | ||
|
|
9cb6591491 | ||
|
|
ebec8ee156 | ||
|
|
f810fb83d3 | ||
|
|
12079974fd | ||
|
|
ca79f5421f | ||
|
|
c438367d67 | ||
|
|
4c6511231c | ||
|
|
ba750d153d | ||
|
|
72647f01d0 | ||
|
|
c6e32a36fb | ||
|
|
c3a0eb1439 | ||
|
|
030a9c576d | ||
|
|
8a5e0eb783 | ||
|
|
bce53546e7 | ||
|
|
b2b19e1ad4 | ||
|
|
58fa773bb7 | ||
|
|
ea88c3da66 | ||
|
|
b905b4802f | ||
|
|
40f2c6c49e | ||
|
|
61a7f9602b | ||
|
|
5465fda927 | ||
|
|
f7c46a6c55 | ||
|
|
4550458806 | ||
|
|
93a2bac94b | ||
|
|
24adc09406 | ||
|
|
bf9fc628b3 | ||
|
|
d76534dd3a | ||
|
|
d9c5dcf206 | ||
|
|
26448d58ee | ||
|
|
9cfeb817bd | ||
|
|
b6534dceba | ||
|
|
b47bd693ba | ||
|
|
1a7d3a698f | ||
|
|
f7b03f7fb5 | ||
|
|
44971541fe | ||
|
|
53ed5e0a42 | ||
|
|
63eccda0a1 | ||
|
|
f430c90715 | ||
|
|
1f18ec1d63 | ||
|
|
ec7423f493 | ||
|
|
e50dda28d1 | ||
|
|
8ad176921f | ||
|
|
d71b116be8 | ||
|
|
d9d3f35ac3 | ||
|
|
7cc8d8930e | ||
|
|
6446a5bd89 | ||
|
|
8b0cb8be65 | ||
|
|
c73bb0548e |
200
ChangeLog
200
ChangeLog
@@ -1,3 +1,203 @@
|
||||
2018-11-25 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* VERSION: 1.2.12.
|
||||
|
||||
2018-11-21 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/usb-ccid.c (ccid_thread): Fix a race condition sending
|
||||
result APDU by ack button, time out, sending time extension block
|
||||
again while tx_busy=1.
|
||||
|
||||
2018-11-17 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/main.c (device_initialize_once): Depends on MHZ to
|
||||
distinguish GD32F103.
|
||||
* src/openpgp-do.c (do_openpgpcard_aid): Ditto.
|
||||
|
||||
2018-11-12 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* VERSION: 1.2.11.
|
||||
|
||||
* chopstx: Update to 1.12.
|
||||
* src/configure (ackbtn_support): Always yes.
|
||||
* src/usb-ccid.c: Fix comma separator.
|
||||
|
||||
2018-11-09 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tool/kdf_calc.py (kdf_calc): Use libgcrypt.so.20.
|
||||
|
||||
2018-11-09 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/openpgp.c (cmd_pso, cmd_internal_authenticate): Use
|
||||
ccid_comm.
|
||||
|
||||
* src/usb-ccid.c (struct ccid): New field tx_busy.
|
||||
(ccid_error, ccid_power_on, ccid_send_status, ccid_power_off)
|
||||
(ccid_send_data_block_internal, ccid_send_data_block_0x9000)
|
||||
(ccid_send_data_block_gr, ccid_send_params): Set c->tx_busy.
|
||||
(ccid_state_p): Remove.
|
||||
(ccid_get_ccid_state): New.
|
||||
(ccid_thread): Only handle EV_TX_FINISHED event when c->tx_busy.
|
||||
|
||||
* src/usb_ctrl.c (usb_setup, usb_ctrl_write_finish): Use
|
||||
ccid_get_ccid_state.
|
||||
* src/main.c (display_status_code): Likewise.
|
||||
|
||||
2018-11-09 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/usb-ccid.c (ccid_handle_data): Set c->state for pinpad input.
|
||||
(ccid_send_data_block_internal): Fix the case of len == 0.
|
||||
|
||||
* src/main.c (display_status_code): There is
|
||||
no case where ccid_state == CCID_STATE_RECEIVE.
|
||||
* src/gnuk.h (CCID_STATE_RECEIVE): Remove.
|
||||
(CCID_STATE_SEND): Remove.
|
||||
|
||||
2018-10-12 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/usb-ccid.c (ccid_thread): Notify host about ack button.
|
||||
|
||||
2018-10-02 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/stack-def.h (SIZE_0): Increase.
|
||||
|
||||
* chopstx: Update to 1.11.
|
||||
|
||||
2018-10-01 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/gnuk.h (EV_EXEC_ACK_REQUIRED): New.
|
||||
(EV_EXEC_FINISHED_ACK): Remove.
|
||||
(CCID_STATE_CONFIRM_ACK): Remove.
|
||||
(CCID_STATE_ACK_REQUIRED_0, CCID_STATE_ACK_REQUIRED_1): New.
|
||||
* src/openpgp.c (cmd_pso): Send EV_EXEC_ACK_REQUIRED, if needed.
|
||||
(cmd_internal_authenticate): Likewise.
|
||||
(process_command_apdu): No return value.
|
||||
(openpgp_card_thread): Always send EV_EXEC_FINISHED.
|
||||
* src/usb-ccid.c (ccid_send_data_block_time_extension): Follow the
|
||||
change of state.
|
||||
(ccid_handle_data, ccid_handle_timeout): Likewise.
|
||||
(ccid_thread): Handle EV_EXEC_ACK_REQUIRED.
|
||||
Change for LED blink.
|
||||
* src/main.c (main): LED blink during waiting ACK.
|
||||
|
||||
2018-09-27 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/gnuk.h (NR_DO_UIF_SIG, NR_DO_UIF_DEC, NR_DO_UIF_AUT): New.
|
||||
* src/openpgp-do.c (rw_uif) [ACKBTN_SUPPORT]: New.
|
||||
(GPG_DO_UIF_SIG, GPG_DO_UIF_DEC, GPG_DO_UIF_AUT): New.
|
||||
(feature_mngmnt) [ACKBTN_SUPPORT]: New.
|
||||
(cmp_app_data, cmp_discretionary): Add ACKBTN_SUPPORT.
|
||||
(gpg_do_table): Add for GPG_DO_UIF_SIG, GPG_DO_UIF_DEC,
|
||||
GPG_DO_UIF_AUT, and GPG_DO_FEATURE_MNGMNT.
|
||||
(gpg_do_get_uif) [ACKBTN_SUPPORT]: New.
|
||||
(gpg_data_scan): Handle uif_flags.
|
||||
* src/openpgp.c (process_command_apdu) [ACKBTN_SUPPORT]: Add user
|
||||
interaction handling.
|
||||
|
||||
2018-09-27 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/gnuk.h (LED_WAIT_FOR_BUTTON): New.
|
||||
* src/main.c (main): Blink rapidly when asking ACK.
|
||||
* src/usb-ccid.c (ccid_thread): Use LED_WAIT_FOR_BUTTON.
|
||||
|
||||
2018-09-27 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/config.h.in: Add @ACKBTN_DEFINE@.
|
||||
* src/configure: Add ACKBTN_SUPPORT.
|
||||
* src/gnuk.h (EV_EXEC_FINISHED_ACK): New.
|
||||
(CCID_STATE_CONFIRM_ACK): New.
|
||||
* src/openpgp.c (process_command_apdu): Change for cmd_pso, and
|
||||
cmd_internal_authenticate.
|
||||
* src/usb-ccid.c (ccid_send_data_block_time_extension): Report
|
||||
time extension differently when waiting ack button.
|
||||
(ccid_handle_data): Support case of CCID_STATE_CONFIRM_ACK.
|
||||
(ccid_handle_timeout): Likewise.
|
||||
(ack_intr) [ACKBTN_SUPPORT]: New.
|
||||
(ccid_thread) [ACKBTN_SUPPORT]: Add ack button handling.
|
||||
|
||||
2018-09-26 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* chopstx: Update.
|
||||
* src/usb-ccid.c (usb_event_handle): Fix for chopstx_intr_done.
|
||||
* src/pin-cir.c (tim_main, ext_main): Likewise.
|
||||
|
||||
* src/configure (FST_01SZ): Set MHZ=96.
|
||||
|
||||
2018-07-04 Szczepan Zalega <szczepan@nitrokey.com>
|
||||
|
||||
* tool/upgrade_by_passwd.py: Catch exception, when no KDF data is
|
||||
found.
|
||||
Output 'second' for 1 second.
|
||||
|
||||
2018-05-10 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* VERSION: 1.2.10.
|
||||
|
||||
* src/Makefile (build/gnuk.elf): New target.
|
||||
(build/gnuk-vidpid.elf): Remove.
|
||||
|
||||
* chopstx: Update to 1.9.
|
||||
|
||||
2018-04-26 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/usb_ctrl.c (usb_device_reset): Don't stop the endpoints.
|
||||
|
||||
* src/configure (MHZ, def_mhz): New.
|
||||
|
||||
2018-04-05 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* VERSION: 1.2.9.
|
||||
|
||||
* tests: Add test cases for admin-less mode.
|
||||
|
||||
* src/openpgp.c (cmd_change_password): Care admin-less mode.
|
||||
|
||||
2018-04-04 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tests: Add more tests, key generation and KDF support.
|
||||
|
||||
* src/openpgp.c (cmd_reset_user_password): Check length of
|
||||
new passphrase.
|
||||
|
||||
* src/openpgp-do.c (proc_resetting_code): Support removal.
|
||||
(gpg_do_kdf_check): Fix for the case of resetting PW3.
|
||||
|
||||
* tests/test_004_reset_pw3.py: New.
|
||||
|
||||
2018-04-03 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/openpgp-do.c (rw_kdf): Clear all auth state.
|
||||
|
||||
* tool/upgrade_by_passwd.py (main): Fix for byte compare.
|
||||
* tool/gnuk_remove_keys_libusb.py (main): Likewise.
|
||||
|
||||
2018-04-02 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* tool/gnuk_token.py (parse_kdf_data): New.
|
||||
* tool/kdf_calc.py: New.
|
||||
|
||||
* tool/gnuk_remove_keys_libusb.py (main): Support KDF auth.
|
||||
* tool/upgrade_by_passwd.py (main): Likewise.
|
||||
|
||||
2018-03-30 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/openpgp-do.c (rw_kdf): Support single-salt KDF.
|
||||
(gpg_do_get_initial_pw_setting): Likewise.
|
||||
(gpg_do_kdf_check): Likewise.
|
||||
|
||||
2018-03-22 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/openpgp-do.c (rw_kdf): Do format validation earlier.
|
||||
|
||||
2018-03-13 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/flash.c [FLASH_UPGRADE_SUPPORT] (flash_terminate): Erase
|
||||
the page for upgrade public keys.
|
||||
|
||||
2018-02-12 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* src/openpgp-do.c (rw_kdf): Return 0 when NULL.
|
||||
|
||||
2018-01-23 NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
* VERSION: 1.2.8.
|
||||
|
||||
64
NEWS
64
NEWS
@@ -1,5 +1,62 @@
|
||||
Gnuk NEWS - User visible changes
|
||||
|
||||
|
||||
* Major changes in Gnuk 1.2.12
|
||||
|
||||
Released 2018-11-25, by NIIBE Yutaka
|
||||
|
||||
** FST-01SZ fixes
|
||||
Fixes for Ack button support and serial number.
|
||||
|
||||
|
||||
* Major changes in Gnuk 1.2.11
|
||||
|
||||
Released 2018-11-12, by NIIBE Yutaka
|
||||
|
||||
** Experimental ACK button support with FST-01SZ
|
||||
While OpenPGP card specification verison 3 has description for the
|
||||
"user interaction" button and data objects, there were no
|
||||
implementation. To evaluate the feature, experimental support is
|
||||
added.
|
||||
|
||||
** Upgrade of Chopstx
|
||||
We use Chopstx 1.12, which comes with ACK button driver and supports
|
||||
FST-01SZ.
|
||||
|
||||
|
||||
* Major changes in Gnuk 1.2.10
|
||||
|
||||
Released 2018-05-10, by NIIBE Yutaka
|
||||
|
||||
** No inclusion of VID:PID in gnuk-no-vidpid.elf
|
||||
Now, we have new file named gnuk-no-vidpid.elf with no VID:PID. The
|
||||
file gnuk.elf has the VID:PID, like version 1.2.7 or older.
|
||||
|
||||
** Upgrade of Chopstx
|
||||
We use Chopstx 1.9, which supports GD32F103.
|
||||
|
||||
|
||||
* Major changes in Gnuk 1.2.9
|
||||
|
||||
Released 2018-04-05, by NIIBE Yutaka
|
||||
|
||||
** A test suite fix: Clear PW3
|
||||
Until 1.2.8, after running the test suite under "tests", PW3 keystring
|
||||
remained, which affects use of admin-less mode. New test case is
|
||||
added to clear PW3.
|
||||
|
||||
** tool/upgrade_by_passwd.py supports KDF-DO auth
|
||||
With KDF-DO, firmware upgrade didn't work. Now, it's supported.
|
||||
|
||||
** Add "single-salt" support for KDF-DO
|
||||
With KDF-DO, "admin-less" mode didn't work well. With new feature of
|
||||
"single-salt" support, we can use "admin-less" mode with KDF-DO.
|
||||
|
||||
** factory-reset deletes all upgrade public keys
|
||||
By card-edit/factory-reset by GnuPG, it deletes all upgrade public
|
||||
keys, now.
|
||||
|
||||
|
||||
* Major changes in Gnuk 1.2.8
|
||||
|
||||
Released 2018-01-23, by NIIBE Yutaka
|
||||
@@ -830,6 +887,7 @@ Gnuk Token could run with GPG4WIN on MS Windows. GPG4WIN runs with
|
||||
|
||||
** This is initial release. Only it supports digital signing.
|
||||
|
||||
Local Variables:
|
||||
mode: outline
|
||||
End:
|
||||
|
||||
# Local Variables:
|
||||
# mode: outline
|
||||
# End:
|
||||
|
||||
61
README
61
README
@@ -1,14 +1,14 @@
|
||||
Gnuk - An Implementation of USB Cryptographic Token for GnuPG
|
||||
|
||||
Version 1.2.8
|
||||
2018-01-23
|
||||
Version 1.2.12
|
||||
2018-11-25
|
||||
Niibe Yutaka
|
||||
Free Software Initiative of Japan
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
This is the release of Gnuk, version 1.2.8, which has major
|
||||
This is the release of Gnuk, version 1.2.12, which has major
|
||||
incompatible changes to Gnuk 1.0.x. Specifically, it now supports
|
||||
overriding key import, but importing keys (or generating keys) results
|
||||
password reset. Also, you need to import private keys before changing
|
||||
@@ -24,10 +24,12 @@ It also supports RSA-4096, but users should know that it takes more
|
||||
than 8 seconds to sign/decrypt. Key generation of RSA-4096 just fails,
|
||||
because the device doesn't have enough memory.
|
||||
|
||||
It supports new KDF-DO feature. To use the feature, you need to use
|
||||
newer GnuPG (forthcoming 2.2.5 or later). And you need to manually
|
||||
prepare the KDF-DO on your token. Please note that this is
|
||||
experimental. Better way to prepare KDF-DO will be expected.
|
||||
It supports new KDF-DO feature. Please note that this is
|
||||
experimental. To use the feature, you need to use newer GnuPG (2.2.6
|
||||
or later). You need to prepare the KDF-DO on your token by the
|
||||
card-edit/kdf-setup command.
|
||||
|
||||
With FST-01SZ, experimental ack button support is available for test.
|
||||
|
||||
|
||||
What's Gnuk?
|
||||
@@ -35,7 +37,7 @@ What's Gnuk?
|
||||
|
||||
Gnuk is an implementation of USB cryptographic token for GNU Privacy
|
||||
Guard. Gnuk supports OpenPGP card protocol version 3, and it runs on
|
||||
STM32F103 processor.
|
||||
STM32F103 processor (and its compatible).
|
||||
|
||||
I wish that Gnuk will be a developer's soother who uses GnuPG. I have
|
||||
been nervous of storing secret key(s) on usual secondary storage.
|
||||
@@ -83,8 +85,7 @@ A3: Orthodox choice is Olimex STM32-H103.
|
||||
choice for experiment.
|
||||
|
||||
Q4: What's version of GnuPG are you using?
|
||||
A4: In Debian GNU/Linux system, I use GnuPG modern 2.1.18 in
|
||||
unstable.
|
||||
A4: In Debian GNU/Linux system, I use GnuPG modern 2.1.18.
|
||||
|
||||
Q5: What's version of pcscd and libccid are you using?
|
||||
A5: I don't use them, pcscd and libccid are optional, you can use Gnuk
|
||||
@@ -178,15 +179,6 @@ Original features of Gnuk, tested manually lightly:
|
||||
* Card holder certificate (write by UPDATE BINARY)
|
||||
* Upgrading with "EXTERNAL AUTHENTICATE" by reGNUal
|
||||
|
||||
It is known not-working well:
|
||||
|
||||
* It is known that the specific combination of libccid 1.4.1
|
||||
(or newer) with libusb 1.0.8 (or older) had a minor problem.
|
||||
It is rare but it is possible for USB communication to be
|
||||
failed, because of a bug in libusb implementation. Use
|
||||
libusbx 1.0.9 or newer, or don't use PC/SC, but use internal
|
||||
CCID driver of GnuPG.
|
||||
|
||||
|
||||
Targets
|
||||
=======
|
||||
@@ -219,7 +211,8 @@ script prepending 'bash' before './configure'.
|
||||
|
||||
Some tools are written in Python. If your Python is not installed as
|
||||
/usr/bin/python, please prepend 'python' for your command invocation.
|
||||
Python 2.7 and PyUSB 0.4.3 is assumed.
|
||||
Python 2.7 and PyUSB 0.4.3 is assumed. I also use Python 3.5 and
|
||||
PyUSB 1.0.0.
|
||||
|
||||
|
||||
Source code
|
||||
@@ -256,7 +249,7 @@ External source code
|
||||
|
||||
Gnuk is distributed with external source code.
|
||||
|
||||
* chopstx/ -- Chopstx 1.8
|
||||
* chopstx/ -- Chopstx 1.12
|
||||
|
||||
We use Chopstx as the kernel for Gnuk.
|
||||
|
||||
@@ -378,10 +371,10 @@ You need GNU toolchain and newlib for 'arm-none-eabi' target.
|
||||
On Debian we can install the packages of gcc-arm-none-eabi,
|
||||
gdb-arm-none-eabi and its friends. I'm using:
|
||||
|
||||
binutils-arm-none-eabi 2.28-4+9+b3
|
||||
gcc-arm-none-eabi 15:6.3.1+svn253039-1
|
||||
binutils-arm-none-eabi 2.31.1-2+10
|
||||
gcc-arm-none-eabi 15:7-2018-q2-4
|
||||
gdb-arm-none-eabi 7.12-6+9+b2
|
||||
libnewlib-arm-none-eabi 2.4.0.20160527-3
|
||||
libnewlib-arm-none-eabi 3.0.0.20180802-2
|
||||
|
||||
Or else, see https://launchpad.net/gcc-arm-embedded for preparation of
|
||||
GNU Toolchain for 'arm-none-eabi' target.
|
||||
@@ -405,9 +398,11 @@ Then, type:
|
||||
|
||||
Then, we will have "gnuk.elf" under src/build directory.
|
||||
|
||||
Next, we can get the final image by running following command.
|
||||
|
||||
$ make build/gnuk-vidpid.elf
|
||||
If you are not the authorized vendor, please never distribute this
|
||||
file of "gnuk.elf", which includes VID:PID in the image. If you would
|
||||
like to distribute the image (for example, to check if it's
|
||||
reproducible or not), the file "gnuk-no-vidpid.elf" is the one with no
|
||||
VID:PID.
|
||||
|
||||
|
||||
How to install
|
||||
@@ -417,11 +412,11 @@ Olimex STM32-H103 board
|
||||
-----------------------
|
||||
|
||||
If you are using Olimex JTAG-Tiny, type following to invoke OpenOCD
|
||||
and write "gnuk-vidpid.elf" to Flash ROM:
|
||||
and write "gnuk.elf" to Flash ROM:
|
||||
|
||||
$ openocd -f interface/ftdi/olimex-jtag-tiny.cfg \
|
||||
-f board/olimex_stm32_h103.cfg \
|
||||
-c "program build/gnuk-vidpid.elf verify reset exit"
|
||||
-c "program build/gnuk.elf verify reset exit"
|
||||
|
||||
Command invocation is assumed in src/ directory.
|
||||
|
||||
@@ -434,7 +429,7 @@ If you are using Flying Stone Tiny 01, you need a SWD writer.
|
||||
OpenOCD 0.9.0 now supports ST-Link/V2. We can use it like:
|
||||
|
||||
$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \
|
||||
-c "program build/gnuk-vidpid.elf verify reset exit"
|
||||
-c "program build/gnuk.elf verify reset exit"
|
||||
|
||||
|
||||
|
||||
@@ -445,7 +440,7 @@ Reset the board with "USER" switch pushed. Type following to write
|
||||
to flash:
|
||||
|
||||
# cd ../tool
|
||||
# ./dfuse.py ../src/build/gnuk-vidpid.hex
|
||||
# ./dfuse.py ../src/build/gnuk.hex
|
||||
|
||||
Then, reset the board.
|
||||
|
||||
@@ -535,8 +530,8 @@ Type following command to see Gnuk runs:
|
||||
$ gpg --card-status
|
||||
|
||||
|
||||
Besides, there is a functionality test under test/ directory. See
|
||||
test/README.
|
||||
Besides, there is a functionality test under tests/ directory. See
|
||||
tests/README.
|
||||
|
||||
|
||||
Personalize the Token, import keys, and change the password
|
||||
|
||||
1
THANKS
1
THANKS
@@ -17,6 +17,7 @@ Bertrand Jacquin bertrand@jacquin.bzh
|
||||
Clint Adams clint@softwarefreedom.org
|
||||
Daniel Kahn Gillmor dkg@fifthhorseman.net
|
||||
Elliott Mitchell
|
||||
Fabio Utzig utzig@apache.org
|
||||
Hironobu SUZUKI hironobu@h2np.net
|
||||
Jan Suhr jan@suhr.info
|
||||
Jeremy Drake jeremydrake+gnuk@eacceleration.com
|
||||
|
||||
2
chopstx
2
chopstx
Submodule chopstx updated: aa63ac79bc...39683dbc5f
@@ -58,6 +58,8 @@ Type: ::
|
||||
|
||||
Then, we will have "gnuk.elf" under src/build directory.
|
||||
|
||||
Next, we can get the final image by running following command. ::
|
||||
|
||||
$ make build/gnuk-vidpid.elf
|
||||
If you are not the authorized vendor, please never distribute this
|
||||
file of "gnuk.elf", which includes VID:PID in the image. If you would
|
||||
like to distribute the image (for example, to check if it's
|
||||
reproducible or not), the file "gnuk-no-vidpid.elf" is the one with no
|
||||
VID:PID.
|
||||
|
||||
@@ -16,7 +16,7 @@ In addition to settings of Gnuk, I create a file
|
||||
|
||||
# For updating firmware, permission settings are needed.
|
||||
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \
|
||||
SUBSYSTEMS=="usb", ATTR{idVendor}=="234b", ATTR{idProduct}=="0000", \
|
||||
ENV{ID_USB_INTERFACES}=="*:ff0000:*", GROUP="pcscd"
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ Invoking firmware update
|
||||
|
||||
We specify reGNUal binary and Gnuk binary.
|
||||
|
||||
$ ../tool/gnuk_upgrade.py ../regnual/regnual.bin gnuk-vidpid.bin
|
||||
$ ../tool/gnuk_upgrade.py ../regnual/regnual.bin gnuk.bin
|
||||
|
||||
|
||||
Two or more tokens
|
||||
|
||||
@@ -54,7 +54,7 @@ setting). It should have lines something like: ::
|
||||
# Gnuk Token by FSIJ
|
||||
|
||||
SUBSYSTEMS=="usb", ACTION=="add", \
|
||||
ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \
|
||||
ATTR{idVendor}=="234b", ATTR{idProduct}=="0000", \
|
||||
ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"
|
||||
|
||||
I have those lines in /etc/udev/rules.d/69-gnuk.rules.
|
||||
@@ -78,14 +78,15 @@ is FST-01.
|
||||
|
||||
Then you get build/gnuk.elf.
|
||||
|
||||
Next, we can get the final image by running following command.
|
||||
|
||||
$ make build/gnuk-vidpid.elf
|
||||
|
||||
If you are not the authorized vendor, please never distribute this
|
||||
file of "gnuk.elf", which includes VID:PID in the image. If you would
|
||||
like to distribute the image (for example, to check if it's
|
||||
reproducible or not), the file "gnuk-no-vidpid.elf" is the one with no
|
||||
VID:PID.
|
||||
|
||||
Invoking configure with FSIJ's USB ID (234b:0000) and generating
|
||||
gnuk-vidpid.elf means that you are using FSIJ's USB ID (for reGNUal in
|
||||
this case). Please note that FSIJ only allows use of its USB ID for
|
||||
gnuk.elf means that you are using FSIJ's USB ID (for reGNUal in this
|
||||
case). Please note that FSIJ only allows use of its USB ID for
|
||||
specific situations. Please read README of Gnuk about that.
|
||||
|
||||
|
||||
@@ -121,7 +122,7 @@ your environment for Gnuk Token.
|
||||
How to run the script: ::
|
||||
|
||||
$ cd tool
|
||||
$ ./upgrade_by_passwd.py ../regnual/regnual.bin ../src/build/gnuk-vidpid.bin
|
||||
$ ./upgrade_by_passwd.py ../regnual/regnual.bin ../src/build/gnuk.bin
|
||||
|
||||
Then, the script on your host PC invoke the steps described above, and
|
||||
you will get new version of Gnuk installed.
|
||||
|
||||
@@ -30,9 +30,9 @@ command.
|
||||
|
||||
Or, you can use ``gpgconf`` command. Type::
|
||||
|
||||
$ gpgconf --reload scdameon
|
||||
$ gpgconf --reload scdaemon
|
||||
|
||||
will do the samething.
|
||||
will do the same thing.
|
||||
|
||||
|
||||
Let GPG-AGENT/SCDAEMON learn
|
||||
|
||||
@@ -37,7 +37,7 @@ When we only install "gnupg2" package for 2.0 (with no "gnupg" package),
|
||||
there will be no udev rules (there is a bug report #543217 for this issue).
|
||||
In this case, we need something like this in /etc/udev/rules.d/60-gnuk.rules::
|
||||
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \
|
||||
SUBSYSTEMS=="usb", ATTR{idVendor}=="234b", ATTR{idProduct}=="0000", \
|
||||
ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"
|
||||
|
||||
Usually, udev daemon automatically handles for the changes of configuration
|
||||
|
||||
10
src/Makefile
10
src/Makefile
@@ -77,11 +77,11 @@ distclean: clean
|
||||
usb-strings.c.inc usb-vid-pid-ver.c.inc
|
||||
|
||||
ifeq ($(EMULATION),)
|
||||
build/gnuk-vidpid.elf: build/gnuk.elf binary-edit.sh put-vid-pid-ver.sh
|
||||
cp -p build/gnuk.elf build/gnuk-vidpid.elf
|
||||
env FILE="build/gnuk-vidpid.elf" bash put-vid-pid-ver.sh
|
||||
$(OBJCOPY) -O ihex build/gnuk-vidpid.elf build/gnuk-vidpid.hex
|
||||
$(OBJCOPY) -O binary build/gnuk-vidpid.elf build/gnuk-vidpid.bin
|
||||
build/gnuk.elf: build/gnuk-no-vidpid.elf binary-edit.sh put-vid-pid-ver.sh
|
||||
cp -p build/gnuk-no-vidpid.elf build/gnuk.elf
|
||||
env FILE="build/gnuk.elf" bash put-vid-pid-ver.sh
|
||||
$(OBJCOPY) -O ihex build/gnuk.elf build/gnuk.hex
|
||||
$(OBJCOPY) -O binary build/gnuk.elf build/gnuk.bin
|
||||
else
|
||||
# By specifying DESTDIR on invocation of "make", you can install
|
||||
# program to different ROOT.
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
@PINPAD_MORE_DEFINE@
|
||||
@CERTDO_DEFINE@
|
||||
@HID_CARD_CHANGE_DEFINE@
|
||||
@SERIALNO_STR_LEN_DEFINE@
|
||||
@LIFE_CYCLE_MANAGEMENT_DEFINE@
|
||||
@ACKBTN_DEFINE@
|
||||
@SERIALNO_STR_LEN_DEFINE@
|
||||
|
||||
27
src/configure
vendored
27
src/configure
vendored
@@ -44,6 +44,7 @@ pinpad=no
|
||||
certdo=no
|
||||
hid_card_change=no
|
||||
factory_reset=no
|
||||
ackbtn_support=yes
|
||||
flash_override=""
|
||||
# For emulation
|
||||
prefix=/usr/local
|
||||
@@ -135,6 +136,7 @@ Configuration:
|
||||
supported targets are:
|
||||
FST_01
|
||||
FST_01G
|
||||
FST_01SZ
|
||||
OLIMEX_STM32_H103
|
||||
MAPLE_MINI
|
||||
ST_DONGLE
|
||||
@@ -167,6 +169,8 @@ BOARD_HEADER_FILE=board-$(echo $target | tr '_[:upper:]' '-[:lower:]').h
|
||||
echo "Header file is: $BOARD_HEADER_FILE"
|
||||
ln -sf "../chopstx/board/$BOARD_HEADER_FILE" board.h
|
||||
|
||||
# Frequency
|
||||
MHZ=72
|
||||
# Flash page size in byte
|
||||
FLASH_PAGE_SIZE=1024
|
||||
# Flash memory size in KiB
|
||||
@@ -196,6 +200,12 @@ STBEE)
|
||||
if test "$with_dfu" = "default"; then
|
||||
with_dfu=yes;
|
||||
fi ;;
|
||||
BLUE_PILL_G)
|
||||
MHZ=96
|
||||
;;
|
||||
FST_01SZ)
|
||||
MHZ=96
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
@@ -208,6 +218,7 @@ if test "$target" = "GNU_LINUX"; then
|
||||
mcu="none"
|
||||
def_emulation="-DGNU_LINUX_EMULATION"
|
||||
def_memory_size="-DMEMORY_SIZE=1024"
|
||||
def_mhz=""
|
||||
enable_hexoutput=""
|
||||
libs="-lpthread"
|
||||
else
|
||||
@@ -218,6 +229,7 @@ else
|
||||
mcu="cortex-m3"
|
||||
def_emulation=""
|
||||
def_memory_size="-DMEMORY_SIZE=$MEMORY_SIZE"
|
||||
def_mhz="-DMHZ=$MHZ"
|
||||
enable_hexoutput=yes
|
||||
libs=""
|
||||
fi
|
||||
@@ -306,6 +318,15 @@ else
|
||||
echo "Life cycle management is NOT supported"
|
||||
fi
|
||||
|
||||
# Acknowledge button support
|
||||
if test "$ackbtn_support" = "yes"; then
|
||||
ACKBTN_DEFINE="#define ACKBTN_SUPPORT 1"
|
||||
echo "Acknowledge button is supported"
|
||||
else
|
||||
ACKBTN_DEFINE="#undef ACKBTN_SUPPORT"
|
||||
echo "Acknowledge button is not supported"
|
||||
fi
|
||||
|
||||
### !!! Replace following string of "FSIJ" to yours !!! ####
|
||||
SERIALNO="FSIJ-$(sed -e 's%^[^/]*/%%' <../VERSION)-"
|
||||
|
||||
@@ -442,13 +463,16 @@ fi
|
||||
echo "EMULATION=$emulation";
|
||||
echo "CROSS=$cross";
|
||||
echo "MCU=$mcu";
|
||||
echo "DEFS=$use_sys3 $flash_override $def_emulation $def_memory_size";
|
||||
echo "DEFS=$use_sys3 $flash_override $def_emulation $def_memory_size $def_mhz";
|
||||
echo "LDSCRIPT=$ldscript";
|
||||
echo "LIBS=$libs";
|
||||
echo "$DEBUG_MAKE_OPTION";
|
||||
echo "$PINPAD_MAKE_OPTION";
|
||||
echo "ENABLE_FRAUCHEKY=$enable_fraucheky";
|
||||
echo "ENABLE_OUTPUT_HEX=$enable_hexoutput"
|
||||
if test "$ackbtn_support" = "yes"; then
|
||||
echo "USE_ACKBTN=yes"
|
||||
fi
|
||||
if test "$emulation" = "yes"; then
|
||||
echo "prefix=$prefix"
|
||||
echo "exec_prefix=$exec_prefix"
|
||||
@@ -476,6 +500,7 @@ sed -e "s/@DEBUG_DEFINE@/$DEBUG_DEFINE/" \
|
||||
-e "s/@CERTDO_DEFINE@/$CERTDO_DEFINE/" \
|
||||
-e "s/@HID_CARD_CHANGE_DEFINE@/$HID_CARD_CHANGE_DEFINE/" \
|
||||
-e "s/@LIFE_CYCLE_MANAGEMENT_DEFINE@/$LIFE_CYCLE_MANAGEMENT_DEFINE/" \
|
||||
-e "s/@ACKBTN_DEFINE@/$ACKBTN_DEFINE/" \
|
||||
-e "s/@SERIALNO_STR_LEN_DEFINE@/$SERIALNO_STR_LEN_DEFINE/" \
|
||||
< config.h.in > config.h
|
||||
exit 0
|
||||
|
||||
10
src/flash.c
10
src/flash.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM
|
||||
*
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* Free Software Initiative of Japan
|
||||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
*
|
||||
@@ -71,7 +71,7 @@ static const uint8_t *data_pool;
|
||||
static uint8_t *last_p;
|
||||
|
||||
/* The first halfword is generation for the data page (little endian) */
|
||||
const uint8_t const flash_data[4] __attribute__ ((section (".gnuk_data"))) = {
|
||||
const uint8_t flash_data[4] __attribute__ ((section (".gnuk_data"))) = {
|
||||
0x00, 0x00, 0xff, 0xff
|
||||
};
|
||||
|
||||
@@ -157,6 +157,12 @@ flash_terminate (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
#ifdef FLASH_UPGRADE_SUPPORT
|
||||
const uint8_t *p;
|
||||
|
||||
p = gpg_get_firmware_update_key (0);
|
||||
flash_erase_page ((uintptr_t)p);
|
||||
#endif
|
||||
for (i = 0; i < 3; i++)
|
||||
flash_erase_page ((uintptr_t)flash_key_getpage (i));
|
||||
flash_erase_page ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START);
|
||||
|
||||
40
src/gnuk.h
40
src/gnuk.h
@@ -24,10 +24,11 @@ extern struct apdu apdu;
|
||||
void ccid_card_change_signal (int how);
|
||||
|
||||
/* CCID thread */
|
||||
#define EV_RX_DATA_READY 1 /* USB Rx data available */
|
||||
#define EV_EXEC_FINISHED 2 /* OpenPGP Execution finished */
|
||||
#define EV_TX_FINISHED 4 /* CCID Tx finished */
|
||||
#define EV_CARD_CHANGE 8
|
||||
#define EV_RX_DATA_READY 1 /* USB Rx data available */
|
||||
#define EV_EXEC_ACK_REQUIRED 2 /* OpenPGPcard Execution ACK required*/
|
||||
#define EV_EXEC_FINISHED 4 /* OpenPGPcard Execution finished */
|
||||
#define EV_TX_FINISHED 8 /* CCID Tx finished */
|
||||
#define EV_CARD_CHANGE 16
|
||||
|
||||
/* OpenPGPcard thread */
|
||||
#define EV_PINPAD_INPUT_DONE 1
|
||||
@@ -53,17 +54,17 @@ enum ccid_state {
|
||||
CCID_STATE_NOCARD, /* No card available */
|
||||
CCID_STATE_START, /* Initial */
|
||||
CCID_STATE_WAIT, /* Waiting APDU */
|
||||
/* Busy1, Busy2, Busy3, Busy5 */
|
||||
CCID_STATE_EXECUTE, /* Busy4 */
|
||||
CCID_STATE_RECEIVE, /* APDU Received Partially */
|
||||
CCID_STATE_SEND, /* APDU Sent Partially */
|
||||
|
||||
CCID_STATE_EXITED, /* ICC Thread Terminated */
|
||||
CCID_STATE_EXECUTE, /* Executing command */
|
||||
CCID_STATE_ACK_REQUIRED_0, /* Ack required (executing)*/
|
||||
CCID_STATE_ACK_REQUIRED_1, /* Waiting user's ACK (execution finished) */
|
||||
|
||||
CCID_STATE_EXITED, /* CCID Thread Terminated */
|
||||
CCID_STATE_EXEC_REQUESTED, /* Exec requested */
|
||||
};
|
||||
|
||||
|
||||
extern enum ccid_state *const ccid_state_p;
|
||||
enum ccid_state ccid_get_ccid_state (void);
|
||||
|
||||
extern volatile uint8_t auth_status;
|
||||
#define AC_NONE_AUTHORIZED 0x00
|
||||
@@ -123,8 +124,8 @@ const uint8_t *gpg_get_firmware_update_key (uint8_t keyno);
|
||||
|
||||
enum kind_of_key {
|
||||
GPG_KEY_FOR_SIGNING = 0,
|
||||
GPG_KEY_FOR_DECRYPTION,
|
||||
GPG_KEY_FOR_AUTHENTICATION,
|
||||
GPG_KEY_FOR_DECRYPTION = 1,
|
||||
GPG_KEY_FOR_AUTHENTICATION = 2,
|
||||
};
|
||||
|
||||
enum size_of_key {
|
||||
@@ -296,6 +297,7 @@ void gpg_increment_digital_signature_counter (void);
|
||||
void gpg_do_get_initial_pw_setting (int is_pw3, int *r_len,
|
||||
const uint8_t **r_p);
|
||||
int gpg_do_kdf_check (int len, int how_many);
|
||||
int gpg_do_get_uif (enum kind_of_key kk);
|
||||
|
||||
|
||||
void fatal (uint8_t code) __attribute__ ((noreturn));
|
||||
@@ -378,7 +380,17 @@ extern uint8_t admin_authorized;
|
||||
#define NR_KEY_ALGO_ATTR_DEC 0xf2
|
||||
#define NR_KEY_ALGO_ATTR_AUT 0xf3
|
||||
/*
|
||||
* NR_UINT_SOMETHING could be here... Use 0xf[456789abcd]
|
||||
* Representation of User Interaction Flag:
|
||||
* 0 (UIF disabled): 0xf?00 or No record in flash memory
|
||||
* 1 (UIF enabled): 0xf?01
|
||||
* 2 (UIF permanently enabled): 0xf?02
|
||||
*
|
||||
*/
|
||||
#define NR_DO_UIF_SIG 0xf6
|
||||
#define NR_DO_UIF_DEC 0xf7
|
||||
#define NR_DO_UIF_AUT 0xf8
|
||||
/*
|
||||
* NR_UINT_SOMETHING could be here... Use 0xf[459abcd]
|
||||
*/
|
||||
/* 123-counters: Recorded in flash memory by 2-halfword (4-byte). */
|
||||
/*
|
||||
@@ -434,6 +446,7 @@ extern const uint8_t gnuk_string_serial[];
|
||||
#define LED_GNUK_EXEC 32
|
||||
#define LED_START_COMMAND 64
|
||||
#define LED_FINISH_COMMAND 128
|
||||
#define LED_WAIT_FOR_BUTTON 256
|
||||
#define LED_OFF LED_FINISH_COMMAND
|
||||
void led_blink (int spec);
|
||||
|
||||
@@ -461,6 +474,7 @@ int pinpad_getline (int msg_code, uint32_t timeout_usec);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
extern uint8_t _regnual_start, __heap_end__[];
|
||||
|
||||
uint8_t * sram_address (uint32_t offset);
|
||||
|
||||
21
src/main.c
21
src/main.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* main.c - main routine of Gnuk
|
||||
*
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2015, 2016, 2017
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2015, 2016, 2017, 2018
|
||||
* Free Software Initiative of Japan
|
||||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
*
|
||||
@@ -68,7 +68,7 @@ device_initialize_once (void)
|
||||
* This is the first time invocation.
|
||||
* Setup serial number by unique device ID.
|
||||
*/
|
||||
const uint8_t *u = unique_device_id () + 8;
|
||||
const uint8_t *u = unique_device_id () + (MHZ < 96 ? 8: 0);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
@@ -146,7 +146,7 @@ emit_led (uint32_t on_time, uint32_t off_time)
|
||||
static void
|
||||
display_status_code (void)
|
||||
{
|
||||
enum ccid_state ccid_state = *ccid_state_p;
|
||||
enum ccid_state ccid_state = ccid_get_ccid_state ();
|
||||
uint32_t usec;
|
||||
|
||||
if (ccid_state == CCID_STATE_START)
|
||||
@@ -170,8 +170,7 @@ display_status_code (void)
|
||||
{
|
||||
usec = LED_TIMEOUT_INTERVAL;
|
||||
chopstx_poll (&usec, 1, led_event_poll);
|
||||
emit_led (ccid_state == CCID_STATE_RECEIVE?
|
||||
LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO, LED_TIMEOUT_STOP);
|
||||
emit_led (LED_TIMEOUT_ZERO, LED_TIMEOUT_STOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,6 +237,7 @@ main (int argc, const char *argv[])
|
||||
uintptr_t entry;
|
||||
#endif
|
||||
chopstx_t ccid_thd;
|
||||
int wait_for_ack = 0;
|
||||
|
||||
chopstx_conf_idle (1);
|
||||
|
||||
@@ -354,7 +354,11 @@ main (int argc, const char *argv[])
|
||||
{
|
||||
eventmask_t m;
|
||||
|
||||
m = eventflag_wait (&led_event);
|
||||
if (wait_for_ack)
|
||||
m = eventflag_wait_timeout (&led_event, LED_TIMEOUT_INTERVAL);
|
||||
else
|
||||
m = eventflag_wait (&led_event);
|
||||
|
||||
switch (m)
|
||||
{
|
||||
case LED_ONESHOT:
|
||||
@@ -375,8 +379,11 @@ main (int argc, const char *argv[])
|
||||
break;
|
||||
case LED_GNUK_EXEC:
|
||||
goto exec;
|
||||
case LED_WAIT_FOR_BUTTON:
|
||||
wait_for_ack ^= 1;
|
||||
/* fall through */
|
||||
default:
|
||||
emit_led (LED_TIMEOUT_ZERO, LED_TIMEOUT_STOP);
|
||||
emit_led (LED_TIMEOUT_ZERO, LED_TIMEOUT_ZERO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
278
src/openpgp-do.c
278
src/openpgp-do.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* openpgp-do.c -- OpenPGP card Data Objects (DO) handling
|
||||
*
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* Free Software Initiative of Japan
|
||||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
*
|
||||
@@ -135,6 +135,14 @@ static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = {
|
||||
0x01, 0x00,
|
||||
};
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
/* General Feature Management */
|
||||
static const uint8_t feature_mngmnt[] __attribute__ ((aligned (1))) = {
|
||||
3,
|
||||
0x81, 0x01, 0x20,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Algorithm Attributes */
|
||||
#define OPENPGP_ALGO_RSA 0x01
|
||||
#define OPENPGP_ALGO_ECDH 0x12
|
||||
@@ -649,7 +657,7 @@ do_openpgpcard_aid (uint16_t tag, int with_tag)
|
||||
|
||||
if (vid == 0xffff || vid == 0x0000)
|
||||
{
|
||||
const uint8_t *u = unique_device_id () + 8;
|
||||
const uint8_t *u = unique_device_id () + (MHZ < 96 ? 8: 0);
|
||||
|
||||
memcpy (res_p, openpgpcard_aid, 8);
|
||||
res_p += 8;
|
||||
@@ -807,7 +815,66 @@ rw_algorithm_attr (uint16_t tag, int with_tag,
|
||||
}
|
||||
}
|
||||
|
||||
#define SIZE_OF_KDF_DO 110
|
||||
|
||||
static uint8_t uif_flags; /* Six bits of flags */
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
int
|
||||
gpg_do_get_uif (enum kind_of_key kk)
|
||||
{
|
||||
return ((uif_flags >> (kk * 2)) & 3) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rw_uif (uint16_t tag, int with_tag, const uint8_t *data, int len, int is_write)
|
||||
{
|
||||
uint8_t nr;
|
||||
int v;
|
||||
|
||||
if (tag != GPG_DO_UIF_SIG && tag != GPG_DO_UIF_DEC && tag != GPG_DO_UIF_AUT)
|
||||
return 0; /* Failure */
|
||||
|
||||
nr = (tag - GPG_DO_UIF_SIG) + NR_DO_UIF_SIG;
|
||||
v = (uif_flags >> ((tag - GPG_DO_UIF_SIG) * 2)) & 3;
|
||||
if (is_write)
|
||||
{
|
||||
const uint8_t *p;
|
||||
|
||||
if (len != 2 || data[1] != 0x20)
|
||||
return 0;
|
||||
|
||||
if (v == 2)
|
||||
return 0;
|
||||
|
||||
if (data[0] != 0x00 && data[0] != 0x01 && data[0] != 0x02)
|
||||
return 0;
|
||||
|
||||
p = flash_enum_write (nr, data[0]);
|
||||
if (p == NULL)
|
||||
return 0;
|
||||
|
||||
uif_flags &= ~(3 << ((nr - NR_DO_UIF_SIG) * 2));
|
||||
uif_flags |= (data[0] & 3) << ((nr - NR_DO_UIF_SIG) * 2);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (with_tag)
|
||||
{
|
||||
copy_tag (tag);
|
||||
*res_p++ = 2;
|
||||
}
|
||||
|
||||
*res_p++ = v;
|
||||
*res_p++ = 0x20;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define SIZE_OF_KDF_DO_MIN 90
|
||||
#define SIZE_OF_KDF_DO_MAX 110
|
||||
#define OPENPGP_KDF_ITERSALTED_S2K 3
|
||||
#define OPENPGP_SHA256 8
|
||||
|
||||
@@ -826,32 +893,55 @@ rw_kdf (uint16_t tag, int with_tag, const uint8_t *data, int len, int is_write)
|
||||
|| do_ptr[NR_DO_PRVKEY_AUT])
|
||||
return 0;
|
||||
|
||||
/* The valid data format is:
|
||||
Deleting:
|
||||
nothing
|
||||
Minimum (for admin-less):
|
||||
81 01 03 = KDF_ITERSALTED_S2K
|
||||
82 01 08 = SHA256
|
||||
83 04 4-byte... = count
|
||||
84 08 8-byte... = salt
|
||||
87 20 32-byte user hash
|
||||
88 20 32-byte admin hash
|
||||
Full:
|
||||
81 01 03 = KDF_ITERSALTED_S2K
|
||||
82 01 08 = SHA256
|
||||
83 04 4-byte... = count
|
||||
84 08 8-byte... = salt user
|
||||
85 08 8-byte... = salt reset-code
|
||||
86 08 8-byte... = salt admin
|
||||
87 20 32-byte user hash
|
||||
88 20 32-byte admin hash
|
||||
*/
|
||||
if (!(len == 0
|
||||
|| (len == SIZE_OF_KDF_DO_MIN &&
|
||||
(data[0] == 0x81 && data[3] == 0x82 && data[6] == 0x83
|
||||
&& data[12] == 0x84 && data[22] == 0x87 && data[56] == 0x88))
|
||||
|| (len == SIZE_OF_KDF_DO_MAX &&
|
||||
(data[0] == 0x81 && data[3] == 0x82 && data[6] == 0x83
|
||||
&& data[12] == 0x84 && data[22] == 0x85 && data[32] == 0x86
|
||||
&& data[42] == 0x87 && data[76] == 0x88))))
|
||||
return 0;
|
||||
|
||||
if (*do_data_p)
|
||||
flash_do_release (*do_data_p);
|
||||
|
||||
/* Clear all keystrings and auth states */
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW1, NULL, 0);
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0);
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0);
|
||||
ac_reset_admin ();
|
||||
ac_reset_pso_cds ();
|
||||
ac_reset_other ();
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
*do_data_p = NULL;
|
||||
return 1;
|
||||
}
|
||||
else if (len != SIZE_OF_KDF_DO ||
|
||||
!(data[0] == 0x81 && data[3] == 0x82 && data[6] == 0x83
|
||||
&& data[12] == 0x84 && data[22] == 0x85 && data[32] == 0x86
|
||||
&& data[42] == 0x87 && data[76] == 0x88))
|
||||
/* Format validation failed. The valid format is:
|
||||
81 01 03 = KDF_ITERSALTED_S2K
|
||||
82 01 08 = SHA256
|
||||
83 04 4-byte... = count
|
||||
84 08 8-byte... = salt user
|
||||
85 08 8-byte... = salt reset-code
|
||||
86 08 8-byte... = salt admin
|
||||
87 20 32-byte user hash
|
||||
88 20 32-byte admin hash
|
||||
*/
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
*do_data_p = flash_do_write (NR_DO_KDF, data, SIZE_OF_KDF_DO);
|
||||
*do_data_p = flash_do_write (NR_DO_KDF, data, len);
|
||||
if (*do_data_p)
|
||||
return 1;
|
||||
else
|
||||
@@ -860,7 +950,11 @@ rw_kdf (uint16_t tag, int with_tag, const uint8_t *data, int len, int is_write)
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_do_1 (tag, do_ptr[NR_DO_KDF], with_tag);
|
||||
if (do_ptr[NR_DO_KDF])
|
||||
copy_do_1 (tag, do_ptr[NR_DO_KDF], with_tag);
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -868,10 +962,22 @@ rw_kdf (uint16_t tag, int with_tag, const uint8_t *data, int len, int is_write)
|
||||
int
|
||||
gpg_do_kdf_check (int len, int how_many)
|
||||
{
|
||||
const uint8_t *kdf_spec = gpg_do_read_simple (NR_DO_KDF);
|
||||
const uint8_t *kdf_do = do_ptr[NR_DO_KDF];
|
||||
|
||||
if (kdf_spec && (kdf_spec[43] * how_many) != len)
|
||||
return 0;
|
||||
if (kdf_do)
|
||||
{
|
||||
const uint8_t *kdf_spec = kdf_do+1;
|
||||
int kdf_do_len = kdf_do[0];
|
||||
int hash_len;
|
||||
|
||||
if (kdf_do_len == SIZE_OF_KDF_DO_MIN)
|
||||
hash_len = kdf_spec[23];
|
||||
else
|
||||
hash_len = kdf_spec[43];
|
||||
|
||||
if ((hash_len * how_many) != len && hash_len != len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -879,15 +985,29 @@ gpg_do_kdf_check (int len, int how_many)
|
||||
void
|
||||
gpg_do_get_initial_pw_setting (int is_pw3, int *r_len, const uint8_t **r_p)
|
||||
{
|
||||
const uint8_t *kdf_spec = gpg_do_read_simple (NR_DO_KDF);
|
||||
const uint8_t *kdf_do = do_ptr[NR_DO_KDF];
|
||||
|
||||
if (kdf_spec)
|
||||
if (kdf_do)
|
||||
{
|
||||
int len = kdf_do[0];
|
||||
const uint8_t *kdf_spec = kdf_do+1;
|
||||
|
||||
*r_len = 32;
|
||||
if (is_pw3)
|
||||
*r_p = kdf_spec + 78;
|
||||
|
||||
if (len == SIZE_OF_KDF_DO_MIN)
|
||||
{
|
||||
if (is_pw3)
|
||||
*r_p = kdf_spec + 58;
|
||||
else
|
||||
*r_p = kdf_spec + 24;
|
||||
}
|
||||
else
|
||||
*r_p = kdf_spec + 44;
|
||||
{
|
||||
if (is_pw3)
|
||||
*r_p = kdf_spec + 78;
|
||||
else
|
||||
*r_p = kdf_spec + 44;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -917,34 +1037,45 @@ proc_resetting_code (const uint8_t *data, int len)
|
||||
|
||||
DEBUG_INFO ("Resetting Code!\r\n");
|
||||
|
||||
if (gpg_do_kdf_check (len, 1) == 0)
|
||||
return 0;
|
||||
if (len == 0)
|
||||
{ /* Removal of resetting code. */
|
||||
enum kind_of_key kk0;
|
||||
|
||||
newpw_len = len;
|
||||
newpw = data;
|
||||
new_ks0[0] = newpw_len;
|
||||
random_get_salt (salt);
|
||||
s2k (salt, SALT_SIZE, newpw, newpw_len, new_ks);
|
||||
r = gpg_change_keystring (admin_authorized, old_ks, BY_RESETCODE, new_ks);
|
||||
if (r <= -2)
|
||||
{
|
||||
DEBUG_INFO ("memory error.\r\n");
|
||||
return 0;
|
||||
}
|
||||
else if (r < 0)
|
||||
{
|
||||
DEBUG_INFO ("security error.\r\n");
|
||||
return 0;
|
||||
}
|
||||
else if (r == 0)
|
||||
{
|
||||
DEBUG_INFO ("error (no prvkey).\r\n");
|
||||
return 0;
|
||||
for (kk0 = 0; kk0 <= GPG_KEY_FOR_AUTHENTICATION; kk0++)
|
||||
gpg_do_chks_prvkey (kk0, BY_RESETCODE, NULL, 0, NULL);
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("done.\r\n");
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, new_ks0, KS_META_SIZE);
|
||||
if (gpg_do_kdf_check (len, 1) == 0)
|
||||
return 0;
|
||||
|
||||
newpw_len = len;
|
||||
newpw = data;
|
||||
new_ks0[0] = newpw_len;
|
||||
random_get_salt (salt);
|
||||
s2k (salt, SALT_SIZE, newpw, newpw_len, new_ks);
|
||||
r = gpg_change_keystring (admin_authorized, old_ks, BY_RESETCODE, new_ks);
|
||||
if (r <= -2)
|
||||
{
|
||||
DEBUG_INFO ("memory error.\r\n");
|
||||
return 0;
|
||||
}
|
||||
else if (r < 0)
|
||||
{
|
||||
DEBUG_INFO ("security error.\r\n");
|
||||
return 0;
|
||||
}
|
||||
else if (r == 0)
|
||||
{
|
||||
DEBUG_INFO ("error (no prvkey).\r\n");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("done.\r\n");
|
||||
gpg_do_write_simple (NR_DO_KEYSTRING_RC, new_ks0, KS_META_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
gpg_pw_reset_err_counter (PW_ERR_RC);
|
||||
@@ -1549,18 +1680,32 @@ static const uint16_t cmp_ch_data[] = {
|
||||
};
|
||||
|
||||
static const uint16_t cmp_app_data[] = {
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
4,
|
||||
#else
|
||||
3,
|
||||
#endif
|
||||
GPG_DO_AID,
|
||||
GPG_DO_HIST_BYTES,
|
||||
GPG_DO_DISCRETIONARY,
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
GPG_DO_FEATURE_MNGMNT,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const uint16_t cmp_discretionary[] = {
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
11,
|
||||
#else
|
||||
8,
|
||||
#endif
|
||||
GPG_DO_EXTCAP,
|
||||
GPG_DO_ALG_SIG, GPG_DO_ALG_DEC, GPG_DO_ALG_AUT,
|
||||
GPG_DO_PW_STATUS,
|
||||
GPG_DO_FP_ALL, GPG_DO_CAFP_ALL, GPG_DO_KGTIME_ALL
|
||||
GPG_DO_FP_ALL, GPG_DO_CAFP_ALL, GPG_DO_KGTIME_ALL,
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
GPG_DO_UIF_SIG, GPG_DO_UIF_DEC, GPG_DO_UIF_AUT
|
||||
#endif
|
||||
};
|
||||
|
||||
static const uint16_t cmp_ss_temp[] = { 1, GPG_DO_DS_COUNT };
|
||||
@@ -1599,11 +1744,19 @@ gpg_do_table[] = {
|
||||
rw_algorithm_attr },
|
||||
{ GPG_DO_ALG_AUT, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED,
|
||||
rw_algorithm_attr },
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
{ GPG_DO_UIF_SIG, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, rw_uif },
|
||||
{ GPG_DO_UIF_DEC, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, rw_uif },
|
||||
{ GPG_DO_UIF_AUT, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, rw_uif },
|
||||
#endif
|
||||
{ GPG_DO_KDF, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED,
|
||||
rw_kdf },
|
||||
/* Fixed data */
|
||||
{ GPG_DO_HIST_BYTES, DO_FIXED, AC_ALWAYS, AC_NEVER, historical_bytes },
|
||||
{ GPG_DO_EXTCAP, DO_FIXED, AC_ALWAYS, AC_NEVER, extended_capabilities },
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
{ GPG_DO_FEATURE_MNGMNT, DO_FIXED, AC_ALWAYS, AC_NEVER, feature_mngmnt },
|
||||
#endif
|
||||
/* Compound data: Read access only */
|
||||
{ GPG_DO_CH_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ch_data },
|
||||
{ GPG_DO_APP_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_app_data },
|
||||
@@ -1642,6 +1795,11 @@ gpg_data_scan (const uint8_t *do_start, const uint8_t *do_end)
|
||||
pw_err_counter_p[PW_ERR_PW3] = NULL;
|
||||
algo_attr_sig_p = algo_attr_dec_p = algo_attr_aut_p = NULL;
|
||||
digital_signature_counter = 0;
|
||||
uif_flags = 0;
|
||||
|
||||
/* Clear all data objects. */
|
||||
for (i = 0; i < NR_DO__LAST__; i++)
|
||||
do_ptr[i] = NULL;
|
||||
|
||||
/* When the card is terminated no data objects are valid. */
|
||||
if (do_start == NULL)
|
||||
@@ -1700,6 +1858,13 @@ gpg_data_scan (const uint8_t *do_start, const uint8_t *do_end)
|
||||
algo_attr_aut_p = p - 1;
|
||||
p++;
|
||||
break;
|
||||
case NR_DO_UIF_SIG:
|
||||
case NR_DO_UIF_DEC:
|
||||
case NR_DO_UIF_AUT:
|
||||
uif_flags &= ~(3 << ((nr - NR_DO_UIF_SIG) * 2));
|
||||
uif_flags |= (second_byte & 3) << ((nr - NR_DO_UIF_SIG) * 2);
|
||||
p++;
|
||||
break;
|
||||
case NR_COUNTER_123:
|
||||
p++;
|
||||
if (second_byte <= PW_ERR_PW3)
|
||||
@@ -1799,6 +1964,13 @@ gpg_data_copy (const uint8_t *p_start)
|
||||
p += 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
if ((v = (uif_flags & (3 << (i * 2)))))
|
||||
{
|
||||
flash_enum_write_internal (p, NR_DO_UIF_SIG + 1, v);
|
||||
p += 2;
|
||||
}
|
||||
|
||||
data_objects_number_of_bytes = 0;
|
||||
for (i = 0; i < NR_DO__LAST__; i++)
|
||||
if (do_ptr[i] != NULL)
|
||||
|
||||
108
src/openpgp.c
108
src/openpgp.c
@@ -145,7 +145,7 @@ get_pinpad_input (int msg_code)
|
||||
#endif
|
||||
|
||||
static void
|
||||
cmd_verify (void)
|
||||
cmd_verify (struct eventflag *ccid_comm)
|
||||
{
|
||||
int len;
|
||||
uint8_t p1 = P1 (apdu);
|
||||
@@ -153,6 +153,7 @@ cmd_verify (void)
|
||||
int r;
|
||||
const uint8_t *pw;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - VERIFY\r\n");
|
||||
DEBUG_BYTE (p2);
|
||||
|
||||
@@ -171,7 +172,8 @@ cmd_verify (void)
|
||||
r = ac_check_status (AC_ADMIN_AUTHORIZED);
|
||||
|
||||
if (r)
|
||||
GPG_SUCCESS (); /* If authentication done already, return success. */
|
||||
/* If authentication done already, return success. */
|
||||
GPG_SUCCESS ();
|
||||
else
|
||||
{ /* If not, return retry counter, encoded. */
|
||||
r = gpg_pw_get_retry_counter (p2);
|
||||
@@ -274,7 +276,7 @@ gpg_change_keystring (int who_old, const uint8_t *old_ks,
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_change_password (void)
|
||||
cmd_change_password (struct eventflag *ccid_comm)
|
||||
{
|
||||
uint8_t old_ks[KEYSTRING_MD_SIZE];
|
||||
uint8_t new_ks0[KEYSTRING_SIZE];
|
||||
@@ -294,6 +296,7 @@ cmd_change_password (void)
|
||||
int salt_len;
|
||||
const uint8_t *ks_pw3;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO ("Change PW\r\n");
|
||||
DEBUG_BYTE (who);
|
||||
|
||||
@@ -365,8 +368,24 @@ cmd_change_password (void)
|
||||
|
||||
if (ks_pw3 == NULL)
|
||||
{
|
||||
salt = NULL;
|
||||
salt_len = 0;
|
||||
if (admin_authorized == BY_USER)
|
||||
{
|
||||
const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1);
|
||||
|
||||
if (ks_pw1 == NULL)
|
||||
{
|
||||
GPG_SECURITY_FAILURE ();
|
||||
return;
|
||||
}
|
||||
|
||||
salt = KS_GET_SALT (ks_pw1);
|
||||
salt_len = SALT_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
salt = NULL;
|
||||
salt_len = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -530,7 +549,7 @@ s2k (const unsigned char *salt, size_t slen,
|
||||
|
||||
|
||||
static void
|
||||
cmd_reset_user_password (void)
|
||||
cmd_reset_user_password (struct eventflag *ccid_comm)
|
||||
{
|
||||
uint8_t p1 = P1 (apdu);
|
||||
int len;
|
||||
@@ -544,6 +563,7 @@ cmd_reset_user_password (void)
|
||||
const uint8_t *salt;
|
||||
int salt_len;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO ("Reset PW1\r\n");
|
||||
DEBUG_BYTE (p1);
|
||||
|
||||
@@ -554,6 +574,7 @@ cmd_reset_user_password (void)
|
||||
{
|
||||
const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC);
|
||||
uint8_t old_ks[KEYSTRING_MD_SIZE];
|
||||
const uint8_t *ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3);
|
||||
|
||||
if (gpg_do_kdf_check (len, 2) == 0)
|
||||
{
|
||||
@@ -580,6 +601,16 @@ cmd_reset_user_password (void)
|
||||
salt_len = SALT_SIZE;
|
||||
newpw = pw + pw_len;
|
||||
newpw_len = len - pw_len;
|
||||
|
||||
/* Check length of new password */
|
||||
if ((ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN)
|
||||
|| newpw_len < USER_PASSWD_MINLEN)
|
||||
{
|
||||
DEBUG_INFO ("new password length is too short.");
|
||||
GPG_CONDITION_NOT_SATISFIED ();
|
||||
return;
|
||||
}
|
||||
|
||||
random_get_salt (new_salt);
|
||||
s2k (salt, salt_len, pw, pw_len, old_ks);
|
||||
s2k (new_salt, SALT_SIZE, newpw, newpw_len, new_ks);
|
||||
@@ -667,12 +698,13 @@ cmd_reset_user_password (void)
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_put_data (void)
|
||||
cmd_put_data (struct eventflag *ccid_comm)
|
||||
{
|
||||
uint8_t *data;
|
||||
uint16_t tag;
|
||||
int len;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - PUT DATA\r\n");
|
||||
|
||||
tag = ((P1 (apdu)<<8) | P2 (apdu));
|
||||
@@ -682,8 +714,9 @@ cmd_put_data (void)
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_pgp_gakp (void)
|
||||
cmd_pgp_gakp (struct eventflag *ccid_comm)
|
||||
{
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - Generate Asymmetric Key Pair\r\n");
|
||||
DEBUG_BYTE (P1 (apdu));
|
||||
|
||||
@@ -717,12 +750,13 @@ gpg_get_firmware_update_key (uint8_t keyno)
|
||||
#endif
|
||||
|
||||
static void
|
||||
cmd_read_binary (void)
|
||||
cmd_read_binary (struct eventflag *ccid_comm)
|
||||
{
|
||||
int is_short_EF = (P1 (apdu) & 0x80) != 0;
|
||||
uint8_t file_id;
|
||||
uint16_t offset;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - Read binary\r\n");
|
||||
|
||||
if (is_short_EF)
|
||||
@@ -793,8 +827,9 @@ cmd_read_binary (void)
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_select_file (void)
|
||||
cmd_select_file (struct eventflag *ccid_comm)
|
||||
{
|
||||
(void)ccid_comm;
|
||||
if (P1 (apdu) == 4) /* Selection by DF name */
|
||||
{
|
||||
DEBUG_INFO (" - select DF by name\r\n");
|
||||
@@ -863,10 +898,11 @@ cmd_select_file (void)
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_get_data (void)
|
||||
cmd_get_data (struct eventflag *ccid_comm)
|
||||
{
|
||||
uint16_t tag = ((P1 (apdu)<<8) | P2 (apdu));
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - Get Data\r\n");
|
||||
|
||||
gpg_do_get_data (tag, 0);
|
||||
@@ -881,7 +917,7 @@ cmd_get_data (void)
|
||||
#define ECC_CIPHER_DO_HEADER_SIZE 7
|
||||
|
||||
static void
|
||||
cmd_pso (void)
|
||||
cmd_pso (struct eventflag *ccid_comm)
|
||||
{
|
||||
int len = apdu.cmd_apdu_data_len;
|
||||
int r = -1;
|
||||
@@ -908,6 +944,11 @@ cmd_pso (void)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
if (gpg_do_get_uif (GPG_KEY_FOR_SIGNING))
|
||||
eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED);
|
||||
#endif
|
||||
|
||||
if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
|
||||
{
|
||||
/* Check size of digestInfo */
|
||||
@@ -999,6 +1040,13 @@ cmd_pso (void)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
if (gpg_do_get_uif (GPG_KEY_FOR_DECRYPTION))
|
||||
eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED);
|
||||
#else
|
||||
(void)ccid_comm;
|
||||
#endif
|
||||
|
||||
if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
|
||||
{
|
||||
/* Skip padding 0x00 */
|
||||
@@ -1075,7 +1123,7 @@ cmd_pso (void)
|
||||
|
||||
#define MAX_RSA_DIGEST_INFO_LEN 102 /* 40% */
|
||||
static void
|
||||
cmd_internal_authenticate (void)
|
||||
cmd_internal_authenticate (struct eventflag *ccid_comm)
|
||||
{
|
||||
int attr = gpg_get_algo_attr (GPG_KEY_FOR_AUTHENTICATION);
|
||||
int pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_AUTHENTICATION,
|
||||
@@ -1105,6 +1153,13 @@ cmd_internal_authenticate (void)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
if (gpg_do_get_uif (GPG_KEY_FOR_AUTHENTICATION))
|
||||
eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED);
|
||||
#else
|
||||
(void)ccid_comm;
|
||||
#endif
|
||||
|
||||
if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
|
||||
{
|
||||
if (len > MAX_RSA_DIGEST_INFO_LEN)
|
||||
@@ -1281,10 +1336,11 @@ modify_binary (uint8_t op, uint8_t p1, uint8_t p2, int len)
|
||||
|
||||
#if defined(CERTDO_SUPPORT)
|
||||
static void
|
||||
cmd_update_binary (void)
|
||||
cmd_update_binary (struct eventflag *ccid_comm)
|
||||
{
|
||||
int len = apdu.cmd_apdu_data_len;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - UPDATE BINARY\r\n");
|
||||
modify_binary (MBD_OPRATION_UPDATE, P1 (apdu), P2 (apdu), len);
|
||||
DEBUG_INFO ("UPDATE BINARY done.\r\n");
|
||||
@@ -1293,10 +1349,11 @@ cmd_update_binary (void)
|
||||
|
||||
|
||||
static void
|
||||
cmd_write_binary (void)
|
||||
cmd_write_binary (struct eventflag *ccid_comm)
|
||||
{
|
||||
int len = apdu.cmd_apdu_data_len;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - WRITE BINARY\r\n");
|
||||
modify_binary (MBD_OPRATION_WRITE, P1 (apdu), P2 (apdu), len);
|
||||
DEBUG_INFO ("WRITE BINARY done.\r\n");
|
||||
@@ -1305,7 +1362,7 @@ cmd_write_binary (void)
|
||||
|
||||
#ifdef FLASH_UPGRADE_SUPPORT
|
||||
static void
|
||||
cmd_external_authenticate (void)
|
||||
cmd_external_authenticate (struct eventflag *ccid_comm)
|
||||
{
|
||||
const uint8_t *pubkey;
|
||||
const uint8_t *signature = apdu.cmd_apdu_data;
|
||||
@@ -1313,6 +1370,7 @@ cmd_external_authenticate (void)
|
||||
uint8_t keyno = P2 (apdu);
|
||||
int r;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - EXTERNAL AUTHENTICATE\r\n");
|
||||
|
||||
if (keyno >= 4)
|
||||
@@ -1348,10 +1406,11 @@ cmd_external_authenticate (void)
|
||||
#endif
|
||||
|
||||
static void
|
||||
cmd_get_challenge (void)
|
||||
cmd_get_challenge (struct eventflag *ccid_comm)
|
||||
{
|
||||
int len = apdu.expected_res_size;
|
||||
|
||||
(void)ccid_comm;
|
||||
DEBUG_INFO (" - GET CHALLENGE\r\n");
|
||||
|
||||
if (len > CHALLENGE_LEN)
|
||||
@@ -1376,8 +1435,9 @@ cmd_get_challenge (void)
|
||||
|
||||
#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
|
||||
static void
|
||||
cmd_activate_file (void)
|
||||
cmd_activate_file (struct eventflag *ccid_comm)
|
||||
{
|
||||
(void)ccid_comm;
|
||||
if (file_selection != FILE_CARD_TERMINATED)
|
||||
{
|
||||
GPG_NO_RECORD ();
|
||||
@@ -1390,13 +1450,13 @@ cmd_activate_file (void)
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_terminate_df (void)
|
||||
cmd_terminate_df (struct eventflag *ccid_comm)
|
||||
{
|
||||
const uint8_t *ks_pw3;
|
||||
|
||||
uint8_t p1 = P1 (apdu);
|
||||
uint8_t p2 = P2 (apdu);
|
||||
|
||||
(void)ccid_comm;
|
||||
if (file_selection != FILE_DF_OPENPGP)
|
||||
{
|
||||
GPG_NO_RECORD ();
|
||||
@@ -1440,7 +1500,7 @@ cmd_terminate_df (void)
|
||||
struct command
|
||||
{
|
||||
uint8_t command;
|
||||
void (*cmd_handler) (void);
|
||||
void (*cmd_handler) (struct eventflag *ccid_comm);
|
||||
};
|
||||
|
||||
const struct command cmds[] = {
|
||||
@@ -1474,7 +1534,7 @@ const struct command cmds[] = {
|
||||
#define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command)))
|
||||
|
||||
static void
|
||||
process_command_apdu (void)
|
||||
process_command_apdu (struct eventflag *ccid_comm)
|
||||
{
|
||||
int i;
|
||||
uint8_t cmd = INS (apdu);
|
||||
@@ -1498,7 +1558,7 @@ process_command_apdu (void)
|
||||
else
|
||||
{
|
||||
chopstx_setcancelstate (1);
|
||||
cmds[i].cmd_handler ();
|
||||
cmds[i].cmd_handler (ccid_comm);
|
||||
chopstx_setcancelstate (0);
|
||||
}
|
||||
}
|
||||
@@ -1612,7 +1672,7 @@ openpgp_card_thread (void *arg)
|
||||
break;
|
||||
|
||||
led_blink (LED_START_COMMAND);
|
||||
process_command_apdu ();
|
||||
process_command_apdu (ccid_comm);
|
||||
led_blink (LED_FINISH_COMMAND);
|
||||
done:
|
||||
eventflag_signal (ccid_comm, EV_EXEC_FINISHED);
|
||||
|
||||
@@ -986,6 +986,7 @@ tim_main (void *arg)
|
||||
{
|
||||
chopstx_intr_wait (&interrupt);
|
||||
cir_timer_interrupt ();
|
||||
chopstx_intr_done (&interrupt);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -1006,6 +1007,7 @@ ext_main (void *arg)
|
||||
{
|
||||
chopstx_intr_wait (&interrupt);
|
||||
cir_ext_interrupt ();
|
||||
chopstx_intr_done (&interrupt);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define SIZE_2 4096
|
||||
#define SIZE_3 (5 * 4096)
|
||||
#else
|
||||
#define SIZE_0 0x0150 /* Main */
|
||||
#define SIZE_0 0x0160 /* Main */
|
||||
#define SIZE_1 0x01a0 /* CCID */
|
||||
#define SIZE_2 0x0180 /* RNG */
|
||||
#if MEMORY_SIZE >= 32
|
||||
|
||||
128
src/usb-ccid.c
128
src/usb-ccid.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* usb-ccid.c -- USB CCID protocol handling
|
||||
*
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* Free Software Initiative of Japan
|
||||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
*
|
||||
@@ -29,6 +29,10 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
#include <contrib/ackbtn.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "usb-cdc.h"
|
||||
#include "debug.h"
|
||||
@@ -182,9 +186,10 @@ struct ccid_header {
|
||||
|
||||
/* Data structure handled by CCID layer */
|
||||
struct ccid {
|
||||
enum ccid_state ccid_state;
|
||||
uint8_t state;
|
||||
uint8_t err;
|
||||
uint32_t ccid_state : 4;
|
||||
uint32_t state : 4;
|
||||
uint32_t err : 1;
|
||||
uint32_t tx_busy : 1;
|
||||
|
||||
uint8_t *p;
|
||||
size_t len;
|
||||
@@ -241,6 +246,7 @@ struct ccid {
|
||||
static void ccid_reset (struct ccid *c)
|
||||
{
|
||||
c->err = 0;
|
||||
c->tx_busy = 0;
|
||||
c->state = APDU_STATE_WAIT_COMMAND;
|
||||
c->p = c->a->cmd_apdu_data;
|
||||
c->len = MAX_CMD_APDU_DATA_SIZE;
|
||||
@@ -252,10 +258,11 @@ static void ccid_init (struct ccid *c, struct ep_in *epi, struct ep_out *epo,
|
||||
struct apdu *a)
|
||||
{
|
||||
c->ccid_state = CCID_STATE_START;
|
||||
c->err = 0;
|
||||
c->tx_busy = 0;
|
||||
c->state = APDU_STATE_WAIT_COMMAND;
|
||||
c->p = a->cmd_apdu_data;
|
||||
c->len = MAX_CMD_APDU_DATA_SIZE;
|
||||
c->err = 0;
|
||||
memset (&c->ccid_header, 0, sizeof (struct ccid_header));
|
||||
c->sw1sw2[0] = 0x90;
|
||||
c->sw1sw2[1] = 0x00;
|
||||
@@ -765,6 +772,7 @@ usb_tx_done (uint8_t ep_num, uint16_t len)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ATR (Answer To Reset) string
|
||||
*
|
||||
@@ -820,6 +828,7 @@ static void ccid_error (struct ccid *c, int offset)
|
||||
#else
|
||||
usb_lld_write (c->epi->ep_num, ccid_reply, CCID_MSG_HEADER_SIZE);
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
}
|
||||
|
||||
extern void *openpgp_card_thread (void *arg);
|
||||
@@ -893,7 +902,7 @@ ccid_power_on (struct ccid *c)
|
||||
usb_lld_tx_enable (c->epi->ep_num, CCID_MSG_HEADER_SIZE + size_atr);
|
||||
#endif
|
||||
DEBUG_INFO ("ON\r\n");
|
||||
|
||||
c->tx_busy = 1;
|
||||
return CCID_STATE_WAIT;
|
||||
}
|
||||
|
||||
@@ -934,6 +943,7 @@ ccid_send_status (struct ccid *c)
|
||||
#ifdef DEBUG_MORE
|
||||
DEBUG_INFO ("St\r\n");
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
}
|
||||
|
||||
static enum ccid_state
|
||||
@@ -949,6 +959,7 @@ ccid_power_off (struct ccid *c)
|
||||
c->ccid_state = CCID_STATE_START; /* This status change should be here */
|
||||
ccid_send_status (c);
|
||||
DEBUG_INFO ("OFF\r\n");
|
||||
c->tx_busy = 1;
|
||||
return CCID_STATE_START;
|
||||
}
|
||||
|
||||
@@ -982,12 +993,16 @@ ccid_send_data_block_internal (struct ccid *c, uint8_t status, uint8_t error)
|
||||
#endif
|
||||
if (len == 0)
|
||||
{
|
||||
c->epi->buf = NULL;
|
||||
c->epi->tx_done = 1;
|
||||
|
||||
#ifdef GNU_LINUX_EMULATION
|
||||
usb_lld_tx_enable_buf (c->epi->ep_num, endp1_tx_buf,
|
||||
CCID_MSG_HEADER_SIZE);
|
||||
#else
|
||||
usb_lld_tx_enable (c->epi->ep_num, CCID_MSG_HEADER_SIZE);
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1065,6 +1080,7 @@ ccid_send_data_block_internal (struct ccid *c, uint8_t status, uint8_t error)
|
||||
#ifdef DEBUG_MORE
|
||||
DEBUG_INFO ("DATA\r\n");
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1076,7 +1092,8 @@ ccid_send_data_block (struct ccid *c)
|
||||
static void
|
||||
ccid_send_data_block_time_extension (struct ccid *c)
|
||||
{
|
||||
ccid_send_data_block_internal (c, CCID_CMD_STATUS_TIMEEXT, 1);
|
||||
ccid_send_data_block_internal (c, CCID_CMD_STATUS_TIMEEXT,
|
||||
c->ccid_state == CCID_STATE_EXECUTE? 1: 0xff);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1115,6 +1132,7 @@ ccid_send_data_block_0x9000 (struct ccid *c)
|
||||
#ifdef DEBUG_MORE
|
||||
DEBUG_INFO ("DATA\r\n");
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1211,6 +1229,7 @@ ccid_send_data_block_gr (struct ccid *c, size_t chunk_len)
|
||||
#ifdef DEBUG_MORE
|
||||
DEBUG_INFO ("DATA\r\n");
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1259,6 +1278,7 @@ ccid_send_params (struct ccid *c)
|
||||
#ifdef DEBUG_MORE
|
||||
DEBUG_INFO ("PARAMS\r\n");
|
||||
#endif
|
||||
c->tx_busy = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1413,6 +1433,7 @@ ccid_handle_data (struct ccid *c)
|
||||
c->a->res_apdu_data_len = 0;
|
||||
c->a->res_apdu_data = &c->p[5];
|
||||
|
||||
c->state = APDU_STATE_COMMAND_RECEIVED;
|
||||
eventflag_signal (&c->openpgp_comm, EV_VERIFY_CMD_AVAILABLE);
|
||||
next_state = CCID_STATE_EXECUTE;
|
||||
}
|
||||
@@ -1447,6 +1468,7 @@ ccid_handle_data (struct ccid *c)
|
||||
c->a->res_apdu_data_len = 0;
|
||||
c->a->res_apdu_data = &ccid_buffer[5];
|
||||
|
||||
c->state = APDU_STATE_COMMAND_RECEIVED;
|
||||
eventflag_signal (&c->openpgp_comm, EV_MODIFY_CMD_AVAILABLE);
|
||||
next_state = CCID_STATE_EXECUTE;
|
||||
}
|
||||
@@ -1461,6 +1483,8 @@ ccid_handle_data (struct ccid *c)
|
||||
}
|
||||
break;
|
||||
case CCID_STATE_EXECUTE:
|
||||
case CCID_STATE_ACK_REQUIRED_0:
|
||||
case CCID_STATE_ACK_REQUIRED_1:
|
||||
if (c->ccid_header.msg_type == CCID_POWER_OFF)
|
||||
next_state = ccid_power_off (c);
|
||||
else if (c->ccid_header.msg_type == CCID_SLOT_STATUS)
|
||||
@@ -1489,6 +1513,8 @@ ccid_handle_timeout (struct ccid *c)
|
||||
switch (c->ccid_state)
|
||||
{
|
||||
case CCID_STATE_EXECUTE:
|
||||
case CCID_STATE_ACK_REQUIRED_0:
|
||||
case CCID_STATE_ACK_REQUIRED_1:
|
||||
ccid_send_data_block_time_extension (c);
|
||||
break;
|
||||
default:
|
||||
@@ -1500,7 +1526,13 @@ ccid_handle_timeout (struct ccid *c)
|
||||
}
|
||||
|
||||
static struct ccid ccid;
|
||||
enum ccid_state *const ccid_state_p = &ccid.ccid_state;
|
||||
|
||||
enum ccid_state
|
||||
ccid_get_ccid_state (void)
|
||||
{
|
||||
return ccid.ccid_state;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccid_card_change_signal (int how)
|
||||
@@ -1561,6 +1593,10 @@ extern int usb_get_descriptor (struct usb_dev *dev);
|
||||
extern void random_init (void);
|
||||
extern void random_fini (void);
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
static chopstx_intr_t ack_intr;
|
||||
#endif
|
||||
static chopstx_intr_t usb_intr;
|
||||
|
||||
/*
|
||||
* Return 0 for normal USB event
|
||||
@@ -1575,6 +1611,7 @@ usb_event_handle (struct usb_dev *dev)
|
||||
|
||||
e = usb_lld_event_handler (dev);
|
||||
ep_num = USB_EVENT_ENDP (e);
|
||||
chopstx_intr_done (&usb_intr);
|
||||
|
||||
/* Transfer to endpoint (not control endpoint) */
|
||||
if (ep_num != 0)
|
||||
@@ -1674,11 +1711,13 @@ usb_event_handle (struct usb_dev *dev)
|
||||
}
|
||||
|
||||
|
||||
static chopstx_intr_t interrupt;
|
||||
static chopstx_poll_cond_t ccid_event_poll_desc;
|
||||
static struct chx_poll_head *const ccid_poll[] = {
|
||||
(struct chx_poll_head *const)&interrupt,
|
||||
(struct chx_poll_head *const)&ccid_event_poll_desc
|
||||
(struct chx_poll_head *const)&usb_intr,
|
||||
(struct chx_poll_head *const)&ccid_event_poll_desc,
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
(struct chx_poll_head *const)&ack_intr
|
||||
#endif
|
||||
};
|
||||
#define CCID_POLL_NUM (sizeof (ccid_poll)/sizeof (struct chx_poll_head *))
|
||||
|
||||
@@ -1696,9 +1735,12 @@ ccid_thread (void *arg)
|
||||
eventflag_init (&ccid.openpgp_comm);
|
||||
|
||||
usb_lld_init (&dev, USB_INITIAL_FEATURE);
|
||||
chopstx_claim_irq (&interrupt, INTR_REQ_USB);
|
||||
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
|
||||
usb_event_handle (&dev); /* For old SYS < 3.0 */
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
ackbtn_init (&ack_intr);
|
||||
#endif
|
||||
eventflag_prepare_poll (&c->ccid_comm, &ccid_event_poll_desc);
|
||||
|
||||
reset:
|
||||
@@ -1724,14 +1766,20 @@ ccid_thread (void *arg)
|
||||
{
|
||||
eventmask_t m;
|
||||
|
||||
if (bDeviceState == USB_DEVICE_STATE_CONFIGURED)
|
||||
if (!c->tx_busy && bDeviceState == USB_DEVICE_STATE_CONFIGURED)
|
||||
timeout_p = &timeout;
|
||||
else
|
||||
timeout_p = NULL;
|
||||
|
||||
chopstx_poll (timeout_p, CCID_POLL_NUM, ccid_poll);
|
||||
eventflag_set_mask (&c->ccid_comm, c->tx_busy ? EV_TX_FINISHED : ~0);
|
||||
|
||||
if (interrupt.ready)
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
chopstx_poll (timeout_p, CCID_POLL_NUM - c->tx_busy, ccid_poll);
|
||||
#else
|
||||
chopstx_poll (timeout_p, CCID_POLL_NUM, ccid_poll);
|
||||
#endif
|
||||
|
||||
if (usb_intr.ready)
|
||||
{
|
||||
if (usb_event_handle (&dev) == 0)
|
||||
continue;
|
||||
@@ -1751,6 +1799,20 @@ ccid_thread (void *arg)
|
||||
goto reset;
|
||||
}
|
||||
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
if (ack_intr.ready)
|
||||
{
|
||||
ackbtn_disable ();
|
||||
chopstx_intr_done (&ack_intr);
|
||||
led_blink (LED_WAIT_FOR_BUTTON);
|
||||
if (c->ccid_state == CCID_STATE_ACK_REQUIRED_1)
|
||||
goto exec_done;
|
||||
|
||||
c->ccid_state = CCID_STATE_EXECUTE;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
timeout = USB_CCID_TIMEOUT;
|
||||
m = eventflag_get (&c->ccid_comm);
|
||||
|
||||
@@ -1778,6 +1840,9 @@ ccid_thread (void *arg)
|
||||
else if (m == EV_EXEC_FINISHED)
|
||||
if (c->ccid_state == CCID_STATE_EXECUTE)
|
||||
{
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
exec_done:
|
||||
#endif
|
||||
if (c->a->sw == GPG_THREAD_TERMINATED)
|
||||
{
|
||||
c->sw1sw2[0] = 0x90;
|
||||
@@ -1807,21 +1872,34 @@ ccid_thread (void *arg)
|
||||
c->ccid_state = CCID_STATE_WAIT;
|
||||
}
|
||||
}
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
else if (c->ccid_state == CCID_STATE_ACK_REQUIRED_0)
|
||||
c->ccid_state = CCID_STATE_ACK_REQUIRED_1;
|
||||
#endif
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("ERR07\r\n");
|
||||
DEBUG_INFO ("ERR05\r\n");
|
||||
}
|
||||
#ifdef ACKBTN_SUPPORT
|
||||
else if (m == EV_EXEC_ACK_REQUIRED)
|
||||
if (c->ccid_state == CCID_STATE_EXECUTE)
|
||||
{
|
||||
ackbtn_enable ();
|
||||
led_blink (LED_WAIT_FOR_BUTTON);
|
||||
c->ccid_state = CCID_STATE_ACK_REQUIRED_0;
|
||||
ccid_send_data_block_time_extension (c);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_INFO ("ERR06\r\n");
|
||||
}
|
||||
#endif
|
||||
else if (m == EV_TX_FINISHED)
|
||||
{
|
||||
if (c->state == APDU_STATE_RESULT)
|
||||
{
|
||||
c->state = APDU_STATE_WAIT_COMMAND;
|
||||
c->p = c->a->cmd_apdu_data;
|
||||
c->len = MAX_CMD_APDU_DATA_SIZE;
|
||||
c->err = 0;
|
||||
c->a->cmd_apdu_data_len = 0;
|
||||
c->a->expected_res_size = 0;
|
||||
}
|
||||
ccid_reset (c);
|
||||
else
|
||||
c->tx_busy = 0;
|
||||
|
||||
if (c->state == APDU_STATE_WAIT_COMMAND
|
||||
|| c->state == APDU_STATE_COMMAND_CHAINING
|
||||
@@ -1841,7 +1919,7 @@ ccid_thread (void *arg)
|
||||
/* Loading reGNUal. */
|
||||
while (bDeviceState != USB_DEVICE_STATE_UNCONNECTED)
|
||||
{
|
||||
chopstx_intr_wait (&interrupt);
|
||||
chopstx_intr_wait (&usb_intr);
|
||||
usb_event_handle (&dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* usb_ctrl.c - USB control pipe device specific code for Gnuk
|
||||
*
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2015, 2016, 2017
|
||||
* Copyright (C) 2010, 2011, 2012, 2013, 2015, 2016, 2017, 2018
|
||||
* Free Software Initiative of Japan
|
||||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
*
|
||||
@@ -202,8 +202,6 @@ gnuk_setup_endpoints_for_interface (struct usb_dev *dev,
|
||||
void
|
||||
usb_device_reset (struct usb_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
usb_lld_reset (dev, USB_INITIAL_FEATURE);
|
||||
|
||||
/* Initialize Endpoint 0 */
|
||||
@@ -214,10 +212,6 @@ usb_device_reset (struct usb_dev *dev)
|
||||
64);
|
||||
#endif
|
||||
|
||||
/* Stop the interface */
|
||||
for (i = 0; i < NUM_INTERFACES; i++)
|
||||
gnuk_setup_endpoints_for_interface (dev, i, 1);
|
||||
|
||||
bDeviceState = USB_DEVICE_STATE_DEFAULT;
|
||||
}
|
||||
|
||||
@@ -287,7 +281,7 @@ usb_setup (struct usb_dev *dev)
|
||||
if (arg->request == USB_FSIJ_GNUK_DOWNLOAD)
|
||||
{
|
||||
#ifdef FLASH_UPGRADE_SUPPORT
|
||||
if (*ccid_state_p != CCID_STATE_EXITED)
|
||||
if (ccid_get_ccid_state () != CCID_STATE_EXITED)
|
||||
return -1;
|
||||
|
||||
if (addr < &_regnual_start || addr + arg->len > __heap_end__)
|
||||
@@ -305,7 +299,7 @@ usb_setup (struct usb_dev *dev)
|
||||
else if (arg->request == USB_FSIJ_GNUK_EXEC && arg->len == 0)
|
||||
{
|
||||
#ifdef FLASH_UPGRADE_SUPPORT
|
||||
if (*ccid_state_p != CCID_STATE_EXITED)
|
||||
if (ccid_get_ccid_state () != CCID_STATE_EXITED)
|
||||
return -1;
|
||||
|
||||
if (((uintptr_t)addr & 0x03))
|
||||
@@ -410,7 +404,7 @@ usb_ctrl_write_finish (struct usb_dev *dev)
|
||||
{
|
||||
if (USB_SETUP_SET (arg->type) && arg->request == USB_FSIJ_GNUK_EXEC)
|
||||
{
|
||||
if (*ccid_state_p != CCID_STATE_EXITED)
|
||||
if (ccid_get_ccid_state () != CCID_STATE_EXITED)
|
||||
return;
|
||||
|
||||
bDeviceState = USB_DEVICE_STATE_UNCONNECTED;
|
||||
|
||||
@@ -6,7 +6,7 @@ Gnuk Token is supported as well.
|
||||
|
||||
You need to install:
|
||||
|
||||
$ sudo apt-get install python3-pytest python3-usb
|
||||
$ sudo apt install python3-pytest python3-usb python3-cffi
|
||||
|
||||
Please run test by typing:
|
||||
|
||||
|
||||
4
tests/card_const.py
Normal file
4
tests/card_const.py
Normal file
@@ -0,0 +1,4 @@
|
||||
FACTORY_PASSPHRASE_PW1=b"123456"
|
||||
FACTORY_PASSPHRASE_PW3=b"12345678"
|
||||
KEY_ATTRIBUTES_RSA4K=b"\x01\x10\x00\x00\x20\x00"
|
||||
KEY_ATTRIBUTES_RSA2K=b"\x01\x08\x00\x00\x20\x00"
|
||||
21
tests/constants_for_test.py
Normal file
21
tests/constants_for_test.py
Normal file
@@ -0,0 +1,21 @@
|
||||
PW1_TEST0=b"another user pass phrase"
|
||||
PW1_TEST1=b"PASSPHRASE SHOULD BE LONG"
|
||||
PW1_TEST2=b"new user pass phrase"
|
||||
PW1_TEST3=b"next user pass phrase"
|
||||
PW1_TEST4=b"another user pass phrase"
|
||||
PW3_TEST0=b"admin pass phrase"
|
||||
PW3_TEST1=b"another admin pass phrase"
|
||||
|
||||
RESETCODE_TEST=b"example reset code 000"
|
||||
|
||||
PLAIN_TEXT0=b"This is a test message."
|
||||
PLAIN_TEXT1=b"RSA decryption is as easy as pie."
|
||||
PLAIN_TEXT2=b"This is another test message.\nMultiple lines.\n"
|
||||
|
||||
KDF_FULL=b"\x81\x01\x03\x82\x01\x08\x83\x04\x00\xC8\x00\x00\x84\x08\xC5\x1F\xB7\xF9\xEE\xF3\xD3\xE8\x85\x08\x75\x1A\x2A\x70\xC0\x7C\xB1\x81\x86\x08\xE6\xB2\x4E\x0C\xEE\x92\xAB\x93\x87\x20\xA2\x08\x89\x16\x09\xD3\xE4\x3D\x48\xAC\x5B\xAA\xBD\xE9\xF1\xB8\xD2\xFC\xD8\x3C\x5F\x35\xCB\xEB\xDA\xFB\x75\x92\x88\xC6\x8B\x2D\x88\x20\xF1\x34\x00\xD2\x5F\x28\x57\xE6\x66\xAA\x9E\xBB\xE0\x7C\x57\x4B\x84\x8B\xD6\x52\x14\xE7\x31\x99\x1A\x3D\x2D\xAA\x3F\x8A\x7F\x03"
|
||||
KDF_FULL_HASH_PW1=b"\xA2\x08\x89\x16\x09\xD3\xE4\x3D\x48\xAC\x5B\xAA\xBD\xE9\xF1\xB8\xD2\xFC\xD8\x3C\x5F\x35\xCB\xEB\xDA\xFB\x75\x92\x88\xC6\x8B\x2D"
|
||||
KDF_FULL_HASH_PW3=b"\xF1\x34\x00\xD2\x5F\x28\x57\xE6\x66\xAA\x9E\xBB\xE0\x7C\x57\x4B\x84\x8B\xD6\x52\x14\xE7\x31\x99\x1A\x3D\x2D\xAA\x3F\x8A\x7F\x03"
|
||||
|
||||
KDF_SINGLE=b"\x81\x01\x03\x82\x01\x08\x83\x04\x00\xC8\x00\x00\x84\x08\x69\x9E\xDA\xAD\x5A\x72\x5F\x4C\x87\x20\x12\xDB\x16\x9A\x9D\x1A\x56\xCA\x2B\x9E\x96\x7F\xC4\xDA\xB8\xAE\x70\x41\x51\x8E\x3C\xEF\x49\x7E\xC2\x56\x60\x32\x2F\x4C\x03\x07\x88\x20\x1C\x93\x99\xC3\x6D\x49\x92\x27\x54\x39\x76\x7E\xA7\xB4\xEE\xE5\x16\xDA\x92\xC0\x0E\xF4\x74\xBD\x01\x28\x0F\x0C\x30\x45\x4B\xBB"
|
||||
KDF_SINGLE_HASH_PW1=b"\x12\xDB\x16\x9A\x9D\x1A\x56\xCA\x2B\x9E\x96\x7F\xC4\xDA\xB8\xAE\x70\x41\x51\x8E\x3C\xEF\x49\x7E\xC2\x56\x60\x32\x2F\x4C\x03\x07"
|
||||
KDF_SINGLE_HASH_PW3=b"\x1C\x93\x99\xC3\x6D\x49\x92\x27\x54\x39\x76\x7E\xA7\xB4\xEE\xE5\x16\xDA\x92\xC0\x0E\xF4\x74\xBD\x01\x28\x0F\x0C\x30\x45\x4B\xBB"
|
||||
1
tests/kdf_calc.py
Symbolic link
1
tests/kdf_calc.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../tool/kdf_calc.py
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
openpgp_card.py - a library for OpenPGP card
|
||||
|
||||
Copyright (C) 2011, 2012, 2013, 2015, 2016
|
||||
Copyright (C) 2011, 2012, 2013, 2015, 2016, 2018
|
||||
Free Software Initiative of Japan
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
@@ -21,7 +21,8 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from struct import pack
|
||||
from struct import pack, unpack
|
||||
from kdf_calc import kdf_calc
|
||||
|
||||
def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
|
||||
data_len = len(data)
|
||||
@@ -54,6 +55,83 @@ class OpenPGP_Card(object):
|
||||
"""
|
||||
|
||||
self.__reader = reader
|
||||
self.__kdf_iters = None
|
||||
self.__kdf_salt_user = None
|
||||
self.__kdf_salt_reset = None
|
||||
self.__kdf_salt_admin = None
|
||||
|
||||
def configure_with_kdf(self):
|
||||
kdf_data = self.cmd_get_data(0x00, 0xf9)
|
||||
if kdf_data != b"":
|
||||
algo, subalgo, iters, salt_user, salt_reset, salt_admin, hash_user, hash_admin = parse_kdf_data(kdf_data)
|
||||
self.__kdf_iters = iters
|
||||
self.__kdf_salt_user = salt_user
|
||||
self.__kdf_salt_reset = salt_reset
|
||||
self.__kdf_salt_admin = salt_admin
|
||||
else:
|
||||
self.__kdf_iters = None
|
||||
self.__kdf_salt_user = None
|
||||
self.__kdf_salt_reset = None
|
||||
self.__kdf_salt_admin = None
|
||||
|
||||
# Higher layer VERIFY possibly using KDF Data Object
|
||||
def verify(self, who, passwd):
|
||||
if self.__kdf_iters:
|
||||
salt = self.__kdf_salt_user
|
||||
if who == 3 and self.__kdf_salt_admin:
|
||||
salt = self.__kdf_salt_admin
|
||||
pw_hash = kdf_calc(passwd, salt, self.__kdf_iters)
|
||||
return self.cmd_verify(who, pw_hash)
|
||||
else:
|
||||
return self.cmd_verify(who, passwd)
|
||||
|
||||
# Higher layer CHANGE_PASSWD possibly using KDF Data Object
|
||||
def change_passwd(self, who, passwd_old, passwd_new):
|
||||
if self.__kdf_iters:
|
||||
salt = self.__kdf_salt_user
|
||||
if who == 3 and self.__kdf_salt_admin:
|
||||
salt = self.__kdf_salt_admin
|
||||
hash_old = kdf_calc(passwd_old, salt, self.__kdf_iters)
|
||||
if passwd_new:
|
||||
hash_new = kdf_calc(passwd_new, salt, self.__kdf_iters)
|
||||
else:
|
||||
hash_new = b""
|
||||
return self.cmd_change_reference_data(who, hash_old + hash_new)
|
||||
else:
|
||||
if not passwd_new:
|
||||
passwd_new = b""
|
||||
return self.cmd_change_reference_data(who, passwd_old + passwd_new)
|
||||
|
||||
# Higher layer SETUP_RESET_CODE possibly using KDF Data Object
|
||||
def setup_reset_code(self, resetcode):
|
||||
if self.__kdf_iters:
|
||||
salt = self.__kdf_salt_user
|
||||
if self.__kdf_salt_reset:
|
||||
salt = self.__kdf_salt_user
|
||||
reset_hash = kdf_calc(resetcode, salt, self.__kdf_iters)
|
||||
return self.cmd_put_data(0x00, 0xd3, reset_hash)
|
||||
else:
|
||||
return self.cmd_put_data(0x00, 0xd3, resetcode)
|
||||
|
||||
# Higher layer reset passwd possibly using KDF Data Object
|
||||
def reset_passwd_by_resetcode(self, resetcode, pw1):
|
||||
if self.__kdf_iters:
|
||||
salt = self.__kdf_salt_user
|
||||
if self.__kdf_salt_reset:
|
||||
salt = self.__kdf_salt_user
|
||||
reset_hash = kdf_calc(resetcode, salt, self.__kdf_iters)
|
||||
pw1_hash = kdf_calc(pw1, self.__kdf_salt_user, self.__kdf_iters)
|
||||
return self.cmd_reset_retry_counter(0, 0x81, reset_hash + pw1_hash)
|
||||
else:
|
||||
return self.cmd_reset_retry_counter(0, 0x81, resetcode + pw1)
|
||||
|
||||
# Higher layer reset passwd possibly using KDF Data Object
|
||||
def reset_passwd_by_admin(self, pw1):
|
||||
if self.__kdf_iters:
|
||||
pw1_hash = kdf_calc(pw1, self.__kdf_salt_user, self.__kdf_iters)
|
||||
return self.cmd_reset_retry_counter(2, 0x81, pw1_hash)
|
||||
else:
|
||||
return self.cmd_reset_retry_counter(2, 0x81, pw1)
|
||||
|
||||
def cmd_get_response(self, expected_len):
|
||||
result = b""
|
||||
@@ -336,3 +414,48 @@ class OpenPGP_Card(object):
|
||||
raise ValueError(sw)
|
||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||
|
||||
def parse_kdf_data(kdf_data):
|
||||
if len(kdf_data) == 90:
|
||||
single_salt = True
|
||||
elif len(kdf_data) == 110:
|
||||
single_salt = False
|
||||
else:
|
||||
raise ValueError("length does not much", kdf_data)
|
||||
|
||||
if kdf_data[0:2] != b'\x81\x01':
|
||||
raise ValueError("data does not much")
|
||||
algo = kdf_data[2]
|
||||
if kdf_data[3:5] != b'\x82\x01':
|
||||
raise ValueError("data does not much")
|
||||
subalgo = kdf_data[5]
|
||||
if kdf_data[6:8] != b'\x83\x04':
|
||||
raise ValueError("data does not much")
|
||||
iters = unpack(">I", kdf_data[8:12])[0]
|
||||
if kdf_data[12:14] != b'\x84\x08':
|
||||
raise ValueError("data does not much")
|
||||
salt = kdf_data[14:22]
|
||||
if single_salt:
|
||||
salt_reset = None
|
||||
salt_admin = None
|
||||
if kdf_data[22:24] != b'\x87\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_user = kdf_data[24:56]
|
||||
if kdf_data[56:58] != b'\x88\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_admin = kdf_data[58:90]
|
||||
else:
|
||||
if kdf_data[22:24] != b'\x85\x08':
|
||||
raise ValueError("data does not much")
|
||||
salt_reset = kdf_data[24:32]
|
||||
if kdf_data[32:34] != b'\x86\x08':
|
||||
raise ValueError("data does not much")
|
||||
salt_admin = kdf_data[34:42]
|
||||
if kdf_data[42:44] != b'\x87\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_user = kdf_data[44:76]
|
||||
if kdf_data[76:78] != b'\x88\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_admin = kdf_data[78:110]
|
||||
return ( algo, subalgo, iters, salt, salt_reset, salt_admin,
|
||||
hash_user, hash_admin )
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
test_empty_card.py - test empty card
|
||||
|
||||
Copyright (C) 2016 g10 Code GmbH
|
||||
Copyright (C) 2016, 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -24,13 +24,11 @@ from binascii import hexlify
|
||||
from re import match, DOTALL
|
||||
from struct import pack
|
||||
from util import *
|
||||
from card_const import *
|
||||
import pytest
|
||||
|
||||
EMPTY_60=bytes(60)
|
||||
|
||||
FACTORY_PASSPHRASE_PW1=b"123456"
|
||||
FACTORY_PASSPHRASE_PW3=b"12345678"
|
||||
|
||||
def test_login(card):
|
||||
login = get_data_object(card, 0x5e)
|
||||
assert check_null(login)
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
test_personalize_card.py - test personalizing card
|
||||
|
||||
Copyright (C) 2016 g10 Code GmbH
|
||||
Copyright (C) 2016, 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -24,25 +24,15 @@ from struct import pack
|
||||
from re import match, DOTALL
|
||||
from util import *
|
||||
import rsa_keys
|
||||
|
||||
FACTORY_PASSPHRASE_PW1=b"123456"
|
||||
FACTORY_PASSPHRASE_PW3=b"12345678"
|
||||
PW1_TEST0=b"another user pass phrase"
|
||||
PW1_TEST1=b"PASSPHRASE SHOULD BE LONG"
|
||||
PW1_TEST2=b"new user pass phrase"
|
||||
PW1_TEST3=b"next user pass phrase"
|
||||
PW1_TEST4=b"another user pass phrase"
|
||||
PW3_TEST0=b"admin pass phrase"
|
||||
PW3_TEST1=b"another admin pass phrase"
|
||||
|
||||
RESETCODE_TEST=b"example reset code 000"
|
||||
from card_const import *
|
||||
from constants_for_test import *
|
||||
|
||||
def test_setup_pw3_0(card):
|
||||
r = card.cmd_change_reference_data(3, FACTORY_PASSPHRASE_PW3 + PW3_TEST0)
|
||||
r = card.change_passwd(3, FACTORY_PASSPHRASE_PW3, PW3_TEST0)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_0(card):
|
||||
v = card.cmd_verify(3, PW3_TEST0)
|
||||
v = card.verify(3, PW3_TEST0)
|
||||
assert v
|
||||
|
||||
def test_login_put(card):
|
||||
@@ -164,89 +154,85 @@ def test_public_key_3(card):
|
||||
assert rsa_keys.key[2][0] == pk[9:9+256]
|
||||
|
||||
def test_setup_pw1_0(card):
|
||||
r = card.cmd_change_reference_data(1, FACTORY_PASSPHRASE_PW1 + PW1_TEST0)
|
||||
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_0(card):
|
||||
v = card.cmd_verify(1, PW1_TEST0)
|
||||
v = card.verify(1, PW1_TEST0)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_0_2(card):
|
||||
v = card.cmd_verify(2, PW1_TEST0)
|
||||
v = card.verify(2, PW1_TEST0)
|
||||
assert v
|
||||
|
||||
def test_setup_pw1_1(card):
|
||||
r = card.cmd_change_reference_data(1, PW1_TEST0 + PW1_TEST1)
|
||||
r = card.change_passwd(1, PW1_TEST0, PW1_TEST1)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_1(card):
|
||||
v = card.cmd_verify(1, PW1_TEST1)
|
||||
v = card.verify(1, PW1_TEST1)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_1_2(card):
|
||||
v = card.cmd_verify(2, PW1_TEST1)
|
||||
v = card.verify(2, PW1_TEST1)
|
||||
assert v
|
||||
|
||||
def test_setup_reset_code(card):
|
||||
r = card.cmd_put_data(0x00, 0xd3, RESETCODE_TEST)
|
||||
r = card.setup_reset_code(RESETCODE_TEST)
|
||||
assert r
|
||||
|
||||
def test_reset_code(card):
|
||||
r = card.cmd_reset_retry_counter(0, 0x81, RESETCODE_TEST + PW1_TEST2)
|
||||
r = card.reset_passwd_by_resetcode(RESETCODE_TEST, PW1_TEST2)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_2(card):
|
||||
v = card.cmd_verify(1, PW1_TEST2)
|
||||
v = card.verify(1, PW1_TEST2)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_2_2(card):
|
||||
v = card.cmd_verify(2, PW1_TEST2)
|
||||
v = card.verify(2, PW1_TEST2)
|
||||
assert v
|
||||
|
||||
def test_setup_pw3_1(card):
|
||||
r = card.cmd_change_reference_data(3, PW3_TEST0 + PW3_TEST1)
|
||||
r = card.change_passwd(3, PW3_TEST0, PW3_TEST1)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_1(card):
|
||||
v = card.cmd_verify(3, PW3_TEST1)
|
||||
v = card.verify(3, PW3_TEST1)
|
||||
assert v
|
||||
|
||||
def test_reset_userpass_admin(card):
|
||||
r = card.cmd_reset_retry_counter(2, 0x81, PW1_TEST3)
|
||||
r = card.reset_passwd_by_admin(PW1_TEST3)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_3(card):
|
||||
v = card.cmd_verify(1, PW1_TEST3)
|
||||
v = card.verify(1, PW1_TEST3)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_3_2(card):
|
||||
v = card.cmd_verify(2, PW1_TEST3)
|
||||
v = card.verify(2, PW1_TEST3)
|
||||
assert v
|
||||
|
||||
def test_setup_pw1_4(card):
|
||||
r = card.cmd_change_reference_data(1, PW1_TEST3 + PW1_TEST4)
|
||||
r = card.change_passwd(1, PW1_TEST3, PW1_TEST4)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_4(card):
|
||||
v = card.cmd_verify(1, PW1_TEST4)
|
||||
v = card.verify(1, PW1_TEST4)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_4_2(card):
|
||||
v = card.cmd_verify(2, PW1_TEST4)
|
||||
v = card.verify(2, PW1_TEST4)
|
||||
assert v
|
||||
|
||||
def test_setup_pw3_2(card):
|
||||
r = card.cmd_change_reference_data(3, PW3_TEST1 + PW3_TEST0)
|
||||
r = card.change_passwd(3, PW3_TEST1, PW3_TEST0)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_2(card):
|
||||
v = card.cmd_verify(3, PW3_TEST0)
|
||||
v = card.verify(3, PW3_TEST0)
|
||||
assert v
|
||||
|
||||
PLAIN_TEXT0=b"This is a test message."
|
||||
PLAIN_TEXT1=b"RSA decryption is as easy as pie."
|
||||
PLAIN_TEXT2=b"This is another test message.\nMultiple lines.\n"
|
||||
|
||||
def test_sign_0(card):
|
||||
digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0)
|
||||
r = card.cmd_pso(0x9e, 0x9a, digestinfo)
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
test_personalize_reset_card.py - test resetting personalization of card
|
||||
test_personalize_reset.py - test resetting personalization of card
|
||||
|
||||
Copyright (C) 2016 g10 Code GmbH
|
||||
Copyright (C) 2016, 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -24,18 +24,8 @@ from struct import pack
|
||||
from re import match, DOTALL
|
||||
from util import *
|
||||
import rsa_keys
|
||||
|
||||
FACTORY_PASSPHRASE_PW1=b"123456"
|
||||
FACTORY_PASSPHRASE_PW3=b"12345678"
|
||||
PW1_TEST0=b"another user pass phrase"
|
||||
PW1_TEST1=b"PASSPHRASE SHOULD BE LONG"
|
||||
PW1_TEST2=b"new user pass phrase"
|
||||
PW1_TEST3=b"next user pass phrase"
|
||||
PW1_TEST4=b"another user pass phrase"
|
||||
PW3_TEST0=b"admin pass phrase"
|
||||
PW3_TEST1=b"another admin pass phrase"
|
||||
|
||||
RESETCODE_TEST=b"example reset code 000"
|
||||
from card_const import *
|
||||
from constants_for_test import *
|
||||
|
||||
def test_login_put(card):
|
||||
r = card.cmd_put_data(0x00, 0x5e, b"")
|
||||
@@ -67,25 +57,25 @@ def test_pw1_status_put(card):
|
||||
assert r
|
||||
|
||||
def test_setup_pw3_0(card):
|
||||
r = card.cmd_change_reference_data(3, PW3_TEST0 + FACTORY_PASSPHRASE_PW3)
|
||||
r = card.change_passwd(3, PW3_TEST0, FACTORY_PASSPHRASE_PW3)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_0(card):
|
||||
v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
|
||||
def test_setup_pw1_0(card):
|
||||
r = card.cmd_change_reference_data(1, PW1_TEST4 + FACTORY_PASSPHRASE_PW1)
|
||||
r = card.change_passwd(1, PW1_TEST4, FACTORY_PASSPHRASE_PW1)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_0(card):
|
||||
v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1)
|
||||
v = card.verify(1, FACTORY_PASSPHRASE_PW1)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_0_2(card):
|
||||
v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1)
|
||||
v = card.verify(2, FACTORY_PASSPHRASE_PW1)
|
||||
assert v
|
||||
|
||||
def test_setup_reset_code(card):
|
||||
def test_delete_reset_code(card):
|
||||
r = card.cmd_put_data(0x00, 0xd3, b"")
|
||||
assert r
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
test_remove_keys_card.py - test removing keys on card
|
||||
test_remove_keys.py - test removing keys on card
|
||||
|
||||
Copyright (C) 2016 g10 Code GmbH
|
||||
Copyright (C) 2016, 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -22,22 +22,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Remove a key material on card by changing algorithm attributes of the key
|
||||
|
||||
KEY_ATTRIBUTES_RSA4K=b"\x01\x10\x00\x00\x20\x00"
|
||||
KEY_ATTRIBUTES_RSA2K=b"\x01\x08\x00\x00\x20\x00"
|
||||
from card_const import *
|
||||
|
||||
def test_rsa_import_key_1(card):
|
||||
def test_rsa_keyattr_change_1(card):
|
||||
r = card.cmd_put_data(0x00, 0xc1, KEY_ATTRIBUTES_RSA4K)
|
||||
if r:
|
||||
r = card.cmd_put_data(0x00, 0xc1, KEY_ATTRIBUTES_RSA2K)
|
||||
assert r
|
||||
|
||||
def test_rsa_import_key_2(card):
|
||||
def test_rsa_keyattr_change_2(card):
|
||||
r = card.cmd_put_data(0x00, 0xc2, KEY_ATTRIBUTES_RSA4K)
|
||||
if r:
|
||||
r = card.cmd_put_data(0x00, 0xc2, KEY_ATTRIBUTES_RSA2K)
|
||||
assert r
|
||||
|
||||
def test_rsa_import_key_3(card):
|
||||
def test_rsa_keyattr_change_3(card):
|
||||
r = card.cmd_put_data(0x00, 0xc3, KEY_ATTRIBUTES_RSA4K)
|
||||
if r:
|
||||
r = card.cmd_put_data(0x00, 0xc3, KEY_ATTRIBUTES_RSA2K)
|
||||
41
tests/test_004_reset_pw3.py
Normal file
41
tests/test_004_reset_pw3.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
test_004_reset_pw3.py - test resetting pw3
|
||||
|
||||
Copyright (C) 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from card_const import *
|
||||
|
||||
# Gnuk specific feature of clear PW3
|
||||
def test_setup_pw3_null(card):
|
||||
r = card.change_passwd(3, FACTORY_PASSPHRASE_PW3, None)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3(card):
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
|
||||
# Check PW1 again to see the possiblity of admin-less mode
|
||||
def test_verify_pw1(card):
|
||||
v = card.verify(1, FACTORY_PASSPHRASE_PW1)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_2(card):
|
||||
v = card.verify(2, FACTORY_PASSPHRASE_PW1)
|
||||
assert v
|
||||
310
tests/test_005_personalize_admin_less.py
Normal file
310
tests/test_005_personalize_admin_less.py
Normal file
@@ -0,0 +1,310 @@
|
||||
"""
|
||||
test_005_personalize_admin_less.py - test admin-less mode
|
||||
|
||||
Copyright (C) 2016, 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from struct import pack
|
||||
from re import match, DOTALL
|
||||
from util import *
|
||||
import rsa_keys
|
||||
from card_const import *
|
||||
from constants_for_test import *
|
||||
|
||||
def test_verify_pw3_0(card):
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
|
||||
def test_rsa_import_key_1(card):
|
||||
t = rsa_keys.build_privkey_template(1, 0)
|
||||
r = card.cmd_put_data_odd(0x3f, 0xff, t)
|
||||
assert r
|
||||
|
||||
def test_rsa_import_key_2(card):
|
||||
t = rsa_keys.build_privkey_template(2, 1)
|
||||
r = card.cmd_put_data_odd(0x3f, 0xff, t)
|
||||
assert r
|
||||
|
||||
def test_rsa_import_key_3(card):
|
||||
t = rsa_keys.build_privkey_template(3, 2)
|
||||
r = card.cmd_put_data_odd(0x3f, 0xff, t)
|
||||
assert r
|
||||
|
||||
def test_fingerprint_1_put(card):
|
||||
fpr1 = rsa_keys.fpr[0]
|
||||
r = card.cmd_put_data(0x00, 0xc7, fpr1)
|
||||
assert r
|
||||
|
||||
def test_fingerprint_2_put(card):
|
||||
fpr2 = rsa_keys.fpr[1]
|
||||
r = card.cmd_put_data(0x00, 0xc8, fpr2)
|
||||
assert r
|
||||
|
||||
def test_fingerprint_3_put(card):
|
||||
fpr3 = rsa_keys.fpr[2]
|
||||
r = card.cmd_put_data(0x00, 0xc9, fpr3)
|
||||
assert r
|
||||
|
||||
def test_timestamp_1_put(card):
|
||||
timestamp1 = rsa_keys.timestamp[0]
|
||||
r = card.cmd_put_data(0x00, 0xce, timestamp1)
|
||||
assert r
|
||||
|
||||
def test_timestamp_2_put(card):
|
||||
timestamp2 = rsa_keys.timestamp[1]
|
||||
r = card.cmd_put_data(0x00, 0xcf, timestamp2)
|
||||
assert r
|
||||
|
||||
def test_timestamp_3_put(card):
|
||||
timestamp3 = rsa_keys.timestamp[2]
|
||||
r = card.cmd_put_data(0x00, 0xd0, timestamp3)
|
||||
assert r
|
||||
|
||||
def test_ds_counter_0(card):
|
||||
c = get_data_object(card, 0x7a)
|
||||
assert c == b'\x93\x03\x00\x00\x00'
|
||||
|
||||
def test_pw1_status(card):
|
||||
s = get_data_object(card, 0xc4)
|
||||
assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL)
|
||||
|
||||
def test_app_data(card):
|
||||
app_data = get_data_object(card, 0x6e)
|
||||
hist_len = app_data[20]
|
||||
# FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6
|
||||
assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \
|
||||
app_data[18:18+2] == b"\x5f\x52"
|
||||
|
||||
def test_public_key_1(card):
|
||||
pk = card.cmd_get_public_key(1)
|
||||
assert rsa_keys.key[0][0] == pk[9:9+256]
|
||||
|
||||
def test_public_key_2(card):
|
||||
pk = card.cmd_get_public_key(2)
|
||||
assert rsa_keys.key[1][0] == pk[9:9+256]
|
||||
|
||||
def test_public_key_3(card):
|
||||
pk = card.cmd_get_public_key(3)
|
||||
assert rsa_keys.key[2][0] == pk[9:9+256]
|
||||
|
||||
# Changing PW1 to admin-less mode
|
||||
|
||||
def test_setup_pw1_0(card):
|
||||
r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0)
|
||||
assert r
|
||||
|
||||
# Now, it's admin-less mode, auth-status admin cleared
|
||||
|
||||
def test_verify_pw3_fail_1(card):
|
||||
try:
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
except ValueError as e:
|
||||
v = False
|
||||
assert not v
|
||||
|
||||
def test_verify_pw1_0(card):
|
||||
v = card.verify(1, PW1_TEST0)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_0_2(card):
|
||||
v = card.verify(2, PW1_TEST0)
|
||||
assert v
|
||||
|
||||
def test_setup_pw1_1(card):
|
||||
r = card.change_passwd(1, PW1_TEST0, PW1_TEST1)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_1(card):
|
||||
v = card.verify(1, PW1_TEST1)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_1_2(card):
|
||||
v = card.verify(2, PW1_TEST1)
|
||||
assert v
|
||||
|
||||
def test_verify_pw3_admin_less_1(card):
|
||||
v = card.verify(3, PW1_TEST1)
|
||||
assert v
|
||||
|
||||
def test_setup_reset_code(card):
|
||||
r = card.setup_reset_code(RESETCODE_TEST)
|
||||
assert r
|
||||
|
||||
def test_reset_code(card):
|
||||
r = card.reset_passwd_by_resetcode(RESETCODE_TEST, PW1_TEST2)
|
||||
assert r
|
||||
|
||||
# Changing PW1, auth status for admin cleared
|
||||
def test_login_put_fail(card):
|
||||
try:
|
||||
r = card.cmd_put_data(0x00, 0x5e, b"gpg_user")
|
||||
except ValueError as e:
|
||||
r = e.args[0]
|
||||
assert r == "6982"
|
||||
|
||||
def test_verify_pw1_2(card):
|
||||
v = card.verify(1, PW1_TEST2)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_2_2(card):
|
||||
v = card.verify(2, PW1_TEST2)
|
||||
assert v
|
||||
|
||||
def test_verify_pw3_fail_2(card):
|
||||
try:
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
except ValueError as e:
|
||||
v = e.args[0]
|
||||
assert v == "6982"
|
||||
|
||||
def test_sign_0(card):
|
||||
digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0)
|
||||
r = card.cmd_pso(0x9e, 0x9a, digestinfo)
|
||||
sig = rsa_keys.compute_signature(0, digestinfo)
|
||||
sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
|
||||
assert r == sig_bytes
|
||||
|
||||
# Since forcesig setting, failed
|
||||
def test_sign_1(card):
|
||||
digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1)
|
||||
try:
|
||||
r = card.cmd_pso(0x9e, 0x9a, digestinfo)
|
||||
except ValueError as e:
|
||||
r = e.args[0]
|
||||
assert r == "6982"
|
||||
|
||||
def test_ds_counter_1(card):
|
||||
c = get_data_object(card, 0x7a)
|
||||
assert c == b'\x93\x03\x00\x00\x01'
|
||||
|
||||
def test_sign_auth_0(card):
|
||||
digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0)
|
||||
r = card.cmd_internal_authenticate(digestinfo)
|
||||
sig = rsa_keys.compute_signature(2, digestinfo)
|
||||
sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
|
||||
assert r == sig_bytes
|
||||
|
||||
def test_sign_auth_1(card):
|
||||
digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1)
|
||||
r = card.cmd_internal_authenticate(digestinfo)
|
||||
sig = rsa_keys.compute_signature(2, digestinfo)
|
||||
sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
|
||||
assert r == sig_bytes
|
||||
|
||||
def test_decrypt_0(card):
|
||||
ciphertext = rsa_keys.encrypt(1, PLAIN_TEXT0)
|
||||
r = card.cmd_pso(0x80, 0x86, ciphertext)
|
||||
assert r == PLAIN_TEXT0
|
||||
|
||||
def test_decrypt_1(card):
|
||||
ciphertext = rsa_keys.encrypt(1, PLAIN_TEXT1)
|
||||
r = card.cmd_pso(0x80, 0x86, ciphertext)
|
||||
assert r == PLAIN_TEXT1
|
||||
|
||||
def test_verify_pw3_admin_less_2(card):
|
||||
v = card.verify(3, PW1_TEST2)
|
||||
assert v
|
||||
|
||||
def test_login_put(card):
|
||||
r = card.cmd_put_data(0x00, 0x5e, b"gpg_user")
|
||||
assert r
|
||||
|
||||
def test_name_put(card):
|
||||
r = card.cmd_put_data(0x00, 0x5b, b"GnuPG User")
|
||||
assert r
|
||||
|
||||
def test_lang_put(card):
|
||||
r = card.cmd_put_data(0x5f, 0x2d, b"ja")
|
||||
assert r
|
||||
|
||||
def test_sex_put(card):
|
||||
r = card.cmd_put_data(0x5f, 0x35, b"1")
|
||||
assert r
|
||||
|
||||
def test_url_put(card):
|
||||
r = card.cmd_put_data(0x5f, 0x50, b"https://www.fsij.org/gnuk/")
|
||||
assert r
|
||||
|
||||
def test_pw1_status_put(card):
|
||||
r = card.cmd_put_data(0x00, 0xc4, b"\x01")
|
||||
assert r
|
||||
|
||||
def test_login(card):
|
||||
login = get_data_object(card, 0x5e)
|
||||
assert login == b"gpg_user"
|
||||
|
||||
def test_name_lang_sex(card):
|
||||
name = b"GnuPG User"
|
||||
lang = b"ja"
|
||||
sex = b"1"
|
||||
expected = b'\x5b' + pack('B', len(name)) + name \
|
||||
+ b'\x5f\x2d' + pack('B', len(lang)) + lang \
|
||||
+ b'\x5f\x35' + pack('B', len(sex)) + sex
|
||||
name_lang_sex = get_data_object(card, 0x65)
|
||||
assert name_lang_sex == expected
|
||||
|
||||
def test_url(card):
|
||||
url = get_data_object(card, 0x5f50)
|
||||
assert url == b"https://www.fsij.org/gnuk/"
|
||||
|
||||
def test_pw1_status(card):
|
||||
s = get_data_object(card, 0xc4)
|
||||
assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL)
|
||||
|
||||
# Setting PW3, changed to admin-full mode
|
||||
|
||||
def test_setup_pw3_1(card):
|
||||
r = card.change_passwd(3, PW1_TEST2, PW3_TEST1)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_1(card):
|
||||
v = card.verify(3, PW3_TEST1)
|
||||
assert v
|
||||
|
||||
def test_reset_userpass_admin(card):
|
||||
r = card.reset_passwd_by_admin(PW1_TEST3)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_3(card):
|
||||
v = card.verify(1, PW1_TEST3)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_3_2(card):
|
||||
v = card.verify(2, PW1_TEST3)
|
||||
assert v
|
||||
|
||||
def test_setup_pw1_4(card):
|
||||
r = card.change_passwd(1, PW1_TEST3, PW1_TEST4)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_4(card):
|
||||
v = card.verify(1, PW1_TEST4)
|
||||
assert v
|
||||
|
||||
def test_verify_pw1_4_2(card):
|
||||
v = card.verify(2, PW1_TEST4)
|
||||
assert v
|
||||
|
||||
def test_setup_pw3_2(card):
|
||||
r = card.change_passwd(3, PW3_TEST1, PW3_TEST0)
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_2(card):
|
||||
v = card.verify(3, PW3_TEST0)
|
||||
assert v
|
||||
1
tests/test_006_personalize_reset.py
Symbolic link
1
tests/test_006_personalize_reset.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_002_personalize_reset.py
|
||||
1
tests/test_007_remove_keys.py
Symbolic link
1
tests/test_007_remove_keys.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_003_remove_keys.py
|
||||
1
tests/test_008_reset_pw3.py
Symbolic link
1
tests/test_008_reset_pw3.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_004_reset_pw3.py
|
||||
83
tests/test_009_keygen.py
Normal file
83
tests/test_009_keygen.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
test_005_keygen.py - test key generation
|
||||
|
||||
Copyright (C) 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from binascii import hexlify
|
||||
import rsa_keys
|
||||
from card_const import *
|
||||
|
||||
def test_keygen_1(card):
|
||||
pk = card.cmd_genkey(1)
|
||||
fpr_date = rsa_keys.calc_fpr(pk[0], pk[1])
|
||||
r = card.cmd_put_data(0x00, 0xc7, fpr_date[0])
|
||||
if r:
|
||||
r = card.cmd_put_data(0x00, 0xce, fpr_date[1])
|
||||
assert r
|
||||
|
||||
def test_keygen_2(card):
|
||||
pk = card.cmd_genkey(2)
|
||||
fpr_date = rsa_keys.calc_fpr(pk[0], pk[1])
|
||||
r = card.cmd_put_data(0x00, 0xc8, fpr_date[0])
|
||||
if r:
|
||||
r = card.cmd_put_data(0x00, 0xcf, fpr_date[1])
|
||||
assert r
|
||||
|
||||
def test_keygen_3(card):
|
||||
pk = card.cmd_genkey(3)
|
||||
fpr_date = rsa_keys.calc_fpr(pk[0], pk[1])
|
||||
r = card.cmd_put_data(0x00, 0xc9, fpr_date[0])
|
||||
if r:
|
||||
r = card.cmd_put_data(0x00, 0xd0, fpr_date[1])
|
||||
assert r
|
||||
|
||||
def test_verify_pw1(card):
|
||||
v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1)
|
||||
assert v
|
||||
|
||||
def test_signature_sigkey(card):
|
||||
msg = b"Sign me please"
|
||||
pk = card.cmd_get_public_key(1)
|
||||
pk_info = (pk[9:9+256], pk[9+256+2:9+256+2+3])
|
||||
digest = rsa_keys.compute_digestinfo(msg)
|
||||
sig = int(hexlify(card.cmd_pso(0x9e, 0x9a, digest)),16)
|
||||
r = rsa_keys.verify_signature(pk_info, digest, sig)
|
||||
assert r
|
||||
|
||||
def test_verify_pw1_2(card):
|
||||
v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1)
|
||||
assert v
|
||||
|
||||
def test_decryption(card):
|
||||
msg = b"encrypt me please"
|
||||
pk = card.cmd_get_public_key(2)
|
||||
pk_info = (pk[9:9+256], pk[9+256+2:9+256+2+3])
|
||||
ciphertext = rsa_keys.encrypt_with_pubkey(pk_info, msg)
|
||||
r = card.cmd_pso(0x80, 0x86, ciphertext)
|
||||
assert r == msg
|
||||
|
||||
def test_signature_authkey(card):
|
||||
msg = b"Sign me please to authenticate"
|
||||
pk = card.cmd_get_public_key(3)
|
||||
pk_info = (pk[9:9+256], pk[9+256+2:9+256+2+3])
|
||||
digest = rsa_keys.compute_digestinfo(msg)
|
||||
sig = int(hexlify(card.cmd_internal_authenticate(digest)),16)
|
||||
r = rsa_keys.verify_signature(pk_info, digest, sig)
|
||||
assert r
|
||||
1
tests/test_010_remove_keys.py
Symbolic link
1
tests/test_010_remove_keys.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_003_remove_keys.py
|
||||
34
tests/test_011_kdf_full.py
Normal file
34
tests/test_011_kdf_full.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
test_007_kdf_full.py - test KDF data object
|
||||
|
||||
Copyright (C) 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from card_const import *
|
||||
from constants_for_test import *
|
||||
|
||||
def test_verify_pw3(card):
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
|
||||
def test_kdf_put_full(card):
|
||||
r = card.cmd_put_data(0x00, 0xf9, KDF_FULL)
|
||||
if r:
|
||||
card.configure_with_kdf()
|
||||
assert r
|
||||
1
tests/test_012_personalize_card.py
Symbolic link
1
tests/test_012_personalize_card.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_001_personalize_card.py
|
||||
1
tests/test_013_personalize_reset.py
Symbolic link
1
tests/test_013_personalize_reset.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_002_personalize_reset.py
|
||||
1
tests/test_014_remove_keys.py
Symbolic link
1
tests/test_014_remove_keys.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_003_remove_keys.py
|
||||
1
tests/test_015_reset_pw3.py
Symbolic link
1
tests/test_015_reset_pw3.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_004_reset_pw3.py
|
||||
34
tests/test_016_kdf_single.py
Normal file
34
tests/test_016_kdf_single.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
test_012_kdf_single.py - test KDF data object
|
||||
|
||||
Copyright (C) 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from card_const import *
|
||||
from constants_for_test import *
|
||||
|
||||
def test_verify_pw3(card):
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
|
||||
def test_kdf_put_single(card):
|
||||
r = card.cmd_put_data(0x00, 0xf9, KDF_SINGLE)
|
||||
if r:
|
||||
card.configure_with_kdf()
|
||||
assert r
|
||||
1
tests/test_017_personalize_card.py
Symbolic link
1
tests/test_017_personalize_card.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_001_personalize_card.py
|
||||
1
tests/test_018_personalize_reset.py
Symbolic link
1
tests/test_018_personalize_reset.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_002_personalize_reset.py
|
||||
1
tests/test_019_remove_keys.py
Symbolic link
1
tests/test_019_remove_keys.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_003_remove_keys.py
|
||||
1
tests/test_020_reset_pw3.py
Symbolic link
1
tests/test_020_reset_pw3.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_004_reset_pw3.py
|
||||
1
tests/test_021_personalize_admin_less.py
Symbolic link
1
tests/test_021_personalize_admin_less.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_005_personalize_admin_less.py
|
||||
1
tests/test_022_personalize_reset.py
Symbolic link
1
tests/test_022_personalize_reset.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_002_personalize_reset.py
|
||||
1
tests/test_023_remove_keys.py
Symbolic link
1
tests/test_023_remove_keys.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_003_remove_keys.py
|
||||
1
tests/test_024_reset_pw3.py
Symbolic link
1
tests/test_024_reset_pw3.py
Symbolic link
@@ -0,0 +1 @@
|
||||
test_004_reset_pw3.py
|
||||
38
tests/test_025_kdf_none.py
Normal file
38
tests/test_025_kdf_none.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
test_017_kdf_none.py - test KDF data object
|
||||
|
||||
Copyright (C) 2018 g10 Code GmbH
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from card_const import *
|
||||
from constants_for_test import *
|
||||
|
||||
def test_verify_pw3(card):
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
|
||||
def test_kdf_put_none(card):
|
||||
r = card.cmd_put_data(0x00, 0xf9, b"")
|
||||
if r:
|
||||
card.configure_with_kdf()
|
||||
assert r
|
||||
|
||||
def test_verify_pw3_1(card):
|
||||
v = card.verify(3, FACTORY_PASSPHRASE_PW3)
|
||||
assert v
|
||||
@@ -3,7 +3,7 @@
|
||||
"""
|
||||
gnuk_remove_keys_libusb.py - a tool to remove keys in Gnuk Token
|
||||
|
||||
Copyright (C) 2012 Free Software Initiative of Japan
|
||||
Copyright (C) 2012, 2018 Free Software Initiative of Japan
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -24,7 +24,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys, os
|
||||
|
||||
from gnuk_token import *
|
||||
from gnuk_token import gnuk_devices, gnuk_token, parse_kdf_data
|
||||
from kdf_calc import kdf_calc
|
||||
|
||||
# Assume only single CCID device is attached to computer and it's Gnuk Token
|
||||
|
||||
@@ -42,13 +43,28 @@ def main(passwd):
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if not gnuk:
|
||||
raise ValueError("No ICC present")
|
||||
if gnuk.icc_get_status() == 2:
|
||||
raise ValueError("No ICC present")
|
||||
elif gnuk.icc_get_status() == 1:
|
||||
gnuk.icc_power_on()
|
||||
gnuk.cmd_select_openpgp()
|
||||
gnuk.cmd_verify(BY_ADMIN, passwd.encode('UTF-8'))
|
||||
gnuk.cmd_select_openpgp()
|
||||
# Compute passwd data
|
||||
kdf_data = gnuk.cmd_get_data(0x00, 0xf9).tostring()
|
||||
if kdf_data == b"":
|
||||
passwd_data = passwd.encode('UTF-8')
|
||||
else:
|
||||
algo, subalgo, iters, salt_user, salt_reset, salt_admin, \
|
||||
hash_user, hash_admin = parse_kdf_data(kdf_data)
|
||||
if salt_admin:
|
||||
salt = salt_admin
|
||||
else:
|
||||
salt = salt_user
|
||||
passwd_data = kdf_calc(passwd, salt, iters)
|
||||
# And authenticate with the passwd data
|
||||
gnuk.cmd_verify(BY_ADMIN, passwd_data)
|
||||
# Do remove keys and related data objects
|
||||
gnuk.cmd_put_data_remove(0x00, 0xc7) # FP_SIG
|
||||
gnuk.cmd_put_data_remove(0x00, 0xce) # KGTIME_SIG
|
||||
gnuk.cmd_put_data_key_import_remove(1)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
gnuk_token.py - a library for Gnuk Token
|
||||
|
||||
Copyright (C) 2011, 2012, 2013, 2015, 2017
|
||||
Copyright (C) 2011, 2012, 2013, 2015, 2017, 2018
|
||||
Free Software Initiative of Japan
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
@@ -635,3 +635,48 @@ def UNSIGNED(n):
|
||||
def crc32(bytestr):
|
||||
crc = binascii.crc32(bytestr)
|
||||
return UNSIGNED(crc)
|
||||
|
||||
def parse_kdf_data(kdf_data):
|
||||
if len(kdf_data) == 90:
|
||||
single_salt = True
|
||||
elif len(kdf_data) == 110:
|
||||
single_salt = False
|
||||
else:
|
||||
raise ValueError("length does not much", kdf_data)
|
||||
|
||||
if kdf_data[0:2] != b'\x81\x01':
|
||||
raise ValueError("data does not much")
|
||||
algo = kdf_data[2]
|
||||
if kdf_data[3:5] != b'\x82\x01':
|
||||
raise ValueError("data does not much")
|
||||
subalgo = kdf_data[5]
|
||||
if kdf_data[6:8] != b'\x83\x04':
|
||||
raise ValueError("data does not much")
|
||||
iters = unpack(">I", kdf_data[8:12])[0]
|
||||
if kdf_data[12:14] != b'\x84\x08':
|
||||
raise ValueError("data does not much")
|
||||
salt = kdf_data[14:22]
|
||||
if single_salt:
|
||||
salt_reset = None
|
||||
salt_admin = None
|
||||
if kdf_data[22:24] != b'\x87\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_user = kdf_data[24:56]
|
||||
if kdf_data[56:58] != b'\x88\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_admin = kdf_data[58:90]
|
||||
else:
|
||||
if kdf_data[22:24] != b'\x85\x08':
|
||||
raise ValueError("data does not much")
|
||||
salt_reset = kdf_data[24:32]
|
||||
if kdf_data[32:34] != b'\x86\x08':
|
||||
raise ValueError("data does not much")
|
||||
salt_admin = kdf_data[34:42]
|
||||
if kdf_data[42:44] != b'\x87\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_user = kdf_data[44:76]
|
||||
if kdf_data[76:78] != b'\x88\x20':
|
||||
raise ValueError("data does not much")
|
||||
hash_admin = kdf_data[78:110]
|
||||
return ( algo, subalgo, iters, salt, salt_reset, salt_admin,
|
||||
hash_user, hash_admin )
|
||||
|
||||
51
tool/kdf_calc.py
Normal file
51
tool/kdf_calc.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
kdf_calc.py - a library for calculating hash by KDF
|
||||
|
||||
Copyright (C) 2018 Free Software Initiative of Japan
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
|
||||
Gnuk is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gnuk 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 General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from cffi import FFI
|
||||
|
||||
DEF_gcry_kdf_derive="""
|
||||
typedef unsigned int gpg_error_t;
|
||||
gpg_error_t gcry_kdf_derive (const void *passphrase, size_t passphraselen,
|
||||
int algo, int subalgo, const void *salt,
|
||||
size_t saltlen, unsigned long iterations,
|
||||
size_t keysize, void *keybuffer);
|
||||
"""
|
||||
|
||||
GCRY_KDF_ITERSALTED_S2K = 19
|
||||
GCRY_MD_SHA256 = 8
|
||||
|
||||
def kdf_calc(pw_string, salt_byte, iterations):
|
||||
ffi = FFI()
|
||||
ffi.cdef(DEF_gcry_kdf_derive)
|
||||
libgcrypt = ffi.dlopen("libgcrypt.so.20")
|
||||
if isinstance(pw_string, str):
|
||||
pw_byte = pw_string.encode('UTF-8')
|
||||
else:
|
||||
pw_byte = pw_string
|
||||
pw=ffi.new("char []", pw_byte)
|
||||
salt = ffi.new("char []", salt_byte)
|
||||
kb = ffi.new("char []", 32)
|
||||
r = libgcrypt.gcry_kdf_derive(pw, len(pw_string), GCRY_KDF_ITERSALTED_S2K,
|
||||
GCRY_MD_SHA256, salt, 8, iterations, 32, kb)
|
||||
if r != 0:
|
||||
raise ValueError("libgcrypt error", r)
|
||||
return ffi.unpack(kb, 32)
|
||||
@@ -4,7 +4,8 @@
|
||||
upgrade_by_passwd.py - a tool to install another firmware for Gnuk Token
|
||||
which is just shipped from factory
|
||||
|
||||
Copyright (C) 2012, 2013, 2015 Free Software Initiative of Japan
|
||||
Copyright (C) 2012, 2013, 2015, 2018
|
||||
Free Software Initiative of Japan
|
||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
This file is a part of Gnuk, a GnuPG USB Token implementation.
|
||||
@@ -23,9 +24,13 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from gnuk_token import *
|
||||
from gnuk_token import get_gnuk_device, gnuk_devices_by_vidpid, \
|
||||
gnuk_token, regnual, SHA256_OID_PREFIX, crc32, parse_kdf_data
|
||||
from kdf_calc import kdf_calc
|
||||
|
||||
import sys, binascii, time, os
|
||||
import rsa
|
||||
from struct import pack
|
||||
|
||||
DEFAULT_PW3 = "12345678"
|
||||
BY_ADMIN = 3
|
||||
@@ -45,7 +50,23 @@ def main(wait_e, keyno, passwd, data_regnual, data_upgrade):
|
||||
|
||||
gnuk = get_gnuk_device()
|
||||
gnuk.cmd_select_openpgp()
|
||||
gnuk.cmd_verify(BY_ADMIN, passwd.encode('UTF-8'))
|
||||
# Compute passwd data
|
||||
try:
|
||||
kdf_data = gnuk.cmd_get_data(0x00, 0xf9).tostring()
|
||||
except:
|
||||
kdf_data = b""
|
||||
if kdf_data == b"":
|
||||
passwd_data = passwd.encode('UTF-8')
|
||||
else:
|
||||
algo, subalgo, iters, salt_user, salt_reset, salt_admin, \
|
||||
hash_user, hash_admin = parse_kdf_data(kdf_data)
|
||||
if salt_admin:
|
||||
salt = salt_admin
|
||||
else:
|
||||
salt = salt_user
|
||||
passwd_data = kdf_calc(passwd, salt, iters)
|
||||
# And authenticate with the passwd data
|
||||
gnuk.cmd_verify(BY_ADMIN, passwd_data)
|
||||
gnuk.cmd_write_binary(1+keyno, rsa_raw_pubkey, False)
|
||||
|
||||
gnuk.cmd_select_openpgp()
|
||||
@@ -71,7 +92,7 @@ def main(wait_e, keyno, passwd, data_regnual, data_upgrade):
|
||||
reg = None
|
||||
print("Waiting for device to appear:")
|
||||
while reg == None:
|
||||
print(" Wait %d seconds..." % wait_e)
|
||||
print(" Wait {} second{}...".format(wait_e, 's' if wait_e > 1 else ''))
|
||||
time.sleep(wait_e)
|
||||
for dev in gnuk_devices_by_vidpid():
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user