Compare commits
9 Commits
release/0.
...
extended-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48d89973c6 | ||
|
|
60c1fe47ce | ||
|
|
c53afe1f96 | ||
|
|
3074058ff7 | ||
|
|
a0c8cf2ff4 | ||
|
|
c0ab2ae830 | ||
|
|
a08669dfcd | ||
|
|
4a75aa47df | ||
|
|
1145fe0ad8 |
48
ChangeLog
48
ChangeLog
@@ -1,3 +1,51 @@
|
||||
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.
|
||||
|
||||
27
NEWS
27
NEWS
@@ -1,5 +1,32 @@
|
||||
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
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
@DFU_DEFINE@
|
||||
@PINPAD_DEFINE@
|
||||
@PINPAD_MORE_DEFINE@
|
||||
@CERTDO_DEFINE@
|
||||
|
||||
36
src/configure
vendored
36
src/configure
vendored
@@ -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)
|
||||
@@ -88,6 +87,7 @@ Configuration:
|
||||
--enable-debug debug with virtual COM port [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
|
||||
@@ -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
|
||||
|
||||
16
src/flash.c
16
src/flash.c
@@ -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];
|
||||
|
||||
@@ -362,6 +362,7 @@ 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
|
||||
|
||||
@@ -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 :
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -72,13 +72,16 @@ pinpad_getline (int msg_code, systime_t timeout)
|
||||
chSysUnlock ();
|
||||
|
||||
led_blink (0);
|
||||
if (msg == 1)
|
||||
if (msg != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
msc_media_insert_change (0);
|
||||
|
||||
return pin_input_len;
|
||||
if (msg == 1)
|
||||
return pin_input_len;
|
||||
else
|
||||
return -1; /* cancel */
|
||||
}
|
||||
|
||||
static void pinpad_input (void)
|
||||
@@ -89,10 +92,13 @@ static void pinpad_input (void)
|
||||
chSysUnlock ();
|
||||
}
|
||||
|
||||
static void pinpad_finish_entry (void)
|
||||
static void pinpad_finish_entry (int cancel)
|
||||
{
|
||||
chSysLock ();
|
||||
pin_thread->p_u.rdymsg = 1;
|
||||
if (cancel)
|
||||
pin_thread->p_u.rdymsg = 2;
|
||||
else
|
||||
pin_thread->p_u.rdymsg = 1;
|
||||
chSchReadyI (pin_thread);
|
||||
chSysUnlock ();
|
||||
}
|
||||
@@ -328,7 +334,7 @@ static void parse_directory_sector (const uint8_t *p, uint8_t index)
|
||||
}
|
||||
|
||||
if (index == 0 && num_children == 1)
|
||||
pinpad_finish_entry ();
|
||||
pinpad_finish_entry (0);
|
||||
else if (input)
|
||||
pinpad_input ();
|
||||
}
|
||||
@@ -357,3 +363,10 @@ msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msc_scsi_stop (uint8_t code)
|
||||
{
|
||||
(void)code;
|
||||
pinpad_finish_entry (1);
|
||||
}
|
||||
|
||||
@@ -169,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 */
|
||||
@@ -186,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)
|
||||
{
|
||||
|
||||
@@ -388,6 +388,17 @@ void msc_handle_command (void)
|
||||
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)
|
||||
{
|
||||
@@ -397,9 +408,9 @@ void msc_handle_command (void)
|
||||
return;
|
||||
}
|
||||
/* fall through */
|
||||
success:
|
||||
case SCSI_SYNCHRONIZE_CACHE:
|
||||
case SCSI_VERIFY10:
|
||||
case SCSI_START_STOP_UNIT:
|
||||
case SCSI_ALLOW_MEDIUM_REMOVAL:
|
||||
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
|
||||
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
|
||||
|
||||
@@ -288,6 +288,7 @@ 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)
|
||||
@@ -300,6 +301,7 @@ msc_lun_info (uint16_t len)
|
||||
|
||||
return (uint8_t *)lun_table;
|
||||
}
|
||||
#endif
|
||||
|
||||
static RESULT
|
||||
gnuk_setup_with_data (uint8_t RequestNo)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
291
tool/pinpad-test.py
Executable 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
|
||||
Reference in New Issue
Block a user