#include "certificate_builder.hpp" #include #include VANETZA_ASN1_SECURITY_HEADER(Certificate.h) #include VANETZA_ASN1_SECURITY_HEADER(PsidGroupPermissions.h) #include #include #include #include #include #include #include #include #include #include #include namespace vanetza { namespace security { namespace pqc { namespace certificate_builder { namespace { void assign_octets(OCTET_STRING_t& destination, const void* source, std::size_t size) { if (size > static_cast(std::numeric_limits::max()) || OCTET_STRING_fromBuf( &destination, static_cast(source), static_cast(size)) != 0) { throw std::runtime_error("cannot allocate ASN.1 octet string"); } } ::vanetza::security::PrivateKey convert_private_key(const ecdsa256::PrivateKey& input) { ::vanetza::security::PrivateKey output; output.type = KeyType::NistP256; output.key.assign(input.key.begin(), input.key.end()); return output; } void set_verification_key(v3::Certificate& certificate, const ecdsa256::PublicKey& public_key) { auto& indicator = certificate->toBeSigned.verifyKeyIndicator; indicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey; auto& verification_key = indicator.choice.verificationKey; verification_key.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256; verification_key.choice.ecdsaNistP256 = v3::to_asn1(compress_public_key(public_key)); } void set_encryption_key(v3::Certificate& certificate, const ecdsa256::PublicKey& public_key) { certificate->toBeSigned.encryptionKey = vanetza::asn1::allocate(); auto& encryption_key = certificate->toBeSigned.encryptionKey->publicKey; encryption_key.present = Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256; encryption_key.choice.eciesNistP256 = v3::to_asn1(compress_public_key(public_key)); } void set_issuer(v3::Certificate& certificate, const v3::Certificate* issuer) { if (!issuer) { certificate->issuer.present = Vanetza_Security_IssuerIdentifier_PR_self; certificate->issuer.choice.self = Vanetza_Security_HashAlgorithm_sha256; return; } const auto digest = issuer->calculate_digest(); if (!digest) { throw std::invalid_argument("issuer certificate has no canonical digest"); } certificate->issuer.present = Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest; assign_octets( certificate->issuer.choice.sha256AndDigest, digest->data(), digest->size()); } void set_subject_name(v3::Certificate& certificate, const std::string& name) { if (name.empty()) { throw std::invalid_argument("CA subject name must not be empty"); } certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_name; assign_octets(certificate->toBeSigned.id.choice.name, name.data(), name.size()); } void set_validity( v3::Certificate& certificate, Clock::time_point now, int validity_days) { constexpr int maximum_days = std::numeric_limits::max() / 24; if (validity_days < 1 || validity_days > maximum_days) { throw std::invalid_argument("validity must be between 1 and 2730 days"); } certificate->toBeSigned.validityPeriod.start = v2::convert_time32(now - std::chrono::hours(1)); certificate->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_hours; certificate->toBeSigned.validityPeriod.duration.choice.hours = validity_days * 24; } void initialize_certificate( v3::Certificate& certificate, const ecdsa256::PublicKey& subject_key, const v3::Certificate* issuer, int validity_days, Clock::time_point now) { certificate->version = 3; certificate->type = Vanetza_Security_CertificateType_explicit; set_issuer(certificate, issuer); static const std::array craca_id {{ 0, 0, 0 }}; assign_octets(certificate->toBeSigned.cracaId, craca_id.data(), craca_id.size()); certificate->toBeSigned.crlSeries = 0; set_validity(certificate, now, validity_days); set_verification_key(certificate, subject_key); } std::vector effective_issue_aids(const std::vector& aids) { if (!aids.empty()) { return aids; } return { aid::CA, aid::DEN, aid::CP, aid::GN_MGMT, aid::IPV6_ROUTING }; } std::vector effective_application_aids(const std::vector& aids) { return aids.empty() ? std::vector { aid::CA, aid::DEN } : aids; } void add_issue_permission_for_aid( v3::asn1::PsidGroupPermissions* group, ItsAid application_id) { switch (application_id) { case aid::CA: v3::add_psid_group_permission( group, application_id, { 0x01, 0xff, 0xfc }, { 0xff, 0x00, 0x03 }); break; case aid::DEN: v3::add_psid_group_permission( group, application_id, { 0x01, 0xff, 0xff, 0xff }, { 0xff, 0x00, 0x00, 0x00 }); break; case aid::CP: v3::add_psid_group_permission(group, application_id, { 0x00 }, { 0xff }); break; case aid::TLM: v3::add_psid_group_permission(group, application_id, { 0x01, 0xe0 }, { 0xff, 0x1f }); break; case aid::RLT: v3::add_psid_group_permission(group, application_id, { 0x01, 0xc0 }, { 0xff, 0x3f }); break; case aid::IVI: v3::add_psid_group_permission( group, application_id, { 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8 }, { 0xff, 0x00, 0x00, 0x00, 0x00, 0x07 }); break; case aid::TLC_R: v3::add_psid_group_permission( group, application_id, { 0x02, 0xff, 0xff, 0xe0 }, { 0xff, 0x00, 0x00, 0x1f }); break; case aid::GN_MGMT: case aid::IPV6_ROUTING: default: v3::add_psid_group_permission(group, application_id, { 0x00 }, { 0xff }); break; } } void add_issue_permissions(v3::Certificate& certificate, const std::vector& aids) { auto* group = vanetza::asn1::allocate(); group->subjectPermissions.present = Vanetza_Security_SubjectPermissions_PR_explicit; for (ItsAid application_id : effective_issue_aids(aids)) { add_issue_permission_for_aid(group, application_id); } certificate.add_cert_issue_permission(group); } void add_application_permissions(v3::Certificate& certificate, const std::vector& aids) { for (ItsAid application_id : effective_application_aids(aids)) { if (application_id == aid::CA) { certificate.add_app_permission(application_id, ByteBuffer { 1, 0, 0 }); } else if (application_id == aid::DEN) { certificate.add_app_permission(application_id, ByteBuffer { 1, 0, 0, 0 }); } else { certificate.add_app_permission(application_id, {}); } } } v3::Certificate canonical_certificate(v3::Certificate certificate) { auto canonical = certificate.canonicalize(); if (!canonical) { throw std::runtime_error("generated certificate cannot be canonicalized"); } std::string error; if (!canonical->validate(error)) { throw std::runtime_error("generated certificate violates ASN.1 constraints: " + error); } return std::move(*canonical); } } // namespace v3::Certificate build_ecc_root_certificate( ::vanetza::security::Backend& ecc_backend, const ecdsa256::KeyPair& subject_key, const CertificateParameters& parameters, Clock::time_point now) { v3::Certificate certificate; initialize_certificate( certificate, subject_key.public_key, nullptr, parameters.validity_days, now); set_subject_name(certificate, parameters.subject_name); add_issue_permissions(certificate, parameters.application_ids); certificate.add_app_permission(aid::CRL, ByteBuffer { 1 }); certificate.add_app_permission(aid::CTL, ByteBuffer { 0x18 }); sign_primary_certificate( certificate, nullptr, ecc_backend, convert_private_key(subject_key.private_key)); return canonical_certificate(std::move(certificate)); } v3::Certificate build_hybrid_root_certificate( ::vanetza::security::Backend& ecc_backend, Backend& pqc_backend, const ecdsa256::KeyPair& subject_key, const KeyPair& subject_pqc_key, const CertificateParameters& parameters, Clock::time_point now) { v3::Certificate certificate; initialize_certificate( certificate, subject_key.public_key, nullptr, parameters.validity_days, now); set_subject_name(certificate, parameters.subject_name); add_issue_permissions(certificate, parameters.application_ids); certificate.add_app_permission(aid::CRL, ByteBuffer { 1 }); certificate.add_app_permission(aid::CTL, ByteBuffer { 0x18 }); set_alternative_public_key(certificate, subject_pqc_key.public_key); sign_alternative_certificate( certificate, nullptr, ecc_backend, pqc_backend, subject_pqc_key.private_key); sign_primary_certificate( certificate, nullptr, ecc_backend, convert_private_key(subject_key.private_key)); return canonical_certificate(std::move(certificate)); } v3::Certificate build_ecc_authorization_authority_certificate( ::vanetza::security::Backend& ecc_backend, const ecdsa256::KeyPair& issuer_key, const v3::Certificate& issuer_certificate, const ecdsa256::PublicKey& subject_key, const CertificateParameters& parameters, Clock::time_point now) { v3::Certificate certificate; initialize_certificate( certificate, subject_key, &issuer_certificate, parameters.validity_days, now); set_subject_name(certificate, parameters.subject_name); add_issue_permissions(certificate, parameters.application_ids); set_encryption_key(certificate, subject_key); sign_primary_certificate( certificate, &issuer_certificate, ecc_backend, convert_private_key(issuer_key.private_key)); return canonical_certificate(std::move(certificate)); } v3::Certificate build_hybrid_authorization_authority_certificate( ::vanetza::security::Backend& ecc_backend, Backend& pqc_backend, const ecdsa256::KeyPair& issuer_key, const PrivateKey& issuer_pqc_key, const v3::Certificate& issuer_certificate, const ecdsa256::PublicKey& subject_key, const PublicKey& subject_pqc_key, const CertificateParameters& parameters, Clock::time_point now) { v3::Certificate certificate; initialize_certificate( certificate, subject_key, &issuer_certificate, parameters.validity_days, now); set_subject_name(certificate, parameters.subject_name); add_issue_permissions(certificate, parameters.application_ids); set_encryption_key(certificate, subject_key); set_alternative_public_key(certificate, subject_pqc_key); sign_alternative_certificate( certificate, &issuer_certificate, ecc_backend, pqc_backend, issuer_pqc_key); sign_primary_certificate( certificate, &issuer_certificate, ecc_backend, convert_private_key(issuer_key.private_key)); return canonical_certificate(std::move(certificate)); } v3::Certificate build_ecc_authorization_ticket( ::vanetza::security::Backend& ecc_backend, const ecdsa256::KeyPair& issuer_key, const v3::Certificate& issuer_certificate, const ecdsa256::PublicKey& subject_key, const CertificateParameters& parameters, Clock::time_point now) { v3::Certificate certificate; initialize_certificate( certificate, subject_key, &issuer_certificate, parameters.validity_days, now); certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none; add_application_permissions(certificate, parameters.application_ids); sign_primary_certificate( certificate, &issuer_certificate, ecc_backend, convert_private_key(issuer_key.private_key)); return canonical_certificate(std::move(certificate)); } v3::Certificate build_hybrid_authorization_ticket( ::vanetza::security::Backend& ecc_backend, Backend& pqc_backend, const ecdsa256::KeyPair& issuer_key, const PrivateKey& issuer_pqc_key, const v3::Certificate& issuer_certificate, const ecdsa256::PublicKey& subject_key, const CertificateParameters& parameters, Clock::time_point now) { v3::Certificate certificate; initialize_certificate( certificate, subject_key, &issuer_certificate, parameters.validity_days, now); certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none; add_application_permissions(certificate, parameters.application_ids); sign_alternative_certificate( certificate, &issuer_certificate, ecc_backend, pqc_backend, issuer_pqc_key); sign_primary_certificate( certificate, &issuer_certificate, ecc_backend, convert_private_key(issuer_key.private_key)); return canonical_certificate(std::move(certificate)); } } // namespace certificate_builder } // namespace pqc } // namespace security } // namespace vanetza