Files
vsmartcard/virtualsmartcard/src/vpicc/virtualsmartcard/cards/ePass.py
oepen 67f9e654c7 Added documentation
git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@507 96b47cad-a561-4643-ad3b-153ac7d7599c
2011-08-23 13:43:24 +00:00

142 lines
5.4 KiB
Python

#
# Copyright (C) 2011 Dominik Oepen
#
# This file is part of virtualsmartcard.
#
# virtualsmartcard is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# virtualsmartcard is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
from virtualsmartcard.SmartcardSAM import SAM
from virtualsmartcard.SEutils import Security_Environment
import virtualsmartcard.CryptoUtils as vsCrypto
from virtualsmartcard.SWutils import SwError, SW
from virtualsmartcard.utils import stringtoint
import hashlib, struct
from os import urandom
class ePass_SE(Security_Environment):
"""This class implements the Security Environment of the ICAO Passports. It
is required in order to use the send sequence counter for secure messaging.
"""
def __init__(self, SE, ssc=None):
self.ssc = ssc
Security_Environment.__init__(self, SE)
def compute_cryptographic_checksum(self, p1, p2, data):
"""
Compute a cryptographic checksum (e.g. MAC) for the given data.
Algorithm and key are specified in the current (CAPDU) SE. The ePass
uses a Send Sequence Counter for MAC calculation
"""
if p1 != 0x8E or p2 != 0x80:
raise SwError(SW["ERR_INCORRECTP1P2"])
self.ssc += 1
checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key,
data, self.cct.iv, self.ssc)
return SW["NORMAL"], checksum
class PassportSAM(SAM):
"""
SAM for ICAO ePassport. Implements Basic access control and key derivation
for Secure Messaging.
"""
def __init__(self, mf):
import virtualsmartcard.SmartcardFilesystem as vsFS
ef_dg1 = vsFS.walk(mf, "\x00\x04\x01\x01")
dg1 = ef_dg1.readbinary(5)
self.mrz1 = dg1[:43]
self.mrz2 = dg1[44:]
self.KSeed = None
self.KEnc = None
self.KMac = None
self.__computeKeys()
SAM.__init__(self, None, None, mf)
self.current_SE = ePass_SE(None, None)
self.current_SE.cct.algorithm = "CC"
self.current_SE.ct.algorithm = "DES3-CBC"
def __computeKeys(self):
"""
Computes the keys depending on the machine readable
zone of the passport according to TR-PKI mrtds ICC read-only
access v1.1 annex E.1.
"""
MRZ_information = self.mrz2[0:10] + self.mrz2[13:20] + self.mrz2[21:28]
H = hashlib.sha1(MRZ_information).digest()
self.KSeed = H[:16]
self.KEnc = self.derive_key(self.KSeed, 1)
self.KMac = self.derive_key(self.KSeed, 2)
def derive_key(self, seed, 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 = seed + struct.pack(">i", c)
H = hashlib.sha1(D).digest()
Ka = H[0:8]
Kb = H[8:16]
return Ka + Kb
def external_authenticate(self, p1, p2, resp_data):
"""Performs the basic access control protocol as defined in
the ICAO MRTD standard"""
rnd_icc = self.last_challenge
#Receive Mutual Authenticate APDU from terminal
#Decrypt data and check MAC
Eifd = resp_data[:-8]
Mifd = self._mac(self.KMac, Eifd)
#Check the MAC
if not Mifd == resp_data[-8:]:
raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
#Decrypt the data
plain = vsCrypto.decrypt("DES3-CBC", self.KEnc, resp_data[:-8])
#Split decrypted data into the two nonces and
if plain[8:16] != rnd_icc:
raise SwError(SW["WARN_NOINFO63"])
#Extract keying material from IFD, generate ICC keying material
Kifd = plain[16:]
rnd_ifd = plain[:8]
Kicc = urandom(16)
#Generate Answer
data = plain[8:16] + plain[:8] + Kicc
Eicc = vsCrypto.encrypt("DES3-CBC", self.KEnc, data)
Micc = self._mac(self.KMac, Eicc)
#Derive the final keys and set the current SE
KSseed = vsCrypto.operation_on_string(Kicc, Kifd, lambda a, b: a^b)
self.current_SE.ct.key = self.derive_key(KSseed, 1)
self.current_SE.cct.key = self.derive_key(KSseed, 2)
self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
return SW["NORMAL"], Eicc + Micc
def _mac(self, key, data, ssc = None, dopad=True):
""" Compute a message authentication code using a given key and an
optional send sequence counter."""
if ssc:
data = ssc + data
if dopad:
topad = 8 - len(data) % 8
data = data + "\x80" + ("\x00" * (topad-1))
a = vsCrypto.encrypt("des-cbc", key[:8], data)
b = vsCrypto.decrypt("des-ecb", key[8:16], a[-8:])
c = vsCrypto.encrypt("des-ecb", key[:8], b)
return c