Compare commits

...

63 Commits

Author SHA1 Message Date
NIIBE Yutaka
83414a747a Version 1.2.5. 2017-08-11 22:12:09 +09:00
NIIBE Yutaka
8a615d087b Update .gitignore. 2017-08-11 22:11:49 +09:00
NIIBE Yutaka
967b949967 Tweak process size of gpg. 2017-08-11 22:00:01 +09:00
NIIBE Yutaka
11afbdde14 src/config.mk generation. 2017-08-11 21:06:59 +09:00
NIIBE Yutaka
328766af12 Merge branch 'master' of git.gniibe.org:gnuk/gnuk 2017-08-04 08:33:46 +09:00
NIIBE Yutaka
2b340ee1c5 Fix factory-reset for admin-less mode.
Reported-by: Stanislas Bach <sbach@0g.re>
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-08-04 08:32:39 +09:00
NIIBE Yutaka
86e6adf47e Fix factory-reset for admin-less mode.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-08-03 21:35:20 +09:00
Jeremy Drake
eea011fe70 Allow compile-time override of detected flash size.
On the STM32F103C8, as used in the "blue pill" boards, it has been
determined that, despite these only officially having 64KiB flash, it is
possible to actually use 128KiB of flash.

This commit allows for a preprocessor define
STM32F103_OVERRIDE_FLASH_SIZE which, when set, is used as the size of
flash in KiB instead of reading it from the FLASH_SIZE_REG.
2017-08-03 21:20:47 +09:00
Jeremy Drake
e736227de7 Erase CERTDO on terminate.
When both certdo and lifecycle support are enabled, flash_terminate
neglected to erase the certdo pages.  It now does so.
2017-08-02 11:13:02 +09:00
NIIBE Yutaka
22156ea7f9 Fix factory-reset. 2017-08-01 13:30:02 +09:00
Anthony Romano
db45e62ebe configure: sanitize for shellcheck
Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-20 12:25:24 +09:00
Anthony Romano
3270740631 docker: source checking container
Includes shellcheck and scan-build

Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-20 12:24:44 +09:00
NIIBE Yutaka
e4e72a29ae Initialize TMP to avoid confusion by static analysis.
--

The computation using TMP is keeping it constant-time only, but
it is better not to confuse static analysis.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-07-19 11:55:20 +09:00
NIIBE Yutaka
25d3f021c1 Support no git situation.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-07-19 11:25:38 +09:00
NIIBE Yutaka
ae76d66d53 Fix accessing garbage on error path.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
Reported-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-19 10:48:16 +09:00
NIIBE Yutaka
10c5010141 Git is assumed for the source with .git.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-07-18 14:12:20 +09:00
Anthony Romano
d12483c3c9 Support building with docker.
Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-18 13:38:46 +09:00
Anthony Romano
67acb670d1 call fatal if mem_head size is corrupted.
Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-18 13:31:06 +09:00
Anthony Romano
a44244b27e avoid null dereference when openpgp algo goes from !rsa2k to rsa2k.
Detected with scan-build.

Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-18 13:27:12 +09:00
Anthony Romano
2622840e27 remove unused calculations from mod_reduce.
Detected with scan-build.

Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-18 13:19:42 +09:00
Anthony Romano
a51ac8593b call-rsa: free modulus buffers on error paths.
* MPI_CHK jumps to cleanup on ret != 0, so p_q_modulus is never freed if
  rsa_gen_key fails (detected via scan-build).
* modulus_calc never freed its modulus buffer on error.

