# # Copyright (C) 2011 Dominik Oepen # # This file is part of virtualsmartcard. # # virtualsmartcard is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # virtualsmartcard is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # virtualsmartcard. If not, see . # import binascii import getpass import logging try: import readline except ImportError: import pyreadline as readline import sys from pickle import loads, dumps from virtualsmartcard.TLVutils import pack from virtualsmartcard.utils import inttostring from virtualsmartcard.SmartcardFilesystem import MF, DF, TransparentStructureEF from virtualsmartcard.ConstantDefinitions import FDB, ALGO_MAPPING from virtualsmartcard.SmartcardSAM import SAM # 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')) class CardGenerator(object): """This class is used to generate the SAM and filesystem for the different supported card types. It is also able used for persistent storage (in encrypted form) of the card on disks. """ def __init__(self, card_type=None, sam=None, mf=None): self.type = card_type self.mf = mf self.sam = sam self.password = None self.datagroups = {} def __generate_iso_card(self): default_pin = "1234".encode('ascii') default_cardno = "1234567890".encode('ascii') logging.warning("Using default SAM parameters. PIN=%s, Card Nr=%s" % (default_pin, default_cardno)) # TODO: Use user provided data self.sam = SAM(default_pin, default_cardno) self.mf = MF(filedescriptor=FDB["DF"]) self.sam.set_MF(self.mf) def __generate_ePass(self): """Generate the MF and SAM of an ICAO passport. This method is responsible for generating the filesystem and filling it with content. Therefore it must interact with the user by prompting for the MRZ and optionally for the path to a photo.""" from PIL import Image from virtualsmartcard.cards.ePass import PassportSAM # TODO: Sanity checks MRZ = raw_input("Please enter the MRZ as one string: ") readline.set_completer_delims("") readline.parse_and_bind("tab: complete") picturepath = raw_input("Please enter the path to an image: ") picturepath = picturepath.strip() # MRZ1 = "P PlaceOfResidence = self.datagroups["PlaceOfResidence"] if \ "PlaceOfResidence" in self.datagroups else '' Country = self.datagroups["Country"] if "Country" in self.datagroups \ else 'D' City = self.datagroups["City"] if "City" in self.datagroups else \ 'KOLN' ZIP = self.datagroups["ZIP"] if "ZIP" in self.datagroups else '51147' Street = self.datagroups["Street"] if "Street" in \ self.datagroups else 'HEIDESTRASSE 17' CommunityID = self.datagroups["CommunityID"] if "CommunityID" in \ self.datagroups else '02760378900276' if (CommunityID.rstrip() != ""): # the plain CommunityID integer value has to be translated into # its binary representation. '0276...' will be '\x02\x76\...' CommunityID_Binary = binascii.unhexlify(CommunityID) # ResidencePermit1 and ResidencePermit2 are part of eAT only ResidencePermit1 = self.datagroups["ResidencePermit1"] if \ "ResidencePermit1" in self.datagroups else \ 'ResidencePermit1 field up to 750 characters' ResidencePermit2 = self.datagroups["ResidencePermit2"] if \ "ResidencePermit2" in self.datagroups else \ 'ResidencePermit1 field up to 250 characters' # Currently, those data groups are for further usage: dg12_param = self.datagroups["dg12"] if "dg12" in \ self.datagroups else '' dg14_param = self.datagroups["dg14"] if "dg14" in \ self.datagroups else '' dg15_param = self.datagroups["dg15"] if "dg15" in \ self.datagroups else '' dg16_param = self.datagroups["dg16"] if "dg16" in \ self.datagroups else '' dg21_param = self.datagroups["dg21"] if "dg21" in \ self.datagroups else '' # "Attribute not on Chip" makes sence only for ReligiousArtisticName, # Nationality, BirthName, ResidencePermit1 and ResidencePermit2, refer # to BSI TR-03127 if (DocumentType.rstrip() != ""): dg1 = pack([(0x61, 0, [(0x13, 0, DocumentType)])], True) else: dg1 = None if (IssuingState.rstrip() != ""): dg2 = pack([(0x62, 0, [(0x13, 0, IssuingState)])], True) else: dg2 = None if (DateOfExpiry.rstrip() != ""): dg3 = pack([(0x63, 0, [(0x12, 0, DateOfExpiry)])], True) else: dg3 = None if (GivenNames.rstrip() != ""): dg4 = pack([(0x64, 0, [(0x0C, 0, GivenNames)])], True) else: dg4 = None if (FamilyNames.rstrip() != ""): dg5 = pack([(0x65, 0, [(0x0C, 0, FamilyNames)])], True) else: dg5 = None if (ReligiousArtisticName.rstrip() != ""): dg6 = pack([(0x66, 0, [(0x0C, 0, ReligiousArtisticName)])], True) else: dg6 = None if (AcademicTitle.rstrip() != ""): dg7 = pack([(0x67, 0, [(0x0C, 0, AcademicTitle)])], True) else: dg7 = None if (DateOfBirth.rstrip() != ""): dg8 = pack([(0x68, 0, [(0x12, 0, DateOfBirth)])], True) else: dg8 = None if (PlaceOfBirth.rstrip() != ""): dg9 = pack([(0x69, 0, [(0xA1, 0, [(0x0C, 0, PlaceOfBirth)])])], True) else: dg9 = None if (Nationality.rstrip() != ""): dg10 = pack([(0x6A, 0, [(0x13, 0, Nationality)])], True) else: dg10 = None if (Sex.rstrip() != ""): dg11 = pack([(0x6B, 0, [(0x13, 0, Sex)])], True) else: dg11 = None if (dg12_param.rstrip() != ""): dg12 = dg12_param else: dg12 = None if (BirthName.rstrip() != ""): dg13 = pack([(0x6D, 0, [(0x0C, 0, BirthName)])], True) else: dg13 = None if (dg14_param.rstrip() != ""): dg14 = dg14_param else: dg14 = None if (dg15_param.rstrip() != ""): dg15 = dg15_param else: dg15 = None if (dg16_param.rstrip() != ""): dg16 = dg16_param else: dg16 = None if (PlaceOfResidence.rstrip() != ""): dg17 = pack([(0x71, 0, [(0x30, 0, [ (0xAA, 0, [(0x0C, 0, Street)]), (0xAB, 0, [(0x0C, 0, City)]), (0xAD, 0, [(0x13, 0, Country)]), (0xAE, 0, [(0x13, 0, ZIP)]) ])])], True) else: dg17 = None if (CommunityID.rstrip() != ""): dg18 = pack([(0x72, 0, [(0x04, 0, CommunityID_Binary)])], True) else: dg18 = None if (ResidencePermit1.rstrip() != ""): dg19 = pack([(0x73, 0, [(0xA1, 0, [(0x0C, 0, ResidencePermit1)])])], True) else: dg19 = None if (ResidencePermit1.rstrip() != ""): dg20 = pack([(0x74, 0, [(0xA1, 0, [(0x0C, 0, ResidencePermit2)])])], True) else: dg20 = None if (dg21_param.rstrip() != ""): dg21 = dg21_param else: dg21 = None # If eid.append is not done for a DG, it results into required # SwError() with FileNotFound "6A82" APDU return code if dg1: eid.append(TransparentStructureEF(parent=eid, fid=0x0101, shortfid=0x01, data=dg1)) if dg2: eid.append(TransparentStructureEF(parent=eid, fid=0x0102, shortfid=0x02, data=dg2)) if dg3: eid.append(TransparentStructureEF(parent=eid, fid=0x0103, shortfid=0x03, data=dg3)) if dg4: eid.append(TransparentStructureEF(parent=eid, fid=0x0104, shortfid=0x04, data=dg4)) if dg5: eid.append(TransparentStructureEF(parent=eid, fid=0x0105, shortfid=0x05, data=dg5)) if dg6: eid.append(TransparentStructureEF(parent=eid, fid=0x0106, shortfid=0x06, data=dg6)) if dg7: eid.append(TransparentStructureEF(parent=eid, fid=0x0107, shortfid=0x07, data=dg7)) if dg8: eid.append(TransparentStructureEF(parent=eid, fid=0x0108, shortfid=0x08, data=dg8)) if dg9: eid.append(TransparentStructureEF(parent=eid, fid=0x0109, shortfid=0x09, data=dg9)) if dg10: eid.append(TransparentStructureEF(parent=eid, fid=0x010a, shortfid=0x0a, data=dg10)) if dg11: eid.append(TransparentStructureEF(parent=eid, fid=0x010b, shortfid=0x0b, data=dg11)) if dg12: eid.append(TransparentStructureEF(parent=eid, fid=0x010c, shortfid=0x0c, data=dg12)) if dg13: eid.append(TransparentStructureEF(parent=eid, fid=0x010d, shortfid=0x0d, data=dg13)) if dg14: eid.append(TransparentStructureEF(parent=eid, fid=0x010e, shortfid=0x0e, data=dg14)) if dg15: eid.append(TransparentStructureEF(parent=eid, fid=0x010f, shortfid=0x0f, data=dg15)) if dg16: eid.append(TransparentStructureEF(parent=eid, fid=0x0110, shortfid=0x10, data=dg16)) if dg17: eid.append(TransparentStructureEF(parent=eid, fid=0x0111, shortfid=0x11, data=dg17)) if dg18: eid.append(TransparentStructureEF(parent=eid, fid=0x0112, shortfid=0x12, data=dg18)) if dg19: eid.append(TransparentStructureEF(parent=eid, fid=0x0113, shortfid=0x13, data=dg19)) if dg20: eid.append(TransparentStructureEF(parent=eid, fid=0x0114, shortfid=0x14, data=dg20)) if dg21: eid.append(TransparentStructureEF(parent=eid, fid=0x0115, shortfid=0x15, data=dg21)) self.mf.append(eid) # DF.CIA cia = DF(parent=self.mf, fid=0xfffe, dfname=b'\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49' b'\x47\x4E') # EF.OD / EF.ODF cia.append(TransparentStructureEF(parent=cia, fid=0x5031, shortfid=0x11, data=b'\xa0\x060\x04\x04\x02D\x00\xa4' b'\x060\x04\x04\x02D\x04\xa8\x060\x04' b'\x04\x02D\x08')) # EF.CIAInfo / EF.TokenInfo cia.append(TransparentStructureEF(parent=cia, fid=0x5032, shortfid=0x12, data=b'06\x02\x01\x01\x80\x11eSign ' b'Application\x03\x02\x06\xc0\xa2' b'\x1a0\x18\x02\x01\x01\x02\x02\x10A' b'\x05\x00\x03\x02\x06@\x06\t\x04\x00' b'\x7f\x00\x07\x01\x01\x04\x01')) # EF.PrKD / EF.PrKDF cia.append(TransparentStructureEF(parent=cia, fid=0x4400, data=b'\xa080\x17\x0c\x0bPrK.ICC.QES' b'\x03\x02\x07\x80\x04\x01\x01\x02' b'\x01\x010\x15\x04\x01F\x03\x03\x06' b'\x00@\x03\x02\x03\xb8\x02\x02\x00' b'\x84\xa1\x03\x02\x01\x01\xa1\x060' b'\x040\x02\x04\x00')) # EF.PuKD / EF.PuKDF cia.append(TransparentStructureEF(parent=cia, fid=0x4404, data=b'050!\x0c\x1fZertifikat des ZDA' b' f\xc3\xbcr die QES0\x06\x04\x01E' b'\x01\x01\xff\xa1\x080\x060\x04\x04' b'\x02\xc0\x000:0&\x0c$Zertifikat des' b' Inhabers f\xc3\xbcr die QES0\x06' b'\x04\x01F\x01\x01\x00\xa1\x080\x060' b'\x04\x04\x02\xc0\x01')) # EF.AOD / EF.AODF cia.append(TransparentStructureEF(parent=cia, fid=0x4408, data=b'0/0\x0f\x0c\teSign-PIN\x03\x02' b'\x06@0\x03\x04\x01\x01\xa1\x170\x15' b'\x03\x03\x02H\x1c\n\x01\x01\x02\x01' b'\x06\x02\x01\x00\x80\x01\x810\x02' b'\x04\x00')) self.mf.append(cia) # DF.eSign esign = DF(parent=self.mf, fid=0xfffd, dfname=b'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E') # ZDA certificate esign.append(TransparentStructureEF(parent=esign, fid=0xC000, data=b'')) # User's certificate esign.append(TransparentStructureEF(parent=esign, fid=0xC001, data=b'')) self.mf.append(esign) self.sam = nPA_SAM(eid_pin="111111".encode('ascii'), can="222222".encode('ascii'), mrz="IDD<