pinpad-test.py: add --pinmin, --unblock2, and --put2

This commit is contained in:
NIIBE Yutaka
2012-01-04 11:57:19 +09:00
parent 18f3e72c32
commit 35a62354ae

View File

@@ -30,14 +30,20 @@ from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.util import toHexString
from getpass import getpass
CM_IOCTL_GET_FEATURE_REQUEST = (0x42000000 + 3400)
FEATURE_VERIFY_PIN_DIRECT = 0x06
FEATURE_MODIFY_PIN_DIRECT = 0x07
BY_ADMIN = 3
BY_USER = 1
PIN_MIN_DEFAULT = 6 # min of OpenPGP card
PIN_MAX_DEFAULT = 15 # max of VASCO DIGIPASS 920
def s2l(s):
return [ ord(c) for c in s ]
def confirm_pin_setting(single_step):
if single_step:
return 0x01 # bConfirmPIN: new PIN twice
@@ -45,7 +51,7 @@ def confirm_pin_setting(single_step):
return 0x03 # bConfirmPIN: old PIN and new PIN twice
class Card(object):
def __init__(self, add_a_byte, pinmax):
def __init__(self, add_a_byte, pinmin, pinmax):
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=1, cardType=cardtype)
cardservice = cardrequest.waitforcard()
@@ -53,6 +59,7 @@ class Card(object):
self.verify_ioctl = -1
self.modify_ioctl = -1
self.another_byte = add_a_byte
self.pinmin = pinmin
self.pinmax = pinmax
def get_features(self):
@@ -97,7 +104,7 @@ class Card(object):
0x00, # bmPINBlockString
0x00, # bmPINLengthFormat
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
1, # wPINMaxExtraDigit High (PINmin)
self.pinmin, # wPINMaxExtraDigit High (PINmin)
0x02, # bEntryValidationCondition
0x01, # bNumberMessage
0x00, # wLangId Low
@@ -126,7 +133,7 @@ class Card(object):
0x00, # bInsertionOffsetOld
0x00, # bInsertionOffsetNew
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
1, # wPINMaxExtraDigit High (PINmin)
self.pinmin, # wPINMaxExtraDigit High (PINmin)
confirm_pin_setting(single_step),
0x02, # bEntryValidationCondition
0x03, # bNumberMessage
@@ -147,13 +154,29 @@ class Card(object):
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("%s %02x %02x" % (command, sw1, sw2))
def cmd_reset_retry_counter(self, who, data):
if who == BY_ADMIN:
apdu = [0x00, 0x2c, 0x02, 0x81, len(data) ] + data # BY_ADMIN
else:
apdu = [0x00, 0x2c, 0x00, 0x81, len(data) ] + data # BY_USER with resetcode
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("cmd_reset_retry_counter %02x %02x" % (sw1, sw2))
# Note: CCID specification doesn't permit this (only 0x20 and 0x24)
def cmd_reset_retry_counter_pinpad(self, who):
if who == BY_ADMIN:
apdu = [0x00, 0x2c, 0x02, 0x81] # BY_ADMIN
self.send_modify_pinpad(apdu, True, "cmd_reset_retry_counter_pinpad")
else:
apdu = [0x00, 0x2c, 0x00, 0x81] # BY_USER with resetcode
self.send_modify_pinpad(apdu, False, "cmd_reset_retry_counter_pinpad")
self.send_modify_pinpad(apdu, False, "cmd_reset_retry_counter_pinpad")
def cmd_put_resetcode(self, data):
apdu = [0x00, 0xda, 0x00, 0xd3, len(data) ] + data # BY_ADMIN
response, sw1, sw2 = self.connection.transmit(apdu)
if not (sw1 == 0x90 and sw2 == 0x00):
raise ValueError, ("cmd_put_resetcode %02x %02x" % (sw1, sw2))
# Note: CCID specification doesn't permit this (only 0x20 and 0x24)
def cmd_put_resetcode_pinpad(self):
@@ -168,11 +191,8 @@ class Card(object):
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)
def main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps):
card = Card(add_a_byte, pinmin, pinmax)
card.connection.connect()
print "Reader/Token:", card.connection.getReader()
@@ -208,20 +228,38 @@ def main(who, method, add_a_byte, pinmax, change_by_two_steps):
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"
if change_by_two_steps:
# It means using keyboard for new PIN
if who == BY_USER:
resetcode=s2l(getpass("Please input reset code from keyboard: "))
newpin=s2l(getpass("Please input New User's PIN from keyboard: "))
card.cmd_reset_retry_counter(who,resetcode+newpin)
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
newpin=s2l(getpass("Please input New User's PIN from keyboard: "))
card.cmd_reset_retry_counter(who,newpin)
else:
if who == BY_USER:
print "Please input reset code"
print "and New User's PIN twice"
else:
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
print "Please input New User's PIN twice"
card.cmd_reset_retry_counter_pinpad(who)
elif method == "put":
if change_by_two_steps:
# It means using keyboard for new PIN
print "Please input Admin's PIN"
card.cmd_verify_pinpad(BY_ADMIN)
resetcode=s2l(getpass("Please input New Reset Code from keyboard: "))
card.cmd_put_resetcode(resetcode)
else:
print "Please input Admin's PIN"
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()
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()
@@ -238,10 +276,13 @@ def print_usage():
print "\t--change:\tchange PIN (old PIN, new PIN twice)"
print "\t--change2:\tchange PIN by two steps (old PIN, new PIN twice)"
print "\t--unblock:\tunblock PIN (admin PIN/resetcode, new PIN twice)"
print "\t--unblock2:\tunblock PIN (admin PIN:pinpad, new PIN:kbd)"
print "\t--put:\t\tsetup resetcode (admin PIN, new PIN twice)"
print "\t--put2::\t\tsetup resetcode (admin PIN:pinpad, new PIN:kbd)"
print " options:"
print "\t--admin:\tby administrator\t\t\t[False]"
print "\t--add:\t\tadd a dummy byte at the end of APDU\t[False]"
print "\t--pinmin:\tspecify minimum length of PIN\t\t[6]"
print "\t--pinmax:\tspecify maximum length of PIN\t\t[15]"
print "EXAMPLES:"
print " $ pinpad-test # verify user's PIN "
@@ -258,6 +299,7 @@ if __name__ == '__main__':
who = BY_USER
method = "verify"
add_a_byte = False
pinmin = PIN_MIN_DEFAULT
pinmax = PIN_MAX_DEFAULT
change_by_two_steps = False
while len(sys.argv) >= 2:
@@ -272,13 +314,22 @@ if __name__ == '__main__':
change_by_two_steps = True
elif option == '--unblock':
method = "unblock"
elif option == '--unblock2':
method = "unblock"
change_by_two_steps = True
elif option == '--add':
add_a_byte = True
elif option == '--pinmin':
pinmin = int(sys.argv[1])
sys.argv.pop(1)
elif option == '--pinmax':
pinmax = int(sys.argv[1])
sys.argv.pop(1)
elif option == '--put':
method = "put"
elif option == '--put2':
method = "put"
change_by_two_steps = True
elif option == "verify":
method = "verify"
elif option == '--help':
@@ -286,11 +337,33 @@ if __name__ == '__main__':
exit(0)
else:
raise ValueError, option
main(who, method, add_a_byte, pinmax, change_by_two_steps)
main(who, method, add_a_byte, pinmin, pinmax, change_by_two_steps)
# Failure
# 67 00: Wrong length; no further indication
# 69 82: Security status not satisfied: pin doesn't match
# 69 85: Conditions of use not satisfied
# 6b 00: Wrong parameters P1-P2
# 6b 80
# 64 02: PIN different
# "FSIJ Gnuk (0.16-34006F06) 00 00"
# Works well except --change2
# "Vasco DIGIPASS 920 [CCID] 00 00"
# OK: --verify
# OK: --verify --admin
# OK: --change
# OK: --change --admin
# FAIL: --change2 fails with 6b 00
# FAIL: --change2 --admin fails with 6b 00
# FAIL: --put fails with 6b 80
# OK: --put2
# OK: --unblock
# FAIL: --unblock --admin fails with 69 85
# FAIL: --unblock2 fails with 69 85
# FAIL: --unblock2 --admin fails with 69 85
# Gemalto GemPC Pinpad 00 00
# It asks users PIN with --add but it results 67 00
# It seems that it doesn't support variable length PIN