Compare commits

...

41 Commits

Author SHA1 Message Date
NIIBE Yutaka
f92ee76db5 fixes two bugs 2012-01-19 14:23:01 +09:00
NIIBE Yutaka
87e4fd50c1 New API between gpg and ccid layer 2012-01-18 18:59:16 +09:00
NIIBE Yutaka
cbb2c6aab5 clean up USB API 2 2012-01-16 13:24:31 +09:00
NIIBE Yutaka
00541d7627 clean up USB API 2012-01-16 12:17:45 +09:00
NIIBE Yutaka
27277e56ee update pinpadtest.py 2012-01-12 10:39:16 +09:00
NIIBE Yutaka
4ee4417fb5 fix adding semicolon 2012-01-10 16:47:48 +09:00
NIIBE Yutaka
11351c8cae pinmodify is OK for unblock and put resetcode 2012-01-10 16:02:53 +09:00
NIIBE Yutaka
6abac73354 add an entry for NEWS 2012-01-05 17:04:00 +09:00
NIIBE Yutaka
41537cc827 check DF name on SELECT command 2012-01-05 15:14:35 +09:00
NIIBE Yutaka
a4e0e67340 pinpadtest.py: name change 2012-01-05 10:15:00 +09:00
NIIBE Yutaka
58f8470446 pinpad-test.py: comment change 2012-01-05 10:13:42 +09:00
NIIBE Yutaka
885e1c4ecb pinpad-test.py: add comment 2012-01-05 10:12:24 +09:00
NIIBE Yutaka
2c111d0b20 add result for pinpad-test 2012-01-04 17:33:34 +09:00
NIIBE Yutaka
35a62354ae pinpad-test.py: add --pinmin, --unblock2, and --put2 2012-01-04 11:57:19 +09:00
NIIBE Yutaka
18f3e72c32 fix pinpad-test.py for --add 2012-01-04 09:32:41 +09:00
NIIBE Yutaka
121df70044 fix long standing bug of RxCount setting for buffer bound (was bug in STmicro's software) 2011-12-28 22:16:14 +09:00
NIIBE Yutaka
48d89973c6 fixed long standing bug of ZLP 2011-12-28 12:27:16 +09:00
NIIBE Yutaka
60c1fe47ce fix pinpad-test.py 2011-12-27 14:10:39 +09:00
NIIBE Yutaka
c53afe1f96 fix pinpad-test.py 2011-12-27 10:34:24 +09:00
NIIBE Yutaka
3074058ff7 more fix to CERTDO_SUPPORT 2011-12-22 17:10:41 +09:00
NIIBE Yutaka
a0c8cf2ff4 Data Object 0x7f21 is now optional 2011-12-21 14:14:28 +09:00
NIIBE Yutaka
c0ab2ae830 DnDpinentry: cancellation 2011-12-20 10:39:47 +09:00
NIIBE Yutaka
a08669dfcd add messages to pinpad-test.py 2011-12-19 12:28:01 +09:00
NIIBE Yutaka
4a75aa47df improve pinpad-test.py 2011-12-19 12:17:22 +09:00
NIIBE Yutaka
1145fe0ad8 add new tool pinpad-test.py 2011-12-16 16:42:09 +09:00
NIIBE Yutaka
e2a4be1444 version 0.16 2011-12-14 12:51:26 +09:00
NIIBE Yutaka
b4d36a429f Fixes for PINPAD_SUPPORT 2011-12-14 12:45:20 +09:00
NIIBE Yutaka
a955cbec2f doc changes 2011-12-13 14:25:07 +09:00
NIIBE Yutaka
828b8f5768 merge dnd-support branch
fix vcom, change volume label
2011-12-13 13:29:23 +09:00
NIIBE Yutaka
d9be456fed fix 2011-12-13 11:26:12 +09:00
NIIBE Yutaka
5a9194d136 working now 2011-12-13 11:26:12 +09:00
NIIBE Yutaka
f58233aa5d more 2011-12-13 11:26:12 +09:00
NIIBE Yutaka
ec409fe8a4 rename 2011-12-13 11:26:12 +09:00
NIIBE Yutaka
231c50d9b5 cleanup 2011-12-13 11:26:11 +09:00
NIIBE Yutaka
696de23b52 usb mass storage class 2011-12-13 11:26:11 +09:00
NIIBE Yutaka
b113e6fa7b verify_other should not fail with no keys, but really checks PW1 2011-12-13 11:21:45 +09:00
NIIBE Yutaka
9dcb59f6aa minor cleanup 2011-12-08 10:56:48 +09:00
NIIBE Yutaka
351ce68729 better USB interoperability 2011-12-07 10:16:02 +09:00
NIIBE Yutaka
142dbabfd8 pinpad entry parameter handling 2011-12-07 09:38:48 +09:00
NIIBE Yutaka
1e94b262af pinpad support change 2011-12-01 18:23:10 +09:00
NIIBE Yutaka
268c41634a pinpad input for reset code 2011-11-29 15:18:25 +09:00
42 changed files with 2586 additions and 493 deletions

15
AUTHORS
View File

@@ -6,6 +6,14 @@ Kaz Kojima:
NIIBE Yutaka:
Founder of the project.
Added FST_01 support:
boards/FST_01/board.c
boards/FST_01/board.h
boards/FST_01/mcuconf.h
Added FST_01_00 support:
boards/FST_01_00/board.c
boards/FST_01_00/board.h
boards/FST_01_00/mcuconf.h
Added STBee support:
boards/STBEE/board.c
boards/STBEE/board.h
@@ -28,6 +36,9 @@ NIIBE Yutaka:
tool/intel_hex.py
Wrote a tool for Gnuk:
tool/gnuk_put_binary.py
tool/gnuk_put_binary_libusb.py
Wrote a tool for USB Hub:
tool/hub_ctrl.py
Wrote:
gnuk.svg
src/configure
@@ -49,5 +60,9 @@ NIIBE Yutaka:
src/random.c
src/pin-cir.c
src/pin-dial.c
src/pin-dnd.c
src/usb_msc.c
src/usb_msc.h
src/neug.c
*
and others.

208
ChangeLog
View File

@@ -1,3 +1,211 @@
2012-01-19 Niibe Yutaka <gniibe@fsij.org>
* src/usb-icc.c (icc_handle_data): Handle the case when it only
sends 0x90 and 0x00 correctly.
* src/openpgp-do.c (gpg_do_get_data): Fix res_apdu_data_len.
2012-01-18 Niibe Yutaka <gniibe@fsij.org>
Clean up API between application layer and CCID layer.
* tool/gnuk_put_binary.py, gnuk_put_binary_libusb.py: Don't append
0x9000 at the data, any more.
* src/usb-icc.c (icc_data_size, icc_buffer, icc_seq): Make them
internal.
(res_APDU_size, res_APDU_pointer): Removed.
(icc_handle_data, USBthread): Follow new API of struct apdu.
* src/call-rsa.c (rsa_sign, rsa_decrypt): Likewise.
* src/openpgp.c (CLS, INS, P1, P2): New.
(set_res_apdu, cmd_verify, cmd_change_password)
(cmd_reset_user_password, cmd_put_data, cmd_pgp_gakp)
(cmd_read_binary, cmd_select_file, cmd_pso)
(cmd_internal_authenticate, cmd_update_binary, cmd_write_binary)
(process_command_apdu, GPGthread): Follow new API of struct apdu.
* src/openpgp-do.c (gpg_do_get_data, gpg_do_public_key): Follow
new API of struct apdu.
* src/gnuk.h (struct apdu, apdu): New.
(cmd_APDU, icc_data_size, cmd_APDU_size, icc_buffer): Removed.
(res_APDU, res_APDU_size): Use members of struct apdu.
2012-01-16 Niibe Yutaka <gniibe@fsij.org>
Adopt new USB API.
* src/usb_msc.c (usb_start_transmit): Use usb_lld_write.
(EP6_IN_Callback): Use usb_lld_tx_data_len and usb_lld_write.
(usb_start_receive): Use usb_lld_rx_enable.
(EP7_OUT_Callback): Use usb_lld_rx_data_len, usb_lld_rxcpy
and usb_lld_rx_enable
(msc_handle_command): Use usb_lld_stall_rx and usb_lld_stall_tx.
* src/usb_lld.h (usb_lld_stall_tx, usb_lld_stall_rx)
(usb_lld_tx_data_len): New.
* src/main.c (STDOUTthread): Use usb_lld_write.
* src/usb-icc.c (EP1_IN_Callback, icc_error, icc_power_on)
(icc_send_status, icc_send_data_block, icc_send_params): Use
usb_lld_write (was: USB_SIL_Write).
(EP2_OUT_Callback): Use usb_lld_rx_data_len, usb_lld_rxcpy,
and usb_lld_rx_enable (was: USB_SIL_Read and SetEPRxValid).
(icc_prepare_receive): Use usb_lld_rx_enable.
* src/stmusb.mk (STMUSBSRC): Dont' include usb_sil.c.
* src/usb_lld.h (usb_lld_txcpy, usb_lld_tx_enable)
(usb_lld_write, usb_lld_rx_enable, usb_lld_rx_data_len)
(usb_lld_rxcpy): New.
* src/usb_prop.c (SetEPRxCount_allocated_size): Fix the
implementation. (ST's SetEPRxCount is actually meant to
setup allocated size, which is confusing).
(gnuk_device_init): Don't call USB_SIL_Init.
2012-01-10 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp.c (GPGthread): Allow INS_RESET_RETRY_COUNTER and
INS_PUT_DATA for pinentry targets.
2012-01-05 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp.c (cmd_select_file): Check DF name.
* tool/pinpadtest.py: Rename from pinpad-test.py.
2011-12-28 Niibe Yutaka <gniibe@fsij.org>
* src/usb_prop.c (SetEPRxCount_allocated_size): New.
(gnuk_device_reset): Use SetEPRxCount_allocated_size.
* src/usb_msc.c (usb_start_receive): Don't set RxCount register
here.
* STM32_USB-FS-Device_Driver/src/usb_core.c (Standard_ClearFeature)
(Post0_Process): Don't need to set RxCount register.
* src/usb_prop.c (msc_lun_info) [PINPAD_DND_SUPPORT]: ifdef-out.
* src/usb-icc.c (EP2_OUT_Callback): Fix apdu size == 49 bug,
we don't assume host sends ZLP (But accepts ZLP, just in case).
2011-12-22 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (extended_capabilities) [CERTDO_SUPPORT]:
conditionalize.
2011-12-21 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp-do.c (gpg_do_get_data) [CERTDO_SUPPORT]: ifdef out.
* src/gnuk.ld.in (.gnuk_ch_certificate): Only valid
when --enable-certdo.
* src/flash.c (flash_check_blank) [CERTDO_SUPPORT]: ifdef out.
(flash_erase_binary) [CERTDO_SUPPORT]: Likewise.
(flash_write_binary) [CERTDO_SUPPORT]: Likewise.
* src/configure (certdo): New.
(--enable-certdo, --disable-certdo): New options.
Remove cheking for /dev/random.
* src/config.h.in (@CERTDO_DEFINE@): New.
2011-12-20 Niibe Yutaka <gniibe@fsij.org>
* src/usb_msc.c (msc_handle_command): SCSI_START_STOP_UNIT command
with stop/eject/close means cancelling pinentry.
* src/pin-dnd.c (pinpad_finish_entry, parse_directory_sector):
Implement "cancel".
(pinpad_getline): Likewise.
(msc_scsi_stop): New.
2011-12-16 Niibe Yutaka <gniibe@fsij.org>
* tool/gnuk_put_binary_libusb.py (gnuk_token.cmd_select_openpgp):
Fix apdu parameter.
* tool/gnuk_put_binary.py (GnukToken.cmd_select_openpgp): Ditto.
* tool/pinpad-test.py: New.
2011-12-14 Niibe Yutaka <gniibe@fsij.org>
* Version 0.16.
* src/usb_desc.c (gnukStringSerial): Updated.
* boards/STM8S_DISCOVERY/board.h, board.c: Fix for PINPAD_SUPPORT.
* boards/STBEE_MINI/board.h, board.c: Likewise.
* boards/STBEE/board.h, board.c: Likewise.
* boards/FST_01/board.c: Likewise.
2011-12-13 Niibe Yutaka <gniibe@fsij.org>
Add pinpad DND support.
* src/Makefile.in (CSRC) [ENABLE_PINPAD]: Add usb_msc.c.
* src/configure (pinpad): Add dnd support.
* src/gnuk.h [PINPAD_DND_SUPPORT]: Add declarations.
* src/main.c (STDOUTthread): Add PUSH packet.
(main) [PINPAD_DND_SUPPORT]: Call msc_init.
* src/usb_conf.h (EP_NUM): Add the case of PINPAD_DND_SUPPORT.
(ENDP6_TXADDR, ENDP7_RXADDR): New.
(ENDP4_TXADDR, ENDP5_RXADDR): Changed for smaller buffer.
* src/usb_desc.c (gnukConfigDescriptor): Add Mass storage device.
* src/usb_msc.c, src/usb_msc.h, src/pin-dnd.c: New.
* src/usb_prop.c: Include "usb_msc.h".
(gnuk_device_reset): Add initialization of ENDP6 and ENDP7.
(gnuk_device_SetInterface): Add initialization of ENDP6 and ENDP7.
(NUM_INTERFACES): Handle cases for PINPAD_DND_SUPPORT.
(msc_lun_info): New.
(gnuk_setup_with_data, gnuk_setup_with_nodata): Handle standard
request for Mass storage device.
* Virtual_COM_Port/usb_desc.h (VIRTUAL_COM_PORT_DATA_SIZE): Since
there isn't enough hardware buffer, smaller value (was: 64).
* src/ac.c (verify_user_0): Add access argument.
(verify_pso_cds, verify_other, verify_admin_0): Follow the change.
* src/openpgp.c (cmd_change_password): Likewise.
2011-12-08 Niibe Yutaka <gniibe@fsij.org>
* src/usb-icc.c: Not include "usb_desc.h".
* src/usb_endp.c (EP5_OUT_Callback): Fix minor bug.
2011-12-07 Niibe Yutaka <gniibe@fsij.org>
* src/usb_desc.c (gnukDeviceDescriptor): Changed bcdUSB = 1.1.
Gnuk device conforms to USB 2.0 full speed device, but when it was
2.0, some OS informs users, "you can connect the device to 2.0
compliant hub so that it can have better bandwidth", which is not
the case for full speed device.
* src/openpgp.c (GPGthread): Handle bConfirmPIN parameter.
* src/usb-icc.c (icc_handle_data): Pass PC_to_RDR_Secure
information to gpg_thread using memory of cmd_APDU.
2011-12-01 Niibe Yutaka <gniibe@fsij.org>
* src/gnuk.h (EV_PINPAD_INPUT_DONE, EV_NOP, EV_CMD_AVAILABLE)
(EV_VERIFY_CMD_AVAILABLE, EV_MODIFY_CMD_AVAILABLE): New.
* src/usb-icc.c (icc_power_off, icc_handle_data): Use EV_NOP,
EV_CMD_AVAILABLE, EV_VERIFY_CMD_AVAILABLE, and EV_MODIFY_CMD_AVAILABLE.
* src/pin-cir.c (cir_timer_interrupt): Use EV_PINPAD_INPUT_DONE.
* src/pin-dial.c (dial_sw_interrupt, pinpad_getline): Ditto.
(EV_SW_PUSH): Remove.
* src/openpgp.h (GPG_FUNCTION_NOT_SUPPORTED): New.
(GPG_CONDITION_NOT_SATISFIED): New.
* src/openpgp.c (cmd_change_password): Use GPG_FUNCTION_NOT_SUPPORTED.
* src/openpgp.c (cmd_verify, cmd_change_password)
(cmd_reset_user_password, cmd_put_data): Remove pinpad handling...
(GPGthread): ... and implement pinpad handling here.
2011-11-29 Niibe Yutaka <gniibe@fsij.org>
* src/openpgp.c (cmd_put_data) [PINPAD_SUPPORT]: Support pinpad
input (for reset code).
2011-11-24 Niibe Yutaka <gniibe@fsij.org>
* Version 0.15.

57
NEWS
View File

@@ -1,5 +1,60 @@
Gnuk NEWS - User visible changes
* Major changes in Gnuk 0.17
Released 2012-01-XX, by NIIBE Yutaka
** ISO 7816 SELECT command behavior is strict now
Old implementations do not check DF name for SELECT command.
This causes some trouble when Gnuk Token is identified as if it were
different card/token. Now, DF name of OpenPGP card is checked.
** USB CCID/ICCD low level bug is fixed
When the size of command APDU data is just 49, the lower level packet
size is 64. This is maximum size of BULK-OUT transfer packet, and
caused trouble in the past implementations. Example is setting url
(0x5f50) as: http://www.gniibe.org/adpu-string-size-is-just-49
This is because the past implementations expect ZLP (zero length
packet). Now, it has been fixed. You can use any size of string.
** CERT.3 Data Object (0x7f21) is now optional
As there's no valid use case for this data object and it does not
work as current version of GnuPG, this is now optional feature.
You can enable this data object by specifying --enable-certdo at
configure time.
** With DnD pinentry, user can cancel pin input
Now, user can cancel pin input by unmounting device before finishing
DnD.
** New tool: pinpadtest.py
The tool pinpadtest.py is PC/SC test tool for pinentry of pinpad with
OpenPGP card v2.
* Major changes in Gnuk 0.16
Released 2011-12-14, by NIIBE Yutaka
** DnD pinentry support is added and it's default to pinentry support
DnD pinentry support doesn't require any hardware extension, but
emulates mass storage class device of USB. User inputs pass phrase
by "drag and drop"-ing folders using file manager or something.
** Bug fix for VERIFY for CHV2
With no keys, VERIFY command for CHV2 used to fail even if pass phrase
is correct. It was intentional, because CHV2 verification would be
useless with no keys. But there is a corner case for PRIVATE-DOs,
which may requires CHV2 verification. Even though Gnuk doesn't
support any PRIVATE-DOs, it is good to be fixed.
** Changed bcdUSB = 1.1
Gnuk device conforms to USB 2.0 full speed device, but when it was
2.0, some OS informs users, "you can connect the device to 2.0
compliant hub so that it can have better bandwidth", which is not
the case for full speed device.
* Major changes in Gnuk 0.15
Released 2011-11-24, by NIIBE Yutaka
@@ -9,7 +64,7 @@ Flying Stone Technology's open hardware, Flying Stone Tiny 01 is
supported.
** Flash writing tool for "DfuSe" is improved
Now, it support holes and unaligned blocks in hex file.
Now, it supports holes and unaligned blocks in hex file.
** Experimental PIN-pad support (by TV controller) change
Now, Gnuk has codetables for conversion from CIR code to ASCII code.

27
README
View File

@@ -1,7 +1,7 @@
Gnuk - software for GnuPG USB Token
Version 0.15
2011-11-24
Version 0.16
2011-12-14
Niibe Yutaka
Free Software Initiative of Japan
@@ -9,8 +9,8 @@ What's Gnuk?
============
Gnuk is software implementation of a USB token for GNU Privacy Guard.
Gnuk supports OpenPGP card protocol version 2, and it runs on STM32
processor.
Gnuk supports OpenPGP card protocol version 2, and it runs on
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.
@@ -94,21 +94,22 @@ Qa: With GNOME, I can't use Gnuk Token for SSH. How can we use it for SSH?
Aa: You need to deactivate seahorse-agent and gnome-keyring, but use
gpg-agant for the role of ssh-agent. For gnome-keyring please do:
$ gconftool-2 --type bool --set /apps/gnome-keyring/daemon-components/ssh false
$ gconftool-2 --type bool --set /apps/gnome-keyring/daemon-components/ssh false
Qb: With GNOME 3, I can't use Gnuk Token at all. Why?
Ab: That's because gnome-keyring-daemon interferes GnuPG. Type:
$ gnome-session-properties
$ gnome-session-properties
and at the tab of "Startup Programs", disable check buttons for "GPG Password Agent" and "SSH Key Agent".
and at the tab of "Startup Programs", disable check buttons for
"GPG Password Agent" and "SSH Key Agent".
Release notes
=============
This is sixteenth release of Gnuk. While it works well for specific
This is seventeenth release of Gnuk. While it works well for specific
usages and it is considered stable, it is still somewhat experimental.
Tested features are:
@@ -172,6 +173,16 @@ Another PIN-pad support is connecting rotary encoder, push switch and
7-segment LED display. Both of PIN verification and PIN modification
are supported for this circuit extension.
Recently, "DnDpinentry" support is added. This is using usual file
manager for pinentry. User does "drag and drop" folders and it will
be pin entry. This feature doesn't require any additional hardware.
See doc/settings-for-DnDpinentry for your desktop configuration.
Note that you need pinpad support for GnuPG, it's currently in the
master branch of GnuPG git repository at git.gnupg.org, and it's under
evaluation. When it will be considered stable, it will be put onto
stable branch.
Souce code
==========

View File

@@ -308,7 +308,6 @@ RESULT Standard_ClearFeature(void)
if (Related_Endpoint == ENDP0)
{
/* After clear the STALL, enable the default endpoint receiver */
SetEPRxCount(Related_Endpoint, Device_Property.MaxPacketSize);
_SetEPRxStatus(Related_Endpoint, EP_RX_VALID);
}
else
@@ -1019,8 +1018,6 @@ uint8_t Post0_Process(void)
USB_OTG_EP *ep;
#endif /* STM32F10X_CL */
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
if (pInformation->ControlState == STALLED)
{
vSetEPRxStatus(EP_RX_STALL);

View File

@@ -28,7 +28,7 @@
#define USB_INTERFACE_DESCRIPTOR_TYPE 0x04
#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05
#define VIRTUAL_COM_PORT_DATA_SIZE 64
#define VIRTUAL_COM_PORT_DATA_SIZE 16
#define VIRTUAL_COM_PORT_INT_SIZE 8
#define VIRTUAL_COM_PORT_SIZ_DEVICE_DESC 18

View File

@@ -15,7 +15,7 @@ hwinit1 (void)
{
hwinit1_common ();
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
/* PA0/TIM2_CH1 = 1 (pull up) */
/* PA1/TIM2_CH2 = 0 (pull down) */
/* PA2/TIM2_CH3 <= Vout of CIR receiver module */
@@ -66,7 +66,7 @@ set_led (int value)
palClearPad (IOPORT2, GPIOB_LED);
}
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
void
cir_ext_disable (void)
{

View File

@@ -15,7 +15,6 @@ hwinit1 (void)
{
hwinit1_common ();
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
/* EXTI0 <= PB0 */
AFIO->EXTICR[0] = AFIO_EXTICR1_EXTI0_PB;
@@ -69,7 +68,6 @@ hwinit1 (void)
/* Generate UEV to upload PSC and ARR */
TIM4->EGR = TIM_EGR_UG;
#endif
#endif
}
void
@@ -90,7 +88,6 @@ set_led (int value)
palSetPad (IOPORT4, GPIOD_LED1);
}
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
void
cir_ext_disable (void)
@@ -156,4 +153,3 @@ CH_IRQ_HANDLER (EXTI2_IRQHandler)
CH_IRQ_EPILOGUE ();
}
#endif
#endif

View File

@@ -38,7 +38,7 @@
#define BOARD_STBEE
#define BOARD_NAME "STBee"
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DIAL_SUPPORT)
#define HAVE_7SEGLED 1
/*
* Timer assignment for CIR
@@ -89,7 +89,7 @@
* Please refer to the STM32 Reference Manual for details.
*/
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DIAL_SUPPORT)
/*
* Port A setup.
* PA6 - (TIM3_CH1) input with pull-up

View File

@@ -15,11 +15,12 @@ hwinit1 (void)
{
hwinit1_common ();
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_SUPPORT) && !defined(DFU_SUPPORT)
palWritePort(IOPORT2, 0x7fff); /* Only clear GPIOB_7SEG_DP */
while (palReadPad (IOPORT2, GPIOB_BUTTON) != 0)
; /* Wait for JTAG debugger connection */
palWritePort(IOPORT2, 0xffff); /* All set */
#endif
#if defined(PINPAD_CIR_SUPPORT)
/* EXTI0 <= PB0 */
@@ -73,7 +74,6 @@ hwinit1 (void)
TIM4->ARR = 31;
/* Generate UEV to upload PSC and ARR */
TIM4->EGR = TIM_EGR_UG;
#endif
#endif
/*
* Disable JTAG and SWD, done after hwinit1_common as HAL resets AFIO
@@ -101,7 +101,6 @@ set_led (int value)
palSetPad (IOPORT1, GPIOA_LED1);
}
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
void
cir_ext_disable (void)
@@ -167,4 +166,3 @@ CH_IRQ_HANDLER (EXTI2_IRQHandler)
CH_IRQ_EPILOGUE ();
}
#endif
#endif

View File

@@ -39,7 +39,7 @@
#define BOARD_NAME "STBee Mini"
#define CPU_WITH_NO_GPIOE 1
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DIAL_SUPPORT)
#define HAVE_7SEGLED 1
/*
* Timer assignment for CIR
@@ -92,7 +92,7 @@
* Please refer to the STM32 Reference Manual for details.
*/
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DIAL_SUPPORT)
/*
* Port A setup.
* PA6 - (TIM3_CH1) input with pull-up

View File

@@ -15,7 +15,7 @@ hwinit1 (void)
{
hwinit1_common ();
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
/* EXTI5 <= PB5 */
AFIO->EXTICR[1] = AFIO_EXTICR2_EXTI5_PB;
EXTI->IMR = 0;
@@ -62,7 +62,7 @@ set_led (int value)
palClearPad (IOPORT1, GPIOA_LED);
}
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
void
cir_ext_disable (void)
{

View File

@@ -96,7 +96,7 @@
#define VAL_GPIOACRH 0x88888883 /* PA15...PA8 */
#define VAL_GPIOAODR 0xFFFFFFFF
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
/*
* Port B setup.
* Everything input with pull-up except:

View File

@@ -0,0 +1,34 @@
On GNU/Linux Desktop, I use udisks-glue so that DnDpinentry folder can
be mounted with sync and noatime options.
After installing udisks-glue, I invoke gnome-session-properties to
add udisks-glue to "Startup Program".
Then, I have two files to configure udisks (disable udisks for
DnDpinentry) and udisks-glue (enable and specify options for DnDpinentry).
/etc/udev/rules.d/88-udisks.rules
---------------
ENV{ID_VENDOR}=="FSIJ", ENV{DEVTYPE}=="disk", ENV{ID_FS_LABEL}=="DnDpinentry", ENV{UDISKS_PRESENTATION_NOPOLICY}="1"
---------------
~/.udisks-glue.conf
---------------
filter gone {
label = "DnDpinentry"
optical = false
}
match gone {
automount = true
automount_options = { sync, noatime }
}
---------------
We need following setting for pinentry. Or else, you can't do
anything when pinentry grabs mouse focus.
~/.gnupg/gpg-agent.conf
---------------
no-grab
---------------

View File

@@ -101,6 +101,10 @@ ifneq ($(ENABLE_PINPAD),)
CSRC += pin-$(ENABLE_PINPAD).c
endif
ifeq ($(ENABLE_PINPAD),dnd)
CSRC += usb_msc.c
endif
# List ASM source files here
ASMSRC = $(PORTASM) \
$(CHIBIOS)/os/ports/GCC/ARMCMx/STM32F10x/vectors.s

View File

@@ -57,11 +57,11 @@ ac_reset_other (void)
}
int
verify_user_0 (const uint8_t *pw, int buf_len, int pw_len_known,
verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len, int pw_len_known,
const uint8_t *ks_pw1)
{
int pw_len;
int r;
int r1, r2;
uint8_t keystring[KEYSTRING_MD_SIZE];
if (gpg_pw_locked (PW_ERR_PW1))
@@ -90,10 +90,23 @@ verify_user_0 (const uint8_t *pw, int buf_len, int pw_len_known,
success_one_step:
sha1 (pw, pw_len, keystring);
if ((r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring))
< 0)
goto failure;
else if (r == 0)
if (access == AC_PSO_CDS_AUTHORIZED)
{
r1 = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring);
r2 = 0;
}
else
{
r1 = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER, keystring);
r2 = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER, keystring);
}
if (r1 < 0 || r2 < 0)
{
gpg_pw_increment_err_counter (PW_ERR_PW1);
return -1;
}
else if (r1 == 0 && r2 == 0)
if (ks_pw1 != NULL && memcmp (ks_pw1+1, keystring, KEYSTRING_MD_SIZE) != 0)
goto failure;
@@ -113,7 +126,7 @@ verify_pso_cds (const uint8_t *pw, int pw_len)
DEBUG_INFO ("verify_pso_cds\r\n");
DEBUG_BYTE (pw_len);
r = verify_user_0 (pw, pw_len, pw_len, ks_pw1);
r = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, pw_len, pw_len, ks_pw1);
if (r > 0)
auth_status |= AC_PSO_CDS_AUTHORIZED;
return r;
@@ -122,29 +135,16 @@ verify_pso_cds (const uint8_t *pw, int pw_len)
int
verify_other (const uint8_t *pw, int pw_len)
{
int r1, r2;
uint8_t keystring[KEYSTRING_MD_SIZE];
const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1);
int r;
DEBUG_INFO ("verify_other\r\n");
DEBUG_BYTE (pw_len);
if (gpg_pw_locked (PW_ERR_PW1))
return 0;
sha1 (pw, pw_len, keystring);
if ((r1 = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER, keystring)) < 0
|| (r2 = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER,
keystring)) < 0)
{
gpg_pw_increment_err_counter (PW_ERR_PW1);
return -1;
}
else if (r1 == 0 && r2 == 0)
/* No key is available. Fail even if password can match. */
return -1;
gpg_pw_reset_err_counter (PW_ERR_PW1);
auth_status |= AC_OTHER_AUTHORIZED;
return 1;
r = verify_user_0 (AC_OTHER_AUTHORIZED, pw, pw_len, pw_len, ks_pw1);
if (r > 0)
auth_status |= AC_OTHER_AUTHORIZED;
return r;
}
/*
@@ -230,7 +230,8 @@ verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known)
if (ks_pw1 != NULL)
{ /* empty PW3, but PW1 exists */
int r = verify_user_0 (pw, buf_len, pw_len_known, ks_pw1);
int r = verify_user_0 (AC_PSO_CDS_AUTHORIZED,
pw, buf_len, pw_len_known, ks_pw1);
if (r > 0)
admin_authorized = BY_USER;

