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