Compare commits

...

67 Commits

Author SHA1 Message Date
NIIBE Yutaka
b619db59d2 version 1.0.3 2013-03-14 14:46:53 +09:00
NIIBE Yutaka
8870d58b69 stabilize stlinkv2.py 2013-03-14 14:02:23 +09:00
NIIBE Yutaka
4abcddef93 fix for newer GNU Toolchain 2013-03-14 13:24:35 +09:00
NIIBE Yutaka
ca46cc465c more 2013-03-12 14:49:52 +09:00
NIIBE Yutaka
3cf5ed482f more 2013-03-12 14:24:47 +09:00
NIIBE Yutaka
87d95d0864 more change for stlinkv2.py 2013-03-12 14:10:50 +09:00
NIIBE Yutaka
428adc7ac6 add changelog 2013-03-12 11:43:44 +09:00
NIIBE Yutaka
cc185eabb5 Improve initialization 2013-03-12 11:42:07 +09:00
NIIBE Yutaka
80e3cda267 change main.c for relocatable reGNUal 2013-03-09 10:18:24 +09:00
NIIBE Yutaka
ee867794e7 update regnual 2013-03-09 10:17:16 +09:00
NIIBE Yutaka
a4f7386b8a relocatable reGNUal 2013-03-09 10:16:51 +09:00
NIIBE Yutaka
7d4d6cff5e fix usb. 2013-03-07 13:07:31 +09:00
NIIBE Yutaka
b09460f0ae follow change of USB stack 2013-03-07 09:42:49 +09:00
NIIBE Yutaka
4298809ffb USB stack implementation improvement 2013-03-07 09:37:49 +09:00
NIIBE Yutaka
b297cf22b6 add changelog entry for the bug fix of usb-icc 2013-02-27 20:23:01 +09:00
NIIBE Yutaka
f84f52156c bug fix 2013-02-27 20:20:59 +09:00
NIIBE Yutaka
1564a4fbe6 Fix configure typo, type punning pointers 2013-02-25 15:46:02 +09:00
NIIBE Yutaka
16610ca5c7 Merge branch 'STABLE-BRANCH-1-0' of www.gniibe.org:git/gnuk into STABLE-BRANCH-1-0 2013-02-22 21:34:16 +09:00
NIIBE Yutaka
352f81a61f Add Quan to THANKS 2013-02-22 21:32:58 +09:00
NIIBE Yutaka
7c82839fba apply GPG_DO_DISCRETIONARY from master 2013-02-22 21:29:27 +09:00
NIIBE Yutaka
10c685be16 add Paul to THANKS (should be done at the beginning) 2013-02-20 16:56:10 +09:00
NIIBE Yutaka
ed61ed980d version 1.0.2 2013-02-15 11:45:52 +09:00
NIIBE Yutaka
7ff0baf5df test script changes 2013-02-14 16:59:41 +09:00
NIIBE Yutaka
46259ce63d fix Le field handling 2013-02-14 16:38:38 +09:00
NIIBE Yutaka
0aca10f307 fixes for removing update keys 2013-02-14 11:09:06 +09:00
NIIBE Yutaka
5f2a8b835c update documentation 2013-02-13 14:23:24 +09:00
NIIBE Yutaka
5213d9ab82 fix GET CHALLENGE 2013-02-13 13:32:38 +09:00
NIIBE Yutaka
c12f331217 fix typo in openpgp.c 2013-02-04 16:40:26 +09:00
NIIBE Yutaka
62b4369d2c minor change to external_authenticate 2013-01-30 16:50:19 +09:00
NIIBE Yutaka
9dde59867d Implement VERIFY with empty data 2013-01-28 12:10:14 +09:00
NIIBE Yutaka
eae955b15e try VEGA ALPHA specific change 2013-01-22 21:08:55 +09:00
NIIBE Yutaka
7e8dd12654 New tool/pageant_proxy_to_gpg.py 2013-01-21 12:10:25 +09:00
NIIBE Yutaka
8c91d2ef2e Use APPDATA for HOME equivalent 2013-01-21 12:09:33 +09:00
NIIBE Yutaka
6b6913c676 add tool/sexp.py 2013-01-21 12:05:06 +09:00
NIIBE Yutaka
3ad9373163 fix gpg_agent.py 2013-01-21 09:22:47 +09:00
NIIBE Yutaka
220d5c0307 Add tool/pageant_proxy_to_gpg.py 2013-01-20 21:18:21 +09:00
NIIBE Yutaka
de7f9f6417 improve 2013-01-20 21:17:43 +09:00
NIIBE Yutaka
a302585602 move 2013-01-20 20:40:34 +09:00
NIIBE Yutaka
ea2191105f improve gpg-agent.py 2013-01-20 20:40:19 +09:00
NIIBE Yutaka
32094099dd add tool/gpg-agent.py 2013-01-20 18:01:24 +09:00
NIIBE Yutaka
9b71d70b73 fix pinpadtest.py 2013-01-11 13:21:38 +09:00
NIIBE Yutaka
77d06fb301 tool/pinpadtest: add fixed length input 2013-01-11 12:16:50 +09:00
NIIBE Yutaka
78b642507b change the order of sections 2012-12-26 14:08:49 +09:00
NIIBE Yutaka
90a11859dc update doc (partially) 2012-12-26 11:11:18 +09:00
NIIBE Yutaka
ad9a901e1b upgrade_by_passwd.py 2012-12-25 14:47:49 +09:00
NIIBE Yutaka
500b12b60d update NEWS 2012-12-19 16:07:33 +09:00
NIIBE Yutaka
4bfe087583 add test/factory_upgrade.py 2012-12-19 15:53:07 +09:00
NIIBE Yutaka
22d0cb689a add regnual class 2012-12-19 13:13:52 +09:00
NIIBE Yutaka
fe6337f988 new methods for gnuk_token.py 2012-12-19 12:57:36 +09:00
NIIBE Yutaka
1a2560531d fix 2012-12-19 11:19:17 +09:00
NIIBE Yutaka
40e234b799 Use tool/gnuk_token.py 2012-12-19 10:44:50 +09:00
NIIBE Yutaka
deccb981ad minor: fix style 2012-12-19 10:04:45 +09:00
NIIBE Yutaka
12bd1161a4 password length check for adminlessmode 2012-12-14 10:31:46 +09:00
NIIBE Yutaka
d72a0b7893 fix src/openpgp-do.c 2012-12-14 09:53:16 +09:00
NIIBE Yutaka
ee5b6a2a82 fix tool/gnuk_remove_keys* 2012-12-12 15:30:40 +09:00
NIIBE Yutaka
f9b43a67ee Add tool/gnuk_remove_keys_libusb.py 2012-12-05 14:27:15 +09:00
NIIBE Yutaka
70846e8b81 add explanation for gnuk-personalization.rst 2012-11-12 10:49:27 +09:00
NIIBE Yutaka
f6df7701f9 add explanation for gnuk-personalization.rst 2012-11-12 10:44:44 +09:00
NIIBE Yutaka
b9772265cf proper bError value for time extension 2012-11-07 11:30:00 +09:00
NIIBE Yutaka
254c521c6f tool/gnuk_upgrade.py: Support non-smartcard auth 2012-11-01 09:18:55 +09:00
NIIBE Yutaka
d7c6b95ba1 add tool/get_raw_public_key.py 2012-10-31 16:23:19 +09:00
NIIBE Yutaka
2e7d93a556 change the product string 2012-10-26 11:05:33 +09:00
NIIBE Yutaka
db2d897c3f Update README and fix for STBEE_MINI 2012-10-13 08:40:37 +09:00
NIIBE Yutaka
23a9fe3bdc stlinkv2 fixes 2012-09-25 09:00:10 +09:00
NIIBE Yutaka
e8f773d2f6 fix stlinkv2.py 2012-09-18 15:32:12 +09:00
NIIBE Yutaka
6b5fc04c0d fix sha256 2012-09-12 14:16:40 +09:00
NIIBE Yutaka
39bee2ee01 Fix by Henry Hu 2012-08-29 13:16:27 +09:00
59 changed files with 2198 additions and 747 deletions

307
ChangeLog
View File

@@ -1,3 +1,310 @@
2013-03-14 Niibe Yutaka <gniibe@fsij.org>
* Version 1.0.3.
* src/usb_desc.c (gnukStringSerial): Updated.
* tool/stlinkv2.py (stlinkv2.start): Call write_debug_reg to run
the core again.
2013-03-13 Niibe Yutaka <gniibe@fsij.org>
* ChibiOS_2.0.8/os/ports/GCC/ARMCMx/cmsis/core_cm3.c (__STREXB)
(__STREXH, __STREXW): Specify R2 to avoid %0 and %2 will be same
register. This is for newer GNU binutils (>= 2.22).
2013-03-12 Niibe Yutaka <gniibe@fsij.org>
* tool/stlinkv2.py (stlinkv2.exit_from_debug_swd)
(stlinkv2.exit_from_debug_swim): New.
(stlinkv2.start): Call exit_from_debug_swd or
exit_from_debug_swim.
2013-03-09 Niibe Yutaka <gniibe@fsij.org>
* src/main.c (calculate_regnual_entry_address): New.
(main): Use calculate_regnual_entry_address for entry point.
2013-03-08 Niibe Yutaka <gniibe@fsij.org>
Relocatable reGNUal.
* regnual/regnual.ld (MEMORY): 0x1400 was the value of Gnuk 1.0.1.
Keep this value.
(.text): Include .text.entry next to the .vectors.
(.got): New.
* regnual/sys.c (entry): Now, it's at .text.entry section.
Do relocations.
Don't use absolute values which causes relocations, but
access at GOT.
* regnual/Makefile (CFLAGS): Add -fpie.
2013-03-07 Niibe Yutaka <gniibe@fsij.org>
* src/usb_stm32f103.c (handle_setup0): Fix selecting handler.
Follow the USB stack change.
* regnual/regnual.c (usb_cb_device_reset): Rename from
regnual_device_reset.
(mem): Change type to uint32_t.
(mem_info): Removed.
(fetch): Avoid pointer punning.
(usb_cb_ctrl_write_finish): Rename from regnual_ctrl_write_finish.
(usb_cb_setup): Rename from regnual_setup.
(usb_cb_get_descriptor): Rename from regnual_get_descriptor.
(usb_cb_handle_event): Rename regnual_usb_event.
(usb_cb_interface): Rename regnual_interface.
(Device_Method): Remove.
(usb_cb_get_descriptor): Not use struct Descriptor.
2013-03-06 Niibe Yutaka <gniibe@fsij.org>
USB stack implementation improvement.
* src/usb_lld.c (Device_Method, method_p): Remove.
(usb_interrupt_handler): Call usb_cb_device_reset.
(std_get_descriptor): Call usb_cb_get_descriptor.
(std_set_configuration): Call usb_cb_handle_event.
(std_get_status, std_get_interface, std_set_interface): Call
usb_cb_interface.
(handle_setup0): Call usb_cb_setup.
(handle_in0): Call usb_cb_handle_event and
usb_cb_ctrl_write_finish.
(request_handler): Remove.
(handle_setup0): Call std_* directly, not indirectly by
request_handler.
(ep_intr_handler_IN, ep_intr_handler_OUT): Remove.
(usb_handle_transfer): Call EP*_Callback directly, not indirectly
by ep_intr_handler_IN, ep_intr_handler_OUT.
* src/usb_lld.h (struct usb_device_method, Device_Method): Remove.
(usb_cb_device_reset, usb_cb_ctrl_write_finish)
(usb_cb_setup, usb_cb_get_descriptor, usb_cb_handle_event)
(usb_cb_interface): Define callbacks.
(usb_initial_feature): New.
(struct Descriptor): Move to ...
* src/usb_desc.c: ... here.
(usb_initial_feature): New.
(usb_cb_get_descriptor): Rename from gnuk_get_descriptor and move
from usb_ctrl.c.
* src/usb_ctrl.c (usb_cb_device_reset): Rename from
gnuk_device_reset.
(usb_cb_setup): Rename from gnuk_setup.
(usb_cb_ctrl_write_finish): Rename from gnuk_ctrl_write_finish.
(usb_cb_event): Rename from gnuk_usb_event.
(usb_cb_interface): Rename from gnuk_interface.
(Device_Method): Remove.
* src/main.c (main): Use usb_initial_feature.
2013-02-27 Niibe Yutaka <gniibe@fsij.org>
* src/usb-icc.c (set_sw1sw2): Arguments are C and CHUNK_LEN.
Fix reporting remaining bytes.
(icc_send_data_block_gr): Follow the arguments change of
set_sw1sw2.
2013-02-25 Niibe Yutaka <gniibe@fsij.org>
* src/configure: Correct typo in help text.
* src/gnuk.h (struct key_data_internal): Use uint32_t.
* src/openpgp-do.c (do_openpgpcard_aid): Fix calculation of VID.
(compute_key_data_checksum): Don't use type-punning pointer.
(gpg_do_write_prvkey): Use coercing to char *.
2013-02-22 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (GPG_DO_DISCRETIONARY, cmp_discretionary): New.
(cmp_app_data): Change to factor out GPG_DO_DISCRETIONARY.
(gpg_do_table): Add GPG_DO_DISCRETIONARY.
2013-02-15 Niibe Yutaka <gniibe@fsij.org>
* Version 1.0.2.
* src/usb_desc.c (gnukStringSerial): Updated.
2013-02-14 Niibe Yutaka <gniibe@fsij.org>
* test/features/002_get_data_static.feature: Value of extended
capabilities changed.
* test/features/402_get_data_static.feature: Ditto.
* test/features/802_get_data_static.feature: Ditto.
* src/openpgp.c (cmd_write_binary): Move erasing page of update
keys to...
(modify_binary): ...here.
* src/flash.c (flash_write_binary): Handle removal of update keys.
2013-02-13 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp.c (cmd_get_challenge): Handle Le field.
* src/openpgp-do.c (extended_capabilities): Fix for GET CHALLENGE.
* src/gnuk.h (CHALLENGE_LEN): Moved here (was: openpgp.c).
* tool/gnuk_token.py (iso7816_compose): Add Le field.
2013-01-30 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp.c (cmd_external_authenticate): Fix off-by-one error.
* tool/gnuk_token.py (gnuk_token.cmd_external_authenticate): Add
KEYNO to the arguments.
* tool/upgrade_by_passwd.py (main): Explicitly say it's KEYNO.
2013-01-28 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (gpg_pw_get_retry_counter): New.
* src/openpgp.c (cmd_verify): Implement VERIFY with empty data.
2013-01-22 Niibe Yutaka <gniibe@fsij.org>
* tool/pinpadtest.py (Card.cmd_vega_alpha_disable_empty_verify):
New.
(main): call cmd_vega_alpha_disable_empty_verify if it's
COVADIS_VEGA_ALPHA.
2013-01-21 Niibe Yutaka <gniibe@fsij.org>
* tool/pageant_proxy_to_gpg.py: New.
* tool/sexp.py: New.
2013-01-20 Niibe Yutaka <gniibe@fsij.org>
* tool/gpg_agent.py: New.
2013-01-11 Niibe Yutaka <gniibe@fsij.org>
* tool/pinpadtest.py: Add fixed length input.
2012-12-25 Niibe Yutaka <gniibe@fsij.org>
* tool/rsa.py: New.
* tool/rsa_example.key: New. Example RSA key information.
* tool/upgrade_by_passwd.py: New.
2012-12-19 Niibe Yutaka <gniibe@fsij.org>
* src/Makefile.in (USE_OPT): -O3 and -Os (was: -O2).
* tool/gnuk_token.py (gnuk_token.stop_gnuk, gnuk_token.mem_info)
(gnuk_token.download, gnuk_token.execute)
(gnuk_token.cmd_get_challenge)
(gnuk_token.cmd_external_authenticate): New.
(gnuk_devices_by_vidpid): New.
(regnual): New.
2012-12-18 Niibe Yutaka <gniibe@fsij.org>
* test/gnuk.py: Remove.
* test/features/steps.py: Use tool/gnuk_token.py.
* tool/gnuk_put_binary_libusb.py: Use gnuk_token.py.
(main): Follow the API change.
* tool/gnuk_token.py (list_to_string): New.
(gnuk_token.get_string, gnuk_token.increment_seq)
(gnuk_token.reset_device, gnuk_token.release_gnuk): New.
(gnuk_token.icc_power_on): Set self.atr and it's now string.
(gnuk_token.icc_send_cmd): Handle time extension.
(gnuk_token.cmd_get_response): Return string (was: list).
(gnuk_token.cmd_get_data): Return "" when success.
(gnuk_token.cmd_change_reference_data, gnuk_token.cmd_put_data)
(gnuk_token.cmd_put_data_odd)
(gnuk_token.cmd_reset_retry_counter, gnuk_token.cmd_pso)
(gnuk_token.cmd_pso_longdata)
(gnuk_token.cmd_internal_authenticate, gnuk_token.cmd_genkey)
(gnuk_token.cmd_get_public_key): New.
(compare): New.
(get_gnuk_device): New.
2012-12-14 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp.c (cmd_change_password): Check password length
for admin less mode.
2012-12-13 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (gpg_do_put_data): Add GPG_SUCCESS for
completeness (it worked because of lower layer goodness).
2012-12-12 Niibe Yutaka <gniibe@fsij.org>
* tool/gnuk_token.py: Add module imports.
* tool/gnuk_remove_keys.py (main): Fix data object number
for KGTIME_SIG, KGTIME_DEC and KGTIME_AUT.
* tool/gnuk_remove_keys_libusb.py (main): Likewise.
2012-12-05 Niibe Yutaka <gniibe@fsij.org>
* tool/gnuk_remove_keys_libusb.py: New.
* tool/gnuk_token.py: New.
2012-11-07 Niibe Yutaka <gniibe@fsij.org>
* src/usb-icc.c (icc_send_data_block_internal): New.
(icc_send_data_block_time_extension): New.
(icc_handle_timeout): Use icc_send_data_block_time_extension.
(icc_send_data_block): Only one argument.
(USBthread): Follow the change.
2012-11-01 Niibe Yutaka <gniibe@fsij.org>
* tool/gnuk_upgrade.py (main): New option '-k' to specify keygrip
for non-smartcard key.
(gpg_sign): Support non-smartcard key.
2012-10-31 Niibe Yutaka <gniibe@fsij.org>
* tool/get_raw_public_key.py: New.
2012-10-26 Niibe Yutaka <gniibe@fsij.org>
* GNUK_USB_DEVICE_ID (Product_STRING): It's considered better not
to include vendor name. Change the name to "Gnuk Token" (was:
FSIJ USB Token).
2012-10-13 Niibe Yutaka <gniibe@fsij.org>
* boards/STBEE_MINI/board.c [!DFU_SUPPORT] (hwinit1): Don't run
when "user switch" is pushed. This is for JTAG/SWD debugger.
2012-09-25 Niibe Yutaka <gniibe@fsij.org>
* tool/stlinkv2.py (main): Print out option bytes value.
Call reset_sys before blank_check.
2012-09-18 Niibe Yutaka <gniibe@fsij.org>
* tool/stlinkv2.py (stlinkv2.option_bytes_erase)
(stlinkv2.flash_erase_all, stlinkv2.flash_erase_page): : Fix
OperationFailure (was OperationError).
(main): Call option_bytes_erase if it's not 0xff.
2012-09-12 Niibe Yutaka <gniibe@fsij.org>
* src/sha256.c: Include <stdint.h>.
* src/sha256.h (SHA256_DIGEST_SIZE, SHA256_BLOCK_SIZE): Move
from sha256.c.
2012-08-29 Niibe Yutaka <gniibe@fsij.org>
* tool/hub_ctrl.py (__main__): Fix to busnum (was: bunum).
Thanks to Henry Hu.
2012-08-03 Niibe Yutaka <gniibe@fsij.org>
* Version 1.0.1.

