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.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
if(NOT TARGET Boost::program_options)
|
||||
message(STATUS "Skip build of certify-pqc because Boost::program_options is unavailable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET CryptoPP::CryptoPP)
|
||||
message(STATUS "Skip build of certify-pqc because CryptoPP is unavailable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_executable(certify-pqc
|
||||
certificate_builder.cpp
|
||||
files.cpp
|
||||
main.cpp
|
||||
)
|
||||
target_include_directories(certify-pqc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(certify-pqc PRIVATE Boost::program_options vanetza)
|
||||
install(TARGETS certify-pqc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(BUILD_TESTS AND TARGET certify)
|
||||
add_test(NAME CertifyPqcWorkflow
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCERTIFY=$<TARGET_FILE:certify>
|
||||
-DCERTIFY_PQC=$<TARGET_FILE:certify-pqc>
|
||||
-DWORK_DIRECTORY=${PROJECT_BINARY_DIR}/tests/certify-pqc-workflow
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/test_workflow.cmake)
|
||||
endif()
|
||||
@@ -0,0 +1,337 @@
|
||||
#include "certificate_builder.hpp"
|
||||
#include <vanetza/asn1/security_profile.hpp>
|
||||
#include VANETZA_ASN1_SECURITY_HEADER(Certificate.h)
|
||||
#include VANETZA_ASN1_SECURITY_HEADER(PsidGroupPermissions.h)
|
||||
#include <vanetza/security/backend.hpp>
|
||||
#include <vanetza/security/ecc_point.hpp>
|
||||
#include <vanetza/security/pqc/hybrid_certificate.hpp>
|
||||
#include <vanetza/security/private_key.hpp>
|
||||
#include <vanetza/security/v2/basic_elements.hpp>
|
||||
#include <vanetza/security/v3/asn1_conversions.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace security
|
||||
{
|
||||
namespace pqc
|
||||
{
|
||||
namespace certificate_builder
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void assign_octets(OCTET_STRING_t& destination, const void* source, std::size_t size)
|
||||
{
|
||||
if (size > static_cast<std::size_t>(std::numeric_limits<int>::max()) ||
|
||||
OCTET_STRING_fromBuf(
|
||||
&destination, static_cast<const char*>(source), static_cast<int>(size)) != 0) {
|
||||
throw std::runtime_error("cannot allocate ASN.1 octet string");
|
||||
}
|
||||
}
|
||||
|
||||
::vanetza::security::PrivateKey convert_private_key(const ecdsa256::PrivateKey& input)
|
||||
{
|
||||
::vanetza::security::PrivateKey output;
|
||||
output.type = KeyType::NistP256;
|
||||
output.key.assign(input.key.begin(), input.key.end());
|
||||
return output;
|
||||
}
|
||||
|
||||
void set_verification_key(v3::Certificate& certificate, const ecdsa256::PublicKey& public_key)
|
||||
{
|
||||
auto& indicator = certificate->toBeSigned.verifyKeyIndicator;
|
||||
indicator.present = Vanetza_Security_VerificationKeyIndicator_PR_verificationKey;
|
||||
auto& verification_key = indicator.choice.verificationKey;
|
||||
verification_key.present = Vanetza_Security_PublicVerificationKey_PR_ecdsaNistP256;
|
||||
verification_key.choice.ecdsaNistP256 =
|
||||
v3::to_asn1(compress_public_key(public_key));
|
||||
}
|
||||
|
||||
void set_encryption_key(v3::Certificate& certificate, const ecdsa256::PublicKey& public_key)
|
||||
{
|
||||
certificate->toBeSigned.encryptionKey =
|
||||
vanetza::asn1::allocate<v3::asn1::PublicEncryptionKey>();
|
||||
auto& encryption_key = certificate->toBeSigned.encryptionKey->publicKey;
|
||||
encryption_key.present = Vanetza_Security_BasePublicEncryptionKey_PR_eciesNistP256;
|
||||
encryption_key.choice.eciesNistP256 =
|
||||
v3::to_asn1(compress_public_key(public_key));
|
||||
}
|
||||
|
||||
void set_issuer(v3::Certificate& certificate, const v3::Certificate* issuer)
|
||||
{
|
||||
if (!issuer) {
|
||||
certificate->issuer.present = Vanetza_Security_IssuerIdentifier_PR_self;
|
||||
certificate->issuer.choice.self = Vanetza_Security_HashAlgorithm_sha256;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto digest = issuer->calculate_digest();
|
||||
if (!digest) {
|
||||
throw std::invalid_argument("issuer certificate has no canonical digest");
|
||||
}
|
||||
certificate->issuer.present = Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest;
|
||||
assign_octets(
|
||||
certificate->issuer.choice.sha256AndDigest,
|
||||
digest->data(), digest->size());
|
||||
}
|
||||
|
||||
void set_subject_name(v3::Certificate& certificate, const std::string& name)
|
||||
{
|
||||
if (name.empty()) {
|
||||
throw std::invalid_argument("CA subject name must not be empty");
|
||||
}
|
||||
certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_name;
|
||||
assign_octets(certificate->toBeSigned.id.choice.name, name.data(), name.size());
|
||||
}
|
||||
|
||||
void set_validity(
|
||||
v3::Certificate& certificate, Clock::time_point now, int validity_days)
|
||||
{
|
||||
constexpr int maximum_days = std::numeric_limits<unsigned short>::max() / 24;
|
||||
if (validity_days < 1 || validity_days > maximum_days) {
|
||||
throw std::invalid_argument("validity must be between 1 and 2730 days");
|
||||
}
|
||||
|
||||
certificate->toBeSigned.validityPeriod.start =
|
||||
v2::convert_time32(now - std::chrono::hours(1));
|
||||
certificate->toBeSigned.validityPeriod.duration.present =
|
||||
Vanetza_Security_Duration_PR_hours;
|
||||
certificate->toBeSigned.validityPeriod.duration.choice.hours = validity_days * 24;
|
||||
}
|
||||
|
||||
void initialize_certificate(
|
||||
v3::Certificate& certificate, const ecdsa256::PublicKey& subject_key,
|
||||
const v3::Certificate* issuer, int validity_days, Clock::time_point now)
|
||||
{
|
||||
certificate->version = 3;
|
||||
certificate->type = Vanetza_Security_CertificateType_explicit;
|
||||
set_issuer(certificate, issuer);
|
||||
|
||||
static const std::array<char, 3> craca_id {{ 0, 0, 0 }};
|
||||
assign_octets(certificate->toBeSigned.cracaId, craca_id.data(), craca_id.size());
|
||||
certificate->toBeSigned.crlSeries = 0;
|
||||
set_validity(certificate, now, validity_days);
|
||||
set_verification_key(certificate, subject_key);
|
||||
}
|
||||
|
||||
std::vector<ItsAid> effective_issue_aids(const std::vector<ItsAid>& aids)
|
||||
{
|
||||
if (!aids.empty()) {
|
||||
return aids;
|
||||
}
|
||||
return { aid::CA, aid::DEN, aid::CP, aid::GN_MGMT, aid::IPV6_ROUTING };
|
||||
}
|
||||
|
||||
std::vector<ItsAid> effective_application_aids(const std::vector<ItsAid>& aids)
|
||||
{
|
||||
return aids.empty() ? std::vector<ItsAid> { aid::CA, aid::DEN } : aids;
|
||||
}
|
||||
|
||||
void add_issue_permission_for_aid(
|
||||
v3::asn1::PsidGroupPermissions* group, ItsAid application_id)
|
||||
{
|
||||
switch (application_id) {
|
||||
case aid::CA:
|
||||
v3::add_psid_group_permission(
|
||||
group, application_id, { 0x01, 0xff, 0xfc }, { 0xff, 0x00, 0x03 });
|
||||
break;
|
||||
case aid::DEN:
|
||||
v3::add_psid_group_permission(
|
||||
group, application_id,
|
||||
{ 0x01, 0xff, 0xff, 0xff }, { 0xff, 0x00, 0x00, 0x00 });
|
||||
break;
|
||||
case aid::CP:
|
||||
v3::add_psid_group_permission(group, application_id, { 0x00 }, { 0xff });
|
||||
break;
|
||||
case aid::TLM:
|
||||
v3::add_psid_group_permission(group, application_id, { 0x01, 0xe0 }, { 0xff, 0x1f });
|
||||
break;
|
||||
case aid::RLT:
|
||||
v3::add_psid_group_permission(group, application_id, { 0x01, 0xc0 }, { 0xff, 0x3f });
|
||||
break;
|
||||
case aid::IVI:
|
||||
v3::add_psid_group_permission(
|
||||
group, application_id,
|
||||
{ 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8 },
|
||||
{ 0xff, 0x00, 0x00, 0x00, 0x00, 0x07 });
|
||||
break;
|
||||
case aid::TLC_R:
|
||||
v3::add_psid_group_permission(
|
||||
group, application_id,
|
||||
{ 0x02, 0xff, 0xff, 0xe0 }, { 0xff, 0x00, 0x00, 0x1f });
|
||||
break;
|
||||
case aid::GN_MGMT:
|
||||
case aid::IPV6_ROUTING:
|
||||
default:
|
||||
v3::add_psid_group_permission(group, application_id, { 0x00 }, { 0xff });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void add_issue_permissions(v3::Certificate& certificate, const std::vector<ItsAid>& aids)
|
||||
{
|
||||
auto* group = vanetza::asn1::allocate<v3::asn1::PsidGroupPermissions>();
|
||||
group->subjectPermissions.present = Vanetza_Security_SubjectPermissions_PR_explicit;
|
||||
for (ItsAid application_id : effective_issue_aids(aids)) {
|
||||
add_issue_permission_for_aid(group, application_id);
|
||||
}
|
||||
certificate.add_cert_issue_permission(group);
|
||||
}
|
||||
|
||||
void add_application_permissions(v3::Certificate& certificate, const std::vector<ItsAid>& aids)
|
||||
{
|
||||
for (ItsAid application_id : effective_application_aids(aids)) {
|
||||
if (application_id == aid::CA) {
|
||||
certificate.add_app_permission(application_id, ByteBuffer { 1, 0, 0 });
|
||||
} else if (application_id == aid::DEN) {
|
||||
certificate.add_app_permission(application_id, ByteBuffer { 1, 0, 0, 0 });
|
||||
} else {
|
||||
certificate.add_app_permission(application_id, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v3::Certificate canonical_certificate(v3::Certificate certificate)
|
||||
{
|
||||
auto canonical = certificate.canonicalize();
|
||||
if (!canonical) {
|
||||
throw std::runtime_error("generated certificate cannot be canonicalized");
|
||||
}
|
||||
std::string error;
|
||||
if (!canonical->validate(error)) {
|
||||
throw std::runtime_error("generated certificate violates ASN.1 constraints: " + error);
|
||||
}
|
||||
return std::move(*canonical);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
v3::Certificate build_ecc_root_certificate(
|
||||
::vanetza::security::Backend& ecc_backend, const ecdsa256::KeyPair& subject_key,
|
||||
const CertificateParameters& parameters, Clock::time_point now)
|
||||
{
|
||||
v3::Certificate certificate;
|
||||
initialize_certificate(
|
||||
certificate, subject_key.public_key, nullptr, parameters.validity_days, now);
|
||||
set_subject_name(certificate, parameters.subject_name);
|
||||
add_issue_permissions(certificate, parameters.application_ids);
|
||||
certificate.add_app_permission(aid::CRL, ByteBuffer { 1 });
|
||||
certificate.add_app_permission(aid::CTL, ByteBuffer { 0x18 });
|
||||
|
||||
sign_primary_certificate(
|
||||
certificate, nullptr, ecc_backend, convert_private_key(subject_key.private_key));
|
||||
return canonical_certificate(std::move(certificate));
|
||||
}
|
||||
|
||||
v3::Certificate build_hybrid_root_certificate(
|
||||
::vanetza::security::Backend& ecc_backend, Backend& pqc_backend,
|
||||
const ecdsa256::KeyPair& subject_key, const KeyPair& subject_pqc_key,
|
||||
const CertificateParameters& parameters, Clock::time_point now)
|
||||
{
|
||||
v3::Certificate certificate;
|
||||
initialize_certificate(
|
||||
certificate, subject_key.public_key, nullptr, parameters.validity_days, now);
|
||||
set_subject_name(certificate, parameters.subject_name);
|
||||
add_issue_permissions(certificate, parameters.application_ids);
|
||||
certificate.add_app_permission(aid::CRL, ByteBuffer { 1 });
|
||||
certificate.add_app_permission(aid::CTL, ByteBuffer { 0x18 });
|
||||
|
||||
set_alternative_public_key(certificate, subject_pqc_key.public_key);
|
||||
sign_alternative_certificate(
|
||||
certificate, nullptr, ecc_backend, pqc_backend, subject_pqc_key.private_key);
|
||||
sign_primary_certificate(
|
||||
certificate, nullptr, ecc_backend, convert_private_key(subject_key.private_key));
|
||||
return canonical_certificate(std::move(certificate));
|
||||
}
|
||||
|
||||
v3::Certificate build_ecc_authorization_authority_certificate(
|
||||
::vanetza::security::Backend& ecc_backend, const ecdsa256::KeyPair& issuer_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key,
|
||||
const CertificateParameters& parameters, Clock::time_point now)
|
||||
{
|
||||
v3::Certificate certificate;
|
||||
initialize_certificate(
|
||||
certificate, subject_key, &issuer_certificate, parameters.validity_days, now);
|
||||
set_subject_name(certificate, parameters.subject_name);
|
||||
add_issue_permissions(certificate, parameters.application_ids);
|
||||
set_encryption_key(certificate, subject_key);
|
||||
|
||||
sign_primary_certificate(
|
||||
certificate, &issuer_certificate, ecc_backend,
|
||||
convert_private_key(issuer_key.private_key));
|
||||
return canonical_certificate(std::move(certificate));
|
||||
}
|
||||
|
||||
v3::Certificate build_hybrid_authorization_authority_certificate(
|
||||
::vanetza::security::Backend& ecc_backend, Backend& pqc_backend,
|
||||
const ecdsa256::KeyPair& issuer_key, const PrivateKey& issuer_pqc_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key, const PublicKey& subject_pqc_key,
|
||||
const CertificateParameters& parameters, Clock::time_point now)
|
||||
{
|
||||
v3::Certificate certificate;
|
||||
initialize_certificate(
|
||||
certificate, subject_key, &issuer_certificate, parameters.validity_days, now);
|
||||
set_subject_name(certificate, parameters.subject_name);
|
||||
add_issue_permissions(certificate, parameters.application_ids);
|
||||
set_encryption_key(certificate, subject_key);
|
||||
|
||||
set_alternative_public_key(certificate, subject_pqc_key);
|
||||
sign_alternative_certificate(
|
||||
certificate, &issuer_certificate, ecc_backend, pqc_backend, issuer_pqc_key);
|
||||
sign_primary_certificate(
|
||||
certificate, &issuer_certificate, ecc_backend,
|
||||
convert_private_key(issuer_key.private_key));
|
||||
return canonical_certificate(std::move(certificate));
|
||||
}
|
||||
|
||||
v3::Certificate build_ecc_authorization_ticket(
|
||||
::vanetza::security::Backend& ecc_backend, const ecdsa256::KeyPair& issuer_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key,
|
||||
const CertificateParameters& parameters, Clock::time_point now)
|
||||
{
|
||||
v3::Certificate certificate;
|
||||
initialize_certificate(
|
||||
certificate, subject_key, &issuer_certificate, parameters.validity_days, now);
|
||||
certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none;
|
||||
add_application_permissions(certificate, parameters.application_ids);
|
||||
|
||||
sign_primary_certificate(
|
||||
certificate, &issuer_certificate, ecc_backend,
|
||||
convert_private_key(issuer_key.private_key));
|
||||
return canonical_certificate(std::move(certificate));
|
||||
}
|
||||
|
||||
v3::Certificate build_hybrid_authorization_ticket(
|
||||
::vanetza::security::Backend& ecc_backend, Backend& pqc_backend,
|
||||
const ecdsa256::KeyPair& issuer_key, const PrivateKey& issuer_pqc_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key,
|
||||
const CertificateParameters& parameters, Clock::time_point now)
|
||||
{
|
||||
v3::Certificate certificate;
|
||||
initialize_certificate(
|
||||
certificate, subject_key, &issuer_certificate, parameters.validity_days, now);
|
||||
certificate->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none;
|
||||
add_application_permissions(certificate, parameters.application_ids);
|
||||
|
||||
sign_alternative_certificate(
|
||||
certificate, &issuer_certificate, ecc_backend, pqc_backend, issuer_pqc_key);
|
||||
sign_primary_certificate(
|
||||
certificate, &issuer_certificate, ecc_backend,
|
||||
convert_private_key(issuer_key.private_key));
|
||||
return canonical_certificate(std::move(certificate));
|
||||
}
|
||||
|
||||
} // namespace certificate_builder
|
||||
} // namespace pqc
|
||||
} // namespace security
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/common/its_aid.hpp>
|
||||
#include <vanetza/security/ecdsa256.hpp>
|
||||
#include <vanetza/security/pqc/fndsa512.hpp>
|
||||
#include <vanetza/security/v3/certificate.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace security
|
||||
{
|
||||
|
||||
class Backend;
|
||||
|
||||
namespace pqc
|
||||
{
|
||||
namespace certificate_builder
|
||||
{
|
||||
|
||||
struct CertificateParameters
|
||||
{
|
||||
std::string subject_name;
|
||||
int validity_days = 1;
|
||||
std::vector<ItsAid> application_ids;
|
||||
};
|
||||
|
||||
v3::Certificate build_ecc_root_certificate(
|
||||
::vanetza::security::Backend&, const ecdsa256::KeyPair&,
|
||||
const CertificateParameters&, Clock::time_point now);
|
||||
|
||||
v3::Certificate build_hybrid_root_certificate(
|
||||
::vanetza::security::Backend&, Backend&, const ecdsa256::KeyPair&, const KeyPair&,
|
||||
const CertificateParameters&, Clock::time_point now);
|
||||
|
||||
v3::Certificate build_ecc_authorization_authority_certificate(
|
||||
::vanetza::security::Backend&, const ecdsa256::KeyPair& issuer_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key,
|
||||
const CertificateParameters&, Clock::time_point now);
|
||||
|
||||
v3::Certificate build_hybrid_authorization_authority_certificate(
|
||||
::vanetza::security::Backend&, Backend&,
|
||||
const ecdsa256::KeyPair& issuer_key, const PrivateKey& issuer_pqc_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key, const PublicKey& subject_pqc_key,
|
||||
const CertificateParameters&, Clock::time_point now);
|
||||
|
||||
v3::Certificate build_ecc_authorization_ticket(
|
||||
::vanetza::security::Backend&, const ecdsa256::KeyPair& issuer_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key,
|
||||
const CertificateParameters&, Clock::time_point now);
|
||||
|
||||
v3::Certificate build_hybrid_authorization_ticket(
|
||||
::vanetza::security::Backend&, Backend&,
|
||||
const ecdsa256::KeyPair& issuer_key, const PrivateKey& issuer_pqc_key,
|
||||
const v3::Certificate& issuer_certificate,
|
||||
const ecdsa256::PublicKey& subject_key,
|
||||
const CertificateParameters&, Clock::time_point now);
|
||||
|
||||
} // namespace certificate_builder
|
||||
} // namespace pqc
|
||||
} // namespace security
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "files.hpp"
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/security/v2/persistence.hpp>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using vanetza::ByteBuffer;
|
||||
|
||||
ByteBuffer read_file(const std::string& path)
|
||||
{
|
||||
std::ifstream stream(path, std::ios::binary);
|
||||
if (!stream) {
|
||||
throw std::runtime_error("cannot open file for reading: " + path);
|
||||
}
|
||||
return ByteBuffer(std::istreambuf_iterator<char>(stream), {});
|
||||
}
|
||||
|
||||
void write_file(const std::string& path, const ByteBuffer& data)
|
||||
{
|
||||
std::ofstream stream(path, std::ios::binary | std::ios::trunc);
|
||||
if (!stream || !stream.write(
|
||||
reinterpret_cast<const char*>(data.data()), data.size())) {
|
||||
throw std::runtime_error("cannot write file: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T load_exact(const std::string& path, std::size_t expected_size, const char* description)
|
||||
{
|
||||
T output { read_file(path) };
|
||||
if (output.bytes.size() != expected_size) {
|
||||
throw std::runtime_error(
|
||||
std::string(description) + " has an invalid size in: " + path);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vanetza::security::ecdsa256::KeyPair load_ecc_key_pair(const std::string& path)
|
||||
{
|
||||
return vanetza::security::v2::load_private_key_from_file(path);
|
||||
}
|
||||
|
||||
vanetza::security::pqc::KeyPair load_pqc_key_pair(const std::string& base_path)
|
||||
{
|
||||
using namespace vanetza::security::pqc;
|
||||
KeyPair key_pair;
|
||||
key_pair.private_key = load_exact<PrivateKey>(
|
||||
base_path + ".pqc.key", fndsa512_private_key_size, "FN-DSA-512 private key");
|
||||
key_pair.public_key = load_exact<PublicKey>(
|
||||
base_path + ".pqc.pub", fndsa512_public_key_size, "FN-DSA-512 public key");
|
||||
return key_pair;
|
||||
}
|
||||
|
||||
vanetza::security::pqc::PrivateKey load_pqc_private_key(const std::string& base_path)
|
||||
{
|
||||
using namespace vanetza::security::pqc;
|
||||
return load_exact<PrivateKey>(
|
||||
base_path + ".pqc.key", fndsa512_private_key_size, "FN-DSA-512 private key");
|
||||
}
|
||||
|
||||
void save_pqc_key_pair(
|
||||
const std::string& base_path, const vanetza::security::pqc::KeyPair& key_pair)
|
||||
{
|
||||
using namespace vanetza::security::pqc;
|
||||
if (key_pair.private_key.bytes.size() != fndsa512_private_key_size ||
|
||||
key_pair.public_key.bytes.size() != fndsa512_public_key_size) {
|
||||
throw std::invalid_argument("cannot save malformed FN-DSA-512 key pair");
|
||||
}
|
||||
|
||||
const std::string private_path = base_path + ".pqc.key";
|
||||
write_file(private_path, key_pair.private_key.bytes);
|
||||
if (::chmod(private_path.c_str(), S_IRUSR | S_IWUSR) != 0) {
|
||||
throw std::runtime_error("cannot restrict private-key permissions: " + private_path);
|
||||
}
|
||||
write_file(base_path + ".pqc.pub", key_pair.public_key.bytes);
|
||||
}
|
||||
|
||||
vanetza::security::v3::Certificate load_v3_certificate(const std::string& path)
|
||||
{
|
||||
vanetza::security::v3::Certificate certificate;
|
||||
const auto encoded = read_file(path);
|
||||
if (encoded.empty() || !certificate.decode(encoded)) {
|
||||
throw std::runtime_error("cannot decode V3 certificate: " + path);
|
||||
}
|
||||
return certificate;
|
||||
}
|
||||
|
||||
void save_v3_certificate(
|
||||
const std::string& path, const vanetza::security::v3::Certificate& certificate)
|
||||
{
|
||||
std::string error;
|
||||
if (!certificate.validate(error)) {
|
||||
throw std::runtime_error("refusing to save invalid V3 certificate: " + error);
|
||||
}
|
||||
write_file(path, certificate.encode());
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <vanetza/security/ecdsa256.hpp>
|
||||
#include <vanetza/security/pqc/fndsa512.hpp>
|
||||
#include <vanetza/security/v3/certificate.hpp>
|
||||
#include <string>
|
||||
|
||||
vanetza::security::ecdsa256::KeyPair load_ecc_key_pair(const std::string& path);
|
||||
vanetza::security::pqc::KeyPair load_pqc_key_pair(const std::string& base_path);
|
||||
vanetza::security::pqc::PrivateKey load_pqc_private_key(const std::string& base_path);
|
||||
|
||||
void save_pqc_key_pair(
|
||||
const std::string& base_path, const vanetza::security::pqc::KeyPair&);
|
||||
|
||||
vanetza::security::v3::Certificate load_v3_certificate(const std::string& path);
|
||||
void save_v3_certificate(
|
||||
const std::string& path, const vanetza::security::v3::Certificate&);
|
||||
@@ -0,0 +1,423 @@
|
||||
#include "certificate_builder.hpp"
|
||||
#include "files.hpp"
|
||||
#include <vanetza/common/manual_runtime.hpp>
|
||||
#include <vanetza/security/backend.hpp>
|
||||
#include <vanetza/security/pqc/fndsa512.hpp>
|
||||
#include <vanetza/security/pqc/hybrid_certificate.hpp>
|
||||
#include <vanetza/security/pqc/hybrid_certificate_validator.hpp>
|
||||
#include <vanetza/security/v3/issuer_memory_lookup.hpp>
|
||||
#include <vanetza/security/v3/trust_store.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace vanetza;
|
||||
using namespace vanetza::security;
|
||||
using namespace vanetza::security::v3;
|
||||
namespace builder = vanetza::security::pqc::certificate_builder;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
enum class CertificateProfile
|
||||
{
|
||||
Ecc,
|
||||
Hybrid
|
||||
};
|
||||
|
||||
CertificateProfile parse_profile(const std::string& profile)
|
||||
{
|
||||
if (profile == "ecc") {
|
||||
return CertificateProfile::Ecc;
|
||||
}
|
||||
if (profile == "hybrid") {
|
||||
return CertificateProfile::Hybrid;
|
||||
}
|
||||
throw std::invalid_argument("unknown certificate profile: " + profile);
|
||||
}
|
||||
|
||||
void validate_pqc_argument(
|
||||
CertificateProfile profile, const std::string& value, const char* option)
|
||||
{
|
||||
if (profile == CertificateProfile::Hybrid && value.empty()) {
|
||||
throw std::invalid_argument(std::string(option) + " is required for the hybrid profile");
|
||||
}
|
||||
if (profile == CertificateProfile::Ecc && !value.empty()) {
|
||||
throw std::invalid_argument(std::string(option) + " is not valid for the ECC profile");
|
||||
}
|
||||
}
|
||||
|
||||
Clock::time_point now()
|
||||
{
|
||||
return Clock::at(boost::posix_time::microsec_clock::universal_time());
|
||||
}
|
||||
|
||||
std::vector<ItsAid> convert_aids(const std::vector<unsigned>& input)
|
||||
{
|
||||
return std::vector<ItsAid>(input.begin(), input.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> command_arguments(int argc, const char* argv[])
|
||||
{
|
||||
return std::vector<std::string>(argv + 2, argv + argc);
|
||||
}
|
||||
|
||||
po::variables_map parse(
|
||||
const std::vector<std::string>& arguments, const po::options_description& options,
|
||||
const po::positional_options_description& positional = {})
|
||||
{
|
||||
po::variables_map variables;
|
||||
po::store(po::command_line_parser(arguments)
|
||||
.options(options).positional(positional).run(), variables);
|
||||
if (!variables.count("help")) {
|
||||
po::notify(variables);
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
int generate_key(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::string output;
|
||||
po::options_description options("Generate an FN-DSA-512 key pair");
|
||||
options.add_options()
|
||||
("help,h", "Show this help")
|
||||
("output", po::value<std::string>(&output)->required(),
|
||||
"Output base path; .pqc.key and .pqc.pub are appended");
|
||||
po::positional_options_description positional;
|
||||
positional.add("output", 1);
|
||||
const auto variables = parse(arguments, options, positional);
|
||||
if (variables.count("help")) {
|
||||
std::cout << options << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto backend = pqc::create_fndsa512_backend();
|
||||
save_pqc_key_pair(output, backend->generate_key_pair());
|
||||
std::cout << "Wrote " << output << ".pqc.key and " << output << ".pqc.pub\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
builder::CertificateParameters certificate_parameters(
|
||||
const std::string& name, int days, const std::vector<unsigned>& aids)
|
||||
{
|
||||
builder::CertificateParameters parameters;
|
||||
parameters.subject_name = name;
|
||||
parameters.validity_days = days;
|
||||
parameters.application_ids = convert_aids(aids);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
int generate_root(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::string output;
|
||||
std::string subject_key;
|
||||
std::string subject_pqc_key;
|
||||
std::string profile_name = "hybrid";
|
||||
std::string subject_name = "Hello World Root-CA";
|
||||
int days = 365;
|
||||
std::vector<unsigned> aids;
|
||||
po::options_description options("Generate a V3 Root CA certificate");
|
||||
options.add_options()
|
||||
("help,h", "Show this help")
|
||||
("output,o", po::value<std::string>(&output)->required(), "Output certificate")
|
||||
("subject-key", po::value<std::string>(&subject_key)->required(), "ECC private key")
|
||||
("profile", po::value<std::string>(&profile_name)->default_value("hybrid"),
|
||||
"Certificate profile: ecc or hybrid")
|
||||
("subject-pqc-key", po::value<std::string>(&subject_pqc_key),
|
||||
"FN-DSA key base path")
|
||||
("subject-name", po::value<std::string>(&subject_name), "Certificate subject name")
|
||||
("days", po::value<int>(&days), "Validity in days")
|
||||
("aid", po::value<std::vector<unsigned>>(&aids)->multitoken(), "Permitted ITS-AIDs");
|
||||
const auto variables = parse(arguments, options);
|
||||
if (variables.count("help")) {
|
||||
std::cout << options << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto profile = parse_profile(profile_name);
|
||||
validate_pqc_argument(profile, subject_pqc_key, "--subject-pqc-key");
|
||||
auto ecc_backend = create_backend_or_throw("default");
|
||||
const auto subject_ecc = load_ecc_key_pair(subject_key);
|
||||
Certificate certificate;
|
||||
if (profile == CertificateProfile::Hybrid) {
|
||||
auto pqc_backend = pqc::create_fndsa512_backend();
|
||||
certificate = builder::build_hybrid_root_certificate(
|
||||
*ecc_backend, *pqc_backend, subject_ecc, load_pqc_key_pair(subject_pqc_key),
|
||||
certificate_parameters(subject_name, days, aids), now());
|
||||
} else {
|
||||
certificate = builder::build_ecc_root_certificate(
|
||||
*ecc_backend, subject_ecc,
|
||||
certificate_parameters(subject_name, days, aids), now());
|
||||
}
|
||||
save_v3_certificate(output, certificate);
|
||||
std::cout << "Wrote " << profile_name << " V3 Root CA certificate " << output << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int generate_aa(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::string output;
|
||||
std::string sign_key;
|
||||
std::string sign_cert;
|
||||
std::string sign_pqc_key;
|
||||
std::string subject_key;
|
||||
std::string subject_pqc_key;
|
||||
std::string profile_name = "hybrid";
|
||||
std::string subject_name = "Hello World Auth-CA";
|
||||
int days = 180;
|
||||
std::vector<unsigned> aids;
|
||||
po::options_description options("Generate a V3 Authorization Authority certificate");
|
||||
options.add_options()
|
||||
("help,h", "Show this help")
|
||||
("output,o", po::value<std::string>(&output)->required(), "Output certificate")
|
||||
("sign-key", po::value<std::string>(&sign_key)->required(), "Issuer ECC private key")
|
||||
("sign-cert", po::value<std::string>(&sign_cert)->required(), "Issuer certificate")
|
||||
("profile", po::value<std::string>(&profile_name)->default_value("hybrid"),
|
||||
"Certificate profile: ecc or hybrid")
|
||||
("sign-pqc-key", po::value<std::string>(&sign_pqc_key),
|
||||
"Issuer FN-DSA key base path")
|
||||
("subject-key", po::value<std::string>(&subject_key)->required(), "Subject ECC private key")
|
||||
("subject-pqc-key", po::value<std::string>(&subject_pqc_key),
|
||||
"Subject FN-DSA key base path")
|
||||
("subject-name", po::value<std::string>(&subject_name), "Certificate subject name")
|
||||
("days", po::value<int>(&days), "Validity in days")
|
||||
("aid", po::value<std::vector<unsigned>>(&aids)->multitoken(), "Permitted ITS-AIDs");
|
||||
const auto variables = parse(arguments, options);
|
||||
if (variables.count("help")) {
|
||||
std::cout << options << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto profile = parse_profile(profile_name);
|
||||
validate_pqc_argument(profile, sign_pqc_key, "--sign-pqc-key");
|
||||
validate_pqc_argument(profile, subject_pqc_key, "--subject-pqc-key");
|
||||
auto ecc_backend = create_backend_or_throw("default");
|
||||
const auto subject_ecc = load_ecc_key_pair(subject_key);
|
||||
const auto issuer_ecc = load_ecc_key_pair(sign_key);
|
||||
const auto issuer_certificate = load_v3_certificate(sign_cert);
|
||||
Certificate certificate;
|
||||
if (profile == CertificateProfile::Hybrid) {
|
||||
auto pqc_backend = pqc::create_fndsa512_backend();
|
||||
const auto subject_pqc = load_pqc_key_pair(subject_pqc_key);
|
||||
certificate = builder::build_hybrid_authorization_authority_certificate(
|
||||
*ecc_backend, *pqc_backend, issuer_ecc,
|
||||
load_pqc_private_key(sign_pqc_key), issuer_certificate,
|
||||
subject_ecc.public_key, subject_pqc.public_key,
|
||||
certificate_parameters(subject_name, days, aids), now());
|
||||
} else {
|
||||
certificate = builder::build_ecc_authorization_authority_certificate(
|
||||
*ecc_backend, issuer_ecc, issuer_certificate, subject_ecc.public_key,
|
||||
certificate_parameters(subject_name, days, aids), now());
|
||||
}
|
||||
save_v3_certificate(output, certificate);
|
||||
std::cout << "Wrote " << profile_name
|
||||
<< " V3 Authorization Authority certificate " << output << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int generate_ticket(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::string output;
|
||||
std::string sign_key;
|
||||
std::string sign_cert;
|
||||
std::string sign_pqc_key;
|
||||
std::string subject_key;
|
||||
std::string profile_name = "hybrid";
|
||||
int days = 7;
|
||||
std::vector<unsigned> aids;
|
||||
po::options_description options("Generate a V3 Authorization Ticket");
|
||||
options.add_options()
|
||||
("help,h", "Show this help")
|
||||
("output,o", po::value<std::string>(&output)->required(), "Output certificate")
|
||||
("sign-key", po::value<std::string>(&sign_key)->required(), "Issuer ECC private key")
|
||||
("sign-cert", po::value<std::string>(&sign_cert)->required(), "Issuer certificate")
|
||||
("profile", po::value<std::string>(&profile_name)->default_value("hybrid"),
|
||||
"Certificate profile: ecc or hybrid")
|
||||
("sign-pqc-key", po::value<std::string>(&sign_pqc_key),
|
||||
"Issuer FN-DSA key base path")
|
||||
("subject-key", po::value<std::string>(&subject_key)->required(), "Subject ECC private key")
|
||||
("days", po::value<int>(&days), "Validity in days")
|
||||
("aid", po::value<std::vector<unsigned>>(&aids)->multitoken(), "Permitted ITS-AIDs");
|
||||
const auto variables = parse(arguments, options);
|
||||
if (variables.count("help")) {
|
||||
std::cout << options << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto profile = parse_profile(profile_name);
|
||||
validate_pqc_argument(profile, sign_pqc_key, "--sign-pqc-key");
|
||||
auto ecc_backend = create_backend_or_throw("default");
|
||||
const auto issuer_ecc = load_ecc_key_pair(sign_key);
|
||||
const auto issuer_certificate = load_v3_certificate(sign_cert);
|
||||
const auto subject_ecc = load_ecc_key_pair(subject_key);
|
||||
Certificate certificate;
|
||||
if (profile == CertificateProfile::Hybrid) {
|
||||
auto pqc_backend = pqc::create_fndsa512_backend();
|
||||
certificate = builder::build_hybrid_authorization_ticket(
|
||||
*ecc_backend, *pqc_backend, issuer_ecc,
|
||||
load_pqc_private_key(sign_pqc_key), issuer_certificate,
|
||||
subject_ecc.public_key, certificate_parameters({}, days, aids), now());
|
||||
} else {
|
||||
certificate = builder::build_ecc_authorization_ticket(
|
||||
*ecc_backend, issuer_ecc, issuer_certificate, subject_ecc.public_key,
|
||||
certificate_parameters({}, days, aids), now());
|
||||
}
|
||||
save_v3_certificate(output, certificate);
|
||||
std::cout << "Wrote " << profile_name << " V3 Authorization Ticket " << output << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* material_state(pqc::MaterialState state)
|
||||
{
|
||||
switch (state) {
|
||||
case pqc::MaterialState::None: return "none";
|
||||
case pqc::MaterialState::Authority: return "authority (key + signature)";
|
||||
case pqc::MaterialState::EndEntity: return "end entity (signature only)";
|
||||
case pqc::MaterialState::Inconsistent: return "inconsistent";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
int show_certificate(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::string input;
|
||||
po::options_description options("Show V3 certificate information");
|
||||
options.add_options()
|
||||
("help,h", "Show this help")
|
||||
("certificate", po::value<std::string>(&input)->required(), "Certificate file");
|
||||
po::positional_options_description positional;
|
||||
positional.add("certificate", 1);
|
||||
const auto variables = parse(arguments, options, positional);
|
||||
if (variables.count("help")) {
|
||||
std::cout << options << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto certificate = load_v3_certificate(input);
|
||||
const auto key = pqc::get_alternative_public_key(certificate);
|
||||
const auto signature = pqc::get_alternative_signature(certificate);
|
||||
std::cout << "Encoded size: " << certificate.encode().size() << " bytes\n"
|
||||
<< "Alternative material: "
|
||||
<< material_state(pqc::alternative_material_state(certificate)) << "\n"
|
||||
<< "FN-DSA-512 public key: " << (key ? key->bytes.size() : 0) << " bytes\n"
|
||||
<< "FN-DSA-512 signature: " << (signature ? signature->bytes.size() : 0)
|
||||
<< " bytes\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verify_chain(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::string root_path;
|
||||
std::string aa_path;
|
||||
std::string ticket_path;
|
||||
std::string profile_name = "hybrid";
|
||||
unsigned application_id = aid::CA;
|
||||
po::options_description options("Verify a V3 Root -> AA -> AT chain");
|
||||
options.add_options()
|
||||
("help,h", "Show this help")
|
||||
("root", po::value<std::string>(&root_path)->required(), "Trusted Root certificate")
|
||||
("aa", po::value<std::string>(&aa_path)->required(), "Authorization Authority certificate")
|
||||
("ticket", po::value<std::string>(&ticket_path)->required(), "Authorization Ticket certificate")
|
||||
("profile", po::value<std::string>(&profile_name)->default_value("hybrid"),
|
||||
"Expected profile: ecc or hybrid")
|
||||
("aid", po::value<unsigned>(&application_id), "ITS-AID to validate (default: 36)");
|
||||
const auto variables = parse(arguments, options);
|
||||
if (variables.count("help")) {
|
||||
std::cout << options << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto root = load_v3_certificate(root_path);
|
||||
const auto aa = load_v3_certificate(aa_path);
|
||||
const auto ticket = load_v3_certificate(ticket_path);
|
||||
const auto profile = parse_profile(profile_name);
|
||||
if (profile == CertificateProfile::Ecc &&
|
||||
(pqc::alternative_material_state(root) != pqc::MaterialState::None ||
|
||||
pqc::alternative_material_state(aa) != pqc::MaterialState::None ||
|
||||
pqc::alternative_material_state(ticket) != pqc::MaterialState::None)) {
|
||||
throw std::invalid_argument("ECC profile chain contains alternative PQC material");
|
||||
}
|
||||
auto ecc_backend = create_backend_or_throw("default");
|
||||
auto pqc_backend = pqc::create_fndsa512_backend();
|
||||
|
||||
TrustStore trust_store;
|
||||
trust_store.insert(root);
|
||||
IssuerMemoryLookup issuer_lookup;
|
||||
if (!issuer_lookup.insert(root) || !issuer_lookup.insert(aa)) {
|
||||
throw std::runtime_error("Root or AA cannot be used as an issuer certificate");
|
||||
}
|
||||
|
||||
ManualRuntime runtime(now());
|
||||
pqc::HybridCertificateValidator validator;
|
||||
validator.use_runtime(&runtime);
|
||||
validator.disable_location_checks(true);
|
||||
validator.use_issuer_lookup(&issuer_lookup);
|
||||
validator.use_trust_store(&trust_store);
|
||||
validator.use_backends(ecc_backend.get(), pqc_backend.get());
|
||||
validator.use_verification_policy(profile == CertificateProfile::Hybrid ?
|
||||
pqc::HybridCertificateValidator::VerificationPolicy::HybridRequired :
|
||||
pqc::HybridCertificateValidator::VerificationPolicy::HybridIfPresent);
|
||||
|
||||
const auto verdict = validator.valid_for_signing(ticket, application_id);
|
||||
if (verdict != CertificateValidator::Verdict::Valid) {
|
||||
std::cerr << profile_name << " chain verification failed (verdict "
|
||||
<< static_cast<int>(verdict) << ")\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << profile_name << " V3 Root -> AA -> AT chain is valid\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_usage(const char* executable)
|
||||
{
|
||||
std::cout << "Usage: " << executable << " COMMAND [OPTIONS]\n\n"
|
||||
<< "Commands:\n"
|
||||
<< " generate-key Generate an FN-DSA-512 key pair\n"
|
||||
<< " generate-root Generate a V3 Root CA\n"
|
||||
<< " generate-aa Generate a V3 Authorization Authority\n"
|
||||
<< " generate-ticket Generate a V3 Authorization Ticket\n"
|
||||
<< " show Show V3 certificate material\n"
|
||||
<< " verify-chain Verify a Root -> AA -> AT chain\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
try {
|
||||
if (argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h") {
|
||||
print_usage(argv[0]);
|
||||
return argc < 2 ? 1 : 0;
|
||||
}
|
||||
|
||||
const std::string command = argv[1];
|
||||
const auto arguments = command_arguments(argc, argv);
|
||||
if (command == "generate-key") {
|
||||
return generate_key(arguments);
|
||||
}
|
||||
if (command == "generate-root") {
|
||||
return generate_root(arguments);
|
||||
}
|
||||
if (command == "generate-aa") {
|
||||
return generate_aa(arguments);
|
||||
}
|
||||
if (command == "generate-ticket") {
|
||||
return generate_ticket(arguments);
|
||||
}
|
||||
if (command == "show") {
|
||||
return show_certificate(arguments);
|
||||
}
|
||||
if (command == "verify-chain") {
|
||||
return verify_chain(arguments);
|
||||
}
|
||||
throw std::invalid_argument("unknown command: " + command);
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << "Error: " << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
if(NOT CERTIFY OR NOT CERTIFY_PQC OR NOT WORK_DIRECTORY)
|
||||
message(FATAL_ERROR "CERTIFY, CERTIFY_PQC and WORK_DIRECTORY are required")
|
||||
endif()
|
||||
|
||||
file(REMOVE_RECURSE "${WORK_DIRECTORY}")
|
||||
file(MAKE_DIRECTORY "${WORK_DIRECTORY}")
|
||||
|
||||
function(run)
|
||||
execute_process(
|
||||
COMMAND ${ARGV}
|
||||
WORKING_DIRECTORY "${WORK_DIRECTORY}"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error)
|
||||
if(NOT result EQUAL 0)
|
||||
string(JOIN " " command ${ARGV})
|
||||
message(FATAL_ERROR "Command failed (${result}): ${command}\n${output}${error}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(run_fails)
|
||||
execute_process(
|
||||
COMMAND ${ARGV}
|
||||
WORKING_DIRECTORY "${WORK_DIRECTORY}"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error)
|
||||
if(result EQUAL 0)
|
||||
string(JOIN " " command ${ARGV})
|
||||
message(FATAL_ERROR "Command unexpectedly succeeded: ${command}\n${output}${error}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(check_show certificate material public_key_size signature_size)
|
||||
execute_process(
|
||||
COMMAND "${CERTIFY_PQC}" show "${certificate}"
|
||||
WORKING_DIRECTORY "${WORK_DIRECTORY}"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error)
|
||||
if(NOT result EQUAL 0)
|
||||
message(FATAL_ERROR "Cannot inspect ${certificate}:\n${output}${error}")
|
||||
endif()
|
||||
|
||||
set(expectations
|
||||
"Alternative material: ${material}"
|
||||
"FN-DSA-512 public key: ${public_key_size} bytes"
|
||||
"FN-DSA-512 signature: ${signature_size} bytes")
|
||||
foreach(expectation IN LISTS expectations)
|
||||
string(FIND "${output}" "${expectation}" position)
|
||||
if(position EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"Unexpected material in ${certificate}: missing '${expectation}'\n${output}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
run("${CERTIFY}" generate-key root.key)
|
||||
run("${CERTIFY}" generate-key aa.key)
|
||||
run("${CERTIFY}" generate-key ticket.key)
|
||||
run("${CERTIFY_PQC}" generate-key root)
|
||||
run("${CERTIFY_PQC}" generate-key aa)
|
||||
|
||||
run("${CERTIFY_PQC}" generate-root
|
||||
--profile ecc --output ecc-root.cert --subject-key root.key --days 365
|
||||
--aid 36 141)
|
||||
run("${CERTIFY_PQC}" generate-aa
|
||||
--profile ecc --output ecc-aa.cert
|
||||
--sign-key root.key --sign-cert ecc-root.cert
|
||||
--subject-key aa.key --days 180
|
||||
--aid 36 141)
|
||||
run("${CERTIFY_PQC}" generate-ticket
|
||||
--profile ecc --output ecc-ticket.cert
|
||||
--sign-key aa.key --sign-cert ecc-aa.cert
|
||||
--subject-key ticket.key --days 7
|
||||
--aid 141)
|
||||
run("${CERTIFY_PQC}" verify-chain --profile ecc
|
||||
--root ecc-root.cert --aa ecc-aa.cert --ticket ecc-ticket.cert --aid 141)
|
||||
check_show(ecc-root.cert "none" 0 0)
|
||||
check_show(ecc-aa.cert "none" 0 0)
|
||||
check_show(ecc-ticket.cert "none" 0 0)
|
||||
|
||||
run("${CERTIFY_PQC}" generate-root
|
||||
--output root.cert --subject-key root.key --subject-pqc-key root --days 365
|
||||
--aid 36 141)
|
||||
run("${CERTIFY_PQC}" generate-aa
|
||||
--output aa.cert
|
||||
--sign-key root.key --sign-cert root.cert --sign-pqc-key root
|
||||
--subject-key aa.key --subject-pqc-key aa --days 180
|
||||
--aid 36 141)
|
||||
run("${CERTIFY_PQC}" generate-ticket
|
||||
--output ticket.cert
|
||||
--sign-key aa.key --sign-cert aa.cert --sign-pqc-key aa
|
||||
--subject-key ticket.key --days 7
|
||||
--aid 141)
|
||||
run("${CERTIFY_PQC}" verify-chain
|
||||
--root root.cert --aa aa.cert --ticket ticket.cert --aid 141)
|
||||
check_show(root.cert "authority (key + signature)" 897 666)
|
||||
check_show(aa.cert "authority (key + signature)" 897 666)
|
||||
check_show(ticket.cert "end entity (signature only)" 0 666)
|
||||
|
||||
run_fails("${CERTIFY_PQC}" verify-chain --profile hybrid
|
||||
--root ecc-root.cert --aa ecc-aa.cert --ticket ecc-ticket.cert --aid 141)
|
||||
run_fails("${CERTIFY_PQC}" verify-chain --profile ecc
|
||||
--root root.cert --aa aa.cert --ticket ticket.cert --aid 141)
|
||||
Reference in New Issue
Block a user