#
# 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<