View File

@@ -731,7 +731,7 @@ uint32_t __LDREXW(uint32_t *addr)
*/
uint32_t __STREXB(uint8_t value, uint8_t *addr)
{
uint32_t result=0;
register uint32_t result asm ("r2");
__ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
return(result);
@@ -748,7 +748,7 @@ uint32_t __STREXB(uint8_t value, uint8_t *addr)
*/
uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
uint32_t result=0;
register uint32_t result asm ("r2");
__ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
return(result);
@@ -765,7 +765,7 @@ uint32_t __STREXH(uint16_t value, uint16_t *addr)
*/
uint32_t __STREXW(uint32_t value, uint32_t *addr)
{
uint32_t result=0;
register uint32_t result asm ("r2");
__ASM volatile ("strex %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
return(result);

View File

@@ -1,3 +1,3 @@
# VID:PID bcdDev Product_STRING Vender_STRING
234b:0000 0200 FSIJ USB Token Free Software Initiative of Japan
234b:0000 0200 Gnuk Token Free Software Initiative of Japan
##########<TAB> ##<TAB> ##########<TAB> #################

59
NEWS
View File

@@ -1,5 +1,64 @@
Gnuk NEWS - User visible changes
* Major changes in Gnuk 1.0.3
Released 2013-03-14, by NIIBE Yutaka
** Relocatable reGNUal
The upgrade helper, reGNUal, is now relocatable (other than the first
vector table). It runs well when loaded at different address. This
makes the upgrade procedure more stable.
** Compilation by newer GNU Toolchain
Now, Gnuk can be compiled with newer GNU Toolchain, specifically GCC
4.7.x and GNU Binutils 2.22. Old versions of Gnuk had problem for
ChibiOS_2.0.8/os/ports/GCC/ARMCMx/cmsis/core_cm3.c, which was fixed.
** Data object 0x0073
Data object 0x0073 is now available.
* Major changes in Gnuk 1.0.2
Released 2013-02-15, by NIIBE Yutaka
** Product string is now "Gnuk Token" (was: "FSIJ USB Token")
Since the USB ID Repository suggests not including vendor name
in product string, we changed the product string.
** New tool (experimental): test/upgrade_by_passwd.py
This is the tool to install new firmware to Gnuk Token, provided
that it's just shipped from factory (and nothing changed). It
authenticate as admin by factory setting, register a public key
for firmware upgrade, and then, does firmware upgrade.
** tool/gnuk_upgrade.py supports '-k' option
It now supports RSA key on the host PC (not the one on the Token).
** New tool: tool/get_raw_public_key.py
This is a script to dump raw data of RSA public key, which is useful
to register to Gnuk Token as a firmware upgrade key.
** New tool: tool/gnuk_remove_keys_libusb.py
This tool is libusb version of gnuk_remove_keys.py. Besides, a bug in
gnuk_remove_keys.py was fixed.
** CCID protocol fix
When time extension is requested by Gnuk Token to host PC, argument
field was 0, which was wrong (but it works for most PC/SC
implementations and GnuPG internal driver). Now it's 1, which means
1*BWT.
** OpenPGP card protocol enhancement
Now, VERIFY command accepts empty data and returns remaining trial
counts, or 0x9000 (OK) when it's already authenticated. This is
useful for application to synchronize card's authentication status.
** Extended Capabilities
Since Gnuk supports GET CHALLENGE command, the flag for GET CHALLENGE
in extended capabilities are now on.
* Major changes in Gnuk 1.0.1
Released 2012-08-03, by NIIBE Yutaka

44
README
View File

@@ -1,7 +1,7 @@
Gnuk - An Implementation of USB Cryptographic Token for GnuPG
Version 1.0.1
2012-08-03
Version 1.0.3
2013-03-14
Niibe Yutaka
Free Software Initiative of Japan
@@ -42,7 +42,7 @@ A0: Good points of Gnuk are:
"for Free Software"; Gnuk supports GnuPG.
Q1: What kind of key algorithm is supported?
A1: Gnuk only supports 2048-bit RSA.
A1: Gnuk version 1 only supports 2048-bit RSA.
Q2: How long does it take for digital signing?
A2: It takes a second and a half or so.
@@ -108,16 +108,21 @@ Ab: That's because gnome-keyring-daemon interferes GnuPG. Type:
"GPG Password Agent" and "SSH Key Agent".
Qc: Do you know a good SWD debugger to connect FST-01 or something?
Ac: ST-Link/V2 is cheap one. We have a tool/stlinkv2.py as flash ROM
writer program.
Ac: ST-Link/V2 is cheap one and works, although it's not very good
(for example, we have not yet been able to use OpenOCD to write
option bytes of STM32F103). We have a tool/stlinkv2.py as a flash
ROM writer program. Note that some "Discovery Kit" from ST has
the feature of ST-Link/V2, but it is not as stable as the real
one.
Release notes
=============
This is a minor release in version 1.0 series of Gnuk.
This is a third minor release in version 1.0 series of Gnuk.
While it is daily use for more than a year, some newly introduced
While it is daily use for a year and a half, some newly introduced
features (including key generation and firmware upgrade) should be
considered experimental.
@@ -174,7 +179,7 @@ disable read from flash. For real use, please consider killing DfuSe
and enabling read protection using JTAG debugger.
I think that it could run on Olimex STM32-P103, or other boards with
STM32F103. Besides, we are porting it to STM32 Primer 2.
STM32F103. Besides, we did an experiment with STM32 Primer 2.
For PIN-pad support, I connect a consumer IR receive module to STBee
Mini and STM8S Discovery Kit, and use controller for TV. PIN
@@ -261,9 +266,8 @@ please contact Niibe, so that it is listed to the file in the official
release of the source code.
When you are modifing Gnuk and installing the binary to device, you
should replace "FSIJ" in the string gnukStringSerial (usb_desc.c) to
yours, so that the device will say it's modified version by device
serial number.
should replace the vendor string to yours, so that users can see it's
not by original vendor, and it is modified version.
FSIJ allows you to use USB device ID of FSIJ (234b:0000) for devices
with Gnuk under one of following conditions:
@@ -294,8 +298,8 @@ respect users' freedom for computing. Please ask FSIJ for the
license.
Otherwise, companies which want to distribute Gnuk devices, please use
your own USB vendor ID and product ID. Please replace "FSIJ" in the
string gnukStringSerial (usb_desc.c) to yours, when you modify Gnuk.
your own USB vendor ID and product ID. Please replace vendor string
and possibly product string to yours, when you modify Gnuk.
Host Requirements
@@ -339,8 +343,7 @@ Recently, there is "gcc-arm-embedded" project. See:
https://launchpad.net/gcc-arm-embedded/
It is based on GCC 4.6. For version 4.6-2012-q2-update, you'd
need "-O3 -Os" instead of "-O2" and it will be slightly better.
It is based on GCC 4.6 or 4.7.
Change directory to `src':
@@ -658,13 +661,14 @@ linux/Documentation/usb/usbmon.txt
Firmware update
===============
See doc/note/firmware-update.
See doc/note/firmware-update. Note that this is an experimental
feature.
Read-only Git Repository
========================
Git Repositories
================
You can browse at http://www.gniibe.org/gitweb?p=gnuk.git;a=summary
You can browse at: http://www.gniibe.org/gitweb?p=gnuk.git;a=summary
You can get it by:
@@ -675,6 +679,8 @@ or
$ git clone http://www.gniibe.org/git/gnuk.git/
Copy is available at: http://gitorious.org/gnuk
Information on the Web
======================

5
THANKS
View File

@@ -1,3 +1,5 @@
-*- coding: utf-8 -*-
We would like to express our gratitudes to Werner Koch for GnuPG, and
Giovanni Di Sirio for ChibiOS/RT.
@@ -11,7 +13,10 @@ Hironobu SUZUKI hironobu@h2np.net
Jan Suhr jan@suhr.info
Kaz Kojima kkojima@rr.iij4u.or.jp
Ludovic Rousseau ludovic.rousseau@free.fr
Luis Felipe R. Murillo luisfelipe@ucla.edu
MATSUU Takuto matsuu@gentoo.org
NAGAMI Takeshi nagami-takeshi@aist.go.jp
Nguyễn Hồng Quân quannguyen@mbm.vn
Paul Bakker polarssl_maintainer@polarssl.org
Shane Coughlan scoughlan@openinventionnetwork.com
Werner Koch wk@gnupg.org

View File

@@ -9,6 +9,15 @@ hwinit1 (void)
{
hwinit1_common ();
#if !defined(DFU_SUPPORT)
if (palReadPad (IOPORT3, GPIOC_BUTTON) == 0)
/*
* Since LEDs are connected to JTMS/SWDIO and JTDI pin,
* we can't use LED to let know users in this state.
*/
for (;;); /* Wait for JTAG debugger connection */
#endif
#if defined(PINPAD_SUPPORT) && !defined(DFU_SUPPORT)
palWritePort(IOPORT2, 0x7fff); /* Only clear GPIOB_7SEG_DP */
while (palReadPad (IOPORT2, GPIOB_BUTTON) != 0)

3
doc/__update_web Normal file
View File

@@ -0,0 +1,3 @@
cd _build
rsync -rntpv html/ atom.fsij.org:/home/fsij/gnuk-doc-html/
rsync -rtpv html/ atom.fsij.org:/home/fsij/gnuk-doc-html/

View File

@@ -5,10 +5,9 @@ Development Environment
Hardware
--------
For development, it is highly recommended to have JTAG debugger or SWD
debugger.
For development, it is highly recommended to have JTAG/SWD debugger.
For boards with DFU (Device Firmware Upgrade) feature, such as DfuSe,
For boards with DFU (Device Firmware Upgrade) feature (such as DfuSe),
it is possible to develop with that. But it should be considered
*experimental* environment, and it should not be used for usual
purpose. That's because it is basically impossible for DfuSe
@@ -17,17 +16,16 @@ that your secret will be readily extracted by DfuSe.
For JTAG debugger, Olimex JTAG-Tiny is good and supported well. For
SWD debugger, ST-Link/V2 would be good, and it is supported by
the tool of tool/stlinkv2.py.
tool/stlinkv2.py.
OpenOCD
-------
For JTAG debugger or SWD debugger, we can use OpenOCD.
For JTAG/SWD debugger, we can use OpenOCD.
Note that ST-Link/V2 is *not* supported by OpenOCD 0.5.0. It will be
supported by version 0.6 or later, as current development version
supports it.
Note that ST-Link/V2 is *not* supported by OpenOCD 0.5.0. It is
supported by version 0.6 or later.
GNU Toolchain
@@ -35,9 +33,11 @@ GNU Toolchain
You need GNU toolchain and newlib for 'arm-none-eabi' target.
See http://github.com/esden/summon-arm-toolchain/ (which includes fix
of binutils-2.21.1) for preparation of GNU Toolchain for
'arm-none-eabi' target. This is for GCC 4.5.
There is "gcc-arm-embedded" project. See:
https://launchpad.net/gcc-arm-embedded/
It is based on GCC 4.6. You'd need "-O3 -Os" instead of "-O2" and it
will be slightly better.
Note that we need to link correct C library (for string functions).
For this purpose, our src/Makefile.in contains following line:
@@ -45,20 +45,9 @@ For this purpose, our src/Makefile.in contains following line:
MCFLAGS= -mcpu=$(MCU) -mfix-cortex-m3-ldrd
This should not be needed (as -mcpu=cortex-m3 means
-mfix-cortex-m3-ldrd), but it is needed for the configuration of
-mfix-cortex-m3-ldrd), but it was needed for the configuration of
patch-gcc-config-arm-t-arm-elf.diff in summon-arm-toolchain in practice.
In ChibiOS_2.0.8/os/ports/GCC/ARM/rules.mk, it specifies
-mno-thumb-interwork option. This means that you should not link C
library which contains ARM (not Thumb) code.
Recently, there is "gcc-arm-embedded" project. See:
https://launchpad.net/gcc-arm-embedded/
It is based on GCC 4.6. For version 4.6-2012-q2-update, you'd
need "-O3 -s" instead of "-O2" and it will be slightly better.
Building Gnuk
-------------
@@ -72,8 +61,8 @@ Then, run ``configure``:
$ ./configure --vidpid=<VID:PID>
Here, you need to specify USB vendor ID and product ID. For FSIJ's,
it's: --vidpid=234b:0000 . Please read section 'USB vendor ID and
product ID' above.
it's: --vidpid=234b:0000 . Please read the section 'USB vendor ID and
product ID' in README.
Type:

View File

@@ -2,11 +2,45 @@
Generating 2048-bit RSA keys
============================
This document describes how I generate 2048-bit RSA keys.
In this section, we describe how to generate 2048-bit RSA keys.
.. BREAK
Here is the log to generate signature key and encryption subkey.
Key length of RSA
=================
In 2005, NIST (National Institute of Standards and Technology, USA)
has issued the first revision of NIST Special Publication 800-57,
"Recommendation for Key Management".
In 800-57, NIST advises that 1024-bit RSA keys will no longer be
viable after 2010 and advises moving to 2048-bit RSA keys. NIST
advises that 2048-bit keys should be viable until 2030.
As of 2010, GnuPG's default for generating RSA key is 2048-bit.
Some people have preference on RSA 4096-bit keys, considering
"longer is better".
However, "longer is better" is not always true. When it's long, it
requires more computational resource, memory and storage, and it
consumes more power for nomal usages. These days, many people has
enough computational resource, that would be true, but less is better
for power consumption.
For security, the key length is a single factor. We had and will have
algorithm issues, too. It is true that it's difficult to update
our public keys, but this problem wouldn't be solved by just have
longer keys.
We deliberately support only RSA 2048-bit keys for Gnuk, considering
device computation power and host software constraints.
Thus, the key size is 2048-bit in the examples below.
Generating keys on host PC
==========================
Here is the example session to generate main key and a subkey for encryption.
I invoke GnuPG with ``--gen-key`` option. ::
@@ -25,7 +59,7 @@ and GnuPG asks kind of key. Select ``RSA and RSA``. ::
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
and select 2048-bit (as Gnuk Token only suppurt this). ::
and select 2048-bit (as Gnuk Token only supports this). ::
What keysize do you want? (2048)
Requested keysize is 2048 bits
@@ -59,7 +93,12 @@ Then enter user ID. ::
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
and enter passphrase for this **key on PC**. ::
and enter passphrase for this **key on host PC**.
Note that this is a passphrase for the key on host PC.
It is different thing to the password of Gnuk Token.
We enter two same inputs two times
(once for passphrase input, and another for confirmation). ::
You need a Passphrase to protect your secret key.
<PASSWORD-KEY-ON-PC>
@@ -86,43 +125,50 @@ Then, GnuPG generate keys. It takes some time. ::
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u
pub 2048R/28C0CD7C 2011-05-24
Key fingerprint = 0B4D C763 D57B ADBB 1870 A978 BDEE 4A35 28C0 CD7C
pub 2048R/4CA7BABE 2010-10-15
Key fingerprint = 1241 24BD 3B48 62AF 7A0A 42F1 00B4 5EBD 4CA7 BABE
uid Niibe Yutaka <gniibe@fsij.org>
sub 2048R/F01E19B7 2011-05-24
sub 2048R/084239CF 2010-10-15
$
Done.
Then, I create authentication subkey. Authentication subkey is not that common, but very useful (say, for SSH authentication). As it is not that common, we need ``--expert`` option for GnuPG. ::
Then, we create authentication subkey.
Authentication subkey is not that common,
but very useful (for SSH authentication).
As it is not that common, we need ``--expert`` option for GnuPG. ::
$ gpg --expert --edit-key 28C0CD7C
$ gpg --expert --edit-key 4CA7BABE
gpg (GnuPG) 1.4.11; Copyright (C) 2010 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
pub 2048R/28C0CD7C created: 2011-05-24 expires: never usage: SC
pub 2048R/4CA7BABE created: 2010-10-15 expires: never usage: SC
trust: ultimate validity: ultimate
sub 2048R/F01E19B7 created: 2011-05-24 expires: never usage: E
sub 2048R/084239CF created: 2010-10-15 expires: never usage: E
[ultimate] (1). Niibe Yutaka <gniibe@fsij.org>
gpg>
Here, I enter ``addkey`` command. Then, I enter the passphrase of **key on PC**, I specified above. ::
Here, it displays that there are main key and a subkey.
It prompts sub-command with ``gpg>`` .
Here, we enter ``addkey`` sub-command.
Then, we enter the passphrase of **key on host PC**.
It's the one we entered above as <PASSWORD-KEY-ON-PC>. ::
gpg> addkey
Key is protected.
You need a passphrase to unlock the secret key for
user: "Niibe Yutaka <gniibe@fsij.org>"
2048-bit RSA key, ID 28C0CD7C, created 2011-05-24
2048-bit RSA key, ID 4CA7BABE, created 2010-10-15
<PASSWORD-KEY-ON-PC>
gpg: gpg-agent is not available in this session
GnuPG askes kind of key. I select ``RSA (set your own capabilities)``. ::
GnuPG asks kind of key. We select ``RSA (set your own capabilities)``. ::
Please select what kind of key you want:
(3) DSA (sign only)
@@ -133,7 +179,10 @@ GnuPG askes kind of key. I select ``RSA (set your own capabilities)``. ::
(8) RSA (set your own capabilities)
Your selection? 8
And select ``Authenticate`` for the capabilities for this key. Initially, it's ``Sign`` and ``Encrypt``. I need to deselect ``Sign`` and ``Encryp``, and select ``Authenticate``. To do that, I enter ``s``, ``a``, and ``e``. ::
And select ``Authenticate`` for the capabilities for this key.
Initially, it's ``Sign`` and ``Encrypt``.
I need to deselect ``Sign`` and ``Encrypt``, and select ``Authenticate``.
To do that, I enter ``s``, ``e``, and ``a``. ::
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt
@@ -153,31 +202,33 @@ And select ``Authenticate`` for the capabilities for this key. Initially, it's
(A) Toggle the authenticate capability
(Q) Finished
Your selection? a
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Encrypt Authenticate
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? e
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate
Current allowed actions:
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? a
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
OK, I set the capability of ``Authenticate``. I enter ``q`` to finish setting capabilities. ::
OK, we set the capability of ``Authenticate``.
We enter ``q`` to finish setting capabilities. ::
Your selection? q
GnuPG asks bitsize and expiration, I enter 2048 for bitsize and no expiration. Then, I confirm that I really create the key. ::
GnuPG asks bitsize and expiration, we enter 2048 for bitsize and no expiration.
Then, we confirm that we really create the key. ::
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
@@ -202,27 +253,59 @@ Then, GnuPG generate the key. ::
.......+++++
+++++
pub 2048R/28C0CD7C created: 2011-05-24 expires: never usage: SC
pub 2048R/4CA7BABE created: 2010-10-15 expires: never usage: SC
trust: ultimate validity: ultimate
sub 2048R/F01E19B7 created: 2011-05-24 expires: never usage: E
sub 2048R/B8929606 created: 2011-05-24 expires: never usage: A
sub 2048R/084239CF created: 2010-10-15 expires: never usage: E
sub 2048R/5BB065DC created: 2010-10-22 expires: never usage: A
[ultimate] (1). Niibe Yutaka <gniibe@fsij.org>
gpg>
I save the key. ::
We save the key (to the storage of the host PC. ::
gpg> save
$
Now, we have three keys (one primary key for signature and certification, subkey for encryption, and another subkey for authentication).
Now, we have three keys (one primary key for signature and certification,
subkey for encryption, and another subkey for authentication).
Publishing public key
=====================
I make a file for my public key by ``--export`` option of GnuPG. ::
We make a file for the public key by ``--export`` option of GnuPG. ::
$ gpg --armor --output gniibe.asc --export 4CA7BABE
$ gpg --armor --output <YOUR-KEY>.asc --export <YOUR-KEY-ID>
and put it at: http://www.gniibe.org/gniibe.asc
We can publish the file by web server. Or we can publish the key
to a keyserver, by invoking GnuPG with ``--send-keys`` option. ::
$ gpg --keyserver pool.sks-keyservers.net --send-keys <YOUR-KEY-ID>
Here, pool.sks-keyservers.net is a keyserver, which is widely used.
Backup the private key
======================
There are some ways to back up private key, such that backup .gnupg
directory entirely, use of paperkey. Here we describe backup by ASCII
file. ASCII file is good, because it has less risk on transfer.
Binary file has a risk to be modified on transfer.
Note that the key on host PC is protected by passphrase (which
is <PASSWORD-KEY-ON-PC> in the example above). Using the key
from the backup needs this passphrase. It is common that
people will forget passphrase for backup. Never forget it.
You have been warned.
To make ASCII backup for private key,
invokde GnuPG with ``--armor`` option and ``--export-secret-keys``
specifying the key identifier. ::
$ gpg --armor --output <YOUR-SECRET>.asc --export-secret-keys <YOUR-KEY-ID>
From the backup,
we can recover privet key by invoking GnuPG with ``--import`` option. ::
$ gpg --import <YOUR-SECRET>.asc

View File

@@ -4,7 +4,8 @@ GnuPG settings for GNOME 3
In the article `GnuPG settings`_, I wrote how I disable GNOME-keyrings for SSH.
It was for GNOME 2. The old days was good, we just disabled GNOME-keyrings interference to SSH and customizing our desktop was easy for GNU and UNIX users.
It was for GNOME 2. The old days was good, we just disabled GNOME-keyrings
interference to SSH and customizing our desktop was easy for GNU and UNIX users.
.. _GnuPG settings: gpg-settings
@@ -12,19 +13,26 @@ It was for GNOME 2. The old days was good, we just disabled GNOME-keyrings inte
GNOME keyrings in GNOME 3
=========================
It seems that it is more integrated into the desktop. It is difficult to kill it. It would be possible to kill it simply, but then, I can't use, say, wi-fi access (which needs to access "secrets") any more.
It seems that it is more integrated into the desktop.
It is difficult to kill it. It would be possible to kill it simply,
but then, I can't use, say, wi-fi access (which needs to access "secrets")
any more.
We can't use GNOME configuration tool to disable interference by GNOME keyrings any more. It seems that desktop should not have customization these days.
We can't use GNOME configuration tool to disable interference by
GNOME keyrings any more. It seems that desktop should not have
customization these days.
GNOME-SESSION-PROPERTIES
========================
After struggling some ours, I figured out it is GNOME-SESSION-PROPERTIES to disable the interference. Invoking::
After struggling some hours, I figured out it is GNOME-SESSION-PROPERTIES
to disable the interference. Invoking::
$ gnome-session-properties
and at the tab of "Startup Programs", I removed radio check buttons for "GPG Password Agent" and "SSH Key Agent".
and at the tab of "Startup Programs", I removed radio check buttons
for "GPG Password Agent" and "SSH Key Agent".
Now, I use gpg-agent for GnuPG Agent and SSH agent with Gnuk Token.

View File

@@ -2,17 +2,17 @@
Key import from PC to Gnuk Token (no removal)
=============================================
This document describes how I put my **keys on PC** to the Token without removing keys from PC.
This document describes how I put my **keys on PC** to the Token
without removing keys from PC.
The difference is just not-to-save changes after key imports.
.. BREAK
After personalization, I put my keys into the Token.
Here is the log.
I invoke GnuPG with my key (4ca7babe) and with ``--homedir`` option to specify the directory which contains my secret keys. ::
I invoke GnuPG with my key (4ca7babe) and with ``--homedir`` option
to specify the directory which contains my secret keys. ::
$ gpg --homedir=/home/gniibe/tmp/gnuk-testing-dir --edit-key 4ca7babe
gpg (GnuPG) 1.4.11; Copyright (C) 2010 Free Software Foundation, Inc.
@@ -54,7 +54,10 @@ and type ``1`` to say it's signature key. ::
(3) Authentication key
Your selection? 1
Then, GnuPG asks two passwords. One is the passphrase of **keys on PC** and another is the password of **Gnuk Token**. Note that the password of the token and the password of the keys on PC are different things, although they can be same.
Then, GnuPG asks two passwords. One is the passphrase of **keys on PC**
and another is the password of **Gnuk Token**. Note that the password of
the token and the password of the keys on PC are different things,
although they can be same.
I enter these passwords. ::
@@ -74,7 +77,8 @@ I enter these passwords. ::
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
(1) NIIBE Yutaka <gniibe@fsij.org>
The primary key is now on the Token and GnuPG says its card-no (F517 00000001) , where F517 is the vendor ID of FSIJ.
The primary key is now on the Token and GnuPG says its card-no (F517 00000001),
where F517 is the vendor ID of FSIJ.
Secondly, I import my subkey of encryption. I select key number '1'. ::
@@ -87,7 +91,8 @@ Secondly, I import my subkey of encryption. I select key number '1'. ::
(1) NIIBE Yutaka <gniibe@fsij.org>
You can see that the subkey is marked by '*'.
I type ``keytocard`` command to import this subkey to Gnuk Token. I select ``2`` as it's encryption key. ::
I type ``keytocard`` command to import this subkey to Gnuk Token.
I select ``2`` as it's encryption key. ::
gpg> keytocard
Signature key ....: [none]
@@ -126,7 +131,7 @@ I type ``key 1`` to deselect key number '1'. ::
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
(1) NIIBE Yutaka <gniibe@fsij.org>
Thirdly, I select sub key of suthentication which has key number '2'. ::
Thirdly, I select sub key of authentication which has key number '2'. ::
gpg> key 2
@@ -138,7 +143,8 @@ Thirdly, I select sub key of suthentication which has key number '2'. ::
(1) NIIBE Yutaka <gniibe@fsij.org>
You can see that the subkey number '2' is marked by '*'.
I type ``keytocard`` command to import this subkey to Gnuk Token. I select ``3`` as it's authentication key. ::
I type ``keytocard`` command to import this subkey to Gnuk Token.
I select ``3`` as it's authentication key. ::
gpg> keytocard
Signature key ....: [none]

View File

@@ -2,13 +2,17 @@
Key import from PC to Gnuk Token
================================
This document describes how I put my **keys on PC** to the Token, and remove keys from PC.
This document describes how I put my **keys on PC** to the Token,
and remove keys from PC.
Note that there is **no ways** to export keys from the Token, so please be careful.
Note that there is **no ways** to export keys from the Token,
so please be careful.
.. BREAK
If you want to import same keys to multiple Tokens, please copy ``.gnupg`` directory before. In my case, I do something like following: ::
If you want to import same keys to multiple Tokens,
please copy ``.gnupg`` directory beforehand.
In my case, I do something like following: ::
$ cp -a .gnupg tmp/gnuk-testing-dir
@@ -62,7 +66,10 @@ and type ``1`` to say it's signature key. ::
(3) Authentication key
Your selection? 1
Then, GnuPG asks two passwords. One is the passphrase of **keys on PC** and another is the password of **Gnuk Token**. Note that the password of the token and the password of the keys on PC are different things, although they can be same.
Then, GnuPG asks two passwords. One is the passphrase of **keys on PC**
and another is the password of **Gnuk Token**. Note that the password of
the token and the password of the keys on PC are different things,
although they can be same.
I enter these passwords. ::
@@ -95,7 +102,8 @@ Secondly, I import my subkey of encryption. I select key number '1'. ::
(1) NIIBE Yutaka <gniibe@fsij.org>
You can see that the subkey is marked by '*'.
I type ``keytocard`` command to import this subkey to Gnuk Token. I select ``2`` as it's encryption key. ::
I type ``keytocard`` command to import this subkey to Gnuk Token.
I select ``2`` as it's encryption key. ::
gpg> keytocard
Signature key ....: [none]
@@ -134,7 +142,7 @@ I type ``key 1`` to deselect key number '1'. ::
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
(1) NIIBE Yutaka <gniibe@fsij.org>
Thirdly, I select sub key of suthentication which has key number '2'. ::
Thirdly, I select sub key of authentication which has key number '2'. ::
gpg> key 2
@@ -146,7 +154,8 @@ Thirdly, I select sub key of suthentication which has key number '2'. ::
(1) NIIBE Yutaka <gniibe@fsij.org>
You can see that the subkey number '2' is marked by '*'.
I type ``keytocard`` command to import this subkey to Gnuk Token. I select ``3`` as it's authentication key. ::
I type ``keytocard`` command to import this subkey to Gnuk Token.
I select ``3`` as it's authentication key. ::
gpg> keytocard
Signature key ....: [none]
@@ -180,4 +189,5 @@ Lastly, I save changes of **keys on PC** and quit GnuPG. ::
gpg> save
$
All secret keys are imported to Gnuk Token now. On PC, only references (card-no) to the Token remain.
All secret keys are imported to Gnuk Token now.
On PC, only references (card-no) to the Token remain.

View File

@@ -33,7 +33,27 @@ It shows the status of the card (as same as the output of ``gpg --card-status``)
Then, GnuPG enters its own command interaction mode. The prompt is ``gpg/card>``.
Firstly, I change PIN of card user from factory setting (of "123456"). Note that, only changing PIN of user enables "admin less mode" of Gnuk. Admin password will become same one of user's. ::
In the OpenPGPcard specification, there are two passwords: one is
user-password and another is admin-password. In the specification,
user-password is refered as PW1, and admin-password is refered as PW3.
Note that people sometimes use different words than "password" to
refer same thing, in GnuPG and its applications. For example, the
output explained above includes the word "PIN" (Personal
Identification Number), and the helper program for input is named
"pinentry". Note that it is OK (and recommended) to include
characters other than digits for the case of OpenPGPcard.
Besides, some people sometimes prefer the word "passphrase" to
"password", as it can encourage to have longer string, but it means
same thing and it just refer user-password or admin-password.
Firstly, I change PIN of card user from factory setting (of "123456").
Note that, by only changing user's PIN, it enables "admin less mode" of Gnuk.
"Admin less mode" means that admin password will become same one of user's.
That is, PW1 = PW3.
Note that *the length of PIN should be more than (or equals to) 8* for
"admin less mode". ::
gpg/card> passwd
gpg: OpenPGP card no. D276000124010200F517000000010000 detected
@@ -48,7 +68,20 @@ Firstly, I change PIN of card user from factory setting (of "123456"). Note tha
Repeat this PIN: <PASSWORD-OF-GNUK>
PIN changed.
Secondly, enabling admin command, I put name of mine. Note that I input user's PIN (which I set above) here, because it is "admin less mode". ::
The "admin less mode" is Gnuk only feature, not defined in the
OpenPGPcard specification. By using "admin less mode", it will be
only a sigle password for user to memorize, and it will be easier if a token
is used by an individual.
(If you want normal way ("admin full mode" in Gnuk's term),
that is, user-password *and* admin-password independently,
please change admin-password at first.
Then, the token works as same as OpenPGPcard specification
with regards to PW1 and PW3.)
Secondly, enabling admin command, I put name of mine.
Note that I input user's PIN (which I set above) here,
because it is "admin less mode". ::
gpg/card> admin
Admin commands are allowed
@@ -61,7 +94,8 @@ Secondly, enabling admin command, I put name of mine. Note that I input user's
Please enter the Admin PIN
Enter Admin PIN: <PASSWORD-OF-GNUK>
Thirdly, I put some other informations, such as language, sex, login, and URL. URL specifies the place where I put my public keys. ::
Thirdly, I put some other informations, such as language, sex,
login, and URL. URL specifies the place where I put my public keys. ::
gpg/card> lang
Language preferences: ja
@@ -75,7 +109,8 @@ Thirdly, I put some other informations, such as language, sex, login, and URL.
gpg/card> login
Login data (account name): gniibe
Since I don't force PIN input everytime, toggle it to non-force-pin-for-signature. ::
Since I don't force PIN input everytime,
toggle it to non-force-pin-for-signature. ::
gpg/card> forcesig

View File

@@ -2,6 +2,12 @@
Initial Configuration of Gnuk Token
===================================
This is optional. You don't need to setup the serial number of Gnuk Token,
as it comes with its default serial number based on MCU's chip ID.
You can setup the serial number of Gnuk Token only once.
Conditions
==========
@@ -11,7 +17,7 @@ I assume you are using GNU/Linux.
Preparation
===========
We need to kill ``scdaemon`` before configuring Gnuk Token. ::
Make sure there is no ``scdaemon`` for configuring Gnuk Token. You can kill ``scdaemon`` by: ::
$ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye
@@ -19,16 +25,19 @@ We need to kill ``scdaemon`` before configuring Gnuk Token. ::
Serial Number (optional)
========================
In the file ``GNUK_SERIAL_NUMBER``, each line has email and 6-byte serial number.
In the file ``GNUK_SERIAL_NUMBER``, each line has email and 6-byte serial number. The first two bytes are organization number (F5:17 is for FSIJ). Last four bytes are number for tokens.
The tool ``../tool/gnuk_put_binary.py`` examines environment variable of ``EMAIL``, and writes serial number to Gnuk Token. ::
The tool ``../tool/gnuk_put_binary_libusb.py`` examines environment variable of ``EMAIL``, and writes corresponding serial number to Gnuk Token. ::
$ ../tool/gnuk_put_binary.py -s ../GNUK_SERIAL_NUMBER
$ ../tool/gnuk_put_binary_libusb.py -s ../GNUK_SERIAL_NUMBER
Writing serial number
Token: FSIJ Gnuk (0.12-38FF6A06) 00 00
ATR: 3B DA 11 FF 81 B1 FE 55 1F 03 00 31 84 73 80 01 40 00 90 00 24
Device: 006
Configuration: 1
Interface: 0
d2 76 00 01 24 01 02 00 f5 17 00 00 00 01 00 00
The tool ``../tool/gnuk_put_binary.py`` is for PC/SC Lite. Use
``../tool/gnuk_put_binary_libusb.py`` instead, if you don't use
PC/SC Lite but use libusb directly.
The example above is the case of libusb version.
Use the tool ``../tool/gnuk_put_binary.py`` instead , for PC/SC Lite.
You need PyScard for this.

View File

@@ -22,10 +22,12 @@ I create ``.gnupg/gpg.conf`` file with the following content. ::
Let gpg-agent manage SSH key
============================
I deactivate seahose-agent. Also, I deactivate gnome-keyring managing SSH key. ::
I deactivate seahose-agent. Also, for GNOME 2, I deactivate gnome-keyring managing SSH key. ::
$ gconftool-2 --type bool --set /apps/gnome-keyring/daemon-components/ssh false
I edit the file /etc/X11/Xsession.options and comment out use-ssh-agent line.
Then, I create ``.gnupg/gpg-agent.conf`` file with the following content. ::
enable-ssh-support

View File

@@ -18,9 +18,9 @@ Contents:
development.rst
stop-scdaemon.rst
udev-rules.rst
generating-2048-RSA-key.rst
gnuk-token-initial-configuration.rst
gnuk-personalization.rst
generating-2048-RSA-key.rst
gnuk-keytocard.rst
gnuk-keytocard-noremoval.rst
using-gnuk-token-with-another-computer.rst

View File

@@ -38,14 +38,12 @@ We can examine key information of gpg-agent by "KEYINFO" command.
Here is my example::
$ gpg-connect-agent "KEYINFO --list" /bye
S KEYINFO 4970A0D537CA2EF7CE6A106E47AD89B0EFB684C8 D - - - - -
S KEYINFO 65F67E742101C7FE6D5B33FCEFCF4F65EAF0688C T D276000124010200F517000000010000 OPENPGP.2 - - -
S KEYINFO 5D6C89682D07CCFC034AF508420BF2276D8018ED T D276000124010200F517000000010000 OPENPGP.3 - - -
S KEYINFO 7D180C0C2A991B25204110A92F5F92A5A509845B D - - - - -
S KEYINFO 101DE7B639FE29F4636BDEECF442A9273AFA6565 T D276000124010200F517000000010000 OPENPGP.1 - - -
S KEYINFO 5D6C89682D07CCFC034AF508420BF2276D8018ED T D276000124010200F517000000010000 OPENPGP.3 - - -
OK
I have two local keys (in my PC) and three keys in my token.
I have three keys in my token.
With the script below, I extract public key of the keygrip
5D6C89682D07CCFC034AF508420BF2276D8018ED into the file: 5D6C8968.bin::
@@ -63,7 +61,7 @@ Here is the script, get_public_key.py::
result = check_output(["gpg-connect-agent", "READKEY %s" % keygrip, "/bye"])
key = ""
while True:
i = result.find('%')
i = result.find('%')
if i < 0:
key += result
break

View File

@@ -6,8 +6,8 @@ There is a daemon named ``scdaemon`` behind gpg-agent, which handles
communication to smartcard/token.
Ideally, we don't need to care about ``scdaemon``, and it should
everything automatically. But, there are some cases (because of
bugs), where we need to talk to the daemon directly, in practice.
handle everything automatically. But, there are some cases (because
of bugs), where we need to talk to the daemon directly, in practice.
How to communicate SCDAEMON

View File

@@ -7,11 +7,13 @@ needed for permissions. Note that this is not needed for the case of
PC/SC Lite, as it has its own device configuration.
Patching 60-gnupg.rules
=======================
udev rules for Gnuk Token
=========================
In case of Debian, there is a file /lib/udev/rules.d/60-gnupg.rules.
This would be the place we need to change::
In case of Debian, there is a file /lib/udev/rules.d/60-gnupg.rules,
when you install "gnupg" package. This is the place we need to change.
We add lines for Gnuk Token to give a desktop user the permission to
use the device. We specify USB ID of Gnuk Token (by FSIJ)::
--- /lib/udev/rules.d/60-gnupg.rules.orig 2012-06-24 21:51:26.000000000 +0900
+++ /lib/udev/rules.d/60-gnupg.rules 2012-07-13 17:18:55.149587687 +0900
@@ -24,25 +26,26 @@ This would be the place we need to change::
+
LABEL="gnupg_rules_end"
When we install "gnupg2" package only (with no "gnupg" package),
there will be no udev rules (there is a bug report #543217 for this issue).
In this case, we need something like this in /etc/udev/rules.d/60-gnuk.rules::
SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \
ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"
Usually, udev daemon automatically handles for the changes of configuration
files. If not, please let the daemon reload rules::
# udevadm control --reload-rules
Have a another configuration for reGNUal
========================================
For reGNUal (upgrade feature of Gnuk),
I also have a file /etc/udev/rules.d/92-gnuk.rules::
# For updating firmware, permission settings are needed.
SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000", \
ENV{ID_USB_INTERFACES}=="*:ff0000:*", GROUP="pcscd"
Configuration for ST-Link/V2
============================
udev rules for ST-Link/V2
=========================
This is for development, but I also have a file
/etc/udev/rules.d/10-stlink.rules::
We need to have a udev rule for ST-Link/V2. It's like::
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", GROUP="tape", MODE="664", SYMLINK+="stlink"
I have this in the file /etc/udev/rules.d/10-stlink.rules.

View File

@@ -2,16 +2,18 @@
Using Gnuk Token with another computer
======================================
This document describes how you can use Gnuk Token on another PC (which is not the one you generate your keys).
This document describes how you can use Gnuk Token
on another PC (which is not the one you generate your keys).
Note that the Token only brings your secret keys, while ``.gnupg`` directory contains keyrings and trustdb, too.
Note that the Token only brings your secret keys,
while ``.gnupg`` directory contains keyrings and trustdb, too.
.. BREAK
Fetch the public key and connect it to the Token
================================================
Using the Token, we need to put the public key and the secret key reference (to the token) in ``.gnupg``.
Using the Token, we need to put the public key and the secret
key reference (to the token) in ``.gnupg``.
To do that, invoke GnuPG with ``--card-edit`` option. ::
@@ -58,7 +60,9 @@ Good. The public key is now in ``.gnupg``. We can examine by ``gpg --list-keys
However, the secret key reference (to the token) is not in ``.gnupg`` yet.
It will be generated when I do ``--card-status`` by GnuPG with correspoinding public key in ``.gnupg``, or just type return at the ``gpg/card>`` prompt. ::
It will be generated when I do ``--card-status`` by GnuPG with
correspoinding public key in ``.gnupg``, or just type return
at the ``gpg/card>`` prompt. ::
gpg/card>
@@ -99,7 +103,8 @@ OK, now I can use the Token on this computer.
Update trustdb for the key on Gnuk Token
========================================
Yes, I can use the Token by the public key and the secret key reference to the card. More, I need to update the trustdb.
Yes, I can use the Token by the public key and the secret
key reference to the card. More, I need to update the trustdb.
To do that I do: ::

View File

@@ -22,6 +22,7 @@ MCFLAGS= -mcpu=$(MCU) -mfix-cortex-m3-ldrd
DEFS = -DFREE_STANDING
CFLAGS = -O2 -g
CFLAGS += -Wa,-alms=$(notdir $(<:.c=.lst)) -fpie
CFLAGS += $(CWARN) -I . -I ../src -fno-common $(MCFLAGS) $(TOPT) $(DEFS)
LDFLAGS = -T$(LDSCRIPT) -nostartfiles $(MCFLAGS) $(TOPT)

View File

@@ -1,7 +1,7 @@
/*
* regnual.c -- Firmware installation for STM32F103 Flash ROM
*
* Copyright (C) 2012 Free Software Initiative of Japan
* Copyright (C) 2012, 2013 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -99,33 +99,15 @@ static const uint8_t regnual_string_serial[] = {
'0', 0, '.', 0, '0', 0,
};
const struct Descriptor device_desc = {
regnual_device_desc,
sizeof (regnual_device_desc)
};
const struct Descriptor config_desc = {
regnual_config_desc,
sizeof (regnual_config_desc)
};
const struct Descriptor string_descs[] = {
{regnual_string_lang_id, sizeof (regnual_string_lang_id)},
{gnukStringVendor, sizeof (gnukStringVendor)},
{gnukStringProduct, sizeof (gnukStringProduct)},
{regnual_string_serial, sizeof (regnual_string_serial)},
};
#define NUM_STRING_DESC (sizeof (string_descs)/sizeof (struct Descriptor))
static void
regnual_device_reset (void)
void
usb_cb_device_reset (void)
{
/* Set DEVICE as not configured */
usb_lld_set_configuration (0);
/* Current Feature initialization */
usb_lld_set_feature (config_desc.Descriptor[7]);
usb_lld_set_feature (regnual_config_desc[7]);
usb_lld_reset ();
@@ -141,11 +123,9 @@ regnual_device_reset (void)
#define USB_REGNUAL_PROTECT 4
#define USB_REGNUAL_FINISH 5
static uint8_t mem[256];
static uint32_t mem[256/4];
static uint32_t result;
static const uint8_t *const mem_info[] = { &_flash_start, &_flash_end, };
static uint32_t rbit (uint32_t v)
{
@@ -159,7 +139,7 @@ static uint32_t fetch (int i)
{
uint32_t v;
v = *(uint32_t *)(&mem[i*4]);
v = mem[i];
return rbit (v);
}
@@ -186,9 +166,8 @@ static uint32_t calc_crc32 (void)
}
static void regnual_ctrl_write_finish (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index,
uint16_t len)
void usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, uint16_t value,
uint16_t index, uint16_t len)
{
uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
@@ -200,7 +179,7 @@ static void regnual_ctrl_write_finish (uint8_t req, uint8_t req_no,
{
uint32_t dst_addr = (0x08000000 + value * 0x100);
result = flash_write (dst_addr, mem, 256);
result = flash_write (dst_addr, (const uint8_t *)mem, 256);
}
else if (req_no == USB_REGNUAL_PROTECT && len == 0
&& value == 0 && index == 0)
@@ -211,9 +190,9 @@ static void regnual_ctrl_write_finish (uint8_t req, uint8_t req_no,
}
}
static int
regnual_setup (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len)
int
usb_cb_setup (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len)
{
uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
@@ -223,6 +202,10 @@ regnual_setup (uint8_t req, uint8_t req_no,
{
if (req_no == USB_REGNUAL_MEMINFO)
{
static const uint8_t *mem_info[2];
mem_info[0] = &_flash_start;
mem_info[1] = &_flash_end;
usb_lld_set_data_to_send (mem_info, sizeof (mem_info));
return USB_SUCCESS;
}
@@ -240,7 +223,8 @@ regnual_setup (uint8_t req, uint8_t req_no,
return USB_UNSUPPORT;
if (index + len < 256)
memset (mem + index + len, 0xff, 256 - (index + len));
memset ((uint8_t *)mem + index + len, 0xff,
256 - (index + len));
usb_lld_set_data_to_recv (mem + index, len);
return USB_SUCCESS;
@@ -264,38 +248,55 @@ regnual_setup (uint8_t req, uint8_t req_no,
return USB_UNSUPPORT;
}
static int
regnual_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value)
int
usb_cb_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value)
{
(void)index;
if (desc_type == DEVICE_DESCRIPTOR)
{
usb_lld_set_data_to_send (device_desc.Descriptor,
device_desc.Descriptor_Size);
usb_lld_set_data_to_send (regnual_device_desc,
sizeof (regnual_device_desc));
return USB_SUCCESS;
}
else if (desc_type == CONFIG_DESCRIPTOR)
{
usb_lld_set_data_to_send (config_desc.Descriptor,
config_desc.Descriptor_Size);
usb_lld_set_data_to_send (regnual_config_desc,
sizeof (regnual_config_desc));
return USB_SUCCESS;
}
else if (desc_type == STRING_DESCRIPTOR)
{
uint8_t desc_index = value & 0xff;
const uint8_t *str;
int size;
if (desc_index < NUM_STRING_DESC)
switch (desc_index)
{
usb_lld_set_data_to_send (string_descs[desc_index].Descriptor,
string_descs[desc_index].Descriptor_Size);
return USB_SUCCESS;
case 0:
str = regnual_string_lang_id;
size = sizeof (regnual_string_lang_id);
case 1:
str = gnukStringVendor;
size = sizeof (gnukStringVendor);
case 2:
str = gnukStringProduct;
size = sizeof (gnukStringProduct);
case 3:
str = regnual_string_serial;
size = sizeof (regnual_string_serial);
break;
default:
return USB_UNSUPPORT;
}
usb_lld_set_data_to_send (str, size);
return USB_SUCCESS;
}
return USB_UNSUPPORT;
}
static int regnual_usb_event (uint8_t event_type, uint16_t value)
int usb_cb_handle_event (uint8_t event_type, uint16_t value)
{
(void)value;
@@ -311,20 +312,12 @@ static int regnual_usb_event (uint8_t event_type, uint16_t value)
return USB_UNSUPPORT;
}
static int regnual_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
int usb_cb_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
{
(void)cmd; (void)interface; (void)alt;
return USB_UNSUPPORT;
}
const struct usb_device_method Device_Method = {
regnual_device_reset,
regnual_ctrl_write_finish,
regnual_setup,
regnual_get_descriptor,
regnual_usb_event,
regnual_interface,
};
static void wait (int count)
{

View File

@@ -39,6 +39,7 @@ SECTIONS
{
_text = .;
KEEP(*(.vectors))
*(.text.entry)
*(.text)
*(.text.*)
*(.rodata)
@@ -48,6 +49,12 @@ SECTIONS
*(.gcc*)
} > ram1
.got :
{
*(.got)
*(.got.*)
} > ram1
.ctors :
{
PROVIDE(_ctors_start_ = .);

View File

@@ -9,17 +9,41 @@ static void none (void)
{
}
/* Note: it is not reset */
static __attribute__ ((naked))
/*
* Note: the address of this routine 'entry' will be in the vectors as
* RESET, but this will be called from application. It's not RESET
* state, then.
*/
static __attribute__ ((naked,section(".text.entry")))
void entry (void)
{
asm volatile ("ldr r0, =__ram_end__\n\t"
"ldr r1, =__main_stack_size__\n\t"
asm volatile ("mov r0, pc\n\t"
"bic r0, r0, #255\n\t" /* R0 := vector_table address */
"mov r1, #0x90\n" /* R1 := numbers of entries * 4 */
"0:\n\t"
"ldr r2, [r0, r1]\n\t"
"add r2, r2, #-0x20000000\n\t"
"sub r2, r2, #0x1400\n\t"
"add r2, r2, r0\n\t" /* Relocate: -0x20001400 + R0 */
"str r2, [r0, r1]\n\t"
"subs r1, r1, #4\n\t"
"bne 0b\n\t"
/* Relocation done. We don't care the first entry. */
"ldr r3, .L00\n"
".LPIC00:\n\t"
"add r3, pc\n\t" /* R3 := @_GLOBAL_OFFSET_TABLE_ */
"ldr r4, .L00+4\n\t"
"ldr r0, [r3, r4]\n\t"
"ldr r4, .L00+8\n\t"
"ldr r1, [r3, r4]\n\t"
"sub r0, r0, r1\n\t"
"mov sp, r0\n\t"
/* Clear BSS. */
"mov r0, #0\n\t"
"ldr r1, =_bss_start\n\t"
"ldr r2, =_bss_end\n"
"ldr r4, .L00+12\n\t"
"ldr r1, [r3, r4]\n\t"
"ldr r4, .L00+16\n\t"
"ldr r2, [r3, r4]\n"
"0:\n\t"
"str r0, [r1], #4\n\t"
"cmp r2, r1\n\t"
@@ -30,6 +54,12 @@ void entry (void)
"bl main\n"
"1:\n\t"
"b 1b\n"
".L00:\n\t"
".word _GLOBAL_OFFSET_TABLE_-(.LPIC00+4)\n\t"
".word __ram_end__(GOT)\n\t"
".word __main_stack_size__(GOT)\n\t"
".word _bss_start(GOT)\n\t"
".word _bss_end(GOT)"
: /* no output */ : /* no input */ : "memory");
}

View File

@@ -11,7 +11,7 @@ BOARD_DIR=@BOARD_DIR@
# Compiler options here.
ifeq ($(USE_OPT),)
USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
USE_OPT = -O3 -Os -ggdb -fomit-frame-pointer -falign-functions=16
endif
# C++ specific options here (added to USE_OPT).

2
src/configure vendored
View File

@@ -84,7 +84,7 @@ Configuration:
-h, --help display this help and exit [no]
--vidpid=VID:PID specify vendor/product ID [<NONE>]
--target=TARGET specify target [OLIMEX_STM32_H103]
supported targes are:
supported targets are:
OLIMEX_STM32_H103
STM32_PRIMER2
CQ_STARM

View File

@@ -1,7 +1,8 @@
/*
* flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM
*
* Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
* Copyright (C) 2010, 2011, 2012, 2013
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -516,6 +517,12 @@ flash_write_binary (uint8_t file_id, const uint8_t *data,
{
maxsize = KEY_CONTENT_LEN;
p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0);
if (len == 0 && offset == 0)
{ /* This means removal of update key. */
if (flash_program_halfword ((uint32_t)p, 0) != 0)
flash_warning ("DO WRITE ERROR");
return 0;
}
}
#if defined(CERTDO_SUPPORT)
else if (file_id == FILEID_CH_CERTIFICATE)

View File

@@ -94,6 +94,7 @@ extern volatile uint8_t auth_status;
#define PW_ERR_PW1 0
#define PW_ERR_RC 1
#define PW_ERR_PW3 2
extern int gpg_pw_get_retry_counter (int who);
extern int gpg_pw_locked (uint8_t which);
extern void gpg_pw_reset_err_counter (uint8_t which);
extern void gpg_pw_increment_err_counter (uint8_t which);
@@ -115,6 +116,8 @@ extern void ac_fini (void);
extern void set_res_sw (uint8_t sw1, uint8_t sw2);
extern uint16_t data_objects_number_of_bytes;
#define CHALLENGE_LEN 32
extern void gpg_data_scan (const uint8_t *p);
extern void gpg_data_copy (const uint8_t *p);
extern void gpg_do_get_data (uint16_t tag, int with_tag);
@@ -168,8 +171,8 @@ struct key_data {
};
struct key_data_internal {
uint8_t data[KEY_CONTENT_LEN]; /* p and q */
uint8_t checksum[DATA_ENCRYPTION_KEY_SIZE];
uint32_t data[KEY_CONTENT_LEN/4]; /* p and q */
uint32_t checksum[DATA_ENCRYPTION_KEY_SIZE/4];
};
struct prvkey_data {

View File

@@ -1,7 +1,7 @@
/*
* main.c - main routine of Gnuk
*
* Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
* Copyright (C) 2010, 2011, 2012, 2013 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -178,6 +178,7 @@ extern msg_t USBthread (void *arg);
#define LED_TIMEOUT_STOP MS2ST(200)
/* It has two-byte prefix and content is "FSIJ-1.0.1-" (2 + 11*2). */
#define ID_OFFSET 24
static void
device_initialize_once (void)
@@ -318,6 +319,23 @@ led_blink (int spec)
chEvtSignal (main_thread, spec);
}
/*
* In Gnuk 1.0.[12], reGNUal was not relocatable.
* Now, it's relocatable, but we need to calculate its entry address
* based on it's pre-defined address.
*/
#define REGNUAL_START_ADDRESS_COMPATIBLE 0x20001400
static uint32_t
calculate_regnual_entry_address (const uint8_t *addr)
{
const uint8_t *p = addr + 4;
uint32_t v = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
v -= REGNUAL_START_ADDRESS_COMPATIBLE;
v += (uint32_t)addr;
return v;
}
/*
* Entry point.
@@ -329,6 +347,7 @@ int
main (int argc, char *argv[])
{
unsigned int count = 0;
uint32_t entry;
(void)argc;
(void)argv;
@@ -337,7 +356,7 @@ main (int argc, char *argv[])
flash_unlock ();
device_initialize_once ();
usb_lld_init (Config_Descriptor.Descriptor[7]);
usb_lld_init (usb_initial_feature);
random_init ();
while (1)
@@ -428,6 +447,7 @@ main (int argc, char *argv[])
port_disable ();
/* Set vector */
SCB->VTOR = (uint32_t)&_regnual_start;
entry = calculate_regnual_entry_address (&_regnual_start);
#ifdef DFU_SUPPORT
#define FLASH_SYS_START_ADDR 0x08000000
#define FLASH_SYS_END_ADDR (0x08000000+0x1000)
@@ -446,12 +466,12 @@ main (int argc, char *argv[])
flash_write (FLASH_SYS_START_ADDR, &_sys, 0x1000);
/* Leave Gnuk to exec reGNUal */
(*func) (*((void (**)(void))(&_regnual_start+4)));
(*func) ((void (*)(void))entry);
for (;;);
}
#else
/* Leave Gnuk to exec reGNUal */
flash_erase_all_and_exec (*((void (**)(void))(&_regnual_start+4)));
flash_erase_all_and_exec ((void (*)(void))entry);
#endif
/* Never reached */

View File

@@ -1,7 +1,8 @@
/*
* openpgp-do.c -- OpenPGP card Data Objects (DO) handling
*
* Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
* Copyright (C) 2010, 2011, 2012, 2013
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -41,6 +42,17 @@ gpg_pw_get_err_counter (uint8_t which)
return flash_cnt123_get_value (pw_err_counter_p[which]);
}
int
gpg_pw_get_retry_counter (int who)
{
if (who == 0x81 || who == 0x82)
return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW1);
else if (who == 0x83)
return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW3);
else
return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_RC);
}
int
gpg_pw_locked (uint8_t which)
{
@@ -88,16 +100,16 @@ static const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = {
/* Extended Capabilities */
static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = {
10,
0x30, /*
0x70, /*
* No SM,
* No get challenge,
* GET CHALLENGE supported,
* Key import supported,
* PW status byte can be put,
* No private_use_DO,
* No algo change allowed
*/
0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */
0x00, 0x00, /* Max get challenge (0: Get challenge not supported) */
0x00, CHALLENGE_LEN, /* Max size of GET CHALLENGE */
#ifdef CERTDO_SUPPORT
0x08, 0x00, /* max. length of cardholder certificate (2KiB) */
#else
@@ -229,7 +241,7 @@ static const struct do_table_entry *get_do_entry (uint16_t tag);
#define GPG_DO_LOGIN_DATA 0x005e
#define GPG_DO_CH_DATA 0x0065
#define GPG_DO_APP_DATA 0x006e
/* XXX: 0x0073 ??? */
#define GPG_DO_DISCRETIONARY 0x0073
#define GPG_DO_SS_TEMP 0x007a
#define GPG_DO_DS_COUNT 0x0093
#define GPG_DO_EXTCAP 0x00c0
@@ -435,7 +447,7 @@ const uint8_t openpgpcard_aid[] = {
static int
do_openpgpcard_aid (uint16_t tag, int with_tag)
{
uint16_t vid = *((const volatile uint16_t *)&openpgpcard_aid[8]);
uint16_t vid = (openpgpcard_aid[8] << 8) | openpgpcard_aid[9];
if (with_tag)
{
@@ -652,7 +664,7 @@ compute_key_data_checksum (struct key_data_internal *kdi, int check_or_calc)
uint32_t d[4] = { 0, 0, 0, 0 };
for (i = 0; i < KEY_CONTENT_LEN / sizeof (uint32_t); i++)
d[i&3] ^= *(uint32_t *)(&kdi->data[i*4]);
d[i&3] ^= kdi->data[i];
if (check_or_calc == 0) /* store */
{
@@ -778,7 +790,7 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len,
encrypt (dek, iv, (uint8_t *)&kdi, sizeof (struct key_data_internal));
r = flash_key_write (key_addr, kdi.data, modulus);
r = flash_key_write (key_addr, (const uint8_t *)kdi.data, modulus);
if (modulus_allocated_here)
modulus_free (modulus);
@@ -977,10 +989,14 @@ static const uint16_t cmp_ch_data[] = {
};
static const uint16_t cmp_app_data[] = {
10,
3,
GPG_DO_AID,
GPG_DO_HIST_BYTES,
/* XXX Discretionary data objects 0x0073 ??? */
GPG_DO_DISCRETIONARY,
};
static const uint16_t cmp_discretionary[] = {
8,
GPG_DO_EXTCAP,
GPG_DO_ALG_SIG, GPG_DO_ALG_DEC, GPG_DO_ALG_AUT,
GPG_DO_PW_STATUS,
@@ -1026,6 +1042,7 @@ gpg_do_table[] = {
/* Compound data: Read access only */
{ GPG_DO_CH_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ch_data },
{ GPG_DO_APP_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_app_data },
{ GPG_DO_DISCRETIONARY, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_discretionary },
{ GPG_DO_SS_TEMP, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ss_temp },
/* Simple data: write access only */
{ GPG_DO_RESETTING_CODE, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED,
@@ -1381,8 +1398,11 @@ gpg_do_put_data (uint16_t tag, const uint8_t *data, int len)
flash_do_release (*do_data_p);
if (len == 0)
/* make DO empty */
*do_data_p = NULL;
{
/* make DO empty */
*do_data_p = NULL;
GPG_SUCCESS ();
}
else if (len > 255)
GPG_MEMORY_FAILURE ();
else

View File

@@ -1,7 +1,8 @@
/*
* openpgp.c -- OpenPGP card protocol support
*
* Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
* Copyright (C) 2010, 2011, 2012, 2013
* Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -29,6 +30,8 @@
#include "openpgp.h"
#include "sha256.h"
#define ADMIN_PASSWD_MINLEN 8
#define CLS(a) a.cmd_apdu_head[0]
#define INS(a) a.cmd_apdu_head[1]
#define P1(a) a.cmd_apdu_head[2]
@@ -50,7 +53,6 @@
#define INS_PUT_DATA 0xda
#define INS_PUT_DATA_ODD 0xdb /* For key import */
#define CHALLENGE_LEN 32
static const uint8_t *challenge; /* Random bytes */
static const uint8_t
@@ -138,6 +140,27 @@ cmd_verify (void)
len = apdu.cmd_apdu_data_len;
pw = apdu.cmd_apdu_data;
if (len == 0)
{ /* This is to examine status. */
if (p2 == 0x81)
r = ac_check_status (AC_PSO_CDS_AUTHORIZED);
else if (p2 == 0x82)
r = ac_check_status (AC_OTHER_AUTHORIZED);
else
r = ac_check_status (AC_ADMIN_AUTHORIZED);
if (r)
GPG_SUCCESS (); /* If authentication done already, return success. */
else
{ /* If not, return retry counter, encoded. */
r = gpg_pw_get_retry_counter (p2);
set_res_sw (0x63, 0xc0 | (r&0x0f));
}
return;
}
/* This is real authentication. */
if (p2 == 0x81)
r = verify_pso_cds (pw, len);
else if (p2 == 0x82)
@@ -234,7 +257,7 @@ cmd_change_password (void)
if (p1 != 0)
{
GPG_FUNCTION_NOT_SUPPORTED();
GPG_FUNCTION_NOT_SUPPORTED ();
return;
}
@@ -259,8 +282,18 @@ cmd_change_password (void)
}
else
{
const uint8_t *ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3);
newpw = pw + pw_len;
newpw_len = len - pw_len;
/* Check length of password for admin-less mode. */
if (ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN)
{
DEBUG_INFO ("new password length is too short.");
GPG_CONDITION_NOT_SATISFIED ();
return;
}
}
}
else /* PW3 (0x83) */
@@ -914,6 +947,26 @@ modify_binary (uint8_t op, uint8_t p1, uint8_t p2, int len)
return;
}
if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3
&& len == 0 && offset == 0)
{
int i;
const uint8_t *p;
for (i = 0; i < 4; i++)
{
p = gpg_get_firmware_update_key (i);
if (p[0] != 0x00 || p[1] != 0x00) /* still valid */
break;
}
if (i == 4) /* all update keys are removed */
{
p = gpg_get_firmware_update_key (0);
flash_erase_page ((uint32_t)p);
}
}
GPG_SUCCESS ();
}
@@ -935,25 +988,9 @@ static void
cmd_write_binary (void)
{
int len = apdu.cmd_apdu_data_len;
int i;
const uint8_t *p;
DEBUG_INFO (" - WRITE BINARY\r\n");
modify_binary (MBD_OPRATION_WRITE, P1 (apdu), P2 (apdu), len);
for (i = 0; i < 4; i++)
{
p = gpg_get_firmware_update_key (i);
if (p[0] != 0x00 || p[1] != 0x00) /* still valid */
break;
}
if (i == 4) /* all update keys are removed */
{
p = gpg_get_firmware_update_key (0);
flash_erase_page ((uint32_t)p);
}
DEBUG_INFO ("WRITE BINARY done.\r\n");
}
@@ -969,7 +1006,7 @@ cmd_external_authenticate (void)
DEBUG_INFO (" - EXTERNAL AUTHENTICATE\r\n");
if (keyno > 4)
if (keyno >= 4)
{
GPG_CONDITION_NOT_SATISFIED ();
return;
@@ -1002,14 +1039,25 @@ cmd_external_authenticate (void)
static void
cmd_get_challenge (void)
{
int len = apdu.expected_res_size;
DEBUG_INFO (" - GET CHALLENGE\r\n");
if (len > CHALLENGE_LEN)
{
GPG_CONDITION_NOT_SATISFIED ();
return;
}
else if (len == 0)
/* Le is not specified. Return full-sized challenge by GET_RESPONSE. */
len = CHALLENGE_LEN;
if (challenge)
random_bytes_free (challenge);
challenge = random_bytes_get ();
memcpy (res_APDU, challenge, CHALLENGE_LEN);
res_APDU_size = CHALLENGE_LEN;
memcpy (res_APDU, challenge, len);
res_APDU_size = len;
GPG_SUCCESS ();
DEBUG_INFO ("GET CHALLENGE done.\r\n");
}

View File

@@ -46,11 +46,10 @@
*/
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "sha256.h"
#define SHA256_DIGEST_SIZE 32
#define SHA256_BLOCK_SIZE 64
#define SHA256_MASK (SHA256_BLOCK_SIZE - 1)
static void bswap32_buf (uint32_t *p, int n)

View File

@@ -1,3 +1,6 @@
#define SHA256_DIGEST_SIZE 32
#define SHA256_BLOCK_SIZE 64
typedef struct
{
uint32_t total[2];

View File

@@ -321,10 +321,8 @@ static void no_buf (struct ep_in *epi, size_t len)
epi->buf_len = 0;
}
static void set_sw1sw2 (struct ep_in *epi)
static void set_sw1sw2 (struct ccid *c, size_t chunk_len)
{
struct ccid *c = (struct ccid *)epi->priv;
if (c->a->expected_res_size >= c->len)
{
c->sw1sw2[0] = 0x90;
@@ -333,10 +331,10 @@ static void set_sw1sw2 (struct ep_in *epi)
else
{
c->sw1sw2[0] = 0x61;
if (c->len >= 256)
if (c->len - chunk_len >= 256)
c->sw1sw2[1] = 0;
else
c->sw1sw2[1] = (uint8_t)c->len;
c->sw1sw2[1] = (uint8_t)(c->len - chunk_len);
}
}
@@ -827,36 +825,7 @@ icc_power_off (struct ccid *c)
}
static void
icc_send_data_block_0x9000 (struct ccid *c)
{
uint8_t p[ICC_MSG_HEADER_SIZE+2];
size_t len = 2;
p[0] = ICC_DATA_BLOCK_RET;
p[1] = len & 0xFF;
p[2] = (len >> 8)& 0xFF;
p[3] = (len >> 16)& 0xFF;
p[4] = (len >> 24)& 0xFF;
p[5] = 0x00; /* Slot */
p[ICC_MSG_SEQ_OFFSET] = c->a->seq;
p[ICC_MSG_STATUS_OFFSET] = 0;
p[ICC_MSG_ERROR_OFFSET] = 0;
p[ICC_MSG_CHAIN_OFFSET] = 0;
p[ICC_MSG_CHAIN_OFFSET+1] = 0x90;
p[ICC_MSG_CHAIN_OFFSET+2] = 0x00;
usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE + len);
c->epi->buf = NULL;
c->epi->tx_done = 1;
usb_lld_tx_enable (c->epi->ep_num, ICC_MSG_HEADER_SIZE + len);
#ifdef DEBUG_MORE
DEBUG_INFO ("DATA\r\n");
#endif
}
static void
icc_send_data_block (struct ccid *c, uint8_t status)
icc_send_data_block_internal (struct ccid *c, uint8_t status, uint8_t error)
{
int tx_size = USB_LL_BUF_SIZE;
uint8_t p[ICC_MSG_HEADER_SIZE];
@@ -875,7 +844,7 @@ icc_send_data_block (struct ccid *c, uint8_t status)
p[5] = 0x00; /* Slot */
p[ICC_MSG_SEQ_OFFSET] = c->a->seq;
p[ICC_MSG_STATUS_OFFSET] = status;
p[ICC_MSG_ERROR_OFFSET] = 0;
p[ICC_MSG_ERROR_OFFSET] = error;
p[ICC_MSG_CHAIN_OFFSET] = 0;
usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE);
@@ -933,6 +902,50 @@ icc_send_data_block (struct ccid *c, uint8_t status)
#endif
}
static void
icc_send_data_block (struct ccid *c)
{
icc_send_data_block_internal (c, 0, 0);
}
static void
icc_send_data_block_time_extension (struct ccid *c)
{
icc_send_data_block_internal (c, ICC_CMD_STATUS_TIMEEXT, 1);
}
static void
icc_send_data_block_0x9000 (struct ccid *c)
{
uint8_t p[ICC_MSG_HEADER_SIZE+2];
size_t len = 2;
p[0] = ICC_DATA_BLOCK_RET;
p[1] = len & 0xFF;
p[2] = (len >> 8)& 0xFF;
p[3] = (len >> 16)& 0xFF;
p[4] = (len >> 24)& 0xFF;
p[5] = 0x00; /* Slot */
p[ICC_MSG_SEQ_OFFSET] = c->a->seq;
p[ICC_MSG_STATUS_OFFSET] = 0;
p[ICC_MSG_ERROR_OFFSET] = 0;
p[ICC_MSG_CHAIN_OFFSET] = 0;
p[ICC_MSG_CHAIN_OFFSET+1] = 0x90;
p[ICC_MSG_CHAIN_OFFSET+2] = 0x00;
usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE + len);
c->epi->buf = NULL;
c->epi->tx_done = 1;
usb_lld_tx_enable (c->epi->ep_num, ICC_MSG_HEADER_SIZE + len);
#ifdef DEBUG_MORE
DEBUG_INFO ("DATA\r\n");
#endif
}
/*
* Reply to the host for "GET RESPONSE".
*/
static void
icc_send_data_block_gr (struct ccid *c, size_t chunk_len)
{
@@ -953,7 +966,7 @@ icc_send_data_block_gr (struct ccid *c, size_t chunk_len)
usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE);
set_sw1sw2 (c->epi);
set_sw1sw2 (c, chunk_len);
if (chunk_len <= USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE)
{
@@ -1260,7 +1273,7 @@ icc_handle_timeout (struct ccid *c)
switch (c->icc_state)
{
case ICC_STATE_EXECUTE:
icc_send_data_block (c, ICC_CMD_STATUS_TIMEEXT);
icc_send_data_block_time_extension (c);
led_blink (LED_ONESHOT);
break;
default:
@@ -1311,7 +1324,7 @@ USBthread (void *arg)
c->sw1sw2[0] = 0x90;
c->sw1sw2[1] = 0x00;
c->state = APDU_STATE_RESULT;
icc_send_data_block (c, 0);
icc_send_data_block (c);
c->icc_state = ICC_STATE_EXITED;
break;
}
@@ -1323,7 +1336,7 @@ USBthread (void *arg)
if (c->a->res_apdu_data_len <= c->a->expected_res_size)
{
c->state = APDU_STATE_RESULT;
icc_send_data_block (c, 0);
icc_send_data_block (c);
c->icc_state = ICC_STATE_WAIT;
}
else

View File

@@ -1,7 +1,7 @@
/*
* usb_ctrl.c - USB control pipe device specific code for Gnuk
*
* Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
* Copyright (C) 2010, 2011, 2012, 2013 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -147,8 +147,8 @@ gnuk_setup_endpoints_for_interface (uint16_t interface, int stop)
#endif
}
static void
gnuk_device_reset (void)
void
usb_cb_device_reset (void)
{
int i;
@@ -156,7 +156,7 @@ gnuk_device_reset (void)
usb_lld_set_configuration (0);
/* Current Feature initialization */
usb_lld_set_feature (Config_Descriptor.Descriptor[7]);
usb_lld_set_feature (usb_initial_feature);
usb_lld_reset ();
@@ -214,9 +214,9 @@ static int download_check_crc32 (const uint32_t *end_p)
return USB_UNSUPPORT;
}
static int
gnuk_setup (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len)
int
usb_cb_setup (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len)
{
uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
@@ -310,9 +310,8 @@ gnuk_setup (uint8_t req, uint8_t req_no,
return USB_UNSUPPORT;
}
static void gnuk_ctrl_write_finish (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index,
uint16_t len)
void usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, uint16_t value,
uint16_t index, uint16_t len)
{
uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
@@ -329,38 +328,7 @@ static void gnuk_ctrl_write_finish (uint8_t req, uint8_t req_no,
}
static int
gnuk_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value)
{
(void)index;
if (desc_type == DEVICE_DESCRIPTOR)
{
usb_lld_set_data_to_send (Device_Descriptor.Descriptor,
Device_Descriptor.Descriptor_Size);
return USB_SUCCESS;
}
else if (desc_type == CONFIG_DESCRIPTOR)
{
usb_lld_set_data_to_send (Config_Descriptor.Descriptor,
Config_Descriptor.Descriptor_Size);
return USB_SUCCESS;
}
else if (desc_type == STRING_DESCRIPTOR)
{
uint8_t desc_index = value & 0xff;
if (desc_index < NUM_STRING_DESC)
{
usb_lld_set_data_to_send (String_Descriptors[desc_index].Descriptor,
String_Descriptors[desc_index].Descriptor_Size);
return USB_SUCCESS;
}
}
return USB_UNSUPPORT;
}
static int gnuk_usb_event (uint8_t event_type, uint16_t value)
int usb_cb_handle_event (uint8_t event_type, uint16_t value)
{
int i;
uint8_t current_conf;
@@ -401,7 +369,7 @@ static int gnuk_usb_event (uint8_t event_type, uint16_t value)
return USB_UNSUPPORT;
}
static int gnuk_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
int usb_cb_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
{
static uint8_t zero = 0;
@@ -429,18 +397,6 @@ static int gnuk_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
}
}
/*
* Interface to USB core
*/
const struct usb_device_method Device_Method = {
gnuk_device_reset,
gnuk_ctrl_write_finish,
gnuk_setup,
gnuk_get_descriptor,
gnuk_usb_event,
gnuk_interface,
};
CH_IRQ_HANDLER (Vector90)
{

View File

@@ -9,6 +9,13 @@
#include "usb_conf.h"
#include "usb-cdc.h"
struct Descriptor
{
const uint8_t *Descriptor;
uint16_t Descriptor_Size;
};
#define USB_ICC_INTERFACE_CLASS 0x0B
#define USB_ICC_INTERFACE_SUBCLASS 0x00
#define USB_ICC_INTERFACE_BULK_PROTOCOL 0x00
@@ -53,6 +60,15 @@ static const uint8_t gnukDeviceDescriptor[] = {
#define NUM_INTERFACES (ICC_NUM_INTERFACES+VCOM_NUM_INTERFACES+MSC_NUM_INTERFACES)
#if defined(USB_SELF_POWERED)
#define USB_INITIAL_FEATURE 0xC0 /* bmAttributes: self powered */
#else
#define USB_INITIAL_FEATURE 0x80 /* bmAttributes: bus powered */
#endif
const uint8_t usb_initial_feature = USB_INITIAL_FEATURE;
/* Configuation Descriptor */
static const uint8_t gnukConfigDescriptor[] = {
9, /* bLength: Configuation Descriptor size */
@@ -61,11 +77,7 @@ static const uint8_t gnukConfigDescriptor[] = {
NUM_INTERFACES, /* bNumInterfaces: */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
#if defined(USB_SELF_POWERED)
0xC0, /* bmAttributes: self powered */
#else
0x80, /* bmAttributes: bus powered */
#endif
USB_INITIAL_FEATURE, /* bmAttributes*/
50, /* MaxPower 100 mA */
/* Interface Descriptor */
@@ -260,25 +272,25 @@ static const uint8_t gnukStringLangID[] = {
const uint8_t gnukStringSerial[] = {
19*2+2, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
/* FSIJ-1.0 */
/* FSIJ-1.0.3- */
'F', 0, 'S', 0, 'I', 0, 'J', 0, '-', 0,
'1', 0, '.', 0, '0', 0, '.', 0, '1', 0, /* Version number of Gnuk */
'1', 0, '.', 0, '0', 0, '.', 0, '3', 0, /* Version number of Gnuk */
'-', 0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
const struct Descriptor Device_Descriptor = {
static const struct Descriptor Device_Descriptor = {
gnukDeviceDescriptor,
sizeof (gnukDeviceDescriptor)
};
const struct Descriptor Config_Descriptor = {
static const struct Descriptor Config_Descriptor = {
gnukConfigDescriptor,
sizeof (gnukConfigDescriptor)
};
const struct Descriptor String_Descriptors[NUM_STRING_DESC] = {
static const struct Descriptor String_Descriptors[NUM_STRING_DESC] = {
{gnukStringLangID, sizeof (gnukStringLangID)},
{gnukStringVendor, sizeof (gnukStringVendor)},
{gnukStringProduct, sizeof (gnukStringProduct)},
@@ -287,3 +299,34 @@ const struct Descriptor String_Descriptors[NUM_STRING_DESC] = {
{gnuk_config_options, sizeof (gnuk_config_options)},
{sys_version, sizeof (sys_version)},
};
int
usb_cb_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value)
{
(void)index;
if (desc_type == DEVICE_DESCRIPTOR)
{
usb_lld_set_data_to_send (Device_Descriptor.Descriptor,
Device_Descriptor.Descriptor_Size);
return USB_SUCCESS;
}
else if (desc_type == CONFIG_DESCRIPTOR)
{
usb_lld_set_data_to_send (Config_Descriptor.Descriptor,
Config_Descriptor.Descriptor_Size);
return USB_SUCCESS;
}
else if (desc_type == STRING_DESCRIPTOR)
{
uint8_t desc_index = value & 0xff;
if (desc_index < NUM_STRING_DESC)
{
usb_lld_set_data_to_send (String_Descriptors[desc_index].Descriptor,
String_Descriptors[desc_index].Descriptor_Size);
return USB_SUCCESS;
}
}
return USB_UNSUPPORT;
}

View File

@@ -74,12 +74,10 @@ struct DEVICE_INFO
static struct CONTROL_INFO control_info;
static struct DEVICE_INFO device_info;
static struct DATA_INFO data_info;
extern const struct usb_device_method Device_Method;
static struct CONTROL_INFO *const ctrl_p = &control_info;
static struct DEVICE_INFO *const dev_p = &device_info;
static struct DATA_INFO *const data_p = &data_info;
static const struct usb_device_method *const method_p = &Device_Method;
#define REG_BASE (0x40005C00UL) /* USB_IP Peripheral Registers base address */
#define PMA_ADDR (0x40006000UL) /* USB_IP Packet Memory Area base address */
@@ -399,7 +397,7 @@ usb_interrupt_handler (void)
if (istr_value & ISTR_RESET)
{
st103_set_istr (CLR_RESET);
method_p->reset ();
usb_cb_device_reset ();
}
if (istr_value & ISTR_DOVR)
@@ -529,7 +527,7 @@ static int std_get_status (uint8_t req,
if (dev_p->current_configuration == 0)
return USB_UNSUPPORT;
r = (*method_p->interface) (USB_QUERY_INTERFACE, index, 0);
r = usb_cb_interface (USB_QUERY_INTERFACE, index, 0);
if (r != USB_SUCCESS)
return USB_UNSUPPORT;
@@ -700,7 +698,7 @@ static int std_get_descriptor (uint8_t req, uint16_t value,
(void)length;
if (rcp == DEVICE_RECIPIENT)
return (*method_p->get_descriptor) ((value >> 8), index, value);
return usb_cb_get_descriptor ((value >> 8), index, value);
return USB_UNSUPPORT;
}
@@ -736,7 +734,7 @@ static int std_set_configuration (uint8_t req, uint16_t value,
{
int r;
r = (*method_p->event) (USB_EVENT_CONFIG, value);
r = usb_cb_handle_event (USB_EVENT_CONFIG, value);
if (r == USB_SUCCESS)
return USB_SUCCESS;
}
@@ -760,7 +758,7 @@ static int std_get_interface (uint8_t req, uint16_t value,
if (dev_p->current_configuration == 0)
return USB_UNSUPPORT;
return (*method_p->interface) (USB_GET_INTERFACE, index, 0);
return usb_cb_interface (USB_GET_INTERFACE, index, 0);
}
return USB_UNSUPPORT;
@@ -784,7 +782,7 @@ static int std_set_interface (uint8_t req, uint16_t value,
if (dev_p->current_configuration != 0)
return USB_UNSUPPORT;
r = (*method_p->interface) (USB_SET_INTERFACE, index, value);
r = usb_cb_interface (USB_SET_INTERFACE, index, value);
if (r == USB_SUCCESS)
return USB_SUCCESS;
}
@@ -792,21 +790,6 @@ static int std_set_interface (uint8_t req, uint16_t value,
return USB_UNSUPPORT;
}
static const HANDLER std_request_handler[TOTAL_REQUEST] = {
std_get_status,
std_clear_feature,
std_none,
std_set_feature,
std_none,
std_set_address,
std_get_descriptor,
std_none, /* set_descriptor is not supported */
std_get_configuration,
std_set_configuration,
std_get_interface,
std_set_interface,
std_none, /* sync_frame is not supported (for now) */
};
static void handle_setup0 (void)
{
@@ -836,14 +819,27 @@ static void handle_setup0 (void)
{
if (req < TOTAL_REQUEST)
{
handler = std_request_handler[req];
switch (req)
{
case 0: handler = std_get_status; break;
case 1: handler = std_clear_feature; break;
case 3: handler = std_set_feature; break;
case 5: handler = std_set_address; break;
case 6: handler = std_get_descriptor; break;
case 8: handler = std_get_configuration; break;
case 9: handler = std_set_configuration; break;
case 10: handler = std_get_interface; break;
case 11: handler = std_set_interface; break;
default: handler = std_none; break;
}
r = (*handler) (ctrl_p->bmRequestType,
ctrl_p->wValue, ctrl_p->wIndex, ctrl_p->wLength);
}
}
else
r = (*method_p->setup) (ctrl_p->bmRequestType, req,
ctrl_p->wValue, ctrl_p->wIndex, ctrl_p->wLength);
r = usb_cb_setup (ctrl_p->bmRequestType, req,
ctrl_p->wValue, ctrl_p->wIndex, ctrl_p->wLength);
if (r != USB_SUCCESS)
dev_p->state = STALLED;
@@ -890,12 +886,12 @@ static void handle_in0 (void)
== (STANDARD_REQUEST | DEVICE_RECIPIENT)))
{
st103_set_daddr (ctrl_p->wValue);
(*method_p->event) (USB_EVENT_ADDRESS, ctrl_p->wValue);
usb_cb_handle_event (USB_EVENT_ADDRESS, ctrl_p->wValue);
}
else
(*method_p->ctrl_write_finish) (ctrl_p->bmRequestType,
ctrl_p->bRequest, ctrl_p->wValue,
ctrl_p->wIndex, ctrl_p->wLength);
usb_cb_ctrl_write_finish (ctrl_p->bmRequestType,
ctrl_p->bRequest, ctrl_p->wValue,
ctrl_p->wIndex, ctrl_p->wLength);
dev_p->state = STALLED;
}
@@ -938,26 +934,6 @@ void WEAK EP5_OUT_Callback (void);
void WEAK EP6_OUT_Callback (void);
void WEAK EP7_OUT_Callback (void);
void (*const ep_intr_handler_IN[7]) (void) = {
EP1_IN_Callback,
EP2_IN_Callback,
EP3_IN_Callback,
EP4_IN_Callback,
EP5_IN_Callback,
EP6_IN_Callback,
EP7_IN_Callback,
};
void (*const ep_intr_handler_OUT[7]) (void) = {
EP1_OUT_Callback,
EP2_OUT_Callback,
EP3_OUT_Callback,
EP4_OUT_Callback,
EP5_OUT_Callback,
EP6_OUT_Callback,
EP7_OUT_Callback,
};
static void
usb_handle_transfer (void)
{
@@ -1008,13 +984,31 @@ usb_handle_transfer (void)
if ((ep_value & EP_CTR_RX) != 0)
{
st103_ep_clear_ctr_rx (ep_index);
(*ep_intr_handler_OUT[ep_index-1]) ();
switch ((ep_index - 1))
{
case 0: EP1_OUT_Callback (); break;
case 1: EP2_OUT_Callback (); break;
case 2: EP3_OUT_Callback (); break;
case 3: EP4_OUT_Callback (); break;
case 4: EP5_OUT_Callback (); break;
case 5: EP6_OUT_Callback (); break;
case 6: EP7_OUT_Callback (); break;
}
}
if ((ep_value & EP_CTR_TX) != 0)
{
st103_ep_clear_ctr_tx (ep_index);
(*ep_intr_handler_IN[ep_index-1]) ();
switch ((ep_index - 1))
{
case 0: EP1_IN_Callback (); break;
case 1: EP2_IN_Callback (); break;
case 2: EP3_IN_Callback (); break;
case 3: EP4_IN_Callback (); break;
case 4: EP5_IN_Callback (); break;
case 5: EP6_IN_Callback (); break;
case 6: EP7_IN_Callback (); break;
}
}
}
}

View File

@@ -49,29 +49,20 @@ enum DESCRIPTOR_TYPE
#define USB_SETUP_SET(req) ((req & REQUEST_DIR) == 0)
#define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0)
struct Descriptor
{
const uint8_t *Descriptor;
uint16_t Descriptor_Size;
};
enum
{
USB_UNSUPPORT = 0,
USB_SUCCESS = 1,
};
struct usb_device_method
{
void (*reset) (void);
void (*ctrl_write_finish) (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len);
int (*setup) (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len);
int (*get_descriptor) (uint8_t desc_type, uint16_t index, uint16_t value);
int (*event) (uint8_t event_type, uint16_t value);
int (*interface) (uint8_t cmd, uint16_t interface, uint16_t value);
};
void usb_cb_device_reset (void);
void usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no,
uint16_t value, uint16_t index, uint16_t len);
int usb_cb_setup (uint8_t req, uint8_t req_no, uint16_t value,
uint16_t index, uint16_t len);
int usb_cb_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value);
int usb_cb_handle_event (uint8_t event_type, uint16_t value);
int usb_cb_interface (uint8_t cmd, uint16_t interface, uint16_t value);
enum {
USB_EVENT_ADDRESS,
@@ -87,14 +78,6 @@ enum {
USB_QUERY_INTERFACE,
};
extern void USB_Cable_Config (int NewState);
extern const struct usb_device_method Device_Method;
extern const struct Descriptor Device_Descriptor;
extern const struct Descriptor Config_Descriptor;
extern const struct Descriptor String_Descriptors[];
enum DEVICE_STATE
{
UNCONNECTED,
@@ -106,6 +89,7 @@ enum DEVICE_STATE
};
extern uint32_t bDeviceState;
extern const uint8_t usb_initial_feature;
#define STM32_USB_IRQ_PRIORITY 11

View File

@@ -8,7 +8,7 @@ Feature: command GET DATA
Scenario: data object extended capabilities
When requesting extended capabilities: c0
Then data should match: \x30\x00\x00\x00[\x00\x08]\x00\x00\xff\x01\x00
Then data should match: \x70\x00\x00\x20[\x00\x08]\x00\x00\xff\x01\x00
Scenario: data object algorithm attributes 1
When requesting algorithm attributes 1: c1

View File

@@ -8,7 +8,7 @@ Feature: command GET DATA
Scenario: data object extended capabilities
When requesting extended capabilities: c0
Then data should match: \x30\x00\x00\x00[\x00\x08]\x00\x00\xff\x01\x00
Then data should match: \x70\x00\x00\x20[\x00\x08]\x00\x00\xff\x01\x00
Scenario: data object algorithm attributes 1
When requesting algorithm attributes 1: c1

View File

@@ -8,7 +8,7 @@ Feature: command GET DATA
Scenario: data object extended capabilities
When requesting extended capabilities: c0
Then data should match: \x30\x00\x00\x00[\x00\x08]\x00\x00\xff\x01\x00
Then data should match: \x70\x00\x00\x20[\x00\x08]\x00\x00\xff\x01\x00
Scenario: data object algorithm attributes 1
When requesting algorithm attributes 1: c1

View File

@@ -5,7 +5,7 @@ from binascii import hexlify
import ast
import gnuk
import gnuk_token as gnuk
import rsa_keys
@Before

1
test/gnuk_token.py Symbolic link
View File

@@ -0,0 +1 @@
../tool/gnuk_token.py

32
tool/get_raw_public_key.py Executable file
View File

@@ -0,0 +1,32 @@
#! /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
pos_last = key.index(")(1:e3:")
key = key[pos:pos_last]
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()

View File

@@ -25,266 +25,46 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from struct import *
import sys, time, os, binascii, string
from gnuk_token import *
# INPUT: binary file
# Assume only single CCID device is attached to computer, and it's Gnuk Token
import usb
# USB class, subclass, protocol
CCID_CLASS = 0x0B
CCID_SUBCLASS = 0x00
CCID_PROTOCOL_0 = 0x00
def icc_compose(msg_type, data_len, slot, seq, param, data):
return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
def iso7816_compose(ins, p1, p2, data, cls=0x00):
data_len = len(data)
if data_len == 0:
return pack('>BBBB', cls, ins, p1, p2)
else:
return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
# This class only supports Gnuk (for now)
class gnuk_token(object):
def __init__(self, device, configuration, interface):
"""
__init__(device, configuration, interface) -> None
Initialize the device.
device: usb.Device object.
configuration: configuration number.
interface: usb.Interface object representing the interface and altenate setting.
"""
if interface.interfaceClass != CCID_CLASS:
raise ValueError, "Wrong interface class"
if interface.interfaceSubClass != CCID_SUBCLASS:
raise ValueError, "Wrong interface sub class"
self.__devhandle = device.open()
try:
self.__devhandle.setConfiguration(configuration)
except:
pass
self.__devhandle.claimInterface(interface)
self.__devhandle.setAltInterface(interface)
self.__intf = interface.interfaceNumber
self.__alt = interface.alternateSetting
self.__conf = configuration
self.__bulkout = 1
self.__bulkin = 0x81
self.__timeout = 10000
self.__seq = 0
def icc_get_result(self):
msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
if len(msg) < 10:
print msg
raise ValueError, "icc_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)
def icc_get_status(self):
msg = icc_compose(0x65, 0, 0, self.__seq, 0, "")
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
self.__seq += 1
status, chain, data = self.icc_get_result()
# XXX: check chain, data
return status
def icc_power_on(self):
msg = icc_compose(0x62, 0, 0, self.__seq, 0, "")
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
self.__seq += 1
status, chain, data = self.icc_get_result()
# XXX: check status, chain
return data # ATR
def icc_power_off(self):
msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
self.__seq += 1
status, chain, data = self.icc_get_result()
# XXX: check chain, data
return status
def icc_send_data_block(self, data):
msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
self.__seq += 1
return self.icc_get_result()
def icc_send_cmd(self, data):
status, chain, data_rcv = self.icc_send_data_block(data)
if chain == 0:
return data_rcv
elif chain == 1:
d = data_rcv
while True:
msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "")
self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
self.__seq += 1
status, chain, data_rcv = self.icc_get_result()
# XXX: check status
d += data_rcv
if chain == 2:
break
elif chain == 3:
continue
else:
raise ValueError, "icc_send_cmd chain"
return d
else:
raise ValueError, "icc_send_cmd"
def cmd_get_response(self, expected_len):
result = []
while True:
cmd_data = iso7816_compose(0xc0, 0x00, 0x00, '') + pack('>B', expected_len)
response = self.icc_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.icc_send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError, sw
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError, sw
def cmd_read_binary(self, fileid):
cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, '')
sw = self.icc_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.icc_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"
if cmd_data1:
sw = self.icc_send_cmd(cmd_data1)
if len(sw) != 2:
raise ValueError, "cmd_write_binary 1"
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError, "cmd_write_binary 1"
count += 1
def cmd_select_openpgp(self):
cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, "\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):
raise ValueError, ("%02x%02x" % (sw[0], sw[1]))
def cmd_get_data(self, tagh, tagl):
cmd_data = iso7816_compose(0xca, tagh, tagl, "")
sw = self.icc_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 compare(data_original, data_in_device):
i = 0
for d in data_original:
if ord(d) != data_in_device[i]:
raise ValueError, "verify failed at %08x" % i
i += 1
def gnuk_devices():
busses = usb.busses()
for bus in busses:
devices = bus.devices
for dev in devices:
for config in dev.configurations:
for intf in config.interfaces:
for alt in intf:
if alt.interfaceClass == CCID_CLASS and \
alt.interfaceSubClass == CCID_SUBCLASS and \
alt.interfaceProtocol == CCID_PROTOCOL_0:
yield dev, config, alt
DEFAULT_PW3 = "12345678"
BY_ADMIN = 3
def main(fileid, is_update, data, passwd):
icc = None
gnuk = None
for (dev, config, intf) in gnuk_devices():
try:
icc = gnuk_token(dev, config, intf)
gnuk = gnuk_token(dev, config, intf)
print "Device: ", dev.filename
print "Configuration: ", config.value
print "Interface: ", intf.interfaceNumber
break
except:
pass
if icc.icc_get_status() == 2:
if gnuk.icc_get_status() == 2:
raise ValueError, "No ICC present"
elif icc.icc_get_status() == 1:
icc.icc_power_on()
icc.cmd_verify(BY_ADMIN, passwd)
icc.cmd_write_binary(fileid, data, is_update)
icc.cmd_select_openpgp()
elif gnuk.icc_get_status() == 1:
gnuk.icc_power_on()
gnuk.cmd_verify(BY_ADMIN, passwd)
gnuk.cmd_write_binary(fileid, data, is_update)
gnuk.cmd_select_openpgp()
if fileid == 0:
data_in_device = icc.cmd_get_data(0x00, 0x4f)
data_in_device = gnuk.cmd_get_data(0x00, 0x4f)
for d in data_in_device:
print "%02x" % d,
print "%02x" % ord(d),
print
compare(data, data_in_device[8:])
compare(data + '\x00\x00', data_in_device[8:])
elif fileid >= 1 and fileid <= 4:
data_in_device = icc.cmd_read_binary(fileid)
data_in_device = gnuk.cmd_read_binary(fileid)
compare(data, data_in_device)
else:
data_in_device = icc.cmd_get_data(0x7f, 0x21)
data_in_device = gnuk.cmd_get_data(0x7f, 0x21)
compare(data, data_in_device)
icc.icc_power_off()
gnuk.icc_power_off()
return 0
if __name__ == '__main__':

View File

@@ -90,13 +90,13 @@ def main(passwd):
gnuk.cmd_verify(BY_ADMIN, passwd)
gnuk.cmd_select_openpgp()
gnuk.cmd_put_data_remove(0x00, 0xc7) # FP_SIG
gnuk.cmd_put_data_remove(0x00, 0xcd) # KGTIME_SIG
gnuk.cmd_put_data_remove(0x00, 0xce) # KGTIME_SIG
gnuk.cmd_put_data_key_import_remove(1)
gnuk.cmd_put_data_remove(0x00, 0xc8) # FP_DEC
gnuk.cmd_put_data_remove(0x00, 0xce) # KGTIME_DEC
gnuk.cmd_put_data_remove(0x00, 0xcf) # KGTIME_DEC
gnuk.cmd_put_data_key_import_remove(2)
gnuk.cmd_put_data_remove(0x00, 0xc9) # FP_AUT
gnuk.cmd_put_data_remove(0x00, 0xcf) # KGTIME_AUT
gnuk.cmd_put_data_remove(0x00, 0xd0) # KGTIME_AUT
gnuk.cmd_put_data_key_import_remove(3)
gnuk.connection.disconnect()
@@ -105,7 +105,7 @@ def main(passwd):
if __name__ == '__main__':
passwd = DEFAULT_PW3
if sys.argv[1] == '-p':
if len(sys.argv) > 1 and sys.argv[1] == '-p':
from getpass import getpass
passwd = getpass("Admin password: ")
sys.argv.pop(1)

70
tool/gnuk_remove_keys_libusb.py Executable file
View File

@@ -0,0 +1,70 @@
#! /usr/bin/python
"""
gnuk_remove_keys_libusb.py - a tool to remove keys in Gnuk Token
Copyright (C) 2012 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 sys, os, string
from gnuk_token import *
# Assume only single CCID device is attached to computer and it's Gnuk Token
DEFAULT_PW3 = "12345678"
BY_ADMIN = 3
def main(passwd):
gnuk = None
for (dev, config, intf) in gnuk_devices():
try:
gnuk = gnuk_token(dev, config, intf)
print "Device: ", dev.filename
print "Configuration: ", config.value
print "Interface: ", intf.interfaceNumber
break
except:
pass
if gnuk.icc_get_status() == 2:
raise ValueError, "No ICC present"
elif gnuk.icc_get_status() == 1:
gnuk.icc_power_on()
gnuk.cmd_verify(BY_ADMIN, passwd)
gnuk.cmd_select_openpgp()
gnuk.cmd_put_data_remove(0x00, 0xc7) # FP_SIG
gnuk.cmd_put_data_remove(0x00, 0xce) # KGTIME_SIG
gnuk.cmd_put_data_key_import_remove(1)
gnuk.cmd_put_data_remove(0x00, 0xc8) # FP_DEC
gnuk.cmd_put_data_remove(0x00, 0xcf) # KGTIME_DEC
gnuk.cmd_put_data_key_import_remove(2)
gnuk.cmd_put_data_remove(0x00, 0xc9) # FP_AUT
gnuk.cmd_put_data_remove(0x00, 0xd0) # KGTIME_AUT
gnuk.cmd_put_data_key_import_remove(3)
gnuk.icc_power_off()
return 0
if __name__ == '__main__':
passwd = DEFAULT_PW3
if len(sys.argv) > 1 and sys.argv[1] == '-p':
from getpass import getpass
passwd = getpass("Admin password: ")
sys.argv.pop(1)
main(passwd)

View File

@@ -1,8 +1,7 @@
"""
gnuk.py - a library for Gnuk Token
This tool is for importing certificate, writing serial number, etc.
gnuk_token.py - a library for Gnuk Token
Copyright (C) 2011, 2012 Free Software Initiative of Japan
Copyright (C) 2011, 2012, 2013 Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -22,11 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from struct import *
import string
# Assume only single CCID device is attached to computer, and it's Gnuk Token
import usb
import string, binascii
import usb, time
# USB class, subclass, protocol
CCID_CLASS = 0x0B
@@ -36,16 +32,24 @@ CCID_PROTOCOL_0 = 0x00
def icc_compose(msg_type, data_len, slot, seq, param, data):
return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
def iso7816_compose(ins, p1, p2, data, cls=0x00):
def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
data_len = len(data)
if data_len == 0:
return pack('>BBBB', cls, ins, p1, p2)
if not le:
return pack('>BBBB', cls, ins, p1, p2)
else:
return pack('>BBBBB', cls, ins, p1, p2, le)
else:
return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
if not le:
return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
else:
return pack('>BBBBB', cls, ins, p1, p2, data_len) \
+ data + pack('>B', le)
def list_to_string(l):
return string.join([chr(c) for c in l], '')
# This class only supports Gnuk (for now)
class gnuk_token(object):
def __init__(self, device, configuration, interface):
"""
@@ -92,6 +96,50 @@ class gnuk_token(object):
def release_gnuk(self):
self.__devhandle.releaseInterface()
def stop_gnuk(self):
self.__devhandle.releaseInterface()
self.__devhandle.setConfiguration(0)
return
def mem_info(self):
mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0,
value = 0, index = 0, buffer = 8,
timeout = 10)
start = ((mem[3]*256 + mem[2])*256 + mem[1])*256 + mem[0]
end = ((mem[7]*256 + mem[6])*256 + mem[5])*256 + mem[4]
return (start, end)
def download(self, start, data):
addr = start
addr_end = (start + len(data)) & 0xffffff00
i = (addr - 0x20000000) / 0x100
j = 0
print "start %08x" % addr
print "end %08x" % addr_end
while addr < addr_end:
print "# %08x: %d : %d" % (addr, i, 256)
self.__devhandle.controlMsg(requestType = 0x40, request = 1,
value = i, index = 0,
buffer = data[j*256:j*256+256],
timeout = 10)
i = i+1
j = j+1
addr = addr + 256
residue = len(data) % 256
if residue != 0:
print "# %08x: %d : %d" % (addr, i, residue)
self.__devhandle.controlMsg(requestType = 0x40, request = 1,
value = i, index = 0,
buffer = data[j*256:],
timeout = 10)
def execute(self, last_addr):
i = (last_addr - 0x20000000) / 0x100
o = (last_addr - 0x20000000) % 0x100
self.__devhandle.controlMsg(requestType = 0x40, request = 2,
value = i, index = o, buffer = None,
timeout = 10)
def icc_get_result(self):
msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
if len(msg) < 10:
@@ -123,6 +171,7 @@ class gnuk_token(object):
status, chain, data = self.icc_get_result()
# XXX: check status, chain
self.atr = list_to_string(data) # ATR
return self.atr
def icc_power_off(self):
msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
@@ -190,7 +239,7 @@ class gnuk_token(object):
cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, '')
sw = self.icc_send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError, sw
raise ValueError(sw)
if sw[0] != 0x61:
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return self.cmd_get_response(sw[1])
@@ -225,9 +274,9 @@ class gnuk_token(object):
if cmd_data1:
sw = self.icc_send_cmd(cmd_data1)
if len(sw) != 2:
raise ValueError("cmd_write_binary", sw)
raise ValueError("cmd_write_binary 1", sw)
if not (sw[0] == 0x90 and sw[1] == 0x00):
raise ValueError("cmd_write_binary", "%02x%02x" % (sw[0], sw[1]))
raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1]))
count += 1
def cmd_select_openpgp(self):
@@ -363,13 +412,158 @@ class gnuk_token(object):
pk = self.cmd_get_response(sw[1])
return (pk[9:9+256], pk[9+256+2:9+256+2+3])
def cmd_put_data_remove(self, tagh, tagl):
cmd_data = iso7816_compose(0xda, tagh, tagl, "")
sw = self.icc_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 = "\xb6\x00" # SIG
elif keyno == 2:
keyspec = "\xb8\x00" # DEC
else:
keyspec = "\xa4\x00" # AUT
cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, "\x4d\x02" + keyspec)
sw = self.icc_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.icc_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.icc_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.icc_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]))
class regnual(object):
def __init__(self, dev):
conf = dev.configurations[0]
intf_alt = conf.interfaces[0]
intf = intf_alt[0]
if intf.interfaceClass != 0xff:
raise ValueError("Wrong interface class")
self.__devhandle = dev.open()
try:
self.__devhandle.setConfiguration(conf)
except:
pass
self.__devhandle.claimInterface(intf)
self.__devhandle.setAltInterface(intf)
def mem_info(self):
mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0,
value = 0, index = 0, buffer = 8,
timeout = 10000)
start = ((mem[3]*256 + mem[2])*256 + mem[1])*256 + mem[0]
end = ((mem[7]*256 + mem[6])*256 + mem[5])*256 + mem[4]
return (start, end)
def download(self, start, data):
addr = start
addr_end = (start + len(data)) & 0xffffff00
i = (addr - 0x08000000) / 0x100
j = 0
print "start %08x" % addr
print "end %08x" % addr_end
while addr < addr_end:
print "# %08x: %d: %d : %d" % (addr, i, j, 256)
self.__devhandle.controlMsg(requestType = 0x40, request = 1,
value = 0, index = 0,
buffer = data[j*256:j*256+256],
timeout = 10000)
crc32code = crc32(data[j*256:j*256+256])
res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
value = 0, index = 0, buffer = 4,
timeout = 10000)
r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
if (crc32code ^ r_value) != 0xffffffff:
print "failure"
self.__devhandle.controlMsg(requestType = 0x40, request = 3,
value = i, index = 0,
buffer = None,
timeout = 10000)
time.sleep(0.010)
res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
value = 0, index = 0, buffer = 4,
timeout = 10000)
r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
if r_value == 0:
print "failure"
i = i+1
j = j+1
addr = addr + 256
residue = len(data) % 256
if residue != 0:
print "# %08x: %d : %d" % (addr, i, residue)
self.__devhandle.controlMsg(requestType = 0x40, request = 1,
value = 0, index = 0,
buffer = data[j*256:],
timeout = 10000)
crc32code = crc32(data[j*256:].ljust(256,chr(255)))
res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
value = 0, index = 0, buffer = 4,
timeout = 10000)
r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
if (crc32code ^ r_value) != 0xffffffff:
print "failure"
self.__devhandle.controlMsg(requestType = 0x40, request = 3,
value = i, index = 0,
buffer = None,
timeout = 10000)
time.sleep(0.010)
res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
value = 0, index = 0, buffer = 4,
timeout = 10000)
r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
if r_value == 0:
print "failure"
def protect(self):
self.__devhandle.controlMsg(requestType = 0x40, request = 4,
value = 0, index = 0, buffer = None,
timeout = 10000)
time.sleep(0.100)
res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
value = 0, index = 0, buffer = 4,
timeout = 10000)
r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
if r_value == 0:
print "protection failure"
def finish(self):
self.__devhandle.controlMsg(requestType = 0x40, request = 5,
value = 0, index = 0, buffer = None,
timeout = 10000)
def reset_device(self):
try:
self.__devhandle.reset()
except:
pass
def compare(data_original, data_in_device):
i = 0
for d in data_original:
if ord(d) != data_in_device[i]:
raise ValueError, "verify failed at %08x" % i
i += 1
if data_original == data_in_device:
return True
raise ValueError("verify failed")
def gnuk_devices():
busses = usb.busses()
@@ -384,6 +578,20 @@ 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
def get_gnuk_device():
icc = None
for (dev, config, intf) in gnuk_devices():
@@ -405,3 +613,12 @@ def get_gnuk_device():
else:
raise ValueError("Unknown ICC status", status)
return icc
SHA256_OID_PREFIX="3031300d060960864801650304020105000420"
def UNSIGNED(n):
return n & 0xffffffff
def crc32(bytestr):
crc = binascii.crc32(bytestr)
return UNSIGNED(crc)

View File

@@ -25,7 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from struct import *
import sys, time, os, binascii, string
# INPUT: binary file
# INPUT: binary files (regnual_image, upgrade_firmware_image)
# Assume only single CCID device is attached to computer, and it's Gnuk Token
@@ -390,11 +390,18 @@ from subprocess import check_output
SHA256_OID_PREFIX="3031300d060960864801650304020105000420"
def gpg_sign(hash):
result = check_output(["gpg-connect-agent",
"SCD SETDATA " + SHA256_OID_PREFIX + hash,
"SCD PKAUTH OPENPGP.3",
"/bye"])
# When user specify KEYGRIP, use it. Or else, connect to SCD directly.
def gpg_sign(keygrip, hash):
if keygrip:
result = check_output(["gpg-connect-agent",
"SIGKEY %s" % keygrip,
"SETHASH --hash=sha256 %s" % hash,
"PKSIGN --hash=sha256", "/bye"])
else:
result = check_output(["gpg-connect-agent",
"SCD SETDATA " + SHA256_OID_PREFIX + hash,
"SCD PKAUTH OPENPGP.3",
"/bye"])
signed = ""
while True:
i = result.find('%')
@@ -406,8 +413,12 @@ def gpg_sign(hash):
signed += chr(int(hex_str,16))
result = result[i+3:]
pos = signed.index("D ") + 2
signed = signed[pos:-4] # \nOK\n
if keygrip:
pos = signed.index("D (7:sig-val(3:rsa(1:s256:") + 26
signed = signed[pos:-7]
else:
pos = signed.index("D ") + 2
signed = signed[pos:-4] # \nOK\n
if len(signed) != 256:
raise ValueError, binascii.hexlify(signed)
return signed
@@ -419,7 +430,7 @@ def crc32(bytestr):
crc = binascii.crc32(bytestr)
return UNSIGNED(crc)
def main(data_regnual, data_upgrade):
def main(keygrip, data_regnual, data_upgrade):
l = len(data_regnual)
if (l & 0x03) != 0:
data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), chr(0))
@@ -441,7 +452,7 @@ def main(data_regnual, data_upgrade):
icc.icc_power_on()
icc.cmd_select_openpgp()
challenge = icc.cmd_get_challenge()
signed = gpg_sign(binascii.hexlify(to_string(challenge)))
signed = gpg_sign(keygrip, binascii.hexlify(to_string(challenge)))
icc.cmd_external_authenticate(signed)
icc.stop_gnuk()
mem_info = icc.mem_info()
@@ -478,6 +489,11 @@ def main(data_regnual, data_upgrade):
if __name__ == '__main__':
keygrip = None
if sys.argv[1] == '-k':
sys.argv.pop(1)
keygrip = sys.argv[1]
sys.argv.pop(1)
filename_regnual = sys.argv[1]
filename_upgrade = sys.argv[2]
f = open(filename_regnual)
@@ -488,4 +504,4 @@ if __name__ == '__main__':
data_upgrade = f.read()
f.close()
print "%s: %d" % (filename_upgrade, len(data_upgrade))
main(data_regnual, data_upgrade[4096:])
main(keygrip, data_regnual, data_upgrade[4096:])

125
tool/gpg_agent.py Normal file
View File

@@ -0,0 +1,125 @@
"""
gpg_agent.py - a library to connect gpg-agent
Copyright (C) 2013 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 platform, os, socket
IS_WINDOWS=(platform.system() == 'Windows')
BUFLEN=1024
class gpg_agent(object):
def __init__(self):
if IS_WINDOWS:
home = os.getenv("HOME")
if not home:
home = os.getenv("APPDATA")
comm_port = os.path.join(home, "gnupg", "S.gpg-agent")
#
f = open(comm_port, "rb", 0)
infostr = f.read()
f.close()
#
info = infostr.split('\n', 1)
port = int(info[0])
nonce = info[1]
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", port))
s.send(nonce)
else:
infostr = os.getenv("GPG_AGENT_INFO")
info = infostr.split(':', 2)
path = info[0]
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(path)
self.sock = s
self.buf_remained = ""
self.response = None
def read_line(self):
line = ""
if self.buf_remained != "":
chunk = self.buf_remained
else:
chunk = self.sock.recv(BUFLEN)
while True:
pos = chunk.find('\n')
if pos >= 0:
self.buf_remained = chunk[pos+1:]
line = line + chunk[0:pos]
return line
else:
line = line + chunk
chunk = self.sock.recv(BUFLEN)
def get_response(self):
r = self.response
result = ""
while True:
i = r.find('%')
if i < 0:
result += r
break
hex_str = r[i+1:i+3]
result += r[0:i]
result += chr(int(hex_str,16))
r = r[i+3:]
return result
def send_command(self, cmd):
self.sock.send(cmd)
self.response = ""
while True:
while True:
l = self.read_line()
if l[0] != '#':
break
if l[0] == 'D':
self.response += l[2:]
elif l[0] == 'O' and l[1] == 'K':
return True
elif l[0] == 'E' and l[1] == 'R' and l[2] == 'R':
return False
else: # XXX: S, INQUIRE, END
return False
def close(self):
self.sock.send('BYE\n')
bye = self.read_line()
self.sock.close()
return bye # "OK closing connection"
# Test
if __name__ == '__main__':
g = gpg_agent()
print g.read_line()
print g.send_command("KEYINFO --list --data\n")
kl_str = g.get_response()
kl_str = kl_str[0:-1]
kl = kl_str.split('\n')
import re
kl_o3 = [kg for kg in kl if re.search("OPENPGP\\.3", kg)]
print kl_o3
kg = kl_o3[0].split(' ')[0]
print g.send_command("READKEY %s\n" % kg)
r = g.get_response()
import binascii
print binascii.hexlify(r)
print g.close()

View File

@@ -167,7 +167,7 @@ if __name__ == '__main__':
option = sys.argv[1]
sys.argv.pop(1)
if option == '-h':
if bunum != None or devnum != None:
if busnum != None or devnum != None:
exit_with_usage(sys.argv[0])
hub = int(sys.argv[1])
sys.argv.pop(1)

View File

@@ -0,0 +1,205 @@
"""
pagent_proxy_to_gpg.py - Connect gpg-agent as Pagent
Copyright (C) 2013 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 os, sys, re, hashlib, binascii
from struct import *
from gpg_agent import gpg_agent
from sexp import sexp
# Assume it's only OPENPGP.3 key and it's 2048-bit
def debug(string):
print "DEBUG: %s" % string
sys.stdout.flush()
def get_keygrip_list(keyinfo_result):
kl_str = keyinfo_result[0:-1] # Chop last newline
kl = kl_str.split('\n')
# filter by "OPENPGP.3", and only keygrip
return [kg.split(' ')[0] for kg in kl if re.search("OPENPGP\\.3", kg)]
# Connect GPG-Agent, and get list of KEYGRIPs.
g = gpg_agent()
g.read_line() # Greeting message
g.send_command('KEYINFO --list --data\n')
keyinfo_result = g.get_response()
keygrip_list = get_keygrip_list(keyinfo_result)
debug(keygrip_list)
keylist = []
# For each KEYGRIP, get its PUBLIC-KEY.
for kg in keygrip_list:
g.send_command('READKEY %s\n' % kg)
key = sexp(g.get_response())
# [ "public-key" [ "rsa" [ "n" MODULUS ] [ "e" EXPONENT] ] ]
n = key[1][1][1]
e = key[1][2][1]
debug(binascii.hexlify(n))
debug(binascii.hexlify(e))
keylist.append([n, e, kg])
# FIXME: should handle all keys, not only a single key
# FIXME: should support different key size
n = keylist[0][0]
e = keylist[0][1]
keygrip = keylist[0][2]
ssh_rsa_public_blob = "\x00\x00\x00\x07ssh-rsa" + \
"\x00\x00\x00\x03" + e + "\x00\x00\x01\x01" + n
ssh_key_comment = "key_on_gpg" # XXX: get login from card for comment?
import win32con, win32api, win32gui, ctypes, ctypes.wintypes
# For WM_COPYDATA structure
class COPYDATA(ctypes.Structure):
_fields_ = [ ('dwData', ctypes.wintypes.LPARAM),
('cbData', ctypes.wintypes.DWORD),
('lpData', ctypes.c_void_p) ]
P_COPYDATA = ctypes.POINTER(COPYDATA)
class SSH_MSG_HEAD(ctypes.BigEndianStructure):
_pack_ = 1
_fields_ = [ ('msg_len', ctypes.c_uint32),
('msg_type', ctypes.c_byte) ]
P_SSH_MSG_HEAD = ctypes.POINTER(SSH_MSG_HEAD)
class SSH_MSG_ID_ANSWER_HEAD(ctypes.BigEndianStructure):
_pack_ = 1
_fields_ = [ ('msg_len', ctypes.c_uint32),
('msg_type', ctypes.c_byte),
('keys', ctypes.c_uint32)]
P_SSH_MSG_ID_ANSWER = ctypes.POINTER(SSH_MSG_ID_ANSWER_HEAD)
class SSH_MSG_SIGN_RESPONSE_HEAD(ctypes.BigEndianStructure):
_pack_ = 1
_fields_ = [ ('msg_len', ctypes.c_uint32),
('msg_type', ctypes.c_byte),
('sig_len', ctypes.c_uint32)]
P_SSH_MSG_SIGN_RESPONSE = ctypes.POINTER(SSH_MSG_SIGN_RESPONSE_HEAD)
FILE_MAP_ALL_ACCESS=0x000F001F
class windows_ipc_listener(object):
def __init__(self):
message_map = { win32con.WM_COPYDATA: self.OnCopyData }
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = message_map
wc.lpszClassName = 'Pageant'
hinst = wc.hInstance = win32api.GetModuleHandle(None)
classAtom = win32gui.RegisterClass(wc)
self.hwnd = win32gui.CreateWindow (
classAtom,
"Pageant",
0,
0,
0,
win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT,
0,
0,
hinst,
None
)
debug("created: window=%08x" % self.hwnd)
def OnCopyData(self, hwnd, msg, wparam, lparam):
debug("WM_COPYDATA message")
debug(" window=%08x" % hwnd)
debug(" msg =%08x" % msg)
debug(" wparam=%08x" % wparam)
pCDS = ctypes.cast(lparam, P_COPYDATA)
debug(" dwData=%08x" % (pCDS.contents.dwData & 0xffffffff))
debug(" len=%d" % pCDS.contents.cbData)
mapname = ctypes.string_at(pCDS.contents.lpData)
debug(" mapname='%s'" % ctypes.string_at(pCDS.contents.lpData))
hMapObject = ctypes.windll.kernel32.OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, mapname)
if hMapObject == 0:
debug("error on OpenFileMapping")
return 0
pBuf = ctypes.windll.kernel32.MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, 0)
if pBuf == 0:
ctypes.windll.kernel32.CloseHandle(hMapObject)
debug("error on MapViewOfFile")
return 0
pSshMsg = ctypes.cast(pBuf, P_SSH_MSG_HEAD)
debug(" ssh_msg_len: %d" % pSshMsg.contents.msg_len)
debug(" ssh_msg_type: %d" % pSshMsg.contents.msg_type)
if pSshMsg.contents.msg_type == 11: # SSH2_AGENT_REQUEST_IDENTITIES
blob_len = len(ssh_rsa_public_blob)
cmnt_len = len(ssh_key_comment)
pAns = ctypes.cast(pBuf, P_SSH_MSG_ID_ANSWER)
pAns.contents.msg_len = 1+4+4+blob_len+4+cmnt_len
pAns.contents.msg_type = 12 # SSH2_AGENT_IDENTITIES_ANSWER
pAns.contents.keys = 1
ctypes.memmove(pBuf+4+1+4, pack('>I', blob_len), 4)
ctypes.memmove(pBuf+4+1+4+4, ssh_rsa_public_blob, blob_len)
ctypes.memmove(pBuf+4+1+4+4+blob_len, pack('>I', cmnt_len), 4)
ctypes.memmove(pBuf+4+1+4+4+blob_len+4, ssh_key_comment, cmnt_len)
debug("answer is:")
debug(" ssh_msg_len: %d" % pSshMsg.contents.msg_len)
debug(" ssh_msg_type: %d" % pSshMsg.contents.msg_type)
elif pSshMsg.contents.msg_type == 13: # SSH2_AGENT_SIGN_REQUEST
req_blob_len = unpack(">I", ctypes.string_at(pBuf+5, 4))[0]
req_blob = ctypes.string_at(pBuf+5+4, req_blob_len)
req_data_len = unpack(">I", ctypes.string_at(pBuf+5+4+req_blob_len,4))[0]
req_data = ctypes.string_at(pBuf+5+4+req_blob_len+4,req_data_len)
debug(" blob_len=%d" % req_blob_len)
debug(" data_len=%d" % req_data_len)
hash = hashlib.sha1(req_data).hexdigest()
debug(" hash=%s" % hash)
g.send_command('SIGKEY %s\n' % keygrip)
g.send_command('SETHASH --hash=sha1 %s\n' % hash)
g.send_command('PKSIGN\n')
sig = sexp(g.get_response())
# [ "sig-val" [ "rsa" [ "s" "xxx" ] ] ]
sig = sig[1][1][1]
sig = "\x00\x00\x00\x07" + "ssh-rsa" + "\x00\x00\x01\x00" + sig # FIXME: should support different key size
siglen = len(sig)
debug("sig_len=%d" % siglen)
debug("sig=%s" % binascii.hexlify(sig))
pRes = ctypes.cast(pBuf, P_SSH_MSG_SIGN_RESPONSE)
pRes.contents.msg_len = 1+4+siglen
pRes.contents.msg_type = 14 # SSH2_AGENT_SIGN_RESPONSE
pRes.contents.sig_len = siglen
ctypes.memmove(pBuf+4+1+4, sig, siglen)
debug("answer is:")
debug(" ssh_msg_len: %d" % pSshMsg.contents.msg_len)
debug(" ssh_msg_type: %d" % pSshMsg.contents.msg_type)
else:
exit(0)
ctypes.windll.kernel32.UnmapViewOfFile(pBuf)
ctypes.windll.kernel32.CloseHandle(hMapObject)
debug(" ssh_msg: done")
return 1
l = windows_ipc_listener()
win32gui.PumpMessages()

View File

@@ -3,7 +3,7 @@
"""
pinpadtest.py - a tool to test variable length pin entry with pinpad
Copyright (C) 2011, 2012 Free Software Initiative of Japan
Copyright (C) 2011, 2012, 2013 Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -33,6 +33,7 @@ from smartcard.util import toHexString
from getpass import getpass
CM_IOCTL_GET_FEATURE_REQUEST = (0x42000000 + 3400)
CM_IOCTL_VENDOR_IFD_EXCHANGE = (0x42000000 + 1)
FEATURE_VERIFY_PIN_DIRECT = 0x06
FEATURE_MODIFY_PIN_DIRECT = 0x07
@@ -51,7 +52,7 @@ def confirm_pin_setting(single_step):
return 0x03 # bConfirmPIN: old PIN and new PIN twice
class Card(object):
def __init__(self, add_a_byte, pinmin, pinmax):
def __init__(self, add_a_byte, pinmin, pinmax, fixed):
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=10, cardType=cardtype)
cardservice = cardrequest.waitforcard()
@@ -61,6 +62,7 @@ class Card(object):
self.another_byte = add_a_byte
self.pinmin = pinmin
self.pinmax = pinmax
self.fixed = fixed
def get_features(self):
p = self.connection.control(CM_IOCTL_GET_FEATURE_REQUEST, [])
@@ -96,12 +98,21 @@ class Card(object):
else:
return []
def cmd_vega_alpha_disable_empty_verify(self):
apdu = [ 0xB5, # -|
0x01, # | Pre-command parameters
0x00, # -|
0x03, # retry counter value (fixed value)
0x00 # enable 3s timeout
]
data = self.connection.control(CM_IOCTL_VENDOR_IFD_EXCHANGE, apdu)
def cmd_verify_pinpad(self, who):
apdu = [0x00, 0x20, 0x00, 0x80+who ]
pin_verify = [ 0x00, # bTimeOut
0x00, # bTimeOut2
0x82, # bmFormatString: Byte, pos=0, left, ASCII.
0x00, # bmPINBlockString
self.fixed, # bmPINBlockString
0x00, # bmPINLengthFormat
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
self.pinmin, # wPINMaxExtraDigit High (PINmin)
@@ -112,9 +123,13 @@ class Card(object):
0x00, # bMsgIndex
0x00, # bTeoPrologue[0]
0x00, # bTeoPrologue[1]
0x00 # bTeoPrologue[2]
]
apdu += self.possibly_add_dummy_byte()
if self.fixed > 0:
apdu += [ self.fixed ]
apdu += [ 255 ] * self.fixed
else:
apdu += self.possibly_add_dummy_byte()
pin_verify += [ len(apdu) ] # bTeoPrologue[2]
pin_verify += [ len(apdu), 0, 0, 0 ] + apdu
data = self.connection.control(self.verify_ioctl,pin_verify)
sw1 = data[0]
@@ -128,10 +143,10 @@ class Card(object):
pin_modify = [ 0x00, # bTimerOut
0x00, # bTimerOut2
0x82, # bmFormatString: Byte, pos=0, left, ASCII.
0x00, # bmPINBlockString
self.fixed, # bmPINBlockString
0x00, # bmPINLengthFormat
0x00, # bInsertionOffsetOld
0x00, # bInsertionOffsetNew
self.fixed, # bInsertionOffsetNew
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
self.pinmin, # wPINMaxExtraDigit High (PINmin)
confirm_pin_setting(single_step),
@@ -144,9 +159,13 @@ class Card(object):
0x02, # bMsgIndex3
0x00, # bTeoPrologue[0]
0x00, # bTeoPrologue[1]
0x00 # bTeoPrologue[2]
]
apdu += self.possibly_add_dummy_byte()
if self.fixed > 0:
apdu += [ 2*self.fixed ]
apdu += [ 255 ] * (2*self.fixed)
else:
apdu += self.possibly_add_dummy_byte()
pin_modify += [ len(apdu) ] # bTeoPrologue[2]
pin_modify += [ len(apdu), 0, 0, 0 ] + apdu
data = self.connection.control(self.modify_ioctl,pin_modify)
sw1 = data[0]
@@ -191,13 +210,27 @@ class Card(object):
self.send_modify_pinpad(apdu, is_exchange,
"cmd_change_reference_data_pinpad")
def main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps):
card = Card(add_a_byte, pinmin, pinmax)
COVADIS_VEGA_ALPHA="COVADIS VEGA-ALPHA (000000F5) 00 00"
# We need to set ifdDriverOptions in /etc/libccid_Info.plist:
#
# <key>ifdDriverOptions</key>
# <string>0x0001</string>
#
# 1: DRIVER_OPTION_CCID_EXCHANGE_AUTHORIZED
# the CCID Exchange command is allowed. You can use it through
# SCardControl(hCard, IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE, ...)
def main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps, fixed):
card = Card(add_a_byte, pinmin, pinmax, fixed)
card.connection.connect()
print "Reader/Token:", card.connection.getReader()
ident = card.connection.getReader()
print "Reader/Token:", ident
print "ATR:", toHexString( card.connection.getATR() )
if ident == COVADIS_VEGA_ALPHA:
card.cmd_vega_alpha_disable_empty_verify()
card.get_features()
card.cmd_select_openpgp()
@@ -280,6 +313,7 @@ def print_usage():
print "\t--put:\t\tsetup resetcode (admin PIN, new PIN twice)"
print "\t--put2::\t\tsetup resetcode (admin PIN:pinpad, new PIN:kbd)"
print " options:"
print "\t--fixed N:\tUse fixed length input"
print "\t--admin:\tby administrator\t\t\t[False]"
print "\t--add:\t\tadd a dummy byte at the end of APDU\t[False]"
print "\t--pinmin:\tspecify minimum length of PIN\t\t[6]"
@@ -302,6 +336,7 @@ if __name__ == '__main__':
pinmin = PIN_MIN_DEFAULT
pinmax = PIN_MAX_DEFAULT
change_by_two_steps = False
fixed=0
while len(sys.argv) >= 2:
option = sys.argv[1]
sys.argv.pop(1)
@@ -319,6 +354,9 @@ if __name__ == '__main__':
change_by_two_steps = True
elif option == '--add':
add_a_byte = True
elif option == '--fixed':
fixed = int(sys.argv[1])
sys.argv.pop(1)
elif option == '--pinmin':
pinmin = int(sys.argv[1])
sys.argv.pop(1)
@@ -337,7 +375,7 @@ if __name__ == '__main__':
exit(0)
else:
raise ValueError, option
main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps)
main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps, fixed)
# Failure
# 67 00: Wrong length; no further indication

70
tool/rsa.py Normal file
View File

@@ -0,0 +1,70 @@
from binascii import hexlify, unhexlify
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)
# 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 = '\x00' + '\x01' + string.ljust('', 256 - 19 - 32 - 3, '\xff') \
+ '\x00' + digestinfo
return int(hexlify(byte_repr), 16)
def compute_signature(key, digestinfo):
e = key[4]
p = key[5]
q = key[6]
n = key[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):
s = hex(i)[2:]
s = s.rstrip('L')
if len(s) & 1:
s = '0' + s
return string.rjust(unhexlify(s), 256, '\x00')
def get_raw_pubkey(key):
return key[0]

4
tool/rsa_example.key Normal file
View File

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

86
tool/sexp.py Normal file
View File

@@ -0,0 +1,86 @@
# SEXP (S-expressions) Basic Transport Support
#
# See: http://people.csail.mit.edu/rivest/sexp.html
#
"""
sexp.py - a library for SEXP
Copyright (C) 2013 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 re
WHITESPACE='[ \n\t\v\r\f]+'
re_ws = re.compile(WHITESPACE)
DIGITS='[0-9]+'
re_digit = re.compile(DIGITS)
def skip_whitespace(string, pos):
m = re_ws.match(string, pos)
if m:
return m.start()
else:
return pos
def sexp_match(string, ch, pos):
pos = skip_whitespace(string,pos)
if string[pos] == ch:
return pos+1
else:
raise ValueError("expect '%s'" % ch)
def sexp_parse_simple_string(string, pos):
pos = skip_whitespace(string,pos)
m = re_digit.match(string, pos)
if m:
length = int(string[m.start():m.end()],10)
pos = sexp_match(string, ':', m.end())
return (string[pos:pos+length], pos+length)
else:
raise ValueError('expect digit')
def sexp_parse_list(string,pos):
l = []
while True:
pos = skip_whitespace(string,pos)
if string[pos] == ')':
return (l, pos)
else:
(sexp, pos) = sexp_parse(string,pos)
l.append(sexp)
def sexp_parse(string, pos=0):
pos = skip_whitespace(string,pos)
if string[pos] == '(':
(l, pos) = sexp_parse_list(string,pos+1)
pos = sexp_match(string, ')', pos)
return (l, pos)
elif string[pos] == '[':
pos = skip_whitespace(string,pos)
(dsp, pos) = sexp_parse_simple_string(string,pos+1)
pos = sexp_match(string, ']', pos)
pos = skip_whitespace(string,pos)
(ss, pos) = sexp_parse_simple_string(string, pos)
return ((dsp, ss), pos)
else:
return sexp_parse_simple_string(string, pos)
def sexp(string):
(sexp, pos) = sexp_parse(string)
return sexp

View File

@@ -3,7 +3,7 @@
"""
stlinkv2.py - a tool to control ST-Link/V2
Copyright (C) 2012 Free Software Initiative of Japan
Copyright (C) 2012, 2013 Free Software Initiative of Japan
Author: NIIBE Yutaka <gniibe@fsij.org>
This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -165,12 +165,20 @@ class stlinkv2(object):
v = self.execute_get("\xf5\x00", 2)
return (v[1] * 256 + v[0])
def exit_from_debug_swd(self):
self.execute_put("\xf2\x21\x00")
time.sleep(1)
def exit_from_dfu(self):
self.__devhandle.bulkWrite(self.__bulkout, "\xf3\x07", self.__timeout)
self.execute_put("\xf3\x07\x00")
time.sleep(1)
def exit_from_debug_swim(self):
self.execute_put("\xf4\x01\x00")
time.sleep(1)
def enter_swd(self):
self.__devhandle.bulkWrite(self.__bulkout, "\xf2\x20\xa3", self.__timeout)
self.execute_put("\xf2\x20\xa3")
time.sleep(1)
def get_status(self):
@@ -208,6 +216,9 @@ class stlinkv2(object):
def write_reg(self, regno, value):
return self.execute_get("\xf2\x06" + pack('<BI', regno, value), 2)
def write_debug_reg(self, addr, value):
return self.execute_get("\xf2\x35" + pack('<II', addr, value), 2)
def run(self):
v = self.execute_get("\xf2\x09\x00", 2)
return (v[1] << 8) + v[0]
@@ -216,6 +227,11 @@ class stlinkv2(object):
v = self.execute_get("\xf2\x22\x00", 4)
return v[0] + (v[1]<<8) + (v[2]<<16) + (v[3]<<24)
def version(self):
v = self.execute_get("\xf1", 6)
val = (v[0] << 8) + v[1]
return ((val >> 12) & 0x0f, (val >> 6) & 0x3f, val & 0x3f)
# For FST-01-00 and FST-01: LED on, USB connect
def setup_gpio(self):
apb2enr = self.read_memory_u32(0x40021018)
@@ -356,7 +372,7 @@ class stlinkv2(object):
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
if (status & FLASH_SR_EOP) == 0:
raise OperationError("option bytes erase")
raise OperationFailure("option bytes erase")
def flash_write_internal(self, addr, data, off, size):
prog = gen_prog_flash_write(addr,size)
@@ -415,7 +431,7 @@ class stlinkv2(object):
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
if (status & FLASH_SR_EOP) == 0:
raise OperationError("flash erase all")
raise OperationFailure("flash erase all")
def flash_erase_page(self, addr):
self.write_memory_u32(FLASH_KEYR, FLASH_KEY1)
@@ -439,20 +455,30 @@ class stlinkv2(object):
self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK)
if (status & FLASH_SR_EOP) == 0:
raise OperationError("flash page erase")
raise OperationFailure("flash page erase")
def start(self):
mode = self.stl_mode()
if mode == 2:
return
elif mode != 1:
self.exit_from_debug_swd()
elif mode == 5:
self.exit_from_debug_swim()
elif mode != 1 and mode != 4:
self.exit_from_dfu()
mode = self.stl_mode()
print "Change ST-Link/V2 mode to: %04x" % mode
new_mode = self.stl_mode()
print "Change ST-Link/V2 mode %04x -> %04x" % (mode, new_mode)
self.enter_swd()
s = self.get_status()
if s != 0x0080:
raise ValueError("Status of core is not running.", s)
print "Status is %04x" % s
self.run()
s = self.get_status()
if s != 0x0080:
# DCB_DHCSR DBGKEY
self.write_debug_reg(0xE000EDF0, 0xA05F0000)
s = self.get_status()
if s != 0x0080:
raise ValueError("Status of core is not running.", s)
mode = self.stl_mode()
if mode != 2:
raise ValueError("Failed to switch debug mode.", mode)
@@ -511,6 +537,7 @@ def main(show_help, erase_only, no_protect, spi_flash_check,
if not stl:
raise ValueError("No ST-Link/V2 device found.", None)
print "ST-Link/V2 version info: %d %d %d" % stl.version()
stl.start()
core_id = stl.core_id()
chip_id = stl.read_memory_u32(0xE0042000)
@@ -523,17 +550,23 @@ def main(show_help, erase_only, no_protect, spi_flash_check,
print "ON"
else:
print "off"
option_bytes = stl.option_bytes_read()
print "Option bytes: %08x" % option_bytes
if (option_bytes & 0xff) == RDP_KEY:
ob_protection_enable = False
else:
ob_protection_enable = True
option_bytes = stl.option_bytes_read()
print "Option bytes: %08x" % option_bytes
if (option_bytes & 0xff) == RDP_KEY:
ob_protection_enable = False
else:
ob_protection_enable = True
stl.enter_debug()
status = stl.get_status()
if status != 0x0081:
raise ValueError("Status of core is not halt.", status)
print "Core does not halt, try API V2 halt."
# DCB_DHCSR DBGKEY|C_HALT|C_DEBUGEN
stl.write_debug_reg(0xE000EDF0, 0xA05F0003)
status = stl.get_status()
if status != 0x0081:
raise ValueError("Status of core is not halt.", status)
if protection:
if status_only:
@@ -543,6 +576,7 @@ def main(show_help, erase_only, no_protect, spi_flash_check,
raise OperationFailure("Flash ROM is protected")
else:
if not skip_blank_check:
stl.reset_sys()
blank = stl.blank_check()
print "Flash ROM blank check: %s" % blank
else:
@@ -564,6 +598,9 @@ def main(show_help, erase_only, no_protect, spi_flash_check,
stl.setup_gpio()
if unlock:
if option_bytes != 0xff:
stl.reset_sys()
stl.option_bytes_erase()
stl.reset_sys()
stl.option_bytes_write(OPTION_BYTES_ADDR,RDP_KEY)
stl.usb_disconnect()

112
tool/upgrade_by_passwd.py Executable file
View File

@@ -0,0 +1,112 @@
#! /usr/bin/python
"""
upgrade_by_passwd.py - a tool to install another firmware for Gnuk Token
which is just shipped from factory
Copyright (C) 2012, 2013 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 gnuk_token import *
import sys, binascii, time, os
import rsa
DEFAULT_PW3 = "12345678"
BY_ADMIN = 3
KEYNO_FOR_AUTH=2
def main(passwd, data_regnual, data_upgrade):
l = len(data_regnual)
if (l & 0x03) != 0:
data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), chr(0))
crc32code = crc32(data_regnual)
print "CRC32: %04x\n" % crc32code
data_regnual += pack('<I', crc32code)
rsa_key = rsa.read_key_from_file('rsa_example.key')
rsa_raw_pubkey = rsa.get_raw_pubkey(rsa_key)
gnuk = get_gnuk_device()
gnuk.cmd_verify(BY_ADMIN, passwd)
keyno = 0
gnuk.cmd_write_binary(1+keyno, rsa_raw_pubkey, False)
gnuk.cmd_select_openpgp()
challenge = gnuk.cmd_get_challenge()
digestinfo = binascii.unhexlify(SHA256_OID_PREFIX) + challenge
signed = rsa.compute_signature(rsa_key, digestinfo)
signed_bytes = rsa.integer_to_bytes_256(signed)
gnuk.cmd_external_authenticate(keyno, signed_bytes)
gnuk.stop_gnuk()
mem_info = gnuk.mem_info()
print "%08x:%08x" % mem_info
print "Downloading flash upgrade program..."
gnuk.download(mem_info[0], data_regnual)
print "Run flash upgrade program..."
gnuk.execute(mem_info[0] + len(data_regnual) - 4)
#
time.sleep(3)
gnuk.reset_device()
del gnuk
gnuk = None
#
print "Wait 3 seconds..."
time.sleep(3)
# Then, send upgrade program...
reg = None
for dev in gnuk_devices_by_vidpid():
try:
reg = regnual(dev)
print "Device: ", dev.filename
break
except:
pass
mem_info = reg.mem_info()
print "%08x:%08x" % mem_info
print "Downloading the program"
reg.download(mem_info[0], data_upgrade)
reg.protect()
reg.finish()
reg.reset_device()
return 0
if __name__ == '__main__':
if os.getcwd() != os.path.dirname(os.path.abspath(__file__)):
print "Please change working directory to: %s" % os.path.dirname(os.path.abspath(__file__))
exit(1)
passwd = DEFAULT_PW3
if len(sys.argv) > 1 and sys.argv[1] == '-p':
from getpass import getpass
passwd = getpass("Admin password: ")
sys.argv.pop(1)
filename_regnual = sys.argv[1]
filename_upgrade = sys.argv[2]
f = open(filename_regnual)
data_regnual = f.read()
f.close()
print "%s: %d" % (filename_regnual, len(data_regnual))
f = open(filename_upgrade)
data_upgrade = f.read()
f.close()
print "%s: %d" % (filename_upgrade, len(data_upgrade))
# First 4096-byte in data_upgrade is SYS, so, skip it.
main(passwd, data_regnual, data_upgrade[4096:])