Signed-off-by: Anthony Romano <anthony.romano@coreos.com>
2017-07-18 13:15:42 +09:00
NIIBE Yutaka
de81caba3e Update Chopstx.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-07-18 12:58:23 +09:00
NIIBE Yutaka
fa69a85826 Version 1.2.4.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-05-12 17:22:20 +09:00
NIIBE Yutaka
5c3c3e3001 usbip list -r 127.0.0.1 now works.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-05-12 14:13:53 +09:00
NIIBE Yutaka
6dcb4dd027 Add usb-emu.c.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-05-02 15:05:15 +09:00
NIIBE Yutaka
fa08f44cac Fix old documentation (note) for firmware update.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-05-01 14:58:15 +09:00
NIIBE Yutaka
4c2294ea6c Portability change.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-28 16:14:30 +09:00
NIIBE Yutaka
86eaa26d32 New: src/mcu-stm32f103.c.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-28 15:49:38 +09:00
NIIBE Yutaka
9e52789203 Fix long standing buf of digital signature counter.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-28 14:54:15 +09:00
NIIBE Yutaka
702bc8cbde Move data objects at the end of flash.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-27 15:23:25 +09:00
NIIBE Yutaka
2cfce76d91 [SECURITY] Flash memory usage change.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-04-27 14:36:32 +09:00
NIIBE Yutaka
207652246a emulation: USB device emulation by USBIP protocol. 2017-04-18 15:45:25 +09:00
NIIBE Yutaka
32779b6f96 Version 1.2.3. 2017-02-02 16:33:30 +09:00
NIIBE Yutaka
55c1015faa Increase CCID thread stack size by 0x20 for newer GCC. 2017-02-02 14:11:11 +09:00
NIIBE Yutaka
0932465f0b Update Chopstx to 1.3. 2017-02-02 13:07:35 +09:00
NIIBE Yutaka
4417799a51 Update README 2017-02-01 17:16:54 +09:00
Szczepan Zalega
b424cecf1e Regnual update tool: do not allow other than binary formats (upgrade_by_passwd)
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
2017-02-01 15:40:56 +09:00
NIIBE Yutaka
7ef417ae36 tool: Improve tool/*.py.
--

Szczepan Zalega's idea of using the file GNUK_USB_DEVICE_ID would
good, but not merged yet.  Because it makes difficult to distribute
the scripts.  We need to consider installing tools and the file
like GNUK_USB_DEVICE_ID altogether.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-02-01 12:34:35 +09:00
NIIBE Yutaka
d4469c24ec fix NIST P-256 / secp256k1 key generation. 2016-10-21 15:30:07 +09:00
NIIBE Yutaka
e4333c6580 Version 1.2.2 2016-10-15 20:18:20 +09:00
NIIBE Yutaka
d2261d53e3 fix scripts 2016-10-15 20:14:18 +09:00
NIIBE Yutaka
27bd37781a Fix flash initialization 2016-10-15 19:29:23 +09:00
NIIBE Yutaka
bed43d4049 Add tests/ accessing DO 6E 2016-10-15 19:28:57 +09:00
NIIBE Yutaka
f7d857b527 fix stack usage of CCID 2016-10-15 18:37:22 +09:00
NIIBE Yutaka
350528e1f4 tests/ update for signature counter 2016-10-15 18:35:21 +09:00
NIIBE Yutaka
4de605ed63 Add pubkey tests 2016-10-15 16:55:22 +09:00
NIIBE Yutaka
ffa9bf1f94 support factory_reset. 2016-10-14 08:45:01 +09:00
NIIBE Yutaka
34d0b34144 add factory reset support (not-full yet) 2016-10-13 15:06:19 +09:00
NIIBE Yutaka
5795dc9877 rename status-code.h 2016-10-13 11:03:50 +09:00
NIIBE Yutaka
c8b17a8759 Update to Chopstx 1.2 2016-10-13 10:35:22 +09:00
NIIBE Yutaka
38d70e277b Fix difference between original OpenPGP card 2016-10-13 10:33:02 +09:00
NIIBE Yutaka
b00bab8dbf tests/ update 2016-10-13 10:04:27 +09:00
NIIBE Yutaka
3c91dce8b7 fix tests/ 2016-10-12 19:42:22 +09:00
NIIBE Yutaka
f1773c146b fix test/ 2016-10-12 15:32:51 +09:00
NIIBE Yutaka
979992c046 fix test/ 2016-10-12 14:56:57 +09:00
NIIBE Yutaka
50700e3887 more tests (incomplete) 2016-10-12 10:22:57 +09:00
NIIBE Yutaka
b0ee8b4452 TPDU reader works now 2016-10-07 16:39:20 +09:00
NIIBE Yutaka
a73f8cf4fd implement TPDU card reader 2016-10-05 20:00:22 +09:00
NIIBE Yutaka
c1cc75f5b0 New test suite for OpenPGP card 2016-09-30 16:38:27 +09:00
NIIBE Yutaka
47150b5c98 minor fix for configure 2016-09-30 16:20:19 +09:00
NIIBE Yutaka
f46880d2a8 Add Gnuk logo of PNG 2016-09-02 11:01:55 +09:00
NIIBE Yutaka
23bbc9c755 Fix test setup 2016-08-24 10:39:27 +09:00
NIIBE Yutaka
2b784cb3b9 Upgrade tool/hub_ctrl.py 2016-08-03 21:19:34 +09:00
61 changed files with 2841 additions and 340 deletions

5
.gitignore vendored
View File

@@ -2,7 +2,7 @@
*.o
*.pyc
src/.dep
src/Makefile
src/config.mk
src/config.h
src/gnuk.ld
src/board.h
@@ -12,3 +12,6 @@ regnual/regnual.bin
regnual/regnual.hex
regnual/regnual.elf
doc/_build
tests/.cache
tests/__pycache__
emulation/gnuk_emulation

10
AUTHORS
View File

@@ -1,3 +1,13 @@
Anthony Romano:
Modified:
src/call-rsa.c
src/main.c
src/mod.c
Jeremy Drake:
Modified:
regnual/regnual.c
Kaz Kojima:
Added STM32 Primer2 support.

206
ChangeLog
View File

@@ -1,3 +1,209 @@
2017-08-11 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.2.5.
* chopstx: Update to 1.4.
* src/gnuk.ld.in (__process3_stack_size__): Tweak the size.
* src/configure: Define STM32F103_OVERRIDE_FLASH_SIZE_KB for
BULE_PILL.
* src/configure: Let generate src/config.mk.
* src/Makefile: Rename from src/Makefile.in.
* regnual/Makefile: Use src/config.mk.
2017-08-03 NIIBE Yutaka <gniibe@fsij.org>
* src/openpgp.c (cmd_terminate_df): Fix for admin-less mode.
2017-08-03 Jeremy Drake <jeremydrake+gnuk@eacceleration.com>
* regnual/regnual.c (main): Allow compile time
flash size definition by STM32F103_OVERRIDE_FLASH_SIZE_KB.
2017-08-02 Jeremy Drake <jeremydrake+gnuk@eacceleration.com>
* src/flash.c (flash_terminate): Erase Certificate DO, too.
2017-08-01 NIIBE Yutaka <gniibe@fsij.org>
* src/openpgp.c (FILE_CARD_TERMINATED_OPENPGP): Remove.
(cmd_select_file): Don't change file_selection.
2017-07-19 NIIBE Yutaka <gniibe@fsij.org>
* src/mod.c (mod_inv): Clear TMP.
* src/configure (REVISION): Generate even when no git.
* polarssl/library/bignum.c (mpi_exp_mod): Call mpi_grow for X
after the initialization of RR.
2017-07-18 NIIBE Yutaka <gniibe@fsij.org>
* src/configure: Bark when no git available.
2017-07-18 Anthony Romano <anthony.romano@coreos.com>
* docker: New.
2017-07-18 Anthony Romano <anthony.romano@coreos.com>
* src/main.c (MEMORY_SIZE, MEM_HEAD_IS_CORRUPT, MEM_HEAD_CHECK):
New.
(gnuk_malloc, gnuk_free): Add calls to MEM_HEAD_CHECK.
* src/gnuk.h (FATAL_HEAP): New.
2017-07-18 Anthony Romano <anthony.romano@coreos.com>
* src/openpgp-do.c (gpg_reset_algo_attr): New.
(rw_algorithm_attr): Use gpg_reset_algo_attr.
Fix null dereference.
2017-07-18 Anthony Romano <anthony.romano@coreos.com>
* src/mod.c (mod_reduce): Clean up unused code.
2017-07-18 Anthony Romano <anthony.romano@coreos.com>
* src/call-rsa.c (modulus_calc): Free modulus on error.
(rsa_genkey): Remove bogus check, and call chopstx_cleanup_pop
with 1 to release p_q_modulus on error. Assign NULL to clp.arg
when it's goes with no error.
* src/main.c (gnuk_free): Allow NULL.
2017-07-18 NIIBE Yutaka <gniibe@fsij.org>
* Update chopstx (with USBIP emulation).
2017-05-12 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.2.4.
2017-04-28 NIIBE Yutaka <gniibe@fsij.org>
* src/mcu-stm32f103.c: New.
(check_crc32, sram_address): New.
* src/usb_ctrl.c (download_check_crc32): Use check_crc32 and
sram_address.
* src/openpgp-do.c (gpg_write_digital_signature_counter): Fix
writing lower 10-bit.
2017-04-27 NIIBE Yutaka <gniibe@fsij.org>
* src/gnuk.ld.in (_data_pool): Move to the end.
* src/flash.c (flash_init): Return address of end of data object.
* src/openpgp.c (gpg_init): Get address of end of data object.
* src/openpgp-do.c (gpg_data_scan): Check the end address.
2017-02-02 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.2.3.
* src/gnuk.ld.in (__process1_stack_size__): Increase by 0x20.
* chopstx: Update to 1.3.
* src/configure: Add BLUE_PILL in the help message.
2017-02-01 NIIBE Yutaka <gniibe@fsij.org>
* README: Update README. Thanks to Paul Fertser.
2017-01-02 Szczepan Zalega <szczepan@nitrokey.com>
* tool/upgrade_by_passwd.py: Add file extention check.
2017-02-01 NIIBE Yutaka <gniibe@fsij.org>
* tool/upgrade_by_passwd.py (main): More verbose messages
suggested by Szczepan Zalega <szczepan@nitrokey.com>.
* tool/gnuk_token.py (USB_PRODUCT_LIST): New.
(gnuk_devices_by_vidpid): Support searching by USB_PRODUCT_LIST.
Thanks to Szczepan Zalega <szczepan@nitrokey.com>.
* tool/usb_strings.py: Use gnuk_token.py.
2016-10-21 Niibe Yutaka <gniibe@fsij.org>
* src/ecc.c (check_secret): Fix condition.
2016-10-15 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.2.2.
* tool/gnuk_put_binary_libusb.py (main): Likewise.
* tool/upgrade_by_passwd.py (main): Add call of cmd_select_openpgp
method.
* src/openpgp.c (gpg_init): flash_init_keys shoule be after
gpg_data_scan since flash_init_keys accesses Data Object for
key attributes.
* src/usb-ccid.c (ccid_power_on): Don't waste stack.
2016-10-14 Niibe Yutaka <gniibe@fsij.org>
* src/usb-ccid.c (ccid_power_on) [LIFE_CYCLE_MANAGEMENT_SUPPORT]:
Change LCS value in ATR at run time.
* src/openpgp.c (gpg_init): Handle FILE_CARD_TERMINATED.
(cmd_select_file): Don't return AID.
(cmd_activate_file, cmd_terminate_df): New.
(process_command_apdu): Let return GPG_NO_RECORD() when
not selected.
* src/openpgp-do.c (gpg_do_terminate): New.
(gpg_data_scan): Handle p_start is NULL.
(do_hist_bytes): Remove.
* src/flash.c (flash_data): Change the value from 0x0000.
(flash_init): Support termination state. Fix handling
of the boundary case where gen0 is 0xfffe.
(flash_terminate, flash_activate): New.
(flash_copying_gc): Skip 0xffff for generation number.
2016-10-13 Niibe Yutaka <gniibe@fsij.org>
* src/status-code.h: Rename from openpgp.h.
* chopstx: Update to 1.2.
* tests: New test suite for OpenPGP card with PyTest.
* src/configure (factory_reset): New.
* src/usb-ccid.c (ccid_power_on): Use ATR_head and historical
bytes.
* src/openpgp-do.c (rw_algorithm_attr): Clear fingerprint, timestamp,
and possibly ds_counter.
2016-10-12 Niibe Yutaka <gniibe@fsij.org>
* test/features/steps.py (cmd_reset_retry_counter): Fix.
* tool/gnuk_token.py (gnuk_token.cmd_reset_retry_counter): Fix.
(gnuk_token.cmd_select_openpgp): Fix P2.
2016-09-02 Niibe Yutaka <gniibe@fsij.org>
* src/configure (REVISION): Fix the detection of .git.
It may be a regular file (if it's created by worktree).
2016-08-24 Niibe Yutaka <gniibe@fsij.org>
* test/features/steps.py (ini): Use GLC (the global context),
instead of FTC (the feature context), so that token only is
opened once.
2016-08-03 Niibe Yutaka <gniibe@fsij.org>
* tool/hub_ctrl.py: Port to Python 3.
2016-07-11 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.2.1.

51
NEWS
View File

@@ -1,5 +1,56 @@
Gnuk NEWS - User visible changes
* Major changes in Gnuk 1.2.5
Released 2017-08-11, by NIIBE Yutaka
** "factory-reset" fix
Gnuk's behavior was implemented by referring the gpg implementation.
It found that gpg implementation was not good from the viewpoint of
the OpenPGP card specification. GnuPG was fixed to match the OpenPGP
card specification already. Thus, Gnuk is now fixed.
** Upgrade of Chopstx
We use Chopstx 1.4.
* Major changes in Gnuk 1.2.4
Released 2017-05-12, by NIIBE Yutaka
** Flash ROM security fix
The partial content of flash ROM might be exposed when scanning of
data object had a problem. Added boundary check and changed layout of
flash ROM.
* Major changes in Gnuk 1.2.3
Released 2017-02-02, by NIIBE Yutaka
** ECC key generation on the device
Bug fixed.
** Upgrade of Chopstx
We use Chopstx 1.3.
* Major changes in Gnuk 1.2.2
Released 2016-10-15, by NIIBE Yutaka
** Change of SELECT FILE behavior
Gnuk used to reply AID upon SELECT FILE command. Now, to be compatible
to original OpenPGP card, it returns nothing but status code of 9000.
** Added feature of Factory Reset as compile time option
Original OpenPGP card has the feature, and Gnuk is now configurable to
support the feature.
** Upgrade of Chopstx
We use Chopstx 1.2.
* Major changes in Gnuk 1.2.1
Released 2016-07-11, by NIIBE Yutaka

83
README
View File

@@ -1,14 +1,14 @@
Gnuk - An Implementation of USB Cryptographic Token for GnuPG
Version 1.2.1
2016-07-11
Version 1.2.5
2017-08-11
Niibe Yutaka
Free Software Initiative of Japan
Release Notes
=============
This is the release of Gnuk, version 1.2.1, which has major
This is the release of Gnuk, version 1.2.5, 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. Please update your documentation for Gnuk Token, so
@@ -47,7 +47,7 @@ FAQ
Q0: How Gnuk USB Token is superior than other solutions (OpenPGP
card 2.0, YubiKey, etc.) ?
http://www.g10code.de/p-card.html
https://www.g10code.de/p-card.html
https://www.yubico.com/
A0: Good points of Gnuk are:
* If you have skill of electronics and like DIY, you can build
@@ -77,12 +77,12 @@ 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.13 in
experimental.
A4: In Debian GNU/Linux system, I use GnuPG modern 2.1.18 in
unstable.
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
without them.
Token without them.
I tested pcscd 1.5.5-4 and libccid 1.3.11-2 which were in Debian
squeeze.
@@ -248,7 +248,7 @@ External source code
Gnuk is distributed with external source code.
* chopstx/ -- Chopstx 1.1
* chopstx/ -- Chopstx 1.3
We use Chopstx as the kernel for Gnuk.
@@ -361,10 +361,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.26-4+8
gcc-arm-none-eabi 15:4.9.3+svn231177-1
gdb-arm-none-eabi 7.10-1+9
libnewlib-arm-none-eabi 2.2.0+git20150830.5a3d536-1
binutils-arm-none-eabi 2.28-4+9+b2
gcc-arm-none-eabi 15:5.4.1+svn241155-1
gdb-arm-none-eabi 7.12-6+9+b2
libnewlib-arm-none-eabi 2.4.0.20160527-2
Or else, see https://launchpad.net/gcc-arm-embedded for preparation of
GNU Toolchain for 'arm-none-eabi' target.
@@ -395,18 +395,14 @@ How to install
Olimex STM32-H103 board
-----------------------
If you are using Olimex JTAG-Tiny, type following to invoke OpenOCD:
If you are using Olimex JTAG-Tiny, type following to invoke OpenOCD
and write "gnuk.elf" to Flash ROM:
$ openocd -f interface/ftdi/olimex-jtag-tiny.cfg -f board/olimex_stm32_h103.cfg
$ openocd -f interface/ftdi/olimex-jtag-tiny.cfg \
-f board/olimex_stm32_h103.cfg \
-c "program build/gnuk.elf verify reset exit"
Then, with another terminal, type following to write "gnuk.elf" to Flash ROM:
$ telnet localhost 4444
> reset halt
> flash write_image erase gnuk.elf
> reset
> exit
$
Command invocation is assumed in src/ directory.
Flying Stone Tiny 01
@@ -414,9 +410,10 @@ Flying Stone Tiny 01
If you are using Flying Stone Tiny 01, you need a SWD writer.
OpenOCD 0.9 now supports ST-Link/V2. We can use it:
OpenOCD 0.9.0 now supports ST-Link/V2. We can use it like:
$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x_stlink.cfg
$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \
-c "program build/gnuk.elf verify reset exit"
@@ -435,20 +432,24 @@ Then, reset the board.
How to protect flash ROM
========================
Invoke your OpenOCD and type:
To protect, invoke OpenOCD like (for FST-01):
$ telnet localhost 4444
> reset halt
> stm32f1x lock 0
> reset
> shutdown
$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \
-c init -c "reset halt" -c "stm32f1x lock 0" -c reset -c exit
After power-off / power-on sequence, the contents of flash ROM cannot
be accessible from JTAG debugger.
Unprotecting is:
$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \
-c init -c "reset halt" -c "stm32f1x unlock 0" -c reset -c exit
Upon unprotection, flash is erased.
Note that it would be still possible for some implementation of DfuSe
to access the contents. If you want to protect, killing DfuSe and
accessing by JTAG debugger is recommended.
to access the contents, even if it's protected. If you really want to
protect, killing DfuSe and accessing by JTAG debugger is recommended.
(Optional) Configure serial number and X.509 certificate
@@ -551,6 +552,10 @@ Inside GDB, we can connect OpenOCD by:
(gdb) target remote localhost:3333
or
(gdb) target extended-remote localhost:3333
You can see the output of PCSCD:
@@ -582,14 +587,14 @@ You can browse at: http://git.gniibe.org/gitweb?p=gnuk/gnuk.git;a=summary
I put Chopstx as a submodule of Git. Please do this:
$ git submodule init
$ git submodule update
$ git submodule update --init
We have migrated from ChibiOS/RT to Chopstx in Gnuk 1.1. If you have
old code of ChibiOS/RT, you need:
Gnuk 1.0 uses ChibiOS/RT, and then, we have migrated from to Chopstx
in the development phase of Gnuk 1.1. If you have old code of
ChibiOS/RT, you need:
Edit .git/config to remove chibios reference
git rm --cached chibios
Edit .git/config to remove chibios reference and
$ git rm --cached chibios
Information on the Web
@@ -599,7 +604,7 @@ Please visit: http://www.fsij.org/gnuk/
Please see the FST-01 support pages:
http://www.gniibe.org/category/fst-01.html
https://www.gniibe.org/category/fst-01.html
Please consider to join Gnuk-users mailing list:

5
THANKS
View File

@@ -11,12 +11,14 @@ Achim Pietig achim@pietig.com
Aidan Thornton
Anibal Monsalve Salazar anibal@debian.org
Andre Zepezauer andre.zepezauer@student.uni-halle.de
Anthony Romano anthony.romano@coreos.com
Bertrand Jacquin bertrand@jacquin.bzh
Clint Adams clint@softwarefreedom.org
Daniel Kahn Gillmor dkg@fifthhorseman.net
Elliott Mitchell
Hironobu SUZUKI hironobu@h2np.net
Jan Suhr jan@suhr.info
Jeremy Drake jeremydrake+gnuk@eacceleration.com
Jonathan McDowell noodles@earth.li
Kaz Kojima kkojima@rr.iij4u.or.jp
Kenji Rikitake
@@ -29,9 +31,12 @@ NAGAMI Takeshi nagami-takeshi@aist.go.jp
Nguyễn Hồng Quân quannguyen@mbm.vn
Nico Rikken nico@nicorikken.eu
NOKUBI Takatsugu knok@daionet.gr.jp
Paul Fertser
Paul Bakker polarssl_maintainer@polarssl.org
Santiago Ruano Rincón santiago@debian.org
Shane Coughlan scoughlan@openinventionnetwork.com
Stanislas Bach sbach@0g.re
Szczepan Zalega szczepan@nitrokey.com
Vasily Evseenko
Werner Koch wk@gnupg.org
Yuji Imai ug@xcast.jp

View File

@@ -1 +1 @@
release/1.2.1
release/1.2.5

Submodule chopstx updated: 09f27704f5...d8df82badf

View File

@@ -50,39 +50,9 @@ With the script below, I extract public key of the keygrip
$ ./get_raw_public_key.py 5D6C89682D07CCFC034AF508420BF2276D8018ED
Here is the script, get_raw_public_key.py::
#! /usr/bin/python
import sys, binascii
from subprocess import check_output
def get_gpg_public_key(keygrip):
result = check_output(["gpg-connect-agent", "READKEY %s" % keygrip, "/bye"])
key = ""
while True:
i = result.find('%')
if i < 0:
key += result
break
hex_str = result[i+1:i+3]
key += result[0:i]
key += chr(int(hex_str,16))
result = result[i+3:]
pos = key.index("D (10:public-key(3:rsa(1:n257:") + 31 # skip NUL too
key = key[pos:-17] # )(1:e3:XYZ)))\nOK\n
if len(key) != 256:
raise ValueError, binascii.hexlify(key)
return key
if __name__ == '__main__':
keygrip = sys.argv[1]
k = get_gpg_public_key(keygrip)
shorthand = keygrip[0:8] + ".bin"
f = open(shorthand,"w")
f.write(k)
f.close()
(The script is available in the directory gnuk/tool. Please note that
it was written in the early stage of the development. The quality of
the code is somewhat questionable.)
Then, we can put the data of public key into token by::

7
docker/Dockerfile.check Normal file
View File

@@ -0,0 +1,7 @@
FROM gnuk:latest
LABEL Description="Image for checking gnuK"
RUN apt install -y shellcheck
RUN apt install -y clang libfindbin-libs-perl
RUN apt clean

4
docker/Dockerfile.debug Normal file
View File

@@ -0,0 +1,4 @@
FROM gnuk:latest
LABEL Description="Image for building gnuK with debugging"
RUN apt install -y gdb-arm-none-eabi && apt clean

View File

@@ -0,0 +1,6 @@
FROM debian:latest
LABEL Description="Image for building gnuK"
RUN apt update -y && apt install -y make gcc-arm-none-eabi && apt clean
CMD ["/bin/sh", "-c", "cd /gnuk/src && make clean && ./configure $GNUK_CONFIG && make"]

36
docker/Makefile Normal file
View File

@@ -0,0 +1,36 @@
ifndef GNUK_CONFIG
$(warning configuration flags not set in GNUK_CONFIG)
endif
all: ../chopstx docker-build-release
docker run --user=`id -u` --env GNUK_CONFIG --rm -v `pwd`/..:/gnuk/ -t gnuk:latest
clean: docker-build-release
docker run --user=`id -u` --env GNUK_CONFIG --rm -v `pwd`/..:/gnuk/ -w /gnuk/src -t gnuk:latest make clean
gdb: docker-build-debug
docker run --net host --rm -i -v `pwd`/..:/gnuk/ -t gnuk:latest-debug arm-none-eabi-gdb /gnuk/src/build/gnuk.elf
shellcheck: docker-build-check
docker run --rm -v `pwd`/..:/gnuk/ -t gnuk:latest-check shellcheck /gnuk/src/configure
CHECKERS=security optin nullability core deadcode alpha.core alpha.security
scan-build: clean docker-build-check
docker run --user=`id -u` --rm -v `pwd`/..:/gnuk/ -w /gnuk/src -t gnuk:latest-check scan-build -o scan-build \
-analyze-headers -stats $(addprefix -enable-checker ,$(CHECKERS)) -k \
--use-cc=arm-none-eabi-gcc \
make
../chopstx:
git submodule update --init
docker-build-release:
docker build -t gnuk:latest -f `pwd`/Dockerfile.release ..
docker-build-debug: docker-build-release
docker build -t gnuk:latest-debug -f `pwd`/Dockerfile.debug ..
docker-build-check: docker-build-release
docker build -t gnuk:latest-check -f `pwd`/Dockerfile.check ..
.PHONY: all clean gdb shellcheck scan-build \
docker-build-release docker-build-debug docker-build-check

31
emulation/Makefile Normal file
View File

@@ -0,0 +1,31 @@
SRCS = usbip-server.c usb-emu.c glue.c
OBJS = $(SRCS:.c=.o)
TARGET=gnuk_emulation
GNUKDIR=../src
GNUK_SRCS = main.c call-rsa.c \
usb-ccid.c openpgp.c ac.c openpgp-do.c flash.c \
bn.c mod.c \
modp256r1.c jpc_p256r1.c ec_p256r1.c call-ec_p256r1.c \
modp256k1.c jpc_p256k1.c ec_p256k1.c call-ec_p256k1.c \
mod25638.c ecc-edwards.c ecc-mont.c sha512.c \
random.c neug.c sha256.c
USB_SRCS=usb_desc.c usb_ctrl.c
GNUK_CSRC = $(addprefix $(GNUKDIR)/, $(GNUK_SRCS))
GNUK_OBJS = $(notdir $(GNUK_CSRC:.c=.o))
USB_CSRC = $(addprefix $(GNUKDIR)/, $(USB_SRCS))
USB_OBJS = $(notdir $(USB_CSRC:.c=.o))
# all:
# echo $(GNUK_OBJS)
$(TARGET): $(OBJS) $(USB_OBJS) Makefile
$(CC) -o $(TARGET) $(OBJS) $(USB_OBJS)
$(GNUK_OBJS): %.o : $(GNUKDIR)/%.c Makefile
$(CC) -c $(CFLAGS) -I. -I$(GNUKDIR) -I../chopstx $< -o $@
$(USB_OBJS): %.o : $(GNUKDIR)/%.c Makefile
$(CC) -c $(CFLAGS) -I. -I$(GNUKDIR) -I../chopstx $< -o $@

54
emulation/glue.c Normal file
View File

@@ -0,0 +1,54 @@
#include <stdint.h>
uint8_t _regnual_start;
uint8_t __heap_end__;
int
check_crc32 (const uint32_t *start_p, const uint32_t *end_p)
{
return 0;
}
uint8_t *
sram_address (uint32_t offset)
{
return ((uint8_t *)0x20000000) + offset;
}
const uint8_t sys_version[8] = {
3*2+2, /* bLength */
0x03, /* bDescriptorType = USB_STRING_DESCRIPTOR_TYPE */
/* sys version: "3.0" */
'3', 0, '.', 0, '0', 0,
};
void
led_blink (int spec)
{
}
void
ccid_usb_reset (int full)
{
}
void
ccid_card_change_signal (int how)
{
}
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_EXEC_REQUESTED, /* Exec requested */
};
static enum ccid_state ccid_state;
enum ccid_state *const ccid_state_p = &ccid_state;

103
emulation/usb-emu.c Normal file
View File