View File

@@ -25,6 +25,7 @@
#include "config.h"
#include "ch.h"
#include "gnuk.h"
#include "openpgp.h"
#include "polarssl/config.h"
#include "polarssl/rsa.h"
@@ -84,10 +85,9 @@ rsa_sign (const uint8_t *raw_message, uint8_t *output, int msg_len,
}
else
{
res_APDU[RSA_SIGNATURE_LENGTH] = 0x90;
res_APDU[RSA_SIGNATURE_LENGTH+1] = 0x00;
res_APDU_size = RSA_SIGNATURE_LENGTH + 2;
res_APDU_size = RSA_SIGNATURE_LENGTH;
DEBUG_INFO ("done.\r\n");
GPG_SUCCESS ();
return 0;
}
}
@@ -177,10 +177,9 @@ rsa_decrypt (const uint8_t *input, uint8_t *output, int msg_len,
}
else
{
res_APDU[output_len] = 0x90;
res_APDU[output_len+1] = 0x00;
res_APDU_size = output_len + 2;
res_APDU_size = output_len;
DEBUG_INFO ("done.\r\n");
GPG_SUCCESS ();
return 0;
}
}

View File

@@ -5,3 +5,4 @@
@DFU_DEFINE@
@PINPAD_DEFINE@
@PINPAD_MORE_DEFINE@
@CERTDO_DEFINE@

46
src/configure vendored
View File

@@ -26,12 +26,7 @@ verbose=no
with_dfu=default
debug=no
pinpad=no
# check /dev/random
if test ! -e /dev/random; then
echo "/dev/random is required." >&2
exit 1
fi
certdo=no
# Process each option
for option; do
@@ -57,6 +52,10 @@ for option; do
pinpad=$optarg ;;
--disable-pinpad)
pinpad=no ;;
--enable-certdo)
certdo=yes ;;
--disable-certdo)
certdo=no ;;
--with-dfu)
with_dfu=yes ;;
--without-dfu)
@@ -86,8 +85,9 @@ Configuration:
STM8S_DISCOVERY
STBEE
--enable-debug debug with virtual COM port [no]
--enable-pinpad={cir,dial}
PIN input device support [no]
--enable-pinpad={dnd,cir,dial}
PIN entry support [no]
--enable-certdo support CERT.3 data object [no]
--with-dfu build image for DFU [<target specific>]
EOF
exit 0
@@ -159,10 +159,10 @@ if test "$pinpad" = "no"; then
PINPAD_MORE_DEFINE=""
echo "PIN pad option disabled"
elif test "$pinpad" = "yes"; then
PINPAD_MAKE_OPTION="ENABLE_PINPAD=cir"
PINPAD_MAKE_OPTION="ENABLE_PINPAD=dnd"
PINPAD_DEFINE="#define PINPAD_SUPPORT 1"
PINPAD_MORE_DEFINE="#define PINPAD_CIR_SUPPORT 1"
echo "PIN pad option enabled (cir)"
PINPAD_MORE_DEFINE="#define PINPAD_DND_SUPPORT 1"
echo "PIN pad option enabled (dnd)"
else
PINPAD_MAKE_OPTION="ENABLE_PINPAD=$pinpad"
PINPAD_DEFINE="#define PINPAD_SUPPORT 1"
@@ -170,16 +170,34 @@ else
echo "PIN pad option enabled ($pinpad)"
fi
# --enable-certdo option
if test "$certdo" = "yes"; then
CERTDO_DEFINE="#define CERTDO_SUPPORT 1"
echo "CERT.3 Data Object is supported (Note: it is not supported by GnuPG)"
else
CERTDO_DEFINE="#undef CERTDO_SUPPORT"
echo "CERT.3 Data Object is not supported"
fi
sed -e "s%@BOARD_DIR@%$BOARD_DIR%" \
-e "s%@DEBUG_MAKE_OPTION@%$DEBUG_MAKE_OPTION%" \
-e "s%@PINPAD_MAKE_OPTION@%$PINPAD_MAKE_OPTION%" \
< Makefile.in > Makefile
sed -e "s/@ORIGIN@/$ORIGIN/" -e "s/@FLASH_SIZE@/$FLASH_SIZE/" \
-e "s/@FLASH_PAGE_SIZE@/$FLASH_PAGE_SIZE/" \
if test "$certdo" = "yes"; then
sed -e "/^@CERTDO_SUPPORT_START@$/ d" -e "/^@CERTDO_SUPPORT_END@$/ d" \
-e "s/@ORIGIN@/$ORIGIN/" -e "s/@FLASH_SIZE@/$FLASH_SIZE/" \
-e "s/@FLASH_PAGE_SIZE@/$FLASH_PAGE_SIZE/" \
< gnuk.ld.in > gnuk.ld
else
sed -e "/^@CERTDO_SUPPORT_START@$/,/^@CERTDO_SUPPORT_END@$/ d" \
-e "s/@ORIGIN@/$ORIGIN/" -e "s/@FLASH_SIZE@/$FLASH_SIZE/" \
-e "s/@FLASH_PAGE_SIZE@/$FLASH_PAGE_SIZE/" \
< gnuk.ld.in > gnuk.ld
fi
sed -e "s/@DEBUG_DEFINE@/$DEBUG_DEFINE/" \
-e "s/@DFU_DEFINE@/$DFU_DEFINE/" \
-e "s/@PINPAD_DEFINE@/$PINPAD_DEFINE/" \
-e "s/@PINPAD_MORE_DEFINE@/$PINPAD_MORE_DEFINE/" \
-e "s/@DFU_DEFINE@/$DFU_DEFINE/" \
-e "s/@CERTDO_DEFINE@/$CERTDO_DEFINE/" \
< config.h.in > config.h
exit 0

View File

