Skip to content

Commit

Permalink
security: Better message verification and certificate validation
Browse files Browse the repository at this point in the history
  • Loading branch information
xweissada committed Sep 5, 2024
1 parent 090e181 commit 65f1f81
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 4 deletions.
1 change: 1 addition & 0 deletions vanetza/security/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ add_vanetza_component(security
v3/persistence.cpp
v3/trust_store.cpp
v3/validity_restriction.cpp
v3/verification.cpp
)
target_link_libraries(security PUBLIC asn1 asn1_security common net)
target_link_libraries(security PRIVATE GeographicLib::GeographicLib)
Expand Down
20 changes: 16 additions & 4 deletions vanetza/security/straight_verify_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <vanetza/security/v3/certificate_provider.hpp>
#include <vanetza/security/v3/certificate_validator.hpp>
#include <vanetza/security/v3/sign_header_policy.hpp>
#include <vanetza/security/v3/verification.hpp>
#include <boost/optional.hpp>

namespace vanetza
Expand Down Expand Up @@ -438,6 +439,10 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg)
confirm.report = VerificationReport::Invalid_Timestamp;
return confirm;
}
if (!v3::check_generation_time(msg, m_runtime.now())) {
confirm.report = VerificationReport::Invalid_Timestamp;
return confirm;
}
// TODO further generation time checks depending on application profile

auto signature = msg.signature();
Expand Down Expand Up @@ -481,9 +486,7 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg)
}
// If message is signed by unknown AT, include certificate in next CAM
// See TS 103 097 v2.1.1, section 7.1.1, 1st bullet, 3rd dash
if (msg.its_aid() == aid::CA) {
sign_policy->request_certificate();
}
sign_policy->request_certificate();
}
return confirm;
}
Expand Down Expand Up @@ -514,6 +517,13 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg)
}
}

// Check certificate time validity
if (!v3::check_certificate_time(cert, m_runtime.now())) {
confirm.report = VerificationReport::Invalid_Certificate;
confirm.certificate_validity = CertificateInvalidReason::Off_Time_Period;
return confirm;
}

ByteBuffer data_hash = m_backend.calculate_hash(public_key->type, msg.signing_payload());
ByteBuffer cert_hash = m_backend.calculate_hash(public_key->type, encoded_cert);
ByteBuffer concat_hash = data_hash;
Expand All @@ -525,7 +535,9 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg)
return confirm;
}

