diff --git a/vanetza/asn1/asn1c_wrapper.hpp b/vanetza/asn1/asn1c_wrapper.hpp index 931b76bbd..60081c97e 100644 --- a/vanetza/asn1/asn1c_wrapper.hpp +++ b/vanetza/asn1/asn1c_wrapper.hpp @@ -48,6 +48,8 @@ class asn1c_wrapper_common asn1c_wrapper_common(asn_TYPE_descriptor_t& desc) : m_struct(vanetza::asn1::allocate()), m_type(desc) {} + asn1c_wrapper_common(asn_TYPE_descriptor_t& desc, const T* ptr) : + m_struct(static_cast(copy(desc, ptr))), m_type(desc) {} ~asn1c_wrapper_common() { vanetza::asn1::free(m_type, m_struct); } // copy semantics diff --git a/vanetza/security/ecc_point.cpp b/vanetza/security/ecc_point.cpp index ce6ac7761..f79e67384 100644 --- a/vanetza/security/ecc_point.cpp +++ b/vanetza/security/ecc_point.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -53,5 +55,33 @@ std::size_t get_length(const EccPoint& point) return boost::apply_visitor(visitor, point); } +EccPoint compress_public_key(const PublicKey& public_key) +{ + switch (public_key.compression) + { + case KeyCompression::NoCompression: + if (!public_key.y.empty() && public_key.y.back() & 0x01) { + return Compressed_Lsb_Y_1 {public_key.x }; + } else { + return Compressed_Lsb_Y_0 {public_key.x }; + } + case KeyCompression::Y0: + return Compressed_Lsb_Y_0 {public_key.x }; + case KeyCompression::Y1: + return Compressed_Lsb_Y_1 {public_key.x }; + default: + return Compressed_Lsb_Y_0 {}; + } +} + +EccPoint compress_public_key(const ecdsa256::PublicKey& public_key) +{ + if (!public_key.y.empty() && public_key.y.back() & 0x01) { + return Compressed_Lsb_Y_1 { ByteBuffer { public_key.x.begin(), public_key.x.end() } }; + } else { + return Compressed_Lsb_Y_0 { ByteBuffer { public_key.x.begin(), public_key.x.end() } }; + } +} + } // namespace security } // namespace vanetza diff --git a/vanetza/security/ecc_point.hpp b/vanetza/security/ecc_point.hpp index ba75f1ffd..2079c985b 100644 --- a/vanetza/security/ecc_point.hpp +++ b/vanetza/security/ecc_point.hpp @@ -9,6 +9,10 @@ namespace vanetza namespace security { +// forward declarations +struct PublicKey; +namespace ecdsa256 { struct PublicKey; } + /// X_Coordinate_Only specified in TS 103 097 v1.2.1 in section 4.2.5 struct X_Coordinate_Only { @@ -58,6 +62,14 @@ std::size_t get_length(const EccPoint& ecc_point); */ ByteBuffer convert_for_signing(const EccPoint& ecc_point); +/** + * \brief Compressed ECC point from public key + * \param public_key + * \return compressed ECC point + */ +EccPoint compress_public_key(const PublicKey& public_key); +EccPoint compress_public_key(const ecdsa256::PublicKey& public_key); + } // namespace security } // namespace vanetza diff --git a/vanetza/security/hashed_id.cpp b/vanetza/security/hashed_id.cpp index dd5ab3600..a40290293 100644 --- a/vanetza/security/hashed_id.cpp +++ b/vanetza/security/hashed_id.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include namespace vanetza { @@ -30,6 +32,17 @@ HashedId8 create_hashed_id8(const Sha384Digest& digest) return hashed; } +std::string to_string(const vanetza::security::HashedId8& digest) +{ + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (uint8_t octet : digest) + { + ss << std::setw(2) << static_cast(octet); + } + return ss.str(); +} + } // namespace security } // namespace vanetza diff --git a/vanetza/security/hashed_id.hpp b/vanetza/security/hashed_id.hpp index c005682ff..0b8abb06f 100644 --- a/vanetza/security/hashed_id.hpp +++ b/vanetza/security/hashed_id.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace vanetza { @@ -19,6 +20,8 @@ HashedId3 truncate(const HashedId8&); HashedId8 create_hashed_id8(const Sha256Digest&); HashedId8 create_hashed_id8(const Sha384Digest&); +std::string to_string(const vanetza::security::HashedId8&); + } // namespace security } // namespace vanetza diff --git a/vanetza/security/straight_verify_service.cpp b/vanetza/security/straight_verify_service.cpp index 3ce51f59b..b1cd92904 100644 --- a/vanetza/security/straight_verify_service.cpp +++ b/vanetza/security/straight_verify_service.cpp @@ -469,8 +469,22 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg) v3::CertificateCache* m_cache; } certificate_lookup_visitor(cert_cache); auto signer_identifier = msg.signer_identifier(); + + // track "known" stations + auto maybe_digest = v3::get_certificate_id(signer_identifier); + if (m_context_v3.m_cert_cache && maybe_digest) { + bool was_unknown_station = m_context_v3.m_cert_cache->announce(*maybe_digest); + if (was_unknown_station && msg.its_aid() == aid::CA && m_context_v3.m_sign_policy) { + // CAM from unknown station received, add our certificate to next outgoing message + m_context_v3.m_sign_policy->request_certificate(); + } + } + const v3::asn1::Certificate* certificate = boost::apply_visitor(certificate_lookup_visitor, signer_identifier); - if (!certificate) { + if (!certificate && maybe_digest) { + if (m_context_v3.m_sign_policy) { + m_context_v3.m_sign_policy->request_unrecognized_certificate(*maybe_digest); + } confirm.report = VerificationReport::Signer_Certificate_Not_Found; if (sign_policy) { auto cert_hash = v3::get_certificate_id(signer_identifier); @@ -542,8 +556,22 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg) confirm.its_aid = msg.its_aid(); confirm.permissions = v3::get_app_permissions(*cert, confirm.its_aid); - confirm.certificate_id = v3::get_certificate_id(signer_identifier); + confirm.certificate_id = maybe_digest; confirm.report = VerificationReport::Success; + + if (m_context_v3.m_cert_cache && confirm.certificate_id) { + bool already_cached = m_context_v3.m_cert_cache->lookup(*confirm.certificate_id) != nullptr; + if (!already_cached && confirm.its_aid == aid::CA && m_context_v3.m_sign_policy) { + // CAM from unknown station received, add our certificate to next outgoing message + m_context_v3.m_sign_policy->request_certificate(); + } + + // update certificate cache with received certificate + if (certificate && v3::contains_certificate(signer_identifier)) { + m_context_v3.m_cert_cache->store(v3::Certificate { *certificate }); + } + } + return confirm; } diff --git a/vanetza/security/tests/CMakeLists.txt b/vanetza/security/tests/CMakeLists.txt index 1d8e2a365..7a50d9ca5 100644 --- a/vanetza/security/tests/CMakeLists.txt +++ b/vanetza/security/tests/CMakeLists.txt @@ -22,6 +22,7 @@ configure_gtest_directory(LINK_LIBRARIES Boost::boost security security_test) add_gtest(Backend backend.cpp) add_gtest(CamServiceSpecificPermissions cam_ssp.cpp) add_gtest(Certificate certificate.cpp) +add_gtest(CertificateV3 certificate_v3.cpp) add_gtest(CertificateCache certificate_cache.cpp) add_gtest(DefaultCertificateValidator default_certificate_validator.cpp) add_gtest(DummyVerifyService dummy_verify_service.cpp) diff --git a/vanetza/security/tests/certificate_v3.cpp b/vanetza/security/tests/certificate_v3.cpp new file mode 100644 index 000000000..df8b0f3a4 --- /dev/null +++ b/vanetza/security/tests/certificate_v3.cpp @@ -0,0 +1,12 @@ +#include +#include +#include + +using namespace vanetza; +using namespace vanetza::security; + +TEST(CertificateV3, cache) +{ + v3::CertificateCache cache; + cache.store(v3::fake_certificate()); +} \ No newline at end of file diff --git a/vanetza/security/tests/public_key.cpp b/vanetza/security/tests/public_key.cpp index 203305fb4..e99593c03 100644 --- a/vanetza/security/tests/public_key.cpp +++ b/vanetza/security/tests/public_key.cpp @@ -5,16 +5,15 @@ using namespace vanetza; using namespace vanetza::security; -using namespace vanetza::security::v2; using namespace std; -PublicKey serialize(PublicKey key) +v2::PublicKey serialize(v2::PublicKey key) { std::stringstream stream; OutputArchive oa(stream); serialize(oa, key); - PublicKey deKey; + v2::PublicKey deKey; InputArchive ia(stream); deserialize(ia, deKey); return deKey; @@ -22,31 +21,31 @@ PublicKey serialize(PublicKey key) TEST(PublicKey, Field_Size) { - EXPECT_EQ(32, field_size(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256)); - EXPECT_EQ(32, field_size(PublicKeyAlgorithm::ECIES_NISTP256)); + EXPECT_EQ(32, field_size(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256)); + EXPECT_EQ(32, field_size(v2::PublicKeyAlgorithm::ECIES_NISTP256)); } TEST(PublicKey, ECIES_NISTP256) { - ecies_nistp256 ecies; + v2::ecies_nistp256 ecies; ecies.public_key = Uncompressed { random_byte_sequence(32, 1), random_byte_sequence(32, 2) }; - ecies.supported_symm_alg = SymmetricAlgorithm::AES128_CCM; - PublicKey key = ecies; + ecies.supported_symm_alg = v2::SymmetricAlgorithm::AES128_CCM; + v2::PublicKey key = ecies; - PublicKey deKey = serialize(key); + v2::PublicKey deKey = serialize(key); check(key, deKey); - EXPECT_EQ(PublicKeyAlgorithm::ECIES_NISTP256, get_type(deKey)); + EXPECT_EQ(v2::PublicKeyAlgorithm::ECIES_NISTP256, get_type(deKey)); EXPECT_EQ(67, get_size(deKey)); } TEST(PublicKey, ECDSA_NISTP256_With_SHA256) { - ecdsa_nistp256_with_sha256 ecdsa; + v2::ecdsa_nistp256_with_sha256 ecdsa; ecdsa.public_key = X_Coordinate_Only { random_byte_sequence(32, 1) }; - PublicKey key = ecdsa; + v2::PublicKey key = ecdsa; - PublicKey deKey = serialize(key); + v2::PublicKey deKey = serialize(key); check(key, deKey); - EXPECT_EQ(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256, get_type(deKey)); + EXPECT_EQ(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256, get_type(deKey)); EXPECT_EQ(34, get_size(deKey)); } diff --git a/vanetza/security/v3/certificate.cpp b/vanetza/security/v3/certificate.cpp index 32e8cf070..b1881cd26 100644 --- a/vanetza/security/v3/certificate.cpp +++ b/vanetza/security/v3/certificate.cpp @@ -1,7 +1,9 @@ +#include #include #include #include #include +#include #include namespace vanetza @@ -20,11 +22,27 @@ 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 make_x_only(Vanetza_Security_EccP256CurvePoint&); +bool make_x_only(Vanetza_Security_EccP384CurvePoint&); +bool make_signature_x_only(Vanetza_Security_Signature_t& sig); + +} // namespace + Certificate::Certificate() : asn1::asn1c_oer_wrapper(asn_DEF_Vanetza_Security_EtsiTs103097Certificate) { } +Certificate::Certificate(const asn1::EtsiTs103097Certificate& cert) : + asn1::asn1c_oer_wrapper(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, &cert) +{ +} + boost::optional Certificate::calculate_digest() const { return v3::calculate_digest(*content()); @@ -105,15 +123,82 @@ v2::GeographicRegion Certificate::get_region() const return to_return; } -boost::optional calculate_digest(const asn1::EtsiTs103097Certificate& cert) +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; + } +} + +boost::optional canonicalize(const asn1::EtsiTs103097Certificate& cert) +{ + Certificate canonical { cert }; + 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->signature) { + success &= make_signature_x_only(*canonical->signature); + } + + if (success) { + assert(is_canonical(*canonical)); + return canonical; + } else { + return boost::none; + } +} + +boost::optional calculate_digest_internal(const asn1::EtsiTs103097Certificate& cert, KeyType key_type) { boost::optional digest; - auto key_type = get_verification_key_type(cert); - if (key_type) { + try { ByteBuffer buffer = asn1::encode_oer(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, &cert); - switch (*key_type) + switch (key_type) { case KeyType::NistP256: case KeyType::BrainpoolP256r1: @@ -128,8 +213,24 @@ boost::optional calculate_digest(const asn1::EtsiTs103097Certificate& } catch (const std::exception&) { // cannot calculate digest of non-encodable certificate } + + return digest; } +boost::optional calculate_digest(const asn1::EtsiTs103097Certificate& cert) +{ + boost::optional digest; + auto key_type = get_verification_key_type(cert); + if (key_type) { + if (is_canonical(cert)) { + digest = calculate_digest_internal(cert, *key_type); + } else { + auto maybe_canonical_cert = canonicalize(cert); + if (maybe_canonical_cert) { + digest = calculate_digest_internal(*maybe_canonical_cert.value(), *key_type); + } + } + } return digest; } @@ -558,6 +659,124 @@ ByteBuffer fetch_octets(const OCTET_STRING_t& octets) return buffer; } +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; + if (unc.y.size > 0 && unc.y.buf[unc.y.size - 1] & 0x01) { + 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; + if (unc.y.size > 0 && unc.y.buf[unc.y.size - 1] & 0x01) { + 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) { + assert(&point.choice.uncompressedP256.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) { + assert(&point.choice.uncompressedP384.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; + } +} + ByteBuffer get_x_coordinate(const asn1::EccP256CurvePoint& point) { switch (point.present) { diff --git a/vanetza/security/v3/certificate.hpp b/vanetza/security/v3/certificate.hpp index 0b91e3f68..a6f8f443c 100644 --- a/vanetza/security/v3/certificate.hpp +++ b/vanetza/security/v3/certificate.hpp @@ -22,6 +22,7 @@ namespace v3 struct Certificate : public asn1::asn1c_oer_wrapper { Certificate(); + explicit Certificate(const asn1::EtsiTs103097Certificate&); void add_permission(ItsAid aid, const ByteBuffer& ssp); @@ -56,6 +57,13 @@ struct Certificate : public asn1::asn1c_oer_wrapper calculate_digest(const asn1::EtsiTs103097Certificate& cert); +/** + * Check if certificate is in canonical format suitable for digest calculation. + * \param cert certificate + * \return true if certificate is in canonical format + */ +bool is_canonical(const asn1::EtsiTs103097Certificate& cert); + /** * Extract the public key out of a certificate * \param cert certificate diff --git a/vanetza/security/v3/certificate_cache.cpp b/vanetza/security/v3/certificate_cache.cpp index 8d8539761..2b734c7be 100644 --- a/vanetza/security/v3/certificate_cache.cpp +++ b/vanetza/security/v3/certificate_cache.cpp @@ -22,14 +22,27 @@ const Certificate* CertificateCache::lookup(const HashedId8& digest) const } } -void CertificateCache::store(const Certificate& cert) +void CertificateCache::store(Certificate cert) { auto maybe_hash = cert.calculate_digest(); if (maybe_hash) { - m_storage[*maybe_hash] = cert; + m_storage.emplace(*maybe_hash, std::move(cert)); + m_digests.insert(*maybe_hash); } } +bool CertificateCache::announce(const HashedId8& digest) +{ + bool inserted = false; + std::tie(std::ignore, inserted) = m_digests.insert(digest); + return inserted; +} + +bool CertificateCache::is_known(const HashedId8& digest) const +{ + return m_digests.find(digest) != m_digests.end(); +} + } // namespace v3 } // namespace security } // namespace vanetza diff --git a/vanetza/security/v3/certificate_cache.hpp b/vanetza/security/v3/certificate_cache.hpp index 4bdc93201..836dd552e 100644 --- a/vanetza/security/v3/certificate_cache.hpp +++ b/vanetza/security/v3/certificate_cache.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace vanetza { @@ -31,13 +32,29 @@ class CertificateCache * Store a (pre-validated) certificate in cache * \param cert certificate */ - void store(const Certificate& cert); + void store(Certificate cert); size_t size() const { return m_storage.size(); } + /** + * Announce a station with a given certificate digest. + * \param digest certificate digest + * \return true if digest was not known before + */ + bool announce(const HashedId8& digest); + + /** + * Test if a certificate digest is already known, i.e. either + * its certificate is stored or at least the digest has been announced. + * \param digest certificate digest + * \return true if digest is known + */ + bool is_known(const HashedId8& digest) const; + private: // TODO add bounded capacity and automatic removal of expired certificates std::unordered_map m_storage; + std::unordered_set m_digests; const Runtime& m_runtime; }; diff --git a/vanetza/security/v3/naive_certificate_provider.cpp b/vanetza/security/v3/naive_certificate_provider.cpp index 7bd6551a8..8777eea5b 100644 --- a/vanetza/security/v3/naive_certificate_provider.cpp +++ b/vanetza/security/v3/naive_certificate_provider.cpp @@ -8,6 +8,39 @@ namespace security namespace v3 { +namespace +{ + +struct assign_compressed_ecc_point : public boost::static_visitor<> +{ + assign_compressed_ecc_point(Vanetza_Security_EccP256CurvePoint* point) : point(point) + { + } + + void operator()(const Compressed_Lsb_Y_0& y0) const + { + point->present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0; + OCTET_STRING_fromBuf(&point->choice.compressed_y_0, reinterpret_cast(y0.x.data()), y0.x.size()); + } + + void operator()(const Compressed_Lsb_Y_1& y1) const + { + point->present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1; + OCTET_STRING_fromBuf(&point->choice.compressed_y_1, reinterpret_cast(y1.x.data()), y1.x.size()); + } + + template + void operator()(const T&) const + { + point->present = Vanetza_Security_EccP256CurvePoint_PR_NOTHING; + } + + Vanetza_Security_EccP256CurvePoint* point = nullptr; +}; + +} // namespace + + NaiveCertificateProvider::NaiveCertificateProvider(const Runtime& rt) : m_runtime(rt), m_own_key_pair(m_crypto_backend.generate_key_pair()), @@ -69,12 +102,15 @@ Certificate NaiveCertificateProvider::generate_authorization_ticket() Certificate aa_certificate = this->aa_certificate(); // section 6 in TS 103 097 v2.1.1 certificate->issuer.present= Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest; - HashedId8 aa_certi_hashed = boost::get(calculate_digest(*aa_certificate)); - OCTET_STRING_fromBuf( - &(certificate->issuer.choice.sha256AndDigest), - reinterpret_cast(aa_certi_hashed.data()), - aa_certi_hashed.size() - ); + auto maybe_aa_cert_digest = aa_certificate.calculate_digest(); + if (maybe_aa_cert_digest) { + const HashedId8& aa_cert_digest = *maybe_aa_cert_digest; + OCTET_STRING_fromBuf( + &(certificate->issuer.choice.sha256AndDigest), + reinterpret_cast(aa_cert_digest.data()), + aa_cert_digest.size() + ); + } // section 6 in TS 103 097 v2.1.1 certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none; @@ -140,13 +176,15 @@ Certificate NaiveCertificateProvider::generate_aa_certificate(const std::string& //section 7.2.4 in TS 103 097 v2.1.1 Certificate root_cert = this->root_certificate(); aa_certificate->issuer.present= Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest; - HashedId8 root_certi_hashed = boost::get(calculate_digest(*root_cert)); - OCTET_STRING_fromBuf( - &(aa_certificate->issuer.choice.sha256AndDigest), - reinterpret_cast(root_certi_hashed.data()), - root_certi_hashed.size() - ); - + auto maybe_root_cert_digest = root_cert.calculate_digest(); + if (maybe_root_cert_digest) { + const HashedId8& root_cert_digest = *maybe_root_cert_digest; + OCTET_STRING_fromBuf( + &(aa_certificate->issuer.choice.sha256AndDigest), + reinterpret_cast(root_cert_digest.data()), + root_cert_digest.size() + ); + } aa_certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_name; std::string root_name = "AA-cert"; @@ -185,16 +223,10 @@ Certificate NaiveCertificateProvider::generate_aa_certificate(const std::string& // 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()); + auto coordinates = compress_public_key(m_own_key_pair.public_key); aa_certificate->toBeSigned.verifyKeyIndicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey; aa_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256; - aa_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = Vanetza_Security_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() - ); + boost::apply_visitor(assign_compressed_ecc_point(&aa_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256), coordinates); aa_certificate->toBeSigned.validityPeriod.start = v2::convert_time32(m_runtime.now() - std::chrono::hours(1));; aa_certificate->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_years; @@ -275,16 +307,10 @@ Certificate NaiveCertificateProvider::generate_root_certificate(const std::strin // 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()); + EccPoint coordinates = compress_public_key(m_own_key_pair.public_key); root_certificate->toBeSigned.verifyKeyIndicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey; root_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256; - root_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = Vanetza_Security_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() - ); + boost::apply_visitor(assign_compressed_ecc_point(&root_certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256), coordinates); root_certificate->toBeSigned.validityPeriod.start = v2::convert_time32(m_runtime.now() - std::chrono::hours(1));; root_certificate->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_years; root_certificate->toBeSigned.validityPeriod.duration.choice.hours = 4; diff --git a/vanetza/security/v3/secured_message.cpp b/vanetza/security/v3/secured_message.cpp index 98d3da1db..24440aaed 100644 --- a/vanetza/security/v3/secured_message.cpp +++ b/vanetza/security/v3/secured_message.cpp @@ -218,24 +218,29 @@ std::list SecuredMessage::get_inline_p2pcd_request() const void SecuredMessage::set_inline_p2pcd_request(std::list requests) { if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) { - ASN_STRUCT_FREE_CONTENTS_ONLY( - asn_DEF_Vanetza_Security_SequenceOfHashedId3, - &(m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest) - ); + assert(m_struct->content->choice.signedData); + assert(m_struct->content->choice.signedData->tbsData); + + if (m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest) { + ASN_STRUCT_RESET(asn_DEF_Vanetza_Security_SequenceOfHashedId3, + &m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest); + } + for (HashedId3 request : requests) { this->add_inline_p2pcd_request(request); } } - } -void SecuredMessage::add_inline_p2pcd_request(HashedId3 unkown_certificate_digest) +void SecuredMessage::add_inline_p2pcd_request(HashedId3 unknown_certificate_digest) { if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) { - if (m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest == nullptr) { + if (m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest == nullptr) { m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest = asn1::allocate(); } - ASN_SEQUENCE_ADD(&(m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest), &unkown_certificate_digest); + Vanetza_Security_HashedId3_t* asn_digest = OCTET_STRING_new_fromBuf(&asn_DEF_Vanetza_Security_HashedId3, + reinterpret_cast(unknown_certificate_digest.data()), unknown_certificate_digest.size()); + ASN_SEQUENCE_ADD(m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest, asn_digest); } } @@ -683,6 +688,22 @@ boost::optional get_certificate_id(const SecuredMessage::SignerIdenti return boost::apply_visitor(cert_id_visitor(), identifier); } +bool contains_certificate(const SecuredMessage::SignerIdentifier& identifier) +{ + struct visitor : public boost::static_visitor { + bool operator()(const asn1::HashedId8* digest) const + { + return false; + } + + bool operator()(const asn1::Certificate* cert) const + { + return true; + } + }; + return boost::apply_visitor(visitor(), identifier); +} + } // namespace v3 } // namespace security } // namespace vanetza diff --git a/vanetza/security/v3/secured_message.hpp b/vanetza/security/v3/secured_message.hpp index 248e33c0a..82839f08f 100644 --- a/vanetza/security/v3/secured_message.hpp +++ b/vanetza/security/v3/secured_message.hpp @@ -100,6 +100,13 @@ ByteBuffer convert_to_payload(vanetza::ChunkPacket packet); boost::optional get_certificate_id(const SecuredMessage::SignerIdentifier&); +/** + * Check if signer identifier contains a full certificate + * \param signer_identifier to check + * \param true if signer identifier contains a full certificate + */ +bool contains_certificate(const SecuredMessage::SignerIdentifier& signer_identifier); + } // namespace v3 } // namespace security } // namespace vanetza diff --git a/vanetza/security/v3/sign_header_policy.cpp b/vanetza/security/v3/sign_header_policy.cpp index 73e263836..3a324c3df 100644 --- a/vanetza/security/v3/sign_header_policy.cpp +++ b/vanetza/security/v3/sign_header_policy.cpp @@ -19,22 +19,22 @@ DefaultSignHeaderPolicy::DefaultSignHeaderPolicy(const Runtime& rt, PositionProv } void DefaultSignHeaderPolicy::prepare_header(const SignRequest& request, CertificateProvider& certificate_provider, SecuredMessage& secured_message) -{ +{ + const auto now = m_runtime.now(); secured_message.set_its_aid(request.its_aid); - secured_message.set_generation_time(vanetza::security::v2::convert_time64(m_runtime.now())); + secured_message.set_generation_time(vanetza::security::v2::convert_time64(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) { + if (now < m_cam_next_certificate && !m_cert_requested) { auto maybe_digest = calculate_digest(*certificate_provider.own_certificate()); if (maybe_digest) { secured_message.set_signer_identifier(*maybe_digest); } } else { secured_message.set_signer_identifier(certificate_provider.own_certificate()); - m_cam_next_certificate = m_runtime.now() + std::chrono::seconds(1); + m_cam_next_certificate = now + std::chrono::seconds(1) - std::chrono::milliseconds(50); } if (m_unknown_certificates.size() > 0) {