@@ -581,6 +581,7 @@ flash_cnt123_clear (const uint8_t **addr_p)
}
#if defined(CERTDO_SUPPORT)
static int
flash_check_blank (const uint8_t *page, int size)
{
@@ -592,17 +593,16 @@ flash_check_blank (const uint8_t *page, int size)
return 1;
}
#endif
#define FLASH_CH_CERTIFICATE_SIZE 2048
int
flash_erase_binary (uint8_t file_id)
{
const uint8_t *p;
#if defined(CERTDO_SUPPORT)
if (file_id == FILEID_CH_CERTIFICATE)
{
p = &ch_certificate_start;
const uint8_t *p = &ch_certificate_start;
if (flash_check_blank (p, FLASH_CH_CERTIFICATE_SIZE) == 0)
{
flash_erase_page ((uint32_t)p);
@@ -614,6 +614,9 @@ flash_erase_binary (uint8_t file_id)
return 0;
}
else
#else
(void)file_id;
#endif
return -1;
}
@@ -625,12 +628,15 @@ flash_write_binary (uint8_t file_id, const uint8_t *data,
uint16_t maxsize;
const uint8_t *p;
#if defined(CERTDO_SUPPORT)
if (file_id == FILEID_CH_CERTIFICATE)
{
maxsize = FLASH_CH_CERTIFICATE_SIZE;
p = &ch_certificate_start;
}
else if (file_id == FILEID_SERIAL_NO)
else
#endif
if (file_id == FILEID_SERIAL_NO)
{
maxsize = 6;
p = &openpgpcard_aid[8];

View File

@@ -1,3 +1,17 @@
/*
* We declare some of libc functions here, because we will
* remove dependency on libc in future, possibly.
*/
extern size_t strlen (const char *s);
extern int strncmp(const char *s1, const char *s2, size_t n);
extern void *memcpy (void *dest, const void *src, size_t n);
extern void *memset (void *s, int c, size_t n);
extern int memcmp (const void *s1, const void *s2, size_t n);
extern void *memmove(void *dest, const void *src, size_t n);
/*
* Debug functions
*/
extern Thread *stdout_thread;
#define EV_TX_READY ((eventmask_t)1)
@@ -11,19 +25,36 @@ extern void put_binary (const char *s, int len);
extern void _write (const char *, int);
/*
* We declare some of libc functions here, because we will
* remove dependency on libc in future.
* Application layer <-> CCID layer data structure
*/
extern size_t strlen (const char *s);
extern int strncmp(const char *s1, const char *s2, size_t n);
extern void *memcpy (void *dest, const void *src, size_t n);
extern void *memset (void *s, int c, size_t n);
extern int memcmp (const void *s1, const void *s2, size_t n);
extern void *memmove(void *dest, const void *src, size_t n);
struct apdu {
uint8_t seq;
/* command APDU */
uint8_t *cmd_apdu_head; /* CLS INS P1 P2 [ internal Lc ] */
uint8_t *cmd_apdu_data;
uint16_t cmd_apdu_data_len; /* Nc, calculated by Lc field */
uint32_t expected_res_size; /* Ne, calculated by Le field */
/* response APDU */
uint16_t sw;
uint8_t *res_apdu_data;
uint16_t res_apdu_data_len;
};
extern struct apdu apdu;
#define EV_EXEC_FINISHED ((eventmask_t)2) /* GPG Execution finished */
/* GPG thread */
#define EV_PINPAD_INPUT_DONE ((eventmask_t)1)
#define EV_NOP ((eventmask_t)2)
#define EV_CMD_AVAILABLE ((eventmask_t)4)
#define EV_VERIFY_CMD_AVAILABLE ((eventmask_t)8)
#define EV_MODIFY_CMD_AVAILABLE ((eventmask_t)16)
/* maximum cmd apdu data is key import 22+4+128+128 (proc_key_import) */
#define MAX_CMD_APDU_SIZE (7+282) /* header + data */
/* maximum res apdu data is public key 5+9+256+2 (gpg_do_public_key) */
@@ -31,12 +62,8 @@ extern void *memmove(void *dest, const void *src, size_t n);
#define ICC_MSG_HEADER_SIZE 10
#define cmd_APDU (&icc_buffer[ICC_MSG_HEADER_SIZE])
#define res_APDU (&icc_buffer[ICC_MSG_HEADER_SIZE])
extern int icc_data_size;
#define cmd_APDU_size icc_data_size
extern int res_APDU_size;
extern const uint8_t *res_APDU_pointer;
#define res_APDU apdu.res_apdu_data
#define res_APDU_size apdu.res_apdu_data_len
/* USB buffer size of LL (Low-level): size of single Bulk transaction */
#define USB_LL_BUF_SIZE 64
@@ -47,7 +74,6 @@ extern const uint8_t *res_APDU_pointer;
*/
#define USB_BUF_SIZE ((10 + 10 + MAX_CMD_APDU_SIZE + USB_LL_BUF_SIZE - 1) \
/ USB_LL_BUF_SIZE * USB_LL_BUF_SIZE)
extern uint8_t icc_buffer[USB_BUF_SIZE];
enum icc_state
{
@@ -79,8 +105,8 @@ extern void gpg_pw_increment_err_counter (uint8_t which);
extern int ac_check_status (uint8_t ac_flag);
extern int verify_pso_cds (const uint8_t *pw, int pw_len);
extern int verify_other (const uint8_t *pw, int pw_len);
extern int verify_user_0 (const uint8_t *pw, int buf_len, int pw_len_known,
const uint8_t *ks_pw1);
extern int verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len,
int pw_len_known, const uint8_t *ks_pw1);
extern int verify_admin (const uint8_t *pw, int pw_len);
extern int verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known);
@@ -343,13 +369,20 @@ extern Thread *main_thread;
extern void led_blink (int spec);
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
# if defined(PINPAD_CIR_SUPPORT)
extern void cir_ext_disable (void);
extern void cir_ext_enable (void);
#elif defined(PINPAD_DIAL_SUPPORT)
# elif defined(PINPAD_DIAL_SUPPORT)
extern void dial_sw_disable (void);
extern void dial_sw_enable (void);
#endif
# elif defined(PINPAD_DND_SUPPORT)
extern uint8_t media_available;
extern void msc_init (void);
extern void msc_media_insert_change (int available);
extern int msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size);
extern int msc_scsi_read (uint32_t lba, const uint8_t **sector_p);
extern void msc_scsi_stop (uint8_t code);
# endif
#define PIN_INPUT_CURRENT 1
#define PIN_INPUT_NEW 2
#define PIN_INPUT_CONFIRM 3

View File

