diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py index 93c5ef3..d953a52 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py @@ -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<> 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_ diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py index a8c6377..39f135e 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py @@ -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") diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/HandlerTest.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/HandlerTest.py index 1dae405..f57f926 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/HandlerTest.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/HandlerTest.py @@ -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 diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py index 69c27b3..830bc9f 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py @@ -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]) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py index 7d04fae..4634424 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/cryptoflex.py @@ -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 diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py index c4d1d6b..aa574c9 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py @@ -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: diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py index 490321e..aa91290 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py @@ -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) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/SmartcardSAM_test.py b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/SmartcardSAM_test.py index 239de3a..9181622 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/SmartcardSAM_test.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/SmartcardSAM_test.py @@ -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"]) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/utils_test.py b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/utils_test.py index 3adf3e3..789fc1c 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/utils_test.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/utils_test.py @@ -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) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py index dcb4514..4f769fe 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/utils.py @@ -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: