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
This commit is contained in:
oepen
2010-01-30 18:51:00 +00:00
parent ae067f8d07
commit e500aa3382
6 changed files with 448 additions and 376 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
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<UTOERIKSSON<<ANNA<MARIX<<<<<<<<<<<<<<<<<<<"
MRZ2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14"
MRZ = MRZ1 + MRZ2
picturepath = "jp2.jpg"
try:
im = Image.open(picturepath)
pic_width, pic_height = im.size
fd = open(picturepath,"rb")
picture = fd.read()
fd.close()
except IOError:
print "Could not find picture %s" % picturepath
pic_width = 0
pic_height = 0
picture = None
mf = MF()
#We need a MF with Application DF \xa0\x00\x00\x02G\x10\x01
mf.append(DF(parent=mf, fid=4, dfname='\xa0\x00\x00\x02G\x10\x01', bertlv_data=[]))
df = mf.currentDF()
mf.append(TransparentStructureEF(parent=df, fid=0x011E, filedescriptor=0, data=""))#EF.COM
mf.append(TransparentStructureEF(parent=df, fid=0x0101, filedescriptor=0, data=""))#EF.DG1
mf.append(TransparentStructureEF(parent=df, fid=0x0102, filedescriptor=0, data=""))#EF.DG2
mf.append(TransparentStructureEF(parent=df, fid=0x010D, filedescriptor=0, data=""))#EF.SOD
#EF.COM
COM = pack([(0x5F01,4,"0107"),(0x5F36,6,"040000"),(0x5C,2,"6175")])
COM = pack(((0x60,len(COM),COM),))
fid = df.select("fid",0x011E)
fid.writebinary([0],[COM])
#EF.DG1
DG1 = pack([(0x5F1F,len(MRZ),MRZ)])
DG1 = pack([(0x61,len(DG1),DG1)])
fid = df.select("fid",0x0101)
fid.writebinary([0],[DG1])
#EF.DG2
if picture != None:
IIB = "\x00\x01" + inttostring(pic_width,2) + inttostring(pic_height,2) + 6 * "\x00"
length = 32 + len(picture) #32 is the length of IIB + FIB
FIB = inttostring(length,4) + 16 * "\x00"
FRH = "FAC" + "\x00" + "010" + "\x00" + inttostring(14+length,4) + inttostring(1,2)
picture = FRH + FIB + IIB + picture
DG2 = pack([(0xA1,8,"\x87\x02\x01\x01\x88\x02\x05\x01"),(0x5F2E,len(picture),picture)])
DG2 = pack([(0x02,1,"\x01"),(0x7F60,len(DG2),DG2)])
DG2 = pack([(0x7F61,len(DG2),DG2)])
fid = df.select("fid",0x0102)
fid.writebinary([0],[DG2])
sam = PassportSAM(mf)
return mf, sam
def generate_cryptoflex():
from SmartcardFilesystem import CryptoflexMF
from SmartcardSAM import CryptoflexSAM
mf = CryptoflexMF()
mf.append(TransparentStructureEF(parent=mf, fid=0x0002, filedescriptor=0x01,
data="\x00\x00\x00\x01\x00\x01\x00\x00")) #EF.ICCSN
sam = CryptoflexSAM(mf)
return mf, sam
def loadCard(filename, password=None):
from CryptoUtils import read_protected_string
try:
db = anydbm.open(filename, 'r')
except anydbm.error:
print "Failed to open " + filename
if password is None:
password = getpass.getpass("Please enter your password.")
serializedMF = read_protected_string(db["mf"], password)
serializedSAM = read_protected_string(db["sam"], password)
SAM = loads(serializedSAM)
MF = loads(serializedMF)
#self.type = db["type"]
return SAM, MF
def saveCard(mf, sam, filename, password=None):
from CryptoUtils import protect_string
if filename != None:
print "Saving smartcard configuration to %s" % filename
else: #TODO: Ask user for filename
pass
if password is None:
passwd1 = getpass.getpass("Please enter a password.")
passwd2 = getpass.getpass("Please retype your password.")
if (passwd1 != passwd2):
raise ValueError, "Passwords did not match. Will now exit"
else:
password = passwd1
mf_string = dumps(mf)
sam_string = dumps(sam)
protectedMF = protect_string(mf_string, password)
protectedSAM = protect_string(sam_string, password)
try:
db = anydbm.open(filename, 'c')
db["mf"] = protectedMF
db["sam"] = protectedSAM
#db["type"] = self.type
db.close()
except anydbm.error:
print "Failed to write data to disk"
if __name__ == "__main__":
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-t", "--type", action="store", type="choice",
default='iso7816',
choices=['iso7816', 'cryptoflex', 'ePass'],
help="Type of Smartcard [default: %default]")
parser.add_option("-f", "--file", action="store", type="string",
dest="filename", default=None,
help="Where to save the generated card")
(options, args) = parser.parse_args()
if options.filename == None:
print "You have to provide a filename using the -f option"
sys.exit()
if options.type == 'iso7816':
mf, sam = generate_iso_card()
elif options.type == 'ePass':
mf, sam = generate_ePass()
elif options.type == 'cryptoflex':
mf, sam = generate_cryptoflex()
saveCard(mf, sam, options.filename)