@@ -115,6 +115,7 @@ SECTIONS
PROVIDE(end = .);
_end = .;
@CERTDO_SUPPORT_START@
.gnuk_ch_certificate :
{
. = ALIGN (@FLASH_PAGE_SIZE@);
@@ -123,6 +124,7 @@ SECTIONS
. += 1920;
. = ALIGN (@FLASH_PAGE_SIZE@);
} > flash =0xffffffff
@CERTDO_SUPPORT_END@
.gnuk_flash :
{

View File

@@ -106,10 +106,14 @@ STDOUTthread (void *arg)
p = stdout.str;
len = stdout.size;
while (len > 0)
while (1)
{
int i;
if (len == 0)
if (count_in != VIRTUAL_COM_PORT_DATA_SIZE)
break;
if (len < VIRTUAL_COM_PORT_DATA_SIZE)
{
for (i = 0; i < len; i++)
@@ -128,8 +132,7 @@ STDOUTthread (void *arg)
chEvtClear (EV_TX_READY);
USB_SIL_Write (EP3_IN, buffer_in, count_in);
SetEPTxValid (ENDP3);
usb_lld_write (ENDP3, buffer_in, count_in);
chEvtWaitOne (EV_TX_READY);
}
@@ -386,6 +389,10 @@ main (int argc, char **argv)
chThdCreateStatic (waUSBthread, sizeof(waUSBthread),
NORMALPRIO, USBthread, NULL);
#ifdef PINPAD_DND_SUPPORT
msc_init ();
#endif
while (1)
{
eventmask_t m;

View File

@@ -98,14 +98,18 @@ static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = {
*/
0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */
0x00, 0x00, /* Max get challenge */
#ifdef CERTDO_SUPPORT
0x07, 0xfe, /* max. length of cardholder certificate (2KB - 2)*/
#else
0x00, 0x00,
#endif
/* Max. length of command data */
(MAX_CMD_APDU_SIZE>>8), (MAX_CMD_APDU_SIZE&0xff),
/* Max. length of response data */
#if 0
(MAX_RES_APDU_SIZE>>8), (MAX_RES_APDU_SIZE&0xff),
#else
#ifdef CERTDO_SUPPORT
0x08, 0x00, /* the case of cardholder ceritificate */
#else
(MAX_RES_APDU_SIZE>>8), (MAX_RES_APDU_SIZE&0xff),
#endif
};
@@ -413,10 +417,12 @@ do_kgtime_all (uint16_t tag, int with_tag)
}
const uint8_t openpgpcard_aid[] = {
0xd2, 0x76, 0x00, 0x01, 0x24, 0x01,
0x02, 0x00, /* Version 2.0 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* To be overwritten */
0xd2, 0x76, /* D: National, 276: DEU ISO 3166-1 */
0x00, 0x01, 0x24, /* Registered Application Provider Identifier */
0x01, /* Application: OpenPGPcard */
0x02, 0x00, /* Version 2.0 */
/* v. id */ /* serial number */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* To be overwritten */
};
static int
@@ -1241,20 +1247,22 @@ copy_do (const struct do_table_entry *do_p, int with_tag)
void
gpg_do_get_data (uint16_t tag, int with_tag)
{
#if defined(CERTDO_SUPPORT)
if (tag == GPG_DO_CH_CERTIFICATE)
{
res_APDU_pointer = &ch_certificate_start;
res_APDU_size = ((res_APDU_pointer[2] << 8) | res_APDU_pointer[3]);
if (res_APDU_size == 0xffff)
apdu.res_apdu_data = &ch_certificate_start;
apdu.res_apdu_data_len = ((apdu.res_apdu_data[2] << 8) | apdu.res_apdu_data[3]);
if (apdu.res_apdu_data_len == 0xffff)
{
res_APDU_pointer = NULL;
apdu.res_apdu_data_len = 0;
GPG_NO_RECORD ();
}
else
/* Add length of (tag+len) and status word (0x9000) at the end */
res_APDU_size += 4 + 2;
/* Add length of (tag+len) */
apdu.res_apdu_data_len += 4;
}
else
#endif
{
const struct do_table_entry *do_p = get_do_entry (tag);
@@ -1270,9 +1278,8 @@ gpg_do_get_data (uint16_t tag, int with_tag)
GPG_SECURITY_FAILURE ();
else
{
*res_p++ = 0x90;
*res_p++ = 0x00;
res_APDU_size = res_p - res_APDU;
GPG_SUCCESS ();
}
}
else
@@ -1406,8 +1413,8 @@ gpg_do_public_key (uint8_t kk_byte)
*res_p++ = 0x01; *res_p++ = 0x00; *res_p++ = 0x01;
/* Success */
*res_p++ = 0x90; *res_p++ = 0x00;
res_APDU_size = res_p - res_APDU;
GPG_SUCCESS ();
}
DEBUG_INFO ("done.\r\n");

View File

@@ -29,6 +29,11 @@
#include "polarssl/config.h"
#include "polarssl/sha1.h"
#define CLS(a) a.cmd_apdu_head[0]
#define INS(a) a.cmd_apdu_head[1]
#define P1(a) a.cmd_apdu_head[2]
#define P2(a) a.cmd_apdu_head[3]
#define INS_VERIFY 0x20
#define INS_CHANGE_REFERENCE_DATA 0x24
#define INS_PSO 0x2a
@@ -64,9 +69,7 @@ select_file_TOP_result[] __attribute__ ((aligned (1))) = {
void
set_res_apdu (uint8_t sw1, uint8_t sw2)
{
res_APDU_size = 2;
res_APDU[0] = sw1;
res_APDU[1] = sw2;
apdu.sw = (sw1 << 8) | sw2;
}
#define FILE_NONE 0
@@ -117,40 +120,15 @@ static void
cmd_verify (void)
{
int len;
uint8_t p2 = cmd_APDU[3];
uint8_t p2 = P2 (apdu);
int r;
int data_start = 5;
const uint8_t *pw;
DEBUG_INFO (" - VERIFY\r\n");
DEBUG_BYTE (p2);
#if defined(PINPAD_SUPPORT)
if (cmd_APDU_size == 4)
/* Verification with pinpad */
{
len = get_pinpad_input (PIN_INPUT_CURRENT);
if (len < 0)
{
GPG_ERROR ();
return;
}
pw = pin_input_buffer;
}
else
#endif
{
len = cmd_APDU[4];
if (len == 0) /* extended length */
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
pw = &cmd_APDU[data_start];
}
len = apdu.cmd_apdu_data_len;
pw = apdu.cmd_apdu_data;
if (p2 == 0x81)
r = verify_pso_cds (pw, len);
@@ -231,7 +209,8 @@ cmd_change_password (void)
uint8_t old_ks[KEYSTRING_MD_SIZE];
uint8_t new_ks0[KEYSTRING_MD_SIZE+1];
uint8_t *new_ks = &new_ks0[1];
uint8_t p2 = cmd_APDU[3];
uint8_t p1 = P1 (apdu); /* 0: change (old+new), 1: exchange (new) */
uint8_t p2 = P2 (apdu);
int len;
const uint8_t *pw;
const uint8_t *newpw;
@@ -242,62 +221,20 @@ cmd_change_password (void)
DEBUG_INFO ("Change PW\r\n");
DEBUG_BYTE (who);
#if defined(PINPAD_SUPPORT)
if (cmd_APDU_size == 4)
/* Modification with pinpad */
len = apdu.cmd_apdu_data_len;
pw = apdu.cmd_apdu_data;
if (p1 != 0)
{
pw_len = get_pinpad_input (PIN_INPUT_CURRENT);
if (pw_len < 0)
{
GPG_ERROR ();
return;
}
pw = &cmd_APDU[5];
memcpy (&cmd_APDU[5], pin_input_buffer, pw_len);
newpw = pw + pw_len;
newpw_len = get_pinpad_input (PIN_INPUT_NEW);
if (newpw_len < 0)
{
GPG_ERROR ();
return;
}
memcpy (&cmd_APDU[5]+pw_len, pin_input_buffer, newpw_len);
len = get_pinpad_input (PIN_INPUT_CONFIRM);
if (len < 0)
{
GPG_ERROR ();
return;
}
if (len != newpw_len || memcmp (newpw, pin_input_buffer, len) != 0)
{
GPG_SECURITY_FAILURE ();
return;
}
len = cmd_APDU[4] = pw_len + newpw_len;
}
else
#endif
{
len = cmd_APDU[4];
pw = &cmd_APDU[5];
if (len == 0) /* extended length */
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
pw += 2;
}
GPG_FUNCTION_NOT_SUPPORTED();
return;
}
if (who == BY_USER) /* PW1 */
{
const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1);
pw_len = verify_user_0 (pw, len, -1, ks_pw1);
pw_len = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, len, -1, ks_pw1);
if (pw_len < 0)
{
@@ -383,7 +320,7 @@ cmd_change_password (void)
static void
cmd_reset_user_password (void)
{
uint8_t p1 = cmd_APDU[2];
uint8_t p1 = P1 (apdu);
int len;
const uint8_t *pw;
const uint8_t *newpw;
@@ -395,61 +332,8 @@ cmd_reset_user_password (void)
DEBUG_INFO ("Reset PW1\r\n");
DEBUG_BYTE (p1);
#if defined(PINPAD_SUPPORT)
if (cmd_APDU_size == 4)
/* Modification with pinpad */
{
if (p1 == 0x00) /* by User with Reseting Code */
{
pw_len = get_pinpad_input (PIN_INPUT_CURRENT);
if (pw_len < 0)
{
GPG_ERROR ();
return;
}
memcpy (&cmd_APDU[5], pin_input_buffer, pw_len);
}
else
pw_len = 0;
pw = &cmd_APDU[5];
newpw = pw + pw_len;
newpw_len = get_pinpad_input (PIN_INPUT_NEW);
if (newpw_len < 0)
{
GPG_ERROR ();
return;
}
memcpy (&cmd_APDU[5]+pw_len, pin_input_buffer, newpw_len);
len = get_pinpad_input (PIN_INPUT_CONFIRM);
if (len < 0)
{
GPG_ERROR ();
return;
}
if (len != newpw_len || memcmp (newpw, pin_input_buffer, len) != 0)
{
GPG_SECURITY_FAILURE ();
return;
}
len = cmd_APDU[4] = pw_len + newpw_len;
}
else
#endif
{
len = cmd_APDU[4];
pw = &cmd_APDU[5];
if (len == 0) /* extended length */
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
pw += 2;
}
}
len = apdu.cmd_apdu_data_len;
pw = apdu.cmd_apdu_data;
if (p1 == 0x00) /* by User with Reseting Code */
{
@@ -571,16 +455,9 @@ cmd_put_data (void)
if (file_selection != FILE_DF_OPENPGP)
GPG_NO_RECORD();
tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
len = cmd_APDU_size - 5;
data = &cmd_APDU[5];
if (len >= 256)
/* extended Lc */
{
data += 2;
len -= 2;
}
tag = ((P1 (apdu)<<8) | P2 (apdu));
len = apdu.cmd_apdu_data_len;
data = apdu.cmd_apdu_data;
gpg_do_put_data (tag, data, len);
}
@@ -588,16 +465,11 @@ static void
cmd_pgp_gakp (void)
{
DEBUG_INFO (" - Generate Asymmetric Key Pair\r\n");
DEBUG_BYTE (cmd_APDU[2]);
DEBUG_BYTE (P1 (apdu));
if (cmd_APDU[2] == 0x81)
if (P1 (apdu) == 0x81)
/* Get public key */
{
if (cmd_APDU[4] == 0)
gpg_do_public_key (cmd_APDU[7]);
else
gpg_do_public_key (cmd_APDU[5]);
}
gpg_do_public_key (apdu.cmd_apdu_data[0]);
else
{ /* Generate key pair */
if (!ac_check_status (AC_ADMIN_AUTHORIZED))
@@ -615,7 +487,7 @@ cmd_read_binary (void)
if (file_selection == FILE_EF_SERIAL)
{
if (cmd_APDU[3] >= 6)
if (P2 (apdu) >= 6)
GPG_BAD_P0_P1 ();
else
{
@@ -630,16 +502,23 @@ cmd_read_binary (void)
static void
cmd_select_file (void)
{
if (cmd_APDU[2] == 4) /* Selection by DF name: it must be OpenPGP card */
if (P1 (apdu) == 4) /* Selection by DF name */
{
DEBUG_INFO (" - select DF by name\r\n");
/*
* P2 == 0, LC=6, name = D2 76 00 01 24 01
*/
/* name = D2 76 00 01 24 01 */
if (apdu.cmd_apdu_data_len != 6
|| memcmp (openpgpcard_aid, apdu.cmd_apdu_data, 6) != 0)
{
DEBUG_WORD (apdu.cmd_apdu_data_len);
DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
GPG_NO_FILE ();
return;
}
file_selection = FILE_DF_OPENPGP;
if (cmd_APDU[3] == 0x0c) /* No FCI */
if ((P2 (apdu) & 0x0c) == 0x0c) /* No FCI */
GPG_SUCCESS ();
else
{
@@ -651,8 +530,8 @@ cmd_select_file (void)
res_APDU_size += 2;
}
}
else if (cmd_APDU[4] == 2
&& cmd_APDU[5] == 0x2f && cmd_APDU[6] == 0x02)
else if (apdu.cmd_apdu_data_len == 2
&& apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02)
{
DEBUG_INFO (" - select 0x2f02 EF\r\n");
/*
@@ -661,11 +540,11 @@ cmd_select_file (void)
GPG_SUCCESS ();
file_selection = FILE_EF_SERIAL;
}
else if (cmd_APDU[4] == 2
&& cmd_APDU[5] == 0x3f && cmd_APDU[6] == 0x00)
else if (apdu.cmd_apdu_data_len == 2
&& apdu.cmd_apdu_data[0] == 0x3f && apdu.cmd_apdu_data[1] == 0x00)
{
DEBUG_INFO (" - select ROOT MF\r\n");
if (cmd_APDU[3] == 0x0c)
if (P2 (apdu) == 0x0c)
{
GPG_SUCCESS ();
}
@@ -673,12 +552,11 @@ cmd_select_file (void)
{
int len = sizeof (select_file_TOP_result);
res_APDU_size = 2 + len;
res_APDU_size = len;
memcpy (res_APDU, select_file_TOP_result, len);
res_APDU[2] = (data_objects_number_of_bytes & 0xff);
res_APDU[3] = (data_objects_number_of_bytes >> 8);
res_APDU[len] = 0x90;
res_APDU[len+1] = 0x00;
GPG_SUCCESS ();
}
file_selection = FILE_MF;
@@ -696,7 +574,7 @@ cmd_select_file (void)
static void
cmd_get_data (void)
{
uint16_t tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
uint16_t tag = ((P1 (apdu)<<8) | P2 (apdu));
DEBUG_INFO (" - Get Data\r\n");
@@ -709,21 +587,14 @@ cmd_get_data (void)
static void
cmd_pso (void)
{
int len = cmd_APDU[4];
int data_start = 5;
int len = apdu.cmd_apdu_data_len;
int r;
if (len == 0)
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
DEBUG_INFO (" - PSO: ");
DEBUG_WORD ((uint32_t)&r);
DEBUG_BINARY (cmd_APDU, cmd_APDU_size);
DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
if (cmd_APDU[2] == 0x9e && cmd_APDU[3] == 0x9a)
if (P1 (apdu) == 0x9e && P2 (apdu) == 0x9a)
{
if (!ac_check_status (AC_PSO_CDS_AUTHORIZED))
{
@@ -732,24 +603,24 @@ cmd_pso (void)
return;
}
if (cmd_APDU_size != 7 + 34 + 2 /* MD5 */
/* Header (with Extended Lc)=7, size of digestInfo, and Le=2-byte */
&& cmd_APDU_size != 7 + 35 + 2 /* SHA1 / RIPEMD-160 */
&& cmd_APDU_size != 7 + 47 + 2 /* SHA224 */
&& cmd_APDU_size != 7 + 51 + 2 /* SHA256 */
&& cmd_APDU_size != 7 + 67 + 2 /* SHA384 */
&& cmd_APDU_size != 7 + 83 + 2) /* SHA512 */
/* Check size of digestInfo */
if (len != 34 /* MD5 */
&& len != 35 /* SHA1 / RIPEMD-160 */
&& len != 47 /* SHA224 */
&& len != 51 /* SHA256 */
&& len != 67 /* SHA384 */
&& len != 83) /* SHA512 */
{
DEBUG_INFO (" wrong length: ");
DEBUG_SHORT (cmd_APDU_size);
DEBUG_SHORT (len);
GPG_ERROR ();
}
else
{
DEBUG_SHORT (len); /* Should be cmd_APDU_size - 8 [- 1] */
DEBUG_SHORT (len);
DEBUG_BINARY (&kd[GPG_KEY_FOR_SIGNING], KEY_CONTENT_LEN);
r = rsa_sign (&cmd_APDU[data_start], res_APDU, len,
r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
&kd[GPG_KEY_FOR_SIGNING]);
if (r < 0)
{
@@ -761,7 +632,7 @@ cmd_pso (void)
gpg_increment_digital_signature_counter ();
}
}
else if (cmd_APDU[2] == 0x80 && cmd_APDU[3] == 0x86)
else if (P1 (apdu) == 0x80 && P2 (apdu) == 0x86)
{
DEBUG_SHORT (len);
DEBUG_BINARY (&kd[GPG_KEY_FOR_DECRYPTION], KEY_CONTENT_LEN);
@@ -774,9 +645,8 @@ cmd_pso (void)
}
/* Skip padding 0x00 */
data_start++;
len--;
r = rsa_decrypt (&cmd_APDU[data_start], res_APDU, len,
r = rsa_decrypt (apdu.cmd_apdu_data+1, res_APDU, len,
&kd[GPG_KEY_FOR_DECRYPTION]);
if (r < 0)
GPG_ERROR ();
@@ -784,9 +654,9 @@ cmd_pso (void)
else
{
DEBUG_INFO (" - ??");
DEBUG_BYTE (cmd_APDU[2]);
DEBUG_BYTE (P1 (apdu));
DEBUG_INFO (" - ??");
DEBUG_BYTE (cmd_APDU[3]);
DEBUG_BYTE (P2 (apdu));
GPG_ERROR ();
}
@@ -796,19 +666,12 @@ cmd_pso (void)
static void
cmd_internal_authenticate (void)
{
int len = cmd_APDU[4];
int data_start = 5;
int len = apdu.cmd_apdu_data_len;
int r;
if (len == 0)
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n");
if (cmd_APDU[2] == 0x00 && cmd_APDU[3] == 0x00)
if (P1 (apdu) == 0x00 && P2 (apdu) == 0x00)
{
DEBUG_SHORT (len);
@@ -819,7 +682,7 @@ cmd_internal_authenticate (void)
return;
}
r = rsa_sign (&cmd_APDU[data_start], res_APDU, len,
r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
&kd[GPG_KEY_FOR_AUTHENTICATION]);
if (r < 0)
GPG_ERROR ();
@@ -827,9 +690,9 @@ cmd_internal_authenticate (void)
else
{
DEBUG_INFO (" - ??");
DEBUG_BYTE (cmd_APDU[2]);
DEBUG_BYTE (P1 (apdu));
DEBUG_INFO (" - ??");
DEBUG_BYTE (cmd_APDU[3]);
DEBUG_BYTE (P2 (apdu));
GPG_ERROR ();
}
@@ -840,17 +703,10 @@ cmd_internal_authenticate (void)
static void
cmd_update_binary (void)
{
int len = cmd_APDU[4];
int data_start = 5;
int len = apdu.cmd_apdu_data_len;
uint16_t offset;
int r;
if (len == 0)
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
DEBUG_INFO (" - UPDATE BINARY\r\n");
if (!ac_check_status (AC_ADMIN_AUTHORIZED))
@@ -860,10 +716,10 @@ cmd_update_binary (void)
return;
}
if ((cmd_APDU[2] & 0x80))
if ((cmd_APDU[2] & 0x7f) <= FILEID_RANDOM)
if ((P1 (apdu) & 0x80))
if ((P1 (apdu) & 0x7f) <= FILEID_RANDOM)
{
file_selection = FILE_EF_CH_CERTIFICATE + (cmd_APDU[2] & 0x7f);
file_selection = FILE_EF_CH_CERTIFICATE + (P1 (apdu) & 0x7f);
r = flash_erase_binary (file_selection - FILE_EF_CH_CERTIFICATE);
if (r < 0)
{
@@ -888,14 +744,14 @@ cmd_update_binary (void)
return;
}
offset = (cmd_APDU[2] << 8) | cmd_APDU[3];
offset = (P1 (apdu) << 8) | P2 (apdu);
}
DEBUG_SHORT (len);
DEBUG_SHORT (offset);
r = flash_write_binary (file_selection - FILE_EF_CH_CERTIFICATE,
&cmd_APDU[data_start], len, offset);
apdu.cmd_apdu_data, len, offset);
if (r < 0)
{
DEBUG_INFO ("memory error.\r\n");
@@ -911,17 +767,10 @@ cmd_update_binary (void)
static void
cmd_write_binary (void)
{
int len = cmd_APDU[4];
int data_start = 5;
int len = apdu.cmd_apdu_data_len;
uint16_t offset;
int r;
if (len == 0)
{
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
DEBUG_INFO (" - WRITE BINARY\r\n");
if (!ac_check_status (AC_ADMIN_AUTHORIZED))
@@ -931,10 +780,10 @@ cmd_write_binary (void)
return;
}
if ((cmd_APDU[2] & 0x80))
if ((cmd_APDU[2] & 0x7f) <= FILEID_SERIAL_NO)
if ((P1 (apdu) & 0x80))
if ((P1 (apdu) & 0x7f) <= FILEID_SERIAL_NO)
{
file_selection = FILE_EF_CH_CERTIFICATE + (cmd_APDU[2] & 0x7f);
file_selection = FILE_EF_CH_CERTIFICATE + (P1 (apdu) & 0x7f);
offset = 0;
}
else
@@ -952,14 +801,14 @@ cmd_write_binary (void)
return;
}
offset = (cmd_APDU[2] << 8) | cmd_APDU[3];
offset = (P1 (apdu) << 8) | P2 (apdu);
}
DEBUG_SHORT (len);
DEBUG_SHORT (offset);
r = flash_write_binary (file_selection - FILE_EF_CH_CERTIFICATE,
&cmd_APDU[data_start], len, offset);
apdu.cmd_apdu_data, len, offset);
if (r < 0)
{
DEBUG_INFO ("memory error.\r\n");
@@ -999,7 +848,7 @@ static void
process_command_apdu (void)
{
int i;
uint8_t cmd = cmd_APDU[1];
uint8_t cmd = INS (apdu);
for (i = 0; i < NUM_CMDS; i++)
if (cmds[i].command == cmd)
@@ -1026,17 +875,99 @@ GPGthread (void *arg)
while (!chThdShouldTerminate ())
{
chEvtWaitOne (ALL_EVENTS);
eventmask_t m = chEvtWaitOne (ALL_EVENTS);
#if defined(PINPAD_SUPPORT)
int len, pw_len, newpw_len;
#endif
DEBUG_INFO ("GPG!: ");
res_APDU_pointer = NULL; /* default */
if (icc_data_size != 0)
if (m == EV_VERIFY_CMD_AVAILABLE)
{
process_command_apdu ();
chEvtSignal (icc_thread, EV_EXEC_FINISHED);
#if defined(PINPAD_SUPPORT)
if (INS (apdu) != INS_VERIFY)
{
GPG_CONDITION_NOT_SATISFIED ();
goto done;
}
pw_len = get_pinpad_input (PIN_INPUT_CURRENT);
if (pw_len < 0)
{
GPG_ERROR ();
goto done;
}
memcpy (apdu.cmd_apdu_data, pin_input_buffer, pw_len);
apdu.cmd_apdu_data_len = pw_len;
#else
GPG_ERROR ();
goto done;
#endif
}
else if (m == EV_MODIFY_CMD_AVAILABLE)
{
#if defined(PINPAD_SUPPORT)
uint8_t bConfirmPIN = apdu.cmd_apdu_data[5];
uint8_t *p = apdu.cmd_apdu_data;
if (INS (apdu) != INS_CHANGE_REFERENCE_DATA
&& INS (apdu) != INS_RESET_RETRY_COUNTER
&& INS (apdu) != INS_PUT_DATA)
{
GPG_CONDITION_NOT_SATISFIED ();
goto done;
}
if ((bConfirmPIN & 2)) /* Require old PIN */
{
pw_len = get_pinpad_input (PIN_INPUT_CURRENT);
if (pw_len < 0)
{
GPG_ERROR ();
goto done;
}
memcpy (p, pin_input_buffer, pw_len);
p += pw_len;
}
else
pw_len = 0;
newpw_len = get_pinpad_input (PIN_INPUT_NEW);
if (newpw_len < 0)
{
GPG_ERROR ();
goto done;
}
memcpy (p, pin_input_buffer, newpw_len);
if ((bConfirmPIN & 1)) /* New PIN twice */
{
len = get_pinpad_input (PIN_INPUT_CONFIRM);
if (len < 0)
{
GPG_ERROR ();
goto done;
}
if (len != newpw_len || memcmp (p, pin_input_buffer, len) != 0)
{
GPG_SECURITY_FAILURE ();
goto done;
}
}
apdu.cmd_apdu_data_len = pw_len + newpw_len;
#else
GPG_ERROR ();
goto done;
#endif
}
else if (m == EV_NOP)
continue;
process_command_apdu ();
done:
chEvtSignal (icc_thread, EV_EXEC_FINISHED);
}
gpg_fini ();

View File

@@ -1,7 +1,9 @@
#define GPG_MEMORY_FAILURE() set_res_apdu (0x65, 0x81)
#define GPG_SECURITY_FAILURE() set_res_apdu (0x69, 0x82)
#define GPG_SECURITY_AUTH_BLOCKED() set_res_apdu (0x69, 0x83)
#define GPG_CONDITION_NOT_SATISFIED() set_res_apdu (0x69, 0x85)
#define GPG_COMMAND_NOT_ALLOWED() set_res_apdu (0x69, 0x86)
#define GPG_FUNCTION_NOT_SUPPORTED() set_res_apdu (0x6a, 0x81)
#define GPG_NO_FILE() set_res_apdu (0x6a, 0x82)
#define GPG_NO_RECORD() set_res_apdu (0x6a, 0x88)
#define GPG_BAD_P0_P1() set_res_apdu (0x6b, 0x00)

View File

@@ -910,7 +910,7 @@ cir_timer_interrupt (void)
cir_input_last = now;
/* Notify thread */
if (pin_thread)
chEvtSignalI (pin_thread, (eventmask_t)1);
chEvtSignalI (pin_thread, EV_PINPAD_INPUT_DONE);
}
#if defined(DEBUG_CIR)

View File

@@ -117,13 +117,12 @@ blink_dp (void)
}
static Thread *pin_thread;
#define EV_SW_PUSH (eventmask_t)1
void
dial_sw_interrupt (void)
{
dial_sw_disable ();
chEvtSignalI (pin_thread, EV_SW_PUSH);
chEvtSignalI (pin_thread, EV_PINPAD_INPUT_DONE);
palClearPad (IOPORT1, GPIOA_LED2);
}
@@ -157,7 +156,7 @@ pinpad_getline (int msg_code, systime_t timeout)
dial_sw_enable ();
m = chEvtWaitOneTimeout (ALL_EVENTS, LED_DISP_BLINK_INTERVAL0);
if (m == EV_SW_PUSH || sw_push_count)
if (m == EV_PINPAD_INPUT_DONE || sw_push_count)
{
if (palReadPad (IOPORT2, GPIOB_BUTTON) == 0)
sw_push_count++;

372
src/pin-dnd.c Normal file
View File

@@ -0,0 +1,372 @@
/*
* pin-dnd.c -- PIN input support (Drag and Drop with File Manager)
*
* Copyright (C) 2011 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
*
* Gnuk is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Gnuk is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "ch.h"
#include "board.h"
#include "gnuk.h"
#include "usb_msc.h"
struct folder {
uint8_t parent;
uint8_t children[7];
};
static struct folder folders[8];
static const struct folder folder_ini = { 0, { 1, 2, 3, 4, 5, 6, 7 } };
uint8_t pin_input_buffer[MAX_PIN_CHARS];
uint8_t pin_input_len;
static Thread *pin_thread;
/*
* Let user input PIN string.
* Return length of the string.
* The string itself is in PIN_INPUT_BUFFER.
*/
int
pinpad_getline (int msg_code, systime_t timeout)
{
msg_t msg;
(void)msg_code;
(void)timeout;
DEBUG_INFO (">>>\r\n");
pin_input_len = 0;
msc_media_insert_change (1);
memset (folders, 0, sizeof folders);
memcpy (folders, &folder_ini, sizeof folder_ini);
while (1)
{
chSysLock ();
pin_thread = chThdSelf ();
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
led_blink (0);
if (msg != 0)
break;
}
msc_media_insert_change (0);
if (msg == 1)
return pin_input_len;
else
return -1; /* cancel */
}
static void pinpad_input (void)
{
chSysLock ();
pin_thread->p_u.rdymsg = 0;
chSchReadyI (pin_thread);
chSysUnlock ();
}
static void pinpad_finish_entry (int cancel)
{
chSysLock ();
if (cancel)
pin_thread->p_u.rdymsg = 2;
else
pin_thread->p_u.rdymsg = 1;
chSchReadyI (pin_thread);
chSysUnlock ();
}
#define TOTAL_SECTOR 68
/*
blk=0: master boot record sector
blk=1: fat0
blk=2: fat1
blk=3: root directory
blk=4: fat cluster #2
...
blk=4+63: fat cluster #2+63
*/
static const uint8_t d0_0_sector[] = {
0xeb, 0x3c, /* Jump instruction */
0x90, /* NOP */
0x6d, 0x6b, 0x64, 0x6f, 0x73, 0x66, 0x73, 0x20, /* "mkdosfs " */
0x00, 0x02, /* Bytes per sector: 512 */
0x01, /* sectors per cluster: 1 */
0x01, 0x00, /* reserved sector count: 1 */
0x02, /* Number of FATs: 2 */
0x10, 0x00, /* Max. root directory entries: 16 (1 sector) */
TOTAL_SECTOR, 0x00, /* total sectors: 68 */
0xf8, /* media descriptor: fixed disk */
0x01, 0x00, /* sectors per FAT: 1 */
0x04, 0x00, /* sectors per track: 4 */
0x01, 0x00, /* number of heads: 1 */
0x00, 0x00, 0x00, 0x00, /* hidden sectors: 0 */
0x00, 0x00, 0x00, 0x00, /* total sectors (long) */
0x00, /* drive number */
0x00, /* reserved */
0x29, /* extended boot signature */
0xbf, 0x86, 0x75, 0xea, /* Volume ID (serial number) (Little endian) */
/* Volume label: DNDpinentry */
'D', 'n', 'D', 'p', 'i', 'n', 'e', 'n', 't', 'r', 'y',
0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, /* FAT12 */
0x0e, /* push cs */
0x1f, /* pop ds */
0xbe, 0x5b, 0x7c, /* mov si, offset message_txt */
0xac, /* 1: lodsb */
0x22, 0xc0, /* and al, al */
0x74, 0x0b, /* jz 2f */
0x56, /* push si */
0xb4, 0x0e, /* mov ah, 0eh */
0xbb, 0x07, 0x00, /* mov bx, 0007h */
0xcd, 0x10, /* int 10h ; output char color=white */
0x5e, /* pop si */
0xeb, 0xf0, /* jmp 1b */
0x32, 0xe4, /* 2: xor ah, ah */
0xcd, 0x16, /* int 16h; key input */
0xcd, 0x19, /* int 19h; load OS */
0xeb, 0xfe, /* 3: jmp 3b */
/* "This is not a bootable disk... \r\n" */
0x54, 0x68, 0x69, 0x73, 0x20,
0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61,
0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20,
0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61,
0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79,
0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20,
0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e,
0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
};
static const uint8_t d0_fat0_sector[] = {
0xf8, 0xff, 0xff, /* Media descriptor: fixed disk */ /* EOC */
0xff, 0xff, 0xff, /* cluster 2: used */ /* cluster 3: used */
0xff, 0xff, 0xff, /* cluster 4: used */ /* cluster 5: used */
0xff, 0xff, 0xff, /* cluster 6: used */ /* cluster 7: used */
0xff, 0x0f, 0x00, /* cluster 8: used */ /* cluster 9: free */
};
static uint8_t the_sector[512];
#define FOLDER_INDEX_TO_CLUSTER_NO(i) (i+1)
#define CLUSTER_NO_TO_FOLDER_INDEX(n) (n-1)
#define FOLDER_INDEX_TO_LBA(i) (i+3)
#define LBA_TO_FOLDER_INDEX(lba) (lba-3)
#define FOLDER_INDEX_TO_DIRCHAR(i) ('A'+i-1)
#define DIRCHAR_TO_FOLDER_INDEX(c) (c - 'A' + 1)
static uint8_t *fill_file_entry (uint8_t *p, const uint8_t *filename,
uint16_t cluster_no)
{
memcpy (p, filename, 8+3);
p += 11;
*p++ = 0x10; /* directory */
*p++ = 0x00; /* reserved */
memcpy (p, "\x64\x3b\xa7\x61\x3f", 5); /* create time */
p += 5;
memcpy (p, "\x61\x3f", 2); /* last access */
p += 2;
*p++ = 0x00; *p++ = 0x00; /* ea-index */
memcpy (p, "\x3b\xa7\x61\x3f", 4); /* last modified */
p += 4;
memcpy (p, &cluster_no, 2); /* cluster # */
p += 2;
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; /* file size */
return p;
}
static void build_directory_sector (uint8_t *p, uint8_t index)
{
uint16_t cluster_no = FOLDER_INDEX_TO_CLUSTER_NO (index);
int i;
uint8_t filename[11] = { 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20 };
uint8_t child;
memset (p, 0, 512);
if (index != 0)
{
p = fill_file_entry (p, filename, cluster_no);
filename[1] = 0x2e;
p = fill_file_entry (p, filename, 0);
filename[1] = 0x20;
}
for (i = 0; i < 7; i++)
if ((child = folders[index].children[i]) != 0)
{
filename[0] = FOLDER_INDEX_TO_DIRCHAR (child);
p = fill_file_entry (p, filename, FOLDER_INDEX_TO_CLUSTER_NO (child));
}
else
break;
}
int
msc_scsi_read (uint32_t lba, const uint8_t **sector_p)
{
if (!media_available)
return SCSI_ERROR_NOT_READY;
if (lba >= TOTAL_SECTOR)
return SCSI_ERROR_ILLEAGAL_REQUEST;
switch (lba)
{
case 0:
*sector_p = the_sector;
memcpy (the_sector, d0_0_sector, sizeof d0_0_sector);
memset (the_sector + sizeof d0_0_sector, 0, 512 - sizeof d0_0_sector);
the_sector[510] = 0x55;
the_sector[511] = 0xaa;
return 0;
case 1:
case 2:
*sector_p = the_sector;
memcpy (the_sector, d0_fat0_sector, sizeof d0_fat0_sector);
memset (the_sector + sizeof d0_fat0_sector, 0,
512 - sizeof d0_fat0_sector);
return 0;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
*sector_p = the_sector;
build_directory_sector (the_sector, LBA_TO_FOLDER_INDEX (lba));
return 0;
default:
*sector_p = the_sector;
memset (the_sector, 0, 512);
return 0;
}
}
static void parse_directory_sector (const uint8_t *p, uint8_t index)
{
int i;
uint8_t child;
int input = 0;
int num_children = 0;
if (index != 0)
{
uint16_t cluster_no;
uint8_t dest_index;
p += 32; /* skip "." */
/* ".." */
cluster_no = p[26] | (p[27] << 8);
dest_index = CLUSTER_NO_TO_FOLDER_INDEX (cluster_no);
if (dest_index < 1 || dest_index > 7)
; /* it can be 255 : root_dir */
else
if (pin_input_len < MAX_PIN_CHARS - 2)
{
pin_input_buffer[pin_input_len++]
= FOLDER_INDEX_TO_DIRCHAR (index);
pin_input_buffer[pin_input_len++]
= FOLDER_INDEX_TO_DIRCHAR (dest_index);
input = 1;
}
p += 32;
}
for (i = 0; i < 7; i++)
{
if (*p >= 'A' && *p <= 'G')
{
child = DIRCHAR_TO_FOLDER_INDEX (*p);
folders[index].children[i] = child;
num_children++;
}
else
folders[index].children[i] = 0;
p += 32;
}
if (index == 0 && num_children == 1)
pinpad_finish_entry (0);
else if (input)
pinpad_input ();
}
int
msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size)
{
(void)size;
if (!media_available)
return SCSI_ERROR_NOT_READY;
if (lba >= TOTAL_SECTOR)
return SCSI_ERROR_ILLEAGAL_REQUEST;
if (lba == 1)
return 0; /* updating FAT, just ignore */
if (lba <= 2 || lba >= 11)
return SCSI_ERROR_DATA_PROTECT;
else
{
uint8_t index = LBA_TO_FOLDER_INDEX (lba);
parse_directory_sector (buf, index);
return 0;
}
}
void
msc_scsi_stop (uint8_t code)
{
(void)code;
pinpad_finish_entry (1);
}

View File

@@ -1,7 +1,7 @@
STMUSBDIR = ../STM32_USB-FS-Device_Driver
STMUSBSRCDIR = $(STMUSBDIR)/src
STMUSBINCDIR = $(STMUSBDIR)/inc
STMUSBSRC= $(STMUSBSRCDIR)/usb_sil.c \
STMUSBSRC= \
$(STMUSBSRCDIR)/usb_init.c $(STMUSBSRCDIR)/usb_int.c \
$(STMUSBSRCDIR)/usb_mem.c $(STMUSBSRCDIR)/usb_core.c \
$(STMUSBSRCDIR)/usb_regs.c

View File

@@ -1,7 +1,7 @@
/*
* usb-icc.c -- USB CCID/ICCD protocol handling
*
* Copyright (C) 2010, 2011 Free Software Initiative of Japan
* Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -26,10 +26,12 @@
#include "hal.h"
#include "gnuk.h"
#include "usb_lib.h"
#include "usb_desc.h"
#include "usb_mem.h"
#include "hw_config.h"
#include "usb_istr.h"
#include "usb_lld.h"
struct apdu apdu;
#define ICC_SET_PARAMS 0x61 /* non-ICCD command */
#define ICC_POWER_ON 0x62
@@ -74,7 +76,7 @@ struct icc_header {
uint16_t param;
} __attribute__((packed));
int icc_data_size;
static int icc_data_size;
/*
* USB-ICC communication could be considered "half duplex".
@@ -94,8 +96,8 @@ int icc_data_size;
* The buffer will be filled by multiple RX transactions (Bulk-OUT)
* or will be used for multiple TX transactions (Bulk-IN)
*/
uint8_t icc_buffer[USB_BUF_SIZE];
uint8_t icc_seq;
static uint8_t icc_buffer[USB_BUF_SIZE];
static uint8_t icc_seq;
/*
* Pointer to ICC_BUFFER
@@ -132,8 +134,7 @@ EP1_IN_Callback (void)
{
/* Send the last 0-DATA transcation of Bulk-IN in the transactions */
icc_next_p = NULL;
USB_SIL_Write (EP1_IN, icc_buffer, 0);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, icc_buffer, 0);
}
else
{
@@ -147,8 +148,7 @@ EP1_IN_Callback (void)
tx_size = &icc_buffer[icc_tx_size] - p;
}
USB_SIL_Write (EP1_IN, p, tx_size);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, p, tx_size);
}
}
@@ -160,7 +160,7 @@ icc_prepare_receive (int chain)
else
icc_next_p = icc_buffer;
SetEPRxValid (ENDP2);
usb_lld_rx_enable (ENDP2);
}
/*
@@ -170,13 +170,33 @@ void
EP2_OUT_Callback (void)
{
int len;
struct icc_header *icc_header;
int data_len_so_far;
int data_len;
len = USB_SIL_Read (EP2_OUT, icc_next_p);
len = usb_lld_rx_data_len (ENDP2);
usb_lld_rxcpy (icc_next_p, ENDP2, 0, len);
if (len == 0)
{ /* Just ignore Zero Length Packet (ZLP), if any */
usb_lld_rx_enable (ENDP2);
return;
}
if (len == USB_LL_BUF_SIZE) /* The sequence of transactions continues */
icc_next_p += len;
if (icc_chain_p)
icc_header = (struct icc_header *)icc_chain_p;
else
icc_header = (struct icc_header *)icc_buffer;
data_len = icc_header->data_len; /* NOTE: We're little endian */
data_len_so_far = (icc_next_p - (uint8_t *)icc_header) - ICC_MSG_HEADER_SIZE;
if (len == USB_LL_BUF_SIZE
&& data_len != data_len_so_far)
/* The sequence of transactions continues */
{
icc_next_p += USB_LL_BUF_SIZE;
SetEPRxValid (ENDP2);
usb_lld_rx_enable (ENDP2);
if ((icc_next_p - icc_buffer) >= USB_BUF_SIZE)
/* No room to receive any more */
{
@@ -187,27 +207,18 @@ EP2_OUT_Callback (void)
* (and discard the whole block)
*/
}
/*
* NOTE: It is possible a transaction may stall when the size of
* BULK_OUT transaction it's bigger than USB_BUF_SIZE and stops
* with just USB_LL_BUF_SIZE packet. Device will remain waiting
* another packet.
*/
}
else /* Finished */
{
struct icc_header *icc_header;
int data_len;
icc_next_p += len;
if (icc_chain_p)
{
icc_header = (struct icc_header *)icc_chain_p;
icc_data_size = (icc_next_p - icc_chain_p) - ICC_MSG_HEADER_SIZE;
}
else
{
icc_header = (struct icc_header *)icc_buffer;
icc_data_size = (icc_next_p - icc_buffer) - ICC_MSG_HEADER_SIZE;
}
/* NOTE: We're little endian, nothing to convert */
data_len = icc_header->data_len;
icc_seq = icc_header->seq;
icc_data_size = data_len_so_far;
icc_seq = icc_header->seq; /* NOTE: We're little endian */
if (icc_data_size != data_len)
{
@@ -289,8 +300,7 @@ icc_error (int offset)
icc_next_p = NULL; /* This is a single transaction Bulk-IN */
icc_tx_size = ICC_MSG_HEADER_SIZE;
USB_SIL_Write (EP1_IN, icc_reply, icc_tx_size);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, icc_reply, icc_tx_size);
}
static Thread *gpg_thread;
@@ -323,8 +333,7 @@ icc_power_on (void)
icc_next_p = NULL; /* This is a single transaction Bulk-IN */
icc_tx_size = ICC_MSG_HEADER_SIZE + size_atr;
USB_SIL_Write (EP1_IN, icc_buffer, icc_tx_size);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, icc_buffer, icc_tx_size);
DEBUG_INFO ("ON\r\n");
return ICC_STATE_WAIT;
@@ -358,8 +367,7 @@ icc_send_status (void)
icc_next_p = NULL; /* This is a single transaction Bulk-IN */
icc_tx_size = ICC_MSG_HEADER_SIZE;
USB_SIL_Write (EP1_IN, icc_reply, icc_tx_size);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, icc_reply, icc_tx_size);
#ifdef DEBUG_MORE
DEBUG_INFO ("St\r\n");
@@ -374,7 +382,7 @@ icc_power_off (void)
if (gpg_thread)
{
chThdTerminate (gpg_thread);
chEvtSignal (gpg_thread, (eventmask_t)1);
chEvtSignal (gpg_thread, EV_NOP);
chThdWait (gpg_thread);
gpg_thread = NULL;
}
@@ -385,9 +393,6 @@ icc_power_off (void)
return ICC_STATE_START;
}
int res_APDU_size;
const uint8_t *res_APDU_pointer;
static void
icc_send_data_block (int len, uint8_t status, uint8_t chain)
{
@@ -419,8 +424,7 @@ icc_send_data_block (int len, uint8_t status, uint8_t chain)
else
icc_next_p = p + USB_LL_BUF_SIZE;
USB_SIL_Write (EP1_IN, p, tx_size);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, p, tx_size);
#ifdef DEBUG_MORE
DEBUG_INFO ("DATA\r\n");
#endif
@@ -449,8 +453,7 @@ icc_send_params (void)
icc_next_p = NULL; /* This is a single transaction Bulk-IN */
icc_tx_size = ICC_MSG_HEADER_SIZE + 7;
USB_SIL_Write (EP1_IN, icc_buffer, icc_tx_size);
SetEPTxValid (ENDP1);
usb_lld_write (ENDP1, icc_buffer, icc_tx_size);
#ifdef DEBUG_MORE
DEBUG_INFO ("DATA\r\n");
#endif
@@ -497,7 +500,88 @@ icc_handle_data (void)
{
if (icc_header->param == 0)
{ /* Give this message to GPG thread */
chEvtSignal (gpg_thread, (eventmask_t)1);
run_gpg_thread:
apdu.seq = icc_seq;
apdu.cmd_apdu_head = &icc_buffer[ICC_MSG_HEADER_SIZE];
apdu.sw = 0x9000;
apdu.res_apdu_data_len = 0;
apdu.res_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE];
if (icc_data_size < 4)
{
icc_error (ICC_MSG_HEADER_SIZE + icc_data_size);
next_state = ICC_STATE_WAIT;
break;
}
else if (icc_data_size == 4)
{ /* No Lc and No Le */
apdu.cmd_apdu_data = NULL;
apdu.cmd_apdu_data_len = 0;
apdu.expected_res_size = 0;
apdu.cmd_apdu_head[4] = 0;
}
else if (icc_data_size == 5)
{ /* No Lc but Le */
apdu.cmd_apdu_data = NULL;
apdu.cmd_apdu_data_len = 0;
apdu.expected_res_size = apdu.cmd_apdu_head[4];
if (apdu.expected_res_size == 0)
apdu.expected_res_size = 256;
apdu.cmd_apdu_head[4] = 0;
}
else if (apdu.cmd_apdu_head[4] == 0)
{ /* extended Lc or extended Le */
apdu.cmd_apdu_data = apdu.cmd_apdu_head + 7;
apdu.cmd_apdu_data_len
= (apdu.cmd_apdu_head[5] << 8) + apdu.cmd_apdu_head[6];
if (icc_data_size == 7)
{ /* No Lc but extended Le */
apdu.expected_res_size = apdu.cmd_apdu_data_len;
apdu.cmd_apdu_data_len = 0;
}
else if (icc_data_size == apdu.cmd_apdu_data_len + 7)
apdu.expected_res_size = 0;
else if (icc_data_size == apdu.cmd_apdu_data_len + 7 + 2)
{
apdu.expected_res_size
= (icc_buffer[ICC_MSG_HEADER_SIZE + 7
+ apdu.cmd_apdu_data_len] << 8)
| icc_buffer[ICC_MSG_HEADER_SIZE + 7
+ apdu.cmd_apdu_data_len + 1];
if (apdu.expected_res_size == 0)
apdu.expected_res_size = 65536;
}
else
{
icc_error (ICC_MSG_HEADER_SIZE + 5);
next_state = ICC_STATE_WAIT;
break;
}
}
else /* short Lc */
{
apdu.cmd_apdu_data = apdu.cmd_apdu_head + 5;
apdu.cmd_apdu_data_len = apdu.cmd_apdu_head[4];
if (icc_data_size == apdu.cmd_apdu_data_len + 5)
apdu.expected_res_size = 0;
else if (icc_data_size == apdu.cmd_apdu_data_len + 5 + 1)
{
apdu.expected_res_size
= icc_buffer[ICC_MSG_HEADER_SIZE + 5
+ apdu.cmd_apdu_data_len];
if (apdu.expected_res_size == 0)
apdu.expected_res_size = 256;
}
else
{
icc_error (ICC_MSG_HEADER_SIZE + 5);
next_state = ICC_STATE_WAIT;
break;
}
}
chEvtSignal (gpg_thread, EV_CMD_AVAILABLE);
next_state = ICC_STATE_EXECUTE;
}
else if (icc_header->param == 1)
@@ -519,12 +603,29 @@ icc_handle_data (void)
{
if (icc_buffer[10] == 0x00) /* PIN verification */
{
cmd_APDU[0] = icc_buffer[25];
cmd_APDU[1] = icc_buffer[26];
cmd_APDU[2] = icc_buffer[27];
cmd_APDU[3] = icc_buffer[28];
icc_data_size = 4;
chEvtSignal (gpg_thread, (eventmask_t)1);
icc_buffer[ICC_MSG_HEADER_SIZE+0] = icc_buffer[25];
icc_buffer[ICC_MSG_HEADER_SIZE+1] = icc_buffer[26];
icc_buffer[ICC_MSG_HEADER_SIZE+2] = icc_buffer[27];
icc_buffer[ICC_MSG_HEADER_SIZE+3] = icc_buffer[28];
/**/
icc_buffer[ICC_MSG_HEADER_SIZE+5] = 0; /* bConfirmPIN */
icc_buffer[ICC_MSG_HEADER_SIZE+6] = icc_buffer[17]; /* bEntryValidationCondition */
icc_buffer[ICC_MSG_HEADER_SIZE+7] = icc_buffer[18]; /* bNumberMessage */
icc_buffer[ICC_MSG_HEADER_SIZE+8] = icc_buffer[19]; /* wLangId L */
icc_buffer[ICC_MSG_HEADER_SIZE+9] = icc_buffer[20]; /* wLangId H */
icc_buffer[ICC_MSG_HEADER_SIZE+10] = icc_buffer[21]; /* bMsgIndex */
apdu.seq = icc_seq;
apdu.cmd_apdu_head = &icc_buffer[ICC_MSG_HEADER_SIZE+0];
apdu.cmd_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE+5];
apdu.cmd_apdu_data_len = 6;
apdu.expected_res_size = 0;
apdu.sw = 0x9000;
apdu.res_apdu_data_len = 0;
apdu.res_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE];
chEvtSignal (gpg_thread, EV_VERIFY_CMD_AVAILABLE);
next_state = ICC_STATE_EXECUTE;
}
else if (icc_buffer[10] == 0x01) /* PIN Modification */
@@ -535,12 +636,33 @@ icc_handle_data (void)
num_msgs = 1;
else if (num_msgs == 0xff)
num_msgs = 3;
cmd_APDU[0] = icc_buffer[27 + num_msgs];
cmd_APDU[1] = icc_buffer[28 + num_msgs];
cmd_APDU[2] = icc_buffer[29 + num_msgs];
cmd_APDU[3] = icc_buffer[30 + num_msgs];
icc_data_size = 4;
chEvtSignal (gpg_thread, (eventmask_t)1);
icc_buffer[ICC_MSG_HEADER_SIZE+0] = icc_buffer[27 + num_msgs];
icc_buffer[ICC_MSG_HEADER_SIZE+1] = icc_buffer[28 + num_msgs];
icc_buffer[ICC_MSG_HEADER_SIZE+2] = icc_buffer[29 + num_msgs];
icc_buffer[ICC_MSG_HEADER_SIZE+3] = icc_buffer[30 + num_msgs];
/**/
icc_buffer[ICC_MSG_HEADER_SIZE+5] = icc_buffer[19]; /* bConfirmPIN */
icc_buffer[ICC_MSG_HEADER_SIZE+6] = icc_buffer[20]; /* bEntryValidationCondition */
icc_buffer[ICC_MSG_HEADER_SIZE+7] = icc_buffer[21]; /* bNumberMessage */
icc_buffer[ICC_MSG_HEADER_SIZE+8] = icc_buffer[22]; /* wLangId L */
icc_buffer[ICC_MSG_HEADER_SIZE+9] = icc_buffer[23]; /* wLangId H */
icc_buffer[ICC_MSG_HEADER_SIZE+10] = icc_buffer[24]; /* bMsgIndex, bMsgIndex1 */
if (num_msgs >= 2)
icc_buffer[ICC_MSG_HEADER_SIZE+11] = icc_buffer[25]; /* bMsgIndex2 */
if (num_msgs == 3)
icc_buffer[ICC_MSG_HEADER_SIZE+12] = icc_buffer[26]; /* bMsgIndex3 */
apdu.seq = icc_seq;
apdu.cmd_apdu_head = &icc_buffer[ICC_MSG_HEADER_SIZE+0];
apdu.cmd_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE+5];
apdu.cmd_apdu_data_len = 5 + num_msgs;
apdu.expected_res_size = 0;
apdu.sw = 0x9000;
apdu.res_apdu_data_len = 0;
apdu.res_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE];
chEvtSignal (gpg_thread, EV_MODIFY_CMD_AVAILABLE);
next_state = ICC_STATE_EXECUTE;
}
else
@@ -571,8 +693,7 @@ icc_handle_data (void)
icc_next_p -= ICC_MSG_HEADER_SIZE;
icc_data_size = icc_next_p - icc_buffer - ICC_MSG_HEADER_SIZE;
icc_chain_p = NULL;
next_state = ICC_STATE_EXECUTE;
chEvtSignal (gpg_thread, (eventmask_t)1);
goto run_gpg_thread;
}
else /* icc_header->param == 3 is not supported. */
{
@@ -610,26 +731,78 @@ icc_handle_data (void)
{
if (icc_header->param == 0x10)
{
if (res_APDU_pointer != NULL)
int len = apdu.res_apdu_data_len;
if (apdu.res_apdu_data == NULL)
{ /* send status word(s) of 0x9000 */
len = 0;
if (apdu.res_apdu_data_len == 2)
{
icc_buffer[ICC_MSG_HEADER_SIZE] = 0x90;
len++;
}
icc_buffer[ICC_MSG_HEADER_SIZE+len] = 0x00;
len++;
}
else if (apdu.res_apdu_data != &icc_buffer[ICC_MSG_HEADER_SIZE])
{
memcpy (res_APDU, res_APDU_pointer,
ICC_RESPONSE_MSG_DATA_SIZE);
res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
if (apdu.res_apdu_data_len > ICC_RESPONSE_MSG_DATA_SIZE)
{
memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE],
apdu.res_apdu_data, ICC_RESPONSE_MSG_DATA_SIZE);
apdu.res_apdu_data += ICC_RESPONSE_MSG_DATA_SIZE;
apdu.res_apdu_data_len -= ICC_RESPONSE_MSG_DATA_SIZE;
}
else if (apdu.res_apdu_data_len == ICC_RESPONSE_MSG_DATA_SIZE)
{
memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE],
apdu.res_apdu_data, ICC_RESPONSE_MSG_DATA_SIZE);
apdu.res_apdu_data = NULL;
apdu.res_apdu_data_len = 2;
}
else if (apdu.res_apdu_data_len
== ICC_RESPONSE_MSG_DATA_SIZE - 1)
{
memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE],
apdu.res_apdu_data, apdu.res_apdu_data_len);
icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len]
= 0x90;
apdu.res_apdu_data = NULL;
apdu.res_apdu_data_len = 1;
len += 1;
}
else if (apdu.res_apdu_data_len
<= ICC_RESPONSE_MSG_DATA_SIZE - 2)
{
memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE],
apdu.res_apdu_data, apdu.res_apdu_data_len);
icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len]
= 0x90;
icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len+1]
= 0x00;
apdu.res_apdu_data = NULL;
apdu.res_apdu_data_len = 0;
len += 2;
}
}
else
memmove (res_APDU, res_APDU+ICC_RESPONSE_MSG_DATA_SIZE,
res_APDU_size);
if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
{
icc_send_data_block (res_APDU_size, 0, 0x02);
memmove (&icc_buffer[ICC_MSG_HEADER_SIZE],
&icc_buffer[ICC_MSG_HEADER_SIZE]
+ICC_RESPONSE_MSG_DATA_SIZE,
apdu.res_apdu_data_len);
apdu.res_apdu_data_len -= ICC_RESPONSE_MSG_DATA_SIZE;
}
if (len <= ICC_RESPONSE_MSG_DATA_SIZE)
{
icc_send_data_block (len, 0, 0x02);
next_state = ICC_STATE_WAIT;
}
else
{
icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x03);
res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
}
icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x03);
}
else
{
@@ -698,26 +871,36 @@ USBthread (void *arg)
else if (m == EV_EXEC_FINISHED)
if (icc_state == ICC_STATE_EXECUTE)
{
if (res_APDU_pointer != NULL)
if (apdu.res_apdu_data != &icc_buffer[ICC_MSG_HEADER_SIZE])
{
memcpy (res_APDU, res_APDU_pointer, ICC_RESPONSE_MSG_DATA_SIZE);
res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
/* Assume that
* apdu.res_apdu_data_len > ICC_RESPONSE_MSG_DATA_SIZE
*/
memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE],
apdu.res_apdu_data, ICC_RESPONSE_MSG_DATA_SIZE);
apdu.res_apdu_data += ICC_RESPONSE_MSG_DATA_SIZE;
}
else
{
icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len] = (apdu.sw >> 8);
icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len+1] = (apdu.sw & 0xff);
apdu.res_apdu_data_len += 2;
}
if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
if (apdu.res_apdu_data_len <= ICC_RESPONSE_MSG_DATA_SIZE)
{
icc_send_data_block (res_APDU_size, 0, 0);
icc_send_data_block (apdu.res_apdu_data_len, 0, 0);
icc_state = ICC_STATE_WAIT;
}
else
{
icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x01);
res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
apdu.res_apdu_data_len -= ICC_RESPONSE_MSG_DATA_SIZE;
icc_state = ICC_STATE_SEND;
}
}
else
{ /* XXX: error */
{ /* error */
DEBUG_INFO ("ERR07\r\n");
}
else if (m == EV_TX_FINISHED)

View File

@@ -8,10 +8,14 @@
#ifndef __USB_CONF_H
#define __USB_CONF_H
#ifdef ENABLE_VIRTUAL_COM_PORT
#define EP_NUM (6)
#ifdef PINPAD_DND_SUPPORT
# define EP_NUM (8)
#else
#define EP_NUM (3)
# ifdef ENABLE_VIRTUAL_COM_PORT
# define EP_NUM (6)
# else
# define EP_NUM (3)
# endif
#endif
#define BTABLE_ADDRESS (0x00)
@@ -28,9 +32,14 @@
/* EP3 */
#define ENDP3_TXADDR (0x140)
/* EP4 */
#define ENDP4_TXADDR (0x180)
#define ENDP4_TXADDR (0x150)
/* EP5 */
#define ENDP5_RXADDR (0x190)
#define ENDP5_RXADDR (0x160)
/* EP6 */
#define ENDP6_TXADDR (0x180)
/* EP7 */
#define ENDP7_RXADDR (0x1c0)
#define IMR_MSK (CNTR_CTRM | CNTR_SOFM | CNTR_RESETM )

View File

@@ -15,7 +15,7 @@
static const uint8_t gnukDeviceDescriptor[] = {
18, /* bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x00, 0x02, /* bcdUSB = 2.00 */
0x10, 0x01, /* bcdUSB = 1.1 */
0x00, /* bDeviceClass: 0 means deferred to interface */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
@@ -29,14 +29,29 @@ static const uint8_t gnukDeviceDescriptor[] = {
0x01 /* bNumConfigurations */
};
#define ICC_TOTAL_LENGTH (9+9+54+7+7)
#define ICC_NUM_INTERFACES 1
#ifdef ENABLE_VIRTUAL_COM_PORT
#define W_TOTAL_LENGTH (9+9+54+7+7+9+5+5+4+5+7+9+7+7)
#define NUM_INTERFACES 3 /* two for CDC, one for GPG */
#define VCOM_TOTAL_LENGTH (9+5+5+4+5+7+9+7+7)
#define VCOM_NUM_INTERFACES 2
#else
#define W_TOTAL_LENGTH (9+9+54+7+7)
#define NUM_INTERFACES 1 /* GPG only */
#define VCOM_TOTAL_LENGTH 0
#define VCOM_NUM_INTERFACES 0
#endif
#ifdef PINPAD_DND_SUPPORT
#define MSC_TOTAL_LENGTH (9+7+7)
#define MSC_NUM_INTERFACES 1
#else
#define MSC_TOTAL_LENGTH 0
#define MSC_NUM_INTERFACES 0
#endif
#define W_TOTAL_LENGTH (ICC_TOTAL_LENGTH+VCOM_TOTAL_LENGTH+MSC_TOTAL_LENGTH)
#define NUM_INTERFACES (ICC_NUM_INTERFACES+VCOM_NUM_INTERFACES+MSC_NUM_INTERFACES)
/* Configuation Descriptor */
static const uint8_t gnukConfigDescriptor[] = {
9, /* bLength: Configuation Descriptor size */
@@ -105,7 +120,7 @@ static const uint8_t gnukConfigDescriptor[] = {
0xff, /* bClassEnvelope: */
0, 0, /* wLCDLayout: FIXED VALUE */
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DND_SUPPORT)
1, /* bPinSupport: with PIN pad (verify) */
#elif defined(PINPAD_DIAL_SUPPORT)
3, /* bPinSupport: with PIN pad (verify, modify) */
@@ -193,7 +208,40 @@ static const uint8_t gnukConfigDescriptor[] = {
0x83, /* bEndpointAddress: (IN3) */
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, 0x00, /* wMaxPacketSize: */
0x00 /* bInterval */
0x00, /* bInterval */
#endif
#ifdef PINPAD_DND_SUPPORT
/* Interface Descriptor.*/
9, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
#ifdef ENABLE_VIRTUAL_COM_PORT
0x03, /* bInterfaceNumber. */
#else
0x01, /* bInterfaceNumber. */
#endif
0x00, /* bAlternateSetting. */
0x02, /* bNumEndpoints. */
0x08, /* bInterfaceClass (Mass Stprage). */
0x06, /* bInterfaceSubClass (SCSI
transparent command set, MSCO
chapter 2). */
0x50, /* bInterfaceProtocol (Bulk-Only
Mass Storage, MSCO chapter 3). */
0x00, /* iInterface. */
/* Endpoint Descriptor.*/
7, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x86, /* bEndpointAddress: (IN6) */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval (ignored for bulk). */
/* Endpoint Descriptor.*/
7, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x07, /* bEndpointAddress: (OUT7) */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval (ignored for bulk). */
#endif
};
@@ -227,7 +275,7 @@ static const uint8_t gnukStringProduct[] = {
const uint8_t gnukStringSerial[] = {
13*2+2, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'0', 0, '.', 0, '1', 0, '5', 0, /* Version number of Gnuk */
'0', 0, '.', 0, '1', 0, '6', 0, /* Version number of Gnuk */
'-', 0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,

View File

@@ -18,5 +18,5 @@ EP3_IN_Callback(void)
void
EP5_OUT_Callback(void)
{
SetEPRxValid (ENDP3);
SetEPRxValid (ENDP5);
}

View File

@@ -1,5 +1,6 @@
#include "ch.h"
#include "hal.h"
#include "usb_lib.h"
#include "usb_lld.h"
extern void USB_Istr (void);

View File

@@ -1,2 +1,52 @@
#define STM32_USB_IRQ_PRIORITY 11
void usb_lld_init (void);
extern inline void usb_lld_stall_tx (int ep_num)
{
SetEPTxStatus (ep_num, EP_TX_STALL);
}
extern inline void usb_lld_stall_rx (int ep_num)
{
SetEPRxStatus (ep_num, EP_RX_STALL);
}
extern inline int usb_lld_tx_data_len (int ep_num)
{
return GetEPTxCount (ep_num);
}
extern inline void usb_lld_txcpy (const uint8_t *src,
int ep_num, int offset, size_t len)
{
UserToPMABufferCopy ((uint8_t *)src, GetEPTxAddr (ep_num) + offset, len);
}
extern inline void usb_lld_tx_enable (int ep_num, size_t len)
{
SetEPTxCount (ep_num, len);
SetEPTxValid (ep_num);
}
extern inline void usb_lld_write (uint8_t ep_num, void *buf, size_t len)
{
UserToPMABufferCopy (buf, GetEPTxAddr (ep_num), len);
SetEPTxCount (ep_num, len);
SetEPTxValid (ep_num);
}
extern inline void usb_lld_rx_enable (int ep_num)
{
SetEPRxValid (ep_num);
}
extern inline int usb_lld_rx_data_len (int ep_num)
{
return GetEPRxCount (ep_num);
}
extern inline void usb_lld_rxcpy (uint8_t *dst,
int ep_num, int offset, size_t len)
{
PMAToUserBufferCopy (dst, GetEPRxAddr (ep_num) + offset, len);
}

552
src/usb_msc.c Normal file
View File

@@ -0,0 +1,552 @@
/*
* usb_msc.c -- USB Mass Storage Class protocol handling
*
* Copyright (C) 2011, 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/>.
*
*/
#include "usb_lib.h"
#include "config.h"
#include "ch.h"
#include "gnuk.h"
#include "usb_lld.h"
#include "usb_msc.h"
struct usb_endp_in {
const uint8_t *txbuf; /* Pointer to the transmission buffer. */
size_t txsize; /* Transmit transfer size remained. */
size_t txcnt; /* Transmitted bytes so far. */
};
struct usb_endp_out {
uint8_t *rxbuf; /* Pointer to the receive buffer. */
size_t rxsize; /* Requested receive transfer size. */
size_t rxcnt; /* Received bytes so far. */
};
static struct usb_endp_in ep6_in;
static struct usb_endp_out ep7_out;
static Thread *the_thread;
#define ENDP_MAX_SIZE 64
static uint8_t msc_state;
static void usb_start_transmit (const uint8_t *p, size_t n)
{
size_t pkt_len = n > ENDP_MAX_SIZE ? ENDP_MAX_SIZE : n;
ep6_in.txbuf = p;
ep6_in.txsize = n;
ep6_in.txcnt = 0;
usb_lld_write (ENDP6, (uint8_t *)ep6_in.txbuf, pkt_len);
}
/* "Data Transmitted" callback */
void EP6_IN_Callback (void)
{
size_t n = (size_t)usb_lld_tx_data_len (ENDP6);
ep6_in.txbuf += n;
ep6_in.txcnt += n;
ep6_in.txsize -= n;
if (ep6_in.txsize > 0) /* More data to be sent */
{
if (ep6_in.txsize > ENDP_MAX_SIZE)
n = ENDP_MAX_SIZE;
else
n = ep6_in.txsize;
usb_lld_write (ENDP6, (uint8_t *)ep6_in.txbuf, n);
return;
}
/* Transmit has been completed, notify the waiting thread */
switch (msc_state)
{
case MSC_SENDING_CSW:
case MSC_DATA_IN:
if (the_thread != NULL) {
Thread *tp;
chSysLockFromIsr ();
tp = the_thread;
the_thread = NULL;
tp->p_u.rdymsg = RDY_OK;
chSchReadyI (tp);
chSysUnlockFromIsr ();
}
break;
default:
break;
}
}
static void usb_start_receive (uint8_t *p, size_t n)
{
ep7_out.rxbuf = p;
ep7_out.rxsize = n;
ep7_out.rxcnt = 0;
usb_lld_rx_enable (ENDP7);
}
/* "Data Received" call back */
void EP7_OUT_Callback (void)
{
size_t n = (size_t)usb_lld_rx_data_len (ENDP7);
int err = 0;
if (n > ep7_out.rxsize)
{ /* buffer overflow */
err = 1;
n = ep7_out.rxsize;
}
usb_lld_rxcpy (ep7_out.rxbuf, ENDP7, 0, n);
ep7_out.rxbuf += n;
ep7_out.rxcnt += n;
ep7_out.rxsize -= n;
if (n == ENDP_MAX_SIZE && ep7_out.rxsize != 0)
{ /* More data to be received */
usb_lld_rx_enable (ENDP7);
return;
}
/* Receiving has been completed, notify the waiting thread */
switch (msc_state)
{
case MSC_IDLE:
case MSC_DATA_OUT:
if (the_thread != NULL) {
Thread *tp;
chSysLockFromIsr ();
tp = the_thread;
the_thread = NULL;
tp->p_u.rdymsg = err? RDY_RESET : RDY_OK;
chSchReadyI (tp);
chSysUnlockFromIsr ();
}
break;
default:
break;
}
}
static const uint8_t scsi_inquiry_data_00[] = { 0, 0, 0, 0, 0 };
static const uint8_t scsi_inquiry_data[] = {
0x00, /* Direct Access Device. */
0x80, /* RMB = 1: Removable Medium. */
0x05, /* Version: SPC-3. */
0x02, /* Response format: SPC-3. */
36 - 4, /* Additional Length. */
0x00,
0x00,
0x00,
/* Vendor Identification */
'F', 'S', 'I', 'J', ' ', ' ', ' ', ' ',
/* Product Identification */
'V', 'i', 'r', 't', 'u', 'a', 'l', ' ',
'D', 'i', 's', 'k', ' ', ' ', ' ', ' ',
/* Product Revision Level */
'1', '.', '0', ' '
};
static uint8_t scsi_sense_data_desc[] = {
0x72, /* Response Code: descriptor, current */
0x02, /* Sense Key */
0x3a, /* ASC (additional sense code) */
0x00, /* ASCQ (additional sense code qualifier) */
0x00, 0x00, 0x00,
0x00, /* Additional Sense Length */
};
static uint8_t scsi_sense_data_fixed[] = {
0x70, /* Response Code: fixed, current */
0x00,
0x02, /* Sense Key */
0x00, 0x00, 0x00, 0x00,
0x0a, /* Additional Sense Length */
0x00, 0x00, 0x00, 0x00,
0x3a, /* ASC (additional sense code) */
0x00, /* ASCQ (additional sense code qualifier) */
0x00,
0x00, 0x00, 0x00,
};
static void set_scsi_sense_data(uint8_t sense_key, uint8_t asc) {
scsi_sense_data_desc[1] = scsi_sense_data_fixed[2] = sense_key;
scsi_sense_data_desc[2] = scsi_sense_data_fixed[12] = asc;
}
static uint8_t buf[512];
static uint8_t contingent_allegiance;
static uint8_t keep_contingent_allegiance;
uint8_t media_available;
void msc_media_insert_change (int available)
{
contingent_allegiance = 1;
media_available = available;
if (available)
{
set_scsi_sense_data (0x06, 0x28); /* UNIT_ATTENTION */
keep_contingent_allegiance = 0;
}
else
{
set_scsi_sense_data (0x02, 0x3a); /* NOT_READY */
keep_contingent_allegiance = 1;
}
}
static uint8_t scsi_read_format_capacities (uint32_t *nblocks,
uint32_t *secsize)
{
*nblocks = 68;
*secsize = 512;
if (media_available)
return 2; /* Formatted Media.*/
else
return 3; /* No Media.*/
}
static struct CBW CBW;
static struct CSW CSW;
static int msc_recv_data (void)
{
msg_t msg;
chSysLock ();
msc_state = MSC_DATA_OUT;
the_thread = chThdSelf ();
usb_start_receive (buf, 512);
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
return 0;
}
static void msc_send_data (const uint8_t *p, size_t n)
{
msg_t msg;
chSysLock ();
msc_state = MSC_DATA_IN;
the_thread = chThdSelf ();
usb_start_transmit (p, n);
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
CSW.dCSWDataResidue -= (uint32_t)n;
chSysUnlock();
}
static void msc_send_result (const uint8_t *p, size_t n)
{
msg_t msg;
if (p != NULL)
{
if (n > CBW.dCBWDataTransferLength)
n = CBW.dCBWDataTransferLength;
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
msc_send_data (p, n);
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
}
CSW.dCSWSignature = MSC_CSW_SIGNATURE;
chSysLock ();
msc_state = MSC_SENDING_CSW;
the_thread = chThdSelf ();
usb_start_transmit ((uint8_t *)&CSW, sizeof CSW);
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
}
void msc_handle_command (void)
{
size_t n;
uint32_t nblocks, secsize;
uint32_t lba;
int r;
msg_t msg;
chSysLock();
msc_state = MSC_IDLE;
the_thread = chThdSelf ();
usb_start_receive ((uint8_t *)&CBW, sizeof CBW);
chSchGoSleepTimeoutS (THD_STATE_SUSPENDED, MS2ST (1000));
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
if (msg != RDY_OK)
{
/* Error occured, ignore the request and go into error state */
msc_state = MSC_ERROR;
if (msg != RDY_TIMEOUT)
{
chSysLock ();
usb_lld_stall_rx (ENDP7);
chSysUnlock ();
}
return;
}
n = ep7_out.rxcnt;
if ((n != sizeof (struct CBW)) || (CBW.dCBWSignature != MSC_CBW_SIGNATURE))
{
msc_state = MSC_ERROR;
chSysLock ();
usb_lld_stall_rx (ENDP7);
chSysUnlock ();
return;
}
CSW.dCSWTag = CBW.dCBWTag;
switch (CBW.CBWCB[0]) {
case SCSI_REQUEST_SENSE:
if (CBW.CBWCB[1] & 0x01) /* DESC */
msc_send_result ((uint8_t *)&scsi_sense_data_desc,
sizeof scsi_sense_data_desc);
else
msc_send_result ((uint8_t *)&scsi_sense_data_fixed,
sizeof scsi_sense_data_fixed);
/* After the error is reported, clear it, if it's . */
if (!keep_contingent_allegiance)
{
contingent_allegiance = 0;
set_scsi_sense_data (0x00, 0x00);
}
return;
case SCSI_INQUIRY:
if (CBW.CBWCB[1] & 0x01) /* EVPD */
/* assume page 00 */
msc_send_result ((uint8_t *)&scsi_inquiry_data_00,
sizeof scsi_inquiry_data_00);
else
msc_send_result ((uint8_t *)&scsi_inquiry_data,
sizeof scsi_inquiry_data);
return;
case SCSI_READ_FORMAT_CAPACITIES:
buf[8] = scsi_read_format_capacities (&nblocks, &secsize);
buf[0] = buf[1] = buf[2] = 0;
buf[3] = 8;
buf[4] = (uint8_t)(nblocks >> 24);
buf[5] = (uint8_t)(nblocks >> 16);
buf[6] = (uint8_t)(nblocks >> 8);
buf[7] = (uint8_t)(nblocks >> 0);
buf[9] = (uint8_t)(secsize >> 16);
buf[10] = (uint8_t)(secsize >> 8);
buf[11] = (uint8_t)(secsize >> 0);
msc_send_result (buf, 12);
return;
case SCSI_START_STOP_UNIT:
if (CBW.CBWCB[4] == 0x00 /* stop */
|| CBW.CBWCB[4] == 0x02 /* eject */ || CBW.CBWCB[4] == 0x03 /* close */)
{
msc_scsi_stop (CBW.CBWCB[4]);
set_scsi_sense_data (0x05, 0x24); /* ILLEGAL_REQUEST */
contingent_allegiance = 1;
keep_contingent_allegiance = 1;
}
/* CBW.CBWCB[4] == 0x01 *//* start */
goto success;
case SCSI_TEST_UNIT_READY:
if (contingent_allegiance)
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
CSW.dCSWDataResidue = 0;
msc_send_result (NULL, 0);
return;
}
/* fall through */
success:
case SCSI_SYNCHRONIZE_CACHE:
case SCSI_VERIFY10:
case SCSI_ALLOW_MEDIUM_REMOVAL:
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
msc_send_result (NULL, 0);
return;
case SCSI_MODE_SENSE6:
buf[0] = 0x03;
buf[1] = buf[2] = buf[3] = 0;
msc_send_result (buf, 4);
return;
case SCSI_READ_CAPACITY10:
scsi_read_format_capacities (&nblocks, &secsize);
buf[0] = (uint8_t)((nblocks - 1) >> 24);
buf[1] = (uint8_t)((nblocks - 1) >> 16);
buf[2] = (uint8_t)((nblocks - 1) >> 8);
buf[3] = (uint8_t)((nblocks - 1) >> 0);
buf[4] = (uint8_t)(secsize >> 24);
buf[5] = (uint8_t)(secsize >> 16);
buf[6] = (uint8_t)(secsize >> 8);
buf[7] = (uint8_t)(secsize >> 0);
msc_send_result (buf, 8);
return;
case SCSI_READ10:
case SCSI_WRITE10:
break;
default:
if (CBW.dCBWDataTransferLength == 0)
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
CSW.dCSWDataResidue = 0;
msc_send_result (NULL, 0);
return;
}
else
{
msc_state = MSC_ERROR;
chSysLock ();
usb_lld_stall_tx (ENDP6);
usb_lld_stall_rx (ENDP7);
chSysUnlock ();
return;
}
}
lba = (CBW.CBWCB[2] << 24) | (CBW.CBWCB[3] << 16)
| (CBW.CBWCB[4] << 8) | CBW.CBWCB[5];
/* Transfer direction.*/
if (CBW.bmCBWFlags & 0x80)
{
/* IN, Device to Host.*/
msc_state = MSC_DATA_IN;
if (CBW.CBWCB[0] == SCSI_READ10)
{
const uint8_t *p;
CSW.dCSWDataResidue = 0;
while (1)
{
if (CBW.CBWCB[7] == 0 && CBW.CBWCB[8] == 0)
{
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
break;
}
if ((r = msc_scsi_read (lba, &p)) == 0)
{
msc_send_data (p, 512);
if (++CBW.CBWCB[5] == 0)
if (++CBW.CBWCB[4] == 0)
if (++CBW.CBWCB[3] == 0)
++CBW.CBWCB[2];
if (CBW.CBWCB[8]-- == 0)
CBW.CBWCB[7]--;
CSW.dCSWDataResidue += 512;
}
else
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
contingent_allegiance = 1;
if (r == SCSI_ERROR_NOT_READY)
set_scsi_sense_data (SCSI_ERROR_NOT_READY, 0x3a);
else
set_scsi_sense_data (r, 0x00);
break;
}
}
msc_send_result (NULL, 0);
}
}
else
{
/* OUT, Host to Device.*/
if (CBW.CBWCB[0] == SCSI_WRITE10)
{
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
while (1)
{
if (CBW.CBWCB[8] == 0 && CBW.CBWCB[7] == 0)
{
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
break;
}
msc_recv_data ();
if ((r = msc_scsi_write (lba, buf, 512)) == 0)
{
if (++CBW.CBWCB[5] == 0)
if (++CBW.CBWCB[4] == 0)
if (++CBW.CBWCB[3] == 0)
++CBW.CBWCB[2];
if (CBW.CBWCB[8]-- == 0)
CBW.CBWCB[7]--;
CSW.dCSWDataResidue -= 512;
}
else
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
contingent_allegiance = 1;
if (r == SCSI_ERROR_NOT_READY)
set_scsi_sense_data (SCSI_ERROR_NOT_READY, 0x3a);
else
set_scsi_sense_data (r, 0x00);
break;
}
}
msc_send_result (NULL, 0);
}
}
}
static msg_t
msc_main (void *arg)
{
(void)arg;
/* Initially, it starts with no media */
msc_media_insert_change (0);
while (1)
msc_handle_command ();
return 0;
}
static WORKING_AREA(wa_msc_thread, 128);
void msc_init (void)
{
chThdCreateStatic (wa_msc_thread, sizeof (wa_msc_thread),
NORMALPRIO, msc_main, NULL);
}

52
src/usb_msc.h Normal file
View File

@@ -0,0 +1,52 @@
#define MSC_CBW_SIGNATURE 0x43425355
#define MSC_CSW_SIGNATURE 0x53425355
#define MSC_GET_MAX_LUN_COMMAND 0xFE
#define MSC_MASS_STORAGE_RESET_COMMAND 0xFF
#define MSC_CSW_STATUS_PASSED 0
#define MSC_CSW_STATUS_FAILED 1
#define SCSI_INQUIRY 0x12
#define SCSI_MODE_SENSE6 0x1A
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1E
#define SCSI_READ10 0x28
#define SCSI_READ_CAPACITY10 0x25
#define SCSI_REQUEST_SENSE 0x03
#define SCSI_START_STOP_UNIT 0x1B
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_WRITE10 0x2A
#define SCSI_VERIFY10 0x2F
#define SCSI_READ_FORMAT_CAPACITIES 0x23
#define SCSI_SYNCHRONIZE_CACHE 0x35
#define MSC_IDLE 0
#define MSC_DATA_OUT 1
#define MSC_DATA_IN 2
#define MSC_SENDING_CSW 3
#define MSC_ERROR 4
struct CBW {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
uint8_t bmCBWFlags;
uint8_t bCBWLUN;
uint8_t bCBWCBLength;
uint8_t CBWCB[16];
} __attribute__((packed));
struct CSW {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
} __attribute__((packed));
#define SCSI_ERROR_NOT_READY 2
#define SCSI_ERROR_ILLEAGAL_REQUEST 5
#define SCSI_ERROR_UNIT_ATTENTION 6
#define SCSI_ERROR_DATA_PROTECT 7
extern uint8_t media_available;

View File

@@ -36,6 +36,24 @@
#include "usb-cdc-vport.c"
#endif
#ifdef PINPAD_DND_SUPPORT
#include "usb_msc.h"
#endif
static void
SetEPRxCount_allocated_size (uint8_t bEpNum, uint16_t wCount)
{ /* Assume wCount is even */
uint32_t *pdwReg = _pEPRxCount (bEpNum);
uint16_t value;
if (wCount <= 62)
value = (wCount & 0x3e) << 9;
else
value = 0x8000 | (((wCount >> 5) - 1) << 10);
*pdwReg = (uint32_t)value;
}
static void
gnuk_device_init (void)
@@ -46,7 +64,9 @@ gnuk_device_init (void)
PowerOn ();
/* Perform basic device initialization operations */
USB_SIL_Init ();
_SetISTR (0);
wInterrupt_Mask = IMR_MSK;
_SetCNTR (wInterrupt_Mask);
bDeviceState = UNCONNECTED;
}
@@ -71,7 +91,7 @@ gnuk_device_reset (void)
SetEPRxAddr (ENDP0, ENDP0_RXADDR);
SetEPTxAddr (ENDP0, ENDP0_TXADDR);
Clear_Status_Out (ENDP0);
SetEPRxCount (ENDP0, GNUK_MAX_PACKET_SIZE);
SetEPRxCount_allocated_size (ENDP0, GNUK_MAX_PACKET_SIZE);
SetEPRxValid (ENDP0);
/* Initialize Endpoint 1 */
@@ -83,7 +103,7 @@ gnuk_device_reset (void)
/* Initialize Endpoint 2 */
SetEPType (ENDP2, EP_BULK);
SetEPRxAddr (ENDP2, ENDP2_RXADDR);
SetEPRxCount (ENDP2, GNUK_MAX_PACKET_SIZE);
SetEPRxCount_allocated_size (ENDP2, GNUK_MAX_PACKET_SIZE);
SetEPRxStatus (ENDP2, EP_RX_VALID);
SetEPTxStatus (ENDP2, EP_TX_DIS);
@@ -103,11 +123,26 @@ gnuk_device_reset (void)
/* Initialize Endpoint 5 */
SetEPType (ENDP5, EP_BULK);
SetEPRxAddr (ENDP5, ENDP5_RXADDR);
SetEPRxCount (ENDP5, VIRTUAL_COM_PORT_DATA_SIZE);
SetEPRxCount_allocated_size (ENDP5, VIRTUAL_COM_PORT_DATA_SIZE);
SetEPRxStatus (ENDP5, EP_RX_VALID);
SetEPTxStatus (ENDP5, EP_TX_DIS);
#endif
#ifdef PINPAD_DND_SUPPORT
/* Initialize Endpoint 6 */
SetEPType (ENDP6, EP_BULK);
SetEPTxAddr (ENDP6, ENDP6_TXADDR);
SetEPTxStatus (ENDP6, EP_TX_NAK);
SetEPRxStatus (ENDP6, EP_RX_DIS);
/* Initialize Endpoint 7 */
SetEPType (ENDP7, EP_BULK);
SetEPRxAddr (ENDP7, ENDP7_RXADDR);
SetEPRxCount_allocated_size (ENDP7, 64);
SetEPRxStatus (ENDP7, EP_RX_STALL);
SetEPTxStatus (ENDP7, EP_TX_DIS);
#endif
/* Set this device to response on default address */
SetDeviceAddress (0);
@@ -153,6 +188,21 @@ gnuk_device_SetInterface (void)
ClearDTOG_TX (ENDP3);
}
#endif
#ifdef PINPAD_DND_SUPPORT
# ifdef ENABLE_VIRTUAL_COM_PORT
else if (intf == 3)
{
ClearDTOG_TX (ENDP6);
ClearDTOG_RX (ENDP7);
}
# else
else if (intf == 1)
{
ClearDTOG_TX (ENDP6);
ClearDTOG_RX (ENDP7);
}
# endif
#endif
}
static void
@@ -200,10 +250,18 @@ gnuk_device_GetStringDescriptor (uint16_t Length)
(PONE_DESCRIPTOR)&String_Descriptor[wValue0]);
}
#ifdef ENABLE_VIRTUAL_COM_PORT
#define NUM_INTERFACES 3 /* two for CDC, one for CCID */
#ifdef PINPAD_DND_SUPPORT
# ifdef ENABLE_VIRTUAL_COM_PORT
# define NUM_INTERFACES 4 /* two for CDC, one for CCID, and MSC */
# else
# define NUM_INTERFACES 2 /* CCID and MSC */
# endif
#else
#define NUM_INTERFACES 1 /* CCID only */
# ifdef ENABLE_VIRTUAL_COM_PORT
# define NUM_INTERFACES 3 /* two for CDC, one for CCID */
# else
# define NUM_INTERFACES 1 /* CCID only */
# endif
#endif
static RESULT
@@ -247,11 +305,26 @@ gnuk_data_rates (uint16_t len)
return (uint8_t *)data_rate_table;
}
#if defined(PINPAD_DND_SUPPORT)
static const uint8_t lun_table[] = { 0, 0, 0, 0, };
static uint8_t *
msc_lun_info (uint16_t len)
{
if (len == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof (lun_table);
return NULL;
}
return (uint8_t *)lun_table;
}
#endif
static RESULT
gnuk_setup_with_data (uint8_t RequestNo)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
if (pInformation->USBwIndex0 == 0) /* Interface */
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) /* Interface */
if (pInformation->USBwIndex0 == 0)
{
if (RequestNo == USB_CCID_REQ_GET_CLOCK_FREQUENCIES)
{
@@ -270,23 +343,41 @@ gnuk_setup_with_data (uint8_t RequestNo)
else
return USB_UNSUPPORT;
}
else
#if defined(PINPAD_DND_SUPPORT)
# if defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_Data_Setup (RequestNo);
else if (pInformation->USBwIndex0 == 3)
# else
else if (pInformation->USBwIndex0 == 1)
# endif
{
#if defined(ENABLE_VIRTUAL_COM_PORT)
return Virtual_Com_Port_Data_Setup (RequestNo);
#else
return USB_UNSUPPORT;
#endif
if (RequestNo == MSC_GET_MAX_LUN_COMMAND)
{
pInformation->Ctrl_Info.CopyData = msc_lun_info;
pInformation->Ctrl_Info.Usb_wOffset = 0;
msc_lun_info (0);
return USB_SUCCESS;
}
else
return USB_UNSUPPORT;
}
#elif defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_Data_Setup (RequestNo);
#endif
else
return USB_UNSUPPORT;
else
return USB_UNSUPPORT;
}
static RESULT
gnuk_setup_with_nodata (uint8_t RequestNo)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
if (pInformation->USBwIndex0 == 0) /* Interface */
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) /* Interface */
if (pInformation->USBwIndex0 == 0)
{
if (RequestNo == USB_CCID_REQ_ABORT)
/* wValue: bSeq, bSlot */
@@ -295,14 +386,29 @@ gnuk_setup_with_nodata (uint8_t RequestNo)
else
return USB_UNSUPPORT;
}
else
#if defined(PINPAD_DND_SUPPORT)
# if defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_NoData_Setup (RequestNo);
else if (pInformation->USBwIndex0 == 3)
# else
else if (pInformation->USBwIndex0 == 1)
# endif
{
#if defined(ENABLE_VIRTUAL_COM_PORT)
return Virtual_Com_Port_NoData_Setup (RequestNo);
#else
return USB_UNSUPPORT;
#endif
if (RequestNo == MSC_MASS_STORAGE_RESET_COMMAND)
{
/* Should call resetting MSC thread, something like msc_reset() */
return USB_SUCCESS;
}
else
return USB_UNSUPPORT;
}
#elif defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_NoData_Setup (RequestNo);
#endif
else
return USB_UNSUPPORT;
else
return USB_UNSUPPORT;
}