if (cert_cache) {
if (cert_cache && !cert.issuer_is_self()) {
printf("Adding cert to cache\n");
// cert.print();
cert_cache->store(cert);
}

Expand Down
12 changes: 12 additions & 0 deletions vanetza/security/v3/certificate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,18 @@ boost::optional<Signature> get_signature(const asn1::EtsiTs103097Certificate& ce
return sig;
}

std::list<ItsAid> get_aids(const asn1::EtsiTs103097Certificate& cert)
{
std::list<ItsAid> 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;
Expand Down
3 changes: 3 additions & 0 deletions vanetza/security/v3/certificate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <vanetza/security/v3/asn1_types.hpp>
#include <vanetza/security/v3/validity_restriction.hpp>
#include <boost/optional/optional_fwd.hpp>
#include <list>

namespace vanetza
{
Expand Down Expand Up @@ -80,6 +81,8 @@ boost::optional<PublicKey> get_public_encryption_key(const asn1::EtsiTs103097Cer
*/
boost::optional<Signature> get_signature(const asn1::EtsiTs103097Certificate& cert);

std::list<ItsAid> get_aids(const asn1::EtsiTs103097Certificate& cert);

/**
* Get application permissions (SSP = service specific permissions)
* \param cert certificate containing application permissions
Expand Down
79 changes: 79 additions & 0 deletions vanetza/security/v3/default_certificate_validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,69 @@ DefaultCertificateValidator::DefaultCertificateValidator(Backend& backend, Certi
{
}

bool check_time_consistency(const Certificate& certificate, const Certificate& signer)
{
StartAndEndValidity certificate_time = certificate.get_start_and_end_validity();
StartAndEndValidity signer_time = signer.get_start_and_end_validity();

if (signer_time.start_validity > certificate_time.start_validity) {
return false;
}

if (signer_time.end_validity < certificate_time.end_validity) {
return false;
}

return true;
}

bool check_permission_consistency(const Certificate& certificate, const Certificate& signer)
{
auto certificate_aids = get_aids(*certificate);
auto signer_aids = get_aids(*signer);

auto compare = [](ItsAid a, ItsAid b) { return a < b; };

certificate_aids.sort(compare);
signer_aids.sort(compare);

return std::includes(signer_aids.begin(), signer_aids.end(), certificate_aids.begin(), certificate_aids.end());

}

bool check_subject_assurance_consistency(const Certificate& certificate, const Certificate& signer)
{
// TODO
return true;
}

bool check_region_consistency(const Certificate& certificate, const Certificate& signer)
{
// TODO
return true;
}

bool check_consistency(const Certificate& certificate, const Certificate& signer)
{
if (!check_time_consistency(certificate, signer)) {
return false;
}

if (!check_permission_consistency(certificate, signer)) {
return false;
}

if (!check_subject_assurance_consistency(certificate, signer)) {
return false;
}

if (!check_region_consistency(certificate, signer)) {
return false;
}

return true;
}

CertificateValidity DefaultCertificateValidator::check_certificate(const Certificate& certificate)
{
HashedId8 signer_hash = certificate.get_issuer_identifier();
Expand Down Expand Up @@ -51,11 +114,23 @@ CertificateValidity DefaultCertificateValidator::check_certificate(const Certifi

if (m_backend.verify_digest(*verification_key, signature_input, *sig)) {
// TODO check certificate consistency
if (!check_consistency(certificate, *signer_cert)) {
return CertificateInvalidReason::Inconsistent_With_Signer;
}

return CertificateValidity::valid();
}
}
}

// There seems to be no way of adding AA certificates to the cache as the
// signed message is constrained to only contain ONE certificate, which
// will always be the AT. This would mean that AA certificates shall be
// inserted into the cache when parsing RCA trust lists by calling this
// function with the AA certificate as argument. nfiniity's implementation
// however does not do this, as it believes a SignerIdentifier of type
// certificate chain with the complete chain is legitimate.

// Authorization authority certificates must be signed by root CA, check if
// we have root CA certificate in trust store
auto trust_store_matches = m_trust_store.lookup(signer_hash);
Expand All @@ -74,6 +149,10 @@ CertificateValidity DefaultCertificateValidator::check_certificate(const Certifi

if (m_backend.verify_digest(*verification_key, signature_input, *sig)) {
// TODO check certificate consistency
if (!check_consistency(certificate, *signer_cert)) {
return CertificateInvalidReason::Inconsistent_With_Signer;
}

return CertificateValidity::valid();
}
}
Expand Down
56 changes: 56 additions & 0 deletions vanetza/security/v3/verification.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <vanetza/security/v3/basic_elements.hpp>
#include <vanetza/security/v3/verification.hpp>

namespace vanetza
{
namespace security
{
namespace v3
{

bool check_generation_time(const SecuredMessage& message, Clock::time_point now)
{
using Time64 = std::uint64_t;
using namespace std::chrono;

bool valid = false;
boost::optional<Time64> generation_time = message.generation_time();
if (generation_time) {
// Values are picked from C2C-CC Basic System Profile v1.1.0, see RS_BSP_168
static const auto generation_time_future = milliseconds(40);
static const Clock::duration generation_time_past_default = minutes(10);
static const Clock::duration generation_time_past_ca = seconds(2);
auto generation_time_past = generation_time_past_default;

const ItsAid its_aid = message.its_aid();
if (aid::CA == its_aid) {
generation_time_past = generation_time_past_ca;
}

if (*generation_time > convert_time64(now + generation_time_future)) {
valid = false;
} else if (*generation_time < convert_time64(now - generation_time_past)) {
valid = false;
} else {
valid = true;
}
}

return valid;
}

bool check_certificate_time(const Certificate& certificate, Clock::time_point now)
{
auto time = certificate.get_start_and_end_validity();
auto time_now = convert_time32(now);

if (time.start_validity > time_now || time.end_validity < time_now) {
return false; // premature or outdated
}

return true;
}

} // namespace v3
} // namespace security
} // namespace vanetza
19 changes: 19 additions & 0 deletions vanetza/security/v3/verification.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once
#include <vanetza/common/clock.hpp>
#include <vanetza/common/position_fix.hpp>
#include <vanetza/security/v3/certificate.hpp>
#include <vanetza/security/v3/secured_message.hpp>

namespace vanetza
{
namespace security
{
namespace v3
{

bool check_generation_time(const SecuredMessage& message, Clock::time_point now);
bool check_certificate_time(const Certificate& certificate, Clock::time_point now);

} // namespace v3
} // namespace security
} // namespace vanetza

0 comments on commit 65f1f81

Please sign in to comment.