@@ -0,0 +1,103 @@
/*
* usb-emu.c - USB driver for USBIP emulation
*
* Copyright (C) 2017 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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.
*
* Chopstx 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/>.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include "../chopstx/usb_lld.h"
int
usb_lld_ctrl_ack (struct usb_dev *dev)
{
return 0;
}
int
usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len)
{
return 0;
}
int
usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen)
{
return 0;
}
uint8_t
usb_lld_current_configuration (struct usb_dev *dev)
{
return 0;
}
void
usb_lld_prepare_shutdown (void)
{
}
void
usb_lld_ctrl_error (struct usb_dev *dev)
{
}
void
usb_lld_reset (struct usb_dev *dev, uint8_t feature)
{
}
void
usb_lld_set_configuration (struct usb_dev *dev, uint8_t config)
{
}
void
usb_lld_shutdown (void)
{
}
void
usb_lld_setup_endpoint (int ep_num, int ep_type, int ep_kind,
int ep_rx_addr, int ep_tx_addr,
int ep_rx_memory_size)
{
}
void
usb_lld_stall (int ep_num)
{
}
void
usb_lld_stall_tx (int ep_num)
{
usb_lld_stall (ep_num);
}
void
usb_lld_stall_rx (int ep_num)
{
usb_lld_stall (ep_num);
}

278
emulation/usbip-server.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* usbip-server.c - USB Device Emulation by USBIP Protocol
*
* Copyright (C) 2017 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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.
*
* Chopstx 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/>.
*
*/
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#define USBIP_PORT 3240
#define CMD_REQ_LIST 0x01118005
#define CMD_REQ_ATTACH 0x01118003
#define CMD_URB 0x00000001
#define CMD_DETACH 0x00000002
struct usbip_msg_head {
uint32_t cmd;
uint32_t seq;
};
#define USBIP_REPLY_HEADER_SIZE 12
#define DEVICE_INFO_SIZE (256+32+12+6+6)
#define INTERFACE_INFO_SIZE 4
#define DEVICE_LIST_SIZE (USBIP_REPLY_HEADER_SIZE+DEVICE_INFO_SIZE*1+INTERFACE_INFO_SIZE*1)
#define USBIP_REPLY_DEVICE_LIST "\x01\x11\x00\x05"
#define NETWORK_UINT32_ZERO "\x00\x00\x00\x00"
#define NETWORK_UINT32_ONE "\x00\x00\x00\x01"
#define NETWORK_UINT32_TWO "\x00\x00\x00\x02"
#define NETWORK_UINT16_FSIJ "\x23\x4b"
#define NETWORK_UINT16_ZERO "\x00\x00"
#define NETWORK_UINT16_ONE_ONE "\x01\x01"
static char *
list_devices (size_t *len_p)
{
char *p0, *p;
*len_p = 0;
p0 = malloc (DEVICE_LIST_SIZE);
if (p0 == NULL)
return NULL;
*len_p = DEVICE_LIST_SIZE;
p = p0;
memcpy (p, USBIP_REPLY_DEVICE_LIST, 4);
p += 4;
memcpy (p, NETWORK_UINT32_ZERO, 4);
p += 4;
memcpy (p, NETWORK_UINT32_ONE, 4);
p += 4;
memset (p, 0, 256);
strcpy (p, "/sys/devices/pci0000:00/0000:00:01.1/usb1/1-1");
p += 256;
memset (p, 0, 32);
strcpy (p, "1-1");
p += 32;
memcpy (p, NETWORK_UINT32_ONE, 4); /* Bus */
p += 4;
memcpy (p, NETWORK_UINT32_TWO, 4); /* Dev */
p += 4;
memcpy (p, NETWORK_UINT32_ONE, 4); /* Speed */
p += 4;
memcpy (p, NETWORK_UINT16_FSIJ, 2);
p += 2;
memcpy (p, NETWORK_UINT16_ZERO, 2); /* Gnuk */
p += 2;
memcpy (p, NETWORK_UINT16_ONE_ONE, 2); /* USB 1.1 */
p += 2;
*p++ = 0; /* bDeviceClass */
*p++ = 0; /* bDeviceSubClass */
*p++ = 0; /* bDeviceProtocol */
*p++ = 0; /* bConfigurationValue */
*p++ = 1; /* bConfigurationValue */
*p++ = 1; /* bNumInterfaces */
*p++ = 11; /* bInterfaceClass */
*p++ = 0; /* bInterfaceSubClass */
*p++ = 0; /* bInterfaceProtocol */
*p++ = 0; /* ----pad----------- */
return p0;
}
static char *
attach_device (char busid[32], size_t *len_p)
{
*len_p = 0;
return NULL;
}
static int
handle_urb (int fd)
{
return 0;
}
static void
run_server (void)
{
int sock;
struct sockaddr_in v4addr;
const int one = 1;
if ((sock = socket (PF_INET, SOCK_STREAM, 0)) < 0)
{
perror ("socket");
exit (1);
}
if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
(const char*)&one, sizeof (int)) < 0)
perror ("WARN: setsockopt");
memset (&v4addr, 0, sizeof (v4addr));
v4addr.sin_family = AF_INET;
v4addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
v4addr.sin_port = htons (USBIP_PORT);
if (bind (sock, (const struct sockaddr *)&v4addr, sizeof (v4addr)) < 0)
{
perror ("bind");
exit (1);
}
/* We only accept a connection from a single client. */
if (listen (sock, 1) < 0)
{
perror ("listen");
exit (1);
}
for (;;)
{
int fd;
int attached = 0;
/* We don't care who is connecting. */
if ((fd = accept (sock, NULL, NULL)) < 0)
{
perror ("accept");
exit (1);
}
for (;;)
{
struct usbip_msg_head msg;
if (recv (fd, (char *)&msg, sizeof (msg), 0) != sizeof (msg))
{
perror ("msg recv");
break;
}
msg.cmd = ntohl (msg.cmd);
msg.seq = ntohl (msg.seq);
if (msg.cmd == CMD_REQ_LIST)
{
char *device_list;
size_t device_list_size;
if (attached)
{
fprintf (stderr, "REQ list while attached\n");
break;
}
device_list = list_devices (&device_list_size);
if (send (fd, device_list, device_list_size, 0) != device_list_size)
{
perror ("list send");
break;
}
free (device_list);
}
else if (msg.cmd == CMD_REQ_ATTACH)
{
char busid[32];
char *attach;
size_t attach_size;
if (attached)
{
fprintf (stderr, "REQ attach while attached\n");
break;
}
if (recv (fd, busid, 32, 0) != 32)
{
perror ("attach recv");
break;
}
attach = attach_device (busid, &attach_size);
if (send (fd, attach, attach_size, 0) != attach_size)
{
perror ("list send");
break;
}
free (attach);
attached = 1;
}
else if (msg.cmd == CMD_URB)
{
if (!attached)
{
fprintf (stderr, "URB while attached\n");
break;
}
if (handle_urb (fd) < 0)
{
fprintf (stderr, "URB handling failed\n");
break;
}
}
else if(msg.cmd == CMD_DETACH)
{
if (!attached)
{
fprintf (stderr, "DETACH while attached\n");
break;
}
/* send reply??? */
break;
}
else
{
fprintf (stderr, "Unknown command %08x, disconnecting.\n", msg.cmd);
break;
}
}
close (fd);
}
}
int
main (int argc, const char *argv[])
{
run_server ();
return 0;
}

BIN
gnuk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -1632,7 +1632,6 @@ int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR )
* Init temps and window size
*/
mpi_montg_init( &mm, N );
MPI_CHK( mpi_grow( X, N->n ) );
/*
* If 1st call, pre-compute R^2 mod N
@@ -1658,6 +1657,8 @@ int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR )
memset (d, 0, N->n * ciL); /* Set lower half of D zero. */
}
MPI_CHK( mpi_grow( X, N->n ) );
/*
* W[1] = A * R^2 * R^-1 mod N = A * R mod N
*/

View File

@@ -5,6 +5,8 @@ PROJECT = regnual
OBJS = regnual.o usb-stm32f103.o reset.o
LDSCRIPT= regnual.ld
include ../src/config.mk
###################################
MCU = cortex-m3
@@ -19,7 +21,7 @@ TOPT = -mthumb -DTHUMB -mno-thumb-interwork
# Define C warning options here
CWARN = -Wall -Wextra -Wstrict-prototypes
MCFLAGS= -mcpu=$(MCU)
DEFS = -DFREE_STANDING
DEFS += -DFREE_STANDING
CFLAGS = -O2 -g
CFLAGS += -Wa,-alms=$(notdir $(<:.c=.lst)) -fpie

View File

@@ -1,7 +1,7 @@
/*
* regnual.c -- Firmware installation for STM32F103 Flash ROM
*
* Copyright (C) 2012, 2013, 2015, 2016
* Copyright (C) 2012, 2013, 2015, 2016, 2017
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -365,7 +365,11 @@ main (int argc, char *argv[])
set_led (0);
#if defined(STM32F103_OVERRIDE_FLASH_SIZE_KB)
flash_end = FLASH_START_ADDR + STM32F103_OVERRIDE_FLASH_SIZE_KB*1024;
#else
flash_end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024;
#endif
/*
* NVIC interrupt priority was set by Gnuk.

View File

@@ -8,8 +8,8 @@ CHOPSTX = ../chopstx
# Define linker script file here
LDSCRIPT= gnuk.ld
CSRC = main.c usb_desc.c usb_ctrl.c \
call-rsa.c \
CSRC = main.c call-rsa.c mcu-stm32f103.c \
usb_desc.c usb_ctrl.c \
usb-ccid.c openpgp.c ac.c openpgp-do.c flash.c \
bn.c mod.c \
modp256r1.c jpc_p256r1.c ec_p256r1.c call-ec_p256r1.c \
@@ -27,9 +27,7 @@ CRYPTSRC = $(CRYPTSRCDIR)/bignum.c $(CRYPTSRCDIR)/rsa.c $(CRYPTSRCDIR)/aes.c
CSRC += $(CRYPTSRC)
INCDIR += $(CRYPTINCDIR)
@PINPAD_MAKE_OPTION@
@DEBUG_MAKE_OPTION@
@HEXOUTPUT_MAKE_OPTION@
include config.mk
USE_EVENTFLAG = yes
@@ -58,8 +56,6 @@ OBJCOPY = $(CROSS)objcopy
MCU = cortex-m3
CWARN = -Wall -Wextra -Wstrict-prototypes
# DEFS: Add
DEFS = @USE_SYS3@
OPT = -O3 -Os -g
LIBS =
@@ -75,5 +71,5 @@ sys.c: board.h
build/bignum.o: OPT = -O3 -g
distclean: clean
-rm -f gnuk.ld config.h board.h Makefile \
-rm -f gnuk.ld config.h board.h config.mk \
usb-strings.c.inc usb-vid-pid-ver.c.inc

View File

@@ -30,7 +30,7 @@
#include "config.h"
#include "gnuk.h"
#include "openpgp.h"
#include "status-code.h"
#include "random.h"
#include "polarssl/config.h"
#include "polarssl/rsa.h"
@@ -130,9 +130,11 @@ modulus_calc (const uint8_t *p, int len)
cleanup:
mpi_free (&P); mpi_free (&Q); mpi_free (&N);
if (ret != 0)
return NULL;
else
return modulus;
{
free (modulus);
return NULL;
}
return modulus;
}
@@ -261,23 +263,14 @@ rsa_genkey (int pubkey_len)
cs = chopstx_setcancelstate (0); /* Allow cancellation. */
MPI_CHK( rsa_gen_key (&rsa_ctx, random_gen, &index, pubkey_len * 8,
RSA_EXPONENT) );
if (ret != 0)
{
chopstx_setcancelstate (cs);
chopstx_cleanup_pop (0);
free (p_q_modulus);
rsa_free (&rsa_ctx);
return NULL;
}
MPI_CHK( mpi_write_binary (&rsa_ctx.P, p, pubkey_len / 2) );
MPI_CHK( mpi_write_binary (&rsa_ctx.Q, q, pubkey_len / 2) );
MPI_CHK( mpi_write_binary (&rsa_ctx.N, modulus, pubkey_len) );
clp.arg = NULL;
cleanup:
chopstx_setcancelstate (cs);
chopstx_cleanup_pop (0);
rsa_free (&rsa_ctx);
chopstx_cleanup_pop (1);
if (ret != 0)
return NULL;
else

View File

@@ -8,3 +8,4 @@
@CERTDO_DEFINE@
@HID_CARD_CHANGE_DEFINE@
@SERIALNO_STR_LEN_DEFINE@
@LIFE_CYCLE_MANAGEMENT_DEFINE@

87
src/configure vendored
View File

