diff --git a/tools/socktap/security.cpp b/tools/socktap/security.cpp index f41bbcb7a..da40ea65f 100644 --- a/tools/socktap/security.cpp +++ b/tools/socktap/security.cpp @@ -2,15 +2,20 @@ #include #include #include +#include #include -#include -#include + +#include +#include #include -#include +#include +#include #include -#include + #include +#include #include +#include using namespace vanetza; namespace po = boost::program_options; @@ -22,8 +27,8 @@ class SecurityContext : public security::SecurityEntity runtime(runtime), positioning(positioning), backend(security::create_backend("default")), sign_header_policy(runtime, positioning), - cert_cache(runtime), - cert_validator(*backend, cert_cache, trust_store) + cert_cache() + //cert_validator(*backend, cert_cache, trust_store) { } @@ -49,13 +54,10 @@ class SecurityContext : public security::SecurityEntity throw std::runtime_error("certificate provider is missing"); } std::unique_ptr sign_service { new - security::v2::StraightSignService(*cert_provider, *backend, sign_header_policy) }; + security::v3::StraightSignService(*cert_provider, *backend, sign_header_policy) }; std::unique_ptr verify_service { new security::StraightVerifyService(runtime, *backend, positioning) }; - verify_service->use_certificate_provider(cert_provider.get()); verify_service->use_certificate_cache(&cert_cache); - verify_service->use_certitifcate_validator(&cert_validator); - verify_service->use_sign_header_policy(&sign_header_policy); entity.reset(new security::DelegatingSecurityEntity { std::move(sign_service), std::move(verify_service) }); } @@ -63,11 +65,11 @@ class SecurityContext : public security::SecurityEntity PositionProvider& positioning; std::unique_ptr backend; std::unique_ptr entity; - std::unique_ptr cert_provider; - security::v2::DefaultSignHeaderPolicy sign_header_policy; + std::unique_ptr cert_provider; + security::v3::DefaultSignHeaderPolicy sign_header_policy; security::v2::TrustStore trust_store; - security::v2::CertificateCache cert_cache; - security::v2::DefaultCertificateValidator cert_validator; + security::v3::CertificateCache cert_cache; + //security::v2::DefaultCertificateValidator cert_validator; }; @@ -81,7 +83,7 @@ create_security_entity(const po::variables_map& vm, const Runtime& runtime, Posi // no operation } else if (name == "dummy") { std::unique_ptr sign_service { new - security::v2::DummySignService { runtime, nullptr } }; + vanetza::security::v3::DummySignService { runtime, nullptr } }; std::unique_ptr verify_service { new security::DummyVerifyService { security::VerificationReport::Success, security::CertificateValidity::valid() } }; @@ -97,27 +99,22 @@ create_security_entity(const po::variables_map& vm, const Runtime& runtime, Posi const std::string& certificate_path = vm["certificate"].as(); const std::string& certificate_key_path = vm["certificate-key"].as(); - auto authorization_ticket = security::v2::load_certificate_from_file(certificate_path); - auto authorization_ticket_key = security::v2::load_private_key_from_file(certificate_key_path); - - std::list chain; + auto authorization_ticket = security::v3::load_certificate_from_file(certificate_path); + auto authorization_ticket_key = security::v3::load_private_key_from_file(certificate_key_path); + + std::list chain; if (vm.count("certificate-chain")) { for (auto& chain_path : vm["certificate-chain"].as >()) { - auto chain_certificate = security::v2::load_certificate_from_file(chain_path); + auto chain_certificate = security::v3::load_certificate_from_file(chain_path); chain.push_back(chain_certificate); - context->cert_cache.insert(chain_certificate); - - // Only add root certificates to trust store, so certificate requests are visible for demo purposes. - if (chain_certificate.subject_info.subject_type == security::v2::SubjectType::Root_CA) { - context->trust_store.insert(chain_certificate); - } + context->cert_cache.store(chain_certificate); } } - context->cert_provider.reset(new security::v2::StaticCertificateProvider(authorization_ticket, authorization_ticket_key.private_key, chain)); + context->cert_provider.reset(new security::v3::StaticCertificateProvider(authorization_ticket, authorization_ticket_key.private_key, chain)); } else { - context->cert_provider.reset(new security::v2::NaiveCertificateProvider(runtime)); + context->cert_provider.reset(new security::v3::NaiveCertificateProvider(runtime)); } if (vm.count("trusted-certificate")) { diff --git a/vanetza/security/CMakeLists.txt b/vanetza/security/CMakeLists.txt index 7115c64fe..d82cc6d7b 100644 --- a/vanetza/security/CMakeLists.txt +++ b/vanetza/security/CMakeLists.txt @@ -46,6 +46,12 @@ add_vanetza_component(security v3/certificate.cpp v3/certificate_cache.cpp v3/secured_message.cpp + v3/sign_service.cpp + v3/signer_info.cpp + v3/naive_certificate_provider.cpp + v3/sign_header_policy.cpp + v3/static_certificate_provider.cpp + v3/persistence.cpp ) target_link_libraries(security PUBLIC asn1 asn1_security common net) target_link_libraries(security PRIVATE GeographicLib::GeographicLib) diff --git a/vanetza/security/v3/certificate.cpp b/vanetza/security/v3/certificate.cpp index 2c6dc65d1..dfa51aa55 100644 --- a/vanetza/security/v3/certificate.cpp +++ b/vanetza/security/v3/certificate.cpp @@ -104,6 +104,194 @@ ByteBuffer get_app_permissions(const EtsiTs103097Certificate_t& cert, ItsAid aid return perms; } +void add_psid_group_permission(PsidGroupPermissions* group_permission, ItsAid aid, const ByteBuffer& ssp, const ByteBuffer& bitmask){ + PsidSspRange* psid_range_scr = static_cast(vanetza::asn1::allocate(sizeof(PsidSspRange))); + psid_range_scr->psid = aid; + psid_range_scr->sspRange = static_cast(vanetza::asn1::allocate(sizeof(SspRange))); + psid_range_scr->sspRange->present = SspRange_PR_bitmapSspRange; + OCTET_STRING_fromBuf( + &psid_range_scr->sspRange->choice.bitmapSspRange.sspValue, + reinterpret_cast(ssp.data()), + ssp.size() + ); + OCTET_STRING_fromBuf( + &psid_range_scr->sspRange->choice.bitmapSspRange.sspBitmask, + reinterpret_cast(bitmask.data()), + bitmask.size() + ); + ASN_SEQUENCE_ADD(&group_permission->subjectPermissions.choice.Explicit, psid_range_scr); +} + +void add_app_permissions(Certificate& cert, ItsAid aid) { + SequenceOfPsidSsp_t* seq = cert->toBeSigned.appPermissions; + if (!seq) { + seq = static_cast(vanetza::asn1::allocate(sizeof(SequenceOfPsidSsp_t))); + cert->toBeSigned.appPermissions = seq; + } + // Allocate the memory + PsidSsp* psid_ptr = static_cast(vanetza::asn1::allocate(sizeof(PsidSsp))); + psid_ptr->psid = aid; + ASN_SEQUENCE_ADD(seq, psid_ptr); +} + +void Certificate::add_permission(ItsAid aid, const ByteBuffer& ssp){ + SequenceOfPsidSsp_t* seq = m_struct->toBeSigned.appPermissions; + if (!seq) { + seq = static_cast(vanetza::asn1::allocate(sizeof(SequenceOfPsidSsp_t))); + m_struct->toBeSigned.appPermissions = seq; + } + // Allocate the memory + PsidSsp* psid_ptr = static_cast(vanetza::asn1::allocate(sizeof(PsidSsp))); + psid_ptr->psid = aid; + psid_ptr->ssp = static_cast(vanetza::asn1::allocate(sizeof(ServiceSpecificPermissions))); + psid_ptr->ssp->present = ServiceSpecificPermissions_PR_opaque; + OCTET_STRING_fromBuf( + &(psid_ptr->ssp->choice.opaque), + reinterpret_cast(ssp.data()), + ssp.size() + ); + ASN_SEQUENCE_ADD(seq, psid_ptr); + +} + +void Certificate::add_cert_permission(PsidGroupPermissions* group_permission){ + SequenceOfPsidGroupPermissions* seq = m_struct->toBeSigned.certIssuePermissions; + if (!seq) { + seq = static_cast(vanetza::asn1::allocate(sizeof(SequenceOfPsidGroupPermissions))); + m_struct->toBeSigned.certIssuePermissions = seq; + } + ASN_SEQUENCE_ADD(seq, group_permission); +} + +void Certificate::set_signature(const SomeEcdsaSignature& signature) { + struct ecc_point_visitor : public boost::static_visitor { + EccP256CurvePoint_t operator()(const X_Coordinate_Only& x_only) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &(to_return->choice.x_only), + reinterpret_cast(x_only.x.data()), + x_only.x.size() + ); + return *to_return; + } + EccP256CurvePoint_t operator()(const Compressed_Lsb_Y_0& y0) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_compressed_y_0; + OCTET_STRING_fromBuf( + &(to_return->choice.compressed_y_0), + reinterpret_cast(y0.x.data()), + y0.x.size() + ); + return *to_return; + } + EccP256CurvePoint_t operator()(const Compressed_Lsb_Y_1& y1) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_compressed_y_1; + OCTET_STRING_fromBuf( + &(to_return->choice.compressed_y_1), + reinterpret_cast(y1.x.data()), + y1.x.size() + ); + return *to_return; + } + EccP256CurvePoint_t operator()(const Uncompressed& unc) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_uncompressedP256; + OCTET_STRING_fromBuf( + &(to_return->choice.uncompressedP256.x), + reinterpret_cast(unc.x.data()), + unc.x.size() + ); + OCTET_STRING_fromBuf( + &(to_return->choice.uncompressedP256.y), + reinterpret_cast(unc.y.data()), + unc.y.size() + ); + return *to_return; + } + }; + struct signature_visitor : public boost::static_visitor + { + Signature_t* operator()(const EcdsaSignature& signature) const + { + Signature_t* final_signature = static_cast(vanetza::asn1::allocate(sizeof(Signature_t))); + final_signature->present = Signature_PR_ecdsaNistP256Signature; + OCTET_STRING_fromBuf( + &(final_signature->choice.ecdsaNistP256Signature.sSig), + reinterpret_cast(signature.s.data()), + signature.s.size() + ); + final_signature->choice.ecdsaNistP256Signature.rSig = boost::apply_visitor( + ecc_point_visitor(), + signature.R + ); + return final_signature; + } + Signature_t* operator()(const EcdsaSignatureFuture& signature) const + { + Signature_t* final_signature = static_cast(vanetza::asn1::allocate(sizeof(Signature_t))); +/* EcdsaSignature temp = signature.get(); + final_signature = boost::apply_visitor(signature_visitor(), temp); */ + return final_signature; + } + }; + m_struct->signature = (boost::apply_visitor(signature_visitor(), signature)); +} + +ByteBuffer Certificate::serialize() { + return this->encode(); +} + +ByteBuffer Certificate::convert_for_signing(){ + vanetza::ByteBuffer to_return; + try{ + to_return = vanetza::asn1::encode_oer(asn_DEF_EtsiTs103097Certificate, m_struct); + }catch(std::runtime_error& er){ + } + return to_return; +} + +Certificate fake_certificate() { + + Certificate certi; + certi->issuer.present = IssuerIdentifier_PR_self; + certi->toBeSigned.id.present = CertificateId_PR_none; + std::vector craciId(3, 0); // Correct length for P256 signature part + OCTET_STRING_fromBuf( + &certi->toBeSigned.cracaId, + reinterpret_cast(craciId.data()), + craciId.size() + ); + certi->version = 3; + certi->toBeSigned.crlSeries = 0; + certi->toBeSigned.validityPeriod.start = 0; + certi->toBeSigned.validityPeriod.duration.present = Duration_PR_minutes; + certi->toBeSigned.validityPeriod.duration.choice.minutes = 10080; + certi->toBeSigned.verifyKeyIndicator.present = VerificationKeyIndicator_PR_verificationKey; + certi->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = PublicVerificationKey_PR_ecdsaNistP256; + certi->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = EccP256CurvePoint_PR_x_only; + std::vector dummy_r(32, 0); // Correct length for P256 signature part + dummy_r[0] = 0; // Ensure the leading byte is set to zero if needed + OCTET_STRING_fromBuf( + &certi->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.x_only, + reinterpret_cast(dummy_r.data()), + dummy_r.size() + ); + certi.add_permission(aid::CA, ByteBuffer({ 1, 0, 0 })); + return certi; +} + +void serialize(OutputArchive& ar, Certificate& certificate) { + vanetza::ByteBuffer buffer = certificate.serialize(); + for (auto& temp_byte : buffer){ + ar << temp_byte; + } +} namespace { diff --git a/vanetza/security/v3/certificate.hpp b/vanetza/security/v3/certificate.hpp index 74699ea48..986af8b6c 100644 --- a/vanetza/security/v3/certificate.hpp +++ b/vanetza/security/v3/certificate.hpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include namespace vanetza { @@ -16,6 +19,16 @@ namespace v3 struct Certificate : public asn1::asn1c_oer_wrapper { Certificate(); + + void add_permission(ItsAid aid, const ByteBuffer& ssp); + + void add_cert_permission(PsidGroupPermissions* group_permission); + + void set_signature(const SomeEcdsaSignature& signature); + + ByteBuffer serialize(); + + ByteBuffer convert_for_signing(); }; /** @@ -40,6 +53,12 @@ boost::optional get_public_key(const EtsiTs103097Certificate_t& cert) */ ByteBuffer get_app_permissions(const EtsiTs103097Certificate_t& cert, ItsAid aid); +void add_psid_group_permission(PsidGroupPermissions* group_permission, ItsAid aid, const ByteBuffer& ssp, const ByteBuffer& bitmask); + +void serialize(OutputArchive& ar, Certificate& certificate); + +Certificate fake_certificate(); + } // namespace v3 } // namespace security } // namespace vanetza diff --git a/vanetza/security/v3/certificate_provider.hpp b/vanetza/security/v3/certificate_provider.hpp new file mode 100644 index 000000000..8b6e69496 --- /dev/null +++ b/vanetza/security/v3/certificate_provider.hpp @@ -0,0 +1,41 @@ +#include +#include +#include +#pragma once + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +class CertificateProvider +{ +public: + /** + * Get own certificate to use for signing + * \return own certificate + */ + virtual const Certificate& own_certificate() = 0; + + /** + * Get own certificate chain in root CA → AA → AT order, excluding the AT and root certificate + * \return own certificate chain + */ + virtual std::list own_chain() = 0; + + /** + * Get private key associated with own certificate + * \return private key + */ + virtual const ecdsa256::PrivateKey& own_private_key() = 0; + + virtual ~CertificateProvider() = default; +}; + +} // namespace v2 +} // namespace security +} // namespace vanetza + + diff --git a/vanetza/security/v3/naive_certificate_provider.cpp b/vanetza/security/v3/naive_certificate_provider.cpp new file mode 100644 index 000000000..ef8dddf70 --- /dev/null +++ b/vanetza/security/v3/naive_certificate_provider.cpp @@ -0,0 +1,299 @@ +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +NaiveCertificateProvider::NaiveCertificateProvider(const Runtime& rt) : + m_runtime(rt), + m_own_key_pair(m_crypto_backend.generate_key_pair()), + m_own_certificate(generate_authorization_ticket()) { } + +const Certificate& NaiveCertificateProvider::own_certificate() +{ + // Implement the renewal + return m_own_certificate; +} + +std::list NaiveCertificateProvider::own_chain() +{ + static const std::list chain = { aa_certificate() }; + + return chain; +} + +const ecdsa256::PrivateKey& NaiveCertificateProvider::own_private_key() +{ + return m_own_key_pair.private_key; +} + +const ecdsa256::KeyPair& NaiveCertificateProvider::aa_key_pair() +{ + static const ecdsa256::KeyPair aa_key_pair = m_crypto_backend.generate_key_pair(); + + return aa_key_pair; +} + +const ecdsa256::KeyPair& NaiveCertificateProvider::root_key_pair() +{ + static const ecdsa256::KeyPair root_key_pair = m_crypto_backend.generate_key_pair(); + + return root_key_pair; +} + +const Certificate& NaiveCertificateProvider::aa_certificate() +{ + static const std::string aa_subject("Naive Authorization CA"); + static const Certificate aa_certificate = generate_aa_certificate(aa_subject); + + return aa_certificate; +} + +const Certificate& NaiveCertificateProvider::root_certificate() +{ + static const std::string root_subject("Naive Root CA"); + static const Certificate root_certificate = generate_root_certificate(root_subject); + + return root_certificate; +} + +Certificate NaiveCertificateProvider::generate_authorization_ticket() +{ + // create certificate + Certificate certificate; + + Certificate aa_certificate = this->aa_certificate(); + // section 6 in TS 103 097 v2.1.1 + certificate->issuer.present= IssuerIdentifier_PR_sha256AndDigest; + HashedId8 aa_certi_hashed = boost::get(calculate_hash(*aa_certificate)); + OCTET_STRING_fromBuf( + &(certificate->issuer.choice.sha256AndDigest), + reinterpret_cast(aa_certi_hashed.data()), + aa_certi_hashed.size() + ); + + // section 6 in TS 103 097 v2.1.1 + certificate->toBeSigned.id.present = CertificateId_PR_none; + std::vector craciId(3, 0); + OCTET_STRING_fromBuf( + &certificate->toBeSigned.cracaId, + reinterpret_cast(craciId.data()), + craciId.size() + ); + certificate->version = 3; + certificate->toBeSigned.crlSeries = 0; + + // section 7.2.1 in TS 103 097 v2.1.1 + certificate.add_permission(aid::CA, ByteBuffer({ 1, 0, 0 })); + certificate.add_permission(aid::DEN, ByteBuffer({ 1, 0xff, 0xff, 0xff})); + certificate.add_permission(aid::GN_MGMT, ByteBuffer({})); // required for beacons + certificate.add_permission(aid::IPV6_ROUTING, ByteBuffer({})); // required for routing tests + + // section 6 in TS 103 097 v2.1.1 + // set subject attributes + // set the verification_key + Uncompressed coordinates; + coordinates.x.assign(m_own_key_pair.public_key.x.begin(), m_own_key_pair.public_key.x.end()); + coordinates.y.assign(m_own_key_pair.public_key.y.begin(), m_own_key_pair.public_key.y.end()); + certificate->toBeSigned.verifyKeyIndicator.present = VerificationKeyIndicator_PR_verificationKey; + certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = PublicVerificationKey_PR_ecdsaNistP256; + certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = EccP256CurvePoint_PR_uncompressedP256; + OCTET_STRING_fromBuf( + &certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.uncompressedP256.x, + reinterpret_cast(coordinates.x.data()), + coordinates.x.size() + ); + OCTET_STRING_fromBuf( + &certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.uncompressedP256.y, + reinterpret_cast(coordinates.y.data()), + coordinates.y.size() + ); + + // section 6 in TS 103 097 v2.1.1 + // No constraint + // set validity restriction + + certificate->toBeSigned.validityPeriod.start = v2::convert_time32(m_runtime.now() - std::chrono::hours(1));; + certificate->toBeSigned.validityPeriod.duration.present = Duration_PR_hours; + certificate->toBeSigned.validityPeriod.duration.choice.hours = 23; + + sign_authorization_ticket(certificate); + + return certificate; +} + +void NaiveCertificateProvider::sign_authorization_ticket(Certificate& certificate) +{ + ByteBuffer data_buffer; + data_buffer = certificate.convert_for_signing(); // Correct length for P256 signature parts + certificate.set_signature(m_crypto_backend.sign_data(aa_key_pair().private_key, data_buffer)); +} + +Certificate NaiveCertificateProvider::generate_aa_certificate(const std::string& subject_name) +{ + Certificate aa_certificate; + + //section 7.2.4 in TS 103 097 v2.1.1 + Certificate root_cert = this->root_certificate(); + aa_certificate->issuer.present= IssuerIdentifier_PR_sha256AndDigest; + HashedId8 root_certi_hashed = boost::get(calculate_hash(*root_cert)); + OCTET_STRING_fromBuf( + &(aa_certificate->issuer.choice.sha256AndDigest), + reinterpret_cast(root_certi_hashed.data()), + root_certi_hashed.size() + ); + + + aa_certificate->toBeSigned.id.present = CertificateId_PR_name; + std::string root_name = "AA-cert"; + ByteBuffer root_name_encoded(root_name.begin(), root_name.end()); + OCTET_STRING_fromBuf( + &aa_certificate->toBeSigned.id.choice.name, + reinterpret_cast(root_name_encoded.data()), + root_name_encoded.size() + ); + + // section 6 in TS 103 097 v2.1.1 + std::vector craciId(3, 0); + OCTET_STRING_fromBuf( + &aa_certificate->toBeSigned.cracaId, + reinterpret_cast(craciId.data()), + craciId.size() + ); + aa_certificate->version = 3; + aa_certificate->toBeSigned.crlSeries = 0; + + // section 7.2.4 in TS 103 097 v2.1.1 + // certIssuePermissions shall be used to indicate issuing permissions + // See https://cpoc.jrc.ec.europa.eu/data/documents/e01941_CPOC_Protocol_v3.0_20240206.pdf for detailled cert_permissions + // I.3.8. certIssuePermissions with predefined values + PsidGroupPermissions* cert_permission_message = static_cast(vanetza::asn1::allocate(sizeof(PsidGroupPermissions))); + cert_permission_message->subjectPermissions.present = SubjectPermissions_PR_explicit; + add_psid_group_permission(cert_permission_message,aid::CA,{0x01, 0xff, 0xfc}, {0xff, 0x00, 0x03}); + add_psid_group_permission(cert_permission_message,aid::DEN,{0x01, 0xff, 0xff, 0xff}, {0xff, 0x00, 0x00, 0x00}); + add_psid_group_permission(cert_permission_message,aid::TLM,{0x01, 0xe0}, {0xff, 0x1f}); + add_psid_group_permission(cert_permission_message,aid::RLT,{0x01, 0xc0}, {0xff,0x3f}); + add_psid_group_permission(cert_permission_message,aid::IVI,{0x01, 0xff, 0xff,0xff,0xff,0xf8}, {0xff,0x00,0x00,0x00,0x00,0x07}); + add_psid_group_permission(cert_permission_message,aid::TLC_R,{0x02, 0xff, 0xff,0xe0}, {0xff, 0x00, 0x00, 0x1f}); + add_psid_group_permission(cert_permission_message,aid::GN_MGMT,{0x00}, {0xff}); + aa_certificate.add_cert_permission(cert_permission_message); + + // section 6 in TS 103 097 v2.1.1 + // set subject attributes + // set the verification_key + X_Coordinate_Only coordinates; + coordinates.x.assign(m_own_key_pair.public_key.x.begin(), m_own_key_pair.public_key.x.end()); + aa_certificate->toBeSigned.verifyKeyIndicator.present = VerificationKeyIndicator_PR_verificationKey; + aa_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = PublicVerificationKey_PR_ecdsaNistP256; + aa_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = EccP256CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &aa_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.x_only, + reinterpret_cast(coordinates.x.data()), + coordinates.x.size() + ); + + aa_certificate->toBeSigned.validityPeriod.start = v2::convert_time32(m_runtime.now() - std::chrono::hours(1));; + aa_certificate->toBeSigned.validityPeriod.duration.present = Duration_PR_years; + aa_certificate->toBeSigned.validityPeriod.duration.choice.hours = 4; + + Uncompressed encryption_key; + encryption_key.x.assign(m_own_key_pair.public_key.x.begin(), m_own_key_pair.public_key.x.end()); + encryption_key.y.assign(m_own_key_pair.public_key.y.begin(), m_own_key_pair.public_key.y.end()); + aa_certificate->toBeSigned.encryptionKey = static_cast(vanetza::asn1::allocate(sizeof(PublicEncryptionKey))); + aa_certificate->toBeSigned.encryptionKey->publicKey.present = BasePublicEncryptionKey_PR_eciesNistP256; + aa_certificate->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256.present = EccP256CurvePoint_PR_uncompressedP256; + OCTET_STRING_fromBuf( + &aa_certificate->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256.choice.uncompressedP256.x, + reinterpret_cast(encryption_key.x.data()), + encryption_key.x.size() + ); + OCTET_STRING_fromBuf( + &aa_certificate->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256.choice.uncompressedP256.y, + reinterpret_cast(encryption_key.y.data()), + encryption_key.y.size() + ); + + + sign_authorization_ticket(aa_certificate); + + return aa_certificate; +} + +Certificate NaiveCertificateProvider::generate_root_certificate(const std::string& subject_name) +{ + Certificate root_certificate; + + //section 7.2.3 in TS 103 097 v2.1.1 + root_certificate->issuer.present = IssuerIdentifier_PR_self; + root_certificate->toBeSigned.id.present = CertificateId_PR_name; + std::string root_name = "Root-CA"; + ByteBuffer root_name_encoded(root_name.begin(), root_name.end()); + OCTET_STRING_fromBuf( + &root_certificate->toBeSigned.id.choice.name, + reinterpret_cast(root_name_encoded.data()), + root_name_encoded.size() + ); + + // section 6 in TS 103 097 v2.1.1 + std::vector craciId(3, 0); + OCTET_STRING_fromBuf( + &root_certificate->toBeSigned.cracaId, + reinterpret_cast(craciId.data()), + craciId.size() + ); + root_certificate->version = 3; + root_certificate->toBeSigned.crlSeries = 0; + + // section 7.2.3 in TS 103 097 v2.1.1 + root_certificate.add_permission(aid::CRL, ByteBuffer({0x01})); + root_certificate.add_permission(aid::CTL, ByteBuffer({0x018})); + + // section 7.2.3 in TS 103 097 v2.1.1 + // certIssuePermissions shall be used to indicate issuing permissions + // See https://cpoc.jrc.ec.europa.eu/data/documents/e01941_CPOC_Protocol_v3.0_20240206.pdf for detailled cert_permissions + // I.3.8. certIssuePermissions with predefined values + PsidGroupPermissions* cert_permission = static_cast(vanetza::asn1::allocate(sizeof(PsidGroupPermissions))); + cert_permission->subjectPermissions.present = SubjectPermissions_PR_explicit; + add_psid_group_permission(cert_permission,aid::SCR,{0x01, 0x3e}, {0xff, 0xc1}); + root_certificate.add_cert_permission(cert_permission); + + PsidGroupPermissions* cert_permission_message = static_cast(vanetza::asn1::allocate(sizeof(PsidGroupPermissions))); + cert_permission_message->subjectPermissions.present = SubjectPermissions_PR_explicit; + add_psid_group_permission(cert_permission_message,aid::CA,{0x01, 0xff, 0xfc}, {0xff, 0x00, 0x03}); + add_psid_group_permission(cert_permission_message,aid::DEN,{0x01, 0xff, 0xff, 0xff}, {0xff, 0x00, 0x00, 0x00}); + add_psid_group_permission(cert_permission_message,aid::TLM,{0x01, 0xe0}, {0xff, 0x1f}); + add_psid_group_permission(cert_permission_message,aid::RLT,{0x01, 0xc0}, {0xff,0x3f}); + add_psid_group_permission(cert_permission_message,aid::IVI,{0x01, 0xff, 0xff,0xff,0xff,0xf8}, {0xff,0x00,0x00,0x00,0x00,0x07}); + add_psid_group_permission(cert_permission_message,aid::TLC_R,{0x02, 0xff, 0xff,0xe0}, {0xff, 0x00, 0x00, 0x1f}); + add_psid_group_permission(cert_permission_message,aid::GN_MGMT,{0x00}, {0xff}); + root_certificate.add_cert_permission(cert_permission_message); + + // section 6 in TS 103 097 v2.1.1 + // set subject attributes + // set the verification_key + X_Coordinate_Only coordinates; + coordinates.x.assign(m_own_key_pair.public_key.x.begin(), m_own_key_pair.public_key.x.end()); + root_certificate->toBeSigned.verifyKeyIndicator.present = VerificationKeyIndicator_PR_verificationKey; + root_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = PublicVerificationKey_PR_ecdsaNistP256; + root_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = EccP256CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &root_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.x_only, + reinterpret_cast(coordinates.x.data()), + coordinates.x.size() + ); + root_certificate->toBeSigned.validityPeriod.start = v2::convert_time32(m_runtime.now() - std::chrono::hours(1));; + root_certificate->toBeSigned.validityPeriod.duration.present = Duration_PR_years; + root_certificate->toBeSigned.validityPeriod.duration.choice.hours = 4; + + sign_authorization_ticket(root_certificate); + + return root_certificate; +} + +} // namespace v3 +} // namespace security +} // namespace vanetza diff --git a/vanetza/security/v3/naive_certificate_provider.hpp b/vanetza/security/v3/naive_certificate_provider.hpp new file mode 100644 index 000000000..bc1a45162 --- /dev/null +++ b/vanetza/security/v3/naive_certificate_provider.hpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +/** + * \brief A very simplistic certificate provider + * + * This certificate provider signs its certificates with a randomly generated root certificate. This means the + * signatures produced based on this certificate provider can't be verified by other parties. + * + * It's intended for experimenting with secured messages without validating signatures. + */ +class NaiveCertificateProvider : public CertificateProvider +{ +public: + NaiveCertificateProvider(const Runtime&); + + /** + * \brief get own certificate for signing + * \return own certificate + */ + const Certificate& own_certificate() override; + + /** + * Get own certificate chain, excluding the leaf certificate and root CA + * \return own certificate chain + */ + std::list own_chain() override; + + /** + * \brief get own private key + * \return private key + */ + const ecdsa256::PrivateKey& own_private_key() override; + + /** + * \brief get ticket signer certificate (same for all instances) + * \return signing authorization authority certificate + */ + const Certificate& aa_certificate(); + + /** + * \brief get root certificate (same for all instances) + * \return signing root certificate + */ + const Certificate& root_certificate(); + + /** + * \brief generate an authorization ticket + * \return generated certificate + */ + Certificate generate_authorization_ticket(); + + /** + * \brief sign an authorization ticket + * \param certificate certificate to sign + */ + void sign_authorization_ticket(Certificate& certificate); + +private: + /** + * \brief get root key (same for all instances) + * \return root key + */ + const ecdsa256::KeyPair& aa_key_pair(); + + /** + * \brief get root key (same for all instances) + * \return root key + */ + const ecdsa256::KeyPair& root_key_pair(); + + /** + * \brief generate a authorization authority certificate + * + * \return generated certificate + */ + Certificate generate_aa_certificate(const std::string& subject_name); + + /** + * \brief generate a root certificate + * + * \return generated certificate + */ + Certificate generate_root_certificate(const std::string& subject_name); + + BackendCryptoPP m_crypto_backend; /*< key generation is not a generic backend feature */ + const Runtime& m_runtime; + const ecdsa256::KeyPair m_own_key_pair; + Certificate m_own_certificate; +}; + +} // namespace v3 +} // namespace security +} // namespace vanetza + diff --git a/vanetza/security/v3/persistence.cpp b/vanetza/security/v3/persistence.cpp new file mode 100644 index 000000000..3f82d1403 --- /dev/null +++ b/vanetza/security/v3/persistence.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +ecdsa256::KeyPair load_private_key_from_file(const std::string& key_path) +{ + CryptoPP::AutoSeededRandomPool rng; + + static std::string HEADER = "-----BEGIN PRIVATE KEY-----"; + static std::string FOOTER = "-----END PRIVATE KEY-----"; + std::ifstream t(key_path); + std::string RSA_PRIV_KEY((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + + size_t pos1, pos2; + pos1 = RSA_PRIV_KEY.find(HEADER); + if(pos1 == std::string::npos) + throw "PEM header not found"; + + pos2 = RSA_PRIV_KEY.find(FOOTER, pos1+1); + if(pos2 == std::string::npos) + throw "PEM footer not found"; + + // Start position and length + pos1 = pos1 + HEADER.length(); + pos2 = pos2 - pos1; + std::string keystr = RSA_PRIV_KEY.substr(pos1, pos2); + + // Base64 decode, place in a ByteQueue + CryptoPP::ByteQueue queue; + CryptoPP::Base64Decoder decoder; + + decoder.Attach(new CryptoPP::Redirector(queue)); + decoder.Put((const CryptoPP::byte*)keystr.data(), keystr.length()); + decoder.MessageEnd(); + + CryptoPP::ECDSA::PrivateKey private_key; + private_key.Load(queue); + + if (!private_key.Validate(rng, 3)) { + throw std::runtime_error("Private key validation failed"); + } + + ecdsa256::KeyPair key_pair; + + auto& private_exponent = private_key.GetPrivateExponent(); + private_exponent.Encode(key_pair.private_key.key.data(), key_pair.private_key.key.size()); + + CryptoPP::ECDSA::PublicKey public_key; + private_key.MakePublicKey(public_key); + + auto& public_element = public_key.GetPublicElement(); + public_element.x.Encode(key_pair.public_key.x.data(), key_pair.public_key.x.size()); + public_element.y.Encode(key_pair.public_key.y.data(), key_pair.public_key.y.size()); + + return key_pair; +} + +v2::PublicKey load_public_key_from_file(const std::string& key_path) +{ + v2::PublicKey public_key; + + std::ifstream key_src; + key_src.open(key_path, std::ios::in | std::ios::binary); + vanetza::InputArchive key_archive(key_src); + v2::deserialize(key_archive, public_key); + + return public_key; +} + +void save_public_key_to_file(const std::string& key_path, const v2::PublicKey& public_key) +{ + std::ofstream dest; + dest.open(key_path.c_str(), std::ios::out | std::ios::binary); + + OutputArchive archive(dest); + v2::serialize(archive, public_key); +} + +Certificate load_certificate_from_file(const std::string& certificate_path) +{ + Certificate certificate; + + std::ifstream certificate_src; + certificate_src.open(certificate_path, std::ios::in | std::ios::binary); + vanetza::ByteBuffer buffer(std::istreambuf_iterator(certificate_src), {}); + certificate.decode(buffer); + + return certificate; +} + +void save_certificate_to_file(const std::string& certificate_path, Certificate& certificate) +{ + std::ofstream dest; + dest.open(certificate_path.c_str(), std::ios::out | std::ios::binary); + + OutputArchive archive(dest); + serialize(archive, certificate); +} + +} // namespace v3 +} // namespace security +} // namespace vanetza diff --git a/vanetza/security/v3/persistence.hpp b/vanetza/security/v3/persistence.hpp new file mode 100644 index 000000000..4edb31985 --- /dev/null +++ b/vanetza/security/v3/persistence.hpp @@ -0,0 +1,50 @@ +#include +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +/** + * \brief Loads a private key from a file + * \param key_path file to load the key from + * \return loaded key + */ +ecdsa256::KeyPair load_private_key_from_file(const std::string& key_path); + +/** + * \brief Loads a public key from a file + * \param key_path file to load the key from + * \return loaded key + */ +v2::PublicKey load_public_key_from_file(const std::string& key_path); + +/** + * \brief Saves a public key to a file + * \param key_path file to save the key to + * \param public_key key to save + */ +void save_public_key_to_file(const std::string& key_path, const v2::PublicKey& public_key); + +/** + * \brief Loads a certificate from a file + * \param certificate_path file to load the certificate from + * \return loaded certificate + */ +Certificate load_certificate_from_file(const std::string& certificate_path); + +/** + * \brief Saves a certificate to a file + * \param certificate_path file to save the certificate to + * \param certificate certificate to save + */ +void save_certificate_to_file(const std::string& certificate_path, const Certificate& certificate); + +} // namespace v2 +} // namespace security +} // namespace vanetza + diff --git a/vanetza/security/v3/secured_message.cpp b/vanetza/security/v3/secured_message.cpp index c2ae6bd63..55dbfa7ca 100644 --- a/vanetza/security/v3/secured_message.cpp +++ b/vanetza/security/v3/secured_message.cpp @@ -1,3 +1,8 @@ +#include +#include +#include +#include +#include #include #include #include @@ -118,6 +123,240 @@ ItsAid SecuredMessage::its_aid() const return aid; } +void SecuredMessage::set_its_aid(ItsAid its_aid) { + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + SignedData* signed_data = m_struct->content->choice.signedData; + if (signed_data && signed_data->tbsData) { + signed_data->tbsData->headerInfo.psid = its_aid; + } + } +} + +void SecuredMessage::set_generation_time(Time64 time) { + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + if (m_struct->content->choice.signedData->tbsData->headerInfo.generationTime == nullptr) { + //generationTime is not initialiazed + m_struct->content->choice.signedData->tbsData->headerInfo.generationTime = static_cast(calloc(1, sizeof(Time64_t)));; + } + asn_uint642INTEGER( + (m_struct->content->choice.signedData->tbsData->headerInfo.generationTime), + time + ); + } +} + +void SecuredMessage::set_generation_location(ThreeDLocation location) { + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + if (m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation == nullptr) { + //generationLocation is not initialiazed + m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation = static_cast(calloc(1, sizeof(ThreeDLocation_t)));; + } + m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation->latitude = location.latitude; + m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation->longitude = location.longitude; + m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation->elevation = location.elevation; + } +} + + +void SecuredMessage::set_inline_p2pcd_request(std::list requests){ + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + ASN_STRUCT_FREE_CONTENTS_ONLY( + asn_DEF_SequenceOfHashedId3, + &(m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest) + ); + for (HashedId3 request : requests) { + this->add_inline_p2_pcd_request(request); + } + } + +} + +void SecuredMessage::add_inline_p2_pcd_request(HashedId3 unkown_certificate_digest) { + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + if (m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest == nullptr) { + //generationTime is not initialiazed + m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest = static_cast(calloc(1, sizeof(SequenceOfHashedId3)));; + } + ASN_SEQUENCE_ADD(&(m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest), &unkown_certificate_digest); + } +} + +void SecuredMessage::set_dummy_signature() { + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + SignedData* signed_data = m_struct->content->choice.signedData; + if (signed_data) { + // Reset the signature structure + ASN_STRUCT_RESET(asn_DEF_Signature, &(signed_data->signature)); + + // Set the signature type to ECDSA NIST P256 + signed_data->signature.present = Signature_PR_ecdsaNistP256Signature; + + // Initialize rSig part of the signature + signed_data->signature.choice.ecdsaNistP256Signature.rSig.present = EccP256CurvePoint_PR_x_only; + std::vector dummy_r(32, 0); // Correct length for P256 signature part + dummy_r[0] = 0; // Ensure the leading byte is set to zero if needed + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaNistP256Signature.rSig.choice.x_only, + reinterpret_cast(dummy_r.data()), + dummy_r.size() + ); + + // Initialize sSig part of the signature + std::vector dummy_s(32, 0); // Correct length for P256 signature part + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaNistP256Signature.sSig, + reinterpret_cast(dummy_s.data()), + dummy_s.size() + ); + } + } +} + +void SecuredMessage::set_signature(const Signature& signature) { + if (m_struct->content->present == Ieee1609Dot2Content_PR_signedData) { + SignedData* signed_data = m_struct->content->choice.signedData; + if (signed_data) { + // Reset the signature structure + ASN_STRUCT_RESET(asn_DEF_Signature, &(signed_data->signature)); + + // Set the signature type to ECDSA NIST P256 + switch (signature.type) + { + case vanetza::security::KeyType::NistP256: + signed_data->signature.present = Signature_PR_ecdsaNistP256Signature; + // Initialize rSig and sSig part of the signature + + // Check the type (x_only, y-1, y-0 or uncompressed ??????) + signed_data->signature.choice.ecdsaNistP256Signature.rSig.present = EccP256CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaNistP256Signature.rSig.choice.x_only, + reinterpret_cast(signature.r.data()), + signature.r.size() + ); + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaNistP256Signature.sSig, + reinterpret_cast(signature.s.data()), + signature.s.size() + ); + break; + case vanetza::security::KeyType::BrainpoolP256r1 : + signed_data->signature.present = Signature_PR_ecdsaBrainpoolP256r1Signature; + // Check the type (x_only, y-1, y-0 or uncompressed ??????) + signed_data->signature.choice.ecdsaBrainpoolP256r1Signature.rSig.present = EccP256CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaBrainpoolP256r1Signature.rSig.choice.x_only, + reinterpret_cast(signature.r.data()), + signature.r.size() + ); + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaBrainpoolP256r1Signature.sSig, + reinterpret_cast(signature.s.data()), + signature.s.size() + ); + break; + case vanetza::security::KeyType::BrainpoolP384r1 : + signed_data->signature.present = Signature_PR_ecdsaBrainpoolP384r1Signature; + // Check the type (x_only, y-1, y-0 or uncompressed ??????) + signed_data->signature.choice.ecdsaBrainpoolP384r1Signature.rSig.present = EccP384CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaBrainpoolP384r1Signature.rSig.choice.x_only, + reinterpret_cast(signature.r.data()), + signature.r.size() + ); + OCTET_STRING_fromBuf( + &signed_data->signature.choice.ecdsaBrainpoolP384r1Signature.sSig, + reinterpret_cast(signature.s.data()), + signature.s.size() + ); + break; + default: + this->set_dummy_signature(); + break; + } + } + } +} + +void SecuredMessage::set_signature(const SomeEcdsaSignature& signature) { + struct ecc_point_visitor : public boost::static_visitor { + EccP256CurvePoint_t operator()(const X_Coordinate_Only& x_only) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_x_only; + OCTET_STRING_fromBuf( + &(to_return->choice.x_only), + reinterpret_cast(x_only.x.data()), + x_only.x.size() + ); + return *to_return; + } + EccP256CurvePoint_t operator()(const Compressed_Lsb_Y_0& y0) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_compressed_y_0; + OCTET_STRING_fromBuf( + &(to_return->choice.compressed_y_0), + reinterpret_cast(y0.x.data()), + y0.x.size() + ); + return *to_return; + } + EccP256CurvePoint_t operator()(const Compressed_Lsb_Y_1& y1) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_compressed_y_1; + OCTET_STRING_fromBuf( + &(to_return->choice.compressed_y_1), + reinterpret_cast(y1.x.data()), + y1.x.size() + ); + return *to_return; + } + EccP256CurvePoint_t operator()(const Uncompressed& unc) const + { + EccP256CurvePoint_t* to_return = static_cast(vanetza::asn1::allocate(sizeof(EccP256CurvePoint_t))); + to_return->present = EccP256CurvePoint_PR_uncompressedP256; + OCTET_STRING_fromBuf( + &(to_return->choice.uncompressedP256.x), + reinterpret_cast(unc.x.data()), + unc.x.size() + ); + OCTET_STRING_fromBuf( + &(to_return->choice.uncompressedP256.y), + reinterpret_cast(unc.y.data()), + unc.y.size() + ); + return *to_return; + } + }; + struct signature_visitor : public boost::static_visitor + { + Signature_t operator()(const EcdsaSignature& signature) const + { + Signature_t* final_signature = static_cast(vanetza::asn1::allocate(sizeof(Signature_t))); + final_signature->present = Signature_PR_ecdsaNistP256Signature; + OCTET_STRING_fromBuf( + &(final_signature->choice.ecdsaNistP256Signature.sSig), + reinterpret_cast(signature.s.data()), + signature.s.size() + ); + final_signature->choice.ecdsaNistP256Signature.rSig = boost::apply_visitor( + ecc_point_visitor(), + signature.R + ); + return *final_signature; + } + Signature_t operator()(const EcdsaSignatureFuture& signature) const + { + Signature_t final_signature; +/* EcdsaSignature temp = signature.get(); + final_signature = boost::apply_visitor(signature_visitor(), temp); */ + return final_signature; + } + }; + m_struct->content->choice.signedData->signature = boost::apply_visitor(signature_visitor(), signature); +} + PacketVariant SecuredMessage::payload() const { ByteBuffer buffer; @@ -133,6 +372,86 @@ PacketVariant SecuredMessage::payload() const return CohesivePacket { std::move(buffer), OsiLayer::Network }; } +void SecuredMessage::set_payload(ByteBuffer& payload) { + switch (m_struct->content->present) { + case Ieee1609Dot2Content_PR_unsecuredData: + vanetza::security::v3::set_payload(&m_struct->content->choice.unsecuredData, payload); + break; + case Ieee1609Dot2Content_PR_signedData: + vanetza::security::v3::set_payload(&m_struct->content->choice.signedData->tbsData->payload->data->content->choice.unsecuredData, payload); + break; + } + +} + +void SecuredMessage::set_signer_info(const SignerInfo& signer_info){ + struct signer_info_visitor : public boost::static_visitor + { + SignerIdentifier_t* operator()(const std::nullptr_t& pnullptr ) const + { + SignerIdentifier_t* signer = static_cast(vanetza::asn1::allocate(sizeof(SignerIdentifier_t))); + signer->present = SignerIdentifier_PR_self; + return signer; + } + + SignerIdentifier_t* operator()(const HashedId8& hashedId8) const + { + SignerIdentifier_t* signer = static_cast(vanetza::asn1::allocate(sizeof(SignerIdentifier_t))); + signer->present = SignerIdentifier_PR_digest; + OCTET_STRING_fromBuf( + &(signer->choice.digest), + reinterpret_cast(hashedId8.data()), + hashedId8.size() + ); + return signer; + } + SignerIdentifier_t* operator()(const Certificate cert) const + { + SignerIdentifier_t* signer = static_cast(vanetza::asn1::allocate(sizeof(SignerIdentifier_t))); + signer->present = SignerIdentifier_PR_certificate; + // Encode the certificate input + vanetza::ByteBuffer cert_buffer = cert.encode(); + // Allocate the memory + Certificate_t* certi = static_cast(vanetza::asn1::allocate(sizeof(Certificate_t))); + // Copy the certificate input inisde the allocated memory + vanetza::asn1::decode_oer(asn_DEF_Certificate, (void**)(&certi), cert_buffer); + // Add the certificate to the list + ASN_SEQUENCE_ADD(&(signer->choice.certificate),certi); + return signer; + } + SignerIdentifier_t* operator()(const std::list& certificates) const + { + SignerIdentifier_t* signer = static_cast(vanetza::asn1::allocate(sizeof(SignerIdentifier_t))); + signer->present = SignerIdentifier_PR_certificate; + for (auto const& cert : certificates){ + // Encode the certificate input + vanetza::ByteBuffer cert_buffer = cert.encode(); + // Allocate the memory + Certificate_t* certi = static_cast(vanetza::asn1::allocate(sizeof(Certificate_t))); + // Copy the certificate input inisde the allocated memory + vanetza::asn1::decode_oer(asn_DEF_Certificate, (void**)(&certi), cert_buffer); + // Add the certificate to the list + ASN_SEQUENCE_ADD(&(signer->choice.certificate), certi); + } + return signer; + } + }; + SignerIdentifier_t* temp = boost::apply_visitor(signer_info_visitor(), signer_info); + ASN_STRUCT_RESET( + asn_DEF_SignerIdentifier, + &(m_struct->content->choice.signedData->signer) + ); + m_struct->content->choice.signedData->signer = *temp; +} + +ByteBuffer SecuredMessage::convert_for_signing(){ + vanetza::ByteBuffer to_return; + try{ + to_return = vanetza::asn1::encode_oer(asn_DEF_ToBeSignedData, m_struct->content->choice.signedData->tbsData); + }catch(std::runtime_error& er){ + } + return to_return; +} bool SecuredMessage::is_signed() const { return m_struct->content->present == Ieee1609Dot2Content_PR_signedData; @@ -241,6 +560,27 @@ ByteBuffer get_payload(const Opaque_t* unsecured) return buffer; } +ByteBuffer convert_to_payload(vanetza::DownPacket packet) +{ + ByteBuffer buf; + byte_buffer_sink sink(buf); + + boost::iostreams::stream_buffer stream(sink); + OutputArchive ar(stream); + + serialize(ar, packet); + + stream.close(); + return buf; +} + +void set_payload(Opaque_t* unsecured, const ByteBuffer& buffer) +{ + unsecured->size = buffer.size(); + unsecured->buf = new uint8_t[unsecured->size]; // Allocate memory for the buffer + std::copy(buffer.begin(), buffer.end(), unsecured->buf); +} + ByteBuffer get_payload(const SignedData* signed_data) { ByteBuffer buffer; diff --git a/vanetza/security/v3/secured_message.hpp b/vanetza/security/v3/secured_message.hpp index 679c9f936..f4c019e93 100644 --- a/vanetza/security/v3/secured_message.hpp +++ b/vanetza/security/v3/secured_message.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,19 @@ struct SecuredMessage : public asn1::asn1c_oer_wrapper boost::optional signature() const; SignerIdentifier signer_identifier() const; ByteBuffer signing_payload() const; + + void set_its_aid(ItsAid its_aid); + void set_generation_time(Time64 time); + void set_generation_location(ThreeDLocation location); + void set_payload(ByteBuffer& payload); + void set_signature(const Signature& signature); + void set_inline_p2pcd_request(std::list requests); + void add_inline_p2_pcd_request(HashedId3 unkown_certificate_digest); + ByteBuffer convert_for_signing(); + void set_signature(const SomeEcdsaSignature& signature); + void set_dummy_signature(); + void set_signer_info(const SignerInfo& signer_info); + }; /** @@ -64,6 +78,8 @@ size_t deserialize(InputArchive& ar, SecuredMessage& msg); ByteBuffer get_payload(const Opaque_t*); ByteBuffer get_payload(const SignedData*); +void set_payload(Opaque_t* unsecured, const ByteBuffer& buffer); +ByteBuffer convert_to_payload(vanetza::DownPacket packet); boost::optional get_certificate_id(const SecuredMessage::SignerIdentifier&); diff --git a/vanetza/security/v3/sign_header_policy.cpp b/vanetza/security/v3/sign_header_policy.cpp new file mode 100644 index 000000000..daa13e158 --- /dev/null +++ b/vanetza/security/v3/sign_header_policy.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +DefaultSignHeaderPolicy::DefaultSignHeaderPolicy(const Runtime& rt, PositionProvider& positioning) : + m_runtime(rt), m_positioning(positioning), m_cam_next_certificate(m_runtime.now()), m_cert_requested(false), m_chain_requested(false) +{ +} + +void DefaultSignHeaderPolicy::prepare_header(const SignRequest& request, CertificateProvider& certificate_provider, SecuredMessage& secured_message) +{ + secured_message.set_its_aid(request.its_aid); + secured_message.set_generation_time(vanetza::security::v2::convert_time64(m_runtime.now())); + //header_info.signer_info = certificate_provider.own_certificate(); + + if (request.its_aid == aid::CA) { + // section 7.1.1 in TS 103 097 v2.1.1 + m_cam_next_certificate = m_runtime.now() + std::chrono::seconds(1); + if (m_runtime.now() < m_cam_next_certificate && !m_cert_requested) { + secured_message.set_signer_info(boost::get(calculate_hash(*(certificate_provider.own_certificate())))); + } else { + secured_message.set_signer_info(certificate_provider.own_certificate()); + m_cam_next_certificate = m_runtime.now() + std::chrono::seconds(1); + } + + if (m_unknown_certificates.size() > 0) { + std::list unknown_certificates(m_unknown_certificates.begin(), m_unknown_certificates.end()); + secured_message.set_inline_p2pcd_request(unknown_certificates); + m_unknown_certificates.clear(); + } + m_cert_requested = false; + m_chain_requested = false; + } + else if (request.its_aid == aid::DEN) { + // section 7.1.2 in TS 103 097 v2.1.1 + secured_message.set_signer_info(certificate_provider.own_certificate()); + ThreeDLocation location; + v2::ThreeDLocation location_v2; + auto position = m_positioning.position_fix(); + if (position.altitude) { + location_v2 = v2::ThreeDLocation(position.latitude, position.longitude, v2::to_elevation(position.altitude->value())); + } else { + location_v2 = v2::ThreeDLocation(position.latitude, position.longitude); + } + location.latitude = location_v2.latitude.value(); + location.longitude = location_v2.longitude.value(); + location.elevation = 0; + secured_message.set_generation_location(location); + } + else { + secured_message.set_signer_info(certificate_provider.own_certificate()); + } +} + +void DefaultSignHeaderPolicy::request_unrecognized_certificate(HashedId8 id) +{ + m_unknown_certificates.insert(truncate(id)); +} + +void DefaultSignHeaderPolicy::request_certificate() +{ + m_cert_requested = true; +} + +void DefaultSignHeaderPolicy::request_certificate_chain() +{ + m_chain_requested = true; +} + +} // namespace v3 +} // namespace security +} // namespace vanetza diff --git a/vanetza/security/v3/sign_header_policy.hpp b/vanetza/security/v3/sign_header_policy.hpp new file mode 100644 index 000000000..b17c1dfdc --- /dev/null +++ b/vanetza/security/v3/sign_header_policy.hpp @@ -0,0 +1,90 @@ +#ifndef SIGN_HEADER_POLICY_HPP_KJIIEGCH +#define SIGN_HEADER_POLICY_HPP_KJIIEGCH + +#include +#include +#include +#include +#include + +namespace vanetza +{ + +// forward declaration +class PositionProvider; + +namespace security +{ + +// forward declarations +class CertificateProvider; +struct SignRequest; + +namespace v3 +{ + + +/** + * SignHeaderPolicy is used while signing messages + * + * SignHeaderPolicy determines the header fields to be included in the secured message. + * Other components can influence the policy's behaviour by calling one of its "report" methods. + */ +class SignHeaderPolicy +{ +public: + /** + * Prepare header fields for next secured message. + * + * \param req signing request (including ITS-AID for example) + * \param certprvd available certificates + * \return header fields + */ + virtual void prepare_header(const SignRequest& req, CertificateProvider& certprvd, SecuredMessage& secured_message) = 0; + + /** + * Mark certificate as unrecognized in next secured message + * \param id hash of unknown certificate + */ + virtual void request_unrecognized_certificate(HashedId8 id) = 0; + + /** + * Request a full certificate to be included in next secured message + */ + virtual void request_certificate() = 0; + + /** + * Request a full certificate chain to be included in next secured message + */ + virtual void request_certificate_chain() = 0; + + virtual ~SignHeaderPolicy() = default; +}; + +/** + * DefaultSignHeaderPolicy implements the default behaviour specified by ETSI TS 103 097 V2.1.1 + */ +class DefaultSignHeaderPolicy : public SignHeaderPolicy +{ +public: + DefaultSignHeaderPolicy(const Runtime&, PositionProvider& positioning); + + void prepare_header(const SignRequest& request, CertificateProvider& certificate_provider, SecuredMessage& secured_message) override; + void request_unrecognized_certificate(HashedId8 id) override; + void request_certificate() override; + void request_certificate_chain() override; + +private: + const Runtime& m_runtime; + PositionProvider& m_positioning; + Clock::time_point m_cam_next_certificate; + std::set m_unknown_certificates; + bool m_cert_requested; + bool m_chain_requested; +}; + +} // namespace v3 +} // namespace security +} // namespace vanetza + +#endif /* SIGN_HEADER_POLICY_HPP_KJIIEGCH */ diff --git a/vanetza/security/v3/sign_service.cpp b/vanetza/security/v3/sign_service.cpp new file mode 100644 index 000000000..9249997ad --- /dev/null +++ b/vanetza/security/v3/sign_service.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +void printByteBuffer(const ByteBuffer& buffer) { + for (size_t i = 0; i < buffer.size(); ++i) { + // Print each byte in hexadecimal format + std::cout << std::hex << static_cast(buffer[i]) << ' '; + } + std::cout << std::dec << std::endl; // Reset stream to decimal format +} + +StraightSignService::StraightSignService(CertificateProvider& provider, Backend& backend, SignHeaderPolicy& policy) : + m_certificates(provider), m_backend(backend), m_policy(policy) +{ +} + +SignConfirm StraightSignService::sign(SignRequest&& request) +{ + SecuredMessage secured_message; + secured_message->protocolVersion = 3; + secured_message->content = static_cast(calloc(1, sizeof(struct Ieee1609Dot2Content))); + secured_message->content->present = Ieee1609Dot2Content_PR_signedData; + secured_message->content->choice.signedData = static_cast(calloc(1, sizeof(struct SignedData))); + secured_message->content->choice.signedData->tbsData = static_cast(calloc(1, sizeof(struct ToBeSignedData))); + secured_message->content->choice.signedData->tbsData->payload = static_cast(calloc(1, sizeof(struct SignedDataPayload))); + secured_message->content->choice.signedData->tbsData->payload->data = static_cast(calloc(1, sizeof(struct Ieee1609Dot2Data))); + secured_message->content->choice.signedData->tbsData->payload->data->protocolVersion = 3; + secured_message->content->choice.signedData->tbsData->payload->data->content = static_cast(calloc(1, sizeof(struct Ieee1609Dot2Content))); + secured_message->content->choice.signedData->tbsData->payload->data->content->present = Ieee1609Dot2Content_PR_unsecuredData; + + ByteBuffer payload; + payload = convert_to_payload(request.plain_message); + secured_message.set_payload(payload); + + m_policy.prepare_header(request, m_certificates, secured_message); + + ByteBuffer to_be_signed_data = secured_message.convert_for_signing(); + const auto& private_key = m_certificates.own_private_key(); + EcdsaSignature signature = m_backend.sign_data(private_key, to_be_signed_data); + secured_message.set_signature(signature); + + SignConfirm confirm; + confirm.secured_message = std::move(secured_message); + return confirm; +} + +DummySignService::DummySignService(const Runtime& runtime, const SignerInfo& signer) : + m_runtime(runtime), m_signer_info(signer) +{ +} + +SignConfirm DummySignService::sign(SignRequest&& request) +{ + SecuredMessage secured_message; + secured_message->protocolVersion = 3; + secured_message->content = static_cast(calloc(1, sizeof(struct Ieee1609Dot2Content))); + secured_message->content->present = Ieee1609Dot2Content_PR_signedData; + secured_message->content->choice.signedData = static_cast(calloc(1, sizeof(struct SignedData))); + secured_message->content->choice.signedData->tbsData = static_cast(calloc(1, sizeof(struct ToBeSignedData))); + secured_message->content->choice.signedData->tbsData->payload = static_cast(calloc(1, sizeof(struct SignedDataPayload))); + secured_message->content->choice.signedData->tbsData->payload->data = static_cast(calloc(1, sizeof(struct Ieee1609Dot2Data))); + secured_message->content->choice.signedData->tbsData->payload->data->protocolVersion = 3; + secured_message->content->choice.signedData->tbsData->payload->data->content = static_cast(calloc(1, sizeof(struct Ieee1609Dot2Content))); + secured_message->content->choice.signedData->tbsData->payload->data->content->present = Ieee1609Dot2Content_PR_unsecuredData; + + ByteBuffer payload; + payload = convert_to_payload(request.plain_message); + secured_message.set_payload(payload); + secured_message.set_dummy_signature(); + secured_message.set_its_aid(request.its_aid); + secured_message.set_generation_time(vanetza::security::v2::convert_time64(m_runtime.now())); + secured_message.set_signer_info(m_signer_info); + + SignConfirm confirm; + confirm.secured_message = std::move(secured_message); + return confirm; +} + +} // namespace v3 +} // namespace security +} // namespace vanetza diff --git a/vanetza/security/v3/sign_service.hpp b/vanetza/security/v3/sign_service.hpp new file mode 100644 index 000000000..b9c753399 --- /dev/null +++ b/vanetza/security/v3/sign_service.hpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +namespace vanetza +{ +namespace security +{ + +// forward declarations +class Backend; + +namespace v3 +{ + +/** + * SignService immediately signing the message using given + */ +class StraightSignService : public SignService +{ +public: + StraightSignService(CertificateProvider&, Backend&, SignHeaderPolicy&); + SignConfirm sign(SignRequest&&) override; + +private: + CertificateProvider & m_certificates; + Backend& m_backend; + SignHeaderPolicy& m_policy; +}; + + +/** + * SignService without real cryptography but dummy signature + */ +class DummySignService : public SignService +{ +public: + /** + * \param rt runtime for appropriate generation time + * \param si signer info attached to header fields of secured message + */ + DummySignService(const Runtime& rt, const SignerInfo& si); + SignConfirm sign(SignRequest&&) override; + +private: + const Runtime& m_runtime; + SignerInfo m_signer_info; +}; + +} // namespace v3 +} // namespace security +} // namespace vanetza diff --git a/vanetza/security/v3/signer_info.cpp b/vanetza/security/v3/signer_info.cpp new file mode 100644 index 000000000..7fd0a24c4 --- /dev/null +++ b/vanetza/security/v3/signer_info.cpp @@ -0,0 +1,15 @@ +#include + +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +} // ns v3 +} // ns security +} // ns vanetza diff --git a/vanetza/security/v3/signer_info.hpp b/vanetza/security/v3/signer_info.hpp new file mode 100644 index 000000000..10157de4f --- /dev/null +++ b/vanetza/security/v3/signer_info.hpp @@ -0,0 +1,41 @@ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +struct Certificate; + +/// described in TS 103 097 v1.2.1, section 4.2.11 +enum class SignerInfoType : uint8_t +{ + Self = 0, // nothing -> nullptr_t + Certificate_Digest_With_SHA256 = 1, // HashedId8 + Certificate = 2, // Certificate + Certificate_Chain = 3, // std::list +}; + +/// described in TS 103 097 v1.2.1, section 4.2.10 +using SignerInfo = boost::variant< + std::nullptr_t, + HashedId8, + Certificate, + std::list +>; + +} // namespace v3 +} // namespace security +} // namespace vanetza \ No newline at end of file diff --git a/vanetza/security/v3/static_certificate_provider.cpp b/vanetza/security/v3/static_certificate_provider.cpp new file mode 100644 index 000000000..47446a68a --- /dev/null +++ b/vanetza/security/v3/static_certificate_provider.cpp @@ -0,0 +1,39 @@ +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +StaticCertificateProvider::StaticCertificateProvider(const Certificate& authorization_ticket, + const ecdsa256::PrivateKey& authorization_ticket_key) : + StaticCertificateProvider(authorization_ticket, authorization_ticket_key, std::list {}) +{ +} + +StaticCertificateProvider::StaticCertificateProvider(const Certificate& authorization_ticket, + const ecdsa256::PrivateKey& authorization_ticket_key, const std::list& chain) : + authorization_ticket(authorization_ticket), authorization_ticket_key(authorization_ticket_key), chain(chain) +{ +} + +const ecdsa256::PrivateKey& StaticCertificateProvider::own_private_key() +{ + return authorization_ticket_key; +} + +std::list StaticCertificateProvider::own_chain() +{ + return chain; +} + +const Certificate& StaticCertificateProvider::own_certificate() +{ + return authorization_ticket; +} + +} // namespace v3 +} // namespace security +} // namespace vanetza diff --git a/vanetza/security/v3/static_certificate_provider.hpp b/vanetza/security/v3/static_certificate_provider.hpp new file mode 100644 index 000000000..67ca7addc --- /dev/null +++ b/vanetza/security/v3/static_certificate_provider.hpp @@ -0,0 +1,61 @@ +#include +#include + +namespace vanetza +{ +namespace security +{ +namespace v3 +{ + +/** + * \brief A simple certificate provider + * + * This certificate provider uses a static certificate and key pair that is pre-generated. + */ +class StaticCertificateProvider : public CertificateProvider +{ +public: + /** + * Create static certificate provider with empty chain + * \param authorization_ticket + * \param ticket_key private key of given authorization ticket + */ + StaticCertificateProvider(const Certificate& authorization_ticket, const ecdsa256::PrivateKey& ticket_key); + + /** + * Create static certificate provider with given chain + * \param authorization_ticket + * \param ticket_key private key of given authorization ticket + * \param chain own certificate chain + */ + StaticCertificateProvider(const Certificate& authorization_ticket, const ecdsa256::PrivateKey& ticket_key, + const std::list& chain); + + /** + * Get own certificate to use for signing + * \return own certificate + */ + virtual const Certificate& own_certificate() override; + + /** + * Get own certificate chain, excluding the leaf certificate and root CA + * \return own certificate chain + */ + virtual std::list own_chain() override; + + /** + * Get private key associated with own certificate + * \return private key + */ + virtual const ecdsa256::PrivateKey& own_private_key() override; + +private: + Certificate authorization_ticket; + ecdsa256::PrivateKey authorization_ticket_key; + std::list chain; +}; + +} // namespace v3 +} // namespace security +} // namespace vanetza