Compare commits
77 Commits
release/1.
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93867d0c8b | ||
|
|
a1a112d213 | ||
|
|
e679b4da7a | ||
|
|
095f854fa8 | ||
|
|
f9292f1157 | ||
|
|
b619db59d2 | ||
|
|
8870d58b69 | ||
|
|
4abcddef93 | ||
|
|
ca46cc465c | ||
|
|
3cf5ed482f | ||
|
|
87d95d0864 | ||
|
|
428adc7ac6 | ||
|
|
cc185eabb5 | ||
|
|
80e3cda267 | ||
|
|
ee867794e7 | ||
|
|
a4f7386b8a | ||
|
|
7d4d6cff5e | ||
|
|
b09460f0ae | ||
|
|
4298809ffb | ||
|
|
b297cf22b6 | ||
|
|
f84f52156c | ||
|
|
1564a4fbe6 | ||
|
|
16610ca5c7 | ||
|
|
352f81a61f | ||
|
|
7c82839fba | ||
|
|
10c685be16 | ||
|
|
ed61ed980d | ||
|
|
7ff0baf5df | ||
|
|
46259ce63d | ||
|
|
0aca10f307 | ||
|
|
5f2a8b835c | ||
|
|
5213d9ab82 | ||
|
|
c12f331217 | ||
|
|
62b4369d2c | ||
|
|
9dde59867d | ||
|
|
eae955b15e | ||
|
|
7e8dd12654 | ||
|
|
8c91d2ef2e | ||
|
|
6b6913c676 | ||
|
|
3ad9373163 | ||
|
|
220d5c0307 | ||
|
|
de7f9f6417 | ||
|
|
a302585602 | ||
|
|
ea2191105f | ||
|
|
32094099dd | ||
|
|
9b71d70b73 | ||
|
|
77d06fb301 | ||
|
|
78b642507b | ||
|
|
90a11859dc | ||
|
|
ad9a901e1b | ||
|
|
500b12b60d | ||
|
|
4bfe087583 | ||
|
|
22d0cb689a | ||
|
|
fe6337f988 | ||
|
|
1a2560531d | ||
|
|
40e234b799 | ||
|
|
deccb981ad | ||
|
|
12bd1161a4 | ||
|
|
d72a0b7893 | ||
|
|
ee5b6a2a82 | ||
|
|
f9b43a67ee | ||
|
|
70846e8b81 | ||
|
|
f6df7701f9 | ||
|
|
b9772265cf | ||
|
|
254c521c6f | ||
|
|
d7c6b95ba1 | ||
|
|
2e7d93a556 | ||
|
|
db2d897c3f | ||
|
|
23a9fe3bdc | ||
|
|
e8f773d2f6 | ||
|
|
6b5fc04c0d | ||
|
|
39bee2ee01 | ||
|
|
f39380d3aa | ||
|
|
0d36a58804 | ||
|
|
eb0e913eee | ||
|
|
7575dda42a | ||
|
|
61ec9b7ed7 |
330
ChangeLog
330
ChangeLog
@@ -1,3 +1,333 @@
|
||||
2013-03-15 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* Version 1.0.4.
|
||||
* src/usb_desc.c (gnukStringSerial): Updated.
|
||||
|
||||
* regnual/regnual.ld (.bss): Put at RAM1. This makes reGNUal can
|
||||
be loaded on the lower address.
|
||||
|
||||
* regnual/sys.c (entry): Don't change SP. Put alignment.
|
||||
|
||||
* regnual/regnual.c (usb_cb_get_descriptor): Fix adding break.
|
||||
|
||||
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.
|
||||
* src/usb_desc.c (gnukStringSerial): Updated.
|
||||
* src/main.c (ID_OFFSET): Fix.
|
||||
|
||||
2012-08-02 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* test/gnuk.py (gnuk_token.get_string): New.
|
||||
* test/features/991_version_string.feature: New.
|
||||
|
||||
2012-07-21 Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
* Version 1.0.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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> #################
|
||||
|
||||
77
NEWS
77
NEWS
@@ -1,5 +1,82 @@
|
||||
Gnuk NEWS - User visible changes
|
||||
|
||||
* Major changes in Gnuk 1.0.4
|
||||
|
||||
Released 2013-03-15, by NIIBE Yutaka
|
||||
|
||||
** Relocatable reGNUal, really
|
||||
In 1.0.3, reGNUal was not fully relocatable. It worked loaded on higher
|
||||
address, but didn't work for lower address. This was fixed.
|
||||
|
||||
|
||||
* 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
|
||||
|
||||
** USB SerialNumber String
|
||||
In 1.0, it has a bug for USB SerialNumber String. It has been fixed
|
||||
in 1.0.1.
|
||||
|
||||
|
||||
* Major changes in Gnuk 1.0
|
||||
|
||||
Released 2012-07-21, by NIIBE Yutaka
|
||||
|
||||
70
README
70
README
@@ -1,7 +1,7 @@
|
||||
Gnuk - An Implementation of USB Cryptographic Token for GnuPG
|
||||
|
||||
Version 1.0
|
||||
2012-07-21
|
||||
Version 1.0.4
|
||||
2013-03-15
|
||||
Niibe Yutaka
|
||||
Free Software Initiative of Japan
|
||||
|
||||
@@ -14,9 +14,9 @@ STM32F103 processor.
|
||||
|
||||
I wish that Gnuk will be a developer's soother who uses GnuPG. I have
|
||||
been nervous of storing secret key(s) on usual secondary storage.
|
||||
There is a solution with OpenPGP card, but it is not the choice for me
|
||||
to bring a card reader all the time. With Gnuk, this issue will be
|
||||
solved by a USB token which is small enough.
|
||||
There is a solution with OpenPGP card, but it is not the choice for
|
||||
me, as card reader is not common device. With Gnuk, this issue will
|
||||
be solved by a USB token.
|
||||
|
||||
Please look at the graphics of "gnuk.svg" for the software name. My
|
||||
son used to be with his NUK(R), always, everywhere. Now, I am with a
|
||||
@@ -35,14 +35,14 @@ A0: Good points of Gnuk are:
|
||||
Gnuk Token cheaper (see Q8-A8).
|
||||
* You can study Gnuk to modify and to enhance. For example, you
|
||||
can implement your own authentication method with some sensor
|
||||
such as acceleration sensor.
|
||||
such as an acceleration sensor.
|
||||
* It is "of Free Software"; Gnuk is distributed under GPLv3+,
|
||||
"by Free Software"; Gnuk development requires only Free Software
|
||||
(GNU Toolchain, Python, etc.),
|
||||
"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.
|
||||
@@ -51,8 +51,8 @@ Q3: What's your recommendation for target board?
|
||||
A3: Orthodox choice is Olimex STM32-H103.
|
||||
If you have skill of electronics and like DIY, STM32 part of STM8S
|
||||
Discovery Kit might be the best choice.
|
||||
Currently FST-01 (Flying Stone Tiny 01) is under development,
|
||||
it will be the best choice, hopefully.
|
||||
FST-01 (Flying Stone Tiny 01) will be soon available for sale,
|
||||
and it will be the best choice, hopefully.
|
||||
|
||||
Q4: What's version of GnuPG are you using?
|
||||
A4: In Debian GNU/Linux system, I use gnupg 1.4.11-3 and gnupg-agent
|
||||
@@ -63,12 +63,13 @@ Q5: What's version of pcscd and libccid are you using?
|
||||
A5: In Debian GNU/Linux system, I use pcscd 1.5.5-4 and libccid 1.3.11-2,
|
||||
which is in squeeze. Note that you need to edit /etc/libccid_Info.plist
|
||||
when using libccid (< 1.4.1).
|
||||
Note that pcscd and libccid are optional, you can use Gnuk without them.
|
||||
|
||||
Q6: What kinds of hardware is required for development?
|
||||
A6: You need a target board plus a JTAG debugger. If you just want to
|
||||
test Gnuk for target boards with DfuSe, JTAG debugger is not
|
||||
the requirement. Note that for real use, you need JTAG debugger
|
||||
to enable flash ROM protection.
|
||||
A6: You need a target board plus a JTAG/SWD debugger. If you just
|
||||
want to test Gnuk for target boards with DfuSe, JTAG debugger is
|
||||
not the requirement. Note that for real use, you need JTAG/SWD
|
||||
debugger to enable flash ROM protection.
|
||||
|
||||
Q7: How much does it cost?
|
||||
A7: Olimex STM32-H103 plus ARM-USB-TINY-H cost 70 Euro or so.
|
||||
@@ -107,17 +108,23 @@ 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: STLink 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 version 1.0 release of Gnuk, after a year and eleven months
|
||||
development. While it is daily use for a year or so, some newly
|
||||
introduced features (including key generation and firmware upgrade)
|
||||
should be considered experimental.
|
||||
This is a fourth minor release in version 1.0 series of Gnuk.
|
||||
|
||||
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.
|
||||
|
||||
Tested features are:
|
||||
|
||||
@@ -172,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
|
||||
@@ -259,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:
|
||||
@@ -292,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
|
||||
@@ -337,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':
|
||||
@@ -656,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:
|
||||
|
||||
@@ -673,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
5
THANKS
@@ -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
|
||||
|
||||
@@ -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
3
doc/__update_web
Normal 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/
|
||||
@@ -41,7 +41,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Gnuk Documentation'
|
||||
copyright = u'2012, Niibe Yutaka'
|
||||
copyright = u'2012, NIIBE Yutaka'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -184,7 +184,7 @@ latex_elements = {
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'GnukDocumentation.tex', u'Gnuk Documentation Documentation',
|
||||
u'Niibe Yutaka', 'manual'),
|
||||
u'NIIBE Yutaka', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -214,7 +214,7 @@ latex_documents = [
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'gnukdocumentation', u'Gnuk Documentation Documentation',
|
||||
[u'Niibe Yutaka'], 1)
|
||||
[u'NIIBE Yutaka'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@@ -228,7 +228,7 @@ man_pages = [
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'GnukDocumentation', u'Gnuk Documentation Documentation',
|
||||
u'Niibe Yutaka', 'GnukDocumentation', 'One line description of project.',
|
||||
u'NIIBE Yutaka', 'GnukDocumentation', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
@@ -5,7 +5,27 @@ Development Environment
|
||||
Hardware
|
||||
--------
|
||||
|
||||
JTAG debugger or SWD debugger is required.
|
||||
For development, it is highly recommended to have JTAG/SWD debugger.
|
||||
|
||||
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
|
||||
implementations to disable reading-out from flash ROM. It means
|
||||
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
|
||||
tool/stlinkv2.py.
|
||||
|
||||
|
||||
OpenOCD
|
||||
-------
|
||||
|
||||
For JTAG/SWD debugger, we can use OpenOCD.
|
||||
|
||||
Note that ST-Link/V2 is *not* supported by OpenOCD 0.5.0. It is
|
||||
supported by version 0.6 or later.
|
||||
|
||||
|
||||
GNU Toolchain
|
||||
@@ -13,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:
|
||||
@@ -23,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
|
||||
-------------
|
||||
@@ -50,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:
|
||||
|
||||
|
||||
311
doc/generating-2048-RSA-key.rst
Normal file
311
doc/generating-2048-RSA-key.rst
Normal file
@@ -0,0 +1,311 @@
|
||||
============================
|
||||
Generating 2048-bit RSA keys
|
||||
============================
|
||||
|
||||
In this section, we describe how to generate 2048-bit RSA keys.
|
||||
|
||||
|
||||
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. ::
|
||||
|
||||
$ gpg --gen-key
|
||||
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.
|
||||
|
||||
and GnuPG asks kind of key. Select ``RSA and RSA``. ::
|
||||
|
||||
Please select what kind of key you want:
|
||||
(1) RSA and RSA (default)
|
||||
(2) DSA and Elgamal
|
||||
(3) DSA (sign only)
|
||||
(4) RSA (sign only)
|
||||
Your selection? 1
|
||||
RSA keys may be between 1024 and 4096 bits long.
|
||||
|
||||
and select 2048-bit (as Gnuk Token only supports this). ::
|
||||
|
||||
What keysize do you want? (2048)
|
||||
Requested keysize is 2048 bits
|
||||
|
||||
and select expiration of the key. ::
|
||||
|
||||
Please specify how long the key should be valid.
|
||||
0 = key does not expire
|
||||
<n> = key expires in n days
|
||||
<n>w = key expires in n weeks
|
||||
<n>m = key expires in n months
|
||||
<n>y = key expires in n years
|
||||
Key is valid for? (0) 0
|
||||
Key does not expire at all
|
||||
|
||||
Confirm key types, bitsize and expiration. ::
|
||||
|
||||
Is this correct? (y/N) y
|
||||
|
||||
Then enter user ID. ::
|
||||
|
||||
You need a user ID to identify your key; the software constructs the user ID
|
||||
from the Real Name, Comment and Email Address in this form:
|
||||
"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"
|
||||
|
||||
Real name: Niibe Yutaka
|
||||
Email address: gniibe@fsij.org
|
||||
Comment:
|
||||
You selected this USER-ID:
|
||||
"Niibe Yutaka <gniibe@fsij.org>"
|
||||
|
||||
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
|
||||
|
||||
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>
|
||||
|
||||
Then, GnuPG generate keys. It takes some time. ::
|
||||
|
||||
We need to generate a lot of random bytes. It is a good idea to perform
|
||||
some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
...+++++
|
||||
+++++
|
||||
We need to generate a lot of random bytes. It is a good idea to perform
|
||||
some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
..+++++
|
||||
|
||||
Not enough random bytes available. Please do some other work to give
|
||||
the OS a chance to collect more entropy! (Need 15 more bytes)
|
||||
...+++++
|
||||
gpg: key 28C0CD7C marked as ultimately trusted
|
||||
public and secret key created and signed.
|
||||
|
||||
gpg: checking the trustdb
|
||||
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
|
||||
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/084239CF 2010-10-15
|
||||
$
|
||||
|
||||
Done.
|
||||
|
||||
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 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/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: ultimate validity: ultimate
|
||||
sub 2048R/084239CF created: 2010-10-15 expires: never usage: E
|
||||
[ultimate] (1). Niibe Yutaka <gniibe@fsij.org>
|
||||
|
||||
gpg>
|
||||
|
||||
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 4CA7BABE, created 2010-10-15
|
||||
<PASSWORD-KEY-ON-PC>
|
||||
gpg: gpg-agent is not available in this session
|
||||
|
||||
GnuPG asks kind of key. We select ``RSA (set your own capabilities)``. ::
|
||||
|
||||
Please select what kind of key you want:
|
||||
(3) DSA (sign only)
|
||||
(4) RSA (sign only)
|
||||
(5) Elgamal (encrypt only)
|
||||
(6) RSA (encrypt only)
|
||||
(7) DSA (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 ``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
|
||||
|
||||
(S) Toggle the sign capability
|
||||
(E) Toggle the encrypt capability
|
||||
(A) Toggle the authenticate capability
|
||||
(Q) Finished
|
||||
|
||||
Your selection? s
|
||||
|
||||
Possible actions for a RSA key: Sign Encrypt Authenticate
|
||||
Current allowed actions: Encrypt
|
||||
|
||||
(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:
|
||||
|
||||
(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, we set the capability of ``Authenticate``.
|
||||
We enter ``q`` to finish setting capabilities. ::
|
||||
|
||||
Your selection? q
|
||||
|
||||
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)
|
||||
Requested keysize is 2048 bits
|
||||
Please specify how long the key should be valid.
|
||||
0 = key does not expire
|
||||
<n> = key expires in n days
|
||||
<n>w = key expires in n weeks
|
||||
<n>m = key expires in n months
|
||||
<n>y = key expires in n years
|
||||
Key is valid for? (0) 0
|
||||
Key does not expire at all
|
||||
Is this correct? (y/N) y
|
||||
Really create? (y/N) y
|
||||
|
||||
Then, GnuPG generate the key. ::
|
||||
|
||||
We need to generate a lot of random bytes. It is a good idea to perform
|
||||
some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
.......+++++
|
||||
+++++
|
||||
|
||||
pub 2048R/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: ultimate validity: ultimate
|
||||
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>
|
||||
|
||||
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).
|
||||
|
||||
|
||||
Publishing public key
|
||||
=====================
|
||||
|
||||
We make a file for the public key by ``--export`` option of GnuPG. ::
|
||||
|
||||
$ gpg --armor --output <YOUR-KEY>.asc --export <YOUR-KEY-ID>
|
||||
|
||||
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
|
||||
38
doc/gnome3-gpg-settings.rst
Normal file
38
doc/gnome3-gpg-settings.rst
Normal file
@@ -0,0 +1,38 @@
|
||||
==========================
|
||||
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.
|
||||
|
||||
.. _GnuPG settings: gpg-settings
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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 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".
|
||||
|
||||
|
||||
Now, I use gpg-agent for GnuPG Agent and SSH agent with Gnuk Token.
|
||||
183
doc/gnuk-keytocard-noremoval.rst
Normal file
183
doc/gnuk-keytocard-noremoval.rst
Normal file
@@ -0,0 +1,183 @@
|
||||
=============================================
|
||||
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.
|
||||
|
||||
The difference is just not-to-save changes after key imports.
|
||||
|
||||
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. ::
|
||||
|
||||
$ gpg --homedir=/home/gniibe/tmp/gnuk-testing-dir --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/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: ultimate validity: ultimate
|
||||
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>
|
||||
|
||||
|
||||
Then, GnuPG enters its own command interaction mode. The prompt is ``gpg>``.
|
||||
To enable ``keytocard`` command, I type ``toggle`` command. ::
|
||||
|
||||
gpg> toggle
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
Firstly, I import my primary key into Gnuk Token.
|
||||
I type ``keytocard`` command, answer ``y`` to confirm keyimport,
|
||||
and type ``1`` to say it's signature key. ::
|
||||
|
||||
gpg> keytocard
|
||||
Really move the primary key? (y/N) y
|
||||
gpg: detected reader `FSIJ Gnuk (0.12-38FF6A06) 00 00'
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
|
||||
Please select where to store the key:
|
||||
(1) 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.
|
||||
|
||||
I enter these passwords. ::
|
||||
|
||||
You need a passphrase to unlock the secret key for
|
||||
user: "NIIBE Yutaka <gniibe@fsij.org>"
|
||||
2048-bit RSA key, ID 4CA7BABE, created 2010-10-15
|
||||
<PASSWORD-KEY-4CA7BABE>
|
||||
gpg: writing new key
|
||||
gpg: 3 Admin PIN attempts remaining before card is permanently locked
|
||||
|
||||
Please enter the Admin PIN
|
||||
Enter Admin PIN: <PASSWORD-GNUK>
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
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.
|
||||
|
||||
Secondly, I import my subkey of encryption. I select key number '1'. ::
|
||||
|
||||
gpg> key 1
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/084239CF created: 2010-10-15 expires: never
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(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. ::
|
||||
|
||||
gpg> keytocard
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
|
||||
Please select where to store the key:
|
||||
(2) Encryption key
|
||||
Your selection? 2
|
||||
|
||||
Then, GnuPG asks the passphrase of **keys on PC** again. I enter. ::
|
||||
|
||||
You need a passphrase to unlock the secret key for
|
||||
user: "NIIBE Yutaka <gniibe@fsij.org>"
|
||||
2048-bit RSA key, ID 084239CF, created 2010-10-15
|
||||
<PASSWORD-KEY-4CA7BABE>
|
||||
gpg: writing new key
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
The sub key is now on the Token and GnuPG says its card-no for it.
|
||||
|
||||
I type ``key 1`` to deselect key number '1'. ::
|
||||
|
||||
gpg> key 1
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
Thirdly, I select sub key of authentication which has key number '2'. ::
|
||||
|
||||
gpg> key 2
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(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. ::
|
||||
|
||||
gpg> keytocard
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
|
||||
Please select where to store the key:
|
||||
(3) Authentication key
|
||||
Your selection? 3
|
||||
|
||||
Then, GnuPG asks the passphrase of **keys on PC** again. I enter. ::
|
||||
|
||||
You need a passphrase to unlock the secret key for
|
||||
user: "NIIBE Yutaka <gniibe@fsij.org>"
|
||||
2048-bit RSA key, ID 5BB065DC, created 2010-10-22
|
||||
<PASSWORD-KEY-4CA7BABE>
|
||||
gpg: writing new key
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
card-no: F517 00000001
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
The sub key is now on the Token and GnuPG says its card-no for it.
|
||||
|
||||
Lastly, I quit GnuPG. Note that I **don't** save changes. ::
|
||||
|
||||
gpg> quit
|
||||
Save changes? (y/N) n
|
||||
Quit without saving? (y/N) y
|
||||
$
|
||||
|
||||
All keys are imported to Gnuk Token now.
|
||||
193
doc/gnuk-keytocard.rst
Normal file
193
doc/gnuk-keytocard.rst
Normal file
@@ -0,0 +1,193 @@
|
||||
================================
|
||||
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.
|
||||
|
||||
Note that there is **no ways** to export keys from the Token,
|
||||
so please be careful.
|
||||
|
||||
|
||||
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
|
||||
|
||||
See `another document`_ to import keys to the Token from copied directory.
|
||||
|
||||
.. _another document: gnuk-keytocard-noremoval
|
||||
|
||||
After personalization, I put my keys into the Token.
|
||||
|
||||
Here is the log.
|
||||
|
||||
I invoke GnuPG with my key (4ca7babe). ::
|
||||
|
||||
$ gpg --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/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: ultimate validity: ultimate
|
||||
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>
|
||||
|
||||
|
||||
Then, GnuPG enters its own command interaction mode. The prompt is ``gpg>``.
|
||||
To enable ``keytocard`` command, I type ``toggle`` command. ::
|
||||
|
||||
gpg> toggle
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
Firstly, I import my primary key into Gnuk Token.
|
||||
I type ``keytocard`` command, answer ``y`` to confirm keyimport,
|
||||
and type ``1`` to say it's signature key. ::
|
||||
|
||||
gpg> keytocard
|
||||
Really move the primary key? (y/N) y
|
||||
gpg: detected reader `FSIJ Gnuk (0.12-38FF6A06) 00 00'
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
|
||||
Please select where to store the key:
|
||||
(1) 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.
|
||||
|
||||
I enter these passwords. ::
|
||||
|
||||
You need a passphrase to unlock the secret key for
|
||||
user: "NIIBE Yutaka <gniibe@fsij.org>"
|
||||
2048-bit RSA key, ID 4CA7BABE, created 2010-10-15
|
||||
<PASSWORD-KEY-4CA7BABE>
|
||||
gpg: writing new key
|
||||
gpg: 3 Admin PIN attempts remaining before card is permanently locked
|
||||
|
||||
Please enter the Admin PIN
|
||||
Enter Admin PIN: <PASSWORD-GNUK>
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
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.
|
||||
|
||||
Secondly, I import my subkey of encryption. I select key number '1'. ::
|
||||
|
||||
gpg> key 1
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/084239CF created: 2010-10-15 expires: never
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(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. ::
|
||||
|
||||
gpg> keytocard
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
|
||||
Please select where to store the key:
|
||||
(2) Encryption key
|
||||
Your selection? 2
|
||||
|
||||
Then, GnuPG asks the passphrase of **keys on PC** again. I enter. ::
|
||||
|
||||
You need a passphrase to unlock the secret key for
|
||||
user: "NIIBE Yutaka <gniibe@fsij.org>"
|
||||
2048-bit RSA key, ID 084239CF, created 2010-10-15
|
||||
<PASSWORD-KEY-4CA7BABE>
|
||||
gpg: writing new key
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
The sub key is now on the Token and GnuPG says its card-no for it.
|
||||
|
||||
I type ``key 1`` to deselect key number '1'. ::
|
||||
|
||||
gpg> key 1
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
Thirdly, I select sub key of authentication which has key number '2'. ::
|
||||
|
||||
gpg> key 2
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
(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. ::
|
||||
|
||||
gpg> keytocard
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
|
||||
Please select where to store the key:
|
||||
(3) Authentication key
|
||||
Your selection? 3
|
||||
|
||||
Then, GnuPG asks the passphrase of **keys on PC** again. I enter. ::
|
||||
|
||||
You need a passphrase to unlock the secret key for
|
||||
user: "NIIBE Yutaka <gniibe@fsij.org>"
|
||||
2048-bit RSA key, ID 5BB065DC, created 2010-10-22
|
||||
<PASSWORD-KEY-4CA7BABE>
|
||||
gpg: writing new key
|
||||
|
||||
sec 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb* 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
card-no: F517 00000001
|
||||
(1) NIIBE Yutaka <gniibe@fsij.org>
|
||||
|
||||
The sub key is now on the Token and GnuPG says its card-no for it.
|
||||
|
||||
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.
|
||||
153
doc/gnuk-personalization.rst
Normal file
153
doc/gnuk-personalization.rst
Normal file
@@ -0,0 +1,153 @@
|
||||
=============================
|
||||
Personalization of Gnuk Token
|
||||
=============================
|
||||
|
||||
|
||||
Personalize your Gnuk Token
|
||||
===========================
|
||||
|
||||
Invoke GnuPG with the option ``--card-edit``. ::
|
||||
|
||||
$ gpg --card-edit
|
||||
gpg: detected reader `FSIJ Gnuk (0.12-34006E06) 00 00'
|
||||
Application ID ...: D276000124010200F517000000010000
|
||||
Version ..........: 2.0
|
||||
Manufacturer .....: FSIJ
|
||||
Serial number ....: 00000001
|
||||
Name of cardholder: [not set]
|
||||
Language prefs ...: [not set]
|
||||
Sex ..............: unspecified
|
||||
URL of public key : [not set]
|
||||
Login data .......: [not set]
|
||||
Signature PIN ....: forced
|
||||
Key attributes ...: 2048R 2048R 2048R
|
||||
Max. PIN lengths .: 127 127 127
|
||||
PIN retry counter : 3 3 3
|
||||
Signature counter : 0
|
||||
Signature key ....: [none]
|
||||
Encryption key....: [none]
|
||||
Authentication key: [none]
|
||||
General key info..: [none]
|
||||
|
||||
It shows the status of the card (as same as the output of ``gpg --card-status``). It shows token's name and its USB serial string (0.12-34006E06) from PC/SC-lite.
|
||||
|
||||
Then, GnuPG enters its own command interaction mode. The prompt is ``gpg/card>``.
|
||||
|
||||
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
|
||||
|
||||
Please enter the PIN
|
||||
Enter PIN: 123456
|
||||
|
||||
New PIN
|
||||
Enter New PIN: <PASSWORD-OF-GNUK>
|
||||
|
||||
New PIN
|
||||
Repeat this PIN: <PASSWORD-OF-GNUK>
|
||||
PIN changed.
|
||||
|
||||
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
|
||||
|
||||
gpg/card> name
|
||||
Cardholder's surname: Niibe
|
||||
Cardholder's given name: Yutaka
|
||||
gpg: 3 Admin PIN attempts remaining before card is permanently locked
|
||||
|
||||
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. ::
|
||||
|
||||
gpg/card> lang
|
||||
Language preferences: ja
|
||||
|
||||
gpg/card> sex
|
||||
Sex ((M)ale, (F)emale or space): m
|
||||
|
||||
gpg/card> url
|
||||
URL to retrieve public key: http://www.gniibe.org/gniibe.asc
|
||||
|
||||
gpg/card> login
|
||||
Login data (account name): gniibe
|
||||
|
||||
Since I don't force PIN input everytime,
|
||||
toggle it to non-force-pin-for-signature. ::
|
||||
|
||||
gpg/card> forcesig
|
||||
|
||||
Lastly, I setup reset code. This is optional. ::
|
||||
|
||||
gpg/card> passwd
|
||||
gpg: OpenPGP card no. D276000124010200F517000000010000 detected
|
||||
|
||||
1 - change PIN
|
||||
2 - unblock PIN
|
||||
3 - change Admin PIN
|
||||
4 - set the Reset Code
|
||||
Q - quit
|
||||
|
||||
Your selection? 4
|
||||
gpg: 3 Admin PIN attempts remaining before card is permanently locked
|
||||
|
||||
Please enter the Admin PIN
|
||||
Enter Admin PIN: <PASSWORD-OF-GNUK>
|
||||
|
||||
New Reset Code
|
||||
Enter New PIN: <RESETCODE-OF-GNUK>
|
||||
|
||||
New Reset Code
|
||||
Repeat this PIN: <RESETCODE-OF-GNUK>
|
||||
Reset Code set.
|
||||
|
||||
1 - change PIN
|
||||
2 - unblock PIN
|
||||
3 - change Admin PIN
|
||||
4 - set the Reset Code
|
||||
Q - quit
|
||||
|
||||
Your selection? q
|
||||
|
||||
Then, I quit. ::
|
||||
|
||||
gpg/card> quit
|
||||
|
||||
That's all.
|
||||
43
doc/gnuk-token-initial-configuration.rst
Normal file
43
doc/gnuk-token-initial-configuration.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
===================================
|
||||
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
|
||||
==========
|
||||
|
||||
I assume you are using GNU/Linux.
|
||||
|
||||
|
||||
Preparation
|
||||
===========
|
||||
|
||||
Make sure there is no ``scdaemon`` for configuring Gnuk Token. You can kill ``scdaemon`` by: ::
|
||||
|
||||
$ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye
|
||||
|
||||
|
||||
Serial Number (optional)
|
||||
========================
|
||||
|
||||
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_libusb.py`` examines environment variable of ``EMAIL``, and writes corresponding serial number to Gnuk Token. ::
|
||||
|
||||
$ ../tool/gnuk_put_binary_libusb.py -s ../GNUK_SERIAL_NUMBER
|
||||
Writing serial number
|
||||
Device: 006
|
||||
Configuration: 1
|
||||
Interface: 0
|
||||
d2 76 00 01 24 01 02 00 f5 17 00 00 00 01 00 00
|
||||
|
||||
|
||||
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.
|
||||
43
doc/gpg-settings.rst
Normal file
43
doc/gpg-settings.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
.. -*- coding: utf-8 -*-
|
||||
|
||||
==============
|
||||
GnuPG settings
|
||||
==============
|
||||
|
||||
Here is my GnuPG settings.
|
||||
|
||||
.gnupg/gpg.conf
|
||||
===============
|
||||
|
||||
I create ``.gnupg/gpg.conf`` file with the following content. ::
|
||||
|
||||
use-agent
|
||||
personal-digest-preferences SHA256
|
||||
cert-digest-algo SHA256
|
||||
default-preference-list SHA512 SHA384 SHA256 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
|
||||
|
||||
default-key 0x4ca7babe
|
||||
|
||||
|
||||
Let gpg-agent manage 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
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
* `Creating a new GPG key`_
|
||||
* `Use OpenPGP Keys for OpenSSH, how to use gpg with ssh`_
|
||||
|
||||
.. _Creating a new GPG key: http://keyring.debian.org/creating-key.html
|
||||
.. _Use OpenPGP Keys for OpenSSH, how to use gpg with ssh: http://www.programmierecke.net/howto/gpg-ssh.html
|
||||
BIN
doc/images/gnuk-sticker.png
Normal file
BIN
doc/images/gnuk-sticker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -2,6 +2,9 @@
|
||||
sphinx-quickstart on Wed Jul 4 15:29:05 2012.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
Copyright (C) 2012 NIIBE Yutaka
|
||||
Copyright (C) 2012 Free Software Initiative of Japan
|
||||
This document is licensed under a CC-BY-SA 3.0 Unported License
|
||||
|
||||
Gnuk Documentation
|
||||
==================
|
||||
@@ -13,6 +16,16 @@ Contents:
|
||||
|
||||
intro.rst
|
||||
development.rst
|
||||
stop-scdaemon.rst
|
||||
udev-rules.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
|
||||
gpg-settings.rst
|
||||
gnome3-gpg-settings.rst
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
||||
@@ -13,17 +13,41 @@ STM32F103 processor.
|
||||
Cryptographic token and feature of Gnuk
|
||||
---------------------------------------
|
||||
|
||||
Cryptographic token is a store of private keys and it computes cryptographic functions on the device.
|
||||
Cryptographic token is a store of private keys and it computes cryptographic
|
||||
functions on the device.
|
||||
|
||||
The idea is to separate important secrets to independent device,
|
||||
from where nobody can extract them.
|
||||
|
||||
|
||||
Development Environment
|
||||
-----------------------
|
||||
|
||||
See :doc:`development` for development environment for Gnuk. It builds on Free Software.
|
||||
See :doc:`development` for development environment for Gnuk.
|
||||
Gnuk is developed on the environment where there are only Free Software.
|
||||
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
Target boards for running Gnuk
|
||||
------------------------------
|
||||
|
||||
Hardware requirement for Gnuk is the micro controller STM32F103.
|
||||
In version 1.0, Gnuk supports following boards.
|
||||
|
||||
* FST-01 (Flying Stone Tiny ZERO-ONE)
|
||||
|
||||
* Olimex STM32-H103
|
||||
|
||||
* CQ STARM
|
||||
|
||||
* STBee
|
||||
|
||||
* STBee Mini
|
||||
|
||||
* STM32 part of STM8S Discovery Kit
|
||||
|
||||
|
||||
Host prerequisites for using Gnuk Token
|
||||
---------------------------------------
|
||||
|
||||
* GNU Privacy Guard (GnuPG)
|
||||
|
||||
@@ -36,8 +60,8 @@ Prerequisites
|
||||
* Web: scute, firefox
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
Usages
|
||||
------
|
||||
|
||||
* Sign with GnuPG
|
||||
* Decrypt with GnuPG
|
||||
|
||||
@@ -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
|
||||
|
||||
37
doc/stop-scdaemon.rst
Normal file
37
doc/stop-scdaemon.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
===========================
|
||||
Stopping/Resetting SCDAEMON
|
||||
===========================
|
||||
|
||||
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
|
||||
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
|
||||
===========================
|
||||
|
||||
We have a utility to communicate with a running gpg-agent, that's
|
||||
gpg-connect-agent. We can use it to communicate with scdaemon,
|
||||
as it supports sub-command "SCD", exactly for this purpose.
|
||||
|
||||
|
||||
Stopping SCDAEMON
|
||||
=================
|
||||
|
||||
To stop SCDAEMON and let it exit, type::
|
||||
|
||||
$ gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye
|
||||
|
||||
Then, you can confirm that there is no SCDAEMON any more by ``ps``
|
||||
command.
|
||||
|
||||
|
||||
Let GPG-AGENT/SCDAEMON learn
|
||||
============================
|
||||
|
||||
To let gpg-agent/scdaemon learn, type::
|
||||
|
||||
$ gpg-connect-agent learn /bye
|
||||
51
doc/udev-rules.rst
Normal file
51
doc/udev-rules.rst
Normal file
@@ -0,0 +1,51 @@
|
||||
===============================================
|
||||
Device Configuration for Gnuk Token with libusb
|
||||
===============================================
|
||||
|
||||
In order to use Gnuk Token with libusb, configuration of device is
|
||||
needed for permissions. Note that this is not needed for the case of
|
||||
PC/SC Lite, as it has its own device configuration.
|
||||
|
||||
|
||||
udev rules for Gnuk Token
|
||||
=========================
|
||||
|
||||
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
|
||||
@@ -10,4 +10,7 @@
|
||||
ATTR{idVendor}=="04e6", ATTR{idProduct}=="5115", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"
|
||||
ATTR{idVendor}=="20a0", ATTR{idProduct}=="4107", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"
|
||||
|
||||
+# Gnuk
|
||||
+ATTR{idVendor}=="234b", ATTR{idProduct}=="0000", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"
|
||||
+
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
udev rules for ST-Link/V2
|
||||
=========================
|
||||
|
||||
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.
|
||||
178
doc/using-gnuk-token-with-another-computer.rst
Normal file
178
doc/using-gnuk-token-with-another-computer.rst
Normal file
@@ -0,0 +1,178 @@
|
||||
======================================
|
||||
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).
|
||||
|
||||
Note that the Token only brings your secret keys,
|
||||
while ``.gnupg`` directory contains keyrings and trustdb, too.
|
||||
|
||||
|
||||
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``.
|
||||
|
||||
To do that, invoke GnuPG with ``--card-edit`` option. ::
|
||||
|
||||
$ gpg --card-edit
|
||||
gpg: detected reader `FSIJ Gnuk (0.12-37006A06) 00 00'
|
||||
Application ID ...: D276000124010200F517000000010000
|
||||
Version ..........: 2.0
|
||||
Manufacturer .....: FSIJ
|
||||
Serial number ....: 00000001
|
||||
Name of cardholder: Yutaka Niibe
|
||||
Language prefs ...: ja
|
||||
Sex ..............: male
|
||||
URL of public key : http://www.gniibe.org/gniibe.asc
|
||||
Login data .......: gniibe
|
||||
Signature PIN ....: not forced
|
||||
Key attributes ...: 2048R 2048R 2048R
|
||||
Max. PIN lengths .: 127 127 127
|
||||
PIN retry counter : 3 3 3
|
||||
Signature counter : 6
|
||||
Signature key ....: 1241 24BD 3B48 62AF 7A0A 42F1 00B4 5EBD 4CA7 BABE
|
||||
created ....: 2010-10-15 06:46:33
|
||||
Encryption key....: 42E1 E805 4E6F 1F30 26F2 DC79 79A7 9093 0842 39CF
|
||||
created ....: 2010-10-15 06:46:33
|
||||
Authentication key: B4D9 7142 C42D 6802 F5F7 4E70 9C33 B6BA 5BB0 65DC
|
||||
created ....: 2010-10-22 06:06:36
|
||||
General key info..: [none]
|
||||
|
||||
gpg/card>
|
||||
|
||||
It says, there is no key info related to this token on your PC (``[none]``).
|
||||
|
||||
Fetch the public key from URL specified in the Token. ::
|
||||
|
||||
gpg/card> fetch
|
||||
gpg: requesting key 4CA7BABE from http server www.gniibe.org
|
||||
gpg: key 4CA7BABE: public key "NIIBE Yutaka <gniibe@fsij.org>" imported
|
||||
gpg: no ultimately trusted keys found
|
||||
gpg: Total number processed: 1
|
||||
gpg: imported: 1 (RSA: 1)
|
||||
|
||||
gpg/card>
|
||||
|
||||
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. ::
|
||||
|
||||
gpg/card>
|
||||
|
||||
Application ID ...: D276000124010200F517000000010000
|
||||
Version ..........: 2.0
|
||||
Manufacturer .....: FSIJ
|
||||
Serial number ....: 00000001
|
||||
Name of cardholder: Yutaka Niibe
|
||||
Language prefs ...: ja
|
||||
Sex ..............: male
|
||||
URL of public key : http://www.gniibe.org/gniibe.asc
|
||||
Login data .......: gniibe
|
||||
Signature PIN ....: not forced
|
||||
Key attributes ...: 2048R 2048R 2048R
|
||||
Max. PIN lengths .: 127 127 127
|
||||
PIN retry counter : 3 3 3
|
||||
Signature counter : 6
|
||||
Signature key ....: 1241 24BD 3B48 62AF 7A0A 42F1 00B4 5EBD 4CA7 BABE
|
||||
created ....: 2010-10-15 06:46:33
|
||||
Encryption key....: 42E1 E805 4E6F 1F30 26F2 DC79 79A7 9093 0842 39CF
|
||||
created ....: 2010-10-15 06:46:33
|
||||
Authentication key: B4D9 7142 C42D 6802 F5F7 4E70 9C33 B6BA 5BB0 65DC
|
||||
created ....: 2010-10-22 06:06:36
|
||||
General key info..:
|
||||
pub 2048R/4CA7BABE 2010-10-15 NIIBE Yutaka <gniibe@fsij.org>
|
||||
sec> 2048R/4CA7BABE created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb> 2048R/084239CF created: 2010-10-15 expires: never
|
||||
card-no: F517 00000001
|
||||
ssb> 2048R/5BB065DC created: 2010-10-22 expires: never
|
||||
card-no: F517 00000001
|
||||
|
||||
gpg/card>
|
||||
|
||||
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.
|
||||
|
||||
To do that I do: ::
|
||||
|
||||
$ gpg --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/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: unknown validity: unknown
|
||||
sub 2048R/084239CF created: 2010-10-15 expires: never usage: E
|
||||
sub 2048R/5BB065DC created: 2010-10-22 expires: never usage: A
|
||||
[ unknown] (1). NIIBE Yutaka <gniibe@fsij.org>
|
||||
[ unknown] (2) NIIBE Yutaka <gniibe@debian.org>
|
||||
|
||||
gpg>
|
||||
|
||||
See, the key is ``unknown`` state. Add trust for that. ::
|
||||
|
||||
gpg> trust
|
||||
pub 2048R/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: unknown validity: unknown
|
||||
sub 2048R/084239CF created: 2010-10-15 expires: never usage: E
|
||||
sub 2048R/5BB065DC created: 2010-10-22 expires: never usage: A
|
||||
[ unknown] (1). NIIBE Yutaka <gniibe@fsij.org>
|
||||
[ unknown] (2) NIIBE Yutaka <gniibe@debian.org>
|
||||
|
||||
Please decide how far you trust this user to correctly verify other users' keys
|
||||
(by looking at passports, checking fingerprints from different sources, etc.)
|
||||
|
||||
1 = I don't know or won't say
|
||||
2 = I do NOT trust
|
||||
3 = I trust marginally
|
||||
4 = I trust fully
|
||||
5 = I trust ultimately
|
||||
m = back to the main menu
|
||||
|
||||
Your decision? 5
|
||||
Do you really want to set this key to ultimate trust? (y/N) y
|
||||
|
||||
pub 2048R/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: ultimate validity: unknown
|
||||
sub 2048R/084239CF created: 2010-10-15 expires: never usage: E
|
||||
sub 2048R/5BB065DC created: 2010-10-22 expires: never usage: A
|
||||
[ unknown] (1). NIIBE Yutaka <gniibe@fsij.org>
|
||||
[ unknown] (2) NIIBE Yutaka <gniibe@debian.org>
|
||||
Please note that the shown key validity is not necessarily correct
|
||||
unless you restart the program.
|
||||
|
||||
$
|
||||
|
||||
Next time I invoke GnuPG, it will be ``ultimate`` key. Let's see: ::
|
||||
|
||||
$ gpg --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/4CA7BABE created: 2010-10-15 expires: never usage: SC
|
||||
trust: ultimate validity: ultimate
|
||||
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>
|
||||
[ultimate] (2) NIIBE Yutaka <gniibe@debian.org>
|
||||
|
||||
gpg> quit
|
||||
$
|
||||
@@ -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)
|
||||
|
||||
@@ -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,58 @@ 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);
|
||||
break;
|
||||
case 1:
|
||||
str = gnukStringVendor;
|
||||
size = sizeof (gnukStringVendor);
|
||||
break;
|
||||
case 2:
|
||||
str = gnukStringProduct;
|
||||
size = sizeof (gnukStringProduct);
|
||||
break;
|
||||
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 +315,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)
|
||||
{
|
||||
|
||||
@@ -23,22 +23,11 @@ SECTIONS
|
||||
{
|
||||
. = 0;
|
||||
|
||||
.bss :
|
||||
{
|
||||
_bss_start = .;
|
||||
*(.bss)
|
||||
. = ALIGN(4);
|
||||
*(.bss.*)
|
||||
. = ALIGN(4);
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_bss_end = .;
|
||||
} > ram0
|
||||
|
||||
.text : ALIGN(16) SUBALIGN(16)
|
||||
{
|
||||
_text = .;
|
||||
KEEP(*(.vectors))
|
||||
*(.text.entry)
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
@@ -48,6 +37,12 @@ SECTIONS
|
||||
*(.gcc*)
|
||||
} > ram1
|
||||
|
||||
.got :
|
||||
{
|
||||
*(.got)
|
||||
*(.got.*)
|
||||
} > ram1
|
||||
|
||||
.ctors :
|
||||
{
|
||||
PROVIDE(_ctors_start_ = .);
|
||||
@@ -90,6 +85,18 @@ SECTIONS
|
||||
_edata = .;
|
||||
} > ram1
|
||||
|
||||
.bss :
|
||||
{
|
||||
_bss_start = .;
|
||||
*(.bss)
|
||||
. = ALIGN(4);
|
||||
*(.bss.*)
|
||||
. = ALIGN(4);
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_bss_end = .;
|
||||
} > ram1
|
||||
|
||||
PROVIDE(end = .);
|
||||
_end = .;
|
||||
}
|
||||
|
||||
@@ -9,17 +9,38 @@ 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.
|
||||
*
|
||||
* This routine doesn't change PSP and MSP. Application should
|
||||
* prepare those stack pointers.
|
||||
*/
|
||||
static __attribute__ ((naked,section(".text.entry")))
|
||||
void entry (void)
|
||||
{
|
||||
asm volatile ("ldr r0, =__ram_end__\n\t"
|
||||
"ldr r1, =__main_stack_size__\n\t"
|
||||
"sub r0, r0, r1\n\t"
|
||||
"mov sp, r0\n\t"
|
||||
asm volatile ("mov r0, pc\n\t"
|
||||
"bic r0, r0, #255\n\t" /* R0 := vector_table address */
|
||||
"mov r1, #0x90\n\t" /* R1 := numbers of entries * 4 */
|
||||
"ldr r3, .L01\n" /* R3 := -0x20001400 fixed addr */
|
||||
"0:\n\t"
|
||||
"ldr r2, [r0, r1]\n\t"
|
||||
"add r2, r0\n\t" /* Relocate: R0 - 0x20001400 */
|
||||
"add r2, r3\n\t"
|
||||
"str r2, [r0, r1]\n\t"
|
||||
"subs r1, r1, #4\n\t"
|
||||
"bne 0b\n\t"
|
||||
/* Relocation done. */
|
||||
"ldr r3, .L00\n"
|
||||
".LPIC00:\n\t"
|
||||
"add r3, pc\n\t" /* R3 := @_GLOBAL_OFFSET_TABLE_ */
|
||||
/* Clear BSS. */
|
||||
"mov r0, #0\n\t"
|
||||
"ldr r1, =_bss_start\n\t"
|
||||
"ldr r2, =_bss_end\n"
|
||||
"ldr r4, .L00+4\n\t"
|
||||
"ldr r1, [r3, r4]\n\t"
|
||||
"ldr r4, .L00+8\n\t"
|
||||
"ldr r2, [r3, r4]\n"
|
||||
"0:\n\t"
|
||||
"str r0, [r1], #4\n\t"
|
||||
"cmp r2, r1\n\t"
|
||||
@@ -29,7 +50,14 @@ void entry (void)
|
||||
"mov r1, r0\n\t"
|
||||
"bl main\n"
|
||||
"1:\n\t"
|
||||
"b 1b\n"
|
||||
"b 1b\n\t"
|
||||
".align 2\n"
|
||||
".L01:\n\t"
|
||||
".word -0x20001400\n"
|
||||
".L00:\n\t"
|
||||
".word _GLOBAL_OFFSET_TABLE_-(.LPIC00+4)\n\t"
|
||||
".word _bss_start(GOT)\n\t"
|
||||
".word _bss_end(GOT)"
|
||||
: /* no output */ : /* no input */ : "memory");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
2
src/configure
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
30
src/main.c
30
src/main.c
@@ -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,7 +178,8 @@ extern msg_t USBthread (void *arg);
|
||||
#define LED_TIMEOUT_STOP MS2ST(200)
|
||||
|
||||
|
||||
#define ID_OFFSET 22
|
||||
/* 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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
#define SHA256_BLOCK_SIZE 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t total[2];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
@@ -258,27 +270,27 @@ static const uint8_t gnukStringLangID[] = {
|
||||
#include "usb-strings.c.inc"
|
||||
|
||||
const uint8_t gnukStringSerial[] = {
|
||||
17*2+2, /* bLength */
|
||||
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, /* Version number of Gnuk */
|
||||
'1', 0, '.', 0, '0', 0, '.', 0, '4', 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
8
test/features/991_version_string.feature
Normal file
8
test/features/991_version_string.feature
Normal file
@@ -0,0 +1,8 @@
|
||||
@usb
|
||||
Feature: examine USB version string
|
||||
In order to work as Gnuk Token
|
||||
A token should support version string
|
||||
|
||||
Scenario: USB version string
|
||||
Given USB version string of the token
|
||||
Then data should match: ([a-zA-Z0-9]*)-([.0-9]+)-[0-9A-F]+
|
||||
@@ -5,7 +5,7 @@ from binascii import hexlify
|
||||
|
||||
import ast
|
||||
|
||||
import gnuk
|
||||
import gnuk_token as gnuk
|
||||
import rsa_keys
|
||||
|
||||
@Before
|
||||
@@ -108,6 +108,9 @@ def encrypt_on_host_public_key():
|
||||
def decrypt():
|
||||
scc.result = ftc.token.cmd_pso_longdata(0x80, 0x86, scc.ciphertext)
|
||||
|
||||
@Given("USB version string of the token")
|
||||
def usb_version_string():
|
||||
scc.result = ftc.token.get_string(3)
|
||||
|
||||
@When("requesting (.+): ([0-9a-fA-F]+)")
|
||||
def get_data(name, tag_str):
|
||||
|
||||
1
test/gnuk_token.py
Symbolic link
1
test/gnuk_token.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../tool/gnuk_token.py
|
||||
32
tool/get_raw_public_key.py
Executable file
32
tool/get_raw_public_key.py
Executable 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()
|
||||
@@ -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__':
|
||||
|
||||
@@ -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
70
tool/gnuk_remove_keys_libusb.py
Executable 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)
|
||||
@@ -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):
|
||||
"""
|
||||
@@ -77,6 +81,9 @@ class gnuk_token(object):
|
||||
self.__timeout = 10000
|
||||
self.__seq = 0
|
||||
|
||||
def get_string(self, num):
|
||||
return self.__devhandle.getString(num, 512)
|
||||
|
||||
def increment_seq(self):
|
||||
self.__seq = (self.__seq + 1) & 0xff
|
||||
|
||||
@@ -89,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:
|
||||
@@ -120,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, "")
|
||||
@@ -187,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])
|
||||
@@ -222,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):
|
||||
@@ -360,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()
|
||||
@@ -381,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():
|
||||
@@ -402,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)
|
||||
@@ -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
125
tool/gpg_agent.py
Normal 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()
|
||||
@@ -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)
|
||||
|
||||
205
tool/pageant_proxy_to_gpg.py
Normal file
205
tool/pageant_proxy_to_gpg.py
Normal 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()
|
||||
@@ -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
70
tool/rsa.py
Normal 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
4
tool/rsa_example.key
Normal file
@@ -0,0 +1,4 @@
|
||||
9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
|
||||
010001
|
||||
b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
|
||||
dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf
|
||||
86
tool/sexp.py
Normal file
86
tool/sexp.py
Normal 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
|
||||
@@ -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
112
tool/upgrade_by_passwd.py
Executable 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:])
|
||||
Reference in New Issue
Block a user