View File

@@ -23,6 +23,7 @@ from binascii import b2a_hex
from random import randint
from utils import inttostring, stringtoint, hexdump
import string
import re
try:
# Use PyCrypto (if available)
@@ -33,8 +34,7 @@ except ImportError:
import hmac as HMAC
import sha as SHA1
iv = '\x00' * 8
PADDING = '\x80' + '\x00' * 7
CYBERFLEX_IV = '\x00' * 8
## *******************************************************************
## * Generic methods *
@@ -63,31 +63,57 @@ def get_cipher(cipherspec, key, iv = None):
return cipher
def append_padding(cipherspec, data, padding_class=0x01,keylength = 16):
def get_cipher_keylen(cipherspec):
cipherparts = cipherspec.split("-")
if len(cipherparts) > 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':
@@ -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])
padded = append_padding("DES3", message, 0x01)
block_count = len(padded) / cipher.block_size
crypted = cipher.encrypt(padded)
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 )
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)
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()

View File

@@ -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,18 +58,27 @@ 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!!!"
@@ -120,134 +120,20 @@ class CardContainer:
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
def __init__(self, PIN, cardNumber, mf=None):
self.CardContainer.loadConfiguration(path, password)
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:
@@ -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,7 +422,8 @@ class PassportSAM(SAM):
self.KSenc = None
self.KSmac = None
self.__computeKeys()
SAM.__init__(self,path,key,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"
@@ -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)])
@@ -1344,11 +1218,10 @@ 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:

View File

