crypto_utils: Add generalized operation_on_string
passport_application: Completed BAC authentication and key derivation git-svn-id: svn+ssh://localhost/home/henryk/svn/cyberflex-shell/trunk@174 f711b948-2313-0410-aaa9-d29f33439f0b
This commit is contained in:
parent
82cddea7e7
commit
52fb65dc5d
|
@ -1,17 +1,159 @@
|
|||
from generic_application import Application
|
||||
import struct, sha, binascii
|
||||
from utils import hexdump, C_APDU
|
||||
import crypto_utils
|
||||
|
||||
class Passport_Application(Application):
|
||||
DRIVER_NAME = "Passport"
|
||||
|
||||
APDU_GET_RANDOM = C_APDU(CLA=0, INS=0x84, Le=0x08)
|
||||
APDU_MUTUAL_AUTHENTICATE = C_APDU(CLA=0, INS=0x82, Le=0x28)
|
||||
SW_OK = "\x90\x00"
|
||||
|
||||
AID_LIST = [
|
||||
"a0000002471001"
|
||||
]
|
||||
|
||||
def hello_cmd(self):
|
||||
"Print a friendly greeting. For test purposes."
|
||||
print "Hello world"
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.ssc = None
|
||||
self.KSenc = None
|
||||
self.KSmac = None
|
||||
|
||||
def derive_key(Kseed, c):
|
||||
"""Derive a key according to TR-PKI mrtds ICC read-only access v1.1 annex E.1.
|
||||
c is either 1 for encryption or 2 for MAC computation.
|
||||
Returns: Ka + Kb
|
||||
Note: Does not adjust parity. Nobody uses that anyway ..."""
|
||||
D = Kseed + struct.pack(">i", c)
|
||||
H = sha.sha(D).digest()
|
||||
Ka = H[0:8]
|
||||
Kb = H[8:16]
|
||||
return Ka + Kb
|
||||
derive_key = staticmethod(derive_key)
|
||||
|
||||
def derive_seed(mrz2, verbose=0):
|
||||
"""Derive Kseed from the second line of the MRZ according to TR-PKI mrtds ICC read-only access v1.1 annex F.1.1"""
|
||||
if verbose:
|
||||
print "MRZ_information: '%s' + '%s' + '%s'" % (mrz2[0:10], mrz2[13:20], mrz2[21:28])
|
||||
MRZ_information = mrz2[0:10] + mrz2[13:20] + mrz2[21:28]
|
||||
H = sha.sha(MRZ_information).digest()
|
||||
Kseed = H[:16]
|
||||
print "SHA1('%s')[:16] =\nKseed = %s" % (MRZ_information, hexdump(Kseed))
|
||||
return Kseed
|
||||
derive_seed = staticmethod(derive_seed)
|
||||
|
||||
def cmd_perform_bac(self, mrz2, verbose=1):
|
||||
"Perform the Basic Acess Control authentication and establishment of session keys"
|
||||
Kseed = self.derive_seed(mrz2, verbose)
|
||||
Kenc = self.derive_key(Kseed, 1)
|
||||
Kmac = self.derive_key(Kseed, 2)
|
||||
if verbose:
|
||||
print "Kenc = %s" % hexdump(Kenc)
|
||||
print "Kmac = %s" % hexdump(Kmac)
|
||||
|
||||
print
|
||||
result = self.send_apdu(self.APDU_GET_RANDOM)
|
||||
assert result.sw == self.SW_OK
|
||||
|
||||
rnd_icc = result.data
|
||||
if verbose:
|
||||
print "RND.icc = %s" % hexdump(rnd_icc)
|
||||
|
||||
rndtmp = self._make_random(8 + 16)
|
||||
rnd_ifd = rndtmp[:8]
|
||||
Kifd = rndtmp[8:]
|
||||
if verbose:
|
||||
print "RND.ifd = %s" % hexdump(rnd_ifd)
|
||||
print "Kifd = %s" % hexdump(Kifd, indent=10)
|
||||
|
||||
S = rnd_ifd + rnd_icc + Kifd
|
||||
Eifd = crypto_utils.cipher(True, "des3-cbc", Kenc, S)
|
||||
Mifd = self._mac(Kmac, Eifd)
|
||||
if verbose:
|
||||
print "Eifd = %s" % hexdump(Eifd, indent=10)
|
||||
print "Mifd = %s" % hexdump(Mifd)
|
||||
|
||||
print
|
||||
auth_apdu = C_APDU(self.APDU_MUTUAL_AUTHENTICATE, data = Eifd + Mifd)
|
||||
result = self.send_apdu(auth_apdu)
|
||||
assert result.sw == self.SW_OK
|
||||
|
||||
resp_data = result.data
|
||||
Eicc = resp_data[:-8]
|
||||
Micc = self._mac(Kmac, Eicc)
|
||||
if not Micc == resp_data[-8:]:
|
||||
raise ValueError, "Passport authentication failed: Wrong MAC on incoming data during Mutual Authenticate"
|
||||
|
||||
if verbose:
|
||||
print "Eicc = %s" % hexdump(Eicc, indent=10)
|
||||
print "Micc = %s" % hexdump(Micc)
|
||||
print "Micc verified OK"
|
||||
|
||||
R = crypto_utils.cipher(False, "des3-cbc", Kenc, Eicc)
|
||||
if verbose:
|
||||
print "R = %s" % hexdump(R, indent=10)
|
||||
if not R[:8] == rnd_icc:
|
||||
raise ValueError, "Passport authentication failed: Wrong RND.icc on incoming data during Mutual Authenticate"
|
||||
if not R[8:16] == rnd_ifd:
|
||||
raise ValueError, "Passport authentication failed: Wrong RND.ifd on incoming data during Mutual Authenticate"
|
||||
Kicc = R[16:]
|
||||
|
||||
if verbose:
|
||||
print "Kicc = %s" % hexdump(Kicc)
|
||||
print
|
||||
|
||||
KSseed = crypto_utils.operation_on_string(Kicc, Kifd, lambda a,b: a^b)
|
||||
self.KSenc = self.derive_key(KSseed, 1)
|
||||
self.KSmac = self.derive_key(KSseed, 2)
|
||||
self.ssc = rnd_icc[-4:] + rnd_ifd[-4:]
|
||||
|
||||
if verbose:
|
||||
print "KSseed = %s" % hexdump(KSseed)
|
||||
print "KSenc = %s" % hexdump(self.KSenc)
|
||||
print "KSmac = %s" % hexdump(self.KSmac)
|
||||
print "ssc = %s" % hexdump(self.ssc)
|
||||
|
||||
def _mac(key, data, ssc = None):
|
||||
if ssc:
|
||||
data = ssc + data
|
||||
topad = 8 - len(data) % 8
|
||||
data = data + "\x80" + ("\x00" * (topad-1))
|
||||
a = crypto_utils.cipher(True, "des-cbc", key[:8], data)
|
||||
b = crypto_utils.cipher(False, "des-ecb", key[8:16], a[-8:])
|
||||
c = crypto_utils.cipher(True, "des-ecb", key[:8], b)
|
||||
return c
|
||||
_mac = staticmethod(_mac)
|
||||
|
||||
def _make_random(len):
|
||||
"Get len random bytes from /dev/urandom"
|
||||
urand = file("/dev/urandom","r")
|
||||
try:
|
||||
r = urand.read(len)
|
||||
finally:
|
||||
urand.close()
|
||||
return r
|
||||
_make_random = staticmethod(_make_random)
|
||||
|
||||
COMMANDS = {
|
||||
"hello": hello_cmd,
|
||||
"perform_bac": cmd_perform_bac,
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
mrz1 = "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<"
|
||||
mrz2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14"
|
||||
|
||||
seed = Passport_Application.derive_seed(mrz2)
|
||||
assert seed == binascii.a2b_hex("239AB9CB282DAF66231DC5A4DF6BFBAE")
|
||||
|
||||
k = Passport_Application.derive_key(seed, 1)
|
||||
print hexdump(k)
|
||||
|
||||
print "----------------------------------------------------"
|
||||
sniff_mrz2 = "S1234567D5SGP6001010M0512310<<<<<<<<<<<<<<02"
|
||||
sniffed_Eifd = binascii.a2b_hex("".join("f7 62 81 a3 eb 7c 87 eb 6d 89 1e ec d2 8d 43 7d bf ab a0 bc 20 20 fd c4 3a 76 2a b6 ff 0c f5 61".split()))
|
||||
sniffed_Mifd = binascii.a2b_hex("".join("e1 34 04 96 3e 1c ba c8".split()))
|
||||
|
||||
seed = Passport_Application.derive_seed(sniff_mrz2)
|
||||
k = Passport_Application.derive_key(seed, 2)
|
||||
print hexdump(Passport_Application._mac(k, sniffed_Eifd))
|
||||
print hexdump(sniffed_Mifd)
|
||||
|
||||
|
|
|
@ -43,6 +43,13 @@ def cipher(do_encrypt, cipherspec, key, data, iv = None):
|
|||
del cipher
|
||||
return result
|
||||
|
||||
def operation_on_string(string1, string2, op):
|
||||
if len(string1) != len(string2):
|
||||
raise ValueError, "string1 and string2 must be of equal length"
|
||||
result = []
|
||||
for i in range(len(string1)):
|
||||
result.append( chr(op(ord(string1[i]),ord(string2[i]))) )
|
||||
return "".join(result)
|
||||
|
||||
|
||||
## *******************************************************************
|
||||
|
@ -89,12 +96,7 @@ def generate_host_challenge():
|
|||
return "".join([chr(random.randint(0,255)) for e in range(8)])
|
||||
|
||||
def andstring(string1, string2):
|
||||
if len(string1) != len(string2):
|
||||
raise ValueError, "string1 and string2 must be of equal length"
|
||||
result = []
|
||||
for i in range(len(string1)):
|
||||
result.append( chr(ord(string1[i]) & ord(string2[i])) )
|
||||
return "".join(result)
|
||||
return operation_on_string(string1, string2, lambda a,b: a & b)
|
||||
|
||||
if __name__ == "__main__":
|
||||
default_key = binascii.a2b_hex("404142434445464748494A4B4C4D4E4F")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Project SYSTEM "Project-3.7.dtd">
|
||||
<!-- Project file for project cyberflex-shell -->
|
||||
<!-- Saved: 2007-01-17, 10:14:44 -->
|
||||
<!-- Saved: 2007-02-11, 06:20:08 -->
|
||||
<!-- Copyright (C) 2007 Henryk Plötz, henryk@ploetzli.ch -->
|
||||
<Project version="3.7">
|
||||
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||
|
@ -87,6 +87,14 @@
|
|||
<Source>
|
||||
<Name>parse-usbsnoop.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>cards</Dir>
|
||||
<Name>passport_application.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>cards</Dir>
|
||||
<Name>generic_application.py</Name>
|
||||
</Source>
|
||||
</Sources>
|
||||
<Forms>
|
||||
</Forms>
|
||||
|
@ -97,7 +105,7 @@
|
|||
<Others>
|
||||
</Others>
|
||||
<Vcs>
|
||||
<VcsType>CVS</VcsType>
|
||||
<VcsType>None</VcsType>
|
||||
<VcsOptions>{'status': [u'-v'], 'log': [], 'global': [u'-f'], 'update': [u'-dP'], 'remove': [u'-f'], 'add': [], 'tag': [u'-c'], 'export': [], 'diff': [u'-u3', u'-p'], 'commit': [], 'checkout': [], 'history': [u'-e', u'-a']}</VcsOptions>
|
||||
<VcsOtherData>{}</VcsOtherData>
|
||||
</Vcs>
|
||||
|
|
Loading…
Reference in New Issue