View File

@@ -47,7 +47,7 @@ class GnukToken(object):
apdu = [0x00, 0x20, 0x00, 0x80+who, 0, 0, len(passwd)] + s2l(passwd)
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, "cmd_verify"
raise ValueError, ("%02x%02x" % (sw1, sw2))
def cmd_write_binary(self, fileid, data, is_update):
count = 0
@@ -66,22 +66,22 @@ class GnukToken(object):
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
if is_update:
raise ValueError, "cmd_update_binary"
raise ValueError, ("%02x%02x" % (sw1, sw2))
else:
raise ValueError, "cmd_write_binary"
raise ValueError, ("%02x%02x" % (sw1, sw2))
count += 1
def cmd_select_openpgp(self):
apdu = [0x00, 0xa4, 0x04, 0x0c, 6, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01 ]
apdu = [0x00, 0xa4, 0x04, 0x00, 6, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01 ]
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, "cmd_select_openpgp"
raise ValueError, ("%02x%02x" % (sw1, sw2))
def cmd_get_data(self, tagh, tagl):
apdu = [0x00, 0xca, tagh, tagl]
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, "cmd_get_data"
raise ValueError, ("%02x%02x" % (sw1, sw2))
return response
def compare(data_original, data_in_device):
@@ -165,6 +165,5 @@ if __name__ == '__main__':
data = f.read()
f.close()
print "%s: %d" % (filename, len(data))
data += "\x90\x00"
print "Updating card holder certificate"
main(fileid, is_update, data, passwd)

