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:
hploetz 2007-02-12 01:51:20 +00:00
parent 82cddea7e7
commit 52fb65dc5d
3 changed files with 165 additions and 13 deletions

View File

@ -1,17 +1,159 @@
from generic_application import Application from generic_application import Application
import struct, sha, binascii
from utils import hexdump, C_APDU
import crypto_utils
class Passport_Application(Application): class Passport_Application(Application):
DRIVER_NAME = "Passport" 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 = [ AID_LIST = [
"a0000002471001" "a0000002471001"
] ]
def hello_cmd(self): def __init__(self, *args, **kwargs):
"Print a friendly greeting. For test purposes." self.ssc = None
print "Hello world" 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 = { 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)

View File

@ -43,6 +43,13 @@ def cipher(do_encrypt, cipherspec, key, data, iv = None):
del cipher del cipher
return result 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)]) return "".join([chr(random.randint(0,255)) for e in range(8)])
def andstring(string1, string2): def andstring(string1, string2):
if len(string1) != len(string2): return operation_on_string(string1, string2, lambda a,b: a & b)
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)
if __name__ == "__main__": if __name__ == "__main__":
default_key = binascii.a2b_hex("404142434445464748494A4B4C4D4E4F") default_key = binascii.a2b_hex("404142434445464748494A4B4C4D4E4F")

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-3.7.dtd"> <!DOCTYPE Project SYSTEM "Project-3.7.dtd">
<!-- Project file for project cyberflex-shell --> <!-- 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 --> <!-- Copyright (C) 2007 Henryk Plötz, henryk@ploetzli.ch -->
<Project version="3.7"> <Project version="3.7">
<ProgLanguage mixed="0">Python</ProgLanguage> <ProgLanguage mixed="0">Python</ProgLanguage>
@ -87,6 +87,14 @@
<Source> <Source>
<Name>parse-usbsnoop.py</Name> <Name>parse-usbsnoop.py</Name>
</Source> </Source>
<Source>
<Dir>cards</Dir>
<Name>passport_application.py</Name>
</Source>
<Source>
<Dir>cards</Dir>
<Name>generic_application.py</Name>
</Source>
</Sources> </Sources>
<Forms> <Forms>
</Forms> </Forms>
@ -97,7 +105,7 @@
<Others> <Others>
</Others> </Others>
<Vcs> <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> <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> <VcsOtherData>{}</VcsOtherData>
</Vcs> </Vcs>