Merge pull request #77 from d0/pep8

Refactor card implementations to PEP8
This commit is contained in:
Frank Morgner
2016-04-07 16:11:03 +02:00
6 changed files with 279 additions and 205 deletions

View File

@@ -153,10 +153,10 @@ class SAM(object):
:param cipher: Public/private key object from used for encryption
:param keytype: Type of the public key (e.g. RSA, DSA)
"""
if keytype not in range(0x07, 0x08):
if keytype not in (0x07, 0x08):
raise SwError(SW["ERR_INCORRECTP1P2"])
else:
self.cipher = type
self.cipher = keytype
self.asym_key = cipher
def verify(self, p1, p2, PIN):

View File

@@ -1,18 +1,18 @@
#
# Copyright (C) 2011-2015 Frank Morgner
#
#
# This file is part of virtualsmartcard.
#
#
# virtualsmartcard is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
#
# virtualsmartcard is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
#
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
#
@@ -32,16 +32,18 @@ class HandlerTestOS(SmartcardOS):
lastCommandOffcut = ''
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):
le = (ord(msg[2])<<8)+ord(msg[3])
return ''.join([chr(num&0xff) for num in xrange(le)])
le = (ord(msg[2]) << 8) + ord(msg[3])
return ''.join([chr(num & 0xff) for num in xrange(le)])
def execute(self, msg):
ok = '\x90\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')
return ok
elif msg.startswith('\x80\x38\x00'):
@@ -65,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 '' + chr(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

@@ -1,24 +1,27 @@
#
# Copyright (C) 2015 Frank Morgner
#
#
# This file is part of virtualsmartcard.
#
#
# virtualsmartcard is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
#
# virtualsmartcard is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
#
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
#
import atexit
import logging
import smartcard
import sys
from virtualsmartcard.VirtualSmartcard import SmartcardOS
import smartcard, logging, sys, atexit
class RelayOS(SmartcardOS):
@@ -30,7 +33,8 @@ class RelayOS(SmartcardOS):
"""
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
@@ -80,7 +84,7 @@ class RelayOS(SmartcardOS):
sys.exit()
return "".join([chr(b) for b in atr])
def powerUp(self):
# When powerUp is called multiple times the session is valid (and the
# card is implicitly powered) we can check for an ATR. But when

View File

