Files
MicrOBU/microbu-esp32c5/external/vanetza-idf/vanetza/security/v3/secured_message.cpp
T
Ashin Walpola 0e9525162d Keep the colleague's microbu-esp32c5 tree in this repository
obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but
that tree was gitignored, so a clone of this repository could not build the
firmware it ships. It is now committed here as ordinary files in its own
folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP
bridge's signature verification (--trust) used on the bench. Nothing is
fetched from or pushed to the colleague's repository; this repository and
its remotes carry everything. The folder's own .gitignore keeps build output,
downloaded components and private key material out, as it did there; the
committed file set is identical to that repository's tracked files.

The ESP32-C5 is still flashed from obu-firmware/, which only takes
vanetza-idf from microbu-esp32c5/, so the two stay separate folders.
FLASHING.md says how to take a newer version of the colleague's tree (copy
it over the folder, rebuild, test, commit).
2026-09-23 17:46:40 +02:00

748 lines
30 KiB
C++

#include <vanetza/asn1/asn1c_wrapper.hpp>
#include <vanetza/asn1/security_profile.hpp>
#include VANETZA_ASN1_SECURITY_HEADER(Certificate.h)
#include VANETZA_ASN1_SECURITY_HEADER(EtsiTs103097Data.h)
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/byte_buffer_sink.hpp>
#include <vanetza/net/packet.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/v3/asn1_conversions.hpp>
#include <vanetza/security/v3/secured_message.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/optional/optional.hpp>
#include <boost/variant/static_visitor.hpp>
// asn1c quirk
struct Vanetza_Security_Certificate : public Vanetza_Security_CertificateBase {};
namespace vanetza
{
namespace security
{
namespace v3
{
namespace
{
const asn1::SignedData* get_signed_data(const asn1::EtsiTs103097Data* data)
{
if (data && data->content && data->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
return data->content->choice.signedData;
} else {
return nullptr;
}
}
const asn1::HeaderInfo* get_header_info(const asn1::EtsiTs103097Data* data)
{
const asn1::SignedData* signed_data = get_signed_data(data);
if (signed_data) {
return &signed_data->tbsData->headerInfo;
} else {
return nullptr;
}
}
HashedId8 make_hashed_id8(const asn1::HashedId8& asn)
{
HashedId8 result;
std::copy_n(asn.buf, std::min(asn.size, result.size()), result.data());
return result;
}
ByteBuffer copy_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 copy_octets(point.choice.compressed_y_0);
break;
case Vanetza_Security_EccP256CurvePoint_PR_compressed_y_1:
return copy_octets(point.choice.compressed_y_1);
break;
case Vanetza_Security_EccP256CurvePoint_PR_x_only:
return copy_octets(point.choice.x_only);
break;
case Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256:
return copy_octets(point.choice.uncompressedP256.x);
break;
default:
return ByteBuffer {};
break;
}
}
ByteBuffer get_x_coordinate(const asn1::EccP384CurvePoint& point)
{
switch (point.present) {
case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_0:
return copy_octets(point.choice.compressed_y_0);
break;
case Vanetza_Security_EccP384CurvePoint_PR_compressed_y_1:
return copy_octets(point.choice.compressed_y_1);
break;
case Vanetza_Security_EccP384CurvePoint_PR_x_only:
return copy_octets(point.choice.x_only);
break;
case Vanetza_Security_EccP384CurvePoint_PR_uncompressedP384:
return copy_octets(point.choice.uncompressedP384.x);
break;
default:
return ByteBuffer {};
break;
}
}
} // namespace
SecuredMessage SecuredMessage::with_signed_data()
{
SecuredMessage secured_message;
secured_message->protocolVersion = 3;
secured_message->content = asn1::allocate<asn1::Ieee1609Dot2Content>();
secured_message->content->present = Vanetza_Security_Ieee1609Dot2Content_PR_signedData;
secured_message->content->choice.signedData = asn1::allocate<asn1::SignedData>();
secured_message->content->choice.signedData->tbsData = asn1::allocate<asn1::ToBeSignedData>();
secured_message->content->choice.signedData->tbsData->payload = asn1::allocate<asn1::SignedDataPayload>();
secured_message->content->choice.signedData->tbsData->payload->data = asn1::allocate<asn1::Ieee1609Dot2Data>();
secured_message->content->choice.signedData->tbsData->payload->data->protocolVersion = 3;
secured_message->content->choice.signedData->tbsData->payload->data->content = asn1::allocate<asn1::Ieee1609Dot2Content>();
secured_message->content->choice.signedData->tbsData->payload->data->content->present = Vanetza_Security_Ieee1609Dot2Content_PR_unsecuredData;
return secured_message;
}
SecuredMessage SecuredMessage::with_signed_data_hash()
{
SecuredMessage secured_message;
secured_message->protocolVersion = 3;
secured_message->content = asn1::allocate<asn1::Ieee1609Dot2Content>();
secured_message->content->present = Vanetza_Security_Ieee1609Dot2Content_PR_signedData;
secured_message->content->choice.signedData = asn1::allocate<asn1::SignedData>();
secured_message->content->choice.signedData->tbsData = asn1::allocate<asn1::ToBeSignedData>();
secured_message->content->choice.signedData->tbsData->payload = asn1::allocate<asn1::SignedDataPayload>();
secured_message->content->choice.signedData->tbsData->payload->extDataHash = asn1::allocate<asn1::HashedData>();
secured_message->content->choice.signedData->tbsData->payload->extDataHash->present = Vanetza_Security_HashedData_PR_sha256HashedData;
return secured_message;
}
SecuredMessage SecuredMessage::with_encrypted_data()
{
SecuredMessage secured_message;
secured_message->protocolVersion = 3;
secured_message->content = asn1::allocate<asn1::Ieee1609Dot2Content>();
secured_message->content->present = Vanetza_Security_Ieee1609Dot2Content_PR_encryptedData;
return secured_message;
}
SecuredMessage::SecuredMessage() :
asn1::asn1c_oer_wrapper<asn1::EtsiTs103097Data>(asn_DEF_Vanetza_Security_EtsiTs103097Data)
{
}
uint8_t SecuredMessage::protocol_version() const
{
return m_struct->protocolVersion;
}
boost::optional<HashedId8> SecuredMessage::certificate_id() const
{
return get_certificate_id(signer_identifier());
}
ItsAid SecuredMessage::its_aid() const
{
ItsAid aid = 0;
const asn1::SignedData* signed_data = get_signed_data(m_struct);
if (signed_data && signed_data->tbsData) {
aid = signed_data->tbsData->headerInfo.psid;
}
return aid;
}
void SecuredMessage::set_its_aid(ItsAid its_aid)
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
asn1::SignedData* signed_data = m_struct->content->choice.signedData;
if (signed_data && signed_data->tbsData) {
signed_data->tbsData->headerInfo.psid = its_aid;
}
}
}
void SecuredMessage::set_generation_time(Time64 time)
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
if (m_struct->content->choice.signedData->tbsData->headerInfo.generationTime == nullptr) {
m_struct->content->choice.signedData->tbsData->headerInfo.generationTime = asn1::allocate<asn1::Time64>();
}
asn_uint642INTEGER(m_struct->content->choice.signedData->tbsData->headerInfo.generationTime, time);
}
}
void SecuredMessage::set_generation_location(const asn1::ThreeDLocation& location)
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
if (m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation == nullptr) {
m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation = asn1::allocate<asn1::ThreeDLocation>();
}
m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation->latitude = location.latitude;
m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation->longitude = location.longitude;
m_struct->content->choice.signedData->tbsData->headerInfo.generationLocation->elevation = location.elevation;
}
}
std::list<HashedId3> SecuredMessage::get_inline_p2pcd_request() const
{
std::list<HashedId3> requests;
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
const asn1::SequenceOfHashedId3* inline_p2pcd_request = m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest;
if (inline_p2pcd_request) {
for (int i = 0; i < inline_p2pcd_request->list.count; i++) {
// vanetza-idf: the element is a 3-octet HashedId3; widening it through the
// 8-octet conversion and truncating kept the wrong end (zero padding).
requests.push_back(create_hashed_id3(*inline_p2pcd_request->list.array[i]));
}
}
}
return requests;
}
void SecuredMessage::set_inline_p2pcd_request(std::list<HashedId3> requests)
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
assert(m_struct->content->choice.signedData);
assert(m_struct->content->choice.signedData->tbsData);
if (m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest) {
asn1::reset(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 unknown_certificate_digest)
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
if (m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest == nullptr) {
m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest = asn1::allocate<asn1::SequenceOfHashedId3>();
}
Vanetza_Security_HashedId3_t* asn_digest = OCTET_STRING_new_fromBuf(&asn_DEF_Vanetza_Security_HashedId3,
reinterpret_cast<char*>(unknown_certificate_digest.data()), unknown_certificate_digest.size());
ASN_SEQUENCE_ADD(m_struct->content->choice.signedData->tbsData->headerInfo.inlineP2pcdRequest, asn_digest);
}
}
void SecuredMessage::set_dummy_signature()
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
asn1::SignedData* signed_data = m_struct->content->choice.signedData;
if (signed_data) {
// Reset the signature structure
asn1::reset(signed_data->signature);
// Set the signature type to ECDSA NIST P256
signed_data->signature.present = Vanetza_Security_Signature_PR_ecdsaNistP256Signature;
// Initialize rSig part of the signature
signed_data->signature.choice.ecdsaNistP256Signature.rSig.present = Vanetza_Security_EccP256CurvePoint_PR_x_only;
std::vector<uint8_t> dummy_r(32, 0); // Correct length for P256 signature part
dummy_r[0] = 0; // Ensure the leading byte is set to zero if needed
assign(&signed_data->signature.choice.ecdsaNistP256Signature.rSig.choice.x_only, dummy_r);
// Initialize sSig part of the signature
std::vector<uint8_t> dummy_s(32, 0); // Correct length for P256 signature part
assign(&signed_data->signature.choice.ecdsaNistP256Signature.sSig, dummy_s);
}
}
}
void SecuredMessage::set_signature(const Signature& signature)
{
if (m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData) {
asn1::SignedData* signed_data = m_struct->content->choice.signedData;
if (signed_data) {
// Reset the signature structure
asn1::reset(signed_data->signature);
// Set the signature type to ECDSA NIST P256
switch (signature.type)
{
case vanetza::security::KeyType::NistP256:
signed_data->signature.present = Vanetza_Security_Signature_PR_ecdsaNistP256Signature;
// Initialize rSig and sSig part of the signature
// Check the type (x_only, y-1, y-0 or uncompressed ??????)
signed_data->signature.choice.ecdsaNistP256Signature.rSig.present = Vanetza_Security_EccP256CurvePoint_PR_x_only;
assign(&signed_data->signature.choice.ecdsaNistP256Signature.rSig.choice.x_only, signature.r);
assign(&signed_data->signature.choice.ecdsaNistP256Signature.sSig, signature.s);
break;
case vanetza::security::KeyType::BrainpoolP256r1 :
signed_data->signature.present = Vanetza_Security_Signature_PR_ecdsaBrainpoolP256r1Signature;
// Check the type (x_only, y-1, y-0 or uncompressed ??????)
signed_data->signature.choice.ecdsaBrainpoolP256r1Signature.rSig.present = Vanetza_Security_EccP256CurvePoint_PR_x_only;
assign(&signed_data->signature.choice.ecdsaBrainpoolP256r1Signature.rSig.choice.x_only, signature.r);
assign(&signed_data->signature.choice.ecdsaBrainpoolP256r1Signature.sSig, signature.s);
break;
case vanetza::security::KeyType::BrainpoolP384r1 :
signed_data->signature.present = Vanetza_Security_Signature_PR_ecdsaBrainpoolP384r1Signature;
// Check the type (x_only, y-1, y-0 or uncompressed ??????)
signed_data->signature.choice.ecdsaBrainpoolP384r1Signature.rSig.present = Vanetza_Security_EccP384CurvePoint_PR_x_only;
assign(&signed_data->signature.choice.ecdsaBrainpoolP384r1Signature.rSig.choice.x_only, signature.r);
assign(&signed_data->signature.choice.ecdsaBrainpoolP384r1Signature.sSig, signature.s);
break;
default:
this->set_dummy_signature();
break;
}
}
}
}
void SecuredMessage::set_signature(const SomeEcdsaSignature& signature)
{
struct signature_visitor : public boost::static_visitor<asn1::Signature>
{
asn1::Signature operator()(const EcdsaSignature& signature) const
{
asn1::Signature final_signature = {};
final_signature.present = Vanetza_Security_Signature_PR_ecdsaNistP256Signature;
assign(&final_signature.choice.ecdsaNistP256Signature.sSig, signature.s);
final_signature.choice.ecdsaNistP256Signature.rSig = to_asn1(signature.R);
return final_signature;
}
asn1::Signature operator()(const EcdsaSignatureFuture& signature) const
{
return this->operator()(signature.get());
}
};
asn1::reset(m_struct->content->choice.signedData->signature);
m_struct->content->choice.signedData->signature = boost::apply_visitor(signature_visitor(), signature);
}
PacketVariant SecuredMessage::payload() const
{
ByteBuffer buffer;
switch (m_struct->content->present) {
case Vanetza_Security_Ieee1609Dot2Content_PR_unsecuredData:
buffer = get_payload(&m_struct->content->choice.unsecuredData);
break;
case Vanetza_Security_Ieee1609Dot2Content_PR_signedData:
buffer = get_payload(m_struct->content->choice.signedData);
break;
default:
// empty buffer as fallback
break;
}
return CohesivePacket { std::move(buffer), OsiLayer::Network };
}
void SecuredMessage::set_payload(const ByteBuffer& payload)
{
switch (m_struct->content->present) {
case Vanetza_Security_Ieee1609Dot2Content_PR_unsecuredData:
vanetza::security::v3::set_payload(&m_struct->content->choice.unsecuredData, payload);
break;
case Vanetza_Security_Ieee1609Dot2Content_PR_signedData:
vanetza::security::v3::set_payload(&m_struct->content->choice.signedData->tbsData->payload->data->content->choice.unsecuredData, payload);
break;
default:
// cannot copy payload into secured message
break;
}
}
void SecuredMessage::set_external_payload_hash(const Sha256Digest& hash)
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData);
asn1::HashedData* hashed_data = m_struct->content->choice.signedData->tbsData->payload->extDataHash;
asn1::reset(hashed_data);
hashed_data->present = Vanetza_Security_HashedData_PR_sha256HashedData;
OCTET_STRING_fromBuf(&hashed_data->choice.sha256HashedData, reinterpret_cast<const char*>(hash.data()), hash.size());
}
HashAlgorithm SecuredMessage::hash_id() const
{
HashAlgorithm algo = HashAlgorithm::Unspecified;
const asn1::SignedData* signed_data = get_signed_data(m_struct);
if (signed_data) {
switch (signed_data->hashId) {
case Vanetza_Security_HashAlgorithm_sha256:
algo = HashAlgorithm::SHA256;
break;
case Vanetza_Security_HashAlgorithm_sha384:
algo = HashAlgorithm::SHA384;
break;
default:
break;
}
}
return algo;
}
void SecuredMessage::set_hash_id(HashAlgorithm hash)
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData);
switch (hash) {
case HashAlgorithm::SHA256:
m_struct->content->choice.signedData->hashId = Vanetza_Security_HashAlgorithm_sha256;
break;
case HashAlgorithm::SHA384:
m_struct->content->choice.signedData->hashId = Vanetza_Security_HashAlgorithm_sha384;
break;
default:
m_struct->content->choice.signedData->hashId = -1;
}
}
void SecuredMessage::set_signer_identifier_self()
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData);
asn1::SignerIdentifier* signer = &m_struct->content->choice.signedData->signer;
asn1::reset(signer);
signer->present = Vanetza_Security_SignerIdentifier_PR_self;
}
void SecuredMessage::set_signer_identifier(const HashedId8& digest)
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData);
asn1::SignerIdentifier* signer = &m_struct->content->choice.signedData->signer;
asn1::reset(signer);
signer->present = Vanetza_Security_SignerIdentifier_PR_digest;
OCTET_STRING_fromBuf(&signer->choice.digest, reinterpret_cast<const char*>(digest.data()), digest.size());
}
void SecuredMessage::set_signer_identifier(const Certificate& cert)
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData);
asn1::SignerIdentifier* signer = &m_struct->content->choice.signedData->signer;
asn1::reset(signer);
signer->present = Vanetza_Security_SignerIdentifier_PR_certificate;
ASN_SEQUENCE_ADD(&signer->choice.certificate, asn1::copy(asn_DEF_Vanetza_Security_EtsiTs103097Certificate, cert.content()));
}
void SecuredMessage::get_aes_ccm_ciphertext(ByteBuffer &ccm_ciphertext, std::array<uint8_t, 12> &nonce) const
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_encryptedData);
const asn1::SymmetricCiphertext &symmetric_ciphertext = m_struct->content->choice.encryptedData.ciphertext;
assert(symmetric_ciphertext.present == Vanetza_Security_SymmetricCiphertext_PR_aes128ccm);
const asn1::AesCcmCiphertext &aes_ccm_ciphertext = symmetric_ciphertext.choice.aes128ccm;
ccm_ciphertext = copy_octets(aes_ccm_ciphertext.ccmCiphertext);
std::memcpy(nonce.data(), aes_ccm_ciphertext.nonce.buf, nonce.size());
}
void SecuredMessage::set_aes_ccm_ciphertext(const ByteBuffer &ccm_ciphertext, const std::array<uint8_t, 12> &nonce)
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_encryptedData);
asn1::SymmetricCiphertext &symmetric_ciphertext = m_struct->content->choice.encryptedData.ciphertext;
CHOICE_variant_set_presence(&asn_DEF_Vanetza_Security_SymmetricCiphertext, &symmetric_ciphertext, Vanetza_Security_SymmetricCiphertext_PR_aes128ccm);
asn1::AesCcmCiphertext &aes_ccm_ciphertext = symmetric_ciphertext.choice.aes128ccm;
OCTET_STRING_fromBuf(&aes_ccm_ciphertext.ccmCiphertext, reinterpret_cast<const char *>(ccm_ciphertext.data()), ccm_ciphertext.size());
OCTET_STRING_fromBuf(&aes_ccm_ciphertext.nonce, reinterpret_cast<const char *>(nonce.data()), nonce.size());
}
void SecuredMessage::set_cert_recip_info(const HashedId8& recipient_id,
const std::array<uint8_t, 16>& ecies_ciphertext,
const std::array<uint8_t, 16>& ecies_tag,
const PublicKey& ecies_pub_key)
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_encryptedData);
// own the RecipientInfo until it is handed to the message (exception safety)
auto cert_recip_info = asn1::make_unique<asn1::RecipientInfo>();
CHOICE_variant_set_presence(&asn_DEF_Vanetza_Security_RecipientInfo, cert_recip_info.get(), Vanetza_Security_RecipientInfo_PR_certRecipInfo);
asn1::PKRecipientInfo &pk_recip_info = cert_recip_info->choice.certRecipInfo;
// Set recipient certificate digest
OCTET_STRING_fromBuf(&pk_recip_info.recipientId, reinterpret_cast<const char *>(recipient_id.data()), recipient_id.size());
asn1::EncryptedDataEncryptionKey &enc_data_enc_key = pk_recip_info.encKey;
asn1::EciesP256EncryptedKey *ecies_enc_key = nullptr;
if (ecies_pub_key.type == KeyType::NistP256) {
enc_data_enc_key.present = Vanetza_Security_EncryptedDataEncryptionKey_PR_eciesNistP256;
ecies_enc_key = &enc_data_enc_key.choice.eciesNistP256;
} else if (ecies_pub_key.type == KeyType::BrainpoolP256r1) {
enc_data_enc_key.present = Vanetza_Security_EncryptedDataEncryptionKey_PR_eciesBrainpoolP256r1;
ecies_enc_key = &enc_data_enc_key.choice.eciesBrainpoolP256r1;
} else {
throw std::invalid_argument("Unsupported EC curve");
}
// Set ECIES ciphertext and tag
OCTET_STRING_fromBuf(&ecies_enc_key->c, reinterpret_cast<const char *>(ecies_ciphertext.data()), ecies_ciphertext.size());
OCTET_STRING_fromBuf(&ecies_enc_key->t, reinterpret_cast<const char *>(ecies_tag.data()), ecies_tag.size());
// Set ECIES ephemeral public key
ecies_enc_key->v = to_asn1(make_ecc_point(ecies_pub_key));
ASN_SEQUENCE_ADD(&m_struct->content->choice.encryptedData.recipients.list, cert_recip_info.release());
}
bool SecuredMessage::check_psk_match(const std::array<uint8_t, 16>& psk) const
{
assert(m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_encryptedData);
// Wrap the given PSK into a PSKRecipientInfo_t to calculate the HashedId8
asn1::asn1c_oer_wrapper<asn1::SymmetricEncryptionKey> psk_key(asn_DEF_Vanetza_Security_SymmetricEncryptionKey);
asn1::SymmetricEncryptionKey *psk_key_ptr = &(*psk_key);
CHOICE_variant_set_presence(
&asn_DEF_Vanetza_Security_SymmetricEncryptionKey,
psk_key_ptr,
Vanetza_Security_SymmetricEncryptionKey_PR_aes128Ccm);
OCTET_STRING_fromBuf(&psk_key_ptr->choice.aes128Ccm, reinterpret_cast<const char *>(psk.data()), psk.size());
ByteBuffer bytes = psk_key.encode();
Sha256Digest psk_digest = calculate_sha256_digest(bytes.data(), bytes.size());
HashedId8 psk_id = create_hashed_id8(psk_digest);
// Check every RecipientInfo for a PSKRecipientInfo with matching PSK
const auto &recipient_list = m_struct->content->choice.encryptedData.recipients.list;
for (int i = 0; i < recipient_list.count; i++) {
const asn1::RecipientInfo &recipient_info = *recipient_list.array[i];
if (recipient_info.present != Vanetza_Security_RecipientInfo_PR_pskRecipInfo) {
continue;
}
HashedId8 message_psk_id;
std::memcpy(message_psk_id.data(), recipient_info.choice.pskRecipInfo.buf, message_psk_id.size());
if (psk_id == message_psk_id) {
return true;
}
}
return false;
}
bool SecuredMessage::is_signed() const
{
return m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_signedData;
}
bool SecuredMessage::is_encrypted() const
{
return m_struct->content->present == Vanetza_Security_Ieee1609Dot2Content_PR_encryptedData;
}
boost::optional<SecuredMessage::Time64> SecuredMessage::generation_time() const
{
boost::optional<Time64> gen_time;
auto header_info = get_header_info(m_struct);
if (header_info) {
std::uintmax_t tmp;
if (asn_INTEGER2umax(header_info->generationTime, &tmp) == 0) {
gen_time = tmp;
}
}
return gen_time;
}
boost::optional<Signature> SecuredMessage::signature() const
{
const asn1::SignedData* signed_data = get_signed_data(m_struct);
if (signed_data) {
const asn1::Signature& asn = signed_data->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 = copy_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 = copy_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 = copy_octets(asn.choice.ecdsaBrainpoolP384r1Signature.sSig);
break;
default:
return boost::none;
}
return sig;
}
return boost::none;
}
SecuredMessage::SignerIdentifier SecuredMessage::signer_identifier() const
{
const asn1::SignedData* signed_data = get_signed_data(m_struct);
if (signed_data) {
if (signed_data->signer.present == Vanetza_Security_SignerIdentifier_PR_digest) {
const asn1::HashedId8* digest = &signed_data->signer.choice.digest;
return digest;
} else if (signed_data->signer.present == Vanetza_Security_SignerIdentifier_PR_certificate) {
const asn1::SequenceOfCertificate& certificates = signed_data->signer.choice.certificate;
// TS 103 097 v1.3.1 contraints this to exactly one certificate in clause 5.2
if (certificates.list.count == 1) {
const asn1::Certificate* cert = certificates.list.array[0];
return cert;
}
}
}
return static_cast<asn1::HashedId8*>(nullptr);
}
ByteBuffer SecuredMessage::signing_payload() const
{
const asn1::SignedData* signed_data = get_signed_data(m_struct);
if (signed_data) {
return asn1::encode_oer(asn_DEF_Vanetza_Security_ToBeSignedData, signed_data->tbsData);
} else {
return ByteBuffer {};
}
}
void SecuredMessage::set_requested_certificate(const Certificate& cert)
{
const asn1::SignedData* signed_data = get_signed_data(m_struct);
if (signed_data && signed_data->tbsData) {
if (signed_data->tbsData->headerInfo.requestedCertificate) {
ASN_STRUCT_FREE(asn_DEF_Vanetza_Security_Certificate, signed_data->tbsData->headerInfo.requestedCertificate);
}
signed_data->tbsData->headerInfo.requestedCertificate =
static_cast<Vanetza_Security_Certificate*>(asn1::copy(asn_DEF_Vanetza_Security_Certificate, cert.content()));
}
}
size_t get_size(const SecuredMessage& message)
{
return message.size();
}
void serialize(OutputArchive& ar, const SecuredMessage& msg)
{
ByteBuffer buffer = msg.encode();
ar.save_binary(buffer.data(), buffer.size());
}
size_t deserialize(InputArchive& ar, SecuredMessage& msg)
{
std::size_t len = ar.remaining_bytes();
// TODO optimize decoding step without buffer allocation
ByteBuffer buffer;
buffer.resize(len);
ar.load_binary(buffer.data(), len);
return msg.decode(buffer) ? len : 0;
}
ByteBuffer get_payload(const asn1::Opaque* unsecured)
{
ByteBuffer buffer;
buffer.reserve(unsecured->size);
std::copy_n(unsecured->buf, unsecured->size, std::back_inserter(buffer));
return buffer;
}
ByteBuffer convert_to_payload(vanetza::DownPacket packet)
{
ByteBuffer buf;
byte_buffer_sink sink(buf);
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
OutputArchive ar(stream);
serialize(ar, packet);
stream.close();
return buf;
}
void set_payload(asn1::Opaque* unsecured, const ByteBuffer& buffer)
{
OCTET_STRING_fromBuf(unsecured, reinterpret_cast<const char*>(buffer.data()), buffer.size());
}
ByteBuffer get_payload(const asn1::SignedData* signed_data)
{
ByteBuffer buffer;
if (signed_data->tbsData && signed_data->tbsData->payload) {
const asn1::SignedDataPayload* signed_payload = signed_data->tbsData->payload;
if (signed_payload->data && signed_payload->data->content) {
const asn1::Ieee1609Dot2Content* content = signed_payload->data->content;
if (content->present == Vanetza_Security_Ieee1609Dot2Content_PR_unsecuredData) {
buffer = get_payload(&content->choice.unsecuredData);
}
}
}
return buffer;
}
boost::optional<HashedId8> get_certificate_id(const SecuredMessage::SignerIdentifier& identifier)
{
using result_type = boost::optional<HashedId8>;
struct cert_id_visitor : public boost::static_visitor<result_type> {
result_type operator()(const asn1::HashedId8* digest) const
{
return digest ? make_hashed_id8(*digest) : result_type { };
}
result_type operator()(const asn1::Certificate* cert) const
{
return cert ? calculate_digest(*cert) : result_type { };
}
};
return boost::apply_visitor(cert_id_visitor(), identifier);
}
bool contains_certificate(const SecuredMessage::SignerIdentifier& identifier)
{
struct visitor : public boost::static_visitor<bool> {
bool operator()(const asn1::HashedId8*) const
{
return false;
}
bool operator()(const asn1::Certificate*) const
{
return true;
}
};
return boost::apply_visitor(visitor(), identifier);
}
const asn1::Certificate* get_certificate(const SecuredMessage::SignerIdentifier& identifier)
{
struct visitor : public boost::static_visitor<const asn1::Certificate*> {
const asn1::Certificate* operator()(const asn1::HashedId8*) const
{
return nullptr;
}
const asn1::Certificate* operator()(const asn1::Certificate* cert) const
{
return cert;
}
};
return boost::apply_visitor(visitor(), identifier);
}
} // namespace v3
} // namespace security
} // namespace vanetza