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).
748 lines
30 KiB
C++
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
|