@@ -6,7 +6,7 @@ nl=$'\n'
#
# This file is *NOT* generated by GNU Autoconf, but written by NIIBE Yutaka
#
# Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016
# Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
# Free Software Initiative of Japan
#
# This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -36,33 +36,38 @@ fi
help=no
vidpid=none
target=FST_01
verbose=no
with_dfu=default
debug=no
sys1_compat=yes
pinpad=no
certdo=no
hid_card_change=no
factory_reset=no
flash_override=""
# Revision number
if test -d ../.git; then
REVISION=`git describe --dirty="-modified"`
if test -e ../.git; then
if type git >/dev/null 2>&1; then
REVISION=$(git describe --dirty="-modified")
else
# echo 'No git available, please install git'
GIT_REVISION=$(sed -e 's/^\(.......\).*$/g\1/' "../.git/$(sed -e 's/^ref: //' ../.git/HEAD)")
REVISION=$(cat ../VERSION)-$GIT_REVISION
fi
else
REVISION=`cat ../VERSION`
REVISION=$(cat ../VERSION)
fi
# Process each option
for option; do
case $option in
*=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;;
*=*) optarg=$(expr "X$option" : '[^=]*=\(.*\)') ;;
*) optarg=yes ;;
esac
case $option in
-h | --help)
help=yes ;;
-v | --verbose)
verbose=yes ;;
--vidpid=*)
vidpid=$optarg ;;
--target=*)
@@ -87,6 +92,10 @@ for option; do
sys1_compat=yes ;;
--disable-sys1-compat)
sys1_compat=no ;;
--enable-factory-reset)
factory_reset=yes ;;
--disable-factory-reset)
factory_reset=no ;;
--with-dfu)
with_dfu=yes ;;
--without-dfu)
@@ -111,15 +120,17 @@ Configuration:
--target=TARGET specify target [FST_01]
supported targets are:
FST_01
FST_01G
OLIMEX_STM32_H103
STM32_PRIMER2
STBEE
STBEE_MINI
MAPLE_MINI
ST_DONGLE
ST_NUCLEO_F103
NITROKEY_START
BLUE_PILL
CQ_STARM
STM32_PRIMER2
STBEE
STBEE_MINI
FST_01_00 (unreleased version with 8MHz XTAL)
--enable-debug debug with virtual COM port [no]
--enable-pinpad=cir
@@ -130,6 +141,8 @@ Configuration:
--disable-sys1-compat disable SYS 1.0 compatibility [no]
executable is target independent
but requires SYS 2.0 or newer
--enable-factory-reset
support life cycle management [no]
--with-dfu build image for DFU [<target specific>]
EOF
exit 0
@@ -140,9 +153,9 @@ if test "$vidpid" = "none"; then
exit 1
fi
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
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
# Flash page size in byte
FLASH_PAGE_SIZE=1024
@@ -153,6 +166,8 @@ MEMORY_SIZE=20
# Settings for TARGET
case $target in
BLUE_PILL)
flash_override="-DSTM32F103_OVERRIDE_FLASH_SIZE_KB" ;;
CQ_STARM|STBEE_MINI)
if test "$with_dfu" = "default"; then
with_dfu=yes;
@@ -189,13 +204,14 @@ fi
# --with-dfu option
if test "$with_dfu" = "yes"; then
if test "$target" = "FST_01" -o "$target" = "FST_01_00"; then
if test "$target" = "FST_01" -o "$target" = "FST_01G" \
-o "$target" = "FST_01_00"; then
echo "FST-01 doesn't have DFU loader, you should not use --with-dfu." >&2
exit 1
fi
echo "Configured for DFU"
ORIGIN=0x08003000
FLASH_SIZE=`expr $FLASH_SIZE - 12`
FLASH_SIZE=$((FLASH_SIZE - 12))
DFU_DEFINE="#define DFU_SUPPORT 1"
HEXOUTPUT_MAKE_OPTION="ENABLE_OUTPUT_HEX=yes"
else
@@ -246,8 +262,17 @@ else
echo "Card insert/removal by HID device is NOT supported"
fi
# --enable-factory-reset option
if test "$factory_reset" = "yes"; then
LIFE_CYCLE_MANAGEMENT_DEFINE="#define LIFE_CYCLE_MANAGEMENT_SUPPORT 1"
echo "Life cycle management is supported"
else
LIFE_CYCLE_MANAGEMENT_DEFINE="#undef LIFE_CYCLE_MANAGEMENT_SUPPORT"
echo "Life cycle management is NOT supported"
fi
### !!! Replace following string of "FSIJ" to yours !!! ####
SERIALNO="FSIJ-`cat ../VERSION | sed -e 's%^[^/]*/%%'`-"
SERIALNO="FSIJ-$(sed -e 's%^[^/]*/%%' <../VERSION)-"
SERIALNO_STR_LEN_DEFINE="#define SERIALNO_STR_LEN ${#SERIALNO}"
@@ -267,8 +292,8 @@ else
fi
output_vid_pid_version () {
echo $VIDPID | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\):\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* idVendor */\\${nl} 0x\4, 0x\3, /* idProduct */%p"
echo $VERSION | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* bcdDevice */%p"
echo "$VIDPID" | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\):\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* idVendor */\\${nl} 0x\4, 0x\3, /* idProduct */%p"
echo "$VERSION" | sed -n -e "s%^\([0-9a-f][0-9a-f]\)\([0-9a-f][0-9a-f]\)$% 0x\2, 0x\1, /* bcdDevice */%p"
}
output_vendor_product_serial_strings () {
@@ -278,14 +303,14 @@ output_vendor_product_serial_strings () {
echo " ${#VENDOR}*2+2, /* bLength */"
echo " STRING_DESCRIPTOR, /* bDescriptorType */"
echo " /* Manufacturer: \"$VENDOR\" */"
echo $VENDOR | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo "$VENDOR" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo '};'
echo
echo "static const uint8_t ${prefix}string_product[] = {"
echo " ${#PRODUCT}*2+2, /* bLength */"
echo " STRING_DESCRIPTOR, /* bDescriptorType */"
echo " /* Product name: \"$PRODUCT\" */"
echo $PRODUCT | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo "$PRODUCT" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo '};'
if test -n "$prefix"; then
@@ -294,7 +319,7 @@ output_vendor_product_serial_strings () {
echo " ${#SERIALNO}*2+2+16, /* bLength */"
echo " STRING_DESCRIPTOR, /* bDescriptorType */"
echo " /* Serial number: \"$SERIALNO\" */"
echo $SERIALNO | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo "$SERIALNO" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo " 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,"
echo " 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,"
echo '};'
@@ -304,7 +329,7 @@ output_vendor_product_serial_strings () {
echo " ${#REVISION}*2+2, /* bLength */"
echo " STRING_DESCRIPTOR, /* bDescriptorType */"
echo " /* revision detail: \"$REVISION\" */"
echo $REVISION | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo "$REVISION" | sed -e "s/\(........\)/\1\\${nl}/g" | sed -n -e "s/\(.\)/'\1', 0, /g" -e "s/^/ /" -e "/^ ./s/ $//p"
echo '};'
echo
echo "static const uint8_t ${prefix}config_options[] = {"
@@ -317,8 +342,8 @@ output_vendor_product_serial_strings () {
fi
}
if !(IFS=" "
while read VIDPID VERSION PRODUCT VENDOR; do
if ! (IFS=" "
while read -r VIDPID VERSION PRODUCT VENDOR; do
if test "$vidpid" = "$VIDPID"; then
output_vid_pid_version > usb-vid-pid-ver.c.inc
output_vendor_product_serial_strings gnuk_ >usb-strings.c.inc
@@ -343,11 +368,12 @@ else
fi
sed -e "s%@USE_SYS3@%$use_sys3%" \
-e "s%@DEBUG_MAKE_OPTION@%$DEBUG_MAKE_OPTION%" \
-e "s%@PINPAD_MAKE_OPTION@%$PINPAD_MAKE_OPTION%" \
-e "s%@HEXOUTPUT_MAKE_OPTION@%$HEXOUTPUT_MAKE_OPTION%" \
< Makefile.in > Makefile
echo -e "DEFS=$use_sys3 $flash_override" '\n' \
"$DEBUG_MAKE_OPTION" '\n' \
"$PINPAD_MAKE_OPTION" '\n' \
"$HEXOUTPUT_MAKE_OPTION" \
> config.mk
if test "$certdo" = "yes"; then
sed -e "/^@CERTDO_SUPPORT_START@$/ d" -e "/^@CERTDO_SUPPORT_END@$/ d" \
-e "s/@ORIGIN@/$ORIGIN/" -e "s/@FLASH_SIZE@/$FLASH_SIZE/" \
@@ -373,6 +399,7 @@ sed -e "s/@DEBUG_DEFINE@/$DEBUG_DEFINE/" \
-e "s/@PINPAD_MORE_DEFINE@/$PINPAD_MORE_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/@SERIALNO_STR_LEN_DEFINE@/$SERIALNO_STR_LEN_DEFINE/" \
< config.h.in > config.h
exit 0

View File

@@ -384,7 +384,7 @@ FUNC(check_secret) (const bn256 *d0, bn256 *d1)
{
ac Q0[1], Q1[1];
if (bn256_is_zero (d0) || bn256_sub (d1, N, d0) <= 0)
if (bn256_is_zero (d0) || bn256_sub (d1, N, d0) != 0)
/* == 0 or >= N, it's not valid. */
return 0;

View File

@@ -1,7 +1,7 @@
/*
* flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM
*
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -75,7 +75,7 @@ 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"))) = {
0x01, 0x00, 0xff, 0xff
0x00, 0x00, 0xff, 0xff
};
/* Linker set this symbol */
@@ -102,8 +102,8 @@ static int key_available_at (const uint8_t *k, int key_size)
#define CHIP_ID_REG ((uint32_t *)0xe0042000)
const uint8_t *
flash_init (void)
void
flash_init (const uint8_t **p_do_start, const uint8_t **p_do_end)
{
uint16_t gen0, gen1;
uint16_t *gen0_p = (uint16_t *)&_data_pool;
@@ -114,22 +114,60 @@ flash_init (void)
flash_page_size = 2048;
gen1_p = (uint16_t *)(&_data_pool + flash_page_size);
data_pool = &_data_pool;
/* Check data pool generation and choose the page */
gen0 = *gen0_p;
gen1 = *gen1_p;
if (gen0 == 0xffff && gen1 == 0xffff)
{
/* It's terminated. */
*p_do_start = *p_do_end = NULL;
return;
}
if (gen0 == 0xffff)
/* Use another page if a page is erased. */
data_pool = &_data_pool + flash_page_size;
else if (gen1 == 0xffff)
/* Or use different page if another page is erased. */
data_pool = &_data_pool;
else if (gen1 > gen0)
else if ((gen0 == 0xfffe && gen1 == 0) || gen1 > gen0)
/* When both pages have valid header, use newer page. */
data_pool = &_data_pool + flash_page_size;
else
data_pool = &_data_pool;
return data_pool + FLASH_DATA_POOL_HEADER_SIZE;
*p_do_start = data_pool + FLASH_DATA_POOL_HEADER_SIZE;
*p_do_end = data_pool + flash_page_size;
}
static uint8_t *flash_key_getpage (enum kind_of_key kk);
void
flash_terminate (void)
{
int i;
for (i = 0; i < 3; i++)
flash_erase_page ((uint32_t)flash_key_getpage (i));
flash_erase_page ((uint32_t)&_data_pool);
flash_erase_page ((uint32_t)(&_data_pool + flash_page_size));
data_pool = &_data_pool;
last_p = &_data_pool + FLASH_DATA_POOL_HEADER_SIZE;
#if defined(CERTDO_SUPPORT)
flash_erase_page ((uint32_t)&ch_certificate_start);
if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size)
flash_erase_page ((uint32_t)(&ch_certificate_start + flash_page_size));
#endif
}
void
flash_activate (void)
{
flash_program_halfword ((uint32_t)&_data_pool, 0);
}
void
flash_init_keys (void)
{
@@ -209,8 +247,12 @@ flash_copying_gc (void)
generation = *(uint16_t *)src;
data_pool = dst;
gpg_data_copy (data_pool + FLASH_DATA_POOL_HEADER_SIZE);
if (generation == 0xfffe)
generation = 0;
else
generation++;
flash_program_halfword ((uint32_t)dst, generation);
flash_erase_page ((uint32_t)src);
flash_program_halfword ((uint32_t)dst, generation+1);
return 0;
}

View File

@@ -100,12 +100,15 @@ void ac_fini (void);
void set_res_sw (uint8_t sw1, uint8_t sw2);
extern uint8_t file_selection;
extern const uint8_t historical_bytes[];
extern uint16_t data_objects_number_of_bytes;
#define CHALLENGE_LEN 32
void gpg_data_scan (const uint8_t *p);
void gpg_data_scan (const uint8_t *start, const uint8_t *end);
void gpg_data_copy (const uint8_t *p);
void gpg_do_terminate (void);
void gpg_do_get_data (uint16_t tag, int with_tag);
void gpg_do_put_data (uint16_t tag, const uint8_t *data, int len);
void gpg_do_public_key (uint8_t kk_byte);
@@ -136,7 +139,9 @@ enum size_of_key {
int gpg_get_algo_attr (enum kind_of_key kk);
int gpg_get_algo_attr_key_size (enum kind_of_key kk, enum size_of_key s);
const uint8_t *flash_init (void);
void flash_init (const uint8_t **, const uint8_t **);
void flash_terminate (void);
void flash_activate (void);
void flash_init_keys (void);
void flash_do_release (const uint8_t *);
const uint8_t *flash_do_write (uint8_t nr, const uint8_t *data, int len);
@@ -296,6 +301,7 @@ void gpg_increment_digital_signature_counter (void);
void fatal (uint8_t code) __attribute__ ((noreturn));
#define FATAL_FLASH 1
#define FATAL_RANDOM 2
#define FATAL_HEAP 3
extern uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE];
extern uint8_t admin_authorized;
@@ -454,3 +460,6 @@ int pinpad_getline (int msg_code, uint32_t timeout_usec);
#endif
extern uint8_t _regnual_start, __heap_end__[];
int check_crc32 (const uint32_t *start_p, const uint32_t *end_p);
uint8_t * sram_address (uint32_t offset);

View File

@@ -3,9 +3,9 @@
*/
__main_stack_size__ = 0x0080; /* Exception handlers */
__process0_stack_size__ = 0x0100; /* main */
__process1_stack_size__ = 0x0180; /* ccid */
__process1_stack_size__ = 0x01a0; /* ccid */
__process2_stack_size__ = 0x0180; /* rng */
__process3_stack_size__ = 0x1640; /* gpg */
__process3_stack_size__ = 0x1660; /* gpg */
__process4_stack_size__ = 0; /* --- */
__process5_stack_size__ = @MSC_SIZE@; /* msc */
__process6_stack_size__ = @TIM_SIZE@; /* intr: timer */
@@ -171,10 +171,6 @@ SECTIONS
.gnuk_flash :
{
. = ALIGN (@FLASH_PAGE_SIZE@);
_data_pool = .;
KEEP(*(.gnuk_data))
. = ALIGN(@FLASH_PAGE_SIZE@);
. += @FLASH_PAGE_SIZE@;
_keystore_pool = .;
. += 512;
. = ALIGN(@FLASH_PAGE_SIZE@);
@@ -185,6 +181,10 @@ SECTIONS
_updatekey_store = .;
. += 1024;
. = ALIGN(@FLASH_PAGE_SIZE@);
_data_pool = .;
KEEP(*(.gnuk_data))
. = ALIGN(@FLASH_PAGE_SIZE@);
. += @FLASH_PAGE_SIZE@;
} > flash =0xffffffff
}

View File

@@ -354,6 +354,7 @@ extern uint8_t __heap_end__[];
#define MEMORY_END (__heap_end__)
#define MEMORY_ALIGNMENT 16
#define MEMORY_ALIGN(n) (((n) + MEMORY_ALIGNMENT - 1) & ~(MEMORY_ALIGNMENT - 1))
#define MEMORY_SIZE ((uintptr_t)__heap_end__ - (uintptr_t)__heap_base__)
static uint8_t *heap_p;
static chopstx_mutex_t malloc_mtx;
@@ -365,6 +366,10 @@ struct mem_head {
struct mem_head *neighbor; /* backlink to neighbor */
};
#define MEM_HEAD_IS_CORRUPT(x) \
((x)->size != MEMORY_ALIGN((x)->size) || (x)->size > MEMORY_SIZE)
#define MEM_HEAD_CHECK(x) if (MEM_HEAD_IS_CORRUPT(x)) fatal (FATAL_HEAP)
static struct mem_head *free_list;
static void
@@ -421,7 +426,7 @@ gnuk_malloc (size_t size)
m->size = size;
break;
}
MEM_HEAD_CHECK (m);
if (m->size == size)
{
remove_from_free_list (m);
@@ -457,15 +462,20 @@ gnuk_free (void *p)
struct mem_head *m = (struct mem_head *)((void *)p - sizeof (uint32_t));
struct mem_head *m0;
if (p == NULL)
return;
chopstx_mutex_lock (&malloc_mtx);
m0 = free_list;
DEBUG_INFO ("free: ");
DEBUG_SHORT (m->size);
DEBUG_WORD ((uint32_t)p);
MEM_HEAD_CHECK (m);
m->neighbor = NULL;
while (m0)
{
MEM_HEAD_CHECK (m0);
if ((void *)m + m->size == (void *)m0)
m0->neighbor = m;
else if ((void *)m0 + m0->size == (void *)m)
@@ -481,6 +491,7 @@ gnuk_free (void *p)
heap_p -= m->size;
while (mn)
{
MEM_HEAD_CHECK (mn);
heap_p -= mn->size;
remove_from_free_list (mn);
mn = mn->neighbor;

56
src/mcu-stm32f103.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* mcu-stm32f103.c - STM32F103 specific routines
*
* Copyright (C) 2017
* 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/>.
*
*/
#include <stdint.h>
#include "mcu/stm32f103.h"
static uint32_t
rbit (uint32_t v)
{
uint32_t r;
asm ("rbit %0, %1" : "=r" (r) : "r" (v));
return r;
}
int
check_crc32 (const uint32_t *start_p, const uint32_t *end_p)
{
uint32_t crc32 = *end_p;
const uint32_t *p;
RCC->AHBENR |= RCC_AHBENR_CRCEN;
CRC->CR = CRC_CR_RESET;
for (p = start_p; p < end_p; p++)
CRC->DR = rbit (*p);
return (rbit (CRC->DR) ^ crc32) == 0xffffffff;
}
uint8_t *
sram_address (uint32_t offset)
{
return ((uint8_t *)0x20000000) + offset;
}

View File

@@ -36,7 +36,6 @@ mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, const bn256 *MU_lower)
bn512 q_big[1], tmp[1];
uint32_t carry;
#define borrow carry
uint32_t borrow_next;
memset (q, 0, sizeof (bn256));
q->word[0] = A->word[15];
@@ -110,9 +109,7 @@ mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, const bn256 *MU_lower)
= tmp->word[11] = tmp->word[10] = tmp->word[9] = 0;
borrow = bn256_sub (X, (bn256 *)&q_big->word[0], (bn256 *)&tmp->word[0]);
borrow_next = (q_big->word[8] < borrow);
q_big->word[8] -= borrow;
borrow_next += (q_big->word[8] < tmp->word[8]);
q_big->word[8] -= tmp->word[8];
carry = q_big->word[8];
@@ -122,7 +119,7 @@ mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, const bn256 *MU_lower)
bn256_sub (q, X, B);
if (carry)
carry -= bn256_sub (X, X, B);
bn256_sub (X, X, B);
else
bn256_sub (q, X, B);
@@ -159,6 +156,7 @@ mod_inv (bn256 *C, const bn256 *X, const bn256 *N)
#define borrow carry
int n = MAX_GCD_STEPS_BN256;
memset (tmp, 0, sizeof (bn256));
memset (C, 0, sizeof (bn256));
memcpy (u, X, sizeof (bn256));
memcpy (v, N, sizeof (bn256));

View File

@@ -1,7 +1,7 @@
/*
* openpgp-do.c -- OpenPGP card Data Objects (DO) handling
*
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -30,7 +30,7 @@
#include "sys.h"
#include "gnuk.h"
#include "openpgp.h"
#include "status-code.h"
#include "random.h"
#include "polarssl/config.h"
#include "polarssl/aes.h"
@@ -40,7 +40,7 @@
#define CLEAN_PAGE_FULL 1
#define CLEAN_SINGLE 0
static void gpg_do_delete_prvkey (enum kind_of_key kk, int clean_page_full);
static void gpg_reset_digital_signature_counter (void);
#define PASSWORD_ERRORS_MAX 3 /* >= errors, it will be locked */
static const uint8_t *pw_err_counter_p[3];
@@ -90,11 +90,11 @@ uint16_t data_objects_number_of_bytes;
/*
* Compile time vars:
* Historical Bytes (template), Extended Capabilities.
* Historical Bytes, Extended Capabilities.
*/
/* Historical Bytes (template) */
static const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = {
/* Historical Bytes */
const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = {
10,
0x00,
0x31, 0x84, /* Full DF name, GET DATA, MF */
@@ -102,7 +102,12 @@ static const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = {
0x80, 0x01, 0x80, /* Full DF name */
/* 1-byte */
/* Command chaining, No extended Lc and Le */
0x00, 0x90, 0x00 /* Status info (no life cycle management) */
#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
0x05,
#else
0x00,
#endif
0x90, 0x00 /* Status info */
};
/* Extended Capabilities */
@@ -245,6 +250,28 @@ gpg_get_algo_attr (enum kind_of_key kk)
return algo_attr_p[1];
}
static void
gpg_reset_algo_attr (enum kind_of_key kk)
{
gpg_do_delete_prvkey (kk, CLEAN_PAGE_FULL);
if (kk == GPG_KEY_FOR_SIGNING)
{
gpg_reset_digital_signature_counter ();
gpg_do_write_simple (NR_DO_FP_SIG, NULL, 0);
gpg_do_write_simple (NR_DO_KGTIME_SIG, NULL, 0);
}
else if (kk == GPG_KEY_FOR_DECRYPTION)
{
gpg_do_write_simple (NR_DO_FP_DEC, NULL, 0);
gpg_do_write_simple (NR_DO_KGTIME_DEC, NULL, 0);
}
else
{
gpg_do_write_simple (NR_DO_FP_AUT, NULL, 0);
gpg_do_write_simple (NR_DO_KGTIME_AUT, NULL, 0);
}
}
static const uint8_t *
get_algo_attr_data_object (enum kind_of_key kk)
{
@@ -331,7 +358,7 @@ gpg_write_digital_signature_counter (const uint8_t *p, uint32_t dsc)
else
{
hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2);
hw1 = NR_COUNTER_DS_LSB;
hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8);
flash_put_data_internal (p, hw0);
flash_put_data_internal (p+2, hw1);
return p+4;
@@ -485,23 +512,6 @@ copy_tag (uint16_t tag)
}
}
static int
do_hist_bytes (uint16_t tag, int with_tag)
{
/*
* Currently, we support no life cycle management. In case of Gnuk,
* user could flash the MCU with SWD/JTAG, instead. It is also
* possible for user to do firmware upgrade through USB.
*
* Thus, here, it just returns the template as is.
*
* In future (when Gnuk will be on the real smartcard),
* we can support life cycle management by implementing
* TERMINATE DF / ACTIVATE FILE and fix code around here.
*/
copy_do_1 (tag, historical_bytes, with_tag);
return 1;
}
#define SIZE_FP 20
#define SIZE_KGTIME 4
@@ -761,15 +771,15 @@ rw_algorithm_attr (uint16_t tag, int with_tag,
return 0; /* Error. */
else if (algo == ALGO_RSA2K && *algo_attr_pp != NULL)
{
gpg_do_delete_prvkey (kk, CLEAN_PAGE_FULL);
gpg_reset_algo_attr (kk);
flash_enum_clear (algo_attr_pp);
if (*algo_attr_pp != NULL)
return 0;
}
else if ((algo != ALGO_RSA2K && *algo_attr_pp == NULL)
|| (*algo_attr_pp)[1] != algo)
else if ((algo != ALGO_RSA2K && *algo_attr_pp == NULL) ||
(*algo_attr_pp != NULL && (*algo_attr_pp)[1] != algo))
{
gpg_do_delete_prvkey (kk, CLEAN_PAGE_FULL);
gpg_reset_algo_attr (kk);
*algo_attr_pp = flash_enum_write (kk_to_nr (kk), algo);
if (*algo_attr_pp == NULL)
return 0;
@@ -1042,6 +1052,28 @@ gpg_do_delete_prvkey (enum kind_of_key kk, int clean_page_full)
}
}
void
gpg_do_terminate (void)
{
int i;
for (i = 0; i < 3; i++)
kd[i].pubkey = NULL;
for (i = 0; i < NR_DO__LAST__; i++)
do_ptr[i] = NULL;
num_prv_keys = 0;
data_objects_number_of_bytes = 0;
digital_signature_counter = 0;
pw1_lifetime_p = NULL;
pw_err_counter_p[PW_ERR_PW1] = NULL;
pw_err_counter_p[PW_ERR_RC] = NULL;
pw_err_counter_p[PW_ERR_PW3] = NULL;
algo_attr_sig_p = algo_attr_dec_p = algo_attr_aut_p = NULL;
}
static int
gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data,
int prvkey_len, const uint8_t *keystring_admin,
@@ -1459,7 +1491,6 @@ gpg_do_table[] = {
{ GPG_DO_NAME, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[12] },
{ GPG_DO_LANGUAGE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[13] },
/* Pseudo DO READ: calculated */
{ GPG_DO_HIST_BYTES, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_hist_bytes },
{ GPG_DO_FP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_fp_all },
{ GPG_DO_CAFP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_cafp_all },
{ GPG_DO_KGTIME_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_kgtime_all },
@@ -1476,6 +1507,7 @@ gpg_do_table[] = {
{ GPG_DO_ALG_AUT, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED,
rw_algorithm_attr },
/* 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 },
/* Compound data: Read access only */
{ GPG_DO_CH_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ch_data },
@@ -1501,7 +1533,7 @@ gpg_do_table[] = {
* Reading data from Flash ROM, initialize DO_PTR, PW_ERR_COUNTERS, etc.
*/
void
gpg_data_scan (const uint8_t *p_start)
gpg_data_scan (const uint8_t *do_start, const uint8_t *do_end)
{
const uint8_t *p;
int i;
@@ -1514,10 +1546,15 @@ gpg_data_scan (const uint8_t *p_start)
pw_err_counter_p[PW_ERR_RC] = NULL;
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;
/* When the card is terminated no data objects are valid. */
if (do_start == NULL)
return;
/* Traverse DO, counters, etc. in DATA pool */
p = p_start;
while (*p != NR_EMPTY)
p = do_start;
while (p < do_end && *p != NR_EMPTY)
{
uint8_t nr = *p++;
uint8_t second_byte = *p;
@@ -1529,7 +1566,9 @@ gpg_data_scan (const uint8_t *p_start)
if (nr < 0x80)
{
/* It's Data Object */
do_ptr[nr] = p;
if (nr < NR_DO__LAST__)
do_ptr[nr] = p;
p += second_byte + 1; /* second_byte has length */
if (((uint32_t)p & 1))

View File

@@ -1,7 +1,7 @@
/*
* openpgp.c -- OpenPGP card protocol support
*
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016
* Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -31,7 +31,7 @@
#include "gnuk.h"
#include "sys.h"
#include "openpgp.h"
#include "status-code.h"
#include "sha256.h"
#include "random.h"
@@ -48,6 +48,7 @@ static struct eventflag *openpgp_comm;
#define INS_CHANGE_REFERENCE_DATA 0x24
#define INS_PSO 0x2a
#define INS_RESET_RETRY_COUNTER 0x2c
#define INS_ACTIVATE_FILE 0x44
#define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR 0x47
#define INS_EXTERNAL_AUTHENTICATE 0x82
#define INS_GET_CHALLENGE 0x84
@@ -59,6 +60,7 @@ static struct eventflag *openpgp_comm;
#define INS_UPDATE_BINARY 0xd6
#define INS_PUT_DATA 0xda
#define INS_PUT_DATA_ODD 0xdb /* For key import */
#define INS_TERMINATE_DF 0xe6
static const uint8_t *challenge; /* Random bytes */
@@ -96,17 +98,24 @@ set_res_sw (uint8_t sw1, uint8_t sw2)
#define FILE_EF_UPDATE_KEY_2 7
#define FILE_EF_UPDATE_KEY_3 8
#define FILE_EF_CH_CERTIFICATE 9
#define FILE_CARD_TERMINATED 255
static uint8_t file_selection;
uint8_t file_selection;
static void
gpg_init (void)
{
const uint8_t *flash_data_start;
const uint8_t *flash_do_start;
const uint8_t *flash_do_end;
file_selection = FILE_NONE;
flash_data_start = flash_init ();
gpg_data_scan (flash_data_start);
flash_init (&flash_do_start, &flash_do_end);
if (flash_do_start == NULL)
file_selection = FILE_CARD_TERMINATED;
else
file_selection = FILE_NONE;
gpg_data_scan (flash_do_start, flash_do_end);
flash_init_keys ();
}
@@ -616,9 +625,6 @@ cmd_put_data (void)
DEBUG_INFO (" - PUT DATA\r\n");
if (file_selection != FILE_DF_OPENPGP)
GPG_NO_RECORD();
tag = ((P1 (apdu)<<8) | P2 (apdu));
len = apdu.cmd_apdu_data_len;
data = apdu.cmd_apdu_data;
@@ -751,19 +757,16 @@ cmd_select_file (void)
return;
}
file_selection = FILE_DF_OPENPGP;
if ((P2 (apdu) & 0x0c) == 0x0c) /* No FCI */
GPG_SUCCESS ();
else
if (file_selection == FILE_CARD_TERMINATED)
{
gpg_do_get_data (0x004f, 1); /* AID */
memmove (res_APDU+2, res_APDU, res_APDU_size);
res_APDU[0] = 0x6f;
res_APDU[1] = 0x12;
res_APDU[2] = 0x84; /* overwrite: DF name */
res_APDU_size += 2;
GPG_SUCCESS ();
GPG_APPLICATION_TERMINATED ();
return;
}
file_selection = FILE_DF_OPENPGP;
/* Behave just like original OpenPGP card. */
GPG_SUCCESS ();
}
else if (apdu.cmd_apdu_data_len == 2
&& apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02)
@@ -813,9 +816,6 @@ cmd_get_data (void)
DEBUG_INFO (" - Get Data\r\n");
if (file_selection != FILE_DF_OPENPGP)
GPG_NO_RECORD ();
gpg_do_get_data (tag, 0);
}
@@ -1317,6 +1317,69 @@ cmd_get_challenge (void)
}
#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
static void
cmd_activate_file (void)
{
if (file_selection != FILE_CARD_TERMINATED)
{
GPG_NO_RECORD ();
return;
}
flash_activate ();
file_selection = FILE_DF_OPENPGP;
GPG_SUCCESS ();
}
static void
cmd_terminate_df (void)
{
const uint8_t *ks_pw3;
uint8_t p1 = P1 (apdu);
uint8_t p2 = P2 (apdu);
if (file_selection != FILE_DF_OPENPGP)
{
GPG_NO_RECORD ();
return;
}
if (p1 != 0 || p2 != 0)
{
GPG_BAD_P1_P2();
return;
}
if (apdu.cmd_apdu_data_len != 0)
{
GPG_WRONG_LENGTH();
return;
}
ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3);
if (!ac_check_status (AC_ADMIN_AUTHORIZED)
&& !((ks_pw3 && gpg_pw_locked (PW_ERR_PW3))
|| (ks_pw3 == NULL && gpg_pw_locked (PW_ERR_PW1))))
{
/* Only allow the case admin authorized, or, admin pass is locked. */
GPG_SECURITY_FAILURE();
return;
}
ac_reset_admin ();
ac_reset_pso_cds ();
ac_reset_other ();
gpg_do_terminate ();
flash_terminate ();
file_selection = FILE_CARD_TERMINATED;
GPG_SUCCESS ();
}
#endif
struct command
{
uint8_t command;
@@ -1328,13 +1391,16 @@ const struct command cmds[] = {
{ INS_CHANGE_REFERENCE_DATA, cmd_change_password },
{ INS_PSO, cmd_pso },
{ INS_RESET_RETRY_COUNTER, cmd_reset_user_password },
#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
{ INS_ACTIVATE_FILE, cmd_activate_file },
#endif
{ INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp },
{ INS_EXTERNAL_AUTHENTICATE, /* Not in OpenPGP card protocol */
cmd_external_authenticate },
{ INS_GET_CHALLENGE, cmd_get_challenge }, /* Not in OpenPGP card protocol */
{ INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate },
{ INS_SELECT_FILE, cmd_select_file },
{ INS_READ_BINARY, cmd_read_binary },
{ INS_READ_BINARY, cmd_read_binary }, /* Not in OpenPGP card protocol */
{ INS_GET_DATA, cmd_get_data },
{ INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */
#if defined(CERTDO_SUPPORT)
@@ -1342,6 +1408,9 @@ const struct command cmds[] = {
#endif
{ INS_PUT_DATA, cmd_put_data },
{ INS_PUT_DATA_ODD, cmd_put_data },
#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
{ INS_TERMINATE_DF, cmd_terminate_df},
#endif
};
#define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command)))
@@ -1357,9 +1426,22 @@ process_command_apdu (void)
if (i < NUM_CMDS)
{
chopstx_setcancelstate (1);
cmds[i].cmd_handler ();
chopstx_setcancelstate (0);
if (file_selection == FILE_CARD_TERMINATED
&& cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE
&& cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE)
GPG_APPLICATION_TERMINATED ();
else if (file_selection != FILE_DF_OPENPGP
&& cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE
&& cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE
&& cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY
&& cmd != INS_READ_BINARY)
GPG_NO_RECORD ();
else
{
chopstx_setcancelstate (1);
cmds[i].cmd_handler ();
chopstx_setcancelstate (0);
}
}
else
{

View File

@@ -1,4 +1,6 @@
#define GPG_APPLICATION_TERMINATED() set_res_sw (0x62, 0x85)
#define GPG_MEMORY_FAILURE() set_res_sw (0x65, 0x81)
#define GPG_WRONG_LENGTH() set_res_sw (0x67, 0x00)
#define GPG_SECURITY_FAILURE() set_res_sw (0x69, 0x82)
#define GPG_SECURITY_AUTH_BLOCKED() set_res_sw (0x69, 0x83)
#define GPG_CONDITION_NOT_SATISFIED() set_res_sw (0x69, 0x85)

View File

@@ -720,24 +720,12 @@ usb_tx_done (uint8_t ep_num, uint16_t len)
* TB3 = 0x55: BWI = 5, CWI = 5 (BWT timeout 3.2 sec)
* TD3 = 0x1f: TA4 follows, T=15
* TA4 = 0x03: 5V or 3.3V
* Historical bytes: to be explained...
* XOR check
*
* Minimum: 0x3b, 0x8a, 0x80, 0x01, + historical bytes, xor check
* Minimum: 0x3b, 0x8a, 0x80, 0x01
*
*/
static const uint8_t ATR[] = {
static const uint8_t ATR_head[] = {
0x3b, 0xda, 0x11, 0xff, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0x03,
0x00,
0x31, 0x84, /* full DF name, GET DATA, MF */
0x73,
0x80, /* DF full name */
0x01, /* 1-byte */
0x80, /* Command chaining, No extended Lc and extended Le */
0x00,
0x90, 0x00,
(0xda^0x11^0xff^0x81^0xb1^0xfe^0x55^0x1f^0x03
^0x00^0x31^0x84^0x73^0x80^0x01^0x80^0x00^0x90^0x00)
};
/*
@@ -783,8 +771,11 @@ extern uint8_t __process3_stack_base__[], __process3_stack_size__[];
static enum ccid_state
ccid_power_on (struct ccid *c)
{
size_t size_atr = sizeof (ATR);
uint8_t p[CCID_MSG_HEADER_SIZE];
uint8_t p[CCID_MSG_HEADER_SIZE+1]; /* >= size of historical_bytes -1 */
int hist_len = historical_bytes[0];
size_t size_atr = sizeof (ATR_head) + hist_len + 1;
uint8_t xor_check = 0;
int i;
if (c->application == 0)
c->application = chopstx_create (PRIO_GPG, STACK_ADDR_GPG,
@@ -803,7 +794,20 @@ ccid_power_on (struct ccid *c)
p[CCID_MSG_CHAIN_OFFSET] = 0x00;
usb_lld_txcpy (p, c->epi->ep_num, 0, CCID_MSG_HEADER_SIZE);
usb_lld_txcpy (ATR, c->epi->ep_num, CCID_MSG_HEADER_SIZE, size_atr);
usb_lld_txcpy (ATR_head, c->epi->ep_num, CCID_MSG_HEADER_SIZE,
sizeof (ATR_head));
for (i = 1; i < (int)sizeof (ATR_head); i++)
xor_check ^= ATR_head[i];
memcpy (p, historical_bytes + 1, hist_len);
#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
if (file_selection == 255)
p[7] = 0x03;
#endif
for (i = 0; i < hist_len; i++)
xor_check ^= p[i];
p[i] = xor_check;
usb_lld_txcpy (p, c->epi->ep_num, CCID_MSG_HEADER_SIZE + sizeof (ATR_head),
hist_len+1);
/* This is a single packet Bulk-IN transaction */
c->epi->buf = NULL;

View File

@@ -38,7 +38,6 @@
#include "usb_lld.h"
#include "usb_conf.h"
#include "gnuk.h"
#include "mcu/stm32f103.h"
#ifdef ENABLE_VIRTUAL_COM_PORT
#include "usb-cdc.h"
@@ -209,27 +208,11 @@ static const uint8_t *const mem_info[] = { &_regnual_start, __heap_end__, };
#define USB_FSIJ_GNUK_EXEC 2
#define USB_FSIJ_GNUK_CARD_CHANGE 3
static uint32_t rbit (uint32_t v)
{
uint32_t r;
asm ("rbit %0, %1" : "=r" (r) : "r" (v));
return r;
}
/* After calling this function, CRC module remain enabled. */
static int download_check_crc32 (struct usb_dev *dev, const uint32_t *end_p)
static int
download_check_crc32 (struct usb_dev *dev, const uint32_t *end_p)
{
uint32_t crc32 = *end_p;
const uint32_t *p;
RCC->AHBENR |= RCC_AHBENR_CRCEN;
CRC->CR = CRC_CR_RESET;
for (p = (const uint32_t *)&_regnual_start; p < end_p; p++)
CRC->DR = rbit (*p);
if ((rbit (CRC->DR) ^ crc32) == 0xffffffff)
if (check_crc32 ((const uint32_t *)&_regnual_start, end_p))
return usb_lld_ctrl_ack (dev);
return -1;
@@ -250,8 +233,7 @@ usb_setup (struct usb_dev *dev)
}
else /* SETUP_SET */
{
uint8_t *addr = (uint8_t *)(0x20000000 + arg->value * 0x100
+ arg->index);
uint8_t *addr = sram_address ((arg->value * 0x100) + arg->index);
if (arg->request == USB_FSIJ_GNUK_DOWNLOAD)
{
@@ -272,7 +254,7 @@ usb_setup (struct usb_dev *dev)
if (*ccid_state_p != CCID_STATE_EXITED)
return -1;
if (((uint32_t)addr & 0x03))
if (((uintptr_t)addr & 0x03))
return -1;
return download_check_crc32 (dev, (uint32_t *)addr);

View File

@@ -4,7 +4,7 @@ Feature: command GET DATA
Scenario: data object historical bytes
When requesting historical bytes: 5f52
Then you should get: \x00\x31\x84\x73\x80\x01\x80\x00\x90\x00
Then data should match: \x00\x31\x84\x73\x80\x01\x80[\x00\x05]\x90\x00
Scenario: data object extended capabilities
When requesting extended capabilities: c0

View File

@@ -4,7 +4,7 @@ Feature: command GET DATA
Scenario: data object historical bytes
When requesting historical bytes: 5f52
Then you should get: \x00\x31\x84\x73\x80\x01\x80\x00\x90\x00
Then data should match: \x00\x31\x84\x73\x80\x01\x80[\x00\x05]\x90\x00
Scenario: data object extended capabilities
When requesting extended capabilities: c0

View File

@@ -4,7 +4,7 @@ Feature: command GET DATA
Scenario: data object historical bytes
When requesting historical bytes: 5f52
Then you should get: \x00\x31\x84\x73\x80\x01\x80\x00\x90\x00
Then data should match: \x00\x31\x84\x73\x80\x01\x80[\x00\x05]\x90\x00
Scenario: data object extended capabilities
When requesting extended capabilities: c0

View File

@@ -11,19 +11,19 @@ from array import array
@Before
def ini(sc):
if not ftc.token:
ftc.token = gnuk.get_gnuk_device()
ftc.token.cmd_select_openpgp()
if not glc.token:
glc.token = gnuk.get_gnuk_device()
glc.token.cmd_select_openpgp()
@Given("cmd_verify with (.*) and \"(.*)\"")
def cmd_verify(who_str,pass_str):
who = int(who_str)
scc.result = ftc.token.cmd_verify(who, pass_str)
scc.result = glc.token.cmd_verify(who, pass_str)
@Given("cmd_change_reference_data with (.*) and \"(.*)\"")
def cmd_change_reference_data(who_str,pass_str):
who = int(who_str)
scc.result = ftc.token.cmd_change_reference_data(who, pass_str)
scc.result = glc.token.cmd_change_reference_data(who, pass_str)
@Given("cmd_put_data with (.*) and (\".*\")")
def cmd_put_data(tag_str,content_str_repr):
@@ -31,12 +31,12 @@ def cmd_put_data(tag_str,content_str_repr):
tag = int(tag_str, 16)
tagh = tag >> 8
tagl = tag & 0xff
scc.result = ftc.token.cmd_put_data(tagh, tagl, content_str)
scc.result = glc.token.cmd_put_data(tagh, tagl, content_str)
@Given("cmd_reset_retry_counter with (.*) and \"(.*)\"")
def cmd_reset_retry_counter(how_str, data):
how = int(how_str)
scc.result = ftc.token.cmd_reset_retry_counter(how, data)
scc.result = glc.token.cmd_reset_retry_counter(how, 0x81, data)
@Given("a RSA key pair (.*)")
def set_rsa_key(keyno_str):
@@ -46,7 +46,7 @@ def set_rsa_key(keyno_str):
def import_key(openpgp_keyno_str):
openpgp_keyno = int(openpgp_keyno_str)
t = rsa_keys.build_privkey_template(openpgp_keyno, scc.keyno)
scc.result = ftc.token.cmd_put_data_odd(0x3f, 0xff, t)
scc.result = glc.token.cmd_put_data_odd(0x3f, 0xff, t)
@Given("a fingerprint of OPENPGP.(.*) key")
def get_key_fpr(openpgp_keyno_str):
@@ -63,7 +63,7 @@ def cmd_put_data_with_result(tag_str):
tag = int(tag_str, 16)
tagh = tag >> 8
tagl = tag & 0xff
scc.result = ftc.token.cmd_put_data(tagh, tagl, scc.result)
scc.result = glc.token.cmd_put_data(tagh, tagl, scc.result)
@Given("a message (\".*\")")
def set_msg(content_str_repr):
@@ -73,7 +73,7 @@ def set_msg(content_str_repr):
@Given("a public key from token for OPENPGP.(.*)")
def get_public_key(openpgp_keyno_str):
openpgp_keyno = int(openpgp_keyno_str)
scc.pubkey_info = ftc.token.cmd_get_public_key(openpgp_keyno)
scc.pubkey_info = glc.token.cmd_get_public_key(openpgp_keyno)
@Given("verify signature")
def verify_signature():
@@ -81,11 +81,11 @@ def verify_signature():
@Given("let a token compute digital signature")
def compute_signature():
scc.sig = int(hexlify(ftc.token.cmd_pso(0x9e, 0x9a, scc.digestinfo)),16)
scc.sig = int(hexlify(glc.token.cmd_pso(0x9e, 0x9a, scc.digestinfo)),16)
@Given("let a token authenticate")
def internal_authenticate():
scc.sig = int(hexlify(ftc.token.cmd_internal_authenticate(scc.digestinfo)),16)
scc.sig = int(hexlify(glc.token.cmd_internal_authenticate(scc.digestinfo)),16)
@Given("compute digital signature on host with RSA key pair (.*)")
def compute_signature_on_host(keyno_str):
@@ -107,29 +107,29 @@ def encrypt_on_host_public_key():
@Given("let a token decrypt encrypted data")
def decrypt():
scc.result = ftc.token.cmd_pso_longdata(0x80, 0x86, scc.ciphertext).tostring()
scc.result = glc.token.cmd_pso_longdata(0x80, 0x86, scc.ciphertext).tostring()
@Given("USB version string of the token")
def usb_version_string():
scc.result = ftc.token.get_string(3)
scc.result = glc.token.get_string(3)
@When("requesting (.+): ([0-9a-fA-F]+)")
def get_data(name, tag_str):
tag = int(tag_str, 16)
tagh = tag >> 8
tagl = tag & 0xff
scc.result = ftc.token.cmd_get_data(tagh, tagl)
scc.result = glc.token.cmd_get_data(tagh, tagl)
@When("removing a key OPENPGP.(.*)")
def remove_key(openpgp_keyno_str):
openpgp_keyno = int(openpgp_keyno_str)
t = rsa_keys.build_privkey_template_for_remove(openpgp_keyno)
scc.result = ftc.token.cmd_put_data_odd(0x3f, 0xff, t)
scc.result = glc.token.cmd_put_data_odd(0x3f, 0xff, t)
@When("generating a key of OPENPGP.(.*)")
def generate_key(openpgp_keyno_str):
openpgp_keyno = int(openpgp_keyno_str)
pubkey_info = ftc.token.cmd_genkey(openpgp_keyno)
pubkey_info = glc.token.cmd_genkey(openpgp_keyno)
scc.data = rsa_keys.calc_fpr(pubkey_info[0].tostring(), pubkey_info[1].tostring())
@When("put the first data to (.*)")
@@ -137,14 +137,14 @@ def cmd_put_data_first_with_result(tag_str):
tag = int(tag_str, 16)
tagh = tag >> 8
tagl = tag & 0xff
scc.result = ftc.token.cmd_put_data(tagh, tagl, scc.data[0])
scc.result = glc.token.cmd_put_data(tagh, tagl, scc.data[0])
@When("put the second data to (.*)")
def cmd_put_data_second_with_result(tag_str):
tag = int(tag_str, 16)
tagh = tag >> 8
tagl = tag & 0xff
result = ftc.token.cmd_put_data(tagh, tagl, scc.data[1])
result = glc.token.cmd_put_data(tagh, tagl, scc.data[1])
scc.result = (scc.result and result)
@Then("you should get: (.*)")

View File

@@ -60,7 +60,7 @@ def build_privkey_template(openpgp_keyno, keyno):
suffix = b'\x5f\x48' + b'\x82\x01\x04'
t = b'\x4d' + b'\x82\01\16' + exthdr + suffix + e_bytes + p_bytes + q_bytes
t = b'\x4d' + b'\x82\x01\x16' + exthdr + suffix + e_bytes + p_bytes + q_bytes
return t
def build_privkey_template_for_remove(openpgp_keyno):

13
tests/README Normal file
View File

@@ -0,0 +1,13 @@
Here is a test suite for OpenPGP card.
For now, only TPDU card reader is supported for OpenPGP card.
Gnuk Token is supported as well.
You need to install:
$ sudo apt-get install python3-pytest python3-usb
Please run test by typing:
$ py.test-3 -x

329
tests/card_reader.py Normal file
View File

@@ -0,0 +1,329 @@
"""
card_reader.py - a library for smartcard reader
Copyright (C) 2016 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/>.
"""
import usb.core
from struct import pack
from usb.util import find_descriptor, claim_interface, get_string, \
endpoint_type, endpoint_direction, \
ENDPOINT_TYPE_BULK, ENDPOINT_OUT, ENDPOINT_IN
# USB class, subclass, protocol
CCID_CLASS = 0x0B
CCID_SUBCLASS = 0x00
CCID_PROTOCOL_0 = 0x00
def ccid_compose(msg_type, seq, slot=0, rsv=0, param=0, data=b""):
return pack('<BiBBBH', msg_type, len(data), slot, seq, rsv, param) + data
IFSC=254
def compute_edc(pcb, info):
edc = pcb
edc ^= len(info)
for i in range(len(info)):
edc ^= info[i]
return edc
def compose_i_block(ns, info, more):
pcb = 0x00
if ns:
pcb |= 0x40
if more:
pcb |= 0x20
edc = compute_edc(pcb, info)
return bytes([0, pcb, len(info)]) + info + bytes([edc])
def compose_r_block(nr, edc_error=0):
pcb = 0x80
if nr:
pcb |= 0x10
if edc_error:
pcb |= 0x01
return bytes([0, pcb, 0, pcb])
def is_r_block_no_error_or_other(blk):
return (((blk[1] & 0xC0) == 0x80 and (blk[1] & 0x2f) == 0x00)) or \
((blk[1] & 0xC0) != 0x80)
def is_s_block_time_ext(blk):
return (blk[1] == 0xC3)
def is_i_block_last(blk):
return ((blk[1] & 0x80) == 0 and (blk[1] & 0x20) == 0)
def is_i_block_more(blk):
return ((blk[1] & 0x80) == 0 and (blk[1] & 0x20) == 0x20)
def is_edc_error(blk):
# to be implemented
return 0
def i_block_content(blk):
return blk[3:-1]
class CardReader(object):
def __init__(self, dev):
"""
__init__(dev) -> None
Initialize the DEV of CCID.
device: usb.core.Device object.
"""
cfg = dev.get_active_configuration()
intf = find_descriptor(cfg, bInterfaceClass=CCID_CLASS,
bInterfaceSubClass=CCID_SUBCLASS,
bInterfaceProtocol=CCID_PROTOCOL_0)
if intf is None:
raise ValueError("Not a CCID device")
claim_interface(dev, intf)
for ep in intf:
if endpoint_type(ep.bmAttributes) == ENDPOINT_TYPE_BULK and \
endpoint_direction(ep.bEndpointAddress) == ENDPOINT_OUT:
self.__bulkout = ep.bEndpointAddress
if endpoint_type(ep.bmAttributes) == ENDPOINT_TYPE_BULK and \
endpoint_direction(ep.bEndpointAddress) == ENDPOINT_IN:
self.__bulkin = ep.bEndpointAddress
assert len(intf.extra_descriptors) == 54
assert intf.extra_descriptors[1] == 33
if (intf.extra_descriptors[42] & 0x02):
# Short APDU level exchange
self.__use_APDU = True
elif (intf.extra_descriptors[42] & 0x04):
# Short and extended APDU level exchange
self.__use_APDU = True
elif (intf.extra_descriptors[42] & 0x01):
# TPDU level exchange
self.__use_APDU = False
else:
raise ValueError("Unknown exchange level")
# Check other bits???
# intf.extra_descriptors[40]
# intf.extra_descriptors[41]
self.__dev = dev
self.__timeout = 10000
self.__seq = 0
def get_string(self, num):
return get_string(self.__dev, num)
def increment_seq(self):
self.__seq = (self.__seq + 1) & 0xff
def reset_device(self):
try:
self.__dev.reset()
except:
pass
def is_tpdu_reader(self):
return not self.__use_APDU
def ccid_get_result(self):
msg = self.__dev.read(self.__bulkin, 1024, self.__timeout)
if len(msg) < 10:
print(msg)
raise ValueError("ccid_get_result")
msg_type = msg[0]
data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
slot = msg[5]
seq = msg[6]
status = msg[7]
error = msg[8]
chain = msg[9]
data = msg[10:]
# XXX: check msg_type, data_len, slot, seq, error
return (status, chain, data.tobytes())
def ccid_get_status(self):
msg = ccid_compose(0x65, self.__seq)
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
status, chain, data = self.ccid_get_result()
# XXX: check chain, data
return status
def ccid_power_on(self):
msg = ccid_compose(0x62, self.__seq, rsv=1) # Vcc=5V
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
status, chain, data = self.ccid_get_result()
# XXX: check status, chain
self.atr = data
#
if self.__use_APDU == False:
# TPDU reader configuration
self.ns = 0
self.nr = 0
# Set PPS
pps = b"\xFF\x11\x18\xF6"
status, chain, ret_pps = self.ccid_send_data_block(pps)
# Set parameters
param = b"\x18\x10\xFF\x75\x00\xFE\x00"
# ^--- This shoud be adapted by ATR string, see update_param_by_atr
msg = ccid_compose(0x61, self.__seq, rsv=0x1, data=param)
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
status, chain, ret_param = self.ccid_get_result()
# Send an S-block of changing IFSD=254
sblk = b"\x00\xC1\x01\xFE\x3E"
status, chain, ret_sblk = self.ccid_send_data_block(sblk)
return self.atr
def ccid_power_off(self):
msg = ccid_compose(0x63, self.__seq)
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
status, chain, data = self.ccid_get_result()
# XXX: check chain, data
return status
def ccid_send_data_block(self, data):
msg = ccid_compose(0x6f, self.__seq, data=data)
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
return self.ccid_get_result()
def ccid_send_cmd(self, data):
status, chain, data_rcv = self.ccid_send_data_block(data)
if chain == 0:
while status == 0x80:
status, chain, data_rcv = self.ccid_get_result()
return data_rcv
elif chain == 1:
d = data_rcv
while True:
msg = ccid_compose(0x6f, self.__seq, param=0x10)
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
status, chain, data_rcv = self.ccid_get_result()
# XXX: check status
d += data_rcv
if chain == 2:
break
elif chain == 3:
continue
else:
raise ValueError("ccid_send_cmd chain")
return d
else:
raise ValueError("ccid_send_cmd")
def send_tpdu(self, info=None, more=0, response_time_ext=0,
edc_error=0, no_error=0):
if info:
data = compose_i_block(self.ns, info, more)
elif response_time_ext:
# compose S-block
data = b"\x00\xE3\x00\xE3"
elif edc_error:
data = compose_r_block(self.nr, edc_error=1)
elif no_error:
data = compose_r_block(self.nr)
msg = ccid_compose(0x6f, self.__seq, data=data)
self.__dev.write(self.__bulkout, msg, self.__timeout)
self.increment_seq()
def recv_tpdu(self):
status, chain, data = self.ccid_get_result()
return data
def send_cmd(self, cmd):
# Simple APDU case
if self.__use_APDU:
return self.ccid_send_cmd(cmd)
# TPDU case
while len(cmd) > 254:
blk = cmd[0:254]
cmd = cmd[254:]
while True:
self.send_tpdu(info=blk,more=1)
rblk = self.recv_tpdu()
if is_r_block_no_error_or_other(rblk):
break
self.ns = self.ns ^ 1
while True:
self.send_tpdu(info=cmd)
blk = self.recv_tpdu()
if is_r_block_no_error_or_other(blk):
break
self.ns = self.ns ^ 1
res = b""
while True:
if is_s_block_time_ext(blk):
self.send_tpdu(response_time_ext=1)
elif is_i_block_last(blk):
self.nr = self.nr ^ 1
if is_edc_error(blk):
self.send_tpdu(edc_error=1)
else:
res += i_block_content(blk)
break
elif is_i_block_more(blk):
self.nr = self.nr ^ 1
if is_edc_error(blk):
self.send_tpdu(edc_error=1)
else:
res += i_block_content(blk)
self.send_tpdu(no_error=1)
blk = self.recv_tpdu()
return res
class find_class(object):
def __init__(self, usb_class):
self.__class = usb_class
def __call__(self, device):
if device.bDeviceClass == self.__class:
return True
for cfg in device:
intf = find_descriptor(cfg, bInterfaceClass=self.__class)
if intf is not None:
return True
return False
def get_ccid_device():
ccid = None
dev_list = usb.core.find(find_all=True, custom_match=find_class(CCID_CLASS))
for dev in dev_list:
try:
ccid = CardReader(dev)
print("CCID device: Bus %03d Device %03d" % (dev.bus, dev.address))
break
except:
pass
if not ccid:
raise ValueError("No CCID device present")
status = ccid.ccid_get_status()
if status == 0:
# It's ON already
atr = ccid.ccid_power_on()
elif status == 1:
atr = ccid.ccid_power_on()
else:
raise ValueError("Unknown CCID status", status)
return ccid

18
tests/conftest.py Normal file
View File

@@ -0,0 +1,18 @@
import pytest
from card_reader import get_ccid_device
from openpgp_card import OpenPGP_Card
def pytest_addoption(parser):
parser.addoption("--reader", dest="reader", type=str, action="store",
default="gnuk", help="specify reader: gnuk or gemalto")
@pytest.fixture(scope="session")
def card():
print()
print("Test start!")
reader = get_ccid_device()
print("Reader:", reader.get_string(1), reader.get_string(2))
card = OpenPGP_Card(reader)
card.cmd_select_openpgp()
yield card
del card

338
tests/openpgp_card.py Normal file
View File

@@ -0,0 +1,338 @@
"""
openpgp_card.py - a library for OpenPGP card
Copyright (C) 2011, 2012, 2013, 2015, 2016
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 struct import pack
def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
data_len = len(data)
if data_len == 0:
if not le:
return pack('>BBBB', cls, ins, p1, p2)
else:
return pack('>BBBBB', cls, ins, p1, p2, le)
else:
if not le:
if data_len <= 255:
return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
else:
return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \
+ data
else:
if data_len <= 255 and le < 256:
return pack('>BBBBB', cls, ins, p1, p2, data_len) \
+ data + pack('>B', le)
else:
return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \
+ data + pack('>H', le)
class OpenPGP_Card(object):
def __init__(self, reader):
"""
__init__(reader) -> None
Initialize a OpenPGP card with a CardReader.
reader: CardReader object.
"""
self.__reader = reader
def cmd_get_response(self, expected_len):
result = b""
while True:
cmd_data = iso7816_compose(0xc0, 0x00, 0x00, b'') + pack('>B', expected_len)
response = self.__reader.send_cmd(cmd_data)
result += response[:-2]
sw = response[-2:]
if sw[0] == 0x90 and sw[1] == 0x00:
return result
elif sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
else:
expected_len = sw[1]
def cmd_verify(self, who, passwd):
cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_read_binary(self, fileid):
cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, b'')
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return self.cmd_get_response(sw[1])
def cmd_write_binary(self, fileid, data, is_update):
count = 0
data_len = len(data)
if is_update:
ins = 0xd6
else:
ins = 0xd0
while count*256 < data_len:
if count == 0:
if len(data) < 128:
cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
cmd_data1 = None
else:
cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
else:
if len(data[256*count:256*count+128]) < 128:
cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
cmd_data1 = None
else:
cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10)
cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
sw = self.__reader.send_cmd(cmd_data0)
if len(sw) != 2:
raise ValueError("cmd_write_binary 0")
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1]))
if cmd_data1:
sw = self.__reader.send_cmd(cmd_data1)
if len(sw) != 2:
raise ValueError("cmd_write_binary 1", sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1]))
count += 1
def cmd_select_openpgp(self):
cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01")
r = self.__reader.send_cmd(cmd_data)
if len(r) < 2:
raise ValueError(r)
sw = r[-2:]
r = r[0:-2]
if sw[0] == 0x61:
self.cmd_get_response(sw[1])
return True
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_get_data(self, tagh, tagl):
cmd_data = iso7816_compose(0xca, tagh, tagl, b"", le=254)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) < 2:
raise ValueError(sw)
if sw[0] == 0x61:
return self.cmd_get_response(sw[1])
elif sw[-2] == 0x90 and sw[-1] == 0x00:
return sw[0:-2]
if sw[0] == 0x6a and sw[1] == 0x88:
return None
else:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
def cmd_change_reference_data(self, who, data):
cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_put_data(self, tagh, tagl, content):
cmd_data = iso7816_compose(0xda, tagh, tagl, content)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_put_data_odd(self, tagh, tagl, content):
if self.__reader.is_tpdu_reader():
cmd_data = iso7816_compose(0xdb, tagh, tagl, content)
sw = self.__reader.send_cmd(cmd_data)
else:
cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
sw = self.__reader.send_cmd(cmd_data0)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
sw = self.__reader.send_cmd(cmd_data1)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_reset_retry_counter(self, how, who, data):
cmd_data = iso7816_compose(0x2c, how, who, data)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_pso(self, p1, p2, data):
if self.__reader.is_tpdu_reader():
cmd_data = iso7816_compose(0x2a, p1, p2, data, le=256)
r = self.__reader.send_cmd(cmd_data)
if len(r) < 2:
raise ValueError(r)
sw = r[-2:]
r = r[0:-2]
if sw[0] == 0x61:
return self.cmd_get_response(sw[1])
elif sw[0] == 0x90 and sw[1] == 0x00:
return r
else:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
else:
if len(data) > 128:
cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
sw = self.__reader.send_cmd(cmd_data0)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
sw = self.__reader.send_cmd(cmd_data1)
if len(sw) != 2:
raise ValueError(sw)
elif sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return self.cmd_get_response(sw[1])
else:
cmd_data = iso7816_compose(0x2a, p1, p2, data)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if sw[0] == 0x90 and sw[1] == 0x00:
return b""
elif sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return self.cmd_get_response(sw[1])
def cmd_internal_authenticate(self, data):
if self.__reader.is_tpdu_reader():
cmd_data = iso7816_compose(0x88, 0, 0, data, le=256)
else:
cmd_data = iso7816_compose(0x88, 0, 0, data)
r = self.__reader.send_cmd(cmd_data)
if len(r) < 2:
raise ValueError(r)
sw = r[-2:]
r = r[0:-2]
if sw[0] == 0x61:
return self.cmd_get_response(sw[1])
elif sw[0] == 0x90 and sw[1] == 0x00:
return r
else:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
def cmd_genkey(self, keyno):
if keyno == 1:
data = b'\xb6\x00'
elif keyno == 2:
data = b'\xb8\x00'
else:
data = b'\xa4\x00'
cmd_data = iso7816_compose(0x47, 0x80, 0, data)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if sw[0] == 0x90 and sw[1] == 0x00:
return b""
elif sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
pk = self.cmd_get_response(sw[1])
return (pk[9:9+256], pk[9+256+2:9+256+2+3])
def cmd_get_public_key(self, keyno):
if keyno == 1:
data = b'\xb6\x00'
elif keyno == 2:
data = b'\xb8\x00'
else:
data = b'\xa4\x00'
if self.__reader.is_tpdu_reader():
cmd_data = iso7816_compose(0x47, 0x81, 0, data, le=512)
r = self.__reader.send_cmd(cmd_data)
else:
cmd_data = iso7816_compose(0x47, 0x81, 0, data)
r = self.__reader.send_cmd(cmd_data)
if len(r) < 2:
raise ValueError(r)
sw = r[-2:]
r = r[0:-2]
if sw[0] == 0x61:
pk = self.cmd_get_response(sw[1])
elif sw[0] == 0x90 and sw[1] == 0x00:
pk = r
else:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return pk
def cmd_put_data_remove(self, tagh, tagl):
cmd_data = iso7816_compose(0xda, tagh, tagl, b"")
sw = self.__reader.send_cmd(cmd_data)
if sw[0] != 0x90 and sw[1] != 0x00:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
def cmd_put_data_key_import_remove(self, keyno):
if keyno == 1:
keyspec = b"\xb6\x00" # SIG
elif keyno == 2:
keyspec = b"\xb8\x00" # DEC
else:
keyspec = b"\xa4\x00" # AUT
cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, b"\x4d\x02" + keyspec)
sw = self.__reader.send_cmd(cmd_data)
if sw[0] != 0x90 and sw[1] != 0x00:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
def cmd_get_challenge(self):
cmd_data = iso7816_compose(0x84, 0x00, 0x00, '')
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return self.cmd_get_response(sw[1])
def cmd_external_authenticate(self, keyno, signed):
cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[0:128], cls=0x10)
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[128:])
sw = self.__reader.send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("%02x%02x" % (sw[0], sw[1]))

4
tests/rsa-aut.key Normal file
View File

@@ -0,0 +1,4 @@
9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
010001
b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf

4
tests/rsa-dec.key Normal file
View File

@@ -0,0 +1,4 @@
d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3
010001
dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501
f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3

4
tests/rsa-sig.key Normal file
View File

@@ -0,0 +1,4 @@
c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331
010001
cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633
f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b

148
tests/rsa_keys.py Normal file
View File

@@ -0,0 +1,148 @@
from binascii import hexlify, unhexlify
from time import time
from struct import pack
from hashlib import sha1, sha256
import string
from os import urandom
def read_key_from_file(file):
f = open(file)
n_str = f.readline()[:-1]
e_str = f.readline()[:-1]
p_str = f.readline()[:-1]
q_str = f.readline()[:-1]
f.close()
e = int(e_str, 16)
p = int(p_str, 16)
q = int(q_str, 16)
n = int(n_str, 16)
if n != p * q:
raise ValueError("wrong key", p, q, n)
return (unhexlify(n_str), unhexlify(e_str), unhexlify(p_str), unhexlify(q_str), e, p, q, n)
def calc_fpr(n,e):
timestamp = int(time())
timestamp_data = pack('>I', timestamp)
m_len = 6 + 2 + 256 + 2 + 4
m = b'\x99' + pack('>H', m_len) + b'\x04' + timestamp_data + b'\x01' + \
pack('>H', 2048) + n + pack('>H', 17) + e
fpr = sha1(m).digest()
return (fpr, timestamp_data)
key = [ None, None, None ]
fpr = [ None, None, None ]
timestamp = [ None, None, None ]
key[0] = read_key_from_file('rsa-sig.key')
key[1] = read_key_from_file('rsa-dec.key')
key[2] = read_key_from_file('rsa-aut.key')
(fpr[0], timestamp[0]) = calc_fpr(key[0][0], key[0][1])
(fpr[1], timestamp[1]) = calc_fpr(key[1][0], key[1][1])
(fpr[2], timestamp[2]) = calc_fpr(key[2][0], key[2][1])
def build_privkey_template(openpgp_keyno, keyno):
n_bytes = key[keyno][0]
e_bytes = b'\x00' + key[keyno][1]
p_bytes = key[keyno][2]
q_bytes = key[keyno][3]
if openpgp_keyno == 1:
keyspec = b'\xb6'
elif openpgp_keyno == 2:
keyspec = b'\xb8'
else:
keyspec = b'\xa4'
key_template = b'\x91\x04'+ b'\x92\x81\x80' + b'\x93\x81\x80'
exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x08' + key_template
suffix = b'\x5f\x48' + b'\x82\x01\x04'
t = b'\x4d' + b'\x82\x01\x16' + exthdr + suffix + e_bytes + p_bytes + q_bytes
return t
def build_privkey_template_for_remove(openpgp_keyno):
if openpgp_keyno == 1:
keyspec = b'\xb6'
elif openpgp_keyno == 2:
keyspec = b'\xb8'
else:
keyspec = b'\xa4'
return b'\x4d\02' + keyspec + b'\0x00'
def compute_digestinfo(msg):
digest = sha256(msg).digest()
prefix = b'\x30\31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'
return prefix + digest
# egcd and modinv are from wikibooks
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('modular inverse does not exist')
else:
return x % m
def pkcs1_pad_for_sign(digestinfo):
byte_repr = b'\x00' + b'\x01' + bytes.ljust(b'', 256 - 19 - 32 - 3, b'\xff') \
+ b'\x00' + digestinfo
return int(hexlify(byte_repr), 16)
def pkcs1_pad_for_crypt(msg):
padlen = 256 - 3 - len(msg)
byte_repr = b'\x00' + b'\x02' \
+ bytes.replace(urandom(padlen), b'\x00', b'\x01') + b'\x00' + msg
return int(hexlify(byte_repr), 16)
def compute_signature(keyno, digestinfo):
e = key[keyno][4]
p = key[keyno][5]
q = key[keyno][6]
n = key[keyno][7]
p1 = p - 1
q1 = q - 1
h = p1 * q1
d = modinv(e, h)
dp = d % p1
dq = d % q1
qp = modinv(q, p)
input = pkcs1_pad_for_sign(digestinfo)
t1 = pow(input, dp, p)
t2 = pow(input, dq, q)
t = ((t1 - t2) * qp) % p
sig = t2 + t * q
return sig
def integer_to_bytes_256(i):
return i.to_bytes(256, byteorder='big')
def encrypt(keyno, plaintext):
e = key[keyno][4]
n = key[keyno][7]
m = pkcs1_pad_for_crypt(plaintext)
return b'\x00' + integer_to_bytes_256(pow(m, e, n))
def encrypt_with_pubkey(pubkey_info, plaintext):
n = int(hexlify(pubkey_info[0]), 16)
e = int(hexlify(pubkey_info[1]), 16)
m = pkcs1_pad_for_crypt(plaintext)
return b'\x00' + integer_to_bytes_256(pow(m, e, n))
def verify_signature(pubkey_info, digestinfo, sig):
n = int(hexlify(pubkey_info[0]), 16)
e = int(hexlify(pubkey_info[1]), 16)
di_pkcs1 = pow(sig,e,n)
m = pkcs1_pad_for_sign(digestinfo)
return di_pkcs1 == m

184
tests/test_empty_card.py Normal file
View File

@@ -0,0 +1,184 @@
"""
test_empty_card.py - test empty card
Copyright (C) 2016 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
from re import match, DOTALL
from struct import pack
from util 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)
"""
def test_name(card):
name = get_data_object(card, 0x5b)
assert check_null(name)
def test_lang(card):
lang = get_data_object(card, 0x5f2d)
assert check_null(lang)
def test_sex(card):
sex = get_data_object(card, 0x5f35)
assert check_null(sex)
"""
def test_name_lang_sex(card):
name = b""
lang = b""
sex = b"9"
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 == b'' or name_lang_sex == expected
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_url(card):
url = get_data_object(card, 0x5f50)
assert check_null(url)
def test_ds_counter(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'\x00...\x03[\x00\x03]\x03', s, DOTALL)
def test_fingerprint_0(card):
fprlist = get_data_object(card, 0xC5)
assert fprlist == None or fprlist == EMPTY_60
def test_fingerprint_1(card):
fpr = get_data_object(card, 0xC7)
assert check_null(fpr)
def test_fingerprint_2(card):
fpr = get_data_object(card, 0xC8)
assert check_null(fpr)
def test_fingerprint_3(card):
fpr = get_data_object(card, 0xC9)
assert check_null(fpr)
def test_ca_fingerprint_0(card):
cafprlist = get_data_object(card, 0xC6)
assert cafprlist == None or cafprlist == EMPTY_60
def test_ca_fingerprint_1(card):
cafp = get_data_object(card, 0xCA)
assert check_null(cafp)
def test_ca_fingerprint_2(card):
cafp = get_data_object(card, 0xCB)
assert check_null(cafp)
def test_ca_fingerprint_3(card):
cafp = get_data_object(card, 0xCC)
assert check_null(cafp)
def test_timestamp_0(card):
t = get_data_object(card, 0xCD)
assert t == None or t == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
def test_timestamp_1(card):
t = get_data_object(card, 0xCE)
assert check_null(t)
def test_timestamp_2(card):
t = get_data_object(card, 0xCF)
assert check_null(t)
def test_timestamp_3(card):
t = get_data_object(card, 0xD0)
assert check_null(t)
def test_verify_pw1_1(card):
v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw1_2(card):
v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw3(card):
v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3)
assert v
def test_historical_bytes(card):
h = get_data_object(card, 0x5f52)
assert h == b'\x001\xc5s\xc0\x01@\x05\x90\x00' or \
h == b'\x00\x31\x84\x73\x80\x01\x80\x00\x90\x00' or \
h == b'\x00\x31\x84\x73\x80\x01\x80\x05\x90\x00'
def test_extended_capabilities(card):
a = get_data_object(card, 0xc0)
assert a == None or match(b'[\x70\x74]\x00\x00\x20[\x00\x08]\x00\x00\xff\x01\x00', a)
def test_algorithm_attributes_1(card):
a = get_data_object(card, 0xc1)
assert a == None or a == b'\x01\x08\x00\x00\x20\x00'
def test_algorithm_attributes_2(card):
a = get_data_object(card, 0xc2)
assert a == None or a == b'\x01\x08\x00\x00\x20\x00'
def test_algorithm_attributes_3(card):
a = get_data_object(card, 0xc3)
assert a == None or a == b'\x01\x08\x00\x00\x20\x00'
def test_public_key_1(card):
with pytest.raises(Exception) as excinfo:
pk = card.cmd_get_public_key(1)
assert excinfo.value.args[0] == "6a88"
def test_public_key_2(card):
with pytest.raises(Exception) as excinfo:
pk = card.cmd_get_public_key(2)
assert excinfo.value.args[0] == "6a88"
def test_public_key_3(card):
with pytest.raises(Exception) as excinfo:
pk = card.cmd_get_public_key(3)
assert excinfo.value.args[0] == "6a88"
def test_AID(card):
a = get_data_object(card, 0x4f)
print()
print("OpenPGP card version: %d.%d" % (a[6], a[7]))
print("Card Manufacturer: ", hexlify(a[8:10]).decode("UTF-8"))
print("Card serial: ", hexlify(a[10:14]).decode("UTF-8"))
assert match(b'\xd2\x76\x00\x01\\$\x01........\x00\x00', a, DOTALL)

View File

@@ -0,0 +1,290 @@
"""
test_personalize_card.py - test personalizing card
Copyright (C) 2016 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
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"
def test_setup_pw3_0(card):
r = card.cmd_change_reference_data(3, FACTORY_PASSPHRASE_PW3 + PW3_TEST0)
assert r
def test_verify_pw3_0(card):
v = card.cmd_verify(3, PW3_TEST0)
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)
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]
def test_setup_pw1_0(card):
r = card.cmd_change_reference_data(1, FACTORY_PASSPHRASE_PW1 + PW1_TEST0)
assert r
def test_verify_pw1_0(card):
v = card.cmd_verify(1, PW1_TEST0)
assert v
def test_verify_pw1_0_2(card):
v = card.cmd_verify(2, PW1_TEST0)
assert v
def test_setup_pw1_1(card):
r = card.cmd_change_reference_data(1, PW1_TEST0 + PW1_TEST1)
assert r
def test_verify_pw1_1(card):
v = card.cmd_verify(1, PW1_TEST1)
assert v
def test_verify_pw1_1_2(card):
v = card.cmd_verify(2, PW1_TEST1)
assert v
def test_setup_reset_code(card):
r = card.cmd_put_data(0x00, 0xd3, RESETCODE_TEST)
assert r
def test_reset_code(card):
r = card.cmd_reset_retry_counter(0, 0x81, RESETCODE_TEST + PW1_TEST2)
assert r
def test_verify_pw1_2(card):
v = card.cmd_verify(1, PW1_TEST2)
assert v
def test_verify_pw1_2_2(card):
v = card.cmd_verify(2, PW1_TEST2)
assert v
def test_setup_pw3_1(card):
r = card.cmd_change_reference_data(3, PW3_TEST0 + PW3_TEST1)
assert r
def test_verify_pw3_1(card):
v = card.cmd_verify(3, PW3_TEST1)
assert v
def test_reset_userpass_admin(card):
r = card.cmd_reset_retry_counter(2, 0x81, PW1_TEST3)
assert r
def test_verify_pw1_3(card):
v = card.cmd_verify(1, PW1_TEST3)
assert v
def test_verify_pw1_3_2(card):
v = card.cmd_verify(2, PW1_TEST3)
assert v
def test_setup_pw1_4(card):
r = card.cmd_change_reference_data(1, PW1_TEST3 + PW1_TEST4)
assert r
def test_verify_pw1_4(card):
v = card.cmd_verify(1, PW1_TEST4)
assert v
def test_verify_pw1_4_2(card):
v = card.cmd_verify(2, PW1_TEST4)
assert v
def test_setup_pw3_2(card):
r = card.cmd_change_reference_data(3, PW3_TEST1 + PW3_TEST0)
assert r
def test_verify_pw3_2(card):
v = card.cmd_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)
sig = rsa_keys.compute_signature(0, digestinfo)
sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
assert r == sig_bytes
def test_sign_1(card):
digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1)
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
def test_ds_counter_1(card):
c = get_data_object(card, 0x7a)
assert c == b'\x93\x03\x00\x00\x02'
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

View File

@@ -0,0 +1,91 @@
"""
test_personalize_reset_card.py - test resetting personalization of card
Copyright (C) 2016 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
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"
def test_login_put(card):
r = card.cmd_put_data(0x00, 0x5e, b"")
assert r
def test_name_put(card):
r = card.cmd_put_data(0x00, 0x5b, b"")
assert r
def test_lang_put(card):
r = card.cmd_put_data(0x5f, 0x2d, b"")
assert r
def test_sex_put(card):
try:
# Gnuk
r = card.cmd_put_data(0x5f, 0x35, b"")
except ValueError:
# OpenPGP card which doesn't allow b""
r = card.cmd_put_data(0x5f, 0x35, b"9")
assert r
def test_url_put(card):
r = card.cmd_put_data(0x5f, 0x50, b"")
assert r
def test_pw1_status_put(card):
r = card.cmd_put_data(0x00, 0xc4, b"\x00")
assert r
def test_setup_pw3_0(card):
r = card.cmd_change_reference_data(3, PW3_TEST0 + FACTORY_PASSPHRASE_PW3)
assert r
def test_verify_pw3_0(card):
v = card.cmd_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)
assert r
def test_verify_pw1_0(card):
v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1)
assert v
def test_verify_pw1_0_2(card):
v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1)
assert v
def test_setup_reset_code(card):
r = card.cmd_put_data(0x00, 0xd3, b"")
assert r

View File

@@ -0,0 +1,44 @@
"""
test_remove_keys_card.py - test removing keys on card
Copyright (C) 2016 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/>.
"""
# 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"
def test_rsa_import_key_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):
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):
r = card.cmd_put_data(0x00, 0xc3, KEY_ATTRIBUTES_RSA4K)
if r:
r = card.cmd_put_data(0x00, 0xc3, KEY_ATTRIBUTES_RSA2K)
assert r

7
tests/util.py Normal file
View File

@@ -0,0 +1,7 @@
def get_data_object(card, tag):
tagh = tag >> 8
tagl = tag & 0xff
return card.cmd_get_data(tagh, tagl)
def check_null(data_object):
return data_object == None or len(data_object) == 0

View File

@@ -49,6 +49,7 @@ def main(fileid, is_update, data, passwd):
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_write_binary(fileid, data, is_update)
gnuk.cmd_select_openpgp()

View File

@@ -46,6 +46,7 @@ def main(passwd):
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()
gnuk.cmd_put_data_remove(0x00, 0xc7) # FP_SIG

View File

@@ -1,7 +1,7 @@
"""
gnuk_token.py - a library for Gnuk Token
Copyright (C) 2011, 2012, 2013, 2015
Copyright (C) 2011, 2012, 2013, 2015, 2017
Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
@@ -26,6 +26,12 @@ import binascii
import usb, time
from array import array
# Possible Gnuk Token products
USB_PRODUCT_LIST=[
{ 'vendor' : 0x234b, 'product' : 0x0000 }, # FSIJ Gnuk Token
{ 'vendor' : 0x20a0, 'product' : 0x4211 }, # Nitrokey Start
]
# USB class, subclass, protocol
CCID_CLASS = 0x0B
CCID_SUBCLASS = 0x00
@@ -289,13 +295,19 @@ class gnuk_token(object):
count += 1
def cmd_select_openpgp(self):
cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, b"\xD2\x76\x00\x01\x24\x01")
sw = self.icc_send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01")
r = self.icc_send_cmd(cmd_data)
if len(r) < 2:
raise ValueError(r)
sw = r[-2:]
r = r[0:-2]
if sw[0] == 0x61:
self.cmd_get_response(sw[1])
return True
elif sw[0] == 0x90 and sw[1] == 0x00:
return True
else:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_get_data(self, tagh, tagl):
cmd_data = iso7816_compose(0xca, tagh, tagl, b"")
@@ -341,8 +353,8 @@ class gnuk_token(object):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_reset_retry_counter(self, how, data):
cmd_data = iso7816_compose(0x2c, how, 0x00, data)
def cmd_reset_retry_counter(self, how, who, data):
cmd_data = iso7816_compose(0x2c, how, who, data)
sw = self.icc_send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError(sw)
@@ -580,19 +592,18 @@ def gnuk_devices():
alt.interfaceProtocol == CCID_PROTOCOL_0:
yield dev, config, alt
USB_VENDOR_FSIJ=0x234b
USB_PRODUCT_GNUK=0x0000
def gnuk_devices_by_vidpid():
busses = usb.busses()
for bus in busses:
devices = bus.devices
for dev in devices:
if dev.idVendor != USB_VENDOR_FSIJ:
continue
if dev.idProduct != USB_PRODUCT_GNUK:
continue
yield dev
for cand in USB_PRODUCT_LIST:
if dev.idVendor != cand['vendor']:
continue
if dev.idProduct != cand['product']:
continue
yield dev
break
def get_gnuk_device():
icc = None

View File

@@ -3,7 +3,7 @@
"""
hub_ctrl.py - a tool to control port power/led of USB hub
Copyright (C) 2006, 2011 Free Software Initiative of Japan
Copyright (C) 2006, 2011, 2016 Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
@@ -23,6 +23,7 @@ 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 __future__ import print_function
import usb
USB_RT_HUB = (usb.TYPE_CLASS | usb.RECIP_DEVICE)
@@ -43,7 +44,7 @@ def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None):
hubs = []
busses = usb.busses()
if not busses:
raise ValueError, "can't access USB"
raise ValueError("can't access USB")
for bus in busses:
devices = bus.devices
@@ -79,17 +80,17 @@ def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None):
continue
if printout_enable:
print "Hub #%d at %s:%03d" % (number_of_hubs_with_feature,
bus.dirname, dev.devnum)
print("Hub #%d at %s:%03d" % (number_of_hubs_with_feature,
bus.dirname, dev.devnum))
if (desc[3] & 0x03) == 0:
print " INFO: ganged power switching."
print(" INFO: ganged power switching.")
elif (desc[3] & 0x03) == 1:
print " INFO: individual power switching."
print(" INFO: individual power switching.")
elif (desc[3] & 0x03) == 2 or (desc[3] & 0x03) == 3:
print " WARN: no power switching."
print(" WARN: no power switching.")
if (desc[3] & 0x80) == 0:
print " WARN: Port indicators are NOT supported."
print(" WARN: Port indicators are NOT supported.")
hubs.append({ 'busnum' : bus.dirname, 'devnum' : dev.devnum,
'indicator_support' : (desc[3] & 0x80) == 0x80,
@@ -99,7 +100,7 @@ def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None):
return hubs
def hub_port_status(handle, num_ports):
print " Hub Port Status:"
print(" Hub Port Status:")
for i in range(num_ports):
port = i + 1
status = handle.controlMsg(requestType = USB_RT_PORT | usb.ENDPOINT_IN,
@@ -108,31 +109,31 @@ def hub_port_status(handle, num_ports):
index = port, buffer = 4,
timeout = 1000)
print " Port %d: %02x%02x.%02x%02x" % (port, status[3], status[2],
status[1], status[0]),
print(" Port %d: %02x%02x.%02x%02x" % (port, status[3], status[2],
status[1], status[0]),)
if status[1] & 0x10:
print " indicator",
print(" indicator", end='')
if status[1] & 0x08:
print " test" ,
print(" test" , end='')
if status[1] & 0x04:
print " highspeed",
print(" highspeed", end='')
if status[1] & 0x02:
print " lowspeed",
print(" lowspeed", end='')
if status[1] & 0x01:
print " power",
print(" power", end='')
if status[0] & 0x10:
print " RESET",
print(" RESET", end='')
if status[0] & 0x08:
print " oc",
print(" oc", end='')
if status[0] & 0x04:
print " suspend",
print(" suspend", end='')
if status[0] & 0x02:
print " enable",
print(" enable", end='')
if status[0] & 0x01:
print " connect",
print(" connect", end='')
print
print()
import sys
@@ -142,9 +143,9 @@ COMMAND_SET_POWER = 2
HUB_LED_GREEN = 2
def usage(progname):
print >> sys.stderr, """Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}]
print("""Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}]
[-P PORT] [{-p [VALUE]|-l [VALUE]}]
""" % progname
""" % progname, file=sys.stderr)
def exit_with_usage(progname):
usage(progname)
@@ -219,7 +220,7 @@ if __name__ == '__main__':
hubs = find_hubs(listing, verbose, busnum, devnum, hub)
if len(hubs) == 0:
print >> sys.stderr, "No hubs found."
print("No hubs found.", file=sys.stderr)
exit(1)
if listing:
exit(0)
@@ -246,11 +247,11 @@ if __name__ == '__main__':
feature = USB_PORT_FEAT_INDICATOR
index = (value << 8) | port
if verbose:
print "Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d) " % (request, feature, index)
print("Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d) " % (request, feature, index))
uh.controlMsg(requestType = USB_RT_PORT, request = request, value = feature,
index = index, buffer = None, timeout = 1000)
if verbose:
hub_port_status(uh,nports)
hub_port_status(uh,nports)
del uh

View File

@@ -44,6 +44,7 @@ def main(wait_e, keyno, passwd, data_regnual, data_upgrade):
rsa_raw_pubkey = rsa.get_raw_pubkey(rsa_key)
gnuk = get_gnuk_device()
gnuk.cmd_select_openpgp()
gnuk.cmd_verify(BY_ADMIN, passwd.encode('UTF-8'))
gnuk.cmd_write_binary(1+keyno, rsa_raw_pubkey, False)
@@ -68,8 +69,9 @@ def main(wait_e, keyno, passwd, data_regnual, data_upgrade):
gnuk = None
#
reg = None
print("Waiting for device to appear:")
while reg == None:
print("Wait %d seconds..." % wait_e)
print(" Wait %d seconds..." % wait_e)
time.sleep(wait_e)
for dev in gnuk_devices_by_vidpid():
try:
@@ -83,9 +85,13 @@ def main(wait_e, keyno, passwd, data_regnual, data_upgrade):
print("%08x:%08x" % mem_info)
print("Downloading the program")
reg.download(mem_info[0], data_upgrade)
print("Protecting device")
reg.protect()
print("Finish flashing")
reg.finish()
print("Resetting device")
reg.reset_device()
print("Update procedure finished")
return 0
from getpass import getpass
@@ -118,6 +124,9 @@ if __name__ == '__main__':
passwd = getpass("Admin password: ")
filename_regnual = sys.argv[1]
filename_upgrade = sys.argv[2]
if not filename_regnual.endswith('bin') or not filename_upgrade.endswith('bin'):
print("Both input files must be in binary format (*.bin)!")
exit(1)
f = open(filename_regnual,"rb")
data_regnual = f.read()
f.close()

View File

@@ -3,7 +3,7 @@
"""
usb_strings.py - a tool to dump USB string
Copyright (C) 2012, 2015 Free Software Initiative of Japan
Copyright (C) 2012, 2015, 2017 Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -22,28 +22,14 @@ 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 *
import usb, sys
USB_VENDOR_FSIJ=0x234b
USB_PRODUCT_GNUK=0x0000
def gnuk_devices_by_vidpid():
busses = usb.busses()
for bus in busses:
devices = bus.devices
for dev in devices:
if dev.idVendor != USB_VENDOR_FSIJ:
continue
if dev.idProduct != USB_PRODUCT_GNUK:
continue
yield dev
field = ['', 'Vendor', 'Product', 'Serial', 'Revision', 'Config', 'Sys', 'Board']
def main(n):
for dev in gnuk_devices_by_vidpid():
handle = dev.open()
print("Device: %s" % dev.filename)
try:
for i in range(1,n):
s = handle.getString(i, 512)