aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhploetz <hploetz@f711b948-2313-0410-aaa9-d29f33439f0b>2007-02-12 01:51:20 +0000
committerhploetz <hploetz@f711b948-2313-0410-aaa9-d29f33439f0b>2007-02-12 01:51:20 +0000
commit52fb65dc5d0a8162c5083609bd96487e2643f20a (patch)
treeedc94836b64bcc88de828a6f89306c2dc8c3b8bb
parent82cddea7e7bdd15a9c80a13e5c8a80259c8d4aa3 (diff)
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
-rw-r--r--cards/passport_application.py152
-rw-r--r--crypto_utils.py14
-rw-r--r--cyberflex-shell.e3p12
3 files changed, 165 insertions, 13 deletions
diff --git a/cards/passport_application.py b/cards/passport_application.py
index 2061c4a..eed43f8 100644
--- a/cards/passport_application.py
+++ b/cards/passport_application.py
@@ -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)
+
diff --git a/crypto_utils.py b/crypto_utils.py
index d497118..c655e1e 100644
--- a/crypto_utils.py
+++ b/crypto_utils.py
@@ -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")
diff --git a/cyberflex-shell.e3p b/cyberflex-shell.e3p
index bb952fc..1c1a5aa 100644
--- a/cyberflex-shell.e3p
+++ b/cyberflex-shell.e3p
@@ -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>