From 702e248a484f6e3067dd346942eb87e0591c8096 Mon Sep 17 00:00:00 2001 From: psytester Date: Fri, 23 Jan 2015 12:57:26 +0100 Subject: [PATCH] Verify implemented for Age, DocumentValidity and PlaceVerification checks In case of AgeVerification , DocumentValidity and PlaceVerification checks, the return code is not longer hard coded. Based on given reference values a check is does against given DG3, DG8 and DG18 values Still ToDo is incomplete DG8 BirthDate in Format YYYY-XX where the day is missing --- .../src/vpicc/virtualsmartcard/cards/nPA.py | 120 ++++++++++++++++-- 1 file changed, 109 insertions(+), 11 deletions(-) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py index 4a2e0a9..7a5ce5a 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py @@ -20,12 +20,13 @@ from virtualsmartcard.SmartcardSAM import SAM from virtualsmartcard.SEutils import ControlReferenceTemplate, Security_Environment from virtualsmartcard.SWutils import SwError, SW from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE, SM_Class, ALGO_MAPPING -from virtualsmartcard.TLVutils import unpack, bertlv_pack +from virtualsmartcard.TLVutils import unpack, bertlv_pack, decodeDiscretionaryDataObjects, tlv_find_tag from virtualsmartcard.SmartcardFilesystem import make_property from virtualsmartcard.utils import inttostring import virtualsmartcard.CryptoUtils as vsCrypto from chat import CHAT, CVC, PACE_SEC, EAC_CTX import eac +import logging, binascii class nPA_AT_CRT(ControlReferenceTemplate): @@ -33,6 +34,9 @@ class nPA_AT_CRT(ControlReferenceTemplate): PACE_CAN = 0x02 PACE_PIN = 0x03 PACE_PUK = 0x04 + DateOfBirth = None + DateOfExpiry = None + CommunityID = None def __init__(self): ControlReferenceTemplate.__init__(self, CRT_TEMPLATE["AT"]) @@ -71,6 +75,29 @@ class nPA_AT_CRT(ControlReferenceTemplate): print(self.chat) elif tag == 0x67: self.auxiliary_data = bertlv_pack([[tag, length, value]]) + # extract reference values for DocumentValiditaCheck, AgeVerificatiobCheck and PlaceVerificationCheck + # first we have up to three objects, one for each reference value + for subtlv in value: + subtag, sublength, subvalue = subtlv + # extract oid value which is given within TagID = 6 + oidtlv = tlv_find_tag(subvalue, 6) + for oiddetails in oidtlv: + oidtag, oidlength, oidvalue = oiddetails + # extract finally the reference value which is given within TagID = 83 + valtlv = tlv_find_tag(subvalue, 83) + val = decodeDiscretionaryDataObjects(valtlv) + if ALGO_MAPPING[oidvalue] == "DateOfBirth": + logging.info("ALGO_MAPPING[oid] == DateOfBirth matched") + nPA_AT_CRT.DateOfBirth = int(val[0]) + logging.info("extracted value for reference DateOfBirth: " + str(nPA_AT_CRT.DateOfBirth)) + elif ALGO_MAPPING[oidvalue] == "DateOfExpiry": + logging.info("ALGO_MAPPING[oid] == DateOfExpiry matched") + nPA_AT_CRT.DateOfExpiry = int(val[0]) + logging.info("extracted value for reference DateOfExpiry: " + str(nPA_AT_CRT.DateOfExpiry)) + elif ALGO_MAPPING[oidvalue] == "CommunityID": + logging.info("ALGO_MAPPING[oid] == CommunityID matched") + nPA_AT_CRT.CommunityID = binascii.hexlify(val[0]) + logging.info("extracted value for reference CommunityID: " + str(nPA_AT_CRT.CommunityID)) elif tag == 0x80 or tag == 0x84 or tag == 0x83 or tag == 0x91: # handled by ControlReferenceTemplate.parse_SE_config pass @@ -133,7 +160,7 @@ 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 + # 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"]) @@ -539,17 +566,88 @@ class nPA_SAM(SAM): structure = unpack(data) for tag, length, value in structure: if tag == 6 and ALGO_MAPPING[value] == "DateOfExpiry": - # hell yes, this is a valid nPA - # TODO actually check it... - return SW["NORMAL"], "" + logging.info("Doing verify command for DocumentValidityCheck with reference value: " + str(nPA_AT_CRT.DateOfExpiry)) + # extract DateOfExpiry DG3 value + dg3 = self.mf.select('filedescriptor' , 0x38).select('fid', 0x0103).data + dg_structure = unpack(dg3) + # dg_structure is a TLV object with a sub TLV in value part + for data in dg_structure: + datatag, datalength, datavalue = data + # datavalue contains now the datagroup value + dg_entry = tlv_find_tag(datavalue, 18) + for detail in dg_entry: + dgtag, dglength, dgvalue = detail + logging.info("dg3 DateOfExpiry value: " + str(dgvalue)) + dg3_DateOfExpiry = int(dgvalue) + # Verification result OK ==SW["NORMAL"] / Failed = SW["WARN_NOINFO63"] + logging.info("is reference DateOfExpiry < dg3_DateOfExpiry ?") + logging.info("is " + str(nPA_AT_CRT.DateOfExpiry) + " < " + str(dg3_DateOfExpiry) + " ?") + if nPA_AT_CRT.DateOfExpiry < dg3_DateOfExpiry: + logging.info("Verify result: OK") + return SW["NORMAL"], "" + else: + logging.info("Verify result: Failed") + return SW["WARN_NOINFO63"], "" + if tag == 6 and ALGO_MAPPING[value] == "DateOfBirth": - # hell yes, we are old enough - # TODO actually check it... - return SW["NORMAL"], "" + logging.info("Doing verify command for AgeVerificationCheck with reference value: " + str(nPA_AT_CRT.DateOfBirth)) + # extract DateOfBirth DG8 value + dg8 = self.mf.select('filedescriptor' , 0x38).select('fid', 0x0108).data + dg_structure = unpack(dg8) + # dg_structure is a TLV object with a sub TLV in value part + for data in dg_structure: + datatag, datalength, datavalue = data + # datavalue contains now the datagroup value + dg_entry = tlv_find_tag(datavalue, 18) + for detail in dg_entry: + dgtag, dglength, dgvalue = detail + logging.info("dg8 DateOfBirth value: " + str(dgvalue)) + dg8_DateOfBirth = int(dgvalue) + # !!! TODO FixMe validation against incomplete birth date in dg8_DateOfBirth where "XX" is a missing part !!! + # case1: YYYY-MM-DD (normal) + # case2: YYYY-MM-XX --> will be mapped to last day of the given month + # case3: YYYY-XX-XX --> will be mapped to YYYY-12-31 + # Verification result OK ==SW["NORMAL"] / Failed = SW["WARN_NOINFO63"] + if len(str(dg8_DateOfBirth))==6: + logging.info("Verify result: Failed because of missing implementation for validation against incomplete birth date") + return SW["WARN_NOINFO63"], "" + elif len(str(dg8_DateOfBirth))==4: + logging.info("Incomplete birth date with 'year' found only. Will map it to 'YYYY-12-31'") + dg8_DateOfBirth = int(str(dg8_DateOfBirth) + "1231") + + logging.info("is reference DateOfBirth > dg8_DateOfBirth ?") + logging.info("is " + str(nPA_AT_CRT.DateOfBirth) + " > " + str(dg8_DateOfBirth) + " ?") + if nPA_AT_CRT.DateOfBirth > dg8_DateOfBirth: + logging.info("Verify result: OK") + return SW["NORMAL"], "" + else: + logging.info("Verify result: Failed") + return SW["WARN_NOINFO63"], "" + if tag == 6 and ALGO_MAPPING[value] == "CommunityID": - # well OK, we are living there - # TODO actually check it... - return SW["NORMAL"], "" + logging.info("Doing verify command for PlaceVerificationCheck with reference value: " + str(nPA_AT_CRT.CommunityID)) + # extract CommunityID DG18 value + dg18 = self.mf.select('filedescriptor' , 0x38).select('fid', 0x0112).data + dg_structure = unpack(dg18) + # dg_structure is a TLV object with a sub TLV in value part + for data in dg_structure: + datatag, datalength, datavalue = data + # datavalue contains now the datagroup value + dg_entry = tlv_find_tag(datavalue, 4) + for detail in dg_entry: + dgtag, dglength, dgvalue = detail + dg18_CommunityID = binascii.hexlify(dgvalue) + logging.info("dg18 CommunityID value: " + str(dg18_CommunityID)) + # Verification result OK ==SW["NORMAL"] / Failed = SW["WARN_NOINFO63"] + logging.info("does dg18_CommunityID starts with reference CommunityID ?") + logging.info("does '" + str(dg18_CommunityID) + "' starts with '" + str(nPA_AT_CRT.CommunityID) + "' ?") + if dg18_CommunityID.startswith(nPA_AT_CRT.CommunityID): + logging.info("Verify result: OK") + return SW["NORMAL"], "" + else: + logging.info("Verify result: Failed") + return SW["WARN_NOINFO63"], "" + else: return SAM.verify(self, p1, p2, data)