nPA's SM now successfully tested

- fixed SEutils to use new input data to append_padding
- added defines to reference nPA's secrets (as keyref)
- fixed send sequence counter handling in nPA
- added protect_response to nPA to authenticate the status bytes
- implemented reset retry counter for nPA


git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@685 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
frankmorgner
2012-01-22 00:14:30 +00:00
parent 0c1ad104a6
commit 06aebb7de0
3 changed files with 142 additions and 19 deletions

View File

@@ -367,7 +367,7 @@ class Security_Environment(object):
#SM data objects for authentication
if tag == SM_Class["CHECKSUM"]:
auth = vsCrypto.append_padding(self.cct.algorithm, to_authenticate)
auth = vsCrypto.append_padding(self.cct.blocklength, to_authenticate)
sw, checksum = self.compute_cryptographic_checksum(0x8E,
0x80,
auth)

View File

@@ -499,6 +499,7 @@ class NPAOS(Iso7816OS):
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE):
Iso7816OS.__init__(self, mf, sam, ins2handler, maxle)
self.ins2handler[0x86] = self.SAM.general_authenticate
self.ins2handler[0x2c] = self.SAM.reset_retry_counter
self.atr = '\x3B\x8A\x80\x01\x80\x31\xF8\x73\xF7\x41\xE0\x82\x90\x00\x75'
def formatResult(self, seekable, le, data, sw, sm):

View File