@@ -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 = {
@@ -109,41 +84,6 @@ class SmartcardOS(object): # {{{
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: <filename>.mf for the mf and <filename>.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 __init__(self, mf=None, sam=None, ins2handler=None, maxle=MAX_SHORT_LE):
from CardGenerator import generate_ePass
def generate_data_structure(self,mf):
MRZ1 = "P<UTOERIKSSON<<ANNA<MARIX<<<<<<<<<<<<<<<<<<<"
MRZ2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14"
MRZ = MRZ1+MRZ2
from PIL import Image
picturepath = "jp2.jpg"
try:
im = Image.open(picturepath)
pic_width, pic_height = im.size
fd = open(picturepath,"rb")
picture = fd.read()
fd.close()
except IOError:
print "Could not find picture %s" % picturepath
pic_width = 0
pic_height = 0
picture = None
#We need a MF with Application DF \xa0\x00\x00\x02G\x10\x01
mf.append(DF(parent=mf,fid=4, dfname='\xa0\x00\x00\x02G\x10\x01', bertlv_data=[]))
df = mf.currentDF()
mf.append(TransparentStructureEF(parent=df, fid=0x011E, filedescriptor=0, data=""))#EF.COM
mf.append(TransparentStructureEF(parent=df, fid=0x0101, filedescriptor=0, data=""))#EF.DG1
mf.append(TransparentStructureEF(parent=df, fid=0x0102, filedescriptor=0, data=""))#EF.DG2
mf.append(TransparentStructureEF(parent=df, fid=0x010D, filedescriptor=0, data=""))#EF.SOD
#EF.COM
COM = pack([(0x5F01,4,"0107"),(0x5F36,6,"040000"),(0x5C,2,"6175")])
COM = pack(((0x60,len(COM),COM),))
fid = df.select("fid",0x011E)
fid.writebinary([0],[COM])
#EF.DG1
DG1 = pack([(0x5F1F,len(MRZ),MRZ)])
DG1 = pack([(0x61,len(DG1),DG1)])
fid = df.select("fid",0x0101)
fid.writebinary([0],[DG1])
#EF.DG2
if picture != None:
IIB = "\x00\x01" + inttostring(pic_width,2) + inttostring(pic_height,2) + 6 * "\x00"
length = 32 + len(picture) #32 is the length of IIB + FIB
FIB = inttostring(length,4) + 16 * "\x00"
FRH = "FAC" + "\x00" + "010" + "\x00" + inttostring(14+length,4) + inttostring(1,2)
picture = FRH + FIB + IIB + picture
DG2 = pack([(0xA1,8,"\x87\x02\x01\x01\x88\x02\x05\x01"),(0x5F2E,len(picture),picture)])
DG2 = pack([(0x02,1,"\x01"),(0x7F60,len(DG2),DG2)])
DG2 = pack([(0x7F61,len(DG2),DG2)])
fid = df.select("fid",0x0102)
fid.writebinary([0],[DG2])
if mf == None and sam == None:
mf, sam = generate_ePass()
else: #TODO: Sanity check if mf or sam are provided
if mf == None:
mf, tmp = generate_ePass()
if sam == None:
sam = PassportSAM(mf)
SmartcardOS.__init__(self, mf=mf, sam=sam, ins2handler=ins2handler, maxle=maxle)
class CryptoflexOS(SmartcardOS): # {{{
def __init__(self, filename, mf=None, ins2handler=None, maxle=MAX_SHORT_LE):
if filename == None and mf == None:
mf = CryptoflexMF()
mf.append(TransparentStructureEF(parent=mf, fid=0x0002, filedescriptor=0x01,
data="\x00\x00\x00\x01\x00\x01\x00\x00"))#EF.ICCSN
#TODO: Load objects from disk
SmartcardOS.__init__(self, None,mf=mf, ins2handler=ins2handler, maxle=maxle,
sam=CryptoflexSAM("testconfig.sam","DUMMYKEYDUMMYKEY"))
def __init__(self, mf=None, sam=None, ins2handler=None, maxle=MAX_SHORT_LE):
from GenerateCard import generate_cryptoflex
if mf == None and sam == None:
mf, sam = generate_cryptoflex()
else:
if mf == None:
mf, tmp = generate_cryptoflex()
del tmp
if sam == None:
sam = CryptoflexSAM(mf)
SmartcardOS.__init__(self, mf, sam, ins2handler, maxle)
self.atr = '\x3B\xE2\x00\x00\x40\x20\x49\x06'
def execute(self, msg):
@@ -485,28 +385,55 @@ VPCD_CTRL_ATR = 4
class VirtualICC(object): # {{{
def __init__(self, filename,os=None, lenlen=3, host="localhost", port=35963):
if os == None:
self.os = SmartcardOS(filename)
def __init__(self, filename, type, lenlen=3, host="localhost", port=35963):
from os.path import exists
from CardGenerator import loadCard
self.filename = None
SAM = None
MF = None
#If a filename is specified, try to load the card from disk
if filename == None:
print "No filename specified. The card will not be safed!"
else:
self.os = os
self.filename = filename
if exists(filename):
print "Found existing card " + filename + ". Will try to open it"
SAM, MF = loadCard(filename)
else:
print "No file " + filename + " found. Will create new file at termination of program."
#Generate an OS object of the correct type
if type == "iso7816":
self.os = SmartcardOS(MF, SAM)
elif type == "ePass":
self.os = PassportOS(MF, SAM)
elif type == "cryptoflex":
self.os = CryptoflexOS(MF, SAM)
else:
print "Unknown cardtype " + type + ". Will use standard ISO 7816 cardtype"
type = "iso7816"
self.os = SmartcardOS(MF, SAM)
self.type = type
#Connect to the VPCD
try:
self.sock = self.connectToPort(host, port)
self.sock.settimeout(None)
except socket.error as e:
print "Failed to open socket: " + str(e) + ". Is pcscd running? Is vpcd installed?"
sys.exit()
self.lenlen = lenlen
signal.signal(signal.SIGINT, self.signalHandler)
atexit.register(self.signalHandler)
atexit.register(self.saveCard)
def saveCard(self):
if self.filename != None:
print "Saving smartcard configuration to %s" % self.filename
self.os.save()
else:
print "No filename specified, card configuration will not be saved."
#atexit.register(self.signalHandler)
#atexit.register(saveCard)
def signalHandler(self, signal=None, frame=None):
from CardGenerator import saveCard
self.sock.close()
saveCard(self.os.mf, self.os.SAM, self.filename)
sys.exit()
@staticmethod
@@ -567,11 +494,6 @@ if __name__ == "__main__":
dest="filename", default=None,
help="Name of a smartcard stored in the filesystem. The card will be loaded")
(options, args) = parser.parse_args()
if options.type == 'cryptoflex':
vicc = VirtualICC(options.filename,os=CryptoflexOS(options.filename))
elif options.type == 'ePass':
#raise ValueError, "Not implemented yet!"
vicc = VirtualICC(options.filename,os=PassportOS(options.filename))
else:
vicc = VirtualICC(options.filename)
vicc = VirtualICC(options.filename, options.type)
vicc.run()

View File

@@ -1 +0,0 @@
$p5k2$$RGHiUkhA$<24>U<EFBFBD>x<01>U<EFBFBD><55><EFBFBD><EFBFBD><EFBFBD>3I^낎P(<28>f<EFBFBD>Kc<4B><63>iveY/0<><30><EFBFBD><EFBFBD>l<EFBFBD><6C>h<EFBFBD><05>{?<0E>gy<67><79><EFBFBD><EFBFBD>a<EFBFBD><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>P<><50><EFBFBD><EFBFBD><12>?Z<><1F>U!<21><>o<EFBFBD><6F>KΦP<CEA6>,<2C><><EFBFBD><EFBFBD><63>o<EFBFBD>{0F<30>\<5C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>g#<23><>~<7E><><72>l<EFBFBD>/}<7D>l<EFBFBD><6C>3<19><><EFBFBD>#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>%<06>ѱ@<40>OG

View File

@@ -1,2 +0,0 @@
$p5k2$$kj3EFei1$F<>M?<3F><><EFBFBD>?06ՋD<D58B>
# 6$<24>2vz<76><7A>t<EFBFBD>(<28>\rCiKs<4B><73><EFBFBD>:<3A>8<EFBFBD>D<03><>DF<44>poR<6F><52>+<<3C>Y2-<2D>kF<6B><46>$<24><><EFBFBD>C<EFBFBD>C߯<><16>'<27><>#,<2C>