#include #include VANETZA_ASN1_SECURITY_HEADER(Certificate.h) #include #include #include #include #include #include #include #include #include #include namespace vanetza { namespace security { namespace v3 { namespace { HashAlgorithm digest_hash_algorithm(const asn1::EtsiTs103097Certificate& cert); bool copy_curve_point(PublicKey& to, const asn1::EccP256CurvePoint& from); bool copy_curve_point(PublicKey& to, const asn1::EccP384CurvePoint& from); ByteBuffer fetch_octets(const OCTET_STRING_t& octets); ByteBuffer get_x_coordinate(const asn1::EccP256CurvePoint& point); ByteBuffer get_x_coordinate(const asn1::EccP384CurvePoint& point); bool is_compressed(const Vanetza_Security_EccP256CurvePoint& point); bool is_compressed(const Vanetza_Security_EccP384CurvePoint& point); bool is_signature_x_only(const Vanetza_Security_Signature_t& sig); bool compress(Vanetza_Security_EccP256CurvePoint&); bool compress(Vanetza_Security_EccP384CurvePoint&); bool contains_permission(const Vanetza_Security_SequenceOfPsidSspRange_t& permissions, ItsAid aid); bool equal(const asn1::TwoDLocation& lhs, const asn1::TwoDLocation& rhs); bool equal(const asn1::RectangularRegion& lhs, const asn1::RectangularRegion& rhs); bool equal(const asn1::SequenceOfRectangularRegion& lhs, const asn1::SequenceOfRectangularRegion& rhs); bool is_within(const asn1::GeographicRegion& inner, const asn1::CircularRegion& outer); bool is_within(const asn1::GeographicRegion& inner, const asn1::SequenceOfRectangularRegion& outer); bool is_within(const asn1::GeographicRegion& inner, const asn1::PolygonalRegion& outer); bool is_within(const asn1::GeographicRegion& inner, const asn1::GeographicRegion& outer); bool make_x_only(Vanetza_Security_EccP256CurvePoint&); bool make_x_only(Vanetza_Security_EccP384CurvePoint&); bool make_signature_x_only(Vanetza_Security_Signature_t& sig); } // namespace CertificateView::CertificateView(const asn1::EtsiTs103097Certificate* cert) : m_cert(cert) { } Certificate::Certificate() : Wrapper(asn_DEF_Vanetza_Security_EtsiTs103097Certificate), CertificateView { content() } { assert(CertificateView::m_cert == Wrapper::m_struct); } Certificate::Certificate(const asn1::EtsiTs103097Certificate& cert) : Wrapper(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, &cert), CertificateView { content() } { assert(CertificateView::m_cert == Wrapper::m_struct); } Certificate::Certificate(const Certificate& other) : Wrapper(other), CertificateView(content()) { assert(CertificateView::m_cert == Wrapper::m_struct); } Certificate& Certificate::operator=(const Certificate& other) { Wrapper::operator=(other); CertificateView::m_cert = content(); assert(CertificateView::m_cert == Wrapper::m_struct); return *this; } Certificate::Certificate(Certificate&& other) : Wrapper(std::move(other)), CertificateView(content()) { assert(CertificateView::m_cert == Wrapper::m_struct); } Certificate& Certificate::operator=(Certificate&& other) { Wrapper::operator=(std::move(other)); CertificateView::m_cert = content(); assert(CertificateView::m_cert == Wrapper::m_struct); return *this; } boost::optional CertificateView::calculate_digest() const { return m_cert ? v3::calculate_digest(*m_cert) : boost::none; } KeyType CertificateView::get_verification_key_type() const { return m_cert ? v3::get_verification_key_type(*m_cert) : KeyType::Unspecified; } bool CertificateView::valid_at_location(const PositionFix& location, const LocationChecker* lc) const { return m_cert ? lc ? lc->valid_at_location(*m_cert, location) : false : false; } bool CertificateView::valid_at_timepoint(const Clock::time_point& timepoint) const { return m_cert ? v3::valid_at_timepoint(*m_cert, timepoint) : false; } bool valid_at_timepoint(const asn1::EtsiTs103097Certificate& cert, const Clock::time_point& timepoint) { const asn1::ValidityPeriod& validity = cert.toBeSigned.validityPeriod; Clock::time_point start { std::chrono::seconds(validity.start) }; Clock::time_point end = start; switch (validity.duration.present) { case Vanetza_Security_Duration_PR_microseconds: end += std::chrono::microseconds(validity.duration.choice.microseconds); break; case Vanetza_Security_Duration_PR_milliseconds: end += std::chrono::milliseconds(validity.duration.choice.milliseconds); break; case Vanetza_Security_Duration_PR_seconds: end += std::chrono::seconds(validity.duration.choice.seconds); break; case Vanetza_Security_Duration_PR_minutes: end += std::chrono::minutes(validity.duration.choice.minutes); break; case Vanetza_Security_Duration_PR_hours: end += std::chrono::hours(validity.duration.choice.hours); break; case Vanetza_Security_Duration_PR_sixtyHours: end += std::chrono::hours(60) * validity.duration.choice.sixtyHours; break; case Vanetza_Security_Duration_PR_years: // one year is considered 31556952 seconds according to IEEE 1609.2 end += std::chrono::seconds(31556952) * validity.duration.choice.years; break; default: // leave end at start and thus forming an invalid range break; } return timepoint >= start && timepoint < end; } bool CertificateView::valid_for_application(ItsAid aid) const { return m_cert ? v3::valid_for_application(*m_cert, aid) : false; } bool CertificateView::is_allowed_to_issue(ItsAid aid) const { if (!m_cert) { return false; } if (const auto* seq = m_cert->toBeSigned.certIssuePermissions) { for (int i = 0; i < seq->list.count; ++i) { const auto* group = seq->list.array[i]; if (!group) { continue; } const auto& subject_permissions = group->subjectPermissions; if (subject_permissions.present == Vanetza_Security_SubjectPermissions_PR_all) { return true; } else if ( subject_permissions.present == Vanetza_Security_SubjectPermissions_PR_explicit && contains_permission(subject_permissions.choice.Explicit, aid)) { return true; } } } return false; } boost::optional CertificateView::assurance_level() const { if (!m_cert || !m_cert->toBeSigned.assuranceLevel || m_cert->toBeSigned.assuranceLevel->size < 1) { return boost::none; } return m_cert->toBeSigned.assuranceLevel->buf[0]; } bool CertificateView::region_is_within(const CertificateView& issuer) const { if (!m_cert || !issuer.m_cert) { return false; } const auto* issuer_region = issuer.m_cert->toBeSigned.region; const auto* subject_region = m_cert->toBeSigned.region; if (!issuer_region) { return true; } else if (!subject_region) { return false; } else { return is_within(*subject_region, *issuer_region); } } bool valid_for_application(const asn1::EtsiTs103097Certificate& cert, ItsAid aid) { const asn1::SequenceOfPsidSsp* permissions = cert.toBeSigned.appPermissions; if (permissions) { for (int i = 0; i < permissions->list.count; ++i) { if (permissions->list.array[i]->psid == aid) { return true; } } } // only explicitly allowed applications are valid return false; } boost::optional CertificateView::issuer_digest() const { boost::optional digest; if (m_cert != nullptr) { switch (m_cert->issuer.present) { case Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest: digest = create_hashed_id8(m_cert->issuer.choice.sha256AndDigest); break; case Vanetza_Security_IssuerIdentifier_PR_sha384AndDigest: digest = create_hashed_id8(m_cert->issuer.choice.sha384AndDigest); break; default: break; } } return digest; } bool CertificateView::issuer_is_self() const { return m_cert->issuer.present == Vanetza_Security_IssuerIdentifier_PR_self; } bool CertificateView::has_region_restriction() const { return m_cert ? m_cert->toBeSigned.region != nullptr : false; } bool CertificateView::is_ca_certificate() const { return m_cert && m_cert->toBeSigned.certIssuePermissions != nullptr; } bool CertificateView::is_at_certificate() const { return m_cert && m_cert->toBeSigned.certIssuePermissions == nullptr && m_cert->toBeSigned.appPermissions != nullptr; } bool CertificateView::is_canonical() const { return m_cert ? v3::is_canonical(*m_cert) : false; } StartAndEndValidity CertificateView::get_start_and_end_validity() const { StartAndEndValidity start_and_end; start_and_end.start_validity = Time32(m_cert->toBeSigned.validityPeriod.start); Time32 duration = 0; switch (m_cert->toBeSigned.validityPeriod.duration.present) { case Vanetza_Security_Duration_PR_NOTHING: break; case Vanetza_Security_Duration_PR_microseconds: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.microseconds/1000000; break; case Vanetza_Security_Duration_PR_milliseconds: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.milliseconds/1000; break; case Vanetza_Security_Duration_PR_seconds: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.seconds; break; case Vanetza_Security_Duration_PR_minutes: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.minutes*60; break; case Vanetza_Security_Duration_PR_hours: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.hours*60*60; break; case Vanetza_Security_Duration_PR_sixtyHours: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.sixtyHours*60*60*60; break; case Vanetza_Security_Duration_PR_years: duration += (int)m_cert->toBeSigned.validityPeriod.duration.choice.years*60*60*24*365; break; default: break; } start_and_end.end_validity = start_and_end.start_validity + duration; return start_and_end; } bool is_canonical(const asn1::EtsiTs103097Certificate& cert) { bool compressed_point = true; const Vanetza_Security_VerificationKeyIndicator& indicator = cert.toBeSigned.verifyKeyIndicator; if (indicator.present == Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) { const Vanetza_Security_PublicVerificationKey& pubkey = indicator.choice.verificationKey; switch (pubkey.present) { case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256: compressed_point = is_compressed(pubkey.choice.ecdsaNistP256); break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1: compressed_point = is_compressed(pubkey.choice.ecdsaBrainpoolP256r1); break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1: compressed_point = is_compressed(pubkey.choice.ecdsaBrainpoolP384r1); break; default: break; } } else if (indicator.present == Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue) { compressed_point = is_compressed(indicator.choice.reconstructionValue); } if (!compressed_point) { return false; } else if (cert.signature && !is_signature_x_only(*cert.signature)) { return false; } else { return true; } } ByteBuffer CertificateView::encode() const { return m_cert ? asn1::encode_oer(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, m_cert) : ByteBuffer {}; } ByteBuffer Certificate::encode() const { return Wrapper::encode(); } boost::optional CertificateView::canonicalize() const { return m_cert ? v3::canonicalize(*m_cert) : boost::none; } static boost::optional canonicalize(Certificate&& canonical) { bool success = true; if (canonical->toBeSigned.verifyKeyIndicator.present == Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) { Vanetza_Security_PublicVerificationKey& pubkey = canonical->toBeSigned.verifyKeyIndicator.choice.verificationKey; switch (pubkey.present) { case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256: success &= compress(pubkey.choice.ecdsaNistP256); break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1: success &= compress(pubkey.choice.ecdsaBrainpoolP256r1); break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1: success &= compress(pubkey.choice.ecdsaBrainpoolP384r1); break; default: break; } } else if (canonical->toBeSigned.verifyKeyIndicator.present == Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue) { success &= compress(canonical->toBeSigned.verifyKeyIndicator.choice.reconstructionValue); } if (canonical->toBeSigned.encryptionKey) { Vanetza_Security_BasePublicEncryptionKey& pubkey = canonical->toBeSigned.encryptionKey->publicKey; switch (pubkey.present) { case Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256: success &= compress(pubkey.choice.eciesNistP256); break; case Vanetza_Security_BasePublicEncryptionKey_PR_eciesBrainpoolP256r1: success &= compress(pubkey.choice.eciesBrainpoolP256r1); break; default: break; } } if (canonical->signature) { success &= make_signature_x_only(*canonical->signature); } if (success) { assert(is_canonical(*canonical)); return canonical; } else { return boost::none; } } boost::optional canonicalize(const asn1::EtsiTs103097Certificate& cert) { try { Certificate copy { cert }; return canonicalize(std::move(copy)); } catch (const std::exception&) { return boost::none; } } boost::optional calculate_digest_internal(const asn1::EtsiTs103097Certificate& cert, HashAlgorithm hash_algo) { boost::optional digest; try { ByteBuffer buffer = asn1::encode_oer(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, &cert); switch (hash_algo) { case HashAlgorithm::SHA256: digest = create_hashed_id8(calculate_sha256_digest(buffer.data(), buffer.size())); break; case HashAlgorithm::SHA384: digest = create_hashed_id8(calculate_sha384_digest(buffer.data(), buffer.size())); break; default: break; } } catch (const std::exception&) { // cannot calculate digest of non-encodable certificate } return digest; } boost::optional calculate_digest(const asn1::EtsiTs103097Certificate& cert) { boost::optional digest; const HashAlgorithm hash_algo = digest_hash_algorithm(cert); if (hash_algo != HashAlgorithm::Unspecified) { if (is_canonical(cert)) { digest = calculate_digest_internal(cert, hash_algo); } else { auto maybe_canonical_cert = canonicalize(cert); if (maybe_canonical_cert) { digest = calculate_digest_internal(*maybe_canonical_cert.value(), hash_algo); } } } return digest; } KeyType get_verification_key_type(const asn1::EtsiTs103097Certificate& cert) { KeyType key_type = KeyType::Unspecified; if (cert.toBeSigned.verifyKeyIndicator.present == Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) { switch (cert.toBeSigned.verifyKeyIndicator.choice.verificationKey.present) { case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256: key_type = KeyType::NistP256; break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1: key_type = KeyType::BrainpoolP256r1; break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1: key_type = KeyType::BrainpoolP384r1; break; default: break; } } return key_type; } boost::optional get_public_key(const asn1::EtsiTs103097Certificate& cert) { asn1::VerificationKeyIndicator indicator = cert.toBeSigned.verifyKeyIndicator; if (indicator.present != Vanetza_Security_VerificationKeyIndicator_PR_verificationKey) { return boost::none; } const asn1::PublicVerificationKey& input = cert.toBeSigned.verifyKeyIndicator.choice.verificationKey; PublicKey output; switch (input.present) { case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256: output.type = KeyType::NistP256; if (copy_curve_point(output, input.choice.ecdsaNistP256)) { return output; } else { return boost::none; } break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1: output.type = KeyType::BrainpoolP256r1; if (copy_curve_point(output, input.choice.ecdsaBrainpoolP256r1)) { return output; } else { return boost::none; } break; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1: output.type = KeyType::BrainpoolP384r1; if (copy_curve_point(output, input.choice.ecdsaBrainpoolP384r1)) { return output; } else { return boost::none; } break; default: return boost::none; break; } } boost::optional get_public_encryption_key(const asn1::EtsiTs103097Certificate& cert) { const asn1::PublicEncryptionKey* enc_key = cert.toBeSigned.encryptionKey; if (!enc_key || enc_key->supportedSymmAlg != Vanetza_Security_SymmAlgorithm_aes128Ccm) { return boost::none; } PublicKey output; switch (enc_key->publicKey.present) { case Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256: output.type = KeyType::NistP256; if (copy_curve_point(output, enc_key->publicKey.choice.eciesNistP256)) { return output; } else { return boost::none; } break; case Vanetza_Security_BasePublicEncryptionKey_PR_eciesBrainpoolP256r1: output.type = KeyType::BrainpoolP256r1; if (copy_curve_point(output, enc_key->publicKey.choice.eciesBrainpoolP256r1)) { return output; } else { return boost::none; } break; default: return boost::none; break; } } boost::optional get_signature(const asn1::EtsiTs103097Certificate& cert) { if (!cert.signature) { return boost::none; } const asn1::Signature* asn = cert.signature; Signature sig; switch (asn->present) { case Vanetza_Security_Signature_PR_ecdsaNistP256Signature: sig.type = KeyType::NistP256; sig.r = get_x_coordinate(asn->choice.ecdsaNistP256Signature.rSig); sig.s = fetch_octets(asn->choice.ecdsaNistP256Signature.sSig); break; case Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature: sig.type = KeyType::BrainpoolP256r1; sig.r = get_x_coordinate(asn->choice.ecdsaBrainpoolP256r1Signature.rSig); sig.s = fetch_octets(asn->choice.ecdsaBrainpoolP256r1Signature.sSig); break; case Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature: sig.type = KeyType::BrainpoolP384r1; sig.r = get_x_coordinate(asn->choice.ecdsaBrainpoolP384r1Signature.rSig); sig.s = fetch_octets(asn->choice.ecdsaBrainpoolP384r1Signature.sSig); break; default: return boost::none; } return sig; } std::list get_aids(const asn1::EtsiTs103097Certificate& cert) { std::list aids; const asn1::SequenceOfPsidSsp* seq = cert.toBeSigned.appPermissions; if (seq) { for (int i = 0; i < seq->list.count; ++i) { aids.push_back(seq->list.array[i]->psid); } } return aids; } ByteBuffer get_app_permissions(const asn1::EtsiTs103097Certificate& cert, ItsAid aid) { ByteBuffer perms; const asn1::SequenceOfPsidSsp* seq = cert.toBeSigned.appPermissions; if (seq) { for (int i = 0; i < seq->list.count; ++i) { if (seq->list.array[i]->psid == aid && seq->list.array[i]->ssp != nullptr) { const asn1::ServiceSpecificPermissions& ssp = *seq->list.array[i]->ssp; if (ssp.present == Vanetza_Security_ServiceSpecificPermissions_PR_bitmapSsp) { const asn1::BitmapSsp& bitmap = ssp.choice.bitmapSsp; perms.assign(bitmap.buf, bitmap.buf + bitmap.size); break; } else if (ssp.present == Vanetza_Security_ServiceSpecificPermissions_PR_opaque) { const asn1::Opaque& opaque = ssp.choice.opaque; perms.assign(opaque.buf, opaque.buf + opaque.size); break; } } } } return perms; } void add_psid_group_permission(asn1::PsidGroupPermissions* group_permission, ItsAid aid, const ByteBuffer& ssp, const ByteBuffer& bitmask) { auto psid_range_scr = asn1::allocate(); psid_range_scr->psid = aid; psid_range_scr->sspRange = asn1::allocate(); psid_range_scr->sspRange->present = Vanetza_Security_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 Certificate::add_app_permission(ItsAid aid, const ByteBuffer& ssp) { asn1::SequenceOfPsidSsp* seq = m_struct->toBeSigned.appPermissions; if (!seq) { seq = asn1::allocate(); m_struct->toBeSigned.appPermissions = seq; } auto psid_ptr = asn1::allocate(); psid_ptr->psid = aid; if (!ssp.empty()) { psid_ptr->ssp = asn1::allocate(); psid_ptr->ssp->present = Vanetza_Security_ServiceSpecificPermissions_PR_bitmapSsp; OCTET_STRING_fromBuf( &psid_ptr->ssp->choice.bitmapSsp, reinterpret_cast(ssp.data()), ssp.size() ); } ASN_SEQUENCE_ADD(seq, psid_ptr); } void Certificate::add_cert_issue_permission(asn1::PsidGroupPermissions* group_permission) { asn1::SequenceOfPsidGroupPermissions* seq = m_struct->toBeSigned.certIssuePermissions; if (!seq) { seq = asn1::allocate(); m_struct->toBeSigned.certIssuePermissions = seq; } ASN_SEQUENCE_ADD(seq, group_permission); } void Certificate::set_signature(const SomeEcdsaSignature& signature) { if (!m_struct->signature) { m_struct->signature = asn1::allocate(); } else { asn1::reset(m_struct->signature); } struct signature_visitor : public boost::static_visitor<> { asn1::Signature* sig; explicit signature_visitor(asn1::Signature* s) : sig(s) { } void operator()(const EcdsaSignature& signature) const { sig->present = Vanetza_Security_Signature_PR_ecdsaNistP256Signature; assign(&sig->choice.ecdsaNistP256Signature.sSig, signature.s); sig->choice.ecdsaNistP256Signature.rSig = to_asn1(signature.R); } void operator()(const EcdsaSignatureFuture& signature) const { this->operator()(signature.get()); } }; boost::apply_visitor(signature_visitor(m_struct->signature), signature); } Certificate fake_certificate() { Certificate cert; cert->issuer.present = Vanetza_Security_IssuerIdentifier_PR_self; cert->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none; std::array craca_id = { 0, 0, 0}; OCTET_STRING_fromBuf(&cert->toBeSigned.cracaId, craca_id.data(), craca_id.size()); cert->version = 3; cert->toBeSigned.crlSeries = 0; cert->toBeSigned.validityPeriod.start = 0; cert->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_minutes; cert->toBeSigned.validityPeriod.duration.choice.minutes = 10080; cert->toBeSigned.verifyKeyIndicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey; cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256; cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0; std::array dummy_r {}; // 32 octets for P256 signature OCTET_STRING_fromBuf( &cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.x_only, dummy_r.data(), dummy_r.size() ); cert.add_app_permission(aid::CA, ByteBuffer({ 1, 0, 0 })); return cert; } void serialize(OutputArchive& ar, const Certificate& certificate) { ByteBuffer buffer = certificate.encode(); ar.save_binary(buffer.data(), buffer.size()); } namespace { bool contains_permission(const Vanetza_Security_SequenceOfPsidSspRange_t& permissions, ItsAid aid) { for (int i = 0; i < permissions.list.count; ++i) { const auto* permission = permissions.list.array[i]; if (permission && permission->psid == aid) { return true; } } return false; } bool equal(const asn1::TwoDLocation& lhs, const asn1::TwoDLocation& rhs) { return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude; } bool equal(const asn1::RectangularRegion& lhs, const asn1::RectangularRegion& rhs) { return equal(lhs.northWest, rhs.northWest) && equal(lhs.southEast, rhs.southEast); } bool equal(const asn1::SequenceOfRectangularRegion& lhs, const asn1::SequenceOfRectangularRegion& rhs) { if (lhs.list.count != rhs.list.count) { return false; } for (int i = 0; i < lhs.list.count; ++i) { if (!lhs.list.array[i] || !rhs.list.array[i] || !equal(*lhs.list.array[i], *rhs.list.array[i])) { return false; } } return true; } bool is_within(const asn1::GeographicRegion& inner, const asn1::CircularRegion& outer) { if (inner.present != Vanetza_Security_GeographicRegion_PR_circularRegion) { return false; } const auto& circle = inner.choice.circularRegion; if (!is_valid(circle.center) || !is_valid(outer.center) || circle.radius < 0 || outer.radius < 0) { return false; } PositionFix inner_center; inner_center.latitude = convert_latitude(circle.center.latitude); inner_center.longitude = convert_longitude(circle.center.longitude); return distance(inner_center, outer.center) + circle.radius * units::si::meter <= outer.radius * units::si::meter; } bool is_within(const asn1::GeographicRegion& inner, const asn1::SequenceOfRectangularRegion& outer) { return inner.present == Vanetza_Security_GeographicRegion_PR_rectangularRegion && equal(inner.choice.rectangularRegion, outer); } bool is_within(const asn1::GeographicRegion&, const asn1::PolygonalRegion&) { return false; } bool is_within(const asn1::GeographicRegion& inner, const asn1::GeographicRegion& outer) { switch (outer.present) { case Vanetza_Security_GeographicRegion_PR_circularRegion: return is_within(inner, outer.choice.circularRegion); case Vanetza_Security_GeographicRegion_PR_rectangularRegion: return is_within(inner, outer.choice.rectangularRegion); case Vanetza_Security_GeographicRegion_PR_polygonalRegion: return is_within(inner, outer.choice.polygonalRegion); case Vanetza_Security_GeographicRegion_PR_NOTHING: return true; default: return false; } } bool copy_curve_point(PublicKey& to, const asn1::EccP256CurvePoint& from) { bool copied = true; switch (from.present) { case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0: to.compression = KeyCompression::Y0; to.x = fetch_octets(from.choice.compressed_y_0); break; case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1: to.compression = KeyCompression::Y1; to.x = fetch_octets(from.choice.compressed_y_1); break; case Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256: to.compression = KeyCompression::NoCompression; to.x = fetch_octets(from.choice.uncompressedP256.x); to.y = fetch_octets(from.choice.uncompressedP256.y); break; default: copied = false; break; } return copied; } bool copy_curve_point(PublicKey& to, const asn1::EccP384CurvePoint& from) { bool copied = true; switch (from.present) { case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0: to.compression = KeyCompression::Y0; to.x = fetch_octets(from.choice.compressed_y_0); break; case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1: to.compression = KeyCompression::Y1; to.x = fetch_octets(from.choice.compressed_y_1); break; case Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384: to.compression = KeyCompression::NoCompression; to.x = fetch_octets(from.choice.uncompressedP384.x); to.y = fetch_octets(from.choice.uncompressedP384.y); break; default: copied = false; break; } return copied; } HashAlgorithm digest_hash_algorithm(const asn1::EtsiTs103097Certificate& cert) { // IEEE 1609.2 clause 6.4.3: hash algorithm depends on the verification key indicator; // a reconstruction value (implicit certificate) is always an EccP256CurvePoint and // clause 5.3.2 mandates SHA-256 for implicit certificates const asn1::VerificationKeyIndicator& indicator = cert.toBeSigned.verifyKeyIndicator; switch (indicator.present) { case Vanetza_Security_VerificationKeyIndicator_PR_verificationKey: switch (indicator.choice.verificationKey.present) { case Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256: case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP256r1: return HashAlgorithm::SHA256; case Vanetza_Security_PublicVerificationKey_PR_ecdsaBrainpoolP384r1: return HashAlgorithm::SHA384; default: return HashAlgorithm::Unspecified; } case Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue: return HashAlgorithm::SHA256; default: return HashAlgorithm::Unspecified; } } ByteBuffer fetch_octets(const OCTET_STRING_t& octets) { ByteBuffer buffer(octets.size); std::memcpy(buffer.data(), octets.buf, octets.size); return buffer; } ByteBuffer get_x_coordinate(const asn1::EccP256CurvePoint& point) { switch (point.present) { case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0: return fetch_octets(point.choice.compressed_y_0); case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1: return fetch_octets(point.choice.compressed_y_1); case Vanetza_Security_EccP256CurvePoint_PR_x_only: return fetch_octets(point.choice.x_only); case Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256: return fetch_octets(point.choice.uncompressedP256.x); default: return ByteBuffer {}; } } ByteBuffer get_x_coordinate(const asn1::EccP384CurvePoint& point) { switch (point.present) { case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0: return fetch_octets(point.choice.compressed_y_0); case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1: return fetch_octets(point.choice.compressed_y_1); case Vanetza_Security_EccP384CurvePoint_PR_x_only: return fetch_octets(point.choice.x_only); case Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384: return fetch_octets(point.choice.uncompressedP384.x); default: return ByteBuffer {}; } } bool is_compressed(const Vanetza_Security_EccP256CurvePoint& point) { switch (point.present) { case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0: case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1: return true; default: return false; } } bool is_compressed(const Vanetza_Security_EccP384CurvePoint& point) { switch (point.present) { case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0: case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1: return true; default: return false; } } bool is_signature_x_only(const Vanetza_Security_Signature_t& sig) { switch (sig.present) { case Vanetza_Security_Signature_PR_ecdsaNistP256Signature: return sig.choice.ecdsaNistP256Signature.rSig.present == Vanetza_Security_EccP256CurvePoint_PR_x_only; case Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature: return sig.choice.ecdsaBrainpoolP256r1Signature.rSig.present == Vanetza_Security_EccP256CurvePoint_PR_x_only; case Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature: return sig.choice.ecdsaBrainpoolP384r1Signature.rSig.present == Vanetza_Security_EccP384CurvePoint_PR_x_only; default: return true; // not an ECDSA signature at all } } bool compress(Vanetza_Security_EccP256CurvePoint& point) { if (point.present == Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256) { auto& unc = point.choice.uncompressedP256; const bool y_odd = unc.y.size > 0 && unc.y.buf[unc.y.size - 1] & 0x01; asn1::reset(&unc.y); std::free(unc._asn_ctx.ptr); unc._asn_ctx.ptr = nullptr; if (y_odd) { assert(&point.choice.uncompressedP256.x == &point.choice.compressed_y_1); point.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1; } else { assert(&point.choice.uncompressedP256.x == &point.choice.compressed_y_0); point.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0; } return true; } else if (point.present == Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0 || point.present == Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1) { return true; } else { return false; } } bool compress(Vanetza_Security_EccP384CurvePoint& point) { if (point.present == Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384) { auto& unc = point.choice.uncompressedP384; const bool y_odd = unc.y.size > 0 && unc.y.buf[unc.y.size - 1] & 0x01; asn1::reset(&unc.y); std::free(unc._asn_ctx.ptr); unc._asn_ctx.ptr = nullptr; if (y_odd) { assert(&point.choice.uncompressedP384.x == &point.choice.compressed_y_1); point.present = Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1; } else { assert(&point.choice.uncompressedP384.x == &point.choice.compressed_y_0); point.present = Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0; } return true; } else if (point.present == Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0 || point.present == Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1) { return true; } else { return false; } } bool make_x_only(Vanetza_Security_EccP256CurvePoint& point) { if (point.present == Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256) { auto& unc = point.choice.uncompressedP256; asn1::reset(&unc.y); std::free(unc._asn_ctx.ptr); unc._asn_ctx.ptr = nullptr; assert(&unc.x == &point.choice.x_only); point.present = Vanetza_Security_EccP256CurvePoint_PR_x_only; return true; } else if (point.present == Vanetza_Security_EccP256CurvePoint_PR_x_only) { return true; } else { return false; } } bool make_x_only(Vanetza_Security_EccP384CurvePoint& point) { if (point.present == Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384) { auto& unc = point.choice.uncompressedP384; asn1::reset(&unc.y); std::free(unc._asn_ctx.ptr); unc._asn_ctx.ptr = nullptr; assert(&unc.x == &point.choice.x_only); point.present = Vanetza_Security_EccP384CurvePoint_PR_x_only; return true; } else if (point.present == Vanetza_Security_EccP384CurvePoint_PR_x_only) { return true; } else { return false; } } bool make_signature_x_only(Vanetza_Security_Signature& sig) { switch (sig.present) { case Vanetza_Security_Signature_PR_ecdsaNistP256Signature: return make_x_only(sig.choice.ecdsaNistP256Signature.rSig); break; case Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature: return make_x_only(sig.choice.ecdsaBrainpoolP256r1Signature.rSig); break; case Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature: return make_x_only(sig.choice.ecdsaBrainpoolP384r1Signature.rSig); break; default: return false; break; } } } // namespace } // namespace v3 } // namespace security } // namespace vanetza