Refactoring: Moving card specific stuff to separate package
git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@499 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
@@ -3,6 +3,7 @@ CLEANFILES = $(bin_SCRIPTS)
|
||||
EXTRA_DIST = vicc.in
|
||||
|
||||
vpiccdir = $(pythondir)/virtualsmartcard
|
||||
vpicccardsdir = $(vpiccdir)/cards
|
||||
|
||||
vpicc_PYTHON = virtualsmartcard/CardGenerator.py \
|
||||
virtualsmartcard/SEutils.py \
|
||||
@@ -16,6 +17,10 @@ vpicc_PYTHON = virtualsmartcard/CardGenerator.py \
|
||||
virtualsmartcard/VirtualSmartcard.py \
|
||||
virtualsmartcard/__init__.py
|
||||
|
||||
vpicccards_PYTHON = virtualsmartcard/cards/__init__.py \
|
||||
virtualsmartcard/cards/ePass.py \
|
||||
virtualsmartcard/cards/cryptoflex.py
|
||||
|
||||
do_subst = sed -e 's,[@]PYTHON[@],$(PYTHON),g'
|
||||
|
||||
vicc: vicc.in Makefile
|
||||
|
||||
@@ -25,6 +25,7 @@ from virtualsmartcard.SmartcardFilesystem import MF, DF, TransparentStructureEF
|
||||
from virtualsmartcard.ConstantDefinitions import FDB
|
||||
from virtualsmartcard.CryptoUtils import protect_string, read_protected_string
|
||||
import virtualsmartcard.SmartcardSAM
|
||||
from virtualsmartcard.cards import ePass, cryptoflex
|
||||
|
||||
# pgp directory
|
||||
#self.mf.append(DF(parent=self.mf,
|
||||
@@ -127,7 +128,7 @@ class CardGenerator(object):
|
||||
mf.append(df)
|
||||
|
||||
self.mf = mf
|
||||
self.sam = virtualsmartcard.SmartcardSAM.PassportSAM(self.mf)
|
||||
self.sam = ePass.PassportSAM(self.mf)
|
||||
|
||||
def __generate_cryptoflex(self):
|
||||
from virtualsmartcard.SmartcardFilesystem import CryptoflexMF
|
||||
|
||||
@@ -690,87 +690,4 @@ class Security_Environment(object):
|
||||
return SW["NORMAL"], result
|
||||
else:
|
||||
#FIXME: Where to put the keys?
|
||||
return SW["NORMAL"], ""
|
||||
|
||||
#}}}
|
||||
class CryptoflexSE(Security_Environment):
|
||||
def __init__(self, mf):
|
||||
Security_Environment.__init__(self, mf)
|
||||
|
||||
def generate_public_key_pair(self, p1, p2, data):
|
||||
"""
|
||||
In the Cryptoflex card this command only supports RSA keys.
|
||||
|
||||
@param data: Contains the public exponent used for key generation
|
||||
@param p1: The keynumber. Can be used later to refer to the generated key
|
||||
@param p2: Used to specify the keylength.
|
||||
The mapping is: 0x40 => 256 Bit, 0x60 => 512 Bit, 0x80 => 1024
|
||||
"""
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Util.randpool import RandomPool
|
||||
|
||||
keynumber = p1 #TODO: Check if key exists
|
||||
|
||||
keylength_dict = {0x40: 256, 0x60: 512, 0x80: 1024}
|
||||
|
||||
if not keylength_dict.has_key(p2):
|
||||
raise SwError(SW["ERR_INCORRECTP1P2"])
|
||||
else:
|
||||
keylength = keylength_dict[p2]
|
||||
|
||||
rnd = RandomPool()
|
||||
PublicKey = RSA.generate(keylength, rnd.get_bytes)
|
||||
self.dst.key = PublicKey
|
||||
|
||||
e_in = struct.unpack("<i", data)
|
||||
if e_in[0] != 65537:
|
||||
logging.warning("Warning: Exponents different from 65537 are ignored!" +\
|
||||
"The Exponent given is %i" % e_in[0])
|
||||
|
||||
#Encode Public key
|
||||
n = PublicKey.__getstate__()['n']
|
||||
n_str = inttostring(n)
|
||||
n_str = n_str[::-1]
|
||||
e = PublicKey.__getstate__()['e']
|
||||
e_str = inttostring(e, 4)
|
||||
e_str = e_str[::-1]
|
||||
pad = 187 * '\x30' #We don't have CRT components, so we need to pad
|
||||
pk_n = TLVutils.bertlv_pack(((0x81, len(n_str), n_str),
|
||||
(0x01, len(pad), pad),
|
||||
(0x82, len(e_str), e_str)))
|
||||
#Private key
|
||||
d = PublicKey.__getstate__()['d']
|
||||
|
||||
#Write result to FID 10 12 EF-PUB-KEY
|
||||
df = self.mf.currentDF()
|
||||
ef_pub_key = df.select("fid", 0x1012)
|
||||
ef_pub_key.writebinary([0], [pk_n])
|
||||
data = ef_pub_key.getenc('data')
|
||||
|
||||
#Write private key to FID 00 12 EF-PRI-KEY (not necessary?)
|
||||
#How to encode the private key?
|
||||
ef_priv_key = df.select("fid", 0x0012)
|
||||
ef_priv_key.writebinary([0], [inttostring(d)])
|
||||
data = ef_priv_key.getenc('data')
|
||||
return PublicKey
|
||||
|
||||
class ePass_SE(Security_Environment):
|
||||
|
||||
def __init__(self, MF, SE, ssc=None):
|
||||
self.ssc = ssc
|
||||
Security_Environment.__init__(self, MF, 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
|
||||
return SW["NORMAL"], ""
|
||||
@@ -17,14 +17,14 @@
|
||||
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import struct, hashlib, logging
|
||||
import logging
|
||||
from pickle import dumps, loads
|
||||
from os import urandom
|
||||
|
||||
import virtualsmartcard.CryptoUtils as vsCrypto
|
||||
from virtualsmartcard.SWutils import SwError, SW
|
||||
from virtualsmartcard.utils import inttostring, stringtoint
|
||||
from virtualsmartcard.SEutils import Security_Environment, CryptoflexSE, ePass_SE
|
||||
from virtualsmartcard.SEutils import Security_Environment
|
||||
|
||||
def get_referenced_cipher(p1):
|
||||
"""
|
||||
@@ -336,139 +336,7 @@ class SAM(object):
|
||||
|
||||
def manage_security_environment(self, p1, p2, data):
|
||||
return self.current_SE.manage_security_environment(p1, p2, data)
|
||||
|
||||
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.KSenc = None
|
||||
self.KSmac = None
|
||||
self.__computeKeys()
|
||||
SAM.__init__(self, None, None, mf)
|
||||
self.current_SE = ePass_SE(mf, 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"])
|
||||
#Extraxt 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
|
||||
KSseed = vsCrypto.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:]
|
||||
#Set the current SE
|
||||
self.current_SE.ct.key = self.KSenc
|
||||
self.current_SE.cct.key = self.KSmac
|
||||
self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
|
||||
self.current_SE.ct.algorithm = "DES3-CBC"
|
||||
self.current_SE.cct.algorithm = "CC"
|
||||
return SW["NORMAL"], Eicc + Micc
|
||||
|
||||
def _mac(self, key, data, ssc = None, dopad=True):
|
||||
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
|
||||
|
||||
class CryptoflexSAM(SAM):
|
||||
def __init__(self, mf=None):
|
||||
SAM.__init__(self, None, None, mf)
|
||||
self.current_SE = CryptoflexSE(mf)
|
||||
|
||||
def generate_public_key_pair(self, p1, p2, data):
|
||||
asym_key = self.current_SE.generate_public_key_pair(p1, p2, data)
|
||||
#TODO: Use SE instead (and remove SAM.set_asym_algorithm)
|
||||
self.set_asym_algorithm(asym_key, 0x07)
|
||||
return SW["NORMAL"], ""
|
||||
|
||||
def perform_security_operation(self, p1, p2, data):
|
||||
"""
|
||||
In the cryptoflex card, this is the verify key command. A key is send
|
||||
to the card in plain text and compared to a key stored in the card.
|
||||
This is used for authentication
|
||||
@param data: Contains the key to be verified
|
||||
@return: SW[NORMAL] in case of success otherwise SW[WARN_NOINFO63]
|
||||
"""
|
||||
return SW["NORMAL"], ""
|
||||
#FIXME
|
||||
#key = self._get_referenced_key(p1,p2)
|
||||
#if key == data:
|
||||
# return SW["NORMAL"], ""
|
||||
#else:
|
||||
# return SW["WARN_NOINFO63"], ""
|
||||
|
||||
def internal_authenticate(self, p1, p2, data):
|
||||
data = data[::-1] #Reverse Byte order
|
||||
sw, data = SAM.internal_authenticate(self, p1, p2, data)
|
||||
if data != "":
|
||||
data = data[::-1]
|
||||
return sw, data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Unit test:
|
||||
|
||||
121
virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py
Normal file
121
virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#
|
||||
# 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
|
||||
from virtualsmartcard.SWutils import SwError, SW
|
||||
from virtualsmartcard.utils import inttostring
|
||||
from virtualsmartcard import TLVutils
|
||||
|
||||
import struct, logging
|
||||
|
||||
class CryptoflexSE(Security_Environment):
|
||||
def __init__(self, mf):
|
||||
Security_Environment.__init__(self, mf)
|
||||
|
||||
def generate_public_key_pair(self, p1, p2, data):
|
||||
"""
|
||||
In the Cryptoflex card this command only supports RSA keys.
|
||||
|
||||
@param data: Contains the public exponent used for key generation
|
||||
@param p1: The keynumber. Can be used later to refer to the generated key
|
||||
@param p2: Used to specify the keylength.
|
||||
The mapping is: 0x40 => 256 Bit, 0x60 => 512 Bit, 0x80 => 1024
|
||||
"""
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Util.randpool import RandomPool
|
||||
|
||||
keynumber = p1 #TODO: Check if key exists
|
||||
|
||||
keylength_dict = {0x40: 256, 0x60: 512, 0x80: 1024}
|
||||
|
||||
if not keylength_dict.has_key(p2):
|
||||
raise SwError(SW["ERR_INCORRECTP1P2"])
|
||||
else:
|
||||
keylength = keylength_dict[p2]
|
||||
|
||||
rnd = RandomPool()
|
||||
PublicKey = RSA.generate(keylength, rnd.get_bytes)
|
||||
self.dst.key = PublicKey
|
||||
|
||||
e_in = struct.unpack("<i", data)
|
||||
if e_in[0] != 65537:
|
||||
logging.warning("Warning: Exponents different from 65537 are ignored!" +\
|
||||
"The Exponent given is %i" % e_in[0])
|
||||
|
||||
#Encode Public key
|
||||
n = PublicKey.__getstate__()['n']
|
||||
n_str = inttostring(n)
|
||||
n_str = n_str[::-1]
|
||||
e = PublicKey.__getstate__()['e']
|
||||
e_str = inttostring(e, 4)
|
||||
e_str = e_str[::-1]
|
||||
pad = 187 * '\x30' #We don't have CRT components, so we need to pad
|
||||
pk_n = TLVutils.bertlv_pack(((0x81, len(n_str), n_str),
|
||||
(0x01, len(pad), pad),
|
||||
(0x82, len(e_str), e_str)))
|
||||
#Private key
|
||||
d = PublicKey.__getstate__()['d']
|
||||
|
||||
#Write result to FID 10 12 EF-PUB-KEY
|
||||
df = self.mf.currentDF()
|
||||
ef_pub_key = df.select("fid", 0x1012)
|
||||
ef_pub_key.writebinary([0], [pk_n])
|
||||
data = ef_pub_key.getenc('data')
|
||||
|
||||
#Write private key to FID 00 12 EF-PRI-KEY (not necessary?)
|
||||
#How to encode the private key?
|
||||
ef_priv_key = df.select("fid", 0x0012)
|
||||
ef_priv_key.writebinary([0], [inttostring(d)])
|
||||
data = ef_priv_key.getenc('data')
|
||||
return PublicKey
|
||||
|
||||
|
||||
class CryptoflexSAM(SAM):
|
||||
def __init__(self, mf=None):
|
||||
SAM.__init__(self, None, None, mf)
|
||||
self.current_SE = CryptoflexSE(mf)
|
||||
|
||||
def generate_public_key_pair(self, p1, p2, data):
|
||||
asym_key = self.current_SE.generate_public_key_pair(p1, p2, data)
|
||||
#TODO: Use SE instead (and remove SAM.set_asym_algorithm)
|
||||
self.set_asym_algorithm(asym_key, 0x07)
|
||||
return SW["NORMAL"], ""
|
||||
|
||||
def perform_security_operation(self, p1, p2, data):
|
||||
"""
|
||||
In the cryptoflex card, this is the verify key command. A key is send
|
||||
to the card in plain text and compared to a key stored in the card.
|
||||
This is used for authentication
|
||||
@param data: Contains the key to be verified
|
||||
@return: SW[NORMAL] in case of success otherwise SW[WARN_NOINFO63]
|
||||
"""
|
||||
return SW["NORMAL"], ""
|
||||
#FIXME
|
||||
#key = self._get_referenced_key(p1,p2)
|
||||
#if key == data:
|
||||
# return SW["NORMAL"], ""
|
||||
#else:
|
||||
# return SW["WARN_NOINFO63"], ""
|
||||
|
||||
def internal_authenticate(self, p1, p2, data):
|
||||
data = data[::-1] #Reverse Byte order
|
||||
sw, data = SAM.internal_authenticate(self, p1, p2, data)
|
||||
if data != "":
|
||||
data = data[::-1]
|
||||
return sw, data
|
||||
145
virtualsmartcard/src/vpicc/virtualsmartcard/cards/ePass.py
Normal file
145
virtualsmartcard/src/vpicc/virtualsmartcard/cards/ePass.py
Normal file
@@ -0,0 +1,145 @@
|
||||
#
|
||||
# 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):
|
||||
|
||||
def __init__(self, MF, SE, ssc=None):
|
||||
self.ssc = ssc
|
||||
Security_Environment.__init__(self, MF, 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.KSenc = None
|
||||
self.KSmac = None
|
||||
self.__computeKeys()
|
||||
SAM.__init__(self, None, None, mf)
|
||||
self.current_SE = ePass_SE(mf, 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
|
||||
KSseed = vsCrypto.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:]
|
||||
#Set the current SE
|
||||
self.current_SE.ct.key = self.KSenc
|
||||
self.current_SE.cct.key = self.KSmac
|
||||
self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
|
||||
self.current_SE.ct.algorithm = "DES3-CBC"
|
||||
self.current_SE.cct.algorithm = "CC"
|
||||
return SW["NORMAL"], Eicc + Micc
|
||||
|
||||
def _mac(self, key, data, ssc = None, dopad=True):
|
||||
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
|
||||
Reference in New Issue
Block a user