#include "show-certificate.hpp" #include #include #include #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; using namespace vanetza; using namespace vanetza::security; namespace { std::string hex_buffer(const ByteBuffer& buffer) { std::string bytes(buffer.begin(), buffer.end()); return boost::algorithm::hex(bytes); } void print_ecc_point(const EccPoint& point, const std::string& indent) { switch (v2::get_type(point)) { case v2::EccPointType::X_Coordinate_Only: std::cout << indent << "X: " << hex_buffer(boost::get(point).x) << " (x-coordinate only)" << std::endl; break; case v2::EccPointType::Compressed_Lsb_Y_0: std::cout << indent << "X: " << hex_buffer(boost::get(point).x) << " (compressed, y LSB 0)" << std::endl; break; case v2::EccPointType::Compressed_Lsb_Y_1: std::cout << indent << "X: " << hex_buffer(boost::get(point).x) << " (compressed, y LSB 1)" << std::endl; break; case v2::EccPointType::Uncompressed: { const Uncompressed& uncompressed = boost::get(point); std::cout << indent << "X: " << hex_buffer(uncompressed.x) << std::endl; std::cout << indent << "Y: " << hex_buffer(uncompressed.y) << " (uncompressed)" << std::endl; break; } } } void print_public_key(const v2::PublicKey& key, const std::string& indent) { switch (v2::get_type(key)) { case v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256: std::cout << indent << "Algorithm: ECDSA NISTP256 with SHA-256" << std::endl; print_ecc_point(boost::get(key).public_key, indent); break; case v2::PublicKeyAlgorithm::ECIES_NISTP256: std::cout << indent << "Algorithm: ECIES NISTP256" << std::endl; print_ecc_point(boost::get(key).public_key, indent); break; } } } // namespace bool ShowCertificateCommand::parse(const std::vector& opts) { po::options_description desc("Available options"); desc.add_options() ("help", "Print out available options.") ("certificate", po::value(&certificate_path)->required(), "Certificate to show.") ; po::positional_options_description pos; pos.add("certificate", 1); po::variables_map vm; po::store(po::command_line_parser(opts).options(desc).positional(pos).run(), vm); if (vm.count("help")) { std::cerr << desc << std::endl; return false; } try { po::notify(vm); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl << std::endl << desc << std::endl; return false; } return true; } int ShowCertificateCommand::execute() { v2::Certificate cert = v2::load_certificate_from_file(certificate_path); // subject info std::cout << "Subject: "; if (cert.subject_info.subject_type == v2::SubjectType::Enrollment_Credential) { std::cout << "Enrollment Credential"; } else if (cert.subject_info.subject_type == v2::SubjectType::Authorization_Ticket) { std::cout << "Authorization Ticket"; } else if (cert.subject_info.subject_type == v2::SubjectType::Authorization_Authority) { std::cout << "Authorization Authority"; } else if (cert.subject_info.subject_type == v2::SubjectType::Enrollment_Authority) { std::cout << "Enrollment Authority"; } else if (cert.subject_info.subject_type == v2::SubjectType::Root_CA) { std::cout << "Root Authority"; } else if (cert.subject_info.subject_type == v2::SubjectType::CRL_Signer) { std::cout << "CRL Signer"; } if (cert.subject_info.subject_name.size() > 0) { std::string subject_name(reinterpret_cast(&cert.subject_info.subject_name[0]), cert.subject_info.subject_name.size()); std::cout << " (" << subject_name << ")"; } std::cout << std::endl; { HashedId8 cert_id = calculate_hash(cert); std::string cert_id_string(reinterpret_cast(&cert_id[0]), cert_id.size()); std::cout << "Digest: " << boost::algorithm::hex(cert_id_string) << " (SHA-256)" << std::endl; } // signer info std::cout << "Signer: "; v2::SignerInfoType signer_type = get_type(cert.signer_info); if (signer_type == v2::SignerInfoType::Self) { std::cout << "Self-Signed"; } else if (signer_type == v2::SignerInfoType::Certificate_Digest_With_SHA256) { HashedId8 signer = boost::get(cert.signer_info); std::string signer_id(reinterpret_cast(&signer[0]), signer.size()); std::cout << boost::algorithm::hex(signer_id) << " (SHA-256)"; } else { std::cout << "Unknown (" << static_cast(signer_type) << ")"; } std::cout << std::endl; // subject attributes std::cout << std::endl; unsigned certificate_application_ids = 0; for (auto& subject_attr : cert.subject_attributes) { v2::SubjectAttributeType attr_type = get_type(subject_attr); if (attr_type == v2::SubjectAttributeType::Verification_Key) { std::cout << "Verification Key:" << std::endl; print_public_key(boost::get(subject_attr).key, " - "); std::cout << std::endl; } else if (attr_type == v2::SubjectAttributeType::Encryption_Key) { std::cout << "Encryption Key:" << std::endl; print_public_key(boost::get(subject_attr).key, " - "); std::cout << std::endl; } else if (attr_type == v2::SubjectAttributeType::Reconstruction_Value) { std::cout << "Reconstruction Value:" << std::endl; print_ecc_point(boost::get(subject_attr), " - "); std::cout << std::endl; } else if (attr_type == v2::SubjectAttributeType::Assurance_Level) { v2::SubjectAssurance assurance = boost::get(subject_attr); std::cout << "Assurance: " << (assurance.raw & assurance.assurance_mask); std::cout << " with a confidence of " << (assurance.raw & assurance.confidence_mask); std::cout << std::endl << std::endl; } else if (attr_type == v2::SubjectAttributeType::ITS_AID_List) { std::list its_application_ids = boost::get>(subject_attr); std::cout << "ITS Application IDs:" << std::endl; if (its_application_ids.size() == 0) { std::cout << "None"; } else { for (auto& its_application_id : its_application_ids) { certificate_application_ids++; std::cout << " - "; if (its_application_id == aid::CA) { std::cout << "36 (CA-Basic service)"; } else if (its_application_id == aid::DEN) { std::cout << "37 (DEN-Basic service)"; } else { std::cout << its_application_id.get(); } std::cout << std::endl; } } std::cout << std::endl; } else if (attr_type == v2::SubjectAttributeType::ITS_AID_SSP_List) { std::list its_service_specific_permissions = boost::get>(subject_attr); for (auto& its_ssp : its_service_specific_permissions) { if (its_ssp.its_aid == aid::CA) { std::cout << "CA - ITS Service Specific Permissions:" << std::endl; ByteBuffer& ssp = its_ssp.service_specific_permissions; if (ssp.size() == 0) { std::cerr << "Invalid service specific permissions for CA" << std::endl; continue; } // See final draft ETSI EN 302 637-2 V1.3.1 (2014-09) if (ssp[0] == 0) { if (ssp.size() != 1) { std::cout << " - Warning: Length of SSP is expected to be 1, but was " << ssp.size() << std::endl; } else { std::cout << " - No version, shall be used only for testing." << std::endl; } } else if (ssp[0] == 1) { if (ssp.size() != 3) { std::cout << " - Warning: Length of SSP is expected to be 3, but was " << ssp.size() << std::endl; } else { CamPermissions ssp_decoded = CamPermissions::decode(ssp); for (auto permission : ssp_decoded.permissions()) { std::cout << " - " << stringify(permission) << "\n"; } } } else { std::cout << " - Reserved for future usage and not implemented." << std::endl; } std::cout << std::endl; } } } } if (certificate_application_ids == 0) { std::cout << "Warning: Certificate doesn't contain any application IDs." << std::endl << std::endl; } // validity restrictions const boost::posix_time::ptime epoch { boost::gregorian::date(2004, 1, 1), boost::posix_time::milliseconds(0) }; unsigned certificate_time_constraints = 0; for (auto& validity_restriction : cert.validity_restriction) { v2::ValidityRestrictionType restriction_type = get_type(validity_restriction); if (restriction_type == v2::ValidityRestrictionType::Time_End) { certificate_time_constraints++; boost::posix_time::ptime time_end = epoch + boost::posix_time::seconds(boost::get(validity_restriction)); std::cout << "Validity ends " << time_end << std::endl; } else if (restriction_type == v2::ValidityRestrictionType::Time_Start_And_End) { certificate_time_constraints++; v2::StartAndEndValidity start_and_end = boost::get(validity_restriction); boost::posix_time::ptime time_start = epoch + boost::posix_time::seconds(start_and_end.start_validity); boost::posix_time::ptime time_end = epoch + boost::posix_time::seconds(start_and_end.end_validity); std::cout << "Validity starts " << time_start << " and ends " << time_end << std::endl; } else if (restriction_type == v2::ValidityRestrictionType::Time_Start_And_Duration) { certificate_time_constraints++; v2::StartAndDurationValidity start_and_duration = boost::get(validity_restriction); boost::posix_time::ptime time_start = epoch + boost::posix_time::seconds(start_and_duration.start_validity); boost::posix_time::ptime time_end = epoch + boost::posix_time::seconds(start_and_duration.duration.to_seconds().count()); std::cout << "Validity starts " << time_start << " and ends " << time_end << std::endl; } } if (certificate_time_constraints == 0) { std::cout << "Warning: Certificate doesn't have any time based validity restriction." << std::endl; } else if (certificate_time_constraints > 1) { std::cout << "Warning: Certificate has multiple time based validity restrictions." << std::endl; } std::cout << std::endl; bool certificate_region_constraints = false; for (auto& validity_restriction : cert.validity_restriction) { v2::ValidityRestrictionType restriction_type = get_type(validity_restriction); if (restriction_type == v2::ValidityRestrictionType::Region) { certificate_region_constraints = true; v2::GeographicRegion region = boost::get(validity_restriction); std::cout << "This certificate is regionally restricted by "; v2::RegionType region_type = get_type(region); if (region_type == v2::RegionType::None) { std::cout << "nothing"; } else if (region_type == v2::RegionType::Circle) { std::cout << "a circle"; } else if (region_type == v2::RegionType::Rectangle) { std::cout << "a set of rectangles"; } else if (region_type == v2::RegionType::Polygon) { std::cout << "a polygon"; } else if (region_type == v2::RegionType::ID) { std::cout << "an identified region"; } std::cout << "." << std::endl; } } if (!certificate_region_constraints) { std::cout << "This certificate doesn't have any regional restriction." << std::endl; } return 0; }