added more Python 3 compatibility

fixes https://github.com/frankmorgner/vsmartcard/issues/71
This commit is contained in:
Frank Morgner
2018-05-03 16:30:08 +02:00
parent ae0d04f8b7
commit 3b7c04c535
15 changed files with 244 additions and 276 deletions

View File

@@ -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"\

View File

@@ -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]

View File

@@ -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):
"""

View File

@@ -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"]

View File

@@ -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"])

View File

@@ -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_

View File

@@ -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")

View File

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

View File

@@ -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])

View File

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

View 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:

View File

@@ -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)

View File

@@ -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"])

View File

@@ -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)

View File

@@ -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: