/** * @author ETSI / STF544 * @version $URL$ * $Id$ * @desc Module containing functions for ITS PKI ATS * @copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * */ module LibItsPki_Functions { // LibCommon import from LibCommon_Time all; import from LibCommon_VerdictControl all; import from LibCommon_Sync all; import from LibCommon_BasicTypesAndValues all; import from LibCommon_DataStrings all; // LibIts import from IEEE1609dot2BaseTypes language "ASN.1:1997" all; import from IEEE1609dot2 language "ASN.1:1997" all; import from EtsiTs102941BaseTypes language "ASN.1:1997" all; import from EtsiTs102941TypesEnrolment language "ASN.1:1997" all; import from EtsiTs102941MessagesItss language "ASN.1:1997" all; import from EtsiTs103097Module language "ASN.1:1997" all; import from ITS_Container language "ASN.1:1997" all; import from CAM_PDU_Descriptions language "ASN.1:1997" all; // LibItsCommon import from LibItsCommon_TypesAndValues all; import from LibItsCommon_Functions all; import from LibItsCommon_ASN1_NamedNumbers all; import from LibItsCommon_Pixits all; // LibItsSecurity import from LibItsSecurity_TypesAndValues all; import from LibItsSecurity_Templates all; import from LibItsSecurity_Functions all; import from LibItsSecurity_Pixits all; // LibItsHttp import from LibItsHttp_TypesAndValues all; import from LibItsHttp_TestSystem all; // LibItsPki import from LibItsPki_TypesAndValues all; import from LibItsPki_Templates all; import from LibItsPki_Pics all; import from LibItsPki_TestSystem all; group pkiConfigurationFunctions { /** * @desc Setups default configuration * @param p_certificateId The certificate identifier the TA shall use in case of secured IUT */ function f_cfUp( in charstring p_certificateId := "CERT_TS_A_EA" // TODO Use a constant ) runs on ItsPki /* TITAN TODO: system ItsPkiSystem */ { map(self:pkiPort, system:pkiPort); map(self:acPkiPort, system:acPkiPort); f_connect4SelfOrClientSync(); if( not f_loadCertificates(PX_IUT_SEC_CONFIG_NAME) ) { log("*** INFO: TEST CASE NOW STOPPING ITSELF! ***"); stop; } f_prepareCertificates(p_certificateId, vc_aaCertificate, vc_atCertificate); f_readCertificate(p_certificateId, vc_eaCertificate); } // End of function f_cfUp /** * @desc Setups default configuration * @param p_certificateId The certificate identifier the TA shall use in case of secured IUT */ function f_cfHttpUp( in charstring p_certificateId := "CERT_TS_A_EA", // TODO Use a constant in charstring p_peerCertificateId := "CERT_IUT_A_EA" ) runs on ItsPkiHttp /* TITAN TODO: system ItsPkiHttpSystem */ { map(self:httpPort, system:httpPort); f_connect4SelfOrClientSync(); if( not f_loadCertificates(PX_IUT_SEC_CONFIG_NAME) ) { log("*** INFO: TEST CASE NOW STOPPING ITSELF! ***"); stop; } f_prepareCertificates(p_certificateId, vc_aaCertificate, vc_atCertificate); f_readCertificate(p_certificateId, vc_eaCertificate); f_readSigningKey(p_certificateId, vc_eaPrivateKey); f_readEncryptingKey(p_certificateId, vc_eaPrivateEncKey); f_getCertificateDigest(p_certificateId, vc_eaHashedId8); f_getCertificateHash(p_peerCertificateId, vc_eaPeerWholeHash); } // End of function f_cfHttpUp /** * @desc Deletes default configuration */ function f_cfDown() runs on ItsPki /* TITAN TODO: system ItsPkiSystem */ { unmap(self:pkiPort, system:pkiPort); unmap(self:acPkiPort, system:acPkiPort); f_disconnect4SelfOrClientSync(); } // End of function f_cfDown /** * @desc Deletes default configuration */ function f_cfHttpDown() runs on ItsPkiHttp /* TITAN TODO: system ItsPkiHttpSystem */ { unmap(self:httpPort, system:httpPort); f_disconnect4SelfOrClientSync(); } // End of function f_cfHttpDown /** * @desc Initialise secure mode if required */ function f_initialiseSecuredMode() runs on ItsPki { // Local variables // Load certificates if( not f_loadCertificates(PX_IUT_SEC_CONFIG_NAME) ) { log("*** INFO: TEST CASE NOW STOPPING ITSELF! ***"); stop; } } // End of function f_initialiseSecuredMode() function f_uninitialiseSecuredMode() runs on ItsPki { f_unloadCertificates(); } // End of function f_uninitialiseSecuredMode() } // End of pkiConfigurationFunctions group ac_port { function f_sendAcPkiPrimitive( in octetstring p_private_key, in octetstring p_publicKeyCompressed, in integer p_compressedMode ) runs on ItsPki { var AcSetSecurityData v_ac_set_security_data; v_ac_set_security_data := { PICS_TS_CERTIFICATE_ID, PICS_IUT_CERTIFICATE_ID, p_private_key, p_publicKeyCompressed, p_compressedMode }; acPkiPort.send(AcPkiPrimitive: { acSetSecurityData := v_ac_set_security_data }); tc_ac.start; alt { [] acPkiPort.receive(AcPkiResponse: { result := true }) { tc_ac.stop; } [] acPkiPort.receive(AcPkiResponse: { result := false }) { tc_ac.stop; log("*** f_sendAcPkiPrimitive: ERROR: Received unexpected message ***"); f_selfOrClientSyncAndVerdict("error", e_error); } [] tc_ac.timeout { log("*** f_sendAcPkiPrimitive: ERROR: Timeout while waiting for adapter control event result ***"); f_selfOrClientSyncAndVerdict("error", e_timeout); } } // End of 'alt' statement } } // End of group ac_port group http { function f_http_build_enrolment_request( out octetstring p_private_key, out octetstring p_publicKeyX, out octetstring p_publicKeyY, out octetstring p_publicKeyCompressed, out integer p_compressedMode, out Ieee1609Dot2Data p_ieee1609dot2_signed_and_encrypted_data ) runs on ItsPkiHttp { var InnerEcRequest v_inner_ec_request; var Ieee1609Dot2Data v_inner_ec_request_signed_for_pop; var bitstring v_inner_ec_request_signed_for_pop_msg; if (f_generate_inner_ec_request(p_private_key, p_publicKeyX, p_publicKeyY, p_publicKeyCompressed, p_compressedMode, v_inner_ec_request) == false) { log("*** f_http_build_enrolment_request: ERROR: Failed to generate InnerEcRequest ***"); f_selfOrClientSyncAndVerdict("error", e_error); } // Generate InnerEcRequestSignedForPoP if (f_generate_inner_ec_request_signed_for_pop(p_private_key, v_inner_ec_request, v_inner_ec_request_signed_for_pop) == false) { log("*** f_http_build_enrolment_request: ERROR: Failed to generate InnerEcRequestSignedForPop ***"); f_selfOrClientSyncAndVerdict("error", e_error); } // Secure InnerEcRequestSignedForPoP message v_inner_ec_request_signed_for_pop_msg := encvalue(m_etsiTs102941Data_inner_ec_request_signed_for_pop(v_inner_ec_request_signed_for_pop)); if (f_build_pki_secured_message(vc_eaPrivateKey, valueof(m_signerIdentifier_self), vc_eaHashedId8, p_publicKeyCompressed, p_compressedMode, bit2oct(v_inner_ec_request_signed_for_pop_msg), p_ieee1609dot2_signed_and_encrypted_data) == false) { log("*** f_http_build_enrolment_request: ERROR: Failed to generate InnerEcRequestSignedForPop ***"); f_selfOrClientSyncAndVerdict("error", e_error); } log("*** f_http_build_enrolment_request: DEBUF: p_ieee1609dot2_signed_and_encrypted_data = ", p_ieee1609dot2_signed_and_encrypted_data); } // End of function f_http_build_enrolment_request } // End of group http group inner_ec_xxx { function f_generate_inner_ec_request( out octetstring p_private_key, out octetstring p_publicKeyX, out octetstring p_publicKeyY, out octetstring p_publicKeyCompressed, out integer p_compressedMode, out InnerEcRequest p_inner_ec_request ) return boolean { // Local variables var template (value) EccP256CurvePoint v_eccP256_curve_point; // Generate keys for the certificate to be requested if (f_generate_key_pair_nistp256(p_private_key, p_publicKeyX, p_publicKeyY, p_publicKeyCompressed, p_compressedMode) == false) { return false; } log("p_private_key = ", p_private_key); log("p_public_key X= ", p_publicKeyX); log("p_public_key Y= ", p_publicKeyY); log("p_public_key compressed= ", p_publicKeyCompressed, p_compressedMode); if (p_compressedMode == 0) { v_eccP256_curve_point := m_eccP256CurvePoint_compressed_y_0(p_publicKeyCompressed); } else { v_eccP256_curve_point := m_eccP256CurvePoint_compressed_y_1(p_publicKeyCompressed); } // Build the Proof of Possession InnerEcRequest p_inner_ec_request := valueof( m_innerEcRequest( "CanonicalItsId", // TODO Use PIXIT m_publicKeys( m_publicVerificationKey_ecdsaNistP256(v_eccP256_curve_point), m_encryptionKey( -, m_publicEncryptionKey_ecdsaNistP256(v_eccP256_curve_point) ) ), m_certificateSubjectAttributes( { // ETSI TS 102 965 Table A.1: ETSI ITS standardized ITS-AIDs valueof(m_appPermissions(36, { bitmapSsp := '830001'O })), // TODO Use PIXIT valueof(m_appPermissions(37, { bitmapSsp := '830001'O })) // TODO Use PIXIT }, m_validityPeriod( 17469212, m_duration_years(10) // TODO Use PIXIT ), m_geographicRegion_identifiedRegion( { m_identifiedRegion_country_only(12), // TODO Use PIXIT m_identifiedRegion_country_only(34) // TODO Use PIXIT } ), 'C0'O // TODO Use PIXIT ) ) ); return true; } // End of function f_generate_inner_ec_request function f_generate_inner_ec_request_signed_for_pop( in octetstring p_private_key, in InnerEcRequest p_inner_ec_request, out Ieee1609Dot2Data p_inner_ec_request_signed_for_pop ) return boolean { // Local variables var template (value) EccP256CurvePoint v_eccP256_curve_point; var octetstring v_encoded_inner_ec_request; var template (value) ToBeSignedData v_tbs; var octetstring v_tbs_signed; // Encode it v_encoded_inner_ec_request := bit2oct(encvalue(p_inner_ec_request)); // Signed the encoded InnerEcRequestSignedForPop v_tbs := m_toBeSignedData( m_signedDataPayload( m_etsiTs103097Data_unsecured( v_encoded_inner_ec_request ) ), m_headerInfo_inner_ec_request( c_its_aid_SCR, f_getCurrentTimeUtc()) ); // Signed the encoded InnerEcRequestSignedForPop v_tbs_signed := f_signWithEcdsaNistp256WithSha256(bit2oct(encvalue(v_tbs)), int2oct(0, 32), p_private_key); // Finalyse signed InnerEcRequestSignedForPop p_inner_ec_request_signed_for_pop := valueof( m_etsiTs103097Data_signed( m_signedData( sha256, v_tbs, m_signerIdentifier_self, m_signature_ecdsaNistP256( m_ecdsaP256Signature( m_eccP256CurvePoint_x_only( substr(v_tbs_signed, 0, 32) ), substr(v_tbs_signed, 32, 32) ) ) ) ) ); return true; } // End of function f_generate_inner_ec_request_signed_for_pop function f_generate_inner_ec_response( in octetstring p_inner_ec_request_hashed_id, in EtsiTs103097Certificate p_certificate, out InnerEcResponse p_inner_ec_response ) return boolean { // Local variables // Build the Proof of Possession InnerEcResponse p_inner_ec_response := valueof( m_innerEcResponse_ok( substr(p_inner_ec_request_hashed_id, 0, 16), p_certificate ) ); return true; } // End of function f_generate_inner_ec_response } // End of group inner_ec_xxx group pki_functions { /** * @desc Build a signed and encrypted PKI message * @param p_private_key Private key for signature * @param p_signer_identifier Signer identifier for signature, could be self or certificate HashedId8 * @param p_recipientId Recipient identifier to be inclued in encrypted layer. * If value is int2oct(0. 8), the recipient id is the HashedId8 of the symmetric key used by the sender to encrypt the message to which the response is built * @param p_publicKeyCompressed The public compressed key (canonical form) for encryption * @param p_compressedMode The compressed mode of the public compressed key (canonical form) for encryption * @param p_pki_message The PKI message to be secured * @param p_ieee1609dot2_signed_and_encrypted_data The secured message * @return true on success, false otherwise */ function f_build_pki_secured_message( in octetstring p_private_key, in SignerIdentifier p_signer_identifier, in HashedId8 p_recipientId, in octetstring p_publicKeyCompressed, in integer p_compressedMode, in octetstring p_pki_message, out Ieee1609Dot2Data p_ieee1609dot2_signed_and_encrypted_data ) return boolean { // Local variables var template (value) EccP256CurvePoint v_eccP256_curve_point; var template (value) ToBeSignedData v_tbs; var octetstring v_tbs_signed; var template (value) Ieee1609Dot2Data v_ieee1609dot2_signed_data; var octetstring v_encoded_inner_ec_request; var Oct12 v_nonce; var Oct16 v_authentication_vector; var Oct16 v_encrypted_sym_key; var HashedId8 v_recipientId; var octetstring v_publicEphemeralKeyCompressed; var integer v_ephemeralKeyModeCompressed; var octetstring v_encrypted_inner_ec_request; // Signed the encoded PKI message v_tbs := m_toBeSignedData( m_signedDataPayload( m_etsiTs103097Data_unsecured(p_pki_message) ), m_headerInfo_inner_ec_request(12345, f_getCurrentTimeUtc()) // TODO Use PIXIT ); if (ischosen(p_signer_identifier.self_)) { v_tbs_signed := f_signWithEcdsaNistp256WithSha256(bit2oct(encvalue(v_tbs)), int2oct(0, 32), p_private_key); } else { var charstring v_certificate_id; var octetstring v_hash; fx_readCertificateFromDigest(p_signer_identifier.digest, v_certificate_id); // TODO Add a wrapper function f_getCertificateHash(v_certificate_id, v_hash); v_tbs_signed := f_signWithEcdsaNistp256WithSha256(bit2oct(encvalue(v_tbs)), v_hash, p_private_key); } // Add the signature and create EtsiTs103097Data-Signed data structure v_ieee1609dot2_signed_data := m_etsiTs103097Data_signed( m_signedData( sha256, v_tbs, p_signer_identifier, m_signature_ecdsaNistP256( m_ecdsaP256Signature( m_eccP256CurvePoint_x_only( substr(v_tbs_signed, 0, 32) ), substr(v_tbs_signed, 32, 32) ) ) ) ); // Encode EtsiTs103097Data-Signed data structure v_encoded_inner_ec_request := bit2oct(encvalue(v_ieee1609dot2_signed_data)); // Encrypt encode EtsiTs103097Data-Signed data structure v_encrypted_inner_ec_request := f_encryptWithEciesNistp256WithSha256(v_encoded_inner_ec_request, p_publicKeyCompressed, p_compressedMode, v_publicEphemeralKeyCompressed, v_ephemeralKeyModeCompressed, v_encrypted_sym_key, v_authentication_vector, v_nonce); log("p_recipientId=", p_recipientId); if (p_recipientId == int2oct(0, 8)) { log("v_encrypted_sym_key=", v_encrypted_sym_key); log("f_hashWithSha256(v_encrypted_sym_key=", f_hashWithSha256(v_encrypted_sym_key)); v_recipientId := f_HashedId8FromSha256(f_hashWithSha256(v_encrypted_sym_key)); } else { v_recipientId := p_recipientId; } log("v_recipientId=", v_recipientId); // Fill Certificate template with the public compressed keys (canonical form) if (v_ephemeralKeyModeCompressed == 0) { v_eccP256_curve_point := valueof(m_eccP256CurvePoint_compressed_y_0(v_publicEphemeralKeyCompressed)); } else { v_eccP256_curve_point := valueof(m_eccP256CurvePoint_compressed_y_1(v_publicEphemeralKeyCompressed)); } p_ieee1609dot2_signed_and_encrypted_data := valueof( m_etsiTs103097Data_encrypted( m_encryptedData( { m_recipientInfo_signedDataRecipInfo( m_pKRecipientInfo( v_recipientId, m_encryptedDataEncryptionKey_eciesNistP256( m_evciesP256EncryptedKey( v_eccP256_curve_point, v_encrypted_sym_key, v_authentication_vector )))) }, m_SymmetricCiphertext_aes128ccm( m_aesCcmCiphertext( v_nonce, v_encrypted_inner_ec_request ) ) ) ) ); return true; } // End of function f_build_pki_secured_message function f_verify_pki_message( in octetstring v_private_enc_key, in octetstring p_issuer, in Certificate p_peer_certificate, in Ieee1609Dot2Data p_ieee1609dot2_encrypted_and_signed_data, in boolean p_check_signature := true, out EtsiTs102941Data p_etsi_ts_102941_data ) return boolean { // Local variables var Ieee1609Dot2Data v_ieee1609dot2_signed_data; var bitstring v_etsi_ts_102941_data_msg; var bitstring v_tbs; var boolean v_ret; // 1. Decrypt the data if (f_decrypt(v_private_enc_key, p_ieee1609dot2_encrypted_and_signed_data, v_ieee1609dot2_signed_data) == false) { return false; } log("v_ieee1609dot2_signed_data= ", v_ieee1609dot2_signed_data); // 2. Check the signature v_tbs := encvalue(v_ieee1609dot2_signed_data.content.signedData.tbsData); if (ischosen(p_peer_certificate.toBeSigned.verifyKeyIndicator.verificationKey.ecdsaNistP256.compressed_y_0)) { v_ret := f_verifyWithEcdsaNistp256WithSha256( bit2oct(v_tbs), p_issuer, v_ieee1609dot2_signed_data.content.signedData.signature_.ecdsaNistP256Signature.rSig.x_only & v_ieee1609dot2_signed_data.content.signedData.signature_.ecdsaNistP256Signature.sSig, p_peer_certificate.toBeSigned.verifyKeyIndicator.verificationKey.ecdsaNistP256.compressed_y_0, 0); } else { v_ret := f_verifyWithEcdsaNistp256WithSha256( bit2oct(v_tbs), p_issuer, v_ieee1609dot2_signed_data.content.signedData.signature_.ecdsaNistP256Signature.rSig.x_only & v_ieee1609dot2_signed_data.content.signedData.signature_.ecdsaNistP256Signature.sSig, p_peer_certificate.toBeSigned.verifyKeyIndicator.verificationKey.ecdsaNistP256.compressed_y_1, 1); } if ((v_ret == false) and (p_check_signature == true)) { return false; } // 3. Retrun the PKI message v_etsi_ts_102941_data_msg := oct2bit(v_ieee1609dot2_signed_data.content.signedData.tbsData.payload.data.content.unsecuredData); if (decvalue(v_etsi_ts_102941_data_msg, p_etsi_ts_102941_data) != 0) { return false; } if (p_etsi_ts_102941_data.version != PkiProtocolVersion) { return false; } return true; } // End of function f_verify_pki_message } // End of group inner_ec_xxx } // End of module LibItsPki_Functions