View File

@@ -189,7 +189,7 @@ class gnuk_token:
count += 1
def cmd_select_openpgp(self):
cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, "\xD2\x76\x00\x01\x24\x01")
cmd_data = iso7816_compose(0xa4, 0x04, 0x00, "\xD2\x76\x00\x01\x24\x01")
sw = self.icc_send_cmd(cmd_data)
if len(sw) != 2:
raise ValueError, "cmd_select_openpgp"
@@ -289,6 +289,5 @@ if __name__ == '__main__':
data = f.read()
f.close()
print "%s: %d" % (filename, len(data))
data += "\x90\x00"
print "Updating card holder certificate"
main(fileid, is_update, data)

398
tool/pinpadtest.py Executable file
View File

@@ -0,0 +1,398 @@
#! /usr/bin/python
"""
pinpadtest.py - a tool to test variable length pin entry with pinpad
Copyright (C) 2011, 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
# Assume only single CCID device is attached to computer with card
from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.util import toHexString
from getpass import getpass
CM_IOCTL_GET_FEATURE_REQUEST = (0x42000000 + 3400)
FEATURE_VERIFY_PIN_DIRECT = 0x06
FEATURE_MODIFY_PIN_DIRECT = 0x07
BY_ADMIN = 3
BY_USER = 1
PIN_MIN_DEFAULT = 6 # min of OpenPGP card
PIN_MAX_DEFAULT = 15 # max of VASCO DIGIPASS 920
def s2l(s):
return [ ord(c) for c in s ]
def confirm_pin_setting(single_step):
if single_step:
return 0x01 # bConfirmPIN: new PIN twice
else:
return 0x03 # bConfirmPIN: old PIN and new PIN twice
class Card(object):
def __init__(self, add_a_byte, pinmin, pinmax):
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=10, cardType=cardtype)
cardservice = cardrequest.waitforcard()
self.connection = cardservice.connection
self.verify_ioctl = -1
self.modify_ioctl = -1
self.another_byte = add_a_byte
self.pinmin = pinmin
self.pinmax = pinmax
def get_features(self):
p = self.connection.control(CM_IOCTL_GET_FEATURE_REQUEST, [])
i = 0
while i < len(p):
code = p[i]
l = p[i+1]
i = i + 2
if l == 4:
ioctl = (p[i] << 24) | (p[i+1] << 16) | (p[i+2] << 8) | p[i+3]
i = i + l
else:
i = i + l
continue
if code == FEATURE_VERIFY_PIN_DIRECT:
self.verify_ioctl = ioctl
elif code == FEATURE_MODIFY_PIN_DIRECT:
self.modify_ioctl = ioctl
if self.verify_ioctl == -1:
raise ValueError, "Not supported"
def cmd_select_openpgp(self):
apdu = [0x00, 0xa4, 0x04, 0x00, 6, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01 ]
response, sw1, sw2 = self.connection.transmit(apdu)
if sw1 == 0x61: # More data
response, sw1, sw2 = self.connection.transmit([0x00, 0xc0, 0, 0, sw2])
elif not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("cmd_select_openpgp %02x %02x" % (sw1, sw2))
def possibly_add_dummy_byte(self):
if self.another_byte:
return [ 0 ]
else:
return []
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
0x00, # bmPINLengthFormat
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
self.pinmin, # wPINMaxExtraDigit High (PINmin)
0x02, # bEntryValidationCondition
0x01, # bNumberMessage
0x00, # wLangId Low
0x00, # wLangId High
0x00, # bMsgIndex
0x00, # bTeoPrologue[0]
0x00, # bTeoPrologue[1]
0x00 # bTeoPrologue[2]
]
apdu += self.possibly_add_dummy_byte()
pin_verify += [ len(apdu), 0, 0, 0 ] + apdu
data = self.connection.control(self.verify_ioctl,pin_verify)
sw1 = data[0]
sw2 = data[1]
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("cmd_verify_pinpad %02x %02x" % (sw1, sw2))
def send_modify_pinpad(self, apdu, single_step, command):
if self.modify_ioctl == -1:
raise ValueError, "Not supported"
pin_modify = [ 0x00, # bTimerOut
0x00, # bTimerOut2
0x82, # bmFormatString: Byte, pos=0, left, ASCII.
0x00, # bmPINBlockString
0x00, # bmPINLengthFormat
0x00, # bInsertionOffsetOld
0x00, # bInsertionOffsetNew
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
self.pinmin, # wPINMaxExtraDigit High (PINmin)
confirm_pin_setting(single_step),
0x02, # bEntryValidationCondition
0x03, # bNumberMessage
0x00, # wLangId Low
0x00, # wLangId High
0x00, # bMsgIndex1
0x01, # bMsgIndex2
0x02, # bMsgIndex3
0x00, # bTeoPrologue[0]
0x00, # bTeoPrologue[1]
0x00 # bTeoPrologue[2]
]
apdu += self.possibly_add_dummy_byte()
pin_modify += [ len(apdu), 0, 0, 0 ] + apdu
data = self.connection.control(self.modify_ioctl,pin_modify)
sw1 = data[0]
sw2 = data[1]
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("%s %02x %02x" % (command, sw1, sw2))
def cmd_reset_retry_counter(self, who, data):
if who == BY_ADMIN:
apdu = [0x00, 0x2c, 0x02, 0x81, len(data) ] + data # BY_ADMIN
else:
apdu = [0x00, 0x2c, 0x00, 0x81, len(data) ] + data # BY_USER with resetcode
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("cmd_reset_retry_counter %02x %02x" % (sw1, sw2))
# Note: CCID specification doesn't permit this (only 0x20 and 0x24)
def cmd_reset_retry_counter_pinpad(self, who):
if who == BY_ADMIN:
apdu = [0x00, 0x2c, 0x02, 0x81] # BY_ADMIN
self.send_modify_pinpad(apdu, True, "cmd_reset_retry_counter_pinpad")
else:
apdu = [0x00, 0x2c, 0x00, 0x81] # BY_USER with resetcode
self.send_modify_pinpad(apdu, False, "cmd_reset_retry_counter_pinpad")
def cmd_put_resetcode(self, data):
apdu = [0x00, 0xda, 0x00, 0xd3, len(data) ] + data # BY_ADMIN
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("cmd_put_resetcode %02x %02x" % (sw1, sw2))
# Note: CCID specification doesn't permit this (only 0x20 and 0x24)
def cmd_put_resetcode_pinpad(self):
apdu = [0x00, 0xda, 0x00, 0xd3]
self.send_modify_pinpad(apdu, True, "cmd_put_resetcode_pinpad")
def cmd_change_reference_data_pinpad(self, who, is_exchange):
if is_exchange:
apdu = [0x00, 0x24, 1, 0x80+who]
else:
apdu = [0x00, 0x24, 0x00, 0x80+who]
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)
card.connection.connect()
print "Reader/Token:", card.connection.getReader()
print "ATR:", toHexString( card.connection.getATR() )
card.get_features()
card.cmd_select_openpgp()
if method == "verify":
if who == BY_USER:
print "Please input User's PIN"
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(who)
elif method == "change":
if change_by_two_steps:
if who == BY_USER:
print "Please input User's PIN"
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(who)
if who == BY_USER:
print "Please input New User's PIN twice"
else:
print "Please input New Admin's PIN twice"
card.cmd_change_reference_data_pinpad(who, True)
else:
if who == BY_USER:
print "Please input User's PIN"
print "and New User's PIN twice"
else:
print "Please input Admin's PIN"
print "and New Admin's PIN twice"
card.cmd_change_reference_data_pinpad(who, False)
elif method == "unblock":
if change_by_two_steps:
# It means using keyboard for new PIN
if who == BY_USER:
resetcode=s2l(getpass("Please input reset code from keyboard: "))
newpin=s2l(getpass("Please input New User's PIN from keyboard: "))
card.cmd_reset_retry_counter(who,resetcode+newpin)
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
newpin=s2l(getpass("Please input New User's PIN from keyboard: "))
card.cmd_reset_retry_counter(who,newpin)
else:
if who == BY_USER:
print "Please input reset code"
print "and New User's PIN twice"
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
print "Please input New User's PIN twice"
card.cmd_reset_retry_counter_pinpad(who)
elif method == "put":
if change_by_two_steps:
# It means using keyboard for new PIN
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
resetcode=s2l(getpass("Please input New Reset Code from keyboard: "))
card.cmd_put_resetcode(resetcode)
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
print "Please input New Reset Code twice"
card.cmd_put_resetcode_pinpad()
else:
raise ValueError, method
card.connection.disconnect()
print "OK."
return 0
def print_usage():
print "pinpad-test: testing pinentry of PC/SC card reader"
print " help:"
print "\t--help:\t\tthis message"
print " method:\t\t\t\t\t\t\t[verify]"
print "\t--verify:\tverify PIN"
print "\t--change:\tchange PIN (old PIN, new PIN twice)"
print "\t--change2:\tchange PIN by two steps (old PIN, new PIN twice)"
print "\t--unblock:\tunblock PIN (admin PIN/resetcode, new PIN twice)"
print "\t--unblock2:\tunblock PIN (admin PIN:pinpad, new PIN:kbd)"
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--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]"
print "\t--pinmax:\tspecify maximum length of PIN\t\t[15]"
print "EXAMPLES:"
print " $ pinpad-test # verify user's PIN "
print " $ pinpad-test --admin # verify admin's PIN "
print " $ pinpad-test --change # change user's PIN "
print " $ pinpad-test --change --admin # change admin's PIN "
print " $ pinpad-test --change2 # change user's PIN by two steps"
print " $ pinpad-test --change2 --admin # change admin's PIN by two steps"
print " $ pinpad-test --unblock # change user's PIN by reset code"
print " $ pinpad-test --unblock --admin # change user's PIN by admin's PIN"
print " $ pinpad-test --put # setup resetcode "
if __name__ == '__main__':
who = BY_USER
method = "verify"
add_a_byte = False
pinmin = PIN_MIN_DEFAULT
pinmax = PIN_MAX_DEFAULT
change_by_two_steps = False
while len(sys.argv) >= 2:
option = sys.argv[1]
sys.argv.pop(1)
if option == '--admin':
who = BY_ADMIN
elif option == '--change':
method = "change"
elif option == '--change2':
method = "change"
change_by_two_steps = True
elif option == '--unblock':
method = "unblock"
elif option == '--unblock2':
method = "unblock"
change_by_two_steps = True
elif option == '--add':
add_a_byte = True
elif option == '--pinmin':
pinmin = int(sys.argv[1])
sys.argv.pop(1)
elif option == '--pinmax':
pinmax = int(sys.argv[1])
sys.argv.pop(1)
elif option == '--put':
method = "put"
elif option == '--put2':
method = "put"
change_by_two_steps = True
elif option == "verify":
method = "verify"
elif option == '--help':
print_usage()
exit(0)
else:
raise ValueError, option
main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps)
# Failure
# 67 00: Wrong length; no further indication
# 69 82: Security status not satisfied: pin doesn't match
# 69 85: Conditions of use not satisfied
# 6b 00: Wrong parameters P1-P2
# 6b 80
# 64 02: PIN different
# General
# OpenPGP card v2 doesn't support CHANGE REFERENCE DATA in exchanging
# mode (with P1 == 01, replacing PIN).
# FAIL: --change2 fails with 6b 00 (after input of PIN)
# FAIL: --change2 --admin fails with 6b 00 (after input of PIN)
# "FSIJ Gnuk (0.16-34006F06) 00 00"
# Works well except --change2
# It could support --put and --unblock, but currently it's disabled.
# "Vasco DIGIPASS 920 [CCID] 00 00"
# OK: --verify
# OK: --verify --admin
# OK: --change
# OK: --change --admin
# OK: --unblock
# FAIL: --unblock --admin fails with 69 85 (after input of PIN)
# FAIL: --put fails with 6b 80 (before input of resetcode)
# OK: --put2
# FAIL: --unblock2 fails with 69 85
# FAIL: --unblock2 --admin fails with 69 85 (after input of PIN)
# 0c4b:0500 Reiner SCT Kartensysteme GmbH
# "REINER SCT cyberJack RFID standard (7592671050) 00 00"
# OK: --verify
# OK: --verify --admin
# OK: --change
# OK: --change --admin
# OK: --unblock
# OK: --unblock --admin
# FAIL: --put fails with 69 85
# Gemalto GemPC Pinpad 00 00
# It asks users PIN with --add but it results 67 00
# It seems that it doesn't support variable length PIN
# Firmware version: GemTwRC2-V2.10-GL04
# 072f:90d2 Advanced Card Systems, Ltd
# ACS ACR83U 01 00
# --verify failed with 6b 80
# 08e6:34c2 Gemplus
# Gemalto Ezio Shield PinPad 01 00
# works well
# FAIL: --unblock2 fails with 6d 00
# 076b:3821 OmniKey AG CardMan 3821
# OmniKey CardMan 3821 01 00
# Works well with --pinmax 31 --pinmin 1