@@ -1,18 +1,18 @@
#
# Copyright (C) 2011 Dominik Oepen
#
#
# This file is part of virtualsmartcard.
#
#
# virtualsmartcard is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
#
# virtualsmartcard is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
#
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
@@ -27,10 +27,11 @@ from virtualsmartcard.SmartcardFilesystem import MF, TransparentStructureEF, \
RecordStructureEF, DF, EF
from virtualsmartcard.ConstantDefinitions import FDB, MAX_SHORT_LE
import struct, logging
import struct
import logging
class CryptoflexOS(Iso7816OS):
class CryptoflexOS(Iso7816OS):
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_SHORT_LE):
Iso7816OS.__init__(self, mf, sam, ins2handler, maxle)
self.atr = '\x3B\xE2\x00\x00\x40\x20\x49\x06'
@@ -43,14 +44,17 @@ class CryptoflexOS(Iso7816OS):
c = C_APDU(msg)
except ValueError as e:
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:
sw, result = self.ins2handler.get(c.ins, notImplemented)(c.p1, c.p2, c.data)
#print type(result)
sw, result = self.ins2handler.get(c.ins, notImplemented)(c.p1,
c.p2,
c.data)
# print type(result)
except SwError as e:
logging.info(e.message)
#traceback.print_exception(*sys.exc_info())
# traceback.print_exception(*sys.exc_info())
sw = e.sw
result = ""
@@ -62,20 +66,22 @@ class CryptoflexOS(Iso7816OS):
# cryptoflex does not inpterpret le==0 as maxle
self.lastCommandSW = sw
self.lastCommandOffcut = data
r = R_APDU(inttostring(SW["ERR_WRONGLENGTH"] +\
min(0xff, len(data)))).render()
r = R_APDU(inttostring(SW["ERR_WRONGLENGTH"] +
min(0xff, len(data)))).render()
else:
if ins == 0xa4 and len(data):
# get response should be followed by select file
self.lastCommandSW = sw
self.lastCommandOffcut = data
r = R_APDU(inttostring(SW["NORMAL_REST"] +\
min(0xff, len(data)))).render()
r = R_APDU(inttostring(SW["NORMAL_REST"] +
min(0xff, len(data)))).render()
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
class CryptoflexSE(Security_Environment):
def __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.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}
if not keylength_dict.has_key(p2):
if p2 not in keylength_dict:
raise SwError(SW["ERR_INCORRECTP1P2"])
else:
keylength = keylength_dict[p2]
@@ -110,34 +116,34 @@ class CryptoflexSE(Security_Environment):
e_in = struct.unpack("<i", data)
if e_in[0] != 65537:
logging.warning("Warning: Exponents different from 65537 are ignored!" +\
"The Exponent given is %i" % e_in[0])
logging.warning("Warning: Exponents different from 65537 are " +
"ignored! The Exponent given is %i" % e_in[0])
#Encode Public key
# Encode Public key
n = PublicKey.__getstate__()['n']
n_str = inttostring(n)
n_str = n_str[::-1]
e = PublicKey.__getstate__()['e']
e_str = inttostring(e, 4)
e_str = e_str[::-1]
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),
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),
(0x01, len(pad), pad),
(0x82, len(e_str), e_str)))
#Private key
# Private key
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()
ef_pub_key = df.select("fid", 0x1012)
ef_pub_key.writebinary([0], [pk_n])
data = ef_pub_key.getenc('data')
#Write private key to FID 00 12 EF-PRI-KEY (not necessary?)
#How to encode the private key?
# Write private key to FID 00 12 EF-PRI-KEY (not necessary?)
# How to encode the private key?
ef_priv_key = df.select("fid", 0x0012)
ef_priv_key.writebinary([0], [inttostring(d)])
data = ef_priv_key.getenc('data')
ef_priv_key.writebinary([0], [inttostring(d)])
data = ef_priv_key.getenc('data')
return PublicKey
@@ -145,39 +151,40 @@ class CryptoflexSAM(SAM):
def __init__(self, mf=None):
SAM.__init__(self, None, None, mf, default_se=CryptoflexSE)
self.current_SE = self.default_se(self.mf, self)
def generate_public_key_pair(self, 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)
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
to the card in plain text and compared to a key stored in the card.
This is used for authentication
:param data: Contains the key to be verified
:returns: SW[NORMAL] in case of success otherwise SW[WARN_NOINFO63]
:returns: SW[NORMAL] in case of success otherwise SW[WARN_NOINFO63]
"""
return SW["NORMAL"], ""
#FIXME
#key = self._get_referenced_key(p1,p2)
#if key == data:
# return SW["NORMAL"], ""
#else:
# return SW["WARN_NOINFO63"], ""
# FIXME
# key = self._get_referenced_key(p1,p2)
# if key == data:
# return SW["NORMAL"], ""
# else:
# return SW["WARN_NOINFO63"], ""
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)
if data != "":
data = data[::-1]
return sw, data
class CryptoflexMF(MF): # {{{
class CryptoflexMF(MF): # {{{
@staticmethod
def create(p1, p2, data):
@@ -195,15 +202,17 @@ class CryptoflexMF(MF): # {{{
args["filedescriptor"] = FDB["EFSTRUCTURE_TRANSPARENT"]
new_file = TransparentStructureEF(**args)
elif data[6] == "\x02":
if len(data)>16:
if len(data) > 16:
args["maxrecordsize"] = stringtoint(data[16])
elif p2:
# if given a number of records
args["maxrecordsize"] = (stringtoint(data[2:4])/p2)
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_FIXED_NOFURTHERINFO"]
args["maxrecordsize"] = (stringtoint(data[2:4]) / p2)
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_FIXED_"
"NOFURTHERINFO"]
new_file = RecordStructureEF(**args)
elif data[6] == "\x03":
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_VARIABLE_NOFURTHERINFO"]
args["filedescriptor"] = FDB["EFSTRUCTURE_LINEAR_VARIABLE_"
"NOFURTHERINFO"]
new_file = RecordStructureEF(**args)
elif data[6] == "\x04":
args["filedescriptor"] = FDB["EFSTRUCTURE_CYCLIC_NOFURTHERINFO"]
@@ -216,7 +225,6 @@ class CryptoflexMF(MF): # {{{
logging.error("unknown type: 0x%x" % ord(data[6]))
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
return [new_file]
def recordHandlingDecode(self, p1, p2):
@@ -225,7 +233,6 @@ class CryptoflexMF(MF): # {{{
return MF.recordHandlingDecode(self, p1, p2)
def dataUnitsDecodePlain(self, p1, p2, data):
ef = self.currentEF()
# 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
byte long integer and the response data as binary string.
"""
P2_FCI = 0
P2_FCP = 1 << 2
P2_FMD = 2 << 2
P2_FCI = 0
P2_FCP = 1 << 2
P2_FMD = 2 << 2
P2_NONE = 3 << 2
file = self._selectFile(p1, p2, data)
if isinstance(file, EF):
size = inttostring(min(0xffff, len(file.getenc('data'))), 2) # File size (body only)
extra = chr(0) # RFU
if isinstance(file, RecordStructureEF) and file.hasFixedRecordSize() and not file.isCyclic():
extra += chr(0) + chr(min(file.records, 0xff)) # Length of records
# File size (body only)
size = inttostring(min(0xffff, len(file.getenc('data'))), 2)
extra = chr(0) # RFU
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):
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
dfcount = 0
chv1 = None
@@ -274,26 +285,26 @@ class CryptoflexMF(MF): # {{{
if isinstance(f, DF):
dfcount += 1
if chv1:
extra = chr(1) # TODO LSB correct?
extra = chr(1) # TODO LSB correct?
else:
extra = chr(0) # TODO LSB correct?
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 += chr(0) # TODO Number of PINs and unblock CHV PINs
extra += chr(0) # RFU
if chv1:
extra += chr(0) # TODO Number of remaining CHV1 attempts
extra += chr(0) # TODO Number of remaining unblock CHV1 attempts
extra += chr(0) # TODO remaining CHV1 attempts
extra += chr(0) # TODO remaining unblock CHV1 attempts
if chv2:
extra += chr(0) # TODO CHV2 key status
extra += chr(0) # TODO CHV2 unblocking key status
extra += chr(0) # TODO CHV2 key status
extra += chr(0) # TODO CHV2 unblocking key status
data = inttostring(0, 2) # RFU
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 += inttostring(file.filedescriptor) # File type
data += inttostring(0, 4) # ACs TODO
data += chr(file.lifecycle & 1) # File status
data += chr(len(extra))
data += extra

