Compare commits

..

25 Commits

Author SHA1 Message Date
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
37 changed files with 2009 additions and 274 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.

128
ChangeLog
View File

@@ -1,3 +1,131 @@
2011-12-28 Niibe Yutaka <gniibe@fsij.org>
* 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.

52
NEWS
View File

@@ -1,5 +1,55 @@
Gnuk NEWS - User visible changes
* Major changes in Gnuk 0.17
Released 2012-01-XX, by NIIBE Yutaka
** 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 size
packet). Now, it has been fixed. You can use any size.
** 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: pinpad-test.py
The tool pinpad-test.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 +59,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

@@ -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

@@ -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

@@ -24,6 +24,13 @@ extern void *memmove(void *dest, const void *src, size_t n);
#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) */
@@ -79,8 +86,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 +350,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++)
@@ -386,6 +390,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
};
@@ -1241,6 +1245,7 @@ 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;
@@ -1255,6 +1260,7 @@ gpg_do_get_data (uint16_t tag, int with_tag)
res_APDU_size += 4 + 2;
}
else
#endif
{
const struct do_table_entry *do_p = get_do_entry (tag);

View File

@@ -125,32 +125,14 @@ cmd_verify (void)
DEBUG_INFO (" - VERIFY\r\n");
DEBUG_BYTE (p2);
#if defined(PINPAD_SUPPORT)
if (cmd_APDU_size == 4)
/* Verification with pinpad */
len = cmd_APDU[4];
if (len == 0) /* extended length */
{
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 = (cmd_APDU[5]<<8) | cmd_APDU[6];
data_start = 7;
}
pw = &cmd_APDU[data_start];
if (p2 == 0x81)
r = verify_pso_cds (pw, len);
@@ -231,6 +213,7 @@ 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 p1 = cmd_APDU[2]; /* 0: change (old+new), 1: exchange (new) */
uint8_t p2 = cmd_APDU[3];
int len;
const uint8_t *pw;
@@ -242,62 +225,25 @@ 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 = cmd_APDU[4];
pw = &cmd_APDU[5];
if (len == 0) /* extended length */
{
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;
len = (cmd_APDU[5]<<8) | cmd_APDU[6];
pw += 2;
}
else
#endif
if (p1 != 0)
{
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)
{
@@ -395,60 +341,12 @@ 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 */
len = cmd_APDU[4];
pw = &cmd_APDU[5];
if (len == 0) /* extended length */
{
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 = (cmd_APDU[5]<<8) | cmd_APDU[6];
pw += 2;
}
if (p1 == 0x00) /* by User with Reseting Code */
@@ -572,8 +470,9 @@ cmd_put_data (void)
GPG_NO_RECORD();
tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
len = cmd_APDU_size - 5;
data = &cmd_APDU[5];
len = cmd_APDU_size - 5;
if (len >= 256)
/* extended Lc */
{
@@ -1026,15 +925,102 @@ 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 */
res_APDU_pointer = NULL;
if (m == EV_VERIFY_CMD_AVAILABLE)
{
#if defined(PINPAD_SUPPORT)
if (cmd_APDU[1] != 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 (&cmd_APDU[5], pin_input_buffer, pw_len);
cmd_APDU[4] = pw_len;
icc_data_size = 5 + pw_len;
#else
GPG_ERROR ();
goto done;
#endif
}
else if (m == EV_MODIFY_CMD_AVAILABLE)
{
#if defined(PINPAD_SUPPORT)
uint8_t bConfirmPIN = cmd_APDU[4];
uint8_t *p = &cmd_APDU[5];
if (cmd_APDU[1] != INS_CHANGE_REFERENCE_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;
}
}
len = cmd_APDU[4] = pw_len + newpw_len;
icc_data_size = 5 + len;
#else
GPG_ERROR ();
goto done;
#endif
}
else if (m == EV_NOP)
continue;
if (icc_data_size != 0)
{
process_command_apdu ();
done:
chEvtSignal (icc_thread, EV_EXEC_FINISHED);
}
}

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

@@ -26,7 +26,6 @@
#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"
@@ -170,12 +169,31 @@ 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);
if (len == 0)
{ /* Just ignore Zero Length Packet (ZLP), if any */
SetEPRxValid (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);
if ((icc_next_p - icc_buffer) >= USB_BUF_SIZE)
/* No room to receive any more */
@@ -187,27 +205,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)
{
@@ -374,7 +383,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;
}
@@ -497,7 +506,7 @@ icc_handle_data (void)
{
if (icc_header->param == 0)
{ /* Give this message to GPG thread */
chEvtSignal (gpg_thread, (eventmask_t)1);
chEvtSignal (gpg_thread, EV_CMD_AVAILABLE);
next_state = ICC_STATE_EXECUTE;
}
else if (icc_header->param == 1)
@@ -524,7 +533,15 @@ icc_handle_data (void)
cmd_APDU[2] = icc_buffer[27];
cmd_APDU[3] = icc_buffer[28];
icc_data_size = 4;
chEvtSignal (gpg_thread, (eventmask_t)1);
cmd_APDU[4] = 0; /* bConfirmPIN */
cmd_APDU[5] = icc_buffer[17]; /* bEntryValidationCondition */
cmd_APDU[6] = icc_buffer[18]; /* bNumberMessage */
cmd_APDU[7] = icc_buffer[19]; /* wLangId L */
cmd_APDU[8] = icc_buffer[20]; /* wLangId H */
cmd_APDU[9] = icc_buffer[21]; /* bMsgIndex, bMsgIndex1 */
cmd_APDU[10] = 0; /* bMsgIndex2 */
cmd_APDU[11] = 0; /* bMsgIndex3 */
chEvtSignal (gpg_thread, EV_VERIFY_CMD_AVAILABLE);
next_state = ICC_STATE_EXECUTE;
}
else if (icc_buffer[10] == 0x01) /* PIN Modification */
@@ -540,7 +557,18 @@ icc_handle_data (void)
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);
cmd_APDU[4] = icc_buffer[19]; /* bConfirmPIN */
cmd_APDU[5] = icc_buffer[20]; /* bEntryValidationCondition */
cmd_APDU[6] = icc_buffer[21]; /* bNumberMessage */
cmd_APDU[7] = icc_buffer[22]; /* wLangId L */
cmd_APDU[8] = icc_buffer[23]; /* wLangId H */
cmd_APDU[9] = icc_buffer[24]; /* bMsgIndex, bMsgIndex1 */
cmd_APDU[10] = cmd_APDU[11] = 0;
if (num_msgs >= 2)
cmd_APDU[10] = icc_buffer[25]; /* bMsgIndex2 */
if (num_msgs == 3)
cmd_APDU[11] = icc_buffer[26]; /* bMsgIndex3 */
chEvtSignal (gpg_thread, EV_MODIFY_CMD_AVAILABLE);
next_state = ICC_STATE_EXECUTE;
}
else
@@ -572,7 +600,7 @@ icc_handle_data (void)
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);
chEvtSignal (gpg_thread, EV_CMD_AVAILABLE);
}
else /* icc_header->param == 3 is not supported. */
{

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);
}

