improve pinpad-test.py
This commit is contained in:
@@ -34,8 +34,18 @@ CM_IOCTL_GET_FEATURE_REQUEST = (0x42000000 + 3400)
|
|||||||
FEATURE_VERIFY_PIN_DIRECT = 0x06
|
FEATURE_VERIFY_PIN_DIRECT = 0x06
|
||||||
FEATURE_MODIFY_PIN_DIRECT = 0x07
|
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):
|
class Card(object):
|
||||||
def __init__(self, add_a_byte):
|
def __init__(self, add_a_byte, pinmax):
|
||||||
cardtype = AnyCardType()
|
cardtype = AnyCardType()
|
||||||
cardrequest = CardRequest(timeout=1, cardType=cardtype)
|
cardrequest = CardRequest(timeout=1, cardType=cardtype)
|
||||||
cardservice = cardrequest.waitforcard()
|
cardservice = cardrequest.waitforcard()
|
||||||
@@ -43,6 +53,7 @@ class Card(object):
|
|||||||
self.verify_ioctl = -1
|
self.verify_ioctl = -1
|
||||||
self.modify_ioctl = -1
|
self.modify_ioctl = -1
|
||||||
self.another_byte = add_a_byte
|
self.another_byte = add_a_byte
|
||||||
|
self.pinmax = pinmax
|
||||||
|
|
||||||
def get_features(self):
|
def get_features(self):
|
||||||
p = self.connection.control(CM_IOCTL_GET_FEATURE_REQUEST, [])
|
p = self.connection.control(CM_IOCTL_GET_FEATURE_REQUEST, [])
|
||||||
@@ -83,7 +94,7 @@ class Card(object):
|
|||||||
0x82, # bmFormatString: Byte, pos=0, left, ASCII.
|
0x82, # bmFormatString: Byte, pos=0, left, ASCII.
|
||||||
0x00, # bmPINBlockString
|
0x00, # bmPINBlockString
|
||||||
0x00, # bmPINLengthFormat
|
0x00, # bmPINLengthFormat
|
||||||
15, # wPINMaxExtraDigit Low (PINmax)
|
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
|
||||||
1, # wPINMaxExtraDigit High (PINmin)
|
1, # wPINMaxExtraDigit High (PINmin)
|
||||||
0x02, # bEntryValidationCondition
|
0x02, # bEntryValidationCondition
|
||||||
0x01, # bNumberMessage
|
0x01, # bNumberMessage
|
||||||
@@ -101,7 +112,7 @@ class Card(object):
|
|||||||
if not (sw1 == 0x90 and sw2 == 0x00):
|
if not (sw1 == 0x90 and sw2 == 0x00):
|
||||||
raise ValueError, ("cmd_verify_pinpad %02x %02x" % (sw1, sw2))
|
raise ValueError, ("cmd_verify_pinpad %02x %02x" % (sw1, sw2))
|
||||||
|
|
||||||
def send_modify_pinpad(self, adpu, command):
|
def send_modify_pinpad(self, adpu, single_step, command):
|
||||||
if self.modify_ioctl == -1:
|
if self.modify_ioctl == -1:
|
||||||
raise ValueError, "Not supported"
|
raise ValueError, "Not supported"
|
||||||
pin_modify = [ 0x00, # bTimerOut
|
pin_modify = [ 0x00, # bTimerOut
|
||||||
@@ -111,9 +122,9 @@ class Card(object):
|
|||||||
0x00, # bmPINLengthFormat
|
0x00, # bmPINLengthFormat
|
||||||
0x00, # bInsertionOffsetOld
|
0x00, # bInsertionOffsetOld
|
||||||
0x00, # bInsertionOffsetNew
|
0x00, # bInsertionOffsetNew
|
||||||
15, # wPINMaxExtraDigit Low (PINmax)
|
self.pinmax, # wPINMaxExtraDigit Low (PINmax)
|
||||||
1, # wPINMaxExtraDigit High (PINmin)
|
1, # wPINMaxExtraDigit High (PINmin)
|
||||||
0x03, # bConfirmPIN: old PIN and new PIN twice
|
confirm_pin_setting(single_step),
|
||||||
0x02, # bEntryValidationCondition
|
0x02, # bEntryValidationCondition
|
||||||
0x03, # bNumberMessage
|
0x03, # bNumberMessage
|
||||||
0x00, # wLangId Low
|
0x00, # wLangId Low
|
||||||
@@ -132,30 +143,55 @@ class Card(object):
|
|||||||
if not (sw1 == 0x90 and sw2 == 0x00):
|
if not (sw1 == 0x90 and sw2 == 0x00):
|
||||||
raise ValueError, ("%s %02x %02x" % (command, sw1, sw2))
|
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):
|
def cmd_reset_retry_counter_pinpad(self, who):
|
||||||
apdu = [0x00, 0x2c, 0x02, 0x80+who ]
|
if who == BY_ADMIN:
|
||||||
self.send_modify_pinpad(apdu, "cmd_reset_retry_counter_pinpad")
|
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")
|
||||||
|
|
||||||
def cmd_change_reference_data(self, who):
|
# Note: CCID specification doesn't permit this (only 0x20 and 0x24)
|
||||||
apdu = [0x00, 0x24, 0x00, 0x80+who ]
|
def cmd_put_resetcode_pinpad(self):
|
||||||
self.send_modify_pinpad(apdu, "cmd_change_reference_data")
|
apdu = [0x00, 0xda, 0x00, 0xd3]
|
||||||
|
self.send_modify_pinpad(apdu, True, "cmd_put_resetcode_pinpad")
|
||||||
|
|
||||||
|
def cmd_change_reference_data_pinpad(self, who, is_exchange):
|
||||||
|
if is_exchange:
|
||||||
|
apdu = [0x00, 0x24, 1, 0x80+who]
|
||||||
|
else:
|
||||||
|
apdu = [0x00, 0x24, 0x00, 0x80+who]
|
||||||
|
self.send_modify_pinpad(apdu, is_exchange,
|
||||||
|
"cmd_change_reference_data_pinpad")
|
||||||
|
|
||||||
def main(who, method, add_a_byte):
|
# "Vasco DIGIPASS 920 [CCID] 00 00"
|
||||||
card = Card(add_a_byte)
|
# "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()
|
card.connection.connect()
|
||||||
|
|
||||||
print "Reader/Token:", card.connection.getReader()
|
print "Reader/Token:", card.connection.getReader()
|
||||||
print "ATR:", toHexString( card.connection.getATR() )
|
print "ATR:", toHexString( card.connection.getATR() )
|
||||||
|
|
||||||
card.get_features()
|
card.get_features()
|
||||||
|
|
||||||
card.cmd_select_openpgp()
|
card.cmd_select_openpgp()
|
||||||
if method == "verify"
|
if method == "verify":
|
||||||
card.cmd_verify_pinpad(who)
|
card.cmd_verify_pinpad(who)
|
||||||
elif method == "change"
|
elif method == "change":
|
||||||
card.cmd_change_reference_data(self, who):
|
if change_by_two_steps:
|
||||||
elif method == "unblock"
|
card.cmd_verify_pinpad(who)
|
||||||
|
card.cmd_change_reference_data_pinpad(self, who, True)
|
||||||
|
else:
|
||||||
|
card.cmd_change_reference_data_pinpad(self, who, False)
|
||||||
|
elif method == "unblock":
|
||||||
|
# It's always by single step
|
||||||
card.cmd_reset_retry_counter_pinpad(who)
|
card.cmd_reset_retry_counter_pinpad(who)
|
||||||
|
elif method == "put":
|
||||||
|
# It's always by two steps
|
||||||
|
card.cmd_verify_pinpad(BY_ADMIN)
|
||||||
|
card.cmd_put_resetcode_pinpad()
|
||||||
else:
|
else:
|
||||||
raise ValueError, method
|
raise ValueError, method
|
||||||
card.connection.disconnect()
|
card.connection.disconnect()
|
||||||
@@ -163,25 +199,56 @@ def main(who, method, add_a_byte):
|
|||||||
print "OK."
|
print "OK."
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
BY_ADMIN = 3
|
def print_usage():
|
||||||
BY_USER = 1
|
print "pinpad-test: testing pinentry of PC/SC card reader"
|
||||||
|
print "\thelp:"
|
||||||
|
print "\t\t--help:\t\tthis message"
|
||||||
|
print "\tmethod:\t\t\t\t\t\t\t\t[verify]"
|
||||||
|
print "\t\t--verify:\tverify PIN"
|
||||||
|
print "\t\t--change:\tchange PIN (old PIN, new PIN twice)"
|
||||||
|
print "\t\t--change2:\tchange PIN by two steps (old PIN, new PIN twice)"
|
||||||
|
print "\t\t--unblock:\tunblock PIN (admin PIN or resetcode, new PIN twice)"
|
||||||
|
print "\t\t--put:\t\tsetup resetcode (admin PIN, new PIN twice)"
|
||||||
|
print "\toptions:"
|
||||||
|
print "\t\t--admin:\tby administrator\t\t\t[False]"
|
||||||
|
print "\t\t--add:\t\tadd a dummy byte at the end of APDU\t[False]"
|
||||||
|
print "\t\t--pinmax:\tspecify maximum length of PIN\t\t[15]"
|
||||||
|
print "EXAMPLES:"
|
||||||
|
print " $ pinpad-test # verify user's PIN "
|
||||||
|
print " $ pinpad-test --admin --change # change admin's PIN "
|
||||||
|
print " $ pinpad-test --admin --unblock # change user's PIN by admin's PIN"
|
||||||
|
print " $ pinpad-test --unblock # change user's PIN by reset code"
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
who = BY_USER
|
who = BY_USER
|
||||||
method = "verify"
|
method = "verify"
|
||||||
add_a_byte = False
|
add_a_byte = False
|
||||||
|
pinmax = PIN_MAX_DEFAULT
|
||||||
|
change_by_two_steps = False
|
||||||
while len(sys.argv) >= 2:
|
while len(sys.argv) >= 2:
|
||||||
option = sys.argv[1]
|
option = sys.argv[1]
|
||||||
sys.argv.pop(1)
|
sys.argv.pop(1)
|
||||||
if option == '--admin':
|
if option == '--admin':
|
||||||
who = BY_ADMIN
|
who = BY_ADMIN
|
||||||
elif option == '--unblock':
|
|
||||||
who = BY_ADMIN
|
|
||||||
method = "unblock"
|
|
||||||
elif option == '--change':
|
elif option == '--change':
|
||||||
method = "change"
|
method = "change"
|
||||||
|
elif option == '--change2':
|
||||||
|
method = "change"
|
||||||
|
change_by_two_steps = True
|
||||||
|
elif option == '--unblock':
|
||||||
|
method = "unblock"
|
||||||
elif option == '--add':
|
elif option == '--add':
|
||||||
add_a_byte = True
|
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:
|
else:
|
||||||
raise ValueError, option
|
raise ValueError, option
|
||||||
main(who, method, add_a_byte)
|
main(who, method, add_a_byte, pinmax, change_by_two_steps)
|
||||||
|
|||||||
Reference in New Issue
Block a user