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.
306 lines
14 KiB
C++
306 lines
14 KiB
C++
#include <vanetza/common/its_aid.hpp>
|
|
#include <vanetza/security/v3/naive_certificate_provider.hpp>
|
|
#include <array>
|
|
|
|
namespace vanetza
|
|
{
|
|
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<const char*>(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<const char*>(y1.x.data()), y1.x.size());
|
|
}
|
|
|
|
template<typename T>
|
|
void operator()(const T&) const
|
|
{
|
|
point->present = Vanetza_Security_EccP256CurvePoint_PR_NOTHING;
|
|
}
|
|
|
|
Vanetza_Security_EccP256CurvePoint* point = nullptr;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
NaiveCertificateProvider::NaiveCertificateProvider(const Runtime& rt) :
|
|
m_crypto_backend(create_backend("default")),
|
|
m_runtime(rt),
|
|
m_own_key_pair(m_crypto_backend->generate_key_pair()),
|
|
m_own_certificate(generate_authorization_ticket()) { }
|
|
|
|
const Certificate& NaiveCertificateProvider::own_certificate()
|
|
{
|
|
// Implement the renewal
|
|
return m_own_certificate;
|
|
}
|
|
|
|
const PrivateKey& NaiveCertificateProvider::own_private_key()
|
|
{
|
|
static PrivateKey private_key;
|
|
private_key.type = KeyType::NistP256;
|
|
private_key.key.resize(m_own_key_pair.private_key.key.size());
|
|
std::copy(m_own_key_pair.private_key.key.begin(), m_own_key_pair.private_key.key.end(), private_key.key.data());
|
|
return private_key;
|
|
}
|
|
|
|
const ecdsa256::KeyPair& NaiveCertificateProvider::aa_key_pair()
|
|
{
|
|
static const ecdsa256::KeyPair aa_key_pair = m_crypto_backend->generate_key_pair();
|
|
|
|
return aa_key_pair;
|
|
}
|
|
|
|
const ecdsa256::KeyPair& NaiveCertificateProvider::root_key_pair()
|
|
{
|
|
static const ecdsa256::KeyPair root_key_pair = m_crypto_backend->generate_key_pair();
|
|
|
|
return root_key_pair;
|
|
}
|
|
|
|
const Certificate& NaiveCertificateProvider::aa_certificate()
|
|
{
|
|
static const std::string aa_subject("Naive Authorization CA");
|
|
static const Certificate aa_certificate = generate_aa_certificate(aa_subject);
|
|
|
|
return aa_certificate;
|
|
}
|
|
|
|
const Certificate& NaiveCertificateProvider::root_certificate()
|
|
{
|
|
static const std::string root_subject("Naive Root CA");
|
|
static const Certificate root_certificate = generate_root_certificate(root_subject);
|
|
|
|
return root_certificate;
|
|
}
|
|
|
|
Certificate NaiveCertificateProvider::generate_authorization_ticket()
|
|
{
|
|
// create certificate
|
|
Certificate certificate;
|
|
|
|
const Certificate& aa_certificate = this->aa_certificate();
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
certificate->issuer.present= Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest;
|
|
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<const char *>(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;
|
|
std::vector<uint8_t> craciId(3, 0);
|
|
OCTET_STRING_fromBuf(
|
|
&certificate->toBeSigned.cracaId,
|
|
reinterpret_cast<const char*>(craciId.data()),
|
|
craciId.size()
|
|
);
|
|
certificate->version = 3;
|
|
certificate->toBeSigned.crlSeries = 0;
|
|
|
|
// section 7.2.1 in TS 103 097 v2.1.1
|
|
certificate.add_app_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
|
|
certificate.add_app_permission(aid::DEN, ByteBuffer({ 1, 0xff, 0xff, 0xff}));
|
|
certificate.add_app_permission(aid::GN_MGMT, ByteBuffer({})); // required for beacons
|
|
certificate.add_app_permission(aid::IPV6_ROUTING, ByteBuffer({})); // required for routing tests
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
// set subject attributes
|
|
// set the verification_key
|
|
Uncompressed coordinates;
|
|
coordinates.x.assign(m_own_key_pair.public_key.x.begin(), m_own_key_pair.public_key.x.end());
|
|
coordinates.y.assign(m_own_key_pair.public_key.y.begin(), m_own_key_pair.public_key.y.end());
|
|
certificate->toBeSigned.verifyKeyIndicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey;
|
|
certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256;
|
|
certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.present = Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256;
|
|
OCTET_STRING_fromBuf(
|
|
&certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.uncompressedP256.x,
|
|
reinterpret_cast<const char*>(coordinates.x.data()),
|
|
coordinates.x.size()
|
|
);
|
|
OCTET_STRING_fromBuf(
|
|
&certificate->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256.choice.uncompressedP256.y,
|
|
reinterpret_cast<const char*>(coordinates.y.data()),
|
|
coordinates.y.size()
|
|
);
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
// No constraint
|
|
// set validity restriction
|
|
|
|
certificate->toBeSigned.validityPeriod.start = v2::convert_time32(m_runtime.now() - std::chrono::hours(1));;
|
|
certificate->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_hours;
|
|
certificate->toBeSigned.validityPeriod.duration.choice.hours = 23;
|
|
|
|
sign_authorization_ticket(certificate);
|
|
|
|
return certificate;
|
|
}
|
|
|
|
void NaiveCertificateProvider::sign_authorization_ticket(Certificate& certificate)
|
|
{
|
|
ByteBuffer data_buffer = certificate.encode();
|
|
// TODO build to-be-signed data buffer correctly
|
|
certificate.set_signature(m_crypto_backend->sign_data(aa_key_pair().private_key, data_buffer));
|
|
}
|
|
|
|
Certificate NaiveCertificateProvider::generate_aa_certificate(const std::string& name)
|
|
{
|
|
Certificate aa_certificate;
|
|
|
|
//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;
|
|
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<const char *>(root_cert_digest.data()),
|
|
root_cert_digest.size()
|
|
);
|
|
}
|
|
|
|
aa_certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_name;
|
|
OCTET_STRING_fromBuf(&aa_certificate->toBeSigned.id.choice.name, name.data(), name.size());
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
static const std::array<char, 3> craciId { 0, 0, 0 };
|
|
OCTET_STRING_fromBuf(&aa_certificate->toBeSigned.cracaId, craciId.data(), craciId.size());
|
|
aa_certificate->version = 3;
|
|
aa_certificate->toBeSigned.crlSeries = 0;
|
|
|
|
// section 7.2.4 in TS 103 097 v2.1.1
|
|
// certIssuePermissions shall be used to indicate issuing permissions
|
|
// See https://cpoc.jrc.ec.europa.eu/data/documents/e01941_CPOC_Protocol_v3.0_20240206.pdf for detailled cert_permissions
|
|
// I.3.8. certIssuePermissions with predefined values
|
|
asn1::PsidGroupPermissions* cert_permission_message = asn1::allocate<asn1::PsidGroupPermissions>();
|
|
cert_permission_message->subjectPermissions.present = Vanetza_Security_SubjectPermissions_PR_explicit;
|
|
add_psid_group_permission(cert_permission_message,aid::CA, {0x01, 0xff, 0xfc}, {0xff, 0x00, 0x03});
|
|
add_psid_group_permission(cert_permission_message,aid::DEN, {0x01, 0xff, 0xff, 0xff}, {0xff, 0x00, 0x00, 0x00});
|
|
add_psid_group_permission(cert_permission_message,aid::TLM, {0x01, 0xe0}, {0xff, 0x1f});
|
|
add_psid_group_permission(cert_permission_message,aid::RLT, {0x01, 0xc0}, {0xff, 0x3f});
|
|
add_psid_group_permission(cert_permission_message,aid::IVI, {0x01, 0xff, 0xff, 0xff, 0xff, 0xf8}, {0xff, 0x00, 0x00, 0x00, 0x00, 0x07});
|
|
add_psid_group_permission(cert_permission_message,aid::TLC_R, {0x02, 0xff, 0xff, 0xe0}, {0xff, 0x00, 0x00, 0x1f});
|
|
add_psid_group_permission(cert_permission_message,aid::GN_MGMT, {0x00}, {0xff});
|
|
aa_certificate.add_cert_issue_permission(cert_permission_message);
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
// set subject attributes
|
|
// set the verification_key
|
|
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;
|
|
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;
|
|
aa_certificate->toBeSigned.validityPeriod.duration.choice.hours = 4;
|
|
|
|
Uncompressed encryption_key;
|
|
encryption_key.x.assign(m_own_key_pair.public_key.x.begin(), m_own_key_pair.public_key.x.end());
|
|
encryption_key.y.assign(m_own_key_pair.public_key.y.begin(), m_own_key_pair.public_key.y.end());
|
|
aa_certificate->toBeSigned.encryptionKey = asn1::allocate<asn1::PublicEncryptionKey>();
|
|
aa_certificate->toBeSigned.encryptionKey->publicKey.present = Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256;
|
|
aa_certificate->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256.present = Vanetza_Security_EccP256CurvePoint_PR_uncompressedP256;
|
|
OCTET_STRING_fromBuf(
|
|
&aa_certificate->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256.choice.uncompressedP256.x,
|
|
reinterpret_cast<const char*>(encryption_key.x.data()),
|
|
encryption_key.x.size()
|
|
);
|
|
OCTET_STRING_fromBuf(
|
|
&aa_certificate->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256.choice.uncompressedP256.y,
|
|
reinterpret_cast<const char*>(encryption_key.y.data()),
|
|
encryption_key.y.size()
|
|
);
|
|
|
|
|
|
sign_authorization_ticket(aa_certificate);
|
|
|
|
return aa_certificate;
|
|
}
|
|
|
|
Certificate NaiveCertificateProvider::generate_root_certificate(const std::string& name)
|
|
{
|
|
Certificate root_certificate;
|
|
|
|
//section 7.2.3 in TS 103 097 v2.1.1
|
|
root_certificate->issuer.present = Vanetza_Security_IssuerIdentifier_PR_self;
|
|
root_certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_name;
|
|
OCTET_STRING_fromBuf(&root_certificate->toBeSigned.id.choice.name, name.data(), name.size());
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
static const std::array<char, 3> craciId = { 0, 0, 0 };
|
|
OCTET_STRING_fromBuf(&root_certificate->toBeSigned.cracaId, craciId.data(), craciId.size());
|
|
root_certificate->version = 3;
|
|
root_certificate->toBeSigned.crlSeries = 0;
|
|
|
|
// section 7.2.3 in TS 103 097 v2.1.1
|
|
root_certificate.add_app_permission(aid::CRL, ByteBuffer({0x01}));
|
|
root_certificate.add_app_permission(aid::CTL, ByteBuffer({0x018}));
|
|
|
|
// section 7.2.3 in TS 103 097 v2.1.1
|
|
// certIssuePermissions shall be used to indicate issuing permissions
|
|
// See https://cpoc.jrc.ec.europa.eu/data/documents/e01941_CPOC_Protocol_v3.0_20240206.pdf for detailled cert_permissions
|
|
// I.3.8. certIssuePermissions with predefined values
|
|
auto cert_permission = asn1::allocate<asn1::PsidGroupPermissions>();
|
|
cert_permission->subjectPermissions.present = Vanetza_Security_SubjectPermissions_PR_explicit;
|
|
add_psid_group_permission(cert_permission, aid::SCR, {0x01, 0x3e}, {0xff, 0xc1});
|
|
root_certificate.add_cert_issue_permission(cert_permission);
|
|
|
|
auto cert_permission_message = asn1::allocate<asn1::PsidGroupPermissions>();
|
|
cert_permission_message->subjectPermissions.present = Vanetza_Security_SubjectPermissions_PR_explicit;
|
|
add_psid_group_permission(cert_permission_message,aid::CA, {0x01, 0xff, 0xfc}, {0xff, 0x00, 0x03});
|
|
add_psid_group_permission(cert_permission_message,aid::DEN, {0x01, 0xff, 0xff, 0xff}, {0xff, 0x00, 0x00, 0x00});
|
|
add_psid_group_permission(cert_permission_message,aid::TLM, {0x01, 0xe0}, {0xff, 0x1f});
|
|
add_psid_group_permission(cert_permission_message,aid::RLT, {0x01, 0xc0}, {0xff,0x3f});
|
|
add_psid_group_permission(cert_permission_message,aid::IVI, {0x01, 0xff, 0xff,0xff,0xff,0xf8}, {0xff, 0x00, 0x00, 0x00, 0x00, 0x07});
|
|
add_psid_group_permission(cert_permission_message,aid::TLC_R, {0x02, 0xff, 0xff,0xe0}, {0xff, 0x00, 0x00, 0x1f});
|
|
add_psid_group_permission(cert_permission_message,aid::GN_MGMT, {0x00}, {0xff});
|
|
root_certificate.add_cert_issue_permission(cert_permission_message);
|
|
|
|
// section 6 in TS 103 097 v2.1.1
|
|
// set subject attributes
|
|
// set the verification_key
|
|
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;
|
|
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;
|
|
|
|
sign_authorization_ticket(root_certificate);
|
|
|
|
return root_certificate;
|
|
}
|
|
|
|
} // namespace v3
|
|
} // namespace security
|
|
} // namespace vanetza
|