From ad398b5d64dc4fc6eb0d2187f0c8090372232759 Mon Sep 17 00:00:00 2001 From: oepen Date: Tue, 23 Aug 2011 10:10:15 +0000 Subject: [PATCH] Refactoring: Moving card specific stuff to separate package git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@499 96b47cad-a561-4643-ad3b-153ac7d7599c --- virtualsmartcard/src/vpicc/Makefile.am | 5 + .../vpicc/virtualsmartcard/CardGenerator.py | 3 +- .../src/vpicc/virtualsmartcard/SEutils.py | 85 +--------- .../vpicc/virtualsmartcard/SmartcardSAM.py | 138 +---------------- .../vpicc/virtualsmartcard/cards/__init__.py | 0 .../virtualsmartcard/cards/cryptoflex.py | 121 +++++++++++++++ .../src/vpicc/virtualsmartcard/cards/ePass.py | 145 ++++++++++++++++++ 7 files changed, 277 insertions(+), 220 deletions(-) create mode 100644 virtualsmartcard/src/vpicc/virtualsmartcard/cards/__init__.py create mode 100644 virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py create mode 100644 virtualsmartcard/src/vpicc/virtualsmartcard/cards/ePass.py diff --git a/virtualsmartcard/src/vpicc/Makefile.am b/virtualsmartcard/src/vpicc/Makefile.am index 23bcf4a..34838bd 100644 --- a/virtualsmartcard/src/vpicc/Makefile.am +++ b/virtualsmartcard/src/vpicc/Makefile.am @@ -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 diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py index c3e163e..2c5fcc7 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py @@ -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 diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py index 7ef5295..9bcd5e1 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py @@ -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(". # -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: diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/__init__.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py new file mode 100644 index 0000000..ab1001a --- /dev/null +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py @@ -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 . + +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(". + +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 \ No newline at end of file