added more Python 3 compatibility
fixes https://github.com/frankmorgner/vsmartcard/issues/71
This commit is contained in:
@@ -58,8 +58,8 @@ class CardGenerator(object):
|
|||||||
self.datagroups = {}
|
self.datagroups = {}
|
||||||
|
|
||||||
def __generate_iso_card(self):
|
def __generate_iso_card(self):
|
||||||
default_pin = "1234"
|
default_pin = "1234".encode('ascii')
|
||||||
default_cardno = "1234567890"
|
default_cardno = "1234567890".encode('ascii')
|
||||||
|
|
||||||
logging.warning("Using default SAM parameters. PIN=%s, Card Nr=%s"
|
logging.warning("Using default SAM parameters. PIN=%s, Card Nr=%s"
|
||||||
% (default_pin, default_cardno))
|
% (default_pin, default_cardno))
|
||||||
@@ -603,10 +603,10 @@ class CardGenerator(object):
|
|||||||
esign.append(TransparentStructureEF(parent=esign, fid=0xC001, data=''))
|
esign.append(TransparentStructureEF(parent=esign, fid=0xC001, data=''))
|
||||||
self.mf.append(esign)
|
self.mf.append(esign)
|
||||||
|
|
||||||
self.sam = nPA_SAM(eid_pin="111111", can="222222",
|
self.sam = nPA_SAM(eid_pin="111111".encode('ascii'), can="222222".encode('ascii'),
|
||||||
mrz="IDD<<T220001293<<<<<<<<<<<<<<<6408125<2010315D"
|
mrz="IDD<<T220001293<<<<<<<<<<<<<<<6408125<2010315D"
|
||||||
"<<<<<<<<<<<<<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<",
|
"<<<<<<<<<<<<<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<".encode('ascii'),
|
||||||
puk="3333333333", qes_pin="444444", mf=self.mf)
|
puk="3333333333".encode('ascii'), qes_pin="444444".encode('ascii'), mf=self.mf)
|
||||||
# FIXME: add CVCA for inspection systems and signature terminals. Here
|
# FIXME: add CVCA for inspection systems and signature terminals. Here
|
||||||
# we only add the eID CVCA.
|
# we only add the eID CVCA.
|
||||||
self.sam.current_SE.cvca = "\x7f\x21\x82\x01\xb6\x7f\x4e\x82\x01\x6e"\
|
self.sam.current_SE.cvca = "\x7f\x21\x82\x01\xb6\x7f\x4e\x82\x01\x6e"\
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ except ImportError:
|
|||||||
import hmac as HMAC
|
import hmac as HMAC
|
||||||
import sha as SHA1
|
import sha as SHA1
|
||||||
|
|
||||||
CYBERFLEX_IV = '\x00' * 8
|
CYBERFLEX_IV = b'\x00' * 8
|
||||||
|
|
||||||
|
|
||||||
# *******************************************************************
|
# *******************************************************************
|
||||||
@@ -63,8 +63,10 @@ def get_cipher(cipherspec, key, iv=None):
|
|||||||
(cipherparts[1], validModes))
|
(cipherparts[1], validModes))
|
||||||
|
|
||||||
cipher = None
|
cipher = None
|
||||||
if iv is None:
|
if cipherparts[1].upper() == "ECB":
|
||||||
cipher = c_class.new(key, mode, '\x00'*get_cipher_blocklen(cipherspec))
|
cipher = c_class.new(key, mode)
|
||||||
|
elif iv is None:
|
||||||
|
cipher = c_class.new(key, mode, b'\x00'*get_cipher_blocklen(cipherspec))
|
||||||
else:
|
else:
|
||||||
cipher = c_class.new(key, mode, iv)
|
cipher = c_class.new(key, mode, iv)
|
||||||
|
|
||||||
@@ -114,9 +116,9 @@ def append_padding(blocklen, data, padding_class=0x01):
|
|||||||
last_block_length = len(data) % blocklen
|
last_block_length = len(data) % blocklen
|
||||||
padding_length = blocklen - last_block_length
|
padding_length = blocklen - last_block_length
|
||||||
if padding_length == 0:
|
if padding_length == 0:
|
||||||
padding = '\x80' + '\x00' * (blocklen - 1)
|
padding = b'\x80' + b'\x00' * (blocklen - 1)
|
||||||
else:
|
else:
|
||||||
padding = '\x80' + '\x00' * (blocklen - last_block_length - 1)
|
padding = b'\x80' + b'\x00' * (blocklen - last_block_length - 1)
|
||||||
|
|
||||||
return data + padding
|
return data + padding
|
||||||
|
|
||||||
@@ -127,8 +129,11 @@ def strip_padding(blocklen, data, padding_class=0x01):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if padding_class == 0x01:
|
if padding_class == 0x01:
|
||||||
tail = len(data) - 1
|
b = data
|
||||||
while data[tail] != '\x80':
|
if isinstance(b, str):
|
||||||
|
b = map(ord, b)
|
||||||
|
tail = len(b) - 1
|
||||||
|
while b[tail] != 0x80:
|
||||||
tail = tail - 1
|
tail = tail - 1
|
||||||
return data[:tail]
|
return data[:tail]
|
||||||
|
|
||||||
|
|||||||
@@ -158,10 +158,10 @@ class ControlReferenceTemplate:
|
|||||||
if position < len(self.__config_string): # Replace Tag
|
if position < len(self.__config_string): # Replace Tag
|
||||||
length = stringtoint(self.__config_string[position+1])
|
length = stringtoint(self.__config_string[position+1])
|
||||||
self.__config_string = self.__config_string[:position] +\
|
self.__config_string = self.__config_string[:position] +\
|
||||||
chr(tag) + inttostring(len(data)) + data +\
|
inttostring(tag) + inttostring(len(data)) + data +\
|
||||||
self.__config_string[position+2+length:]
|
self.__config_string[position+2+length:]
|
||||||
else: # Add new tag
|
else: # Add new tag
|
||||||
self.__config_string += chr(tag) + inttostring(len(data)) + data
|
self.__config_string += inttostring(tag) + inttostring(len(data)) + data
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -138,14 +138,14 @@ def write(old, newlist, offsets, datacoding, maxsize=None):
|
|||||||
resultindex = offset + newindex
|
resultindex = offset + newindex
|
||||||
while newindex < writenow:
|
while newindex < writenow:
|
||||||
if datacoding == DCB["WRITEOR"]:
|
if datacoding == DCB["WRITEOR"]:
|
||||||
newpiece = chr(
|
newpiece = inttostring(
|
||||||
ord(result[resultindex]) | ord(new[newindex]))
|
ord(result[resultindex]) | ord(new[newindex]))
|
||||||
elif datacoding == DCB["WRITEAND"]:
|
elif datacoding == DCB["WRITEAND"]:
|
||||||
newpiece = chr(
|
newpiece = inttostring(
|
||||||
ord(result[resultindex]) & ord(new[newindex]))
|
ord(result[resultindex]) & ord(new[newindex]))
|
||||||
elif datacoding == DCB["PROPRIETARY"]:
|
elif datacoding == DCB["PROPRIETARY"]:
|
||||||
# we use it for XOR
|
# we use it for XOR
|
||||||
newpiece = chr(
|
newpiece = inttostring(
|
||||||
ord(result[resultindex]) ^ ord(new[newindex]))
|
ord(result[resultindex]) ^ ord(new[newindex]))
|
||||||
result = (result[0:resultindex] + newpiece +
|
result = (result[0:resultindex] + newpiece +
|
||||||
result[resultindex+1:len(result)])
|
result[resultindex+1:len(result)])
|
||||||
@@ -614,24 +614,24 @@ class MF(DF):
|
|||||||
The result is not prepended with tag and length for neither TCP, FMD
|
The result is not prepended with tag and length for neither TCP, FMD
|
||||||
nor FCI template.
|
nor FCI template.
|
||||||
"""
|
"""
|
||||||
fdm = [chr(TAG["FILEIDENTIFIER"]) + "\x02" + inttostring(file.fid, 2),
|
fdm = [inttostring(TAG["FILEIDENTIFIER"]) + b"\x02" + inttostring(file.fid, 2),
|
||||||
chr(TAG["LIFECYCLESTATUS"]) + "\x01" + chr(file.lifecycle)]
|
inttostring(TAG["LIFECYCLESTATUS"]) + b"\x01" + inttostring(file.lifecycle)]
|
||||||
fdm.append(file.extra_fci_data)
|
fdm.append(file.extra_fci_data)
|
||||||
|
|
||||||
# TODO filesize and data objects
|
# TODO filesize and data objects
|
||||||
if isinstance(file, EF):
|
if isinstance(file, EF):
|
||||||
if hasattr(file, 'shortfid'):
|
if hasattr(file, 'shortfid'):
|
||||||
fdm.append("%c\x01%c" % (TAG["SHORTFID"], file.shortfid << 3))
|
fdm.append(b"%c\x01%c" % (TAG["SHORTFID"], file.shortfid << 3))
|
||||||
else:
|
else:
|
||||||
fdm.append("%c\x00" % TAG["SHORTFID"])
|
fdm.append(b"%c\x00" % TAG["SHORTFID"])
|
||||||
|
|
||||||
if isinstance(file, TransparentStructureEF):
|
if isinstance(file, TransparentStructureEF):
|
||||||
l = inttostring(len(file.data), 2, True)
|
l = inttostring(len(file.data), 2, True)
|
||||||
fdm.append("%c%c%s" % (TAG["BYTES_EXCLUDINGSTRUCTURE"],
|
fdm.append(b"%c%c%s" % (TAG["BYTES_EXCLUDINGSTRUCTURE"],
|
||||||
chr(len(l)), l))
|
inttostring(len(l)), l))
|
||||||
fdm.append("%c%c%s" % (TAG["BYTES_INCLUDINGSTRUCTURE"],
|
fdm.append(b"%c%c%s" % (TAG["BYTES_INCLUDINGSTRUCTURE"],
|
||||||
chr(len(l)), l))
|
inttostring(len(l)), l))
|
||||||
fdm.append("%c\x02%c%c" % (TAG["FILEDISCRIPTORBYTE"],
|
fdm.append(b"%c\x02%c%c" % (TAG["FILEDISCRIPTORBYTE"],
|
||||||
file.filedescriptor, file.datacoding))
|
file.filedescriptor, file.datacoding))
|
||||||
|
|
||||||
elif isinstance(file, RecordStructureEF):
|
elif isinstance(file, RecordStructureEF):
|
||||||
@@ -664,7 +664,7 @@ class MF(DF):
|
|||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
|
||||||
return "".join(fdm)
|
return b"".join(fdm)
|
||||||
|
|
||||||
def _selectFile(self, p1, p2, data):
|
def _selectFile(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -735,10 +735,10 @@ class MF(DF):
|
|||||||
file = self._selectFile(p1, p2, data)
|
file = self._selectFile(p1, p2, data)
|
||||||
|
|
||||||
if p2 == P2_NONE:
|
if p2 == P2_NONE:
|
||||||
data = ""
|
data = b""
|
||||||
elif p2 == P2_FMD:
|
elif p2 == P2_FMD:
|
||||||
# TODO
|
# TODO
|
||||||
data = ""
|
data = b""
|
||||||
else:
|
else:
|
||||||
if p2 == P2_FCP:
|
if p2 == P2_FCP:
|
||||||
tag = TAG["FILECONTROLPARAMETERS"]
|
tag = TAG["FILECONTROLPARAMETERS"]
|
||||||
@@ -846,7 +846,7 @@ class MF(DF):
|
|||||||
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data)
|
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data)
|
||||||
ef.writebinary(offsets, datalist)
|
ef.writebinary(offsets, datalist)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def writeBinaryEncapsulated(self, p1, p2, data):
|
def writeBinaryEncapsulated(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -857,7 +857,7 @@ class MF(DF):
|
|||||||
ef, offsets, datalist = self.dataUnitsDecodeEncapsulated(p1, p2, data)
|
ef, offsets, datalist = self.dataUnitsDecodeEncapsulated(p1, p2, data)
|
||||||
ef.writebinary(offsets, datalist)
|
ef.writebinary(offsets, datalist)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def updateBinaryPlain(self, p1, p2, data):
|
def updateBinaryPlain(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -870,7 +870,7 @@ class MF(DF):
|
|||||||
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data)
|
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data)
|
||||||
ef.updatebinary(offsets, datalist)
|
ef.updatebinary(offsets, datalist)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def updateBinaryEncapsulated(self, p1, p2, data):
|
def updateBinaryEncapsulated(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -883,7 +883,7 @@ class MF(DF):
|
|||||||
ef, offsets, datalist = self.dataUnitsDecodeEncapsulated(p1, p2, data)
|
ef, offsets, datalist = self.dataUnitsDecodeEncapsulated(p1, p2, data)
|
||||||
ef.updatebinary(offsets, datalist)
|
ef.updatebinary(offsets, datalist)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def searchBinaryPlain(self, p1, p2, data):
|
def searchBinaryPlain(self, p1, p2, data):
|
||||||
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data)
|
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data)
|
||||||
@@ -929,7 +929,7 @@ class MF(DF):
|
|||||||
eraseto = None
|
eraseto = None
|
||||||
|
|
||||||
ef.erasebinary(erasefrom, eraseto)
|
ef.erasebinary(erasefrom, eraseto)
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def eraseBinaryEncapsulated(self, p1, p2, data):
|
def eraseBinaryEncapsulated(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -959,7 +959,7 @@ class MF(DF):
|
|||||||
erasefrom = None
|
erasefrom = None
|
||||||
|
|
||||||
ef.erasebinary(erasefrom, eraseto)
|
ef.erasebinary(erasefrom, eraseto)
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def recordHandlingDecode(self, p1, p2):
|
def recordHandlingDecode(self, p1, p2):
|
||||||
"""
|
"""
|
||||||
@@ -1002,7 +1002,7 @@ class MF(DF):
|
|||||||
ef, num_id, reference = self.recordHandlingDecode(p1, p2)
|
ef, num_id, reference = self.recordHandlingDecode(p1, p2)
|
||||||
result = ef.readrecord(0, num_id, reference)
|
result = ef.readrecord(0, num_id, reference)
|
||||||
|
|
||||||
r = ""
|
r = b""
|
||||||
for item in result:
|
for item in result:
|
||||||
r += item
|
r += item
|
||||||
|
|
||||||
@@ -1039,7 +1039,7 @@ class MF(DF):
|
|||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
ef.writerecord(num_id, reference, 1, data)
|
ef.writerecord(num_id, reference, 1, data)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def updateRecordPlain(self, p1, p2, data):
|
def updateRecordPlain(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1059,7 +1059,7 @@ class MF(DF):
|
|||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
ef.updaterecord(num_id, reference, 0, data)
|
ef.updaterecord(num_id, reference, 0, data)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def updateRecordEncapsulated(self, p1, p2, data):
|
def updateRecordEncapsulated(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1104,7 +1104,7 @@ class MF(DF):
|
|||||||
decodeDiscretionaryDataObjects(tlv_data)[0],
|
decodeDiscretionaryDataObjects(tlv_data)[0],
|
||||||
DCB["PROPRIETARY"])
|
DCB["PROPRIETARY"])
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def appendRecord(self, p1, p2, data):
|
def appendRecord(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1119,7 +1119,7 @@ class MF(DF):
|
|||||||
ef, num_id, reference = self.recordHandlingDecode(p1, p2)
|
ef, num_id, reference = self.recordHandlingDecode(p1, p2)
|
||||||
sw = ef.appendrecord(data)
|
sw = ef.appendrecord(data)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def eraseRecord(self, p1, p2, data):
|
def eraseRecord(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1137,7 +1137,7 @@ class MF(DF):
|
|||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
ef.eraserecord(num_id, reference)
|
ef.eraserecord(num_id, reference)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def dataObjectHandlingDecodePlain(self, p1, p2, data):
|
def dataObjectHandlingDecodePlain(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1280,7 +1280,7 @@ class MF(DF):
|
|||||||
data)
|
data)
|
||||||
file.putdata(isSimpleTlv, tlvlist)
|
file.putdata(isSimpleTlv, tlvlist)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def putDataEncapsulated(self, p1, p2, data):
|
def putDataEncapsulated(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1293,7 +1293,7 @@ class MF(DF):
|
|||||||
file, tlvlist = self.dataObjectHandlingDecodeEncapsulated(p1, p2, data)
|
file, tlvlist = self.dataObjectHandlingDecodeEncapsulated(p1, p2, data)
|
||||||
file.putdata(False, tlvlist)
|
file.putdata(False, tlvlist)
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(p1, p2, data):
|
def create(p1, p2, data):
|
||||||
@@ -1331,9 +1331,9 @@ class MF(DF):
|
|||||||
TAG["SHORTFID"]: 'shortfid2args(value, args)',
|
TAG["SHORTFID"]: 'shortfid2args(value, args)',
|
||||||
TAG["LIFECYCLESTATUS"]: 'args["lifecycle"] = ' + \
|
TAG["LIFECYCLESTATUS"]: 'args["lifecycle"] = ' + \
|
||||||
'stringtoint(value)',
|
'stringtoint(value)',
|
||||||
TAG["BYTES_EXCLUDINGSTRUCTURE"]: 'args["data"] = chr(0) ' + \
|
TAG["BYTES_EXCLUDINGSTRUCTURE"]: 'args["data"] = bytes(0) ' + \
|
||||||
'* stringtoint(value)',
|
'* stringtoint(value)',
|
||||||
TAG["BYTES_INCLUDINGSTRUCTURE"]: 'args["data"] = chr(0) ' + \
|
TAG["BYTES_INCLUDINGSTRUCTURE"]: 'args["data"] = bytes(0) ' + \
|
||||||
'* stringtoint(value)',
|
'* stringtoint(value)',
|
||||||
}
|
}
|
||||||
fcp_list = tlv_find_tags(bertlv_unpack(data),
|
fcp_list = tlv_find_tags(bertlv_unpack(data),
|
||||||
@@ -1392,7 +1392,7 @@ class MF(DF):
|
|||||||
df.append(file)
|
df.append(file)
|
||||||
self.current = file
|
self.current = file
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
def deleteFile(self, p1, p2, data):
|
def deleteFile(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -1407,7 +1407,7 @@ class MF(DF):
|
|||||||
# FIXME: free memory of file and remove its content from the security
|
# FIXME: free memory of file and remove its content from the security
|
||||||
# device
|
# device
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
|
|
||||||
|
|
||||||
class EF(File):
|
class EF(File):
|
||||||
@@ -1738,7 +1738,7 @@ class RecordStructureEF(EF):
|
|||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
if self.hasFixedRecordSize():
|
if self.hasFixedRecordSize():
|
||||||
data = chr(0)*(self.maxrecordsize) + data
|
data = bytes(0)*(self.maxrecordsize) + data
|
||||||
|
|
||||||
records = self.records
|
records = self.records
|
||||||
if self.isCyclic():
|
if self.isCyclic():
|
||||||
@@ -1756,6 +1756,6 @@ class RecordStructureEF(EF):
|
|||||||
"""
|
"""
|
||||||
records = self.__getRecords(num_id, reference)
|
records = self.__getRecords(num_id, reference)
|
||||||
for r in records:
|
for r in records:
|
||||||
r.data = ""
|
r.data = b""
|
||||||
r.identifier = None
|
r.identifier = None
|
||||||
return SW["NORMAL"]
|
return SW["NORMAL"]
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class SAM(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
logging.debug("Received PIN: %s", PIN.strip())
|
logging.debug("Received PIN: %s", PIN.strip())
|
||||||
PIN = PIN.replace("\0", "") # Strip NULL characters
|
PIN = PIN.replace(b"\0", b"") # Strip NULL characters
|
||||||
|
|
||||||
if p1 != 0x00:
|
if p1 != 0x00:
|
||||||
raise SwError(SW["ERR_INCORRECTP1P2"])
|
raise SwError(SW["ERR_INCORRECTP1P2"])
|
||||||
|
|||||||
@@ -51,26 +51,26 @@ TAG["EXTENDED_HEADER_LIST"] = 0x4D
|
|||||||
|
|
||||||
|
|
||||||
def tlv_unpack(data):
|
def tlv_unpack(data):
|
||||||
ber_class = (ord(data[0]) & 0xC0) >> 6
|
ber_class = (data[0] & 0xC0) >> 6
|
||||||
# 0 = primitive, 0x20 = constructed
|
# 0 = primitive, 0x20 = constructed
|
||||||
constructed = (ord(data[0]) & 0x20) != 0
|
constructed = (data[0] & 0x20) != 0
|
||||||
tag = ord(data[0])
|
tag = data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
if (tag & 0x1F) == 0x1F:
|
if (tag & 0x1F) == 0x1F:
|
||||||
tag = (tag << 8) | ord(data[0])
|
tag = (tag << 8) | data[0]
|
||||||
while ord(data[0]) & 0x80 == 0x80:
|
while data[0] & 0x80 == 0x80:
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
tag = (tag << 8) | ord(data[0])
|
tag = (tag << 8) | data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
|
|
||||||
length = ord(data[0])
|
length = data[0]
|
||||||
if length < 0x80:
|
if length < 0x80:
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
elif length & 0x80 == 0x80:
|
elif length & 0x80 == 0x80:
|
||||||
length_ = 0
|
length_ = 0
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
for i in range(0, length & 0x7F):
|
for i in range(0, length & 0x7F):
|
||||||
length_ = length_ * 256 + ord(data[0])
|
length_ = length_ * 256 + data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
length = length_
|
length = length_
|
||||||
|
|
||||||
@@ -157,13 +157,15 @@ def bertlv_pack(data):
|
|||||||
|
|
||||||
def unpack(data, with_marks=None, offset=0, include_filler=False):
|
def unpack(data, with_marks=None, offset=0, include_filler=False):
|
||||||
result = []
|
result = []
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = map(ord, data)
|
||||||
while len(data) > 0:
|
while len(data) > 0:
|
||||||
if ord(data[0]) in (0x00, 0xFF):
|
if data[0] in (0x00, 0xFF):
|
||||||
if include_filler:
|
if include_filler:
|
||||||
if with_marks is None:
|
if with_marks is None:
|
||||||
result.append((ord(data[0]), None, None))
|
result.append((data[0], None, None))
|
||||||
else:
|
else:
|
||||||
result.append((ord(data[0]), None, None, ()))
|
result.append((data[0], None, None, ()))
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
offset = offset + 1
|
offset = offset + 1
|
||||||
continue
|
continue
|
||||||
@@ -226,15 +228,17 @@ def simpletlv_unpack(data):
|
|||||||
"""Unpacks a simpletlv coded string into a list of 3-tuples (tag, length,
|
"""Unpacks a simpletlv coded string into a list of 3-tuples (tag, length,
|
||||||
newvalue)."""
|
newvalue)."""
|
||||||
result = []
|
result = []
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = map(ord, data)
|
||||||
rest = data
|
rest = data
|
||||||
while rest != '':
|
while rest:
|
||||||
tag = ord(rest[0])
|
tag = rest[0]
|
||||||
if tag == 0 or tag == 0xff:
|
if tag == 0 or tag == 0xff:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
length = ord(rest[1])
|
length = rest[1]
|
||||||
if length == 0xff:
|
if length == 0xff:
|
||||||
length = (ord(rest[2]) << 8) + ord(rest[3])
|
length = (rest[2] << 8) + rest[3]
|
||||||
newvalue = rest[4:4+length]
|
newvalue = rest[4:4+length]
|
||||||
rest = rest[4+length:]
|
rest = rest[4+length:]
|
||||||
else:
|
else:
|
||||||
@@ -265,14 +269,14 @@ def decodeOffsetDataObjects(tlv_data):
|
|||||||
def decodeTagList(tlv_data):
|
def decodeTagList(tlv_data):
|
||||||
taglist = []
|
taglist = []
|
||||||
for (t, l, data) in tlv_find_tag(tlv_data, TAG["TAG_LIST"]):
|
for (t, l, data) in tlv_find_tag(tlv_data, TAG["TAG_LIST"]):
|
||||||
while data != "":
|
while data:
|
||||||
tag = ord(data[0])
|
tag = data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
if (tag & 0x1F) == 0x1F:
|
if (tag & 0x1F) == 0x1F:
|
||||||
tag = (tag << 8) | ord(data[0])
|
tag = (tag << 8) | data[0]
|
||||||
while ord(data[0]) & 0x80 == 0x80:
|
while data[0] & 0x80 == 0x80:
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
tag = (tag << 8) | ord(data[0])
|
tag = (tag << 8) | data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
taglist.append((tag, 0))
|
taglist.append((tag, 0))
|
||||||
return taglist
|
return taglist
|
||||||
@@ -281,24 +285,24 @@ def decodeTagList(tlv_data):
|
|||||||
def decodeHeaderList(tlv_data):
|
def decodeHeaderList(tlv_data):
|
||||||
headerlist = []
|
headerlist = []
|
||||||
for (t, l, data) in tlv_find_tag(tlv_data, TAG["HEADER_LIST"]):
|
for (t, l, data) in tlv_find_tag(tlv_data, TAG["HEADER_LIST"]):
|
||||||
while data != "":
|
while data:
|
||||||
tag = ord(data[0])
|
tag = data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
if (tag & 0x1F) == 0x1F:
|
if (tag & 0x1F) == 0x1F:
|
||||||
tag = (tag << 8) | ord(data[0])
|
tag = (tag << 8) | data[0]
|
||||||
while ord(data[0]) & 0x80 == 0x80:
|
while data[0] & 0x80 == 0x80:
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
tag = (tag << 8) | ord(data[0])
|
tag = (tag << 8) | data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
|
|
||||||
length = ord(data[0])
|
length = data[0]
|
||||||
if length < 0x80:
|
if length < 0x80:
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
elif length & 0x80 == 0x80:
|
elif length & 0x80 == 0x80:
|
||||||
length_ = 0
|
length_ = 0
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
for i in range(0, length & 0x7F):
|
for i in range(0, length & 0x7F):
|
||||||
length_ = length_ * 256 + ord(data[0])
|
length_ = length_ * 256 + data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
length = length_
|
length = length_
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,9 @@ from virtualsmartcard.CardGenerator import CardGenerator
|
|||||||
|
|
||||||
class SmartcardOS(object):
|
class SmartcardOS(object):
|
||||||
"""Base class for a smart card OS"""
|
"""Base class for a smart card OS"""
|
||||||
|
|
||||||
def getATR(self):
|
def getATR(self):
|
||||||
"""Returns the ATR of the card as string of characters"""
|
"""Returns the ATR of the card as string of characters"""
|
||||||
return ""
|
return b""
|
||||||
|
|
||||||
def powerUp(self):
|
def powerUp(self):
|
||||||
"""Powers up the card"""
|
"""Powers up the card"""
|
||||||
@@ -54,7 +53,7 @@ class SmartcardOS(object):
|
|||||||
|
|
||||||
:param msg: the APDU as string of characters
|
:param msg: the APDU as string of characters
|
||||||
"""
|
"""
|
||||||
return ""
|
return b""
|
||||||
|
|
||||||
|
|
||||||
class Iso7816OS(SmartcardOS):
|
class Iso7816OS(SmartcardOS):
|
||||||
@@ -110,14 +109,14 @@ class Iso7816OS(SmartcardOS):
|
|||||||
else:
|
else:
|
||||||
self.maxle = MAX_SHORT_LE
|
self.maxle = MAX_SHORT_LE
|
||||||
|
|
||||||
self.lastCommandOffcut = ""
|
self.lastCommandOffcut = b""
|
||||||
self.lastCommandSW = SW["NORMAL"]
|
self.lastCommandSW = SW["NORMAL"]
|
||||||
el = extended_length # only needed to keep following line short
|
el = extended_length # only needed to keep following line short
|
||||||
tsft = Iso7816OS.makeThirdSoftwareFunctionTable(extendedLe=el)
|
tsft = Iso7816OS.makeThirdSoftwareFunctionTable(extendedLe=el)
|
||||||
card_capabilities = self.mf.firstSFT + self.mf.secondSFT + tsft
|
card_capabilities = self.mf.firstSFT + self.mf.secondSFT + tsft
|
||||||
self.atr = Iso7816OS.makeATR(T=1, directConvention=True, TA1=0x13,
|
self.atr = Iso7816OS.makeATR(T=1, directConvention=True, TA1=0x13,
|
||||||
histChars=chr(0x80) +
|
histChars=inttostring(0x80) +
|
||||||
chr(0x70 + len(card_capabilities)) +
|
inttostring(0x70 + len(card_capabilities)) +
|
||||||
card_capabilities)
|
card_capabilities)
|
||||||
|
|
||||||
def getATR(self):
|
def getATR(self):
|
||||||
@@ -145,9 +144,9 @@ class Iso7816OS(SmartcardOS):
|
|||||||
"""
|
"""
|
||||||
# first byte TS
|
# first byte TS
|
||||||
if args["directConvention"]:
|
if args["directConvention"]:
|
||||||
atr = "\x3b"
|
atr = b"\x3b"
|
||||||
else:
|
else:
|
||||||
atr = "\x3f"
|
atr = b"\x3f"
|
||||||
|
|
||||||
if "T" in args:
|
if "T" in args:
|
||||||
T = args["T"]
|
T = args["T"]
|
||||||
@@ -189,11 +188,11 @@ class Iso7816OS(SmartcardOS):
|
|||||||
|
|
||||||
# add TDi, TAi, TBi and TCi to ATR (TD0 is actually T0)
|
# add TDi, TAi, TBi and TCi to ATR (TD0 is actually T0)
|
||||||
for i in range(0, maxTD+1):
|
for i in range(0, maxTD+1):
|
||||||
atr = atr + "%c" % args["TD" + str(i)]
|
atr = atr + b"%c" % args["TD" + str(i)]
|
||||||
TCK ^= args["TD" + str(i)]
|
TCK ^= args["TD" + str(i)]
|
||||||
for j in ["A", "B", "C"]:
|
for j in ["A", "B", "C"]:
|
||||||
if "T" + j + str(i+1) in args:
|
if "T" + j + str(i+1) in args:
|
||||||
atr += "%c" % args["T" + j + str(i+1)]
|
atr += b"%c" % args["T" + j + str(i+1)]
|
||||||
# calculate checksum for all bytes from T0 to the end
|
# calculate checksum for all bytes from T0 to the end
|
||||||
TCK ^= args["T" + j + str(i+1)]
|
TCK ^= args["T" + j + str(i+1)]
|
||||||
|
|
||||||
@@ -201,11 +200,15 @@ class Iso7816OS(SmartcardOS):
|
|||||||
if "histChars" in args:
|
if "histChars" in args:
|
||||||
atr += args["histChars"]
|
atr += args["histChars"]
|
||||||
for i in range(0, len(args["histChars"])):
|
for i in range(0, len(args["histChars"])):
|
||||||
TCK ^= ord(args["histChars"][i])
|
byte = args["histChars"][i]
|
||||||
|
if isinstance(byte, str):
|
||||||
|
TCK ^= ord(byte)
|
||||||
|
else:
|
||||||
|
TCK ^= byte
|
||||||
|
|
||||||
# checksum is omitted for T=0
|
# checksum is omitted for T=0
|
||||||
if T > 0:
|
if T > 0:
|
||||||
atr += "%c" % TCK
|
atr += b"%c" % TCK
|
||||||
|
|
||||||
return atr
|
return atr
|
||||||
|
|
||||||
@@ -283,7 +286,7 @@ class Iso7816OS(SmartcardOS):
|
|||||||
c = C_APDU(msg)
|
c = C_APDU(msg)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logging.warning(str(e))
|
logging.warning(str(e))
|
||||||
return self.formatResult(False, 0, "",
|
return self.formatResult(False, 0, b"",
|
||||||
SW["ERR_INCORRECTPARAMETERS"], False)
|
SW["ERR_INCORRECTPARAMETERS"], False)
|
||||||
|
|
||||||
logging.info("Parsed APDU:\n%s", str(c))
|
logging.info("Parsed APDU:\n%s", str(c))
|
||||||
@@ -353,7 +356,7 @@ class Iso7816OS(SmartcardOS):
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exception(*sys.exc_info())
|
traceback.print_exception(*sys.exc_info())
|
||||||
sw = e.sw
|
sw = e.sw
|
||||||
result = ""
|
result = b""
|
||||||
answer = self.formatResult(False, 0, result, sw, sm)
|
answer = self.formatResult(False, 0, result, sw, sm)
|
||||||
|
|
||||||
return answer
|
return answer
|
||||||
@@ -454,7 +457,7 @@ class VirtualICC(object):
|
|||||||
try:
|
try:
|
||||||
local_ip = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
|
local_ip = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
|
||||||
custom_url = 'vicc://%s:%d' % (local_ip, port)
|
custom_url = 'vicc://%s:%d' % (local_ip, port)
|
||||||
print('VICC hostname: %s' % local_ip);
|
print('VICC hostname: %s' % local_ip)
|
||||||
print('VICC port: %d' % port)
|
print('VICC port: %d' % port)
|
||||||
print('On your NFC phone with the Android Smart Card Emulator app scan this code:')
|
print('On your NFC phone with the Android Smart Card Emulator app scan this code:')
|
||||||
try:
|
try:
|
||||||
@@ -553,16 +556,16 @@ class VirtualICC(object):
|
|||||||
logging.warning("Error in communication protocol (missing \
|
logging.warning("Error in communication protocol (missing \
|
||||||
size parameter)")
|
size parameter)")
|
||||||
elif size == VPCD_CTRL_LEN:
|
elif size == VPCD_CTRL_LEN:
|
||||||
if msg == chr(VPCD_CTRL_OFF):
|
if msg == inttostring(VPCD_CTRL_OFF):
|
||||||
logging.info("Power Down")
|
logging.info("Power Down")
|
||||||
self.os.powerDown()
|
self.os.powerDown()
|
||||||
elif msg == chr(VPCD_CTRL_ON):
|
elif msg == inttostring(VPCD_CTRL_ON):
|
||||||
logging.info("Power Up")
|
logging.info("Power Up")
|
||||||
self.os.powerUp()
|
self.os.powerUp()
|
||||||
elif msg == chr(VPCD_CTRL_RESET):
|
elif msg == inttostring(VPCD_CTRL_RESET):
|
||||||
logging.info("Reset")
|
logging.info("Reset")
|
||||||
self.os.reset()
|
self.os.reset()
|
||||||
elif msg == chr(VPCD_CTRL_ATR):
|
elif msg == inttostring(VPCD_CTRL_ATR):
|
||||||
self.__sendToVPICC(self.os.getATR())
|
self.__sendToVPICC(self.os.getATR())
|
||||||
else:
|
else:
|
||||||
logging.warning("unknown control command")
|
logging.warning("unknown control command")
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class HandlerTestOS(SmartcardOS):
|
|||||||
|
|
||||||
def __output_from_le(self, msg):
|
def __output_from_le(self, msg):
|
||||||
le = (ord(msg[2]) << 8) + ord(msg[3])
|
le = (ord(msg[2]) << 8) + ord(msg[3])
|
||||||
return ''.join([chr(num & 0xff) for num in xrange(le)])
|
return ''.join([bytes(num & 0xff) for num in xrange(le)])
|
||||||
|
|
||||||
def execute(self, msg):
|
def execute(self, msg):
|
||||||
ok = '\x90\x00'
|
ok = '\x90\x00'
|
||||||
@@ -67,7 +67,7 @@ class HandlerTestOS(SmartcardOS):
|
|||||||
self.lastCommandOffcut = self.__output_from_le(msg)
|
self.lastCommandOffcut = self.__output_from_le(msg)
|
||||||
if len(self.lastCommandOffcut) > 0xFF:
|
if len(self.lastCommandOffcut) > 0xFF:
|
||||||
return '\x61\x00'
|
return '\x61\x00'
|
||||||
return '' + chr(len(self.lastCommandOffcut) & 0xFF)
|
return '' + bytes(len(self.lastCommandOffcut) & 0xFF)
|
||||||
elif len(msg) == 6+ord(msg[4]):
|
elif len(msg) == 6+ord(msg[4]):
|
||||||
logging.info('Case 4, APDU')
|
logging.info('Case 4, APDU')
|
||||||
return self.__output_from_le(msg) + ok
|
return self.__output_from_le(msg) + ok
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class RelayOS(SmartcardOS):
|
|||||||
logging.error("Error getting ATR: %s", e.message)
|
logging.error("Error getting ATR: %s", e.message)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
return "".join([chr(b) for b in atr])
|
return b"".join([bytes(b) for b in atr])
|
||||||
|
|
||||||
def powerUp(self):
|
def powerUp(self):
|
||||||
# When powerUp is called multiple times the session is valid (and the
|
# When powerUp is called multiple times the session is valid (and the
|
||||||
@@ -131,4 +131,4 @@ class RelayOS(SmartcardOS):
|
|||||||
rapdu = rapdu + [sw1, sw2]
|
rapdu = rapdu + [sw1, sw2]
|
||||||
|
|
||||||
# return the response APDU as string
|
# return the response APDU as string
|
||||||
return "".join([chr(b) for b in rapdu])
|
return "".join([bytes(b) for b in rapdu])
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class CryptoflexMF(MF): # {{{
|
|||||||
"fid": stringtoint(data[4:6]),
|
"fid": stringtoint(data[4:6]),
|
||||||
}
|
}
|
||||||
if data[6] == "\x01":
|
if data[6] == "\x01":
|
||||||
args["data"] = chr(0)*stringtoint(data[2:4])
|
args["data"] = bytes(0)*stringtoint(data[2:4])
|
||||||
args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
|
args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
|
||||||
new_file = TransparentStructureEF(**args)
|
new_file = TransparentStructureEF(**args)
|
||||||
elif data[6] == "\x02":
|
elif data[6] == "\x02":
|
||||||
@@ -259,11 +259,11 @@ class CryptoflexMF(MF): # {{{
|
|||||||
if isinstance(file, EF):
|
if isinstance(file, EF):
|
||||||
# File size (body only)
|
# File size (body only)
|
||||||
size = inttostring(min(0xffff, len(file.getenc('data'))), 2)
|
size = inttostring(min(0xffff, len(file.getenc('data'))), 2)
|
||||||
extra = chr(0) # RFU
|
extra = bytes(0) # RFU
|
||||||
if (isinstance(file, RecordStructureEF) and
|
if (isinstance(file, RecordStructureEF) and
|
||||||
file.hasFixedRecordSize() and not file.isCyclic()):
|
file.hasFixedRecordSize() and not file.isCyclic()):
|
||||||
# Length of records
|
# Length of records
|
||||||
extra += chr(0) + chr(min(file.records, 0xff))
|
extra += bytes(0) + bytes(min(file.records, 0xff))
|
||||||
elif isinstance(file, DF):
|
elif isinstance(file, DF):
|
||||||
# Number of unused EEPROM bytes available in the DF
|
# Number of unused EEPROM bytes available in the DF
|
||||||
size = inttostring(0xffff, 2)
|
size = inttostring(0xffff, 2)
|
||||||
@@ -285,27 +285,27 @@ class CryptoflexMF(MF): # {{{
|
|||||||
if isinstance(f, DF):
|
if isinstance(f, DF):
|
||||||
dfcount += 1
|
dfcount += 1
|
||||||
if chv1:
|
if chv1:
|
||||||
extra = chr(1) # TODO LSB correct?
|
extra = bytes(1) # TODO LSB correct?
|
||||||
else:
|
else:
|
||||||
extra = chr(0) # TODO LSB correct?
|
extra = bytes(0) # TODO LSB correct?
|
||||||
extra += chr(efcount)
|
extra += bytes(efcount)
|
||||||
extra += chr(dfcount)
|
extra += bytes(dfcount)
|
||||||
extra += chr(0) # TODO Number of PINs and unblock CHV PINs
|
extra += bytes(0) # TODO Number of PINs and unblock CHV PINs
|
||||||
extra += chr(0) # RFU
|
extra += bytes(0) # RFU
|
||||||
if chv1:
|
if chv1:
|
||||||
extra += chr(0) # TODO remaining CHV1 attempts
|
extra += bytes(0) # TODO remaining CHV1 attempts
|
||||||
extra += chr(0) # TODO remaining unblock CHV1 attempts
|
extra += bytes(0) # TODO remaining unblock CHV1 attempts
|
||||||
if chv2:
|
if chv2:
|
||||||
extra += chr(0) # TODO CHV2 key status
|
extra += bytes(0) # TODO CHV2 key status
|
||||||
extra += chr(0) # TODO CHV2 unblocking key status
|
extra += bytes(0) # TODO CHV2 unblocking key status
|
||||||
|
|
||||||
data = inttostring(0, 2) # RFU
|
data = inttostring(0, 2) # RFU
|
||||||
data += size
|
data += size
|
||||||
data += inttostring(file.fid, 2)
|
data += inttostring(file.fid, 2)
|
||||||
data += inttostring(file.filedescriptor) # File type
|
data += inttostring(file.filedescriptor) # File type
|
||||||
data += inttostring(0, 4) # ACs TODO
|
data += inttostring(0, 4) # ACs TODO
|
||||||
data += chr(file.lifecycle & 1) # File status
|
data += bytes(file.lifecycle & 1) # File status
|
||||||
data += chr(len(extra))
|
data += bytes(len(extra))
|
||||||
data += extra
|
data += extra
|
||||||
|
|
||||||
self.current = file
|
self.current = file
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ class NPAOS(Iso7816OS):
|
|||||||
# different ATR (Answer To Reset) values depending on used Chip version
|
# different ATR (Answer To Reset) values depending on used Chip version
|
||||||
# It's just a playground, because in past one of all those eID clients
|
# It's just a playground, because in past one of all those eID clients
|
||||||
# did not recognize the card correctly with newest ATR values
|
# did not recognize the card correctly with newest ATR values
|
||||||
self.atr = '\x3B\x8A\x80\x01\x80\x31\xF8\x73\xF7\x41\xE0\x82\x90' + \
|
self.atr = b'\x3B\x8A\x80\x01\x80\x31\xF8\x73\xF7\x41\xE0\x82\x90' + \
|
||||||
'\x00\x75'
|
b'\x00\x75'
|
||||||
# self.atr = '\x3B\x8A\x80\x01\x80\x31\xB8\x73\x84\x01\xE0\x82\x90' + \
|
# self.atr = b'\x3B\x8A\x80\x01\x80\x31\xB8\x73\x84\x01\xE0\x82\x90' + \
|
||||||
# '\x00\x06'
|
# b'\x00\x06'
|
||||||
# self.atr = '\x3B\x88\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x09'
|
# self.atr = b'\x3B\x88\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
# self.atr = '\x3B\x87\x80\x01\x80\x31\xB8\x73\x84\x01\xE0\x19'
|
# self.atr = b'\x3B\x87\x80\x01\x80\x31\xB8\x73\x84\x01\xE0\x19'
|
||||||
|
|
||||||
self.SAM.current_SE.disable_checks = disable_checks
|
self.SAM.current_SE.disable_checks = disable_checks
|
||||||
if ef_cardsecurity:
|
if ef_cardsecurity:
|
||||||
@@ -67,7 +67,7 @@ class NPAOS(Iso7816OS):
|
|||||||
if ca_key:
|
if ca_key:
|
||||||
self.SAM.current_SE.ca_key = ca_key
|
self.SAM.current_SE.ca_key = ca_key
|
||||||
esign = self.mf.select('dfname',
|
esign = self.mf.select('dfname',
|
||||||
'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
|
b'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
|
||||||
if esign_ca_cert:
|
if esign_ca_cert:
|
||||||
ef = esign.select('fid', 0xC000)
|
ef = esign.select('fid', 0xC000)
|
||||||
ef.data = esign_ca_cert
|
ef.data = esign_ca_cert
|
||||||
@@ -95,7 +95,7 @@ class NPAOS(Iso7816OS):
|
|||||||
logging.info(e.message)
|
logging.info(e.message)
|
||||||
traceback.print_exception(*sys.exc_info())
|
traceback.print_exception(*sys.exc_info())
|
||||||
sw = e.sw
|
sw = e.sw
|
||||||
result = ""
|
result = b""
|
||||||
answer = self.formatResult(False, 0, result, sw, False)
|
answer = self.formatResult(False, 0, result, sw, False)
|
||||||
|
|
||||||
return R_APDU(result, inttostring(sw)).render()
|
return R_APDU(result, inttostring(sw)).render()
|
||||||
@@ -116,22 +116,22 @@ class nPA_AT_CRT(ControlReferenceTemplate):
|
|||||||
CommunityID = None
|
CommunityID = None
|
||||||
|
|
||||||
def keyref_is_mrz(self):
|
def keyref_is_mrz(self):
|
||||||
if self.keyref_secret_key == '%c' % self.PACE_MRZ:
|
if self.keyref_secret_key == b'%c' % self.PACE_MRZ:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def keyref_is_can(self):
|
def keyref_is_can(self):
|
||||||
if self.keyref_secret_key == '%c' % self.PACE_CAN:
|
if self.keyref_secret_key == b'%c' % self.PACE_CAN:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def keyref_is_pin(self):
|
def keyref_is_pin(self):
|
||||||
if self.keyref_secret_key == '%c' % self.PACE_PIN:
|
if self.keyref_secret_key == b'%c' % self.PACE_PIN:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def keyref_is_puk(self):
|
def keyref_is_puk(self):
|
||||||
if self.keyref_secret_key == '%c' % self.PACE_PUK:
|
if self.keyref_secret_key == b'%c' % self.PACE_PUK:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ class nPA_AT_CRT(ControlReferenceTemplate):
|
|||||||
else:
|
else:
|
||||||
raise SwError(SW["ERR_REFNOTUSABLE"])
|
raise SwError(SW["ERR_REFNOTUSABLE"])
|
||||||
|
|
||||||
return r, ""
|
return r, b""
|
||||||
|
|
||||||
|
|
||||||
class nPA_SE(Security_Environment):
|
class nPA_SE(Security_Environment):
|
||||||
@@ -201,10 +201,10 @@ class nPA_SE(Security_Environment):
|
|||||||
if self.at.algorithm == "PACE":
|
if self.at.algorithm == "PACE":
|
||||||
if self.at.keyref_is_pin():
|
if self.at.keyref_is_pin():
|
||||||
if self.sam.counter <= 0:
|
if self.sam.counter <= 0:
|
||||||
print "Must use PUK to unblock"
|
print("Must use PUK to unblock")
|
||||||
return 0x63c0, ""
|
return 0x63c0, ""
|
||||||
if self.sam.counter == 1 and not self.sam.active:
|
if self.sam.counter == 1 and not self.sam.active:
|
||||||
print "Must use CAN to activate"
|
print("Must use CAN to activate")
|
||||||
return 0x63c1, ""
|
return 0x63c1, ""
|
||||||
self.eac_step = 0
|
self.eac_step = 0
|
||||||
elif self.at.algorithm == "TA":
|
elif self.at.algorithm == "TA":
|
||||||
@@ -233,10 +233,10 @@ class nPA_SE(Security_Environment):
|
|||||||
elif self.eac_step == 6:
|
elif self.eac_step == 6:
|
||||||
# TODO implement RI
|
# TODO implement RI
|
||||||
# "\x7c\x22\x81\x20\" is some prefix and the rest is our RI
|
# "\x7c\x22\x81\x20\" is some prefix and the rest is our RI
|
||||||
return SW["NORMAL"], "\x7c\x22\x81\x20\x48\x1e\x58\xd1\x7c\x12" + \
|
return SW["NORMAL"], b"\x7c\x22\x81\x20\x48\x1e\x58\xd1\x7c\x12" + \
|
||||||
"\x9a\x0a\xb4\x63\x7d\x43\xc7\xf7\xeb\x2b" + \
|
b"\x9a\x0a\xb4\x63\x7d\x43\xc7\xf7\xeb\x2b" + \
|
||||||
"\x06\x10\x6f\x26\x90\xe3\x00\xc4\xe7\x03" + \
|
b"\x06\x10\x6f\x26\x90\xe3\x00\xc4\xe7\x03" + \
|
||||||
"\x54\xa0\x41\xf0\xd3\x90"
|
b"\x54\xa0\x41\xf0\xd3\x90"
|
||||||
|
|
||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
@@ -268,11 +268,11 @@ class nPA_SE(Security_Environment):
|
|||||||
self.PACE_SEC = PACE_SEC(self.sam.can, eac.PACE_CAN)
|
self.PACE_SEC = PACE_SEC(self.sam.can, eac.PACE_CAN)
|
||||||
elif self.at.keyref_is_pin():
|
elif self.at.keyref_is_pin():
|
||||||
if self.sam.counter <= 0:
|
if self.sam.counter <= 0:
|
||||||
print "Must use PUK to unblock"
|
print("Must use PUK to unblock")
|
||||||
return 0x63c0, ""
|
return 0x63c0, b""
|
||||||
if self.sam.counter == 1 and not self.sam.active:
|
if self.sam.counter == 1 and not self.sam.active:
|
||||||
print "Must use CAN to activate"
|
print("Must use CAN to activate")
|
||||||
return 0x63c1, ""
|
return 0x63c1, b""
|
||||||
self.PACE_SEC = PACE_SEC(self.sam.eid_pin, eac.PACE_PIN)
|
self.PACE_SEC = PACE_SEC(self.sam.eid_pin, eac.PACE_PIN)
|
||||||
self.sam.counter -= 1
|
self.sam.counter -= 1
|
||||||
if self.sam.counter <= 1:
|
if self.sam.counter <= 1:
|
||||||
@@ -384,7 +384,7 @@ class nPA_SE(Security_Environment):
|
|||||||
my_token = \
|
my_token = \
|
||||||
eac.PACE_STEP3D_compute_authentication_token(self.eac_ctx,
|
eac.PACE_STEP3D_compute_authentication_token(self.eac_ctx,
|
||||||
self.pace_opp_pub_key)
|
self.pace_opp_pub_key)
|
||||||
token = ""
|
token = b""
|
||||||
for tag, length, value in tlv_data:
|
for tag, length, value in tlv_data:
|
||||||
if tag == 0x85:
|
if tag == 0x85:
|
||||||
token = value
|
token = value
|
||||||
@@ -396,19 +396,19 @@ class nPA_SE(Security_Environment):
|
|||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
|
|
||||||
print "Established PACE channel"
|
print("Established PACE channel")
|
||||||
|
|
||||||
if self.at.keyref_is_can():
|
if self.at.keyref_is_can():
|
||||||
if (self.sam.counter == 1):
|
if (self.sam.counter == 1):
|
||||||
self.sam.active = True
|
self.sam.active = True
|
||||||
print "PIN resumed"
|
print("PIN resumed")
|
||||||
elif self.at.keyref_is_pin():
|
elif self.at.keyref_is_pin():
|
||||||
self.sam.active = True
|
self.sam.active = True
|
||||||
self.sam.counter = 3
|
self.sam.counter = 3
|
||||||
elif self.at.keyref_is_puk():
|
elif self.at.keyref_is_puk():
|
||||||
self.sam.active = True
|
self.sam.active = True
|
||||||
self.sam.counter = 3
|
self.sam.counter = 3
|
||||||
print "PIN unblocked"
|
print("PIN unblocked")
|
||||||
|
|
||||||
self.eac_step += 1
|
self.eac_step += 1
|
||||||
self.at.algorithm = "TA"
|
self.at.algorithm = "TA"
|
||||||
@@ -449,7 +449,7 @@ class nPA_SE(Security_Environment):
|
|||||||
|
|
||||||
self.eac_step += 1
|
self.eac_step += 1
|
||||||
|
|
||||||
print "Generated Nonce and Authentication Token for CA"
|
print("Generated Nonce and Authentication Token for CA")
|
||||||
|
|
||||||
# TODO activate SM
|
# TODO activate SM
|
||||||
self.new_encryption_ctx = eac.EAC_ID_CA
|
self.new_encryption_ctx = eac.EAC_ID_CA
|
||||||
@@ -467,9 +467,9 @@ class nPA_SE(Security_Environment):
|
|||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["ERR_NOINFO69"])
|
raise SwError(SW["ERR_NOINFO69"])
|
||||||
|
|
||||||
print "Imported Certificate"
|
print("Imported Certificate")
|
||||||
|
|
||||||
return ""
|
return b""
|
||||||
|
|
||||||
def external_authenticate(self, p1, p2, data):
|
def external_authenticate(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
@@ -489,14 +489,14 @@ class nPA_SE(Security_Environment):
|
|||||||
if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
|
if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
|
||||||
auxiliary_data, data):
|
auxiliary_data, data):
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
print "Could not verify Terminal's signature"
|
print("Could not verify Terminal's signature")
|
||||||
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
||||||
|
|
||||||
print "Terminal's signature verified"
|
print("Terminal's signature verified")
|
||||||
|
|
||||||
self.eac_step += 1
|
self.eac_step += 1
|
||||||
|
|
||||||
return 0x9000, ""
|
return 0x9000, b""
|
||||||
|
|
||||||
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
||||||
|
|
||||||
@@ -532,12 +532,12 @@ class nPA_SE(Security_Environment):
|
|||||||
:returns: the protected data and the SW bytes
|
:returns: the protected data and the SW bytes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return_data = ""
|
return_data = b""
|
||||||
|
|
||||||
if result != "":
|
if result:
|
||||||
# Encrypt the data included in the RAPDU
|
# Encrypt the data included in the RAPDU
|
||||||
encrypted = self.encipher(0x82, 0x80, result)
|
encrypted = self.encipher(0x82, 0x80, result)
|
||||||
encrypted = "\x01" + encrypted
|
encrypted = b"\x01" + encrypted
|
||||||
encrypted_tlv = bertlv_pack([(
|
encrypted_tlv = bertlv_pack([(
|
||||||
SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"],
|
SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"],
|
||||||
len(encrypted),
|
len(encrypted),
|
||||||
@@ -570,11 +570,11 @@ class nPA_SE(Security_Environment):
|
|||||||
def compute_digital_signature(self, p1, p2, data):
|
def compute_digital_signature(self, p1, p2, data):
|
||||||
# TODO Signing with brainpoolP256r1 or any other key needs some more
|
# TODO Signing with brainpoolP256r1 or any other key needs some more
|
||||||
# effort ;-)
|
# effort ;-)
|
||||||
return '\x0D\xB2\x9B\xB9\x5E\x97\x7D\x42\x73\xCF\xA5\x45\xB7\xED' + \
|
return b'\x0D\xB2\x9B\xB9\x5E\x97\x7D\x42\x73\xCF\xA5\x45\xB7\xED' + \
|
||||||
'\x5C\x39\x3F\xCE\xCD\x4A\xDE\xDC\x2B\x85\x23\x9F\x66\x52' + \
|
b'\x5C\x39\x3F\xCE\xCD\x4A\xDE\xDC\x2B\x85\x23\x9F\x66\x52' + \
|
||||||
'\x10\xC2\x67\xDC\xA6\x35\x94\x2D\x24\xED\xEB\xC8\x34\x6C' + \
|
b'\x10\xC2\x67\xDC\xA6\x35\x94\x2D\x24\xED\xEB\xC8\x34\x6C' + \
|
||||||
'\x4B\xD1\xA1\x15\xB4\x48\x3A\xA4\x4A\xCE\xFF\xED\x97\x0E' + \
|
b'\x4B\xD1\xA1\x15\xB4\x48\x3A\xA4\x4A\xCE\xFF\xED\x97\x0E' + \
|
||||||
'\x07\xF3\x72\xF0\xFB\xA3\x62\x8C'
|
b'\x07\xF3\x72\xF0\xFB\xA3\x62\x8C'
|
||||||
|
|
||||||
|
|
||||||
class nPA_SAM(SAM):
|
class nPA_SAM(SAM):
|
||||||
@@ -602,14 +602,14 @@ class nPA_SAM(SAM):
|
|||||||
# change secret
|
# change secret
|
||||||
if p2 == self.current_SE.at.PACE_CAN:
|
if p2 == self.current_SE.at.PACE_CAN:
|
||||||
self.can = data
|
self.can = data
|
||||||
print "Changed CAN to %r" % self.can
|
print("Changed CAN to %r" % self.can)
|
||||||
elif p2 == self.current_SE.at.PACE_PIN:
|
elif p2 == self.current_SE.at.PACE_PIN:
|
||||||
# TODO: allow terminals to change the PIN with permission
|
# TODO: allow terminals to change the PIN with permission
|
||||||
# "CAN allowed"
|
# "CAN allowed"
|
||||||
if not self.current_SE.at.keyref_is_pin():
|
if not self.current_SE.at.keyref_is_pin():
|
||||||
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
||||||
self.eid_pin = data
|
self.eid_pin = data
|
||||||
print "Changed PIN to %r" % self.eid_pin
|
print("Changed PIN to %r" % self.eid_pin)
|
||||||
else:
|
else:
|
||||||
raise SwError(SW["ERR_DATANOTFOUND"])
|
raise SwError(SW["ERR_DATANOTFOUND"])
|
||||||
elif p1 == 0x03:
|
elif p1 == 0x03:
|
||||||
@@ -620,14 +620,14 @@ class nPA_SAM(SAM):
|
|||||||
elif p2 == self.current_SE.at.PACE_PIN:
|
elif p2 == self.current_SE.at.PACE_PIN:
|
||||||
if self.current_SE.at.keyref_is_can():
|
if self.current_SE.at.keyref_is_can():
|
||||||
self.active = True
|
self.active = True
|
||||||
print "Resumed PIN"
|
print("Resumed PIN")
|
||||||
elif self.current_SE.at.keyref_is_pin():
|
elif self.current_SE.at.keyref_is_pin():
|
||||||
# PACE was successful with PIN, nothing to do
|
# PACE was successful with PIN, nothing to do
|
||||||
# resume/unblock
|
# resume/unblock
|
||||||
pass
|
pass
|
||||||
elif self.current_SE.at.keyref_is_puk():
|
elif self.current_SE.at.keyref_is_puk():
|
||||||
# TODO unblock PIN for signature
|
# TODO unblock PIN for signature
|
||||||
print "Unblocked PIN"
|
print("Unblocked PIN")
|
||||||
self.active = True
|
self.active = True
|
||||||
self.counter = 3
|
self.counter = 3
|
||||||
else:
|
else:
|
||||||
@@ -637,7 +637,7 @@ class nPA_SAM(SAM):
|
|||||||
else:
|
else:
|
||||||
raise SwError(SW["ERR_INCORRECTP1P2"])
|
raise SwError(SW["ERR_INCORRECTP1P2"])
|
||||||
|
|
||||||
return 0x9000, ""
|
return 0x9000, b""
|
||||||
|
|
||||||
def external_authenticate(self, p1, p2, data):
|
def external_authenticate(self, p1, p2, data):
|
||||||
return self.current_SE.external_authenticate(p1, p2, data)
|
return self.current_SE.external_authenticate(p1, p2, data)
|
||||||
@@ -665,8 +665,8 @@ class nPA_SAM(SAM):
|
|||||||
[(tag, _, value)] = structure = unpack(data)
|
[(tag, _, value)] = structure = unpack(data)
|
||||||
if tag == 6:
|
if tag == 6:
|
||||||
mapped_algo = ALGO_MAPPING[value]
|
mapped_algo = ALGO_MAPPING[value]
|
||||||
eid = self.mf.select('dfname', '\xe8\x07\x04\x00\x7f\x00'
|
eid = self.mf.select('dfname', b'\xe8\x07\x04\x00\x7f\x00'
|
||||||
'\x07\x03\x02')
|
b'\x07\x03\x02')
|
||||||
if mapped_algo == "DateOfExpiry":
|
if mapped_algo == "DateOfExpiry":
|
||||||
[(_, _, [(_, _, mine)])] = \
|
[(_, _, [(_, _, mine)])] = \
|
||||||
unpack(eid.select('fid', 0x0103).data)
|
unpack(eid.select('fid', 0x0103).data)
|
||||||
@@ -708,11 +708,11 @@ class nPA_SAM(SAM):
|
|||||||
str(self.current_SE.at.CommunityID))
|
str(self.current_SE.at.CommunityID))
|
||||||
if mine.startswith(self.current_SE.at.CommunityID):
|
if mine.startswith(self.current_SE.at.CommunityID):
|
||||||
print("Community ID verified (living there)")
|
print("Community ID verified (living there)")
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], b""
|
||||||
else:
|
else:
|
||||||
print("Community ID not verified (not living"
|
print("Community ID not verified (not living"
|
||||||
"there)")
|
"there)")
|
||||||
return SW["WARN_NOINFO63"], ""
|
return SW["WARN_NOINFO63"], b""
|
||||||
else:
|
else:
|
||||||
return SwError(SW["ERR_DATANOTFOUND"])
|
return SwError(SW["ERR_DATANOTFOUND"])
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ from virtualsmartcard.CryptoUtils import *
|
|||||||
class TestCryptoUtils(unittest.TestCase):
|
class TestCryptoUtils(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.teststring = "DEADBEEFistatsyksdvhwohfwoehcowc8hw8rogfq8whv75tsg"\
|
self.teststring = b"DEADBEEFistatsyksdvhwohfwoehcowc8hw8rogfq8whv75tsg"\
|
||||||
"ohsav8wress"
|
b"ohsav8wress"
|
||||||
|
|
||||||
def test_padding(self):
|
def test_padding(self):
|
||||||
padded = append_padding(16, self.teststring)
|
padded = append_padding(16, self.teststring)
|
||||||
|
|||||||
@@ -24,33 +24,33 @@ from virtualsmartcard.SmartcardSAM import *
|
|||||||
class TestSmartcardSAM(unittest.TestCase):
|
class TestSmartcardSAM(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.password = "DUMMYKEYDUMMYKEY"
|
self.password = "DUMMYKEYDUMMYKEY".encode('ascii')
|
||||||
self.myCard = SAM("1234", "1234567890")
|
self.myCard = SAM("1234".encode('ascii'), "1234567890".encode('ascii'))
|
||||||
self.secEnv = Security_Environment(None, self.myCard) # TODO: Set CRTs
|
self.secEnv = Security_Environment(None, self.myCard) # TODO: Set CRTs
|
||||||
self.secEnv.ht.algorithm = "SHA"
|
self.secEnv.ht.algorithm = "SHA"
|
||||||
self.secEnv.ct.algorithm = "AES-CBC"
|
self.secEnv.ct.algorithm = "AES-CBC"
|
||||||
|
|
||||||
def test_incorrect_pin(self):
|
def test_incorrect_pin(self):
|
||||||
with self.assertRaises(SwError):
|
with self.assertRaises(SwError):
|
||||||
self.myCard.verify(0x00, 0x00, "5678")
|
self.myCard.verify(0x00, 0x00, "5678".encode('ascii'))
|
||||||
|
|
||||||
def test_counter_decrement(self):
|
def test_counter_decrement(self):
|
||||||
ctr1 = self.myCard.counter
|
ctr1 = self.myCard.counter
|
||||||
try:
|
try:
|
||||||
self.myCard.verify(0x00, 0x00, "3456")
|
self.myCard.verify(0x00, 0x00, "3456".encode('ascii'))
|
||||||
except SwError as e:
|
except SwError as e:
|
||||||
pass
|
pass
|
||||||
self.assertEquals(self.myCard.counter, ctr1 - 1)
|
self.assertEquals(self.myCard.counter, ctr1 - 1)
|
||||||
|
|
||||||
def test_internal_authenticate(self):
|
def test_internal_authenticate(self):
|
||||||
sw, challenge = self.myCard.get_challenge(0x00, 0x00, "")
|
sw, challenge = self.myCard.get_challenge(0x00, 0x00, b"")
|
||||||
blocklen = vsCrypto.get_cipher_blocklen("DES3-ECB")
|
blocklen = vsCrypto.get_cipher_blocklen("DES3-ECB")
|
||||||
padded = vsCrypto.append_padding(blocklen, challenge)
|
padded = vsCrypto.append_padding(blocklen, challenge)
|
||||||
sw, result_data = self.myCard.internal_authenticate(0x00, 0x00, padded)
|
sw, result_data = self.myCard.internal_authenticate(0x00, 0x00, padded)
|
||||||
self.assertEquals(sw, SW["NORMAL"])
|
self.assertEquals(sw, SW["NORMAL"])
|
||||||
|
|
||||||
def test_external_authenticate(self):
|
def test_external_authenticate(self):
|
||||||
sw, challenge = self.myCard.get_challenge(0x00, 0x00, "")
|
sw, challenge = self.myCard.get_challenge(0x00, 0x00, b"")
|
||||||
blocklen = vsCrypto.get_cipher_blocklen("DES3-ECB")
|
blocklen = vsCrypto.get_cipher_blocklen("DES3-ECB")
|
||||||
padded = vsCrypto.append_padding(blocklen, challenge)
|
padded = vsCrypto.append_padding(blocklen, challenge)
|
||||||
sw, result_data = self.myCard.internal_authenticate(0x00, 0x00, padded)
|
sw, result_data = self.myCard.internal_authenticate(0x00, 0x00, padded)
|
||||||
@@ -73,7 +73,7 @@ class TestSmartcardSAM(unittest.TestCase):
|
|||||||
# should this really be secEnv.ct? probably rather secEnv.dst
|
# should this really be secEnv.ct? probably rather secEnv.dst
|
||||||
self.secEnv.ct.algorithm = "RSA"
|
self.secEnv.ct.algorithm = "RSA"
|
||||||
self.secEnv.dst.keylength = 1024
|
self.secEnv.dst.keylength = 1024
|
||||||
sw, pk = self.secEnv.generate_public_key_pair(0x00, 0x00, "")
|
sw, pk = self.secEnv.generate_public_key_pair(0x00, 0x00, b"")
|
||||||
self.assertEquals(sw, SW["NORMAL"])
|
self.assertEquals(sw, SW["NORMAL"])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class TestUtils(unittest.TestCase):
|
|||||||
def test_CAPDU(self):
|
def test_CAPDU(self):
|
||||||
a = C_APDU(1, 2, 3, 4) # case 1
|
a = C_APDU(1, 2, 3, 4) # case 1
|
||||||
b = C_APDU(1, 2, 3, 4, 5) # case 2
|
b = C_APDU(1, 2, 3, 4, 5) # case 2
|
||||||
c = C_APDU((1, 2, 3), cla=0x23, data="hallo") # case 3
|
c = C_APDU((1, 2, 3), cla=0x23, data=b"hallo") # case 3
|
||||||
d = C_APDU(1, 2, 3, 4, 2, 4, 6, 0) # case 4
|
d = C_APDU(1, 2, 3, 4, 2, 4, 6, 0) # case 4
|
||||||
|
|
||||||
print()
|
print()
|
||||||
@@ -54,39 +54,39 @@ class TestUtils(unittest.TestCase):
|
|||||||
print(repr(g))
|
print(repr(g))
|
||||||
print(hexdump(g.render()))
|
print(hexdump(g.render()))
|
||||||
|
|
||||||
h = C_APDU('\x0c\x2a\x00\xbe\x00\x01\x5f\x87\x82\x01\x51\x01\xf0\xa2'
|
h = C_APDU(b'\x0c\x2a\x00\xbe\x00\x01\x5f\x87\x82\x01\x51\x01\xf0\xa2'
|
||||||
'\x21\xa1\x36\x27\xb1\x30\x31\x3e\xd0\x97\x09\xb5\xde\x73'
|
b'\x21\xa1\x36\x27\xb1\x30\x31\x3e\xd0\x97\x09\xb5\xde\x73'
|
||||||
'\x5e\x29\x90\xce\xf1\x3d\x8a\xfd\xe7\x92\xe5\xa4\x70\xb9'
|
b'\x5e\x29\x90\xce\xf1\x3d\x8a\xfd\xe7\x92\xe5\xa4\x70\xb9'
|
||||||
'\x5d\x31\xe2\x34\xe7\xe2\x06\x13\x17\x7a\x3e\xca\x06\x39'
|
b'\x5d\x31\xe2\x34\xe7\xe2\x06\x13\x17\x7a\x3e\xca\x06\x39'
|
||||||
'\x24\x2e\x75\x8c\x29\x6d\xd8\xa3\x1b\x1a\x68\x58\xd0\x1a'
|
b'\x24\x2e\x75\x8c\x29\x6d\xd8\xa3\x1b\x1a\x68\x58\xd0\x1a'
|
||||||
'\x98\xd4\xd8\x19\x50\xe9\x1b\x3c\xd1\xfd\x10\x53\x5b\xf2'
|
b'\x98\xd4\xd8\x19\x50\xe9\x1b\x3c\xd1\xfd\x10\x53\x5b\xf2'
|
||||||
'\x3b\xff\x4a\xf6\x05\xd0\x72\xad\xae\xaa\x93\x1a\x0a\x90'
|
b'\x3b\xff\x4a\xf6\x05\xd0\x72\xad\xae\xaa\x93\x1a\x0a\x90'
|
||||||
'\xc8\xa1\xb1\xf1\x0a\xba\x5b\xd2\x23\x38\xf8\x9a\x38\x9e'
|
b'\xc8\xa1\xb1\xf1\x0a\xba\x5b\xd2\x23\x38\xf8\x9a\x38\x9e'
|
||||||
'\xa2\x04\x8b\xcb\x8b\x8b\xc0\x80\xd9\x2a\x04\x47\x26\x83'
|
b'\xa2\x04\x8b\xcb\x8b\x8b\xc0\x80\xd9\x2a\x04\x47\x26\x83'
|
||||||
'\xda\xfe\x57\x68\x6b\x00\xb9\xa2\xea\x96\xf2\x07\x7f\xc5'
|
b'\xda\xfe\x57\x68\x6b\x00\xb9\xa2\xea\x96\xf2\x07\x7f\xc5'
|
||||||
'\x9c\xee\xbe\xf3\x81\xbf\x24\x19\x1e\x49\x1e\x9a\x85\x8f'
|
b'\x9c\xee\xbe\xf3\x81\xbf\x24\x19\x1e\x49\x1e\x9a\x85\x8f'
|
||||||
'\x34\xcb\x1a\x23\xae\x6d\x7f\xa4\xb6\x7b\x60\x5d\x56\x79'
|
b'\x34\xcb\x1a\x23\xae\x6d\x7f\xa4\xb6\x7b\x60\x5d\x56\x79'
|
||||||
'\x1c\xec\x18\xcc\x09\xdb\xb2\xbb\xf4\x31\xee\x08\x54\x26'
|
b'\x1c\xec\x18\xcc\x09\xdb\xb2\xbb\xf4\x31\xee\x08\x54\x26'
|
||||||
'\xd5\xde\x99\xfa\x43\xa2\x49\x8e\x60\xc0\xaa\x4f\xfd\xf7'
|
b'\xd5\xde\x99\xfa\x43\xa2\x49\x8e\x60\xc0\xaa\x4f\xfd\xf7'
|
||||||
'\xe5\xc8\x89\x43\x5e\x11\xa2\x28\xc4\x92\x11\xda\xba\xe4'
|
b'\xe5\xc8\x89\x43\x5e\x11\xa2\x28\xc4\x92\x11\xda\xba\xe4'
|
||||||
'\x91\xec\x04\xc9\x2c\xbd\x91\x6a\x5e\x7e\xb9\x85\xa2\xfa'
|
b'\x91\xec\x04\xc9\x2c\xbd\x91\x6a\x5e\x7e\xb9\x85\xa2\xfa'
|
||||||
'\x07\xc9\x47\x24\xa4\x3b\x63\xef\x75\x65\xef\xaf\xac\x22'
|
b'\x07\xc9\x47\x24\xa4\x3b\x63\xef\x75\x65\xef\xaf\xac\x22'
|
||||||
'\x75\x99\x8b\x19\xde\x95\x76\xc9\xc8\xbc\x30\x23\x48\x07'
|
b'\x75\x99\x8b\x19\xde\x95\x76\xc9\xc8\xbc\x30\x23\x48\x07'
|
||||||
'\x28\x19\x1e\x49\x9e\xcb\x99\xc3\x48\xdd\x1d\x0f\x44\x62'
|
b'\x28\x19\x1e\x49\x9e\xcb\x99\xc3\x48\xdd\x1d\x0f\x44\x62'
|
||||||
'\x64\x2a\x19\x7b\xeb\xee\xdf\xa1\xa6\xae\x87\x6d\x93\x36'
|
b'\x64\x2a\x19\x7b\xeb\xee\xdf\xa1\xa6\xae\x87\x6d\x93\x36'
|
||||||
'\x2d\x35\x8f\xd9\x61\x73\xef\x2d\x39\xb5\xc5\xe2\x75\x4b'
|
b'\x2d\x35\x8f\xd9\x61\x73\xef\x2d\x39\xb5\xc5\xe2\x75\x4b'
|
||||||
'\x63\x0b\x41\x94\x8c\xbb\x55\xf6\x98\x5f\x9c\x07\xca\xe3'
|
b'\x63\x0b\x41\x94\x8c\xbb\x55\xf6\x98\x5f\x9c\x07\xca\xe3'
|
||||||
'\x15\xe4\xe6\x93\xd0\xa3\x9b\x22\xfa\x62\x18\xc5\x63\xfa'
|
b'\x15\xe4\xe6\x93\xd0\xa3\x9b\x22\xfa\x62\x18\xc5\x63\xfa'
|
||||||
'\x2d\x57\xbb\x29\x2d\x57\x10\xd3\x0c\x05\x80\x15\x27\x4b'
|
b'\x2d\x57\xbb\x29\x2d\x57\x10\xd3\x0c\x05\x80\x15\x27\x4b'
|
||||||
'\xc0\x84\x23\x62\x22\x6b\xae\x39\xa2\x8f\x55\xac\x8e\x08'
|
b'\xc0\x84\x23\x62\x22\x6b\xae\x39\xa2\x8f\x55\xac\x8e\x08'
|
||||||
'\x34\x46\xcc\x83\xf9\x9d\x2a\x75\x00\x00')
|
b'\x34\x46\xcc\x83\xf9\x9d\x2a\x75\x00\x00')
|
||||||
print()
|
print()
|
||||||
print(h)
|
print(h)
|
||||||
print(repr(h))
|
print(repr(h))
|
||||||
|
|
||||||
def test_RAPDU(self):
|
def test_RAPDU(self):
|
||||||
e = R_APDU(0x90, 0)
|
e = R_APDU(0x90, 0)
|
||||||
f = R_APDU("foo\x67\x00")
|
f = R_APDU(b"foo\x67\x00")
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print(e)
|
print(e)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#
|
#
|
||||||
import binascii
|
import binascii
|
||||||
import string
|
import string
|
||||||
|
import struct
|
||||||
from virtualsmartcard.ConstantDefinitions import MAX_SHORT_LE, MAX_EXTENDED_LE
|
from virtualsmartcard.ConstantDefinitions import MAX_SHORT_LE, MAX_EXTENDED_LE
|
||||||
|
|
||||||
|
|
||||||
@@ -28,23 +29,24 @@ def stringtoint(str):
|
|||||||
|
|
||||||
|
|
||||||
def inttostring(i, length=None, len_extendable=False):
|
def inttostring(i, length=None, len_extendable=False):
|
||||||
str = "%x" % i
|
str = b""
|
||||||
if len(str) % 2 == 0:
|
while True:
|
||||||
str = str.decode('hex')
|
str = struct.pack('B', i & 0xFF) + str
|
||||||
else:
|
i = i >> 8
|
||||||
str = ("0"+str).decode('hex')
|
if i <= 0:
|
||||||
|
break
|
||||||
|
|
||||||
if length:
|
if length:
|
||||||
l = len(str)
|
l = len(str)
|
||||||
if l > length and not len_extendable:
|
if l > length and not len_extendable:
|
||||||
raise ValueError("i too big for the specified stringlength")
|
raise ValueError("i too big for the specified stringlength")
|
||||||
else:
|
else:
|
||||||
str = chr(0)*(length-l) + str
|
str = b'\x00'*(length-l) + str
|
||||||
|
|
||||||
return str
|
return str
|
||||||
|
|
||||||
|
|
||||||
_myprintable = " " + string.ascii_letters + string.digits + string.punctuation
|
_myprintable = list(" " + string.ascii_letters + string.digits + string.punctuation)
|
||||||
|
|
||||||
|
|
||||||
def hexdump(data, indent=0, short=False, linelen=16, offset=0):
|
def hexdump(data, indent=0, short=False, linelen=16, offset=0):
|
||||||
@@ -53,12 +55,14 @@ def hexdump(data, indent=0, short=False, linelen=16, offset=0):
|
|||||||
hexdump without adresses and on one line.
|
hexdump without adresses and on one line.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
hexdump('\x00\x41') -> \
|
hexdump(b'\x00\x41') -> \
|
||||||
'0000: 00 41 .A '
|
'0000: 00 41 .A '
|
||||||
hexdump('\x00\x41', short=True) -> '00 41 (.A)'"""
|
hexdump(b'\x00\x41', short=True) -> '00 41 (.A)'"""
|
||||||
|
|
||||||
def hexable(data):
|
def hexable(data):
|
||||||
return " ".join([binascii.b2a_hex(a) for a in data])
|
if isinstance(data, str):
|
||||||
|
data = map(ord, data)
|
||||||
|
return " ".join(map("{0:0>2X}".format, data))
|
||||||
|
|
||||||
def printable(data):
|
def printable(data):
|
||||||
return "".join([e in _myprintable and e or "." for e in data])
|
return "".join([e in _myprintable and e or "." for e in data])
|
||||||
@@ -66,6 +70,8 @@ def hexdump(data, indent=0, short=False, linelen=16, offset=0):
|
|||||||
if short:
|
if short:
|
||||||
return "%s (%s)" % (hexable(data), printable(data))
|
return "%s (%s)" % (hexable(data), printable(data))
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = map(ord, data)
|
||||||
FORMATSTRING = "%04x: %-" + str(linelen*3) + "s %-" + str(linelen) + "s"
|
FORMATSTRING = "%04x: %-" + str(linelen*3) + "s %-" + str(linelen) + "s"
|
||||||
result = ""
|
result = ""
|
||||||
(head, tail) = (data[:linelen], data[linelen:])
|
(head, tail) = (data[:linelen], data[linelen:])
|
||||||
@@ -90,58 +96,6 @@ LIFE_CYCLES = {0x01: "Load file = loaded",
|
|||||||
0xFF: "Applet instance = Locked"}
|
0xFF: "Applet instance = Locked"}
|
||||||
|
|
||||||
|
|
||||||
def parse_status(data):
|
|
||||||
"""Parses the Response APDU of a GetStatus command."""
|
|
||||||
def parse_segment(segment):
|
|
||||||
def parse_privileges(privileges):
|
|
||||||
if privileges == 0x0:
|
|
||||||
return "N/A"
|
|
||||||
else:
|
|
||||||
privs = []
|
|
||||||
if privileges & (1 << 7):
|
|
||||||
privs.append("security domain")
|
|
||||||
if privileges & (1 << 6):
|
|
||||||
privs.append("DAP DES verification")
|
|
||||||
if privileges & (1 << 5):
|
|
||||||
privs.append("delegated management")
|
|
||||||
if privileges & (1 << 4):
|
|
||||||
privs.append("card locking")
|
|
||||||
if privileges & (1 << 3):
|
|
||||||
privs.append("card termination")
|
|
||||||
if privileges & (1 << 2):
|
|
||||||
privs.append("default selected")
|
|
||||||
if privileges & (1 << 1):
|
|
||||||
privs.append("global PIN modification")
|
|
||||||
if privileges & (1 << 0):
|
|
||||||
privs.append("mandated DAP verification")
|
|
||||||
return ", ".join(privs)
|
|
||||||
|
|
||||||
lgth = ord(segment[0])
|
|
||||||
aid = segment[1:1+lgth]
|
|
||||||
lifecycle = ord(segment[1+lgth])
|
|
||||||
privileges = ord(segment[1+lgth+1])
|
|
||||||
|
|
||||||
print("aid length: %i (%x)" % (lgth, lgth))
|
|
||||||
print("aid: %s" % hexdump(aid, indent=18, short=True))
|
|
||||||
print("life cycle state: %x (%s)" %
|
|
||||||
(lifecycle, LIFE_CYCLES.get(lifecycle,
|
|
||||||
"unknown or invalid state")))
|
|
||||||
print("privileges: %x (%s)\n" %
|
|
||||||
(privileges, parse_privileges(privileges)))
|
|
||||||
|
|
||||||
pos = 0
|
|
||||||
while pos < len(data):
|
|
||||||
lgth = ord(data[pos])+3
|
|
||||||
segment = data[pos:pos+lgth]
|
|
||||||
parse_segment(segment)
|
|
||||||
pos = pos + lgth
|
|
||||||
|
|
||||||
|
|
||||||
def _unformat_hexdump(dump):
|
|
||||||
hexdump = " ".join([line[7:54] for line in dump.splitlines()])
|
|
||||||
return binascii.a2b_hex("".join([e != " " and e or "" for e in hexdump]))
|
|
||||||
|
|
||||||
|
|
||||||
def _make_byte_property(prop):
|
def _make_byte_property(prop):
|
||||||
"Make a byte property(). This is meta code."
|
"Make a byte property(). This is meta code."
|
||||||
return property(lambda self: getattr(self, "_"+prop, None),
|
return property(lambda self: getattr(self, "_"+prop, None),
|
||||||
@@ -205,9 +159,11 @@ class APDU(object):
|
|||||||
|
|
||||||
def _setdata(self, value):
|
def _setdata(self, value):
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
self._data = "".join([e for e in value])
|
self._data = b"".join([e for e in value])
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
self._data = "".join([chr(int(e)) for e in value])
|
self._data = b"".join([inttostring(int(e)) for e in value])
|
||||||
|
elif isinstance(value, bytes):
|
||||||
|
self._data = value
|
||||||
else:
|
else:
|
||||||
raise ValueError("'data' attribute can only be a str or a list of "
|
raise ValueError("'data' attribute can only be a str or a list of "
|
||||||
"int, not %s" % type(value))
|
"int, not %s" % type(value))
|
||||||
@@ -265,8 +221,8 @@ class C_APDU(APDU):
|
|||||||
def parse(self, apdu):
|
def parse(self, apdu):
|
||||||
"""Parse a full command APDU and assign the values to our object,
|
"""Parse a full command APDU and assign the values to our object,
|
||||||
overwriting whatever there was."""
|
overwriting whatever there was."""
|
||||||
apdu = map(lambda a: (isinstance(a, str) and (ord(a),) or
|
apdu = list(map(lambda a: (isinstance(a, str) and (ord(a),) or
|
||||||
(a,))[0], apdu)
|
(a,))[0], apdu))
|
||||||
apdu = apdu + [0] * max(4-len(apdu), 0)
|
apdu = apdu + [0] * max(4-len(apdu), 0)
|
||||||
|
|
||||||
self.CLA, self.INS, self.P1, self.P2 = apdu[:4] # case 1, 2, 3, 4
|
self.CLA, self.INS, self.P1, self.P2 = apdu[:4] # case 1, 2, 3, 4
|
||||||
@@ -349,21 +305,21 @@ class C_APDU(APDU):
|
|||||||
buffer = []
|
buffer = []
|
||||||
|
|
||||||
for i in self.CLA, self.INS, self.P1, self.P2:
|
for i in self.CLA, self.INS, self.P1, self.P2:
|
||||||
buffer.append(chr(i))
|
buffer.append(inttostring(i))
|
||||||
|
|
||||||
if len(self.data) > 0:
|
if len(self.data) > 0:
|
||||||
buffer.append(chr(self.Lc))
|
buffer.append(inttostring(self.Lc))
|
||||||
buffer.append(self.data)
|
buffer.append(self.data)
|
||||||
|
|
||||||
if hasattr(self, "_Le"):
|
if hasattr(self, "_Le"):
|
||||||
if self.__extended_length:
|
if self.__extended_length:
|
||||||
buffer.append(chr(0x00))
|
buffer.append(inttostring(0x00))
|
||||||
buffer.append(chr(self.Le >> 8))
|
buffer.append(inttostring(self.Le >> 8))
|
||||||
buffer.append(chr(self.Le - self.Le >> 8))
|
buffer.append(inttostring(self.Le - self.Le >> 8))
|
||||||
else:
|
else:
|
||||||
buffer.append(chr(self.Le))
|
buffer.append(inttostring(self.Le))
|
||||||
|
|
||||||
return "".join(buffer)
|
return b"".join(buffer)
|
||||||
|
|
||||||
def case(self):
|
def case(self):
|
||||||
"Return 1, 2, 3 or 4, depending on which ISO case we represent."
|
"Return 1, 2, 3 or 4, depending on which ISO case we represent."
|
||||||
@@ -383,7 +339,7 @@ class R_APDU(APDU):
|
|||||||
"Class for a response APDU"
|
"Class for a response APDU"
|
||||||
|
|
||||||
def _getsw(self):
|
def _getsw(self):
|
||||||
return chr(self.SW1) + chr(self.SW2)
|
return inttostring(self.SW1) + inttostring(self.SW2)
|
||||||
|
|
||||||
def _setsw(self, value):
|
def _setsw(self, value):
|
||||||
if len(value) != 2:
|
if len(value) != 2:
|
||||||
|
|||||||
Reference in New Issue
Block a user