Files
Ashin Walpola d107534eb2 Keep vanetza-idf in obu-firmware, so a plain clone builds the firmware
obu-firmware builds against the vanetza-idf C-ITS library, which until now
came from the colleague's microbu-esp32c5 tree beside the repository and was
not tracked here, so a clone of this repository could not build the firmware
it ships. The library alone is now part of obu-firmware, as
obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit
cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from
there by default; -DVANETZA_IDF_DIR still points the build elsewhere.

The rest of the colleague's tree (their own VAM firmware, PKI tooling,
station-link Python tools, the V2X2MAP bridge) stays out of this repository
and gitignored; nothing is pushed to their repository. NOTES.md, docs/06,
TODO.md and the pcap verifier's usage line point at the new location.
2026-09-24 10:56:05 +02:00

143 lines
5.3 KiB
C++

#include "validation.hpp"
#include "certificate.hpp"
#include "hashed_id8.hpp"
#include "security_module.hpp"
#include <vanetza/asn1/security/Certificate.h>
#include <algorithm>
#include <cstring>
namespace vanetza
{
namespace pki
{
static bool operator==(const Vanetza_Security_HashedId8_t& asn, const HashedId8& own)
{
if (asn.size == own.octets.size()) {
return std::memcmp(asn.buf, own.octets.data(), own.octets.size()) == 0;
} else {
return false;
}
}
bool check_request_hash(const Sha256Hash& request_digest, const ByteBuffer& response_hash)
{
constexpr std::size_t request_hash_length = 16;
return response_hash.size() == request_hash_length &&
std::equal(response_hash.begin(), response_hash.end(), request_digest.octets.begin());
}
bool validate(SecurityModule& security, const Vanetza_Security_EtsiTs103097Data_t& data, const Certificate& cert)
{
const Vanetza_Security_SignedData_t* signed_data = get_signed_data(data);
if (signed_data) {
return validate(security, *signed_data, cert.raw());
} else {
return false;
}
}
bool validate(SecurityModule& security, const Vanetza_Security_SignedData_t& data,
const Vanetza_Security_Certificate_t& cert)
{
HashAlgorithm hash_algo = get_hash_algorithm(data);
PublicKey public_key = get_public_key(cert);
Signature signature = make_signature(data.signature);
if (hash_algo == HashAlgorithm::SHA256) {
Sha256Hash digest = calculate_digest<Sha256Hash>(security, *data.tbsData, &cert);
return security.verify(digest, signature, public_key);
} else if (hash_algo == HashAlgorithm::SHA384) {
Sha384Hash digest = calculate_digest<Sha384Hash>(security, *data.tbsData, &cert);
return security.verify(digest, signature, public_key);
} else {
return false;
}
}
const Vanetza_Security_SignedData_t* get_signed_data(const Vanetza_Security_EtsiTs103097Data_t& data)
{
const Vanetza_Security_SignedData_t* result = nullptr;
if (data.content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
result = data.content->choice.signedData;
}
return result;
}
const Vanetza_Security_Opaque_t* get_unsecured_data(const Vanetza_Security_EtsiTs103097Data_t& data)
{
const Vanetza_Security_Opaque_t* result = nullptr;
if (data.content->present == Vanetza_Security_Ieee1609Dot2Content_PR_unsecuredData) {
result = &data.content->choice.unsecuredData;
}
return result;
}
bool signed_by(SecurityModule& security, const Vanetza_Security_SignedData_t& data, const HashedId8& digest)
{
bool result = false;
if (data.signer.present == Vanetza_Security_SignerIdentifier_PR_digest) {
result = (data.signer.choice.digest == digest);
} else if (data.signer.present == Vanetza_Security_SignerIdentifier_PR_certificate) {
if (data.signer.choice.certificate.list.count >= 1) {
HashedId8 cert_hid8 = calculate_hashed_id8(security, *data.signer.choice.certificate.list.array[0]);
result = (cert_hid8 == digest);
}
}
return result;
}
HashAlgorithm get_hash_algorithm(const Vanetza_Security_SignedData_t& data)
{
switch (data.hashId) {
case Vanetza_Security_HashAlgorithm_sha256:
return HashAlgorithm::SHA256;
case Vanetza_Security_HashAlgorithm_sha384:
return HashAlgorithm::SHA384;
default:
return HashAlgorithm::Unspecified;
}
}
template<>
Sha256Hash calculate_digest(SecurityModule& security, const Vanetza_Security_ToBeSignedData_t& data,
const Vanetza_Security_Certificate_t* cert)
{
std::array<std::uint8_t, 2 * 32> concat_hash;
ByteBuffer tbs = asn1::encode_oer(asn_DEF_Vanetza_Security_ToBeSignedData, &data);
Sha256Hash tbs_hash = security.calculate_sha256_hash(tbs.data(), tbs.size());
std::copy(tbs_hash.octets.begin(), tbs_hash.octets.end(), concat_hash.data());
if (cert) {
Sha256Hash signer_hash = calculate_sha256_hash(security, *cert);
std::copy(signer_hash.octets.begin(), signer_hash.octets.end(), concat_hash.data() + 32);
} else {
static const Sha256Hash empty_string_hash = security.calculate_sha256_hash(nullptr, 0);
std::copy(empty_string_hash.octets.begin(), empty_string_hash.octets.end(), concat_hash.data() + 32);
}
return security.calculate_sha256_hash(concat_hash.data(), concat_hash.size());
}
template<>
Sha384Hash calculate_digest(SecurityModule& security, const Vanetza_Security_ToBeSignedData_t& data,
const Vanetza_Security_Certificate_t* cert)
{
std::array<std::uint8_t, 2 * 48> concat_hash;
ByteBuffer tbs = asn1::encode_oer(asn_DEF_Vanetza_Security_ToBeSignedData, &data);
Sha384Hash tbs_hash = security.calculate_sha384_hash(tbs.data(), tbs.size());
std::copy(tbs_hash.octets.begin(), tbs_hash.octets.end(), concat_hash.data());
if (cert) {
Sha384Hash signer_hash = calculate_sha384_hash(security, *cert);
std::copy(signer_hash.octets.begin(), signer_hash.octets.end(), concat_hash.data() + 48);
} else {
static const Sha384Hash empty_string_hash = security.calculate_sha384_hash(nullptr, 0);
std::copy(empty_string_hash.octets.begin(), empty_string_hash.octets.end(), concat_hash.data() + 48);
}
return security.calculate_sha384_hash(concat_hash.data(), concat_hash.size());
}
} // namespace pki
} // namespace vanetza