@@ -19,16 +19,44 @@
from virtualsmartcard.SmartcardSAM import SAM
from virtualsmartcard.SEutils import ControlReferenceTemplate, Security_Environment
from virtualsmartcard.SWutils import SwError, SW
from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE
from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE, SM_Class
from virtualsmartcard.TLVutils import unpack, bertlv_pack
from virtualsmartcard.SmartcardFilesystem import make_property
from virtualsmartcard.utils import inttostring
import virtualsmartcard.CryptoUtils as vsCrypto
from chat import CHAT
import pace
class nPA_AT_CRT(ControlReferenceTemplate):
PACE_MRZ = 0x01
PACE_CAN = 0x02
PACE_PIN = 0x03
PACE_PUK = 0x04
def __init__(self):
ControlReferenceTemplate.__init__(self, CRT_TEMPLATE["AT"])
def keyref_is_mrz(self):
if self.keyref == '%c'% self.PACE_MRZ:
return True
return False
def keyref_is_can(self):
if self.keyref == '%c'% self.PACE_CAN:
return True
return False
def keyref_is_pin(self):
if self.keyref == '%c'% self.PACE_PIN:
return True
return False
def keyref_is_puk(self):
if self.keyref == '%c'% self.PACE_PUK:
return True
return False
def parse_SE_config(self, config):
r = 0x9000
try:
@@ -51,19 +79,24 @@ class nPA_AT_CRT(ControlReferenceTemplate):
raise SwError(SW["ERR_REFNOTUSABLE"])
structure = unpack(config)
pin_ref_str = '%c'% self.PACE_PIN
for tlv in structure:
if [0x83, len('\x03'), '\x03'] == tlv:
if [0x83, len(pin_ref_str), pin_ref_str] == tlv:
if self.sam.counter <= 0:
r = 0x63c0
elif self.sam.counter == 1:
r = 0x63c1
elif self.sam.counter == 2:
r = 0x63c2
return r, ""
class nPA_SE(Security_Environment):
# TODO call __eac_abort whenever an error occurred
eac_step = make_property("eac_step", "next step to performed for EAC")
def __init__(self, MF, SAM):
Security_Environment.__init__(self, MF, SAM)
self.at = nPA_AT_CRT()
@@ -94,7 +127,7 @@ class nPA_SE(Security_Environment):
if (p1, p2) != (0x00, 0x00):
raise SwError(SW["ERR_INCORRECTPARAMETERS"])
print "performing PACE step", self.eac_step
print "performing PACE step", self.eac_step + 1
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":
@@ -139,11 +172,11 @@ class nPA_SE(Security_Environment):
self.__eac_abort()
self.eac_ctx = pace.EAC_CTX_new()
if self.at.keyref == '\x01':
if self.at.keyref_is_mrz():
self.sec = pace.PACE_SEC_new(self.sam.mrz, pace.PACE_MRZ)
elif self.at.keyref == '\x02':
elif self.at.keyref_is_can():
self.sec = pace.PACE_SEC_new(self.sam.can, pace.PACE_CAN)
elif self.at.keyref == '\x03':
elif self.at.keyref_is_pin():
if self.sam.counter <= 0:
print "Must use PUK to unblock"
raise SwError(SW["WARN_NOINFO63"])
@@ -154,7 +187,7 @@ class nPA_SE(Security_Environment):
self.sam.counter -= 1
if self.sam.counter <= 1:
self.sam.active = False
elif self.at.keyref == '\x04':
elif self.at.keyref_is_puk():
if self.sam.counter_puk <= 0:
raise SwError(SW["WARN_NOINFO63"])
self.sec = pace.PACE_SEC_new(self.sam.puk, pace.PACE_PUK)
@@ -219,17 +252,17 @@ class nPA_SE(Security_Environment):
pace.print_ossl_err()
raise SwError(SW["WARN_NOINFO63"])
if self.at.keyref == '\x02':
if self.at.keyref_is_can():
if (self.sam.counter == 1):
self.sam.active = True
print "PIN resumed"
elif self.at.keyref == '\x04':
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"
elif self.at.keyref == '\x03':
self.sam.active = True
self.sam.counter = 3
self.eac_step += 1
@@ -303,8 +336,6 @@ class nPA_SE(Security_Environment):
return 0x9000, checksum
def encipher(self, p1, p2, data):
self.ssc += 1
cipher = pace.EAC_encrypt(self.eac_ctx, self.ssc, data)
if not cipher:
pace.print_ossl_err()
@@ -313,8 +344,6 @@ class nPA_SE(Security_Environment):
return 0x9000, cipher
def decipher(self, p1, p2, data):
self.ssc += 1
plain = pace.EAC_decrypt(self.eac_ctx, self.ssc, data)
if not plain:
pace.print_ossl_err()
@@ -322,11 +351,51 @@ class nPA_SE(Security_Environment):
return 0x9000, 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
"""
return_data = ""
if result != "":
# Encrypt the data included in the RAPDU
sw, encrypted = self.encipher(0x82, 0x80, result)
encrypted = "\x01" + encrypted
encrypted_tlv = bertlv_pack([(
SM_Class["CRYPTOGRAM_PADDING_INDICATOR_ODD"],
len(encrypted),
encrypted)])
return_data += encrypted_tlv
sw_str = inttostring(sw)
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:
raise SwError(SW["CONDITIONSNOTSATISFIED"])
elif self.cct.algorithm == "CC":
tag = SM_Class["CHECKSUM"]
padded = vsCrypto.append_padding(self.cct.blocklength, return_data)
sw, auth = self.compute_cryptographic_checksum(0x8E, 0x80, padded)
length = len(auth)
return_data += bertlv_pack([(tag, length, auth)])
elif self.cct.algorithm == "SIGNATURE":
tag = SM_Class["DIGITAL_SIGNATURE"]
hash = self.hash(0x90, 0x80, return_data)
sw, auth = self.compute_digital_signature(0x9E, 0x9A, hash)
length = len(auth)
return_data += bertlv_pack([(tag, length, auth)])
return sw, return_data
class nPA_SAM(SAM):
eac_step = make_property("eac_step", "next step to performed for EAC")
def __init__(self, pin, can, mrz, puk, mf, default_se = nPA_SE):
SAM.__init__(self, pin, None, mf)
self.active = True
@@ -338,3 +407,56 @@ class nPA_SAM(SAM):
def general_authenticate(self, p1, p2, data):
return self.current_SE.general_authenticate(p1, p2, data)
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"])
# 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"
if not self.current_SE.at.keyref_is_pin():
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
self.PIN = data
print "Changed PIN to %r" % self.PIN
else:
raise SwError(SW["ERR_DATANOTFOUND"])
elif p1 == 0x03:
# resume/unblock secret
if p2 == self.current_SE.at.PACE_CAN:
# CAN has no counter
pass
elif p2 == self.current_SE.at.PACE_PIN:
if self.current_SE.at.keyref_is_can():
self.active = True
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"
self.active = True
self.counter = 3
else:
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
else:
raise SwError(SW["ERR_DATANOTFOUND"])
else:
raise SwError(SW["ERR_INCORRECTP1P2"])
return 0x9000, ""
def parse_SM_CAPDU(self, CAPDU, header_authentication):
self.current_SE.ssc += 1
return SAM.parse_SM_CAPDU(self, CAPDU, header_authentication)
def protect_result(self, sw, unprotected_result):
self.current_SE.ssc += 1
return SAM.protect_result(self, sw, unprotected_result)