View File

@@ -1,18 +1,18 @@
#
# Copyright (C) 2011 Dominik Oepen
#
#
# This file is part of virtualsmartcard.
#
#
# virtualsmartcard is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
#
# virtualsmartcard is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
#
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
@@ -22,9 +22,11 @@ import virtualsmartcard.CryptoUtils as vsCrypto
from virtualsmartcard.SWutils import SwError, SW
from virtualsmartcard.utils import stringtoint
import hashlib, struct
import hashlib
import struct
from os import urandom
class ePass_SE(Security_Environment):
"""This class implements the Security Environment of the ICAO Passports. It
is required in order to use the send sequence counter for secure messaging.
@@ -35,7 +37,7 @@ class ePass_SE(Security_Environment):
self.cct.algorithm = "CC"
self.cct.blocklength = 8
self.ct.algorithm = "DES3-CBC"
def compute_cryptographic_checksum(self, p1, p2, data):
"""
Compute a cryptographic checksum (e.g. MAC) for the given data.
@@ -43,45 +45,46 @@ class ePass_SE(Security_Environment):
"""
if p1 != 0x8E or p2 != 0x80:
raise SwError(SW["ERR_INCORRECTP1P2"])
self.ssc += 1
checksum = vsCrypto.crypto_checksum(self.cct.algorithm, self.cct.key,
data, self.cct.iv, self.ssc)
data, self.cct.iv, self.ssc)
return checksum
class PassportSAM(SAM):
class PassportSAM(SAM):
"""
SAM for ICAO ePassport. Implements Basic access control and key derivation
for Secure Messaging.
for Secure Messaging.
"""
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")
dg1 = ef_dg1.readbinary(5)
self.mrz1 = dg1[:43]
self.mrz2 = dg1[44:]
self.KSeed = None
self.KSeed = None
self.KEnc = None
self.KMac = None
self.__computeKeys()
def __computeKeys(self):
"""
Computes the keys depending on the machine readable
zone of the passport according to TR-PKI mrtds ICC read-only
Computes the keys depending on the machine readable
zone of the passport according to TR-PKI mrtds ICC read-only
access v1.1 annex E.1.
"""
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.KEnc = self.derive_key(self.KSeed, 1)
self.KMac = self.derive_key(self.KSeed, 2)
@staticmethod
def derive_key(seed, c):
"""
@@ -91,39 +94,41 @@ class PassportSAM(SAM):
Returns: Ka + Kb
Note: Does not adjust parity. Nobody uses that anyway ..."""
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]
Kb = H[8:16]
return Ka + Kb
def external_authenticate(self, p1, p2, resp_data):
"""Performs the basic access control protocol as defined in
the ICAO MRTD standard"""
rnd_icc = self.last_challenge
#Receive Mutual Authenticate APDU from terminal
#Decrypt data and check MAC
# Receive Mutual Authenticate APDU from terminal
# Decrypt data and check MAC
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)
#Check the MAC
# Check the MAC
if not Mifd == resp_data[-8:]:
raise SwError(SW["ERR_SECMESSOBJECTSINCORRECT"])
#Decrypt the data
# Decrypt the data
plain = vsCrypto.decrypt("DES3-CBC", self.KEnc, resp_data[:-8])
if plain[8:16] != rnd_icc:
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:]
rnd_ifd = plain[:8]
Kicc = urandom(16)
#Generate Answer
# Generate Answer
data = plain[8:16] + plain[:8] + Kicc
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)
#Derive the final keys and set the current SE
KSseed = vsCrypto.operation_on_string(Kicc, Kifd, lambda a, b: a^b)
# Derive the final keys and set the current SE
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.cct.key = self.derive_key(KSseed, 2)
self.current_SE.ssc = stringtoint(rnd_icc[-4:] + rnd_ifd[-4:])

View File

@@ -1,47 +1,59 @@
#
# Copyright (C) 2011 Dominik Oepen, Frank Morgner
#
#
# This file is part of virtualsmartcard.
#
#
# virtualsmartcard is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
#
# virtualsmartcard is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
#
# You should have received a copy of the GNU General Public License along with
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
from virtualsmartcard.SmartcardSAM import SAM
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.ConstantDefinitions import CRT_TEMPLATE, SM_Class, ALGO_MAPPING, MAX_EXTENDED_LE, MAX_SHORT_LE
from virtualsmartcard.TLVutils import unpack, bertlv_pack, decodeDiscretionaryDataObjects, tlv_find_tag
from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE, SM_Class, \
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.utils import inttostring, R_APDU
import virtualsmartcard.CryptoUtils as vsCrypto
from chat import CHAT, CVC, PACE_SEC, EAC_CTX
import binascii
import eac
import logging, binascii
import logging
import sys
import traceback
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)
self.ins2handler[0x86] = self.SAM.general_authenticate
self.ins2handler[0x2c] = self.SAM.reset_retry_counter
# 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'
# 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.SAM.current_SE.disable_checks = disable_checks
if ef_cardsecurity:
@@ -54,7 +66,8 @@ class NPAOS(Iso7816OS):
self.SAM.current_SE.cvca = cvca
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')
esign = self.mf.select('dfname',
'\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
if esign_ca_cert:
ef = esign.select('fid', 0xC000)
ef.data = esign_ca_cert
@@ -62,7 +75,6 @@ class NPAOS(Iso7816OS):
ef = esign.select('fid', 0xC001)
ef.data = esign_cert
def formatResult(self, seekable, le, data, sw, sm):
if seekable:
# 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:
sw = SW["WARN_EOFBEFORENEREAD"]
if le != None:
if le is not None:
result = data[:le]
else:
result = data[:0]
@@ -81,7 +93,6 @@ class NPAOS(Iso7816OS):
sw, result = self.SAM.protect_result(sw, result)
except SwError as e:
logging.info(e.message)
import traceback, sys
traceback.print_exception(*sys.exc_info())
sw = e.sw
result = ""
@@ -105,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 == '%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 == '%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 == '%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 == '%c' % self.PACE_PUK:
return True
return False
@@ -145,13 +156,16 @@ class nPA_AT_CRT(ControlReferenceTemplate):
mapped_algo = ALGO_MAPPING[oidvalue]
if mapped_algo == "DateOfBirth":
self.DateOfBirth = int(reference)
logging.info("Found reference DateOfBirth: " + str(self.DateOfBirth))
logging.info("Found reference DateOfBirth: " +
str(self.DateOfBirth))
elif mapped_algo == "DateOfExpiry":
self.DateOfExpiry = int(reference)
logging.info("Found reference DateOfExpiry: " + str(self.DateOfExpiry))
logging.info("Found reference DateOfExpiry: " +
str(self.DateOfExpiry))
elif mapped_algo == "CommunityID":
self.CommunityID = binascii.hexlify(reference)
logging.info("Found reference CommunityID: " + str(self.CommunityID))
logging.info("Found reference CommunityID: " +
str(self.CommunityID))
except:
pass
elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91:
@@ -169,8 +183,8 @@ class nPA_SE(Security_Environment):
def __init__(self, MF, SAM):
Security_Environment.__init__(self, MF, SAM)
self.at = nPA_AT_CRT()
#This breaks support for 3DES
self.at = nPA_AT_CRT()
# This breaks support for 3DES
self.cct.blocklength = 16
self.cct.algorithm = "CC"
self.eac_step = 0
@@ -183,7 +197,7 @@ class nPA_SE(Security_Environment):
def _set_SE(self, p2, data):
sw, resp = Security_Environment._set_SE(self, p2, data)
if self.at.algorithm == "PACE":
if self.at.keyref_is_pin():
if self.sam.counter <= 0:
@@ -206,7 +220,7 @@ class nPA_SE(Security_Environment):
if (p1, p2) != (0x00, 0x00):
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)
elif self.eac_step == 1 and self.at.algorithm == "PACE":
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":
return self.__eac_ca(data)
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"
# 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"
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
@@ -241,7 +259,7 @@ class nPA_SE(Security_Environment):
def __eac_pace_step1(self, data):
tlv_data = nPA_SE.__unpack_general_authenticate(data)
if tlv_data != []:
if tlv_data != []:
raise SwError(SW["WARN_NOINFO63"])
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_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:
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):
eac.print_ossl_err()
raise SwError(SW["WARN_NOINFO63"])
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)
pubkey = eac.CA_STEP2_get_eph_pubkey(self.eac_ctx)
if not comp_pubkey or not pubkey:
eac.print_ossl_err()
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.
# Better use an ASN.1 parser to do this manipulation
ef_card_security = self.mf.select('fid', 0x011d)
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
nonce = eac.PACE_STEP1_enc_nonce(self.eac_ctx, self.sec)
@@ -327,12 +351,14 @@ class nPA_SE(Security_Environment):
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):
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:
eac.print_ossl_err()
raise SwError(SW["WARN_NOINFO63"])
@@ -341,18 +367,23 @@ class nPA_SE(Security_Environment):
for tag, length, value in tlv_data:
if tag == 0x83:
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:
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
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):
tlv_data = nPA_SE.__unpack_general_authenticate(data)
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 = ""
for tag, length, value in tlv_data:
if tag == 0x85:
@@ -360,7 +391,8 @@ class nPA_SE(Security_Environment):
else:
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()
raise SwError(SW["WARN_NOINFO63"])
@@ -394,7 +426,6 @@ class nPA_SE(Security_Environment):
eac.print_ossl_err()
raise SwError(SW["WARN_NOINFO63"])
return 0x9000, nPA_SE.__pack_general_authenticate(result)
def __eac_ca(self, data):
@@ -409,7 +440,7 @@ class nPA_SE(Security_Environment):
if eac.CA_STEP4_compute_shared_secret(self.eac_ctx, pubkey) != 1:
eac.print_ossl_err()
raise SwError(SW["ERR_NOINFO69"])
raise SwError(SW["ERR_NOINFO69"])
nonce, token = eac.CA_STEP5_derive_keys(self.eac_ctx, pubkey)
if not nonce or not token:
@@ -423,8 +454,9 @@ class nPA_SE(Security_Environment):
# TODO activate SM
self.new_encryption_ctx = eac.EAC_ID_CA
return 0x9000, nPA_SE.__pack_general_authenticate([[0x81,
len(nonce), nonce], [0x82, len(token), token]])
return 0x9000, \
nPA_SE.__pack_general_authenticate([[0x81, len(nonce), nonce],
[0x82, len(token), token]])
def verify_certificate(self, p1, p2, data):
if (p1, p2) != (0x00, 0xbe):
@@ -433,7 +465,7 @@ class nPA_SE(Security_Environment):
cert = bertlv_pack([[0x7f21, len(data), data]])
if 1 != eac.TA_STEP2_import_certificate(self.eac_ctx, cert):
eac.print_ossl_err()
raise SwError(SW["ERR_NOINFO69"])
raise SwError(SW["ERR_NOINFO69"])
print "Imported Certificate"
@@ -444,8 +476,9 @@ class nPA_SE(Security_Environment):
Authenticate the terminal to the card. Check whether Terminal correctly
encrypted the given challenge or not
"""
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)
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)
# FIXME auxiliary_data might be from an older run of PACE
if hasattr(self.at, "auxiliary_data"):
@@ -454,7 +487,7 @@ class nPA_SE(Security_Environment):
auxiliary_data = None
if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
auxiliary_data, data):
auxiliary_data, data):
eac.print_ossl_err()
print "Could not verify Terminal's signature"
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
@@ -471,7 +504,7 @@ class nPA_SE(Security_Environment):
checksum = eac.EAC_authenticate(self.eac_ctx, data)
if not checksum:
eac.print_ossl_err()
raise SwError(SW["ERR_NOINFO69"])
raise SwError(SW["ERR_NOINFO69"])
return checksum
@@ -480,7 +513,7 @@ class nPA_SE(Security_Environment):
cipher = eac.EAC_encrypt(self.eac_ctx, padded)
if not cipher:
eac.print_ossl_err()
raise SwError(SW["ERR_NOINFO69"])
raise SwError(SW["ERR_NOINFO69"])
return cipher
@@ -488,14 +521,14 @@ class nPA_SE(Security_Environment):
plain = eac.EAC_decrypt(self.eac_ctx, data)
if not plain:
eac.print_ossl_err()
raise SwError(SW["ERR_NOINFO69"])
raise SwError(SW["ERR_NOINFO69"])
return plain
def protect_response(self, sw, result):
"""
This method protects a response APDU using secure messaging mechanisms
:returns: the protected data and the SW bytes
"""
@@ -509,15 +542,15 @@ class nPA_SE(Security_Environment):
SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"],
len(encrypted),
encrypted)])
return_data += encrypted_tlv
return_data += encrypted_tlv
sw_str = inttostring(sw)
length = len(sw_str)
length = len(sw_str)
tag = SM_Class["PLAIN_PROCESSING_STATUS"]
tlv_sw = bertlv_pack([(tag, length, sw_str)])
return_data += tlv_sw
if self.cct.algorithm == None:
if self.cct.algorithm is None:
raise SwError(SW["CONDITIONSNOTSATISFIED"])
elif self.cct.algorithm == "CC":
tag = SM_Class["CHECKSUM"]
@@ -531,17 +564,22 @@ class nPA_SE(Security_Environment):
auth = self.compute_digital_signature(0x9E, 0x9A, hash)
length = len(auth)
return_data += bertlv_pack([(tag, length, auth)])
return sw, return_data
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'
# 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'
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)
self.active = True
self.current_SE = default_se(self.mf, self)
@@ -557,18 +595,19 @@ class nPA_SAM(SAM):
def reset_retry_counter(self, p1, p2, data):
# check if PACE was successful
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:
# change secret
if p2 == self.current_SE.at.PACE_CAN:
self.can = data
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"
# 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"])
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
self.eid_pin = data
print "Changed PIN to %r" % self.eid_pin
else:
@@ -583,7 +622,8 @@ class nPA_SAM(SAM):
self.active = True
print "Resumed 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
elif self.current_SE.at.keyref_is_puk():
# TODO unblock PIN for signature
@@ -591,7 +631,7 @@ class nPA_SAM(SAM):
self.active = True
self.counter = 3
else:
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
else:
raise SwError(SW["ERR_DATANOTFOUND"])
else:
@@ -607,8 +647,9 @@ class nPA_SAM(SAM):
# TA
if (p1 != 0x00 or p2 != 0x00):
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:
eac.print_ossl_err()
raise SwError(SW["ERR_NOINFO69"])
@@ -624,11 +665,14 @@ 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', '\xe8\x07\x04\x00\x7f\x00'
'\x07\x03\x02')
if mapped_algo == "DateOfExpiry":
[(_, _, [(_, _, mine)])] = unpack(eid.select('fid', 0x0103).data)
logging.info("DateOfExpiry: " + str(mine)
+ "; reference: " + str(self.current_SE.at.DateOfExpiry))
[(_, _, [(_, _, mine)])] = \
unpack(eid.select('fid', 0x0103).data)
logging.info("DateOfExpiry: " + str(mine) +
"; reference: " +
str(self.current_SE.at.DateOfExpiry))
if self.current_SE.at.DateOfExpiry < mine:
print("Date of expiry verified")
return SW["NORMAL"], ""
@@ -636,16 +680,19 @@ class nPA_SAM(SAM):
print("Date of expiry not verified (expired)")
return SW["WARN_NOINFO63"], ""
elif mapped_algo == "DateOfBirth":
[(_, _, [(_, _, mine)])] = unpack(eid.select('fid', 0x0108).data)
[(_, _, [(_, _, mine)])] = \
unpack(eid.select('fid', 0x0108).data)
# 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
if len(str(mine)) == 6:
mine = int(str(mine) + "31")
elif len(str(mine)) == 4:
mine = int(str(mine) + "1231")
logging.info("DateOfBirth: " + str(mine)
+ "; reference: " + str(self.current_SE.at.DateOfExpiry))
logging.info("DateOfBirth: " + str(mine) +
"; reference: " +
str(self.current_SE.at.DateOfExpiry))
if self.current_SE.at.DateOfBirth < mine:
print("Date of birth verified (old enough)")
return SW["NORMAL"], ""
@@ -653,15 +700,18 @@ class nPA_SAM(SAM):
print("Date of birth not verified (too young)")
return SW["WARN_NOINFO63"], ""
elif mapped_algo == "CommunityID":
[(_, _, [(_, _, mine)])] = unpack(eid.select('fid', 0x0112).data)
[(_, _, [(_, _, mine)])] = \
unpack(eid.select('fid', 0x0112).data)
mine = binascii.hexlify(mine)
logging.info("CommunityID: " + str(mine)
+ "; reference: " + str(self.current_SE.at.CommunityID))
logging.info("CommunityID: " + str(mine) +
"; reference: " +
str(self.current_SE.at.CommunityID))
if mine.startswith(self.current_SE.at.CommunityID):
print("Community ID verified (living there)")
return SW["NORMAL"], ""
else:
print("Community ID not verified (not living there)")
print("Community ID not verified (not living"
"there)")
return SW["WARN_NOINFO63"], ""
else:
return SwError(SW["ERR_DATANOTFOUND"])
@@ -677,10 +727,12 @@ class nPA_SAM(SAM):
protocol = "PACE"
else:
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))
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")