From e500aa3382e8939d535daaa5d23e702513cd45ed Mon Sep 17 00:00:00 2001 From: oepen Date: Sat, 30 Jan 2010 18:51:00 +0000 Subject: [PATCH] Refactoring and Bugfixing: - CryptoUtils: - removing hard coded keylength from append_padding in CryptoUtils - rewrote append_padding, slightly modified strip_padding in CryptoUtils - added get_cipher_keylen and get_cipher_blocklen to CryptoUtils - Updated UnitTest in CryptoUtils. Note: PBKDF2.self_test() fails!!! - added new function protect_string to encrypt and authenticate a string - Removed hard coded padding pattern - VirtualICC: - Added and integrated functions to load and save VirtualICC objects - Added exception handling for opening of the socket - SmartcardSAM: - Removed all object persistance functionaliy from SmartcardSAM - Changed the constructors of the *SAM classes - Moved all the functionality to create cards (especially the pre-initialized filesystems for each card type) to the new file GenerateCards.py - Added minimal command line based interface to GenerateCards.py to generate customized cards and store them in a file - Removed default sam and mf files (testconfig.mf and testconfig.sam) git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@35 96b47cad-a561-4643-ad3b-153ac7d7599c --- virtualsmartcard/vpicc/CardGenerator.py | 188 +++++++++++++++++ virtualsmartcard/vpicc/CryptoUtils.py | 176 ++++++++++++---- virtualsmartcard/vpicc/SmartcardSAM.py | 223 +++++--------------- virtualsmartcard/vpicc/VirtualSmartcard.py | 234 +++++++-------------- virtualsmartcard/vpicc/testconfig.mf | 1 - virtualsmartcard/vpicc/testconfig.sam | 2 - 6 files changed, 448 insertions(+), 376 deletions(-) create mode 100644 virtualsmartcard/vpicc/CardGenerator.py delete mode 100644 virtualsmartcard/vpicc/testconfig.mf delete mode 100644 virtualsmartcard/vpicc/testconfig.sam diff --git a/virtualsmartcard/vpicc/CardGenerator.py b/virtualsmartcard/vpicc/CardGenerator.py new file mode 100644 index 0000000..4924c3f --- /dev/null +++ b/virtualsmartcard/vpicc/CardGenerator.py @@ -0,0 +1,188 @@ +# +# Copyright (C) 2009 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 . +# + +import sys, getpass, anydbm +from pickle import loads, dumps +from TLVutils import pack +from utils import inttostring +from SmartcardFilesystem import MF, TransparentStructureEF + +# pgp directory +#self.mf.append(DF(parent=self.mf, + #fid=4, dfname='\xd2\x76\x00\x01\x24\x01', bertlv_data=[])) +# pkcs-15 directories +#self.mf.append(DF(parent=self.mf, + #fid=1, dfname='\xa0\x00\x00\x00\x01')) +#self.mf.append(DF(parent=self.mf, + #fid=2, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00')) +#self.mf.append(DF(parent=self.mf, + #fid=3, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00')) + +def generate_iso_card(): + from SmartcardSAM import SAM + + print "Using default SAM. Insecure!!!" + sam = SAM("1234", "1234567890") #FIXME: Use user provided data + + mf = MF(filedescriptor=FDB["DF"], lifecycle=LCB["ACTIVATED"], dfname=None) + self.SAM.set_MF(self.mf) + + return mf, sam + +def generate_ePass(): + from PIL import Image + from SmartcardFilesystem import DF + from SmartcardSAM import PassportSAM + + MRZ1 = "P 2: + raise ValueError, 'cipherspec must be of the form "cipher-mode" or "cipher"' + + cipher = cipherparts[0].upper() + #cipher = globals().get(cipherparts[0].upper(), None) + #Note: return cipher.key_size does not work on Ubuntu, because e.g. AES.key_size == 0 + if cipher == "AES": #Pycrypto uses AES128 + return 16 + elif cipher == "DES": + return 8 + elif cipher == "DES3": + return 16 + else: + raise ValueError, "Unsupported Encryption Algorithm: " + cipher + +def get_cipher_blocklen(cipherspec): + cipherparts = cipherspec.split("-") + + if len(cipherparts) > 2: + raise ValueError, 'cipherspec must be of the form "cipher-mode" or "cipher"' + + cipher = globals().get(cipherparts[0].upper(), None) + return cipher.block_size + +def append_padding(cipherspec, data, padding_class=0x01): """Append padding to the data. Length of padding depends on length of data and the block size of the specified encryption algorithm. Different types of padding may be selected via the padding_class parameter """ - key = "DUMMYKEY" * (keylength / 8) #Key doesn't matter for padding - cipher = get_cipher(cipherspec,key,iv) - if padding_class == 0x01: #ISO padding - last_block_length = len(data) % cipher.block_size - padding_length = cipher.block_size - last_block_length - if padding_length == 0: - padding = PADDING - else: - padding = PADDING[:padding_length] + blocklen = get_cipher_blocklen(cipherspec) + + if padding_class == 0x01: #ISO padding + last_block_length = len(data) % blocklen + padding_length = blocklen - last_block_length + if padding_length == 0: + padding = '\x80' + '\x00' * (blocklen - 1) + else: + padding = '\x80' + '\x00' * (blocklen - last_block_length - 1) - del cipher return data + padding -def strip_padding(cipherspec,data,padding_class=0x01,keylength = 16): +def strip_padding(cipherspec,data,padding_class=0x01): """ Strip the padding of decrypted data. Returns data without padding """ - key = "DUMMYKEY" * (keylength / 8) #Key doesn't matter for padding - cipher = get_cipher(cipherspec,key,iv) + + #TODO: Sanity check if padding_class == 0x01: tail = len(data) - 1 while data[tail] != '\x80': tail = tail - 1 return data[:tail] -def crypto_checksum(algo,key,data,iv=None,ssc=None): +def crypto_checksum(algo, key, data, iv=None, ssc=None): if algo not in ("HMAC","MAC","CC"): raise ValueError, "Unknown Algorithm %s" % algo @@ -119,7 +145,7 @@ def cipher(do_encrypt, cipherspec, key, data, iv = None): operation = do_encrypt ? encrypt : decrypt, cipherspec must be of the form "cipher-mode", or "cipher\"""" - cipher = get_cipher(cipherspec,key,iv) + cipher = get_cipher(cipherspec, key, iv) result = None if do_encrypt: @@ -148,37 +174,92 @@ def operation_on_string(string1, string2, op): result.append( chr(op(ord(string1[i]),ord(string2[i]))) ) return "".join(result) +def protect_string(string, password, cipherspec=None): + """ + Encrypt and authenticate a string + """ + + #Derive a key and a salt from password + pbk = crypt(password) + regex = re.compile('\$p5k2\$\$[\w./]*\$') + match = regex.match(pbk) + if match != None: + salt = pbk[7:match.end()-1] + key = pbk[match.end():] + else: + raise ValueError + + #Encrypt the string, authenticate and format it + if cipherspec == None: + cipherspec = "AES-CBC" + else: + pass #TODO: Sanity check for cipher + paddedString = append_padding(cipherspec, string) + cryptedString = cipher(True, cipherspec, key, paddedString) + hmac = crypto_checksum("HMAC", key, cryptedString) + protectedString = "$p5k2$$" + salt + "$" + cryptedString + hmac + + return protectedString + +def read_protected_string(string, password, cipherspec=None): + """ + Decrypt a protected string and verify the authentication data + """ + hmac_length = 32 #FIXME: Ugly + + #Check if the string has the structure, that is generated by protect_string + + regex = re.compile('\$p5k2\$\$[\w./]*\$') #TODO: Ensure the right format! + match = regex.match(string) + if match != None: + crypted = string[match.end():len(string) - hmac_length] + salt = string[7:match.end() - 1] + hmac = string[len(string) - hmac_length:] + else: + raise ValueError, "Wrong string format" + + #Derive key + pbk = crypt(password, salt) + match = regex.match(pbk) + key = pbk[match.end():] + + #Verify HMAC + checksum = crypto_checksum("HMAC", key, crypted) + if checksum != hmac: + print "Found HMAC %s expected %s" % (str(hmac), str(checksum)) + raise ValueError, "Failed to authenticate data. Wrong password?" + + #Decrypt data + if cipherspec == None: + cipherspec = "AES-CBC" + decrypted = cipher(False, cipherspec, key, crypted) + + return strip_padding(cipherspec, decrypted) ## ******************************************************************* ## * Cyberflex specific methods * ## ******************************************************************* -def verify_card_cryptogram(session_key, host_challenge, - card_challenge, card_cryptogram): +def verify_card_cryptogram(session_key, host_challenge, card_challenge, card_cryptogram): message = host_challenge + card_challenge - expected = calculate_MAC(session_key, message, iv) - - print >>sys.stderr, "Original: %s" % binascii.b2a_hex(card_cryptogram) - print >>sys.stderr, "Expected: %s" % binascii.b2a_hex(expected) - + expected = calculate_MAC(session_key, message, CYBERFLEX_IV) + return card_cryptogram == expected -def calculate_host_cryptogram(session_key, card_challenge, - host_challenge): +def calculate_host_cryptogram(session_key, card_challenge, host_challenge): message = card_challenge + host_challenge - return calculate_MAC(session_key, message, iv) + return calculate_MAC(session_key, message, CYBERFLEX_IV) -def calculate_MAC(session_key, message, iv): - print >>sys.stderr, "Doing MAC for: %s" % utils.hexdump(message, indent = 17) +def calculate_MAC(session_key, message, iv=CYBERFLEX_IV): + """" + Cyberflex MAC is the last Block of the input encrypted with DES3-CBC + """ cipher = DES3.new(session_key, DES3.MODE_CBC, iv) - block_count = len(message) / cipher.block_size - for i in range(block_count): - cipher.encrypt(message[i*cipher.block_size:(i+1)*cipher.block_size]) - - last_block_length = len(message) % cipher.block_size - last_block = (message[len(message)-last_block_length:]+PADDING)[:cipher.block_size] - - return cipher.encrypt( last_block ) + padded = append_padding("DES3", message, 0x01) + block_count = len(padded) / cipher.block_size + crypted = cipher.encrypt(padded) + + return crypted[len(padded) - cipher.block_size : ] def get_derivation_data(host_challenge, card_challenge): return card_challenge[4:8] + host_challenge[:4] + \ @@ -546,11 +627,22 @@ if __name__ == "__main__": print "Host-Crypto: ", utils.hexdump( host_crypto ) external_authenticate = binascii.a2b_hex("".join("84 82 01 00 10".split())) + host_crypto - print utils.hexdump(calculate_MAC(session_key, external_authenticate, iv)) + print utils.hexdump(calculate_MAC(session_key, external_authenticate)) too_short = binascii.a2b_hex("".join("89 45 19 BF".split())) - padded = append_padding("DES3-ECB",len(too_short),too_short) + padded = append_padding("DES3-ECB", too_short) print "Padded data: " + utils.hexdump(padded) - unpadded = strip_padding("DES3-ECB",padded) + unpadded = strip_padding("DES3-ECB", padded) print "Without padding: " + utils.hexdump(unpadded) - test_pbkdf2() + + teststring = "DEADBEEFistatsyksdvhihewohfwoehcowc8hw8rogfq8whv75tsgohsav8wress" + foo = append_padding("AES", teststring) + print len(foo) + print strip_padding("AES", foo) + testpass = "SomeRandomPassphrase" + protectedString = protect_string(teststring, testpass) + unprotectedString = read_protected_string(protectedString, testpass) + if teststring != unprotectedString: + print "protect_string test failed" + + #test_pbkdf2() diff --git a/virtualsmartcard/vpicc/SmartcardSAM.py b/virtualsmartcard/vpicc/SmartcardSAM.py index 3ced5bd..b3c51cd 100755 --- a/virtualsmartcard/vpicc/SmartcardSAM.py +++ b/virtualsmartcard/vpicc/SmartcardSAM.py @@ -26,16 +26,6 @@ from Crypto.Cipher import DES3 from ConstantDefinitions import * from SEutils import ControlReferenceTemplate as CRT -def generate_SAM_config(cardNumber,PIN,cardSecret,path,password): - """ - This method generates a new SAM configuration using the given parameters, - stores it in a KeyContainer, serialises the container, encrypts it and - writes it to the disk. - """ - container = CardContainer(cardNumber,PIN,cardSecret) - container.saveConfiguration(path,password) - del container - def get_referenced_cipher(p1): """ P1 defines the algorithm and mode to use. We dispatch it and return a @@ -43,6 +33,7 @@ def get_referenced_cipher(p1): """ ciphertable = { + 0x00: "DES3-ECB", 0x01: "DES3-ECB", 0x02: "DES3-CBC", 0x03: "DES-ECB", @@ -67,17 +58,26 @@ class CardContainer: to the corresponding container. """ - def __init__(self,cardNumber=None,PIN=None,cardSecret=None): + def __init__(self, PIN=None, cardNumber=None, cardSecret=None): + from os import urandom + self.cardNumber = cardNumber self.PIN = PIN - self.cardSecret = cardSecret self.FSkeys = {} self.cipher = 0x01 # Algorithm reference defined in __get_referenced_algorithm(p1) - self.block_size = 16 #Need to be set according to the needs of self.cipher. Ugly! self.master_password = None self.master_key = None self.salt = None self.asym_key = None + + keylen = CryptoUtils.get_cipher_keylen(get_referenced_cipher(self.cipher)) + if cardSecret is None: #Generate a random card secret + self.cardSecret = urandom(keylen) + else: + if len(cardSecret) != keylen: + raise ValueError, "cardSecret has the wrong key length for: " + get_referenced_cipher(self.cipher) + else: + self.cardSecret = cardSecret def __delete__(self): print "Smartcard configuration is NOT saved!!!" @@ -119,135 +119,21 @@ class CardContainer: return plain_key else: return None - - def loadConfiguration(self,path,password): - """ - Reads the configuration of a key container from the disk, decrypts - it and applies it to the current key_ container. - The current configuration gets overwritten. The HMAC is used to verify - data integrity. - """ - decryptedContainer = self.loadFromDisk(path, password, True) - container = loads(decryptedContainer) - self.PIN = container.PIN - self.cardNumber = container.cardNumber - self.cardSecret = container.cardSecret - self.FSkeys = container.FSkeys - self.master_password = password - del decryptedContainer - del container - - def saveConfiguration(self,path,password=None): - """ - Encrypts the configuration of the current key container and writes it - to the disk. Password is used to derivate a key using PBKDF2. A salt and - a HMAC is stored along with the data - """ - serialisedContainer = dumps(self) - if self.master_key == None: #New Configuration - if password == None: - raise ValueError, "Need password but none specified" - pbk = CryptoUtils.crypt(password) - regex = re.compile('\$p5k2\$\$[\w./]*\$') - match = regex.match(pbk) - if match != None: - self.salt = pbk[7:match.end()-1] - self.master_key = pbk[match.end():] - else: - raise ValueError - del pbk - - self.saveToDisk(serialisedContainer,path) - - def saveToDisk(self,data,path,password=None): - if password == None: - key = self.master_key - salt = self.salt - else: - regex = re.compile('\$p5k2\$\$[\w./]*\$') - pbk = CryptoUtils.crypt(password) - match = regex.match(pbk) - salt = pbk[7:match.end()-1] - print salt - key = pbk[match.end():] - - cipher = get_referenced_cipher(self.cipher) - paddedData = CryptoUtils.append_padding(cipher,data) - cryptedData = CryptoUtils.cipher(True,cipher,key,paddedData) - hmac = CryptoUtils.crypto_checksum("HMAC",key,cryptedData) - data = "$p5k2$$" + salt + "$" + cryptedData + hmac - - try: - fd = open(path,"w") - fd.write(data) - fd.close() - except IOError: - print "Failed to write data to disk" - - - def loadFromDisk(self,path,password=None,loading_sam=False): - hmac_length = 32 #FIXME: Ugly - #Read data - try: - fd = open(path,"r") - data = fd.read() - fd.close() - except IOError: - print "Failed to read data from disk" - #Check file structure, ectract components - regex = re.compile('\$p5k2\$\$[\w./]*\$') #TODO: Ensure the right format! - match = regex.match(data) - if match != None: - crypted = data[match.end():len(data) - hmac_length] - salt = data[7:match.end() - 1] - hmac = data[len(data) - hmac_length:] - else: - raise ValueError, "Wrong file format" - if password == None: - if salt != self.salt: - raise ValueError, "Could not decode data" - else: - key = self.master_key - else: - pbk = CryptoUtils.crypt(password,salt) - match = regex.match(pbk) - key = pbk[match.end():] - #Verify HMAC - checksum = CryptoUtils.crypto_checksum("HMAC",key,crypted) - if checksum != hmac: - print "Found HMAC %s expected %s" % (str(hmac), str(checksum)) - raise ValueError, "Failed to authenticate data. Wrong password?" - #Decrypt data - cipher = get_referenced_cipher(self.cipher) - decrypted = CryptoUtils.cipher(False,cipher,key,crypted) - - #If we are loading the SAM config (paramter loading_sam) we set - #the master key and the salt - #This is ugly and should be changed - if loading_sam: - self.master_key = key - self.salt = salt - - return decrypted - class SAM(object): - def __init__(self,path,password,mf=None): - """ - Reads the encrypted SAM configuration from the disk and applies it. - """ - self.CardContainer = CardContainer() - if len(password) % self.CardContainer.block_size != 0: - print "Wrong key length, must be a multiple of %s Bytes" % self.CardContainer.block_size - raise ValueError - - self.CardContainer.loadConfiguration(path, password) + def __init__(self, PIN, cardNumber, mf=None): + + self.CardContainer = CardContainer(PIN, cardNumber) + #self.CardContainer.loadConfiguration(path, password) self.mf = mf - self.card_number = self.CardContainer.cardNumber + + self.cardNumber = cardNumber + self.cardSecret = self.CardContainer.cardSecret + self.last_challenge = None #Will contain non-readable binary string self.counter = 3 #Number of tries for PIN validation - self.cardSecret = self.CardContainer.cardSecret + self.SM_handler = Secure_Messaging(self.mf) def set_MF(self,mf): @@ -322,6 +208,7 @@ class SAM(object): return SW["NORMAL"], "" else: self.counter -= 1 + print self.CardContainer.getPIN() + " != " + PIN raise SwError(SW["WARN_NOINFO63"]) #raise SwError(0X63C0 + self.counter) #Be verbose else: @@ -343,16 +230,16 @@ class SAM(object): to prove key posession """ - if p1 == 0x00: #No information given + if p1 == 0x00: #No information given cipher = get_referenced_cipher(self.CardContainer.cipher) else: cipher = get_referenced_cipher(p1) if cipher == "RSA" or cipher == "DSA": - crypted_challenge = self.CardContainer.asym_key.sign(data,"") - crypted_challenge = crypted_challenge[0] - crypted_challenge = inttostring(crypted_challenge) - else: + crypted_challenge = self.CardContainer.asym_key.sign(data,"") + crypted_challenge = crypted_challenge[0] + crypted_challenge = inttostring(crypted_challenge) + else: key = self._get_referenced_key(p1,p2) crypted_challenge = CryptoUtils.cipher(True,cipher,key,data) @@ -442,7 +329,7 @@ class SAM(object): return SW["NORMAL"], self.last_challenge def get_card_number(self): - return SW["NORMAL"], inttostring(self.card_number) + return SW["NORMAL"], inttostring(self.cardNumber) def _get_referenced_key(self,p1,p2): """ @@ -464,8 +351,7 @@ class SAM(object): key = None qualifier = p2 & 0x1F algo = get_referenced_cipher(p1) - cipher = CryptoUtils.get_cipher(p1) - keylength = cipher.key_size + keylength = CryptoUtils.get_cipher_keylen(algo) if (p2 == 0x00): #No information given, use the global card key key = self.CardContainer.cardSecret @@ -507,19 +393,6 @@ class SAM(object): sw, result = file.readbinary(0) return sw, result - #The following commands define the interface to the CardContainer functions - def saveConfiguration(self,path,password=None): - return self.CardContainer.saveConfiguration(path, password) - - def loadConfiguration(self,path,password): - return self.CardContainer.loadConfiguration(path, password) - - def loadFromDisk(self,path,password=None): - return self.CardContainer.loadFromDisk(path, password) - - def saveToDisk(self,data,path,password=None): - return self.CardContainer.saveToDisk(data, path, password) - #The following commands define the interface to the Secure Messaging functions def generate_public_key_pair(self,p1,p2,data): return self.SM_handler.generate_public_key_pair(p1, p2, data) @@ -537,7 +410,7 @@ class SAM(object): return self.SM_handler.manage_security_environment(p1, p2, data) class PassportSAM(SAM): - def __init__(self,mf,path="testconfig.sam",key="DUMMYKEYDUMMYKEY"): + def __init__(self, mf): df = mf.currentDF() ef_dg1 = df.select("fid", 0x0101) dg1 = ef_dg1.readbinary(5) @@ -549,8 +422,9 @@ class PassportSAM(SAM): self.KSenc = None self.KSmac = None self.__computeKeys() - SAM.__init__(self,path,key,None) - self.SM_handler = ePass_SM(mf,None,None,None) + #SAM.__init__(self, path, key,None) + SAM.__init__(self, None, None, mf) + self.SM_handler = ePass_SM(mf, None, None, None) self.SM_handler.CAPDU_SE.cct.algo = "CC" self.SM_handler.RAPDU_SE.cct.algo = "CC" self.SM_handler.CAPDU_SE.ct.algo = "DES3-CBC" @@ -634,8 +508,8 @@ class PassportSAM(SAM): return c class CryptoflexSAM(SAM): - def __init__(self,path="testconfig.sam",key="DUMMYKEYDUMMYKEY",mf=None): - SAM.__init__(self,path,key,mf) + def __init__(self, mf=None): + SAM.__init__(self, None, None, mf) self.SM_handler = CryptoflexSM(mf) def generate_public_key_pair(self,p1,p2,data): @@ -847,7 +721,7 @@ class Secure_Messaging(object): if header_authentication: to_authenticate = inttostring(CAPDU.cla) + inttostring(CAPDU.ins) + inttostring(CAPDU.p1) + inttostring(CAPDU.p2) - to_authenticate = CryptoUtils.append_padding("DES-CBC",to_authenticate,0x01,8) + to_authenticate = CryptoUtils.append_padding("DES-CBC", to_authenticate) else: to_authenticate = "" @@ -915,7 +789,7 @@ class Secure_Messaging(object): #SM data objects for authentication if tag == SM_Class["CHECKSUM"]: - auth = CryptoUtils.append_padding("DES-CBC",to_authenticate,0x01,8) + auth = CryptoUtils.append_padding("DES-CBC", to_authenticate) sw, checksum = self.compute_cryptographic_checksum(0x8E, 0x80, auth) if checksum != value: print "Failed to verify checksum!" @@ -1002,7 +876,7 @@ class Secure_Messaging(object): raise SwError(SW["CONDITIONSNOTSATISFIED"]) elif self.CAPDU_SE.cct.algo == "CCT": tag = SM_Class["CHECKSUM"] - to_auth = CryptoUtils.append_padding("DES-ECB",return_data,0x01,8) + to_auth = CryptoUtils.append_padding("DES-ECB", return_data) sw, auth = self.compute_cryptographic_checksum(0x8E, 0x80, to_auth) length = len(auth) return_data += TLVutils.pack([(tag,length,auth)]) @@ -1192,8 +1066,8 @@ class Secure_Messaging(object): if key == None or algo == None: return SW["ERR_CONDITIONNOTSATISFIED"], "" else: - padded = CryptoUtils.append_padding(algo,data) - crypted = CryptoUtils.cipher(True,algo,key,padded,self.current_SE.ct.iv) + padded = CryptoUtils.append_padding(algo, data) + crypted = CryptoUtils.cipher(True, algo, key, padded, self.current_SE.ct.iv) return SW["NORMAL"], crypted def decipher(self,p1,p2,data): @@ -1344,27 +1218,26 @@ if __name__ == "__main__": Unit test: """ - path = "testconfig.sam" password = "DUMMYKEYDUMMYKEY" - generate_SAM_config("1234567890","1234","keykeykeykeykeyk",path,password) + #generate_SAM_config("1234567890", "1234", "keykeykeykeykeyk", path, password) - MyCard = SAM(path,password,None) + MyCard = SAM("1234", "1234567890") try: print MyCard.verify(0x00, 0x00, "5678") except SwError, e: print e.message print "Counter = " + str(MyCard.counter) - print MyCard.verify(0x00, 0x00, "1234") + print MyCard.verify(0x00, 0x00, "1234") print "Counter = " + str(MyCard.counter) - sw, challenge = MyCard.get_challenge(0x00,0x00,"") + sw, challenge = MyCard.get_challenge(0x00, 0x00, "") print "Before encryption: " + challenge - padded = CryptoUtils.append_padding("DES3-ECB",challenge) - sw, result = MyCard.internal_authenticate(0x00,0x00,padded) + padded = CryptoUtils.append_padding("DES3-ECB", challenge) + sw, result = MyCard.internal_authenticate(0x00, 0x00, padded) print "Internal Authenticate status code: %x" % sw try: - sw, res = MyCard.external_authenticate(0x00,0x00,result) + sw, res = MyCard.external_authenticate(0x00, 0x00, result) except SwError, e: print e.message sw = e.sw diff --git a/virtualsmartcard/vpicc/VirtualSmartcard.py b/virtualsmartcard/vpicc/VirtualSmartcard.py index 86cdfdd..fe01b4a 100644 --- a/virtualsmartcard/vpicc/VirtualSmartcard.py +++ b/virtualsmartcard/vpicc/VirtualSmartcard.py @@ -25,42 +25,17 @@ from SmartcardSAM import CardContainer, SAM, PassportSAM, CryptoflexSAM from pickle import dumps, loads import socket, struct, sys, signal, atexit, traceback import struct - +import getpass +import shelve, anydbm class SmartcardOS(object): # {{{ - def __init__(self, filename,mf=None,ins2handler=None, maxle=MAX_SHORT_LE,sam=None): - self.config_key = "DUMMYKEYDUMMYKEY" #TODO: Let user enter password - self.filename = filename + def __init__(self, mf=None, sam=None, ins2handler=None, maxle=MAX_SHORT_LE): + from CardGenerator import generate_iso_card self.mf = mf self.SAM = sam - - if self.filename != None: - try: - self.load(filename,self.config_key) - except ValueError: - print "Failed to load configuration %s" % filename - if self.SAM == None: - print "Using default SAM. Insecure!!!" - self.SAM = SAM("testconfig.sam",self.config_key) #FIXME: Replace by defaul_SAM() - if self.mf == None: - print "Using default MF." - self.mf = MF(filedescriptor=FDB["DF"], lifecycle=LCB["ACTIVATED"], dfname=None) - self.SAM.set_MF(self.mf) - - # pgp directory - #self.mf.append(DF(parent=self.mf, - #fid=4, dfname='\xd2\x76\x00\x01\x24\x01', bertlv_data=[])) - # pkcs-15 directories - #self.mf.append(DF(parent=self.mf, - #fid=1, dfname='\xa0\x00\x00\x00\x01')) - #self.mf.append(DF(parent=self.mf, - #fid=2, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00')) - #self.mf.append(DF(parent=self.mf, - #fid=3, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00')) - - #import imp, os.path - #SAM_config = os.path.join(os.path.dirname(imp.find_module("SmartcardSAM")[1]), "testconfig.sam") #Path to initial SAM configuration, stored on disk + if self.mf == None and self.SAM == None: + self.mf, self.SAM = generate_iso_card() if not ins2handler: self.ins2handler = { @@ -108,42 +83,7 @@ class SmartcardOS(object): # {{{ self.atr = SmartcardOS.makeATR(T=1, directConvention = True, TA1=0x13, histChars = chr(0x80) + chr(0x70 + len(card_capabilities)) + card_capabilities) - - def save(self): - """ - Save the configuration of the current Smartcard (MF + SAM) to disk. - To files will be stored: .mf for the mf and .sam - for the SAM. - Both files will be encrypted. - """ - if self.filename == None: - raise ValueError, "No filename specified" - else: - mf = dumps(self.mf) - path = self.filename + ".mf" - mf = self.SAM.saveToDisk(mf,path) - path = self.filename + ".sam" - self.SAM.saveConfiguration(path) - #self.SAM.saveConfiguration(path,"DUMMYKEYDUMMYKEY") - - def load(self,filename,password): - """ - Try to load a configuration from the filesystem. MF or SAM are only loaded if - they aren't yet specified. - """ - if self.SAM == None: - self.SAM = SAM("testconfig.sam","DUMMYKEYDUMMYKEY",None) #FIXME: replace by default_SAM() - path = filename + ".sam" - try: - self.SAM.loadConfiguration(path,password) - except IOError: - print "Failed to open %s" % path - if self.mf == None: - path = filename + ".mf" - data = self.SAM.loadFromDisk(path,password) - self.mf = loads(data) - print "Succesfully loaded MF from %s" % path - + def powerUp(self): pass @@ -363,72 +303,32 @@ class PassportOS(SmartcardOS): Basic Access Control (BAC) mechanisms. """ - def __init__(self, filename,mf=None, ins2handler=None, maxle=MAX_SHORT_LE): - if filename == None and mf == None: - mf = MF() - else: - pass #TODO: Load data from disk - self.generate_data_structure(mf) - self.SAM = PassportSAM(mf) - SmartcardOS.__init__(self, None, mf=mf, ins2handler=ins2handler, maxle=maxle,sam=self.SAM) - - def generate_data_structure(self,mf): - MRZ1 = "P