Refactored the python files in the cards directory according to PEP8
This commit is contained in:
@@ -32,16 +32,18 @@ class HandlerTestOS(SmartcardOS):
|
|||||||
lastCommandOffcut = ''
|
lastCommandOffcut = ''
|
||||||
|
|
||||||
def getATR(self):
|
def getATR(self):
|
||||||
return '\x3B\xD6\x18\x00\x80\xB1\x80\x6D\x1F\x03\x80\x51\x00\x61\x10\x30\x9E'
|
return '\x3B\xD6\x18\x00\x80\xB1\x80\x6D\x1F\x03\x80\x51\x00\x61' + \
|
||||||
|
'\x10\x30\x9E'
|
||||||
|
|
||||||
def __output_from_le(self, msg):
|
def __output_from_le(self, msg):
|
||||||
le = (ord(msg[2])<<8)+ord(msg[3])
|
le = (ord(msg[2]) << 8) + ord(msg[3])
|
||||||
return ''.join([chr(num&0xff) for num in xrange(le)])
|
return ''.join([chr(num & 0xff) for num in xrange(le)])
|
||||||
|
|
||||||
def execute(self, msg):
|
def execute(self, msg):
|
||||||
ok = '\x90\x00'
|
ok = '\x90\x00'
|
||||||
error = '\x6d\x00'
|
error = '\x6d\x00'
|
||||||
if msg == '\x00\xA4\x04\x00\x06\xA0\x00\x00\x00\x18\x50' or msg == '\x00\xA4\x04\x00\x06\xA0\x00\x00\x00\x18\xFF':
|
if (msg == '\x00\xA4\x04\x00\x06\xA0\x00\x00\x00\x18\x50' or
|
||||||
|
msg == '\x00\xA4\x04\x00\x06\xA0\x00\x00\x00\x18\xFF'):
|
||||||
logging.info('Select applet')
|
logging.info('Select applet')
|
||||||
return ok
|
return ok
|
||||||
elif msg.startswith('\x80\x38\x00'):
|
elif msg.startswith('\x80\x38\x00'):
|
||||||
@@ -65,7 +67,7 @@ class HandlerTestOS(SmartcardOS):
|
|||||||
self.lastCommandOffcut = self.__output_from_le(msg)
|
self.lastCommandOffcut = self.__output_from_le(msg)
|
||||||
if len(self.lastCommandOffcut) > 0xFF:
|
if len(self.lastCommandOffcut) > 0xFF:
|
||||||
return '\x61\x00'
|
return '\x61\x00'
|
||||||
return '' + chr(len(self.lastCommandOffcut)&0xFF)
|
return '' + chr(len(self.lastCommandOffcut) & 0xFF)
|
||||||
elif len(msg) == 6+ord(msg[4]):
|
elif len(msg) == 6+ord(msg[4]):
|
||||||
logging.info('Case 4, APDU')
|
logging.info('Case 4, APDU')
|
||||||
return self.__output_from_le(msg) + ok
|
return self.__output_from_le(msg) + ok
|
||||||
|
|||||||
@@ -17,8 +17,11 @@
|
|||||||
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
|
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import logging
|
||||||
|
import smartcard
|
||||||
|
import sys
|
||||||
from virtualsmartcard.VirtualSmartcard import SmartcardOS
|
from virtualsmartcard.VirtualSmartcard import SmartcardOS
|
||||||
import smartcard, logging, sys, atexit
|
|
||||||
|
|
||||||
|
|
||||||
class RelayOS(SmartcardOS):
|
class RelayOS(SmartcardOS):
|
||||||
@@ -30,7 +33,8 @@ class RelayOS(SmartcardOS):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, readernum):
|
def __init__(self, readernum):
|
||||||
"""
|
"""
|
||||||
Initialize the connection to the (physical) smart card via a given reader
|
Initialize the connection to the (physical) smart card via a given
|
||||||
|
reader
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# See which readers are available
|
# See which readers are available
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ from virtualsmartcard.SmartcardFilesystem import MF, TransparentStructureEF, \
|
|||||||
RecordStructureEF, DF, EF
|
RecordStructureEF, DF, EF
|
||||||
from virtualsmartcard.ConstantDefinitions import FDB, MAX_SHORT_LE
|
from virtualsmartcard.ConstantDefinitions import FDB, MAX_SHORT_LE
|
||||||
|
|
||||||
import struct, logging
|
import struct
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class CryptoflexOS(Iso7816OS):
|
class CryptoflexOS(Iso7816OS):
|
||||||
@@ -43,14 +44,17 @@ class CryptoflexOS(Iso7816OS):
|
|||||||
c = C_APDU(msg)
|
c = C_APDU(msg)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logging.debug("Failed to parse APDU %s", msg)
|
logging.debug("Failed to parse APDU %s", msg)
|
||||||
return self.formatResult(False, 0, 0, "", SW["ERR_INCORRECTPARAMETERS"])
|
return self.formatResult(False, 0, 0, "",
|
||||||
|
SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sw, result = self.ins2handler.get(c.ins, notImplemented)(c.p1, c.p2, c.data)
|
sw, result = self.ins2handler.get(c.ins, notImplemented)(c.p1,
|
||||||
#print type(result)
|
c.p2,
|
||||||
|
c.data)
|
||||||
|
# print type(result)
|
||||||
except SwError as e:
|
except SwError as e:
|
||||||
logging.info(e.message)
|
logging.info(e.message)
|
||||||
#traceback.print_exception(*sys.exc_info())
|
# traceback.print_exception(*sys.exc_info())
|
||||||
sw = e.sw
|
sw = e.sw
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
@@ -62,20 +66,22 @@ class CryptoflexOS(Iso7816OS):
|
|||||||
# cryptoflex does not inpterpret le==0 as maxle
|
# cryptoflex does not inpterpret le==0 as maxle
|
||||||
self.lastCommandSW = sw
|
self.lastCommandSW = sw
|
||||||
self.lastCommandOffcut = data
|
self.lastCommandOffcut = data
|
||||||
r = R_APDU(inttostring(SW["ERR_WRONGLENGTH"] +\
|
r = R_APDU(inttostring(SW["ERR_WRONGLENGTH"] +
|
||||||
min(0xff, len(data)))).render()
|
min(0xff, len(data)))).render()
|
||||||
else:
|
else:
|
||||||
if ins == 0xa4 and len(data):
|
if ins == 0xa4 and len(data):
|
||||||
# get response should be followed by select file
|
# get response should be followed by select file
|
||||||
self.lastCommandSW = sw
|
self.lastCommandSW = sw
|
||||||
self.lastCommandOffcut = data
|
self.lastCommandOffcut = data
|
||||||
r = R_APDU(inttostring(SW["NORMAL_REST"] +\
|
r = R_APDU(inttostring(SW["NORMAL_REST"] +
|
||||||
min(0xff, len(data)))).render()
|
min(0xff, len(data)))).render()
|
||||||
else:
|
else:
|
||||||
r = Iso7816OS.formatResult(self, Iso7816OS.seekable(ins), le, data, sw, False)
|
r = Iso7816OS.formatResult(self, Iso7816OS.seekable(ins), le,
|
||||||
|
data, sw, False)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
class CryptoflexSE(Security_Environment):
|
class CryptoflexSE(Security_Environment):
|
||||||
def __init__(self, MF, SAM):
|
def __init__(self, MF, SAM):
|
||||||
Security_Environment.__init__(self, MF, SAM)
|
Security_Environment.__init__(self, MF, SAM)
|
||||||
@@ -95,11 +101,11 @@ class CryptoflexSE(Security_Environment):
|
|||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Util.randpool import RandomPool
|
from Crypto.Util.randpool import RandomPool
|
||||||
|
|
||||||
keynumber = p1 #TODO: Check if key exists
|
keynumber = p1 # TODO: Check if key exists
|
||||||
|
|
||||||
keylength_dict = {0x40: 256, 0x60: 512, 0x80: 1024}
|
keylength_dict = {0x40: 256, 0x60: 512, 0x80: 1024}
|
||||||
|
|
||||||
if not keylength_dict.has_key(p2):
|
if p2 not in keylength_dict:
|
||||||
raise SwError(SW["ERR_INCORRECTP1P2"])
|
raise SwError(SW["ERR_INCORRECTP1P2"])
|
||||||
else:
|
else:
|
||||||
keylength = keylength_dict[p2]
|
keylength = keylength_dict[p2]
|
||||||
@@ -110,31 +116,31 @@ class CryptoflexSE(Security_Environment):
|
|||||||
|
|
||||||
e_in = struct.unpack("<i", data)
|
e_in = struct.unpack("<i", data)
|
||||||
if e_in[0] != 65537:
|
if e_in[0] != 65537:
|
||||||
logging.warning("Warning: Exponents different from 65537 are ignored!" +\
|
logging.warning("Warning: Exponents different from 65537 are " +
|
||||||
"The Exponent given is %i" % e_in[0])
|
"ignored! The Exponent given is %i" % e_in[0])
|
||||||
|
|
||||||
#Encode Public key
|
# Encode Public key
|
||||||
n = PublicKey.__getstate__()['n']
|
n = PublicKey.__getstate__()['n']
|
||||||
n_str = inttostring(n)
|
n_str = inttostring(n)
|
||||||
n_str = n_str[::-1]
|
n_str = n_str[::-1]
|
||||||
e = PublicKey.__getstate__()['e']
|
e = PublicKey.__getstate__()['e']
|
||||||
e_str = inttostring(e, 4)
|
e_str = inttostring(e, 4)
|
||||||
e_str = e_str[::-1]
|
e_str = e_str[::-1]
|
||||||
pad = 187 * '\x30' #We don't have CRT components, so we need to pad
|
pad = 187 * '\x30' # We don't have CRT components, so we need to pad
|
||||||
pk_n = TLVutils.bertlv_pack(((0x81, len(n_str), n_str),
|
pk_n = TLVutils.bertlv_pack(((0x81, len(n_str), n_str),
|
||||||
(0x01, len(pad), pad),
|
(0x01, len(pad), pad),
|
||||||
(0x82, len(e_str), e_str)))
|
(0x82, len(e_str), e_str)))
|
||||||
#Private key
|
# Private key
|
||||||
d = PublicKey.__getstate__()['d']
|
d = PublicKey.__getstate__()['d']
|
||||||
|
|
||||||
#Write result to FID 10 12 EF-PUB-KEY
|
# Write result to FID 10 12 EF-PUB-KEY
|
||||||
df = self.mf.currentDF()
|
df = self.mf.currentDF()
|
||||||
ef_pub_key = df.select("fid", 0x1012)
|
ef_pub_key = df.select("fid", 0x1012)
|
||||||
ef_pub_key.writebinary([0], [pk_n])
|
ef_pub_key.writebinary([0], [pk_n])
|
||||||
data = ef_pub_key.getenc('data')
|
data = ef_pub_key.getenc('data')
|
||||||
|
|
||||||
#Write private key to FID 00 12 EF-PRI-KEY (not necessary?)
|
# Write private key to FID 00 12 EF-PRI-KEY (not necessary?)
|
||||||
#How to encode the private key?
|
# How to encode the private key?
|
||||||
ef_priv_key = df.select("fid", 0x0012)
|
ef_priv_key = df.select("fid", 0x0012)
|
||||||
ef_priv_key.writebinary([0], [inttostring(d)])
|
ef_priv_key.writebinary([0], [inttostring(d)])
|
||||||
data = ef_priv_key.getenc('data')
|
data = ef_priv_key.getenc('data')
|
||||||
@@ -148,11 +154,11 @@ class CryptoflexSAM(SAM):
|
|||||||
|
|
||||||
def generate_public_key_pair(self, p1, p2, data):
|
def generate_public_key_pair(self, p1, p2, data):
|
||||||
asym_key = self.current_SE.generate_public_key_pair(p1, p2, data)
|
asym_key = self.current_SE.generate_public_key_pair(p1, p2, data)
|
||||||
#TODO: Use SE instead (and remove SAM.set_asym_algorithm)
|
# TODO: Use SE instead (and remove SAM.set_asym_algorithm)
|
||||||
self.set_asym_algorithm(asym_key, 0x07)
|
self.set_asym_algorithm(asym_key, 0x07)
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], ""
|
||||||
|
|
||||||
def perform_security_operation(self, p1, p2, data):
|
def pezorform_security_operation(self, p1, p2, data):
|
||||||
"""
|
"""
|
||||||
In the cryptoflex card, this is the verify key command. A key is send
|
In the cryptoflex card, this is the verify key command. A key is send
|
||||||
to the card in plain text and compared to a key stored in the card.
|
to the card in plain text and compared to a key stored in the card.
|
||||||
@@ -163,21 +169,22 @@ class CryptoflexSAM(SAM):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], ""
|
||||||
#FIXME
|
# FIXME
|
||||||
#key = self._get_referenced_key(p1,p2)
|
# key = self._get_referenced_key(p1,p2)
|
||||||
#if key == data:
|
# if key == data:
|
||||||
# return SW["NORMAL"], ""
|
# return SW["NORMAL"], ""
|
||||||
#else:
|
# else:
|
||||||
# return SW["WARN_NOINFO63"], ""
|
# return SW["WARN_NOINFO63"], ""
|
||||||
|
|
||||||
def internal_authenticate(self, p1, p2, data):
|
def internal_authenticate(self, p1, p2, data):
|
||||||
data = data[::-1] #Reverse Byte order
|
data = data[::-1] # Reverse Byte order
|
||||||
sw, data = SAM.internal_authenticate(self, p1, p2, data)
|
sw, data = SAM.internal_authenticate(self, p1, p2, data)
|
||||||
if data != "":
|
if data != "":
|
||||||
data = data[::-1]
|
data = data[::-1]
|
||||||
return sw, data
|
return sw, data
|
||||||
|
|
||||||
class CryptoflexMF(MF): # {{{
|
|
||||||
|
class CryptoflexMF(MF): # {{{
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(p1, p2, data):
|
def create(p1, p2, data):
|
||||||
@@ -195,15 +202,17 @@ class CryptoflexMF(MF): # {{{
|
|||||||
args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
|
args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
|
||||||
new_file = TransparentStructureEF(**args)
|
new_file = TransparentStructureEF(**args)
|
||||||
elif data[6] == "\x02":
|
elif data[6] == "\x02":
|
||||||
if len(data)>16:
|
if len(data) > 16:
|
||||||
args["maxrecordsize"] = stringtoint(data[16])
|
args["maxrecordsize"] = stringtoint(data[16])
|
||||||
elif p2:
|
elif p2:
|
||||||
# if given a number of records
|
# if given a number of records
|
||||||
args["maxrecordsize"] = (stringtoint(data[2:4])/p2)
|
args["maxrecordsize"] = (stringtoint(data[2:4]) / p2)
|
||||||
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_FIXED_NOFURTHERINFO"]
|
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_FIXED_"
|
||||||
|
"NOFURTHERINFO"]
|
||||||
new_file = RecordStructureEF(**args)
|
new_file = RecordStructureEF(**args)
|
||||||
elif data[6] == "\x03":
|
elif data[6] == "\x03":
|
||||||
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_VARIABLE_NOFURTHERINFO"]
|
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_VARIABLE_"
|
||||||
|
"NOFURTHERINFO"]
|
||||||
new_file = RecordStructureEF(**args)
|
new_file = RecordStructureEF(**args)
|
||||||
elif data[6] == "\x04":
|
elif data[6] == "\x04":
|
||||||
args["filedescriptor"] = FDB["EFSTRUCTURE_CYCLIC_NOFURTHERINFO"]
|
args["filedescriptor"] = FDB["EFSTRUCTURE_CYCLIC_NOFURTHERINFO"]
|
||||||
@@ -216,7 +225,6 @@ class CryptoflexMF(MF): # {{{
|
|||||||
logging.error("unknown type: 0x%x" % ord(data[6]))
|
logging.error("unknown type: 0x%x" % ord(data[6]))
|
||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
|
|
||||||
return [new_file]
|
return [new_file]
|
||||||
|
|
||||||
def recordHandlingDecode(self, p1, p2):
|
def recordHandlingDecode(self, p1, p2):
|
||||||
@@ -225,7 +233,6 @@ class CryptoflexMF(MF): # {{{
|
|||||||
|
|
||||||
return MF.recordHandlingDecode(self, p1, p2)
|
return MF.recordHandlingDecode(self, p1, p2)
|
||||||
|
|
||||||
|
|
||||||
def dataUnitsDecodePlain(self, p1, p2, data):
|
def dataUnitsDecodePlain(self, p1, p2, data):
|
||||||
ef = self.currentEF()
|
ef = self.currentEF()
|
||||||
# FIXME: Cyberflex Access TM Software Development Kit Release 3C
|
# FIXME: Cyberflex Access TM Software Development Kit Release 3C
|
||||||
@@ -243,19 +250,23 @@ class CryptoflexMF(MF): # {{{
|
|||||||
integers and 'data' as binary string. Returns the status bytes as two
|
integers and 'data' as binary string. Returns the status bytes as two
|
||||||
byte long integer and the response data as binary string.
|
byte long integer and the response data as binary string.
|
||||||
"""
|
"""
|
||||||
P2_FCI = 0
|
P2_FCI = 0
|
||||||
P2_FCP = 1 << 2
|
P2_FCP = 1 << 2
|
||||||
P2_FMD = 2 << 2
|
P2_FMD = 2 << 2
|
||||||
P2_NONE = 3 << 2
|
P2_NONE = 3 << 2
|
||||||
file = self._selectFile(p1, p2, data)
|
file = self._selectFile(p1, p2, data)
|
||||||
|
|
||||||
if isinstance(file, EF):
|
if isinstance(file, EF):
|
||||||
size = inttostring(min(0xffff, len(file.getenc('data'))), 2) # File size (body only)
|
# File size (body only)
|
||||||
extra = chr(0) # RFU
|
size = inttostring(min(0xffff, len(file.getenc('data'))), 2)
|
||||||
if isinstance(file, RecordStructureEF) and file.hasFixedRecordSize() and not file.isCyclic():
|
extra = chr(0) # RFU
|
||||||
extra += chr(0) + chr(min(file.records, 0xff)) # Length of records
|
if (isinstance(file, RecordStructureEF) and
|
||||||
|
file.hasFixedRecordSize() and not file.isCyclic()):
|
||||||
|
# Length of records
|
||||||
|
extra += chr(0) + chr(min(file.records, 0xff))
|
||||||
elif isinstance(file, DF):
|
elif isinstance(file, DF):
|
||||||
size = inttostring(0xffff, 2) # Number of unused EEPROM bytes available in the DF
|
# Number of unused EEPROM bytes available in the DF
|
||||||
|
size = inttostring(0xffff, 2)
|
||||||
efcount = 0
|
efcount = 0
|
||||||
dfcount = 0
|
dfcount = 0
|
||||||
chv1 = None
|
chv1 = None
|
||||||
@@ -274,26 +285,26 @@ class CryptoflexMF(MF): # {{{
|
|||||||
if isinstance(f, DF):
|
if isinstance(f, DF):
|
||||||
dfcount += 1
|
dfcount += 1
|
||||||
if chv1:
|
if chv1:
|
||||||
extra = chr(1) # TODO LSB correct?
|
extra = chr(1) # TODO LSB correct?
|
||||||
else:
|
else:
|
||||||
extra = chr(0) # TODO LSB correct?
|
extra = chr(0) # TODO LSB correct?
|
||||||
extra += chr(efcount)
|
extra += chr(efcount)
|
||||||
extra += chr(dfcount)
|
extra += chr(dfcount)
|
||||||
extra += chr(0) # TODO Number of PINs and unblock CHV PINs
|
extra += chr(0) # TODO Number of PINs and unblock CHV PINs
|
||||||
extra += chr(0) # RFU
|
extra += chr(0) # RFU
|
||||||
if chv1:
|
if chv1:
|
||||||
extra += chr(0) # TODO Number of remaining CHV1 attempts
|
extra += chr(0) # TODO remaining CHV1 attempts
|
||||||
extra += chr(0) # TODO Number of remaining unblock CHV1 attempts
|
extra += chr(0) # TODO remaining unblock CHV1 attempts
|
||||||
if chv2:
|
if chv2:
|
||||||
extra += chr(0) # TODO CHV2 key status
|
extra += chr(0) # TODO CHV2 key status
|
||||||
extra += chr(0) # TODO CHV2 unblocking key status
|
extra += chr(0) # TODO CHV2 unblocking key status
|
||||||
|
|
||||||
data = inttostring(0, 2) # RFU
|
data = inttostring(0, 2) # RFU
|
||||||
data += size
|
data += size
|
||||||
data += inttostring(file.fid, 2)
|
data += inttostring(file.fid, 2)
|
||||||
data += inttostring(file.filedescriptor) # File type
|
data += inttostring(file.filedescriptor) # File type
|
||||||
data += inttostring(0, 4) # ACs TODO
|
data += inttostring(0, 4) # ACs TODO
|
||||||
data += chr(file.lifecycle & 1) # File status
|
data += chr(file.lifecycle & 1) # File status
|
||||||
data += chr(len(extra))
|
data += chr(len(extra))
|
||||||
data += extra
|
data += extra
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ import virtualsmartcard.CryptoUtils as vsCrypto
|
|||||||
from virtualsmartcard.SWutils import SwError, SW
|
from virtualsmartcard.SWutils import SwError, SW
|
||||||
from virtualsmartcard.utils import stringtoint
|
from virtualsmartcard.utils import stringtoint
|
||||||
|
|
||||||
import hashlib, struct
|
import hashlib
|
||||||
|
import struct
|
||||||
from os import urandom
|
from os import urandom
|
||||||
|
|
||||||
|
|
||||||
class ePass_SE(Security_Environment):
|
class ePass_SE(Security_Environment):
|
||||||
"""This class implements the Security Environment of the ICAO Passports. It
|
"""This class implements the Security Environment of the ICAO Passports. It
|
||||||
is required in order to use the send sequence counter for secure messaging.
|
is required in order to use the send sequence counter for secure messaging.
|
||||||
@@ -46,10 +48,11 @@ class ePass_SE(Security_Environment):
|
|||||||
|
|
||||||
self.ssc += 1
|
self.ssc += 1
|
||||||
checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key,
|
checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key,
|
||||||
data, self.cct.iv, self.ssc)
|
data, self.cct.iv, self.ssc)
|
||||||
|
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
|
|
||||||
class PassportSAM(SAM):
|
class PassportSAM(SAM):
|
||||||
"""
|
"""
|
||||||
SAM for ICAO ePassport. Implements Basic access control and key derivation
|
SAM for ICAO ePassport. Implements Basic access control and key derivation
|
||||||
@@ -58,7 +61,7 @@ class PassportSAM(SAM):
|
|||||||
def __init__(self, mf):
|
def __init__(self, mf):
|
||||||
import virtualsmartcard.SmartcardFilesystem as vsFS
|
import virtualsmartcard.SmartcardFilesystem as vsFS
|
||||||
|
|
||||||
SAM.__init__(self, None, None, mf, default_se = ePass_SE)
|
SAM.__init__(self, None, None, mf, default_se=ePass_SE)
|
||||||
|
|
||||||
ef_dg1 = vsFS.walk(mf, "\x00\x04\x01\x01")
|
ef_dg1 = vsFS.walk(mf, "\x00\x04\x01\x01")
|
||||||
dg1 = ef_dg1.readbinary(5)
|
dg1 = ef_dg1.readbinary(5)
|
||||||
@@ -77,7 +80,7 @@ class PassportSAM(SAM):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
MRZ_information = self.mrz2[0:10] + self.mrz2[13:20] + self.mrz2[21:28]
|
MRZ_information = self.mrz2[0:10] + self.mrz2[13:20] + self.mrz2[21:28]
|
||||||
H = hashlib.sha1(MRZ_information).digest() #pylint: disable=E1101
|
H = hashlib.sha1(MRZ_information).digest() # pylint: disable=E1101
|
||||||
self.KSeed = H[:16]
|
self.KSeed = H[:16]
|
||||||
self.KEnc = self.derive_key(self.KSeed, 1)
|
self.KEnc = self.derive_key(self.KSeed, 1)
|
||||||
self.KMac = self.derive_key(self.KSeed, 2)
|
self.KMac = self.derive_key(self.KSeed, 2)
|
||||||
@@ -91,7 +94,7 @@ class PassportSAM(SAM):
|
|||||||
Returns: Ka + Kb
|
Returns: Ka + Kb
|
||||||
Note: Does not adjust parity. Nobody uses that anyway ..."""
|
Note: Does not adjust parity. Nobody uses that anyway ..."""
|
||||||
D = seed + struct.pack(">i", c)
|
D = seed + struct.pack(">i", c)
|
||||||
H = hashlib.sha1(D).digest() #pylint: disable=E1101
|
H = hashlib.sha1(D).digest() # pylint: disable=E1101
|
||||||
Ka = H[0:8]
|
Ka = H[0:8]
|
||||||
Kb = H[8:16]
|
Kb = H[8:16]
|
||||||
return Ka + Kb
|
return Ka + Kb
|
||||||
@@ -101,29 +104,31 @@ class PassportSAM(SAM):
|
|||||||
the ICAO MRTD standard"""
|
the ICAO MRTD standard"""
|
||||||
rnd_icc = self.last_challenge
|
rnd_icc = self.last_challenge
|
||||||
|
|
||||||
#Receive Mutual Authenticate APDU from terminal
|
# Receive Mutual Authenticate APDU from terminal
|
||||||
#Decrypt data and check MAC
|
# Decrypt data and check MAC
|
||||||
Eifd = resp_data[:-8]
|
Eifd = resp_data[:-8]
|
||||||
padded_Eifd = vsCrypto.append_padding(self.current_SE.cct.blocklength, Eifd)
|
padded_Eifd = vsCrypto.append_padding(self.current_SE.cct.blocklength,
|
||||||
|
Eifd)
|
||||||
Mifd = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eifd)
|
Mifd = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eifd)
|
||||||
#Check the MAC
|
# Check the MAC
|
||||||
if not Mifd == resp_data[-8:]:
|
if not Mifd == resp_data[-8:]:
|
||||||
raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
|
raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
|
||||||
#Decrypt the data
|
# Decrypt the data
|
||||||
plain = vsCrypto.decrypt("DES3-CBC", self.KEnc, resp_data[:-8])
|
plain = vsCrypto.decrypt("DES3-CBC", self.KEnc, resp_data[:-8])
|
||||||
if plain[8:16] != rnd_icc:
|
if plain[8:16] != rnd_icc:
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
#Extract keying material from IFD, generate ICC keying material
|
# Extract keying material from IFD, generate ICC keying material
|
||||||
Kifd = plain[16:]
|
Kifd = plain[16:]
|
||||||
rnd_ifd = plain[:8]
|
rnd_ifd = plain[:8]
|
||||||
Kicc = urandom(16)
|
Kicc = urandom(16)
|
||||||
#Generate Answer
|
# Generate Answer
|
||||||
data = plain[8:16] + plain[:8] + Kicc
|
data = plain[8:16] + plain[:8] + Kicc
|
||||||
Eicc = vsCrypto.encrypt("DES3-CBC", self.KEnc, data)
|
Eicc = vsCrypto.encrypt("DES3-CBC", self.KEnc, data)
|
||||||
padded_Eicc = vsCrypto.append_padding(self.current_SE.cct.blocklength, Eicc)
|
padded_Eicc = vsCrypto.append_padding(self.current_SE.cct.blocklength,
|
||||||
|
Eicc)
|
||||||
Micc = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eicc)
|
Micc = vsCrypto.crypto_checksum("CC", self.KMac, padded_Eicc)
|
||||||
#Derive the final keys and set the current SE
|
# Derive the final keys and set the current SE
|
||||||
KSseed = vsCrypto.operation_on_string(Kicc, Kifd, lambda a, b: a^b)
|
KSseed = vsCrypto.operation_on_string(Kicc, Kifd, lambda a, b: a ^ b)
|
||||||
self.current_SE.ct.key = self.derive_key(KSseed, 1)
|
self.current_SE.ct.key = self.derive_key(KSseed, 1)
|
||||||
self.current_SE.cct.key = self.derive_key(KSseed, 2)
|
self.current_SE.cct.key = self.derive_key(KSseed, 2)
|
||||||
self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
|
self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])
|
||||||
|
|||||||
@@ -18,30 +18,42 @@
|
|||||||
|
|
||||||
from virtualsmartcard.SmartcardSAM import SAM
|
from virtualsmartcard.SmartcardSAM import SAM
|
||||||
from virtualsmartcard.VirtualSmartcard import Iso7816OS
|
from virtualsmartcard.VirtualSmartcard import Iso7816OS
|
||||||
from virtualsmartcard.SEutils import ControlReferenceTemplate, Security_Environment
|
from virtualsmartcard.SEutils import ControlReferenceTemplate, \
|
||||||
|
Security_Environment
|
||||||
from virtualsmartcard.SWutils import SwError, SW
|
from virtualsmartcard.SWutils import SwError, SW
|
||||||
from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE, SM_Class, ALGO_MAPPING, MAX_EXTENDED_LE, MAX_SHORT_LE
|
from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE, SM_Class, \
|
||||||
from virtualsmartcard.TLVutils import unpack, bertlv_pack, decodeDiscretionaryDataObjects, tlv_find_tag
|
ALGO_MAPPING, MAX_EXTENDED_LE, MAX_SHORT_LE
|
||||||
|
from virtualsmartcard.TLVutils import unpack, bertlv_pack, \
|
||||||
|
decodeDiscretionaryDataObjects, tlv_find_tag
|
||||||
from virtualsmartcard.SmartcardFilesystem import make_property
|
from virtualsmartcard.SmartcardFilesystem import make_property
|
||||||
from virtualsmartcard.utils import inttostring, R_APDU
|
from virtualsmartcard.utils import inttostring, R_APDU
|
||||||
import virtualsmartcard.CryptoUtils as vsCrypto
|
import virtualsmartcard.CryptoUtils as vsCrypto
|
||||||
from chat import CHAT, CVC, PACE_SEC, EAC_CTX
|
from chat import CHAT, CVC, PACE_SEC, EAC_CTX
|
||||||
|
import binascii
|
||||||
import eac
|
import eac
|
||||||
import logging, binascii
|
import logging
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
class NPAOS(Iso7816OS):
|
class NPAOS(Iso7816OS):
|
||||||
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None):
|
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE,
|
||||||
|
ef_cardsecurity=None, ef_cardaccess=None, ca_key=None,
|
||||||
|
cvca=None, disable_checks=False, esign_key=None,
|
||||||
|
esign_ca_cert=None, esign_cert=None):
|
||||||
Iso7816OS.__init__(self, mf, sam, ins2handler, maxle)
|
Iso7816OS.__init__(self, mf, sam, ins2handler, maxle)
|
||||||
self.ins2handler[0x86] = self.SAM.general_authenticate
|
self.ins2handler[0x86] = self.SAM.general_authenticate
|
||||||
self.ins2handler[0x2c] = self.SAM.reset_retry_counter
|
self.ins2handler[0x2c] = self.SAM.reset_retry_counter
|
||||||
|
|
||||||
# different ATR (Answer To Reset) values depending on used Chip version
|
# different ATR (Answer To Reset) values depending on used Chip version
|
||||||
# It's just a playground, because in past one of all those eID clients did not recognize the card correctly with newest ATR values
|
# It's just a playground, because in past one of all those eID clients
|
||||||
self.atr = '\x3B\x8A\x80\x01\x80\x31\xF8\x73\xF7\x41\xE0\x82\x90\x00\x75'
|
# did not recognize the card correctly with newest ATR values
|
||||||
#self.atr = '\x3B\x8A\x80\x01\x80\x31\xB8\x73\x84\x01\xE0\x82\x90\x00\x06'
|
self.atr = '\x3B\x8A\x80\x01\x80\x31\xF8\x73\xF7\x41\xE0\x82\x90' + \
|
||||||
#self.atr = '\x3B\x88\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x09'
|
'\x00\x75'
|
||||||
#self.atr = '\x3B\x87\x80\x01\x80\x31\xB8\x73\x84\x01\xE0\x19'
|
# 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.SAM.current_SE.disable_checks = disable_checks
|
self.SAM.current_SE.disable_checks = disable_checks
|
||||||
if ef_cardsecurity:
|
if ef_cardsecurity:
|
||||||
@@ -54,7 +66,8 @@ class NPAOS(Iso7816OS):
|
|||||||
self.SAM.current_SE.cvca = cvca
|
self.SAM.current_SE.cvca = cvca
|
||||||
if ca_key:
|
if ca_key:
|
||||||
self.SAM.current_SE.ca_key = ca_key
|
self.SAM.current_SE.ca_key = ca_key
|
||||||
esign = self.mf.select('dfname', '\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
|
esign = self.mf.select('dfname',
|
||||||
|
'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
|
||||||
if esign_ca_cert:
|
if esign_ca_cert:
|
||||||
ef = esign.select('fid', 0xC000)
|
ef = esign.select('fid', 0xC000)
|
||||||
ef.data = esign_ca_cert
|
ef.data = esign_ca_cert
|
||||||
@@ -62,7 +75,6 @@ class NPAOS(Iso7816OS):
|
|||||||
ef = esign.select('fid', 0xC001)
|
ef = esign.select('fid', 0xC001)
|
||||||
ef.data = esign_cert
|
ef.data = esign_cert
|
||||||
|
|
||||||
|
|
||||||
def formatResult(self, seekable, le, data, sw, sm):
|
def formatResult(self, seekable, le, data, sw, sm):
|
||||||
if seekable:
|
if seekable:
|
||||||
# when le = 0 then we want to have 0x9000. here we only have the
|
# when le = 0 then we want to have 0x9000. here we only have the
|
||||||
@@ -72,7 +84,7 @@ class NPAOS(Iso7816OS):
|
|||||||
if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE:
|
if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE:
|
||||||
sw = SW["WARN_EOFBEFORENEREAD"]
|
sw = SW["WARN_EOFBEFORENEREAD"]
|
||||||
|
|
||||||
if le != None:
|
if le is not None:
|
||||||
result = data[:le]
|
result = data[:le]
|
||||||
else:
|
else:
|
||||||
result = data[:0]
|
result = data[:0]
|
||||||
@@ -81,7 +93,6 @@ class NPAOS(Iso7816OS):
|
|||||||
sw, result = self.SAM.protect_result(sw, result)
|
sw, result = self.SAM.protect_result(sw, result)
|
||||||
except SwError as e:
|
except SwError as e:
|
||||||
logging.info(e.message)
|
logging.info(e.message)
|
||||||
import traceback, sys
|
|
||||||
traceback.print_exception(*sys.exc_info())
|
traceback.print_exception(*sys.exc_info())
|
||||||
sw = e.sw
|
sw = e.sw
|
||||||
result = ""
|
result = ""
|
||||||
@@ -105,22 +116,22 @@ class nPA_AT_CRT(ControlReferenceTemplate):
|
|||||||
CommunityID = None
|
CommunityID = None
|
||||||
|
|
||||||
def keyref_is_mrz(self):
|
def keyref_is_mrz(self):
|
||||||
if self.keyref_secret_key == '%c'% self.PACE_MRZ:
|
if self.keyref_secret_key == '%c' % self.PACE_MRZ:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def keyref_is_can(self):
|
def keyref_is_can(self):
|
||||||
if self.keyref_secret_key == '%c'% self.PACE_CAN:
|
if self.keyref_secret_key == '%c' % self.PACE_CAN:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def keyref_is_pin(self):
|
def keyref_is_pin(self):
|
||||||
if self.keyref_secret_key == '%c'% self.PACE_PIN:
|
if self.keyref_secret_key == '%c' % self.PACE_PIN:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def keyref_is_puk(self):
|
def keyref_is_puk(self):
|
||||||
if self.keyref_secret_key == '%c'% self.PACE_PUK:
|
if self.keyref_secret_key == '%c' % self.PACE_PUK:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -145,13 +156,16 @@ class nPA_AT_CRT(ControlReferenceTemplate):
|
|||||||
mapped_algo = ALGO_MAPPING[oidvalue]
|
mapped_algo = ALGO_MAPPING[oidvalue]
|
||||||
if mapped_algo == "DateOfBirth":
|
if mapped_algo == "DateOfBirth":
|
||||||
self.DateOfBirth = int(reference)
|
self.DateOfBirth = int(reference)
|
||||||
logging.info("Found reference DateOfBirth: " + str(self.DateOfBirth))
|
logging.info("Found reference DateOfBirth: " +
|
||||||
|
str(self.DateOfBirth))
|
||||||
elif mapped_algo == "DateOfExpiry":
|
elif mapped_algo == "DateOfExpiry":
|
||||||
self.DateOfExpiry = int(reference)
|
self.DateOfExpiry = int(reference)
|
||||||
logging.info("Found reference DateOfExpiry: " + str(self.DateOfExpiry))
|
logging.info("Found reference DateOfExpiry: " +
|
||||||
|
str(self.DateOfExpiry))
|
||||||
elif mapped_algo == "CommunityID":
|
elif mapped_algo == "CommunityID":
|
||||||
self.CommunityID = binascii.hexlify(reference)
|
self.CommunityID = binascii.hexlify(reference)
|
||||||
logging.info("Found reference CommunityID: " + str(self.CommunityID))
|
logging.info("Found reference CommunityID: " +
|
||||||
|
str(self.CommunityID))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91:
|
elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91:
|
||||||
@@ -170,7 +184,7 @@ class nPA_SE(Security_Environment):
|
|||||||
def __init__(self, MF, SAM):
|
def __init__(self, MF, SAM):
|
||||||
Security_Environment.__init__(self, MF, SAM)
|
Security_Environment.__init__(self, MF, SAM)
|
||||||
self.at = nPA_AT_CRT()
|
self.at = nPA_AT_CRT()
|
||||||
#This breaks support for 3DES
|
# This breaks support for 3DES
|
||||||
self.cct.blocklength = 16
|
self.cct.blocklength = 16
|
||||||
self.cct.algorithm = "CC"
|
self.cct.algorithm = "CC"
|
||||||
self.eac_step = 0
|
self.eac_step = 0
|
||||||
@@ -206,7 +220,7 @@ class nPA_SE(Security_Environment):
|
|||||||
if (p1, p2) != (0x00, 0x00):
|
if (p1, p2) != (0x00, 0x00):
|
||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
if self.eac_step == 0 and self.at.algorithm == "PACE":
|
if self.eac_step == 0 and self.at.algorithm == "PACE":
|
||||||
return self.__eac_pace_step1(data)
|
return self.__eac_pace_step1(data)
|
||||||
elif self.eac_step == 1 and self.at.algorithm == "PACE":
|
elif self.eac_step == 1 and self.at.algorithm == "PACE":
|
||||||
return self.__eac_pace_step2(data)
|
return self.__eac_pace_step2(data)
|
||||||
@@ -217,8 +231,12 @@ class nPA_SE(Security_Environment):
|
|||||||
elif self.eac_step == 5 and self.at.algorithm == "CA":
|
elif self.eac_step == 5 and self.at.algorithm == "CA":
|
||||||
return self.__eac_ca(data)
|
return self.__eac_ca(data)
|
||||||
elif self.eac_step == 6:
|
elif self.eac_step == 6:
|
||||||
# TODO implement RI "\x7c\x22\x81\x20\" is some prefix and the rest is our RI
|
# TODO implement 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"
|
# "\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"
|
||||||
|
|
||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
@@ -241,7 +259,7 @@ class nPA_SE(Security_Environment):
|
|||||||
|
|
||||||
def __eac_pace_step1(self, data):
|
def __eac_pace_step1(self, data):
|
||||||
tlv_data = nPA_SE.__unpack_general_authenticate(data)
|
tlv_data = nPA_SE.__unpack_general_authenticate(data)
|
||||||
if tlv_data != []:
|
if tlv_data != []:
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
|
|
||||||
if self.at.keyref_is_mrz():
|
if self.at.keyref_is_mrz():
|
||||||
@@ -277,27 +295,33 @@ class nPA_SE(Security_Environment):
|
|||||||
|
|
||||||
ef_card_security = self.mf.select('fid', 0x011d)
|
ef_card_security = self.mf.select('fid', 0x011d)
|
||||||
ef_card_security_data = ef_card_security.data
|
ef_card_security_data = ef_card_security.data
|
||||||
eac.EAC_CTX_init_ef_cardsecurity(ef_card_security_data, self.eac_ctx)
|
eac.EAC_CTX_init_ef_cardsecurity(ef_card_security_data,
|
||||||
|
self.eac_ctx)
|
||||||
|
|
||||||
if self.ca_key:
|
if self.ca_key:
|
||||||
ca_pubkey = eac.CA_get_pubkey(self.eac_ctx, ef_card_security_data)
|
ca_pubkey = eac.CA_get_pubkey(self.eac_ctx,
|
||||||
|
ef_card_security_data)
|
||||||
if 1 != eac.CA_set_key(self.eac_ctx, self.ca_key, ca_pubkey):
|
if 1 != eac.CA_set_key(self.eac_ctx, self.ca_key, ca_pubkey):
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
else:
|
else:
|
||||||
# we don't have a good CA key, so we simply generate an ephemeral one
|
# we don't have a good CA key, so we simply generate an
|
||||||
|
# ephemeral one
|
||||||
comp_pubkey = eac.TA_STEP3_generate_ephemeral_key(self.eac_ctx)
|
comp_pubkey = eac.TA_STEP3_generate_ephemeral_key(self.eac_ctx)
|
||||||
pubkey = eac.CA_STEP2_get_eph_pubkey(self.eac_ctx)
|
pubkey = eac.CA_STEP2_get_eph_pubkey(self.eac_ctx)
|
||||||
if not comp_pubkey or not pubkey:
|
if not comp_pubkey or not pubkey:
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
|
|
||||||
# save public key in EF.CardSecurity (and invalidate the signature)
|
# save public key in EF.CardSecurity (and invalidate the
|
||||||
|
# signature)
|
||||||
# FIXME this only works for the default EF.CardSecurity.
|
# FIXME this only works for the default EF.CardSecurity.
|
||||||
# Better use an ASN.1 parser to do this manipulation
|
# Better use an ASN.1 parser to do this manipulation
|
||||||
ef_card_security = self.mf.select('fid', 0x011d)
|
ef_card_security = self.mf.select('fid', 0x011d)
|
||||||
ef_card_security_data = ef_card_security.data
|
ef_card_security_data = ef_card_security.data
|
||||||
ef_card_security_data = ef_card_security_data[:61+4+239+2+1] + pubkey + ef_card_security_data[61+4+239+2+1+len(pubkey):]
|
ef_card_security_data = \
|
||||||
|
ef_card_security_data[:61+4+239+2+1] + pubkey + \
|
||||||
|
ef_card_security_data[61+4+239+2+1+len(pubkey):]
|
||||||
ef_card_security.data = ef_card_security_data
|
ef_card_security.data = ef_card_security_data
|
||||||
|
|
||||||
nonce = eac.PACE_STEP1_enc_nonce(self.eac_ctx, self.sec)
|
nonce = eac.PACE_STEP1_enc_nonce(self.eac_ctx, self.sec)
|
||||||
@@ -327,12 +351,14 @@ class nPA_SE(Security_Environment):
|
|||||||
|
|
||||||
self.eac_step += 1
|
self.eac_step += 1
|
||||||
|
|
||||||
return 0x9000, nPA_SE.__pack_general_authenticate([[0x82, len(pubkey), pubkey]])
|
return 0x9000, \
|
||||||
|
nPA_SE.__pack_general_authenticate([[0x82, len(pubkey), pubkey]])
|
||||||
|
|
||||||
def __eac_pace_step3(self, data):
|
def __eac_pace_step3(self, data):
|
||||||
tlv_data = nPA_SE.__unpack_general_authenticate(data)
|
tlv_data = nPA_SE.__unpack_general_authenticate(data)
|
||||||
|
|
||||||
self.my_pace_eph_pubkey = eac.PACE_STEP3B_generate_ephemeral_key(self.eac_ctx)
|
self.my_pace_eph_pubkey = \
|
||||||
|
eac.PACE_STEP3B_generate_ephemeral_key(self.eac_ctx)
|
||||||
if not self.my_pace_eph_pubkey:
|
if not self.my_pace_eph_pubkey:
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
@@ -341,18 +367,23 @@ class nPA_SE(Security_Environment):
|
|||||||
for tag, length, value in tlv_data:
|
for tag, length, value in tlv_data:
|
||||||
if tag == 0x83:
|
if tag == 0x83:
|
||||||
self.pace_opp_pub_key = value
|
self.pace_opp_pub_key = value
|
||||||
eac.PACE_STEP3B_compute_shared_secret(self.eac_ctx, self.pace_opp_pub_key)
|
eac.PACE_STEP3B_compute_shared_secret(self.eac_ctx,
|
||||||
|
self.pace_opp_pub_key)
|
||||||
else:
|
else:
|
||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
self.eac_step += 1
|
self.eac_step += 1
|
||||||
|
|
||||||
return 0x9000, nPA_SE.__pack_general_authenticate([[0x84, len(eph_pubkey), eph_pubkey]])
|
return 0x9000, \
|
||||||
|
nPA_SE.__pack_general_authenticate([[0x84, len(eph_pubkey),
|
||||||
|
eph_pubkey]])
|
||||||
|
|
||||||
def __eac_pace_step4(self, data):
|
def __eac_pace_step4(self, data):
|
||||||
tlv_data = nPA_SE.__unpack_general_authenticate(data)
|
tlv_data = nPA_SE.__unpack_general_authenticate(data)
|
||||||
eac.PACE_STEP3C_derive_keys(self.eac_ctx)
|
eac.PACE_STEP3C_derive_keys(self.eac_ctx)
|
||||||
my_token = eac.PACE_STEP3D_compute_authentication_token(self.eac_ctx, self.pace_opp_pub_key)
|
my_token = \
|
||||||
|
eac.PACE_STEP3D_compute_authentication_token(self.eac_ctx,
|
||||||
|
self.pace_opp_pub_key)
|
||||||
token = ""
|
token = ""
|
||||||
for tag, length, value in tlv_data:
|
for tag, length, value in tlv_data:
|
||||||
if tag == 0x85:
|
if tag == 0x85:
|
||||||
@@ -360,7 +391,8 @@ class nPA_SE(Security_Environment):
|
|||||||
else:
|
else:
|
||||||
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
|
||||||
|
|
||||||
if not my_token or 1 != eac.PACE_STEP3D_verify_authentication_token(self.eac_ctx, token):
|
ver = eac.PACE_STEP3D_verify_authentication_token(self.eac_ctx, token)
|
||||||
|
if not my_token or ver != 1:
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
|
|
||||||
@@ -394,7 +426,6 @@ class nPA_SE(Security_Environment):
|
|||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["WARN_NOINFO63"])
|
raise SwError(SW["WARN_NOINFO63"])
|
||||||
|
|
||||||
|
|
||||||
return 0x9000, nPA_SE.__pack_general_authenticate(result)
|
return 0x9000, nPA_SE.__pack_general_authenticate(result)
|
||||||
|
|
||||||
def __eac_ca(self, data):
|
def __eac_ca(self, data):
|
||||||
@@ -423,8 +454,9 @@ class nPA_SE(Security_Environment):
|
|||||||
# TODO activate SM
|
# TODO activate SM
|
||||||
self.new_encryption_ctx = eac.EAC_ID_CA
|
self.new_encryption_ctx = eac.EAC_ID_CA
|
||||||
|
|
||||||
return 0x9000, nPA_SE.__pack_general_authenticate([[0x81,
|
return 0x9000, \
|
||||||
len(nonce), nonce], [0x82, len(token), token]])
|
nPA_SE.__pack_general_authenticate([[0x81, len(nonce), nonce],
|
||||||
|
[0x82, len(token), token]])
|
||||||
|
|
||||||
def verify_certificate(self, p1, p2, data):
|
def verify_certificate(self, p1, p2, data):
|
||||||
if (p1, p2) != (0x00, 0xbe):
|
if (p1, p2) != (0x00, 0xbe):
|
||||||
@@ -444,8 +476,9 @@ class nPA_SE(Security_Environment):
|
|||||||
Authenticate the terminal to the card. Check whether Terminal correctly
|
Authenticate the terminal to the card. Check whether Terminal correctly
|
||||||
encrypted the given challenge or not
|
encrypted the given challenge or not
|
||||||
"""
|
"""
|
||||||
if self.dst.keyref_public_key: # TODO check if this is the correct CAR
|
if self.dst.keyref_public_key: # TODO check if this is the correct CAR
|
||||||
id_picc = eac.EAC_Comp(self.eac_ctx, eac.EAC_ID_PACE, self.my_pace_eph_pubkey)
|
id_picc = eac.EAC_Comp(self.eac_ctx, eac.EAC_ID_PACE,
|
||||||
|
self.my_pace_eph_pubkey)
|
||||||
|
|
||||||
# FIXME auxiliary_data might be from an older run of PACE
|
# FIXME auxiliary_data might be from an older run of PACE
|
||||||
if hasattr(self.at, "auxiliary_data"):
|
if hasattr(self.at, "auxiliary_data"):
|
||||||
@@ -454,7 +487,7 @@ class nPA_SE(Security_Environment):
|
|||||||
auxiliary_data = None
|
auxiliary_data = None
|
||||||
|
|
||||||
if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
|
if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
|
||||||
auxiliary_data, data):
|
auxiliary_data, data):
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
print "Could not verify Terminal's signature"
|
print "Could not verify Terminal's signature"
|
||||||
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
||||||
@@ -517,7 +550,7 @@ class nPA_SE(Security_Environment):
|
|||||||
tlv_sw = bertlv_pack([(tag, length, sw_str)])
|
tlv_sw = bertlv_pack([(tag, length, sw_str)])
|
||||||
return_data += tlv_sw
|
return_data += tlv_sw
|
||||||
|
|
||||||
if self.cct.algorithm == None:
|
if self.cct.algorithm is None:
|
||||||
raise SwError(SW["CONDITIONSNOTSATISFIED"])
|
raise SwError(SW["CONDITIONSNOTSATISFIED"])
|
||||||
elif self.cct.algorithm == "CC":
|
elif self.cct.algorithm == "CC":
|
||||||
tag = SM_Class["CHECKSUM"]
|
tag = SM_Class["CHECKSUM"]
|
||||||
@@ -535,13 +568,18 @@ class nPA_SE(Security_Environment):
|
|||||||
return sw, return_data
|
return sw, return_data
|
||||||
|
|
||||||
def compute_digital_signature(self, p1, p2, data):
|
def compute_digital_signature(self, p1, p2, data):
|
||||||
# TODO Signing with brainpoolP256r1 or any other key needs some more effort ;-)
|
# TODO Signing with brainpoolP256r1 or any other key needs some more
|
||||||
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'
|
# 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'
|
||||||
|
|
||||||
|
|
||||||
class nPA_SAM(SAM):
|
class nPA_SAM(SAM):
|
||||||
|
|
||||||
def __init__(self, eid_pin, can, mrz, puk, qes_pin, mf, default_se = nPA_SE):
|
def __init__(self, eid_pin, can, mrz, puk, qes_pin, mf, default_se=nPA_SE):
|
||||||
SAM.__init__(self, qes_pin, None, mf)
|
SAM.__init__(self, qes_pin, None, mf)
|
||||||
self.active = True
|
self.active = True
|
||||||
self.current_SE = default_se(self.mf, self)
|
self.current_SE = default_se(self.mf, self)
|
||||||
@@ -559,14 +597,15 @@ class nPA_SAM(SAM):
|
|||||||
if self.current_SE.eac_step < 4:
|
if self.current_SE.eac_step < 4:
|
||||||
raise SwError(SW["ERR_SECSTATUS"])
|
raise SwError(SW["ERR_SECSTATUS"])
|
||||||
|
|
||||||
# TODO check CAN and PIN for the correct character set
|
# TODO: check CAN and PIN for the correct character set
|
||||||
if p1 == 0x02:
|
if p1 == 0x02:
|
||||||
# change secret
|
# change secret
|
||||||
if p2 == self.current_SE.at.PACE_CAN:
|
if p2 == self.current_SE.at.PACE_CAN:
|
||||||
self.can = data
|
self.can = data
|
||||||
print "Changed CAN to %r" % self.can
|
print "Changed CAN to %r" % self.can
|
||||||
elif p2 == self.current_SE.at.PACE_PIN:
|
elif p2 == self.current_SE.at.PACE_PIN:
|
||||||
# TODO allow terminals to change the PIN with permission "CAN allowed"
|
# TODO: allow terminals to change the PIN with permission
|
||||||
|
# "CAN allowed"
|
||||||
if not self.current_SE.at.keyref_is_pin():
|
if not self.current_SE.at.keyref_is_pin():
|
||||||
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
|
||||||
self.eid_pin = data
|
self.eid_pin = data
|
||||||
@@ -583,7 +622,8 @@ class nPA_SAM(SAM):
|
|||||||
self.active = True
|
self.active = True
|
||||||
print "Resumed PIN"
|
print "Resumed PIN"
|
||||||
elif self.current_SE.at.keyref_is_pin():
|
elif self.current_SE.at.keyref_is_pin():
|
||||||
# PACE was successful with PIN, nothing to do resume/unblock
|
# PACE was successful with PIN, nothing to do
|
||||||
|
# resume/unblock
|
||||||
pass
|
pass
|
||||||
elif self.current_SE.at.keyref_is_puk():
|
elif self.current_SE.at.keyref_is_puk():
|
||||||
# TODO unblock PIN for signature
|
# TODO unblock PIN for signature
|
||||||
@@ -608,7 +648,8 @@ class nPA_SAM(SAM):
|
|||||||
if (p1 != 0x00 or p2 != 0x00):
|
if (p1 != 0x00 or p2 != 0x00):
|
||||||
raise SwError(SW["ERR_INCORRECTP1P2"])
|
raise SwError(SW["ERR_INCORRECTP1P2"])
|
||||||
|
|
||||||
self.last_challenge = eac.TA_STEP4_get_nonce(self.current_SE.eac_ctx)
|
self.last_challenge = \
|
||||||
|
eac.TA_STEP4_get_nonce(self.current_SE.eac_ctx)
|
||||||
if not self.last_challenge:
|
if not self.last_challenge:
|
||||||
eac.print_ossl_err()
|
eac.print_ossl_err()
|
||||||
raise SwError(SW["ERR_NOINFO69"])
|
raise SwError(SW["ERR_NOINFO69"])
|
||||||
@@ -624,11 +665,14 @@ class nPA_SAM(SAM):
|
|||||||
[(tag, _, value)] = structure = unpack(data)
|
[(tag, _, value)] = structure = unpack(data)
|
||||||
if tag == 6:
|
if tag == 6:
|
||||||
mapped_algo = ALGO_MAPPING[value]
|
mapped_algo = ALGO_MAPPING[value]
|
||||||
eid = self.mf.select('dfname', '\xe8\x07\x04\x00\x7f\x00\x07\x03\x02')
|
eid = self.mf.select('dfname', '\xe8\x07\x04\x00\x7f\x00'
|
||||||
|
'\x07\x03\x02')
|
||||||
if mapped_algo == "DateOfExpiry":
|
if mapped_algo == "DateOfExpiry":
|
||||||
[(_, _, [(_, _, mine)])] = unpack(eid.select('fid', 0x0103).data)
|
[(_, _, [(_, _, mine)])] = \
|
||||||
logging.info("DateOfExpiry: " + str(mine)
|
unpack(eid.select('fid', 0x0103).data)
|
||||||
+ "; reference: " + str(self.current_SE.at.DateOfExpiry))
|
logging.info("DateOfExpiry: " + str(mine) +
|
||||||
|
"; reference: " +
|
||||||
|
str(self.current_SE.at.DateOfExpiry))
|
||||||
if self.current_SE.at.DateOfExpiry < mine:
|
if self.current_SE.at.DateOfExpiry < mine:
|
||||||
print("Date of expiry verified")
|
print("Date of expiry verified")
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], ""
|
||||||
@@ -636,16 +680,19 @@ class nPA_SAM(SAM):
|
|||||||
print("Date of expiry not verified (expired)")
|
print("Date of expiry not verified (expired)")
|
||||||
return SW["WARN_NOINFO63"], ""
|
return SW["WARN_NOINFO63"], ""
|
||||||
elif mapped_algo == "DateOfBirth":
|
elif mapped_algo == "DateOfBirth":
|
||||||
[(_, _, [(_, _, mine)])] = unpack(eid.select('fid', 0x0108).data)
|
[(_, _, [(_, _, mine)])] = \
|
||||||
|
unpack(eid.select('fid', 0x0108).data)
|
||||||
# case1: YYYYMMDD -> good
|
# case1: YYYYMMDD -> good
|
||||||
# case2: YYYYMM -> mapped to last day of given month, i.e. YYYYMM31 ;-)
|
# case2: YYYYMM -> mapped to last day of given month,
|
||||||
|
# i.e. YYYYMM31 ;-)
|
||||||
# case3: YYYY -> mapped to YYYY-12-31
|
# case3: YYYY -> mapped to YYYY-12-31
|
||||||
if len(str(mine)) == 6:
|
if len(str(mine)) == 6:
|
||||||
mine = int(str(mine) + "31")
|
mine = int(str(mine) + "31")
|
||||||
elif len(str(mine)) == 4:
|
elif len(str(mine)) == 4:
|
||||||
mine = int(str(mine) + "1231")
|
mine = int(str(mine) + "1231")
|
||||||
logging.info("DateOfBirth: " + str(mine)
|
logging.info("DateOfBirth: " + str(mine) +
|
||||||
+ "; reference: " + str(self.current_SE.at.DateOfExpiry))
|
"; reference: " +
|
||||||
|
str(self.current_SE.at.DateOfExpiry))
|
||||||
if self.current_SE.at.DateOfBirth < mine:
|
if self.current_SE.at.DateOfBirth < mine:
|
||||||
print("Date of birth verified (old enough)")
|
print("Date of birth verified (old enough)")
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], ""
|
||||||
@@ -653,15 +700,18 @@ class nPA_SAM(SAM):
|
|||||||
print("Date of birth not verified (too young)")
|
print("Date of birth not verified (too young)")
|
||||||
return SW["WARN_NOINFO63"], ""
|
return SW["WARN_NOINFO63"], ""
|
||||||
elif mapped_algo == "CommunityID":
|
elif mapped_algo == "CommunityID":
|
||||||
[(_, _, [(_, _, mine)])] = unpack(eid.select('fid', 0x0112).data)
|
[(_, _, [(_, _, mine)])] = \
|
||||||
|
unpack(eid.select('fid', 0x0112).data)
|
||||||
mine = binascii.hexlify(mine)
|
mine = binascii.hexlify(mine)
|
||||||
logging.info("CommunityID: " + str(mine)
|
logging.info("CommunityID: " + str(mine) +
|
||||||
+ "; reference: " + str(self.current_SE.at.CommunityID))
|
"; reference: " +
|
||||||
|
str(self.current_SE.at.CommunityID))
|
||||||
if mine.startswith(self.current_SE.at.CommunityID):
|
if mine.startswith(self.current_SE.at.CommunityID):
|
||||||
print("Community ID verified (living there)")
|
print("Community ID verified (living there)")
|
||||||
return SW["NORMAL"], ""
|
return SW["NORMAL"], ""
|
||||||
else:
|
else:
|
||||||
print("Community ID not verified (not living there)")
|
print("Community ID not verified (not living"
|
||||||
|
"there)")
|
||||||
return SW["WARN_NOINFO63"], ""
|
return SW["WARN_NOINFO63"], ""
|
||||||
else:
|
else:
|
||||||
return SwError(SW["ERR_DATANOTFOUND"])
|
return SwError(SW["ERR_DATANOTFOUND"])
|
||||||
@@ -677,10 +727,12 @@ class nPA_SAM(SAM):
|
|||||||
protocol = "PACE"
|
protocol = "PACE"
|
||||||
else:
|
else:
|
||||||
protocol = "CA"
|
protocol = "CA"
|
||||||
logging.info("switching to new encryption context established in %s:" % protocol)
|
logging.info("switching to new encryption context established in "
|
||||||
|
"%s:" % protocol)
|
||||||
logging.info(eac.EAC_CTX_print_private(self.current_SE.eac_ctx, 4))
|
logging.info(eac.EAC_CTX_print_private(self.current_SE.eac_ctx, 4))
|
||||||
|
|
||||||
eac.EAC_CTX_set_encryption_ctx(self.current_SE.eac_ctx, self.current_SE.new_encryption_ctx)
|
eac.EAC_CTX_set_encryption_ctx(self.current_SE.eac_ctx,
|
||||||
|
self.current_SE.new_encryption_ctx)
|
||||||
|
|
||||||
delattr(self.current_SE, "new_encryption_ctx")
|
delattr(self.current_SE, "new_encryption_ctx")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user