diff --git a/virtualsmartcard/src/vpicc/Makefile.am b/virtualsmartcard/src/vpicc/Makefile.am
index 015ab9a..fbc4545 100644
--- a/virtualsmartcard/src/vpicc/Makefile.am
+++ b/virtualsmartcard/src/vpicc/Makefile.am
@@ -1,22 +1,26 @@
-EXTRA_DIST = virtualsmartcard.in
+EXTRA_DIST = vicc.in
-vpiccdir = $(pythondir)
+vpiccdir = $(pythondir)/virtualsmartcard
-vpicc_PYTHON = CardGenerator.py SEutils.py TLVutils.py ConstantDefinitions.py \
- SmartcardFilesystem.py utils.py CryptoUtils.py SmartcardSAM.py \
- SWutils.py VirtualSmartcard.py
+vpicc_PYTHON = virtualsmartcard/CardGenerator.py \
+ virtualsmartcard/SEutils.py \
+ virtualsmartcard/TLVutils.py \
+ virtualsmartcard/ConstantDefinitions.py \
+ virtualsmartcard/SmartcardFilesystem.py \
+ virtualsmartcard/utils.py \
+ virtualsmartcard/CryptoUtils.py \
+ virtualsmartcard/SmartcardSAM.py \
+ virtualsmartcard/SWutils.py \
+ virtualsmartcard/VirtualSmartcard.py \
+ virtualsmartcard/__init__.py
all-local:
- perl -ne "s|PYTHONDIR|$(pythondir)| ; print" $(srcdir)/virtualsmartcard.in > tmp
- perl -ne "s|PYTHONBIN|$(PYTHON)| ; print" tmp > virtualsmartcard
- rm -f tmp
-
-install-exec-local: all-local
+ perl -ne "s|PYTHONBIN|$(PYTHON)| ; print" vicc.in > vicc
$(mkinstalldirs) $(DESTDIR)$(bindir)
- $(INSTALL_SCRIPT) virtualsmartcard $(DESTDIR)$(bindir)/virtualsmartcard
+ $(INSTALL_SCRIPT) vicc $(DESTDIR)$(bindir)/vicc
uninstall-local:
- rm -f $(DESTDIR)$(bindir)/virtualsmartcard
+ rm -f $(DESTDIR)$(bindir)/vicc
clean-local:
- rm -f virtualsmartcard
+ rm -f vicc
diff --git a/virtualsmartcard/src/vpicc/CardGenerator.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py
similarity index 94%
rename from virtualsmartcard/src/vpicc/CardGenerator.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py
index ae8cdcc..51736c3 100644
--- a/virtualsmartcard/src/vpicc/CardGenerator.py
+++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py
@@ -20,8 +20,8 @@
import sys, getpass, anydbm, readline, os, dircache
from pickle import loads, dumps
from TLVutils import pack
-from utils import inttostring
-from SmartcardFilesystem import *
+from virtualsmartcard.utils import inttostring
+from virtualsmartcard.SmartcardFilesystem import *
# pgp directory
#self.mf.append(DF(parent=self.mf,
@@ -42,7 +42,7 @@ class CardGenerator(object):
self.sam = None
def __generate_iso_card(self):
- from SmartcardSAM import SAM
+ from virtualsmartcard.SmartcardSAM import SAM
print "Using default SAM. Insecure!!!"
self.sam = SAM("1234", "1234567890") #FIXME: Use user provided data
@@ -52,7 +52,7 @@ class CardGenerator(object):
def __generate_ePass(self):
from PIL import Image
- from SmartcardSAM import PassportSAM
+ from virtualsmartcard.SmartcardSAM import PassportSAM
MRZ = raw_input("Please enter the MRZ as one string: ") #TODO: Sanity checks
@@ -114,7 +114,7 @@ class CardGenerator(object):
self.sam = PassportSAM(self.mf)
def __generate_cryptoflex(self):
- from SmartcardSAM import CryptoflexSAM
+ from virtualsmartcard.SmartcardSAM import CryptoflexSAM
self.mf = CryptoflexMF()
self.mf.append(TransparentStructureEF(parent=self.mf, fid=0x0002, filedescriptor=0x01,
@@ -144,7 +144,7 @@ class CardGenerator(object):
def loadCard(self, filename, password=None):
- from CryptoUtils import read_protected_string
+ from virtualsmartcard.CryptoUtils import read_protected_string
try:
db = anydbm.open(filename, 'r')
@@ -161,7 +161,7 @@ class CardGenerator(object):
self.type = db["type"]
def saveCard(self, filename, password=None):
- from CryptoUtils import protect_string
+ from virtualsmartcard.CryptoUtils import protect_string
if password is None:
passwd1 = getpass.getpass("Please enter a password.")
diff --git a/virtualsmartcard/src/vpicc/ConstantDefinitions.py b/virtualsmartcard/src/vpicc/virtualsmartcard/ConstantDefinitions.py
similarity index 100%
rename from virtualsmartcard/src/vpicc/ConstantDefinitions.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/ConstantDefinitions.py
diff --git a/virtualsmartcard/src/vpicc/CryptoUtils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py
similarity index 99%
rename from virtualsmartcard/src/vpicc/CryptoUtils.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py
index 2c29c95..1e3dd65 100644
--- a/virtualsmartcard/src/vpicc/CryptoUtils.py
+++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py
@@ -21,7 +21,7 @@ from Crypto.Cipher import DES3, DES, AES, ARC4 #,IDEA no longer present in pytho
from struct import pack
from binascii import b2a_hex
from random import randint
-from utils import inttostring, stringtoint, hexdump
+from virtualsmartcard.utils import inttostring, stringtoint, hexdump
import string
import re
diff --git a/virtualsmartcard/src/vpicc/SEutils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py
similarity index 96%
rename from virtualsmartcard/src/vpicc/SEutils.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py
index 620c888..067bf80 100644
--- a/virtualsmartcard/src/vpicc/SEutils.py
+++ b/virtualsmartcard/src/vpicc/virtualsmartcard/SEutils.py
@@ -16,11 +16,11 @@
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see .
#
-import TLVutils
+import virtualsmartcard.TLVutils
from time import time
from random import seed, randint
-from ConstantDefinitions import *
-from utils import inttostring, stringtoint
+from virtualsmartcard.ConstantDefinitions import *
+from virtualsmartcard.utils import inttostring, stringtoint
class ControlReferenceTemplate:
def __init__(self,type,config=""):
diff --git a/virtualsmartcard/src/vpicc/SWutils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/SWutils.py
similarity index 100%
rename from virtualsmartcard/src/vpicc/SWutils.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/SWutils.py
diff --git a/virtualsmartcard/src/vpicc/SmartcardFilesystem.py b/virtualsmartcard/src/vpicc/virtualsmartcard/SmartcardFilesystem.py
similarity index 99%
rename from virtualsmartcard/src/vpicc/SmartcardFilesystem.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/SmartcardFilesystem.py
index d2bd61f..2b9c2ad 100644
--- a/virtualsmartcard/src/vpicc/SmartcardFilesystem.py
+++ b/virtualsmartcard/src/vpicc/virtualsmartcard/SmartcardFilesystem.py
@@ -21,10 +21,10 @@
@todo zu lange daten abschneiden und trotzdem tlv laenge beibehalten
"""
from pickle import dumps, loads
-from ConstantDefinitions import *
-from TLVutils import *
-from SWutils import *
-from utils import stringtoint, inttostring
+from virtualsmartcard.ConstantDefinitions import *
+from virtualsmartcard.TLVutils import *
+from virtualsmartcard.SWutils import *
+from virtualsmartcard.utils import stringtoint, inttostring
def isEqual(list): # {{{
"""Returns True, if all items are equal, otherwise False"""
diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/SmartcardSAM.py b/virtualsmartcard/src/vpicc/virtualsmartcard/SmartcardSAM.py
new file mode 100644
index 0000000..a9acf39
--- /dev/null
+++ b/virtualsmartcard/src/vpicc/virtualsmartcard/SmartcardSAM.py
@@ -0,0 +1,1261 @@
+#
+# 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 virtualsmartcard.SmartcardFilesystem, virtualsmartcard.CryptoUtils, virtualsmartcard.TLVutils
+import random, re, struct, Crypto
+import hashlib
+from virtualsmartcard.SWutils import SwError, SW, SW_MESSAGES
+from pickle import dumps, loads
+from virtualsmartcard.utils import inttostring, stringtoint, hexdump, C_APDU, R_APDU
+from Crypto.Cipher import DES3
+from virtualsmartcard.ConstantDefinitions import *
+from virtualsmartcard.SEutils import ControlReferenceTemplate as CRT
+
+def get_referenced_cipher(p1):
+ """
+ P1 defines the algorithm and mode to use. We dispatch it and return a
+ string that is understood by CryptoUtils.py functions
+ """
+
+ ciphertable = {
+ 0x00: "DES3-ECB",
+ 0x01: "DES3-ECB",
+ 0x02: "DES3-CBC",
+ 0x03: "DES-ECB",
+ 0x04: "DES-CBC",
+ 0x05: "AES-ECB",
+ 0x06: "AES-CBC",
+ 0x07: "RSA",
+ 0x08: "DSA"
+ }
+
+ if (ciphertable.has_key(p1)):
+ return ciphertable[p1]
+ else:
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+
+class CardContainer:
+ """
+ This class is used to store the data needed by the SAM
+ It includes the PIN, the master key of the SAM and a
+ hashmap containing all the keys used by the file encryption
+ system. The keys in the hashmap are indexed via the path
+ to the corresponding container.
+ """
+
+ def __init__(self, PIN=None, cardNumber=None, cardSecret=None):
+ from os import urandom
+
+ self.cardNumber = cardNumber
+ self.PIN = PIN
+ self.FSkeys = {}
+ self.cipher = 0x01 # Algorithm reference defined in __get_referenced_algorithm(p1)
+ self.master_password = None
+ self.master_key = None
+ self.salt = None
+ self.asym_key = None
+
+ keylen = virtualsmartcard.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!!!"
+
+ def getPIN(self):
+ return self.PIN
+
+ def setPIN(self,PIN):
+ self.PIN = PIN
+
+ def getCardSecret(self):
+ return self.cardSecret
+
+ def addKey(self,path,key):
+ """
+ Encrypt key and store it in the SAM
+ """
+ crypted_key = virtualsmartcard.CryptoUtils.cipher(True,self.cipher,self.master_password,key)
+ if self.FSkeys.has_key(path):
+ print "Overwriting key for path %s" % path
+ self.FSkeys[path] = crypted_key
+
+ def removeKey(self,path):
+ """
+ Erase a key from the SAM
+ """
+ if(self.FSkeys.has_key(path)):
+ del self.FSkeys[path]
+ else:
+ raise ValueError
+
+ def getKey(self,path):
+ """
+ Fetch a key from the SAM, decrypt and return it
+ @return: key if the key is in the container, otherwise None
+ """
+ if(self.FSkeys.has_key(path)):
+ plain_key = virtualsmartcard.CryptoUtils.cipher(False,self.cipher, self.master_password,self.FSkeys[path]) #TODO: Error checking:
+ return plain_key
+ else:
+ return None
+
+class SAM(object):
+
+ def __init__(self, PIN, cardNumber, mf=None):
+
+ self.CardContainer = CardContainer(PIN, cardNumber)
+ #self.CardContainer.loadConfiguration(path, password)
+ self.mf = mf
+
+ 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.SM_handler = Secure_Messaging(self.mf)
+
+ def set_MF(self,mf):
+ self.mf = mf
+ self.SM_handler.set_MF(mf)
+
+ def addFSkey(self,path,key):
+ #Deprecated ?
+ self.CardContainer.addKey(path, key)
+
+ def delFSkey(self,path):
+ #Deprecated ?
+ self.CardContainer.removeKey(path)
+
+ def FSencrypt(self,data):
+ """
+ Encrypt the given data, using the parameters stored in the SAM
+ """
+ if self.CardContainer.master_key == None:
+ raise ValueError
+
+ cipher = get_referenced_cipher(self.CardContainer.cipher)
+ padded_data = virtualsmartcard.CryptoUtils.append_padding(cipher,data)
+ crypted_data = virtualsmartcard.CryptoUtils.cipher(True,cipher,self.CardContainer.master_key,padded_data)
+ return crypted_data
+
+ def FSdecrypt(self,data):
+ """
+ Decrypt the given data, using the parameters stored in the SAM
+ """
+ if self.CardContainer.master_key == None:
+ raise ValueError, "No master key set."
+
+ cipher = get_referenced_cipher(self.CardContainer.cipher)
+ decrypted_data = virtualsmartcard.CryptoUtils.cipher(False,cipher,self.CardContainer.master_key,data)
+ unpadded_data = virtualsmartcard.CryptoUtils.strip_padding(cipher,decrypted_data)
+ return unpadded_data
+
+ def set_algorithm(self,algo):
+ if not algo in range(0x01,0x06):
+ raise ValueError, "Illegal Parameter"
+ else:
+ self.CardContainer.cipher = algo
+
+ def set_asym_algorithm(self,cipher,type):
+ """
+ @param cipher: Public/private key object from Crypto.PublicKey used for encryption
+ @param type: Type of the public key (e.g. RSA, DSA)
+ """
+ if not type in range(0x07,0x08):
+ raise ValueError, "Illegal Parameter"
+ else:
+ self.CardContainer.cipher = type
+ self.CardContainer.asym_key = cipher
+
+ def verify(self,p1,p2,PIN):
+ """
+ Authenticate the card user. Check if he entered a valid PIN.
+ If the PIN is invalid decrement retry counter. If retry counter
+ equals zero, block the card until reset with correct PUK
+ """
+
+ print "Received PIN: %s" % PIN.strip()
+ PIN = PIN.replace("\0","") #Strip NULL charakters
+
+ if p1 != 0x00:
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+
+ if self.counter > 0:
+ if self.CardContainer.getPIN() == PIN:
+ self.counter = 3
+ 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:
+ raise SwError(SW["ERR_AUTHBLOCKED"])
+
+ def change_reference_data(self,p1,p2,data):
+ """
+ Change the specified referenced data (e.g. CHV) of the card
+ """
+
+ data = data.replace("\0","") #Strip NULL charakters
+ self.CardContainer.setPIN(data)
+ print "New PIN = %s" % self.CardContainer.getPIN()
+ return SW["NORMAL"], ""
+
+ def internal_authenticate(self,p1,p2,data):
+ """
+ Authenticate card to terminal. Encrypt the challenge of the terminal
+ to prove key posession
+ """
+
+ 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:
+ key = self._get_referenced_key(p1,p2)
+ crypted_challenge = virtualsmartcard.CryptoUtils.cipher(True,cipher,key,data)
+
+ return SW["NORMAL"], crypted_challenge
+
+ def external_authenticate(self,p1,p2,data):
+ """
+ Authenticate the terminal to the card. Check wether Terminal correctly
+ encrypted the given challenge or not
+ """
+ if self.last_challenge is None:
+ raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
+
+ key = self._get_referenced_key(p1,p2)
+ if p1 == 0x00: #No information given
+ cipher = get_referenced_cipher(self.CardContainer.cipher)
+ else:
+ cipher = get_referenced_cipher(p1)
+
+ reference = virtualsmartcard.CryptoUtils.append_padding(cipher,self.last_challenge)
+ reference = virtualsmartcard.CryptoUtils.cipher(True,cipher,key,reference)
+ if(reference == data):
+ #Invalidate last challenge
+ self.last_challenge = None
+ return SW["NORMAL"], ""
+ else:
+ plain = virtualsmartcard.CryptoUtils.cipher(False,cipher,key,data)
+ print plain
+ print "Reference: " + hexdump(reference)
+ print "Data: " + hexdump(data)
+ raise SwError(SW["WARN_NOINFO63"])
+ #TODO: Counter for external authenticate?
+
+ def mutual_authenticate(self,p1,p2,mutual_challenge):
+ """
+ Takes an encrypted challenge in the form
+ 'Terminal Challenge | Card Challenge | Card number'
+ and checks it for validity. If the challenge is succesful
+ the card encrypts 'Card Challenge | Terminal challenge' and
+ returns this value
+ """
+
+ key = self._get_referenced_key(p1,p2)
+ card_number = self.get_card_number()
+
+ if (key == None):
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ if p1 == 0x00: #No information given
+ cipher = get_referenced_cipher(self.CardContainer.cipher)
+ else:
+ cipher = get_referenced_cipher(p1)
+
+ if (cipher == None):
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ plain = virtualsmartcard.CryptoUtils.cipher(False,cipher,key,mutual_challenge)
+ terminal_challenge = plain[:len(self.last_challenge)-1]
+ card_challenge = plain[len(self.last_challenge):len(challenge)-len(card_number)-1]
+ serial_number = plain[(challenge)-len(card_number):]
+ if terminal_challenge != self.last_challenge:
+ print "Mutual authentication failed!"
+ raise SwError(SW["WARN_NOINFO63"])
+ elif serial_number != card_number:
+ print "Mutual authentication failed!"
+ raise SwError(SW["WARN_NOINFO63"])
+
+ result = card_challenge + terminal_challenge
+ return SW["NORMAL"], virtualsmartcard.CryptoUtils.cipher(True,cipher,key,result)
+
+ def get_challenge(self,p1,p2,data):
+ """
+ Generate a random number of maximum 8 Byte and return it.
+ """
+ if (p1 != 0x00 or p2 != 0x00): #RFU
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+
+ length = 8 #Length of the challenge in Byte
+ max = "0x" + length * "ff"
+
+ #cipher = get_referenced_cipher(self.CardContainer.cipher)
+ #block_size = Crypto.Cipher.cipher.block_size
+
+ random.seed()
+ self.last_challenge = random.randint(0, int(max,16))
+ print "Generated challenge: %s" % str(self.last_challenge)
+ self.last_challenge = inttostring(self.last_challenge,length)
+
+ return SW["NORMAL"], self.last_challenge
+
+ def get_card_number(self):
+ return SW["NORMAL"], inttostring(self.cardNumber)
+
+ def _get_referenced_key(self,p1,p2):
+ """
+ This method returns the key specified by the p2 parameter. The key may be
+ stored on the cards filesystem or in memory.
+ @param p1: Specifies the algorithm to use. Needed to know the keylength.
+ @param p2: Specifies a reference to the key to be used for encryption
+ """
+
+ """ Meaning of p2:
+ b8 b7 b6 b5 b4 b3 b2 b1 | Meaning
+ 0 0 0 0 0 0 0 0 | No information is given
+ 0 -- -- -- -- -- -- -- | Global reference data (e.g. an MF secific key)
+ 1 -- -- -- -- -- -- -- | Specific reference data (e.g. DF specific key)
+ -- -- -- x x x x x | Number of the secret
+ Any other value | RFU
+ """
+
+ key = None
+ qualifier = p2 & 0x1F
+ algo = get_referenced_cipher(p1)
+ keylength = virtualsmartcard.CryptoUtils.get_cipher_keylen(algo)
+
+ if (p2 == 0x00): #No information given, use the global card key
+ key = self.CardContainer.cardSecret
+ #elif ((p2 >> 7) == 0x01 or (p2 >> 7) == 0x00): #We treat global and specific reference data alike
+ else:
+ try: #Interpret qualifier as an short fid and try to read the key from the FS
+ if self.mf == None: raise SwError(SW["CONDITIONSNOTSATISFIED"])
+ df = self.mf.currentDF()
+ fid = df.select("fid", stringtoint(qualifier))
+ key = fid.readbinary(keylength)
+ except SwError:
+ key = None
+
+ if key == None: #Try to read the key from the Key Container
+ key = self.CardContainer.getKey(reference_data)
+
+ if key != None:
+ return key
+ else:
+ raise SwError(SW["DATANOTFOUND"])
+
+ def _read_FS_data(self,fid,length):
+
+ """
+ This function creates a dummy filesystem, so that data (for example keys)
+ can be read from the FS. It is here for testing purposes. Later on the OS
+ interface to the FS should be used.
+ """
+ mf = SmartcardFilesystem.MF(filedescriptor=FDB["NOTSHAREABLEFILE"],
+ lifecycle=LCB["NOINFORMATION"], dfname=None)
+
+ mf = SmartcardFilesystem.MF(filedescriptor=FDB["DF"],lifecycle=LCB["ACTIVATED"], dfname=None)
+ df = mf.append(SmartcardFilesystem.DF(parent=mf,fid=4, dfname='\xd2\x76\x00\x01\x24\x01', bertlv_data=[]))
+ mf.append(SmartcardFilesystem.TransparentStructureEF(parent=df, fid=0x1012,
+ filedescriptor=0, data="Key from the FS."))
+
+ df = mf.currentDF()
+ file = df.select('fid', 0X1012)
+ sw, result = file.readbinary(0)
+ return sw, result
+
+ #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)
+
+ def parse_SM_CAPDU(self,CAPDU,header_authentication):
+ return self.SM_handler.parse_SM_CAPDU(CAPDU, header_authentication)
+
+ def protect_result(self,sw,unprotected_result):
+ return self.SM_handler.protect_response(sw, unprotected_result)
+
+ def perform_security_operation(self,p1,p2,data):
+ return self.SM_handler.perform_security_operation(p1, p2, data)
+
+ def manage_security_environment(self,p1,p2,data):
+ return self.SM_handler.manage_security_environment(p1, p2, data)
+
+class PassportSAM(SAM):
+ def __init__(self, mf):
+ df = mf.currentDF()
+ ef_dg1 = df.select("fid", 0x0101)
+ 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, 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"
+ self.SM_handler.RAPDU_SE.ct.algo = "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):
+ """Performes the basic access controll protocoll as defined in
+ the ICAO MRTD standard"""
+ rnd_icc = self.last_challenge
+
+ #Receive Mutual Authenticate APDU from terminal
+ #Decrypt data and check MAC
+ #cipher = DES3.new(self.KEnc,"des3-cbc")
+ Eifd = resp_data[:-8]
+ Mifd = self._mac(self.KMac, Eifd)
+ #Check the MAC
+ if not Mifd == resp_data[-8:]:
+ raise ValueError, "Passport authentication failed: Wrong MAC on incoming data during Mutual Authenticate"
+ #Decrypt the data
+ plain = virtualsmartcard.CryptoUtils.cipher(False,"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]
+ max = "0x" + 16 * "ff"
+ random.seed()
+ Kicc = inttostring(random.randint(0, int(max,16)))
+ #Generate Answer
+ data = plain[8:16] + plain[:8] + Kicc
+ Eicc = virtualsmartcard.CryptoUtils.cipher(True,"DES3-CBC",self.KEnc,data)
+ Micc = self._mac(self.KMac,Eicc)
+ #Derive the final keys
+ KSseed = virtualsmartcard.CryptoUtils.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.SM_handler.CAPDU_SE.ct.key = self.KSenc
+ self.SM_handler.CAPDU_SE.cct.key = self.KSmac
+ self.SM_handler.RAPDU_SE.ct.key = self.KSenc
+ self.SM_handler.RAPDU_SE.cct.key = self.KSmac
+ self.SM_handler.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
+ self.SM_handler.CAPDU_SE.ct.algo = "DES3-CBC"
+ self.SM_handler.RAPDU_SE.ct.algo = "DES3-CBC"
+ self.SM_handler.CAPDU_SE.cct.algo = "CC"
+ self.SM_handler.RAPDU_SE.cct.algo = "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 = virtualsmartcard.CryptoUtils.cipher(True, "des-cbc", key[:8], data)
+ b = virtualsmartcard.CryptoUtils.cipher(False, "des-ecb", key[8:16], a[-8:])
+ c = virtualsmartcard.CryptoUtils.cipher(True, "des-ecb", key[:8], b)
+ return c
+
+class CryptoflexSAM(SAM):
+ 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):
+ asym_key = self.SM_handler.generate_public_key_pair(p1, p2, data)
+ 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
+
+class Security_Environment(object):
+
+ def __init__(self):
+ self.SEID = None
+ self.sm_objects = ""
+ #Default configuration
+ ct = "\x80\x01\x01\x82\x01\x00\x85\x01\x00"
+ #Control Reference Tables
+ self.at = CRT(TEMPLATE_AT)
+ self.kat = CRT(TEMPLATE_KAT)
+ self.ht = CRT(TEMPLATE_HT)
+ self.cct = CRT(TEMPLATE_CCT)
+ self.dst = CRT(TEMPLATE_DST)
+ self.ct = CRT(TEMPLATE_CT)
+
+
+ def mse(self,config):
+ structure = TLVutils.unpack(config)
+ for tag, length, value in structure:
+ if tag == TEMPLATE_AT:
+ self.at.parse_config(config)
+ elif tag == TEMPLATE_CRT:
+ self.crt.parse_config(config)
+ elif tag == TEMPLATE_CCT:
+ self.cct.parse_config(config)
+ elif tag == TEMPLATE_KAT:
+ self.kat.parse_config(config)
+ elif tag == TEMPLATE_CT:
+ self.ct.parse_config(config)
+ elif tag == TEMPLATE_DST:
+ self.dst.parse_config(config)
+ else:
+ raise ValueError
+
+class Secure_Messaging(object):
+ def __init__(self,MF,CAPDU_SE=None,RAPDU_SE=None):
+ self.mf = MF
+ if not CAPDU_SE:
+ self.CAPDU_SE = Security_Environment()
+ else:
+ self.CAPDU_SE = CAPDU_SE
+ if not RAPDU_SE:
+ self.RAPDU_SE = Security_Environment()
+ else:
+ self.RAPDU_SE = RAPDU_SE
+
+ self.current_SE = Security_Environment()
+
+ def set_MF(self,mf):
+ self.mf = mf
+
+ def manage_security_environment(self,p1,p2,data):
+ """
+ This method is used to store, restore or erase Security Environments
+ or to manipualte the various parameters of the current SE.
+ P1 specifies the operation to perform, p2 is either the SEID for the
+ referred SE or the tag of a control reference template
+ """
+
+ """ P1:
+ b8 b7 b6 b5 b4 b3 b2 b1 Meaning
+ - - - 1 - - - - Secure messaging in command data field
+ - - 1 - - - - - Secure messaging in response data field
+ - 1 - - - - - - Computation, decipherment, internal authentication and key agreement
+ 1 - - - - - - - Verification, encipherment, external authentication and key agreement
+ - - - - 0 0 0 1 SET
+ 1 1 1 1 0 0 1 0 STORE
+ 1 1 1 1 0 0 1 1 RESTORE
+ 1 1 1 1 0 1 0 0 ERASE
+ """
+
+ cmd = p1 & 0x0F
+ se = p1 & 0xF0
+ if(cmd == 0x01):
+ if se == 0x01: #Set the right SE
+ self.current_SE = self.CAPDU_SE
+ elif se == 0x02:
+ self.current_SE = self.RAPDU_SE
+ else:
+ pass #FIXME
+ self.__set_SE(p2,data)
+ elif(cmd== 0x02):
+ self.__store_SE(p2)
+ elif(cmd == 0x03):
+ self.__restore_SE(p2)
+ elif(cmd == 0x04):
+ self.__erase_SE(p2)
+ else:
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+
+ def __set_SE(self,p2,data):
+ """
+ Manipulate the current Security Environment. P2 is the tag of a
+ control reference template, data contains control reference objects
+ """
+
+ valid_p2 = (0xA4,0xA6,0xB4,0xB6,0xB8)
+ if not p2 in valid_p2:
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ tag, length, value = TLVutils.unpack(data) #Only one object?
+ if p2 == 0xA4:
+ self.current_SE.at.parse_SE_config(value)
+ elif p2 == 0xA6:
+ self.current_SE.kat.parse_SE_config(value)
+ elif p2 == 0xAA:
+ self.current_SE.ht.parse_SE_config(value)
+ elif p2 == 0xB4:
+ self.current_SE.cct.parse_SE_config(value)
+ elif p2 == 0xB6:
+ self.current_SE.dst.parse_SE_config(value)
+ elif p2 == 0xB8:
+ self.current_SE.ct.parse_SE_config(value)
+
+ def __store_SE(self,SEID):
+ """
+ Stores the current Security environment in the secure access module. The
+ SEID is used as a reference to identify the SE.
+ """
+ SEstr = dumps(self.CAPDU_SE)
+ try:
+ self.SAM.addkey(SEID, SEstr) #TODO: Need SAM reference
+ except ValueError:
+ raise SwError["ERR_INCORRECTP1P2"]
+
+
+ def __restore_SE(self,SEID):
+ """
+ Restores a Security Environment from the SAM and replaces the current SE
+ with it
+ """
+ SEstr = self.SAM.get_key(SEID)
+ SE = loads(SEstr)
+ if isinstance(SE, SecurityEnvironment):
+ self.CAPDU_SE = SE
+ else:
+ raise ValueError
+
+ def __erase_SE(self,SEID):
+ """
+ Erases a Security Environment stored under SEID from the SAM
+ """
+ self.SAM.removeKey(SEID)
+
+ def parse_SM_CAPDU(self,CAPDU,header_authentication):
+ """
+ Frontend for the __parse_SM command. We set the right Security Environment and restore the
+ old one, when the command is finished
+ """
+
+ current_SE = self.current_SE
+ self.current_SE = self.CAPDU_SE
+ try:
+ capdu = self.__parse_SM(CAPDU,header_authentication)
+ except SwError, e: #Restore Security Environment
+ self.current_SE = current_SE
+ raise e
+
+ self.current_SE = current_SE
+ return capdu
+
+ def __parse_SM(self,CAPDU,header_authentication):
+ """
+ This methods parses a data field including Secure Messaging objects.
+ SM_header indicates wether or not the header of the message shall be authenticated
+ It returns an unprotected command APDU
+ @param CAPDU: The protected CAPDU to be parsed
+ @param header_authentication: Wether or not the header should be included in authentication mechanisms
+ @return: Unprotected command APDU
+ """
+ structure = TLVutils.unpack(CAPDU.data)
+ return_data = ["",];
+ expected = self.current_SE.sm_objects
+
+ cla = None
+ ins = None
+ p1 = None
+ p2 = None
+ le = None
+
+ if header_authentication:
+ to_authenticate = inttostring(CAPDU.cla) + inttostring(CAPDU.ins) + inttostring(CAPDU.p1) + inttostring(CAPDU.p2)
+ to_authenticate = virtualsmartcard.CryptoUtils.append_padding("DES-CBC", to_authenticate)
+ else:
+ to_authenticate = ""
+
+ for object in structure:
+ tag, length, value = object
+ #TODO: Sanity checking
+ #if not SM_Class.has_key(tag):
+ # raise ValueError, "unknown Secure messaging tag %#X" % tag
+ if tag % 2 == 1: #Include object in checksum calculation
+ to_authenticate += inttostring(tag) + inttostring(length) + value
+
+ #SM data objects for encapsulating plain values
+ if tag in (SM_Class["PLAIN_VALUE_NO_TLV"],SM_Class["PLAIN_VALUE_NO_TLV_ODD"]):
+ return_data.append(value) #FIXME: Need TLV coding?
+ elif tag in (SM_Class["PLAIN_VALUE_TLV_INCULDING_SM"],SM_Class["PLAIN_VALUE_TLV_INCULDING_SM_ODD"]):
+ #Encapsulated SM objects. Parse them
+ return_data.append(self.parse_SM_CAPDU(value, header_authentication)) #FIXME: Need to pack value into a dummy CAPDU
+ elif tag in (SM_Class["PLAIN_VALUE_TLV_NO_SM"],SM_Class["PLAIN_VALUE_TLV_NO_SM_ODD"]):
+ #Encapsulated plaintext BER-TLV objects
+ return_data.append(value)
+ elif tag in (SM_Class["Ne"],SM_Class["Ne_ODD"]):
+ le = value
+ elif tag == SM_Class["PLAIN_COMMAND_HEADER"]:
+ if len(value) != 8:
+ raise ValueError, "Plain Command Header expected, but received insufficient data"
+ else:
+ cla = value[:2]
+ ins = value[2:4]
+ p1 = value[4:6]
+ p2 = value[6:8]
+
+ #SM data objects for confidentiality
+ if tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM"],SM_Class["CRYPTOGRAM_PLAIN_TLV_INCLUDING_SM_ODD"]):
+ #The Cryptogram includes SM objects. We decrypt them and parse the objects.
+ plain = decipher(tag,0x80,value) #TODO: Need Le = length
+ return_data.append(self.parse_SM_CAPDU(plain, header_authentication))
+ elif tag in (SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM"],SM_Class["CRYPTOGRAM_PLAIN_TLV_NO_SM_ODD"]):
+ #The Cryptogram includes BER-TLV enconded plaintext. We decrypt them and return the objects.
+ plain = decipher(tag,0x80,value)
+ return_data.append(plain)
+ elif tag in (SM_Class["CRYPTOGRAM_PADDING_INDICATOR"],SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"]):
+ #The first byte of the specified data field indicates the padding to use:
+ """
+ Value Meaning
+ '00' No further indication
+ '01' Padding as specified in 6.2.3.1
+ '02' No padding
+ '1X' One to four secret keys for enciphering information, not keys ('X' is a bitmap with any value from '0' to 'F')
+ '11' indicates the first key (e.g., an "even" control word in a pay TV system)
+ '12' indicates the second key (e.g., an "odd" control word in a pay TV system)
+ '13' indicates the first key followed by the second key (e.g., a pair of control words in a pay TV system)
+ '2X' Secret key for enciphering keys, not information ('X' is a reference with any value from '0' to 'F')
+ (e.g., in a pay TV system, either an operational key for enciphering control words, or a management key
+ for enciphering operational keys)
+ '3X' Private key of an asymmetric key pair ('X' is a reference with any value from '0' to 'F')
+ '4X' Password ('X' is a reference with any value from '0' to 'F')
+ '80' to '8E' Proprietary
+ """
+ padding_indicator = stringtoint(value[0])
+ sw, plain = self.decipher(tag,0x80,value[1:])
+ if sw != 0x9000:
+ raise ValueError
+ plain = virtualsmartcard.CryptoUtils.strip_padding(self.CAPDU_SE.ct.algo,plain,padding_indicator)
+ return_data.append(plain)
+
+ #SM data objects for authentication
+ if tag == SM_Class["CHECKSUM"]:
+ auth = virtualsmartcard.CryptoUtils.append_padding("DES-CBC", to_authenticate)
+ sw, checksum = self.compute_cryptographic_checksum(0x8E, 0x80, auth)
+ if checksum != value:
+ print "Failed to verify checksum!"
+ raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
+ elif tag == SM_Class["DIGITAL_SIGNATURE"]:
+ auth = to_authenticate #FIXME: Need padding?
+ sw, signature = self.compute_digital_signature(0x9E, 0x9A, auth)
+ if signature != value:
+ print "Failed to verify signature!"
+ raise SwEroor(SW["ERR_SECMESSOBJECTSINCORRECT"])
+ elif tag in (SM_Class["HASH_CODE"],SM_Class["HASH_CODE_ODD"]):
+ sw, hash = self.hash(p1, p2, to_authenticate)
+ if hash != value:
+ print "Failed to verify hash!"
+ raise SwEroor(SW["ERR_SECMESSOBJECTSINCORRECT"])
+
+ #Check if we just parsed a expected SM Object:
+ pos = 0
+ while (pos < len (expected)):
+ if expected[pos] == tag:
+ expected = expected[:pos-1] + expected[pos:]
+ break
+ pos += 1
+
+ #Form unprotected CAPDU
+ if cla == None:
+ cla = CAPDU.cla
+ if ins == None:
+ ins = CAPDU.ins
+ if p1 == None:
+ p1 = CAPDU.p1
+ if p2 == None:
+ p2 = CAPDU.p2
+ if le == None:
+ le = CAPDU.le
+ if expected != "":
+ raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])
+
+ c = C_APDU(cla=cla,ins=ins,p1=p1,p2=p2,le=le,data="".join(return_data))
+ return c
+
+ def protect_response(self,sw,result):
+ """
+ Frontend for the __protect command. We set the right Security Environment and restore the
+ old one, when the command is finished
+ """
+
+ current_SE = self.current_SE
+ self.current_SE = self.RAPDU_SE
+ try:
+ sw, data = self.__protect(sw,result)
+ except SwError, e: #Restore Security Environment
+ self.current_SE = current_SE
+ raise e
+
+ self.current_SE = current_SE
+ return sw, data
+
+ def __protect(self,sw,result):
+ """
+ This method protects a response APDU using secure messanging mechanisms
+ It returns the protected data and the SW bytes
+ """
+ expected = self.current_SE.sm_objects
+ for pos in range(len(expected)):
+ tag = expected[pos]
+
+ return_data = ""
+ #if sw == SW["NORMAL"]:
+ # sw = inttostring(sw)
+ # length = len(sw)
+ # tag = SM_Class["PLAIN_PROCESSING_STATUS"]
+ # tlv_sw = TLVutils.pack([(tag,length,sw)])
+ # return_data += tlv_sw
+
+ if result != "": # Encrypt the data included in the RAPDU
+ sw, encrypted = self.encipher(0x82, 0x80, result)
+ encrypted = "\x01" + encrypted
+ encrypted_tlv = TLVutils.pack([(SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"],len(encrypted),encrypted)])
+ return_data += encrypted_tlv
+
+ if sw == SW["NORMAL"]:
+ if self.CAPDU_SE.cct.algo == None:
+ raise SwError(SW["CONDITIONSNOTSATISFIED"])
+ elif self.CAPDU_SE.cct.algo == "CCT":
+ tag = SM_Class["CHECKSUM"]
+ to_auth = virtualsmartcard.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)])
+ elif self.CAPDU_SE.cct.algo == "SIGNATURE":
+ tag = SM_Class["DIGITAL_SIGNATURE"]
+ hash = self.hash(0x90, 0x80, return_data)
+ sw, auth = self.compute_digital_signature(0x9E, 0x9A, hash)
+ length = len(auth)
+ return_data += TLVutils.pack([(tag,length,auth)])
+
+ return SW["NORMAL"], return_data
+
+ #The following commands implement ISO 7816-8 {{{
+ def perform_security_operation(self,p1,p2,data):
+ """
+ In the end this command is nothing but a big switch for all the other commands
+ in ISO 7816-8. It will invoke the appropiate command and return its result
+ """
+
+ allowed_P1P2 = ((0x90,0x80),(0x90,0xA0),(0x9E,0x9A),(0x9E,0xAC),(0x9E,0xBC),(0x00,0xA2),
+ (0x00,0xA8),(0x00,0x92),(0x00,0xAE),(0x00,0xBE),(0x82,0x80),(0x84,0x80),
+ (0x86,0x80),(0x80,0x82),(0x80,0x84),(0x80,0x86))
+
+ if (p1,p2) not in allowed_P1P2:
+ raise SwError(SW["INCORRECTP1P2"])
+
+ if(p2 in (0x80,0xA0) and p1==0x90):
+ sw, response_data = self.hash(p1,p2,data)
+ elif(p2 in (0x9A,0xAC,0xBC) and p1 == 0x9E):
+ sw, response_data = self.compute_digital_signature(p1, p2, data)
+ elif(p2 == 0xA2 and p1 == 0x00):
+ sw, response_data = self.verify_cryptographic_checksum(p1, p2, data)
+ elif(p2 == 0xA8 and p1 == 0x00):
+ sw, response_data = self.verify_digital_signature(p1, p2, data)
+ elif(p2 in (0x92,0xAE,0xBE) and p1 == 0x00):
+ sw, response_data = self.verify_certificate(p1, p2, data)
+ elif (p2 == 0x80 and p1 in (0x82,0x84,0x86)):
+ sw, response_data = self.encipher(p1, p2, data)
+ elif (p2 in (0x82,0x84,0x86) and p1 == 0x80):
+ sw, response_data = self.decipher(p1, p2, data)
+
+ if p1 == 0x00:
+ assert response_data == ""
+
+ return sw, response_data
+
+
+ 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
+ @bug: Always use CAPDU settings ???
+ """
+ if p1 != 0x8E or p2 != 0x80:
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ if self.CAPDU_SE.cct.algo == None or self.current_SE.cct.key == None:
+ raise SWError(SE["ERR_CONDITIONNOTSATISFIED"])
+
+ checksum = virtualsmartcard.CryptoUtils.crypto_checksum(self.current_SE.cct.algo,
+ self.current_SE.cct.key,
+ data,
+ self.current_SE.cct.iv)
+ return SW["NORMAL"], checksum
+
+ def compute_digital_signature(self,p1,p2,data):
+ """
+ Compute a digital signature for the given data.
+ Algorithm and key are specified in the current SE
+ @param p1: Must be 0x9E = Secure Messaging class for digital signatures
+ @param p2: Must be one of 0x9A, 0xAC, 0xBC. Indicates what kind of data is
+ included in the data field.
+ """
+
+ if p1 != 0x9E or not p2 in (0x9A,0xAC,0xBC):
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ if self.current_SE.dst.key == None:
+ raise SWError(SE["ERR_CONDITIONNOTSATISFIED"])
+
+ if p2 == 0x9A: #Data to be signed
+ to_sign = data
+ elif p2 == 0xAC: #Data objects, sign values
+ to_sign = ""
+ structure = TLVutils.unpack(data)
+ for tag, length, value in structure:
+ to_sign += value
+ elif p2 == 0xBC: #Data objects to be signed
+ pass
+
+ signature = self.current_SE.dst.key.sign(data,"")
+ return SW["NORMAL"], signature
+
+ def hash(self,p1,p2,data):
+ """
+ Hash the given data using the algorithm specified by the
+ current Security environment.
+ Return raw data (no TLV coding).
+ """
+ if p1 != 0x90 or not p2 in (0x80,0xA0):
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ algo = self.current_SE.ht.algo
+ if algo == None:
+ raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
+ try:
+ hash = virtualsmartcard.CryptoUtils.hash("MD5",data)
+ except ValueError: #FIXME: Type of error
+ raise SwError(SW["ERR_SECMESSNOTSUPPORTED"])
+
+ return SW["NORMAL"], hash
+
+ def verify_cryptographic_checksum(self,p1,p2,data):
+ """
+ Verify the cryptographic checksum contained in the data field. Data field must contain
+ a cryptographic checksum (tag 0x8E) and a plain value (tag 0x80)
+ """
+ plain = ""
+ cct = ""
+
+ algo = self.current_SE.cct.algo
+ key = self.current_SE.cct.key
+ iv = self.current_SE.cct.iv
+ if algo == None or key == None:
+ raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
+
+ structure = TLVutils.unpack(config)
+ for tag, length, value in structure:
+ if tag == 0x80:
+ plain = value
+ elif tag == 0x8E:
+ cct = value
+ if plain == "" or cct == "":
+ raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])
+ else:
+ my_cct = virtualsmartcard.CryptoUtils.crypto_checksum(algo,key,plain,iv)
+ if my_cct == cct:
+ return SW["NORMAL"], ""
+ else:
+ raise SwError["ERR_SECMESSOBJECTSINCORRECT"]
+
+ def verify_digital_signature(self,p1,p2,data):
+ """
+ Verify the digital signature contained in the data field. Data must contain
+ a data to sign (tag 0x9A, 0xAC or 0xBC) and a digital signature (0x9E)
+ """
+ key = self.current_SE.dst.key
+ to_sign = ""
+ signature = ""
+
+ if key == None:
+ raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
+
+ structure = TLVutils.unpack(config)
+ for tag, length, value in structure:
+ if tag == 0x9E:
+ signature = value
+ elif tag == 0x9A: #FIXME: Correct treatment of all possible tags
+ to_sign = value
+ elif tag == 0xAC:
+ pass
+ elif tag == 0xBC:
+ pass
+
+ if to_sign == "" or signature == "":
+ raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])
+
+ my_signature = key.sign(value)
+ if my_signature == signature:
+ return SW["NORMAL"], ""
+ else:
+ raise SwError(["ERR_SECMESSOBJECTSINCORRECT"])
+
+ def verify_certificate(self,p1,p2,data):
+ if p1 != 0x00 or p2 not in (0x92,0xAE,0xBE):
+ raise SwError(SW["ERR_INCORRECTP1P2"])
+ else:
+ raise NotImplementedError
+
+ def encipher(self,p1,p2,data):
+ """
+ Encipher data using key, algorithm, IV and Padding specified
+ by the current Security environment.
+ Return raw data (no TLV coding).
+ """
+ algo = self.current_SE.ct.algo
+ key = self.current_SE.ct.key
+ if key == None or algo == None:
+ return SW["ERR_CONDITIONNOTSATISFIED"], ""
+ else:
+ padded = virtualsmartcard.CryptoUtils.append_padding(algo, data)
+ crypted = virtualsmartcard.CryptoUtils.cipher(True, algo, key, padded, self.current_SE.ct.iv)
+ return SW["NORMAL"], crypted
+
+ def decipher(self,p1,p2,data):
+ """
+ Decipher data using key, algorithm, IV and Padding specified
+ by the current Security environment.
+ Return raw data (no TLV coding). Padding is not removed!!!
+ """
+ algo = self.current_SE.ct.algo
+ key = self.current_SE.ct.key
+ if key == None or algo == None:
+ return SW["ERR_CONDITIONNOTSATISFIED"], ""
+ else:
+ plain = virtualsmartcard.CryptoUtils.cipher(False,algo,key,data,self.current_SE.ct.iv)
+ return SW["NORMAL"], plain
+
+ def generate_public_key_pair(self,p1,p2,data):
+ """
+ Generate a new public-private key pair.
+ """
+ from Crypto.PublicKey import RSA, DSA
+ from Crypto.Util.randpool import RandomPool
+ rnd = RandomPool()
+
+ cipher = self.CAPDU_SE.ct.algo #FIXME: Current SE?
+
+ c_class = locals().get(cipher, None)
+ if c_class is None:
+ raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
+
+ if p1 & 0x01 == 0x00: #Generate key
+ PublicKey = c_class.generate(self.CAPDU_SE.dst.keylength,rnd.get_bytes)
+ self.CAPDU_SE.dst.key = PublicKey
+ else:
+ pass #Read key
+
+ #Encode keys
+ if cipher == "RSA":
+ #Public key
+ n = str(PublicKey.__getstate__()['n'])
+ e = str(PublicKey.__getstate__()['e'])
+ pk = ((0x81,len(n),n),(0x82,len(e),e))
+ result = TLVutils.bertlv_pack(pk)
+ #result = TLVutils.bertlv_pack((0x7F49,len(pk),pk))
+ #Private key
+ d = PublicKey.__getstate__()['d']
+ elif cipher == "DSA":
+ #Public key
+ p = str(PublicKey.__getstate__()['p'])
+ q = str(PublicKey.__getstate__()['q'])
+ g = str(PublicKey.__getstate__()['g'])
+ y = str(PublicKey.__getstate__()['y'])
+ #Private key
+ x = str(PublicKey.__getstate__()['x'])
+ #Add more algorithms here
+
+ if p1 & 0x02 == 0x02:
+ return SW["NORMAL"], result
+ else:
+ #FIXME: Where to put the keys?
+ return SW["NORMAL"], ""
+
+ #}}}
+class CryptoflexSM(Secure_Messaging):
+ def __init__(self,mf):
+ Secure_Messaging.__init__(self,mf) #Does Cryptoflex need its own SE?
+
+ 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 specifiy 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.CAPDU_SE.dst.key = PublicKey
+
+ e_in = struct.unpack(".
#
-from ConstantDefinitions import *
-from TLVutils import *
-from SWutils import SwError, SW
-from SmartcardFilesystem import prettyprint_anything, MF, DF, CryptoflexMF, TransparentStructureEF, make_property
-from utils import C_APDU, R_APDU, hexdump, inttostring
-from SmartcardSAM import SAM, PassportSAM, CryptoflexSAM
+from virtualsmartcard.ConstantDefinitions import *
+from virtualsmartcard.TLVutils import *
+from virtualsmartcard.SWutils import SwError, SW
+from virtualsmartcard.SmartcardFilesystem import prettyprint_anything, MF, DF, CryptoflexMF, TransparentStructureEF, make_property
+from virtualsmartcard.utils import C_APDU, R_APDU, hexdump, inttostring
+from virtualsmartcard.SmartcardSAM import SAM, PassportSAM, CryptoflexSAM
import CardGenerator
from smartcard.CardMonitoring import CardMonitor, CardObserver
@@ -579,28 +579,3 @@ class VirtualICC(object): # {{{
self.cardGenerator.setCard(self.os.mf, self.os.SAM)
self.cardGenerator.saveCard(self.filename)
# }}}
-
-if __name__ == "__main__":
- from optparse import OptionParser
- parser = OptionParser()
- parser.add_option("-t", "--type", action="store", type="choice",
- default='iso7816',
- choices=['iso7816', 'cryptoflex', 'ePass', 'relay'],
- help="Type of Smartcard [default: %default]")
- parser.add_option("-r", "--reader", action="store", type="int",
- default=None,
- help="Number of reader for relaying")
- parser.add_option("-f", "--file", action="store", type="string",
- default=None,
- help="Load a saved card")
- parser.add_option("-n", "--hostname", action="store", type="string",
- default='localhost',
- help="Address of Virtual PCD [default: %default]")
- parser.add_option("-p", "--port", action="store", type="int",
- default=35963,
- help="Port of Virtual PCD [default: %default]")
- (options, args) = parser.parse_args()
-
- vicc = VirtualICC(options.file, options.type, options.hostname,
- options.port, readernum=options.reader)
- vicc.run()
diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/__init__.py b/virtualsmartcard/src/vpicc/virtualsmartcard/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/virtualsmartcard/src/vpicc/utils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py
similarity index 100%
rename from virtualsmartcard/src/vpicc/utils.py
rename to virtualsmartcard/src/vpicc/virtualsmartcard/utils.py