567
src/usb_msc.c Normal file
View File

@@ -0,0 +1,567 @@
/*
* usb_msc.c -- USB Mass Storage Class protocol handling
*
* 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 "usb_lib.h"
#include "config.h"
#include "ch.h"
#include "gnuk.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_stall_transmit (void)
{
SetEPTxStatus (ENDP6, EP_TX_STALL);
}
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_SIL_Write (EP6_IN, (uint8_t *)ep6_in.txbuf, pkt_len);
SetEPTxValid (ENDP6);
}
/* "Data Transmitted" callback */
void EP6_IN_Callback (void)
{
size_t n = (size_t)GetEPTxCount (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_SIL_Write (EP6_IN, (uint8_t *)ep6_in.txbuf, n);
SetEPTxValid (ENDP6);
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_stall_receive (void)
{
SetEPRxStatus (ENDP7, EP_RX_STALL);
}
static void usb_start_receive (uint8_t *p, size_t n)
{
ep7_out.rxbuf = p;
ep7_out.rxsize = n;
ep7_out.rxcnt = 0;
if (n < ENDP_MAX_SIZE)
SetEPRxCount (ENDP7, n);
else
SetEPRxCount (ENDP7, ENDP_MAX_SIZE);
SetEPRxValid (ENDP7);
}
/* "Data Received" call back */
void EP7_OUT_Callback (void)
{
size_t n = (size_t)GetEPRxCount (ENDP7);
int err = 0;
if (n > ep7_out.rxsize)
{ /* buffer overflow */
err = 1;
SetEPRxCount (ENDP7, ep7_out.rxsize);
}
n = USB_SIL_Read (EP7_OUT, ep7_out.rxbuf);
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 */
SetEPRxValid (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_stall_receive ();
chSysUnlock ();
}
return;
}
n = ep7_out.rxcnt;
if ((n != sizeof (struct CBW)) || (CBW.dCBWSignature != MSC_CBW_SIGNATURE))
{
msc_state = MSC_ERROR;
chSysLock ();
usb_stall_receive ();
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_stall_transmit ();
usb_stall_receive ();
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,10 @@
#include "usb-cdc-vport.c"
#endif
#ifdef PINPAD_DND_SUPPORT
#include "usb_msc.h"
#endif
static void
gnuk_device_init (void)
@@ -108,6 +112,20 @@ gnuk_device_reset (void)
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);
SetEPRxStatus (ENDP7, EP_RX_STALL);
SetEPTxStatus (ENDP7, EP_TX_DIS);
#endif
/* Set this device to response on default address */
SetDeviceAddress (0);
@@ -153,6 +171,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 +233,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 +288,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 +326,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 +369,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

@@ -72,7 +72,7 @@ class GnukToken(object):
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"

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"

291
tool/pinpad-test.py Executable file
View File

@@ -0,0 +1,291 @@
#! /usr/bin/python
"""
pinpad-test.py - a tool to test pinpad support by card reader.
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/>.
"""
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
CM_IOCTL_GET_FEATURE_REQUEST = (0x42000000 + 3400)
FEATURE_VERIFY_PIN_DIRECT = 0x06
FEATURE_MODIFY_PIN_DIRECT = 0x07
BY_ADMIN = 3
BY_USER = 1
PIN_MAX_DEFAULT = 15 # max of VASCO DIGIPASS 920
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, pinmax):
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=1, cardType=cardtype)
cardservice = cardrequest.waitforcard()
self.connection = cardservice.connection
self.verify_ioctl = -1
self.modify_ioctl = -1
self.another_byte = add_a_byte
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)
1, # wPINMaxExtraDigit High (PINmin)
0x02, # bEntryValidationCondition
0x01, # bNumberMessage
0x00, # wLangId Low
0x00, # wLangId High
0x00, # bMsgIndex
0x00, # bTeoPrologue[0]
0x00, # bTeoPrologue[1]
0x00 # bTeoPrologue[2]
]
pin_verify += [ len(apdu), 0, 0, 0 ] + apdu + self.possibly_add_dummy_byte()
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)
1, # 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]
]
pin_modify += [ len(apdu), 0, 0, 0 ] + apdu + self.possibly_add_dummy_byte()
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))
# 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
else:
apdu = [0x00, 0x2c, 0x00, 0x81] # BY_USER with resetcode
self.send_modify_pinpad(apdu, False, "cmd_reset_retry_counter_pinpad")
# 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")
# "Vasco DIGIPASS 920 [CCID] 00 00"
# "FSIJ Gnuk (0.16-34006F06) 00 00"
def main(who, method, add_a_byte, pinmax, change_by_two_steps):
card = Card(add_a_byte, 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":
# It's always by single step
if who == BY_USER:
print "Please input reset code"
print "and New User's PIN twice"
else:
print "Please input Admin's PIN"
print "and New User's PIN twice"
card.cmd_reset_retry_counter_pinpad(who)
elif method == "put":
# It's always by two steps
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--put:\t\tsetup resetcode (admin PIN, new PIN twice)"
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--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
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 == '--add':
add_a_byte = True
elif option == '--pinmax':
pinmax = int(sys.argv[1])
sys.argv.pop(1)
elif option == '--put':
method = "put"
elif option == "verify":
method = "verify"
elif option == '--help':
print_usage()
exit(0)
else:
raise ValueError, option
main(who, method, add_a_byte, pinmax, change_by_two_steps)
# 69 82: Security status not satisfied: pin doesn't match
# 69 85: Conditions of use not satisfied
# 6b 00: Wrong parameters P1-P2