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:
Ashin Walpola
2026-09-24 10:56:05 +02:00
parent 2f60623e18
commit d107534eb2
9781 changed files with 1560475 additions and 17 deletions
@@ -0,0 +1,29 @@
#include <vanetza/security/v2/basic_elements.hpp>
#include <algorithm>
#include <cassert>
#include <chrono>
namespace vanetza
{
namespace security
{
namespace v2
{
Time32 convert_time32(const Clock::time_point& tp)
{
using std::chrono::duration_cast;
using seconds = std::chrono::duration<Time32>;
return duration_cast<seconds>(tp.time_since_epoch()).count();
}
Time64 convert_time64(const Clock::time_point& tp)
{
using std::chrono::duration_cast;
using microseconds = std::chrono::duration<Time64, std::micro>;
return duration_cast<microseconds>(tp.time_since_epoch()).count();
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,43 @@
#ifndef BASIC_ELEMENTS_HPP_RALCTYHI
#define BASIC_ELEMENTS_HPP_RALCTYHI
#include <vanetza/common/clock.hpp>
#include <vanetza/security/hashed_id.hpp>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
using Time64 = uint64_t;
using Time32 = uint32_t;
/// Time64WithStandardDeviation specified in TS 103 097 v1.2.1, section 4.2.16
struct Time64WithStandardDeviation
{
Time64 time64;
uint8_t log_std_dev;
};
/**
* Convert time point to time stamp
* \param tp time point
* \return time stamp with second accuracy
*/
Time32 convert_time32(const Clock::time_point& tp);
/**
* Convert time point to time stamp
* \param tp time point
* \return time stamp with microsecond accuracy
*/
Time64 convert_time64(const Clock::time_point& tp);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* BASIC_ELEMENTS_HPP_RALCTYHI */
@@ -0,0 +1,259 @@
#include <vanetza/common/byte_buffer_sink.hpp>
#include <vanetza/common/serialization_buffer.hpp>
#include <vanetza/security/exception.hpp>
#include <vanetza/security/sha.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/length_coding.hpp>
#include <vanetza/security/v2/signer_info.hpp>
#include <vanetza/security/v2/validity_restriction.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/get.hpp>
#include <boost/variant/static_visitor.hpp>
#include <algorithm>
#include <array>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
size_t get_size(const Certificate& cert)
{
size_t size = sizeof(cert.version());
size += get_size(cert.signer_info);
size += get_size(cert.subject_info);
size += get_size(cert.subject_attributes);
size += length_coding_size(get_size(cert.subject_attributes));
size += get_size(cert.validity_restriction);
size += length_coding_size(get_size(cert.validity_restriction));
size += get_size(cert.signature);
return size;
}
void serialize(OutputArchive& ar, const Certificate& cert)
{
serialize(ar, host_cast(cert.version()));
serialize(ar, cert.signer_info);
serialize(ar, cert.subject_info);
serialize(ar, cert.subject_attributes);
serialize(ar, cert.validity_restriction);
serialize(ar, cert.signature);
}
size_t deserialize(InputArchive& ar, Certificate& cert)
{
uint8_t version = 0;
deserialize(ar, version);
size_t size = sizeof(cert.version());
if (2 == version) {
size += deserialize_certificate_signer(ar, cert.signer_info);
size += deserialize(ar, cert.subject_info);
size += deserialize(ar, cert.subject_attributes);
size += length_coding_size(get_size(cert.subject_attributes));
size += deserialize(ar, cert.validity_restriction);
size += length_coding_size(get_size(cert.validity_restriction));
size += deserialize(ar, cert.signature);
} else {
throw deserialization_error("Unsupported Certificate version");
}
return size;
}
ByteBuffer convert_for_signing(const Certificate& cert)
{
ByteBuffer buf;
byte_buffer_sink sink(buf);
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
OutputArchive ar(stream);
const uint8_t version = cert.version();
ar << version;
serialize(ar, cert.signer_info);
serialize(ar, cert.subject_info);
serialize(ar, cert.subject_attributes);
serialize(ar, cert.validity_restriction);
stream.close();
return buf;
}
void sort(Certificate& cert)
{
cert.subject_attributes.sort([](const SubjectAttribute& a, const SubjectAttribute& b) {
const SubjectAttributeType type_a = get_type(a);
const SubjectAttributeType type_b = get_type(b);
// all fields must be encoded in ascending order
using enum_int = std::underlying_type<SubjectAttributeType>::type;
return static_cast<enum_int>(type_a) < static_cast<enum_int>(type_b);
});
cert.validity_restriction.sort([](const ValidityRestriction& a, const ValidityRestriction& b) {
const ValidityRestrictionType type_a = get_type(a);
const ValidityRestrictionType type_b = get_type(b);
// all fields must be encoded in ascending order
using enum_int = std::underlying_type<ValidityRestrictionType>::type;
return static_cast<enum_int>(type_a) < static_cast<enum_int>(type_b);
});
}
boost::optional<Uncompressed> get_uncompressed_public_key(const Certificate& cert, Backend& backend)
{
boost::optional<Uncompressed> public_key_coordinates;
for (auto& attribute : cert.subject_attributes) {
if (get_type(attribute) == SubjectAttributeType::Verification_Key) {
const VerificationKey& verification_key = boost::get<VerificationKey>(attribute);
const EccPoint& ecc_point = boost::get<ecdsa_nistp256_with_sha256>(verification_key.key).public_key;
public_key_coordinates = backend.decompress_point(ecc_point);
break;
}
}
return public_key_coordinates;
}
boost::optional<ecdsa256::PublicKey> get_public_key(const Certificate& cert, Backend& backend)
{
auto unc = get_uncompressed_public_key(cert, backend);
boost::optional<ecdsa256::PublicKey> result;
ecdsa256::PublicKey pub;
if (unc && unc->x.size() == pub.x.size() && unc->y.size() == pub.y.size()) {
std::copy_n(unc->x.begin(), pub.x.size(), pub.x.data());
std::copy_n(unc->y.begin(), pub.y.size(), pub.y.data());
result = std::move(pub);
}
return result;
}
HashedId8 calculate_hash(const Certificate& cert)
{
Certificate canonical_cert = cert;
// canonical encoding according to TS 103 097 V1.2.1, section 4.2.12
boost::optional<EcdsaSignature> signature = extract_ecdsa_signature(cert.signature);
if (signature) {
struct canonical_visitor : public boost::static_visitor<EccPoint>
{
EccPoint operator()(const X_Coordinate_Only& x_only) const
{
return x_only;
}
EccPoint operator()(const Compressed_Lsb_Y_0& y0) const
{
return X_Coordinate_Only { y0.x };
}
EccPoint operator()(const Compressed_Lsb_Y_1& y1) const
{
return X_Coordinate_Only { y1.x };
}
EccPoint operator()(const Uncompressed& unc) const
{
return X_Coordinate_Only { unc.x };
}
};
EcdsaSignature canonical_sig;
canonical_sig.s = signature->s;
canonical_sig.R = boost::apply_visitor(canonical_visitor(), signature->R);
assert(get_type(canonical_sig.R) == EccPointType::X_Coordinate_Only);
canonical_cert.signature = canonical_sig;
}
ByteBuffer bytes;
serialize_into_buffer(canonical_cert, bytes);
HashedId8 id;
Sha256Digest digest = calculate_sha256_digest(bytes.data(), bytes.size());
assert(digest.size() >= id.size());
std::copy(digest.end() - id.size(), digest.end(), id.begin());
return id;
}
const SubjectAttribute* Certificate::get_attribute(SubjectAttributeType sat) const
{
const SubjectAttribute* match = nullptr;
for (auto& attribute : subject_attributes) {
if (get_type(attribute) == sat) {
match = &attribute;
break;
}
}
return match;
}
const ValidityRestriction* Certificate::get_restriction(ValidityRestrictionType vrt) const
{
const ValidityRestriction* match = nullptr;
for (auto& restriction : validity_restriction) {
if (get_type(restriction) == vrt) {
match = &restriction;
break;
}
}
return match;
}
void Certificate::remove_attribute(SubjectAttributeType type)
{
for (auto it = subject_attributes.begin(); it != subject_attributes.end(); /* noop */) {
if (get_type(*it) == type) {
it = subject_attributes.erase(it);
} else {
++it;
}
}
}
void Certificate::remove_restriction(ValidityRestrictionType type)
{
for (auto it = validity_restriction.begin(); it != validity_restriction.end(); /* noop */) {
if (get_type(*it) == type) {
it = validity_restriction.erase(it);
} else {
++it;
}
}
}
void Certificate::add_permission(ItsAid aid)
{
for (auto& item : subject_attributes) {
if (get_type(item) == SubjectAttributeType::ITS_AID_List) {
auto& aid_list = boost::get<std::list<IntX>>(item);
aid_list.push_back(IntX(aid));
return;
}
}
subject_attributes.push_back(std::list<IntX>({ IntX(aid) }));
}
void Certificate::add_permission(ItsAid aid, const ByteBuffer& ssp)
{
ItsAidSsp permission({ IntX(aid), ssp });
for (auto& item : subject_attributes) {
if (get_type(item) == SubjectAttributeType::ITS_AID_SSP_List) {
auto& aid_ssp_list = boost::get<std::list<ItsAidSsp> >(item);
aid_ssp_list.push_back(permission);
return;
}
}
subject_attributes.push_back(std::list<ItsAidSsp>({ permission }));
}
} // ns v2
} // ns security
} // ns vanetza
@@ -0,0 +1,171 @@
#ifndef CERTIFICATE_HPP_LWBWIAVL
#define CERTIFICATE_HPP_LWBWIAVL
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/ecdsa256.hpp>
#include <vanetza/security/v2/basic_elements.hpp>
#include <vanetza/security/v2/ecc_point.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <vanetza/security/v2/signature.hpp>
#include <vanetza/security/v2/signer_info.hpp>
#include <vanetza/security/v2/subject_attribute.hpp>
#include <vanetza/security/v2/subject_info.hpp>
#include <vanetza/security/v2/validity_restriction.hpp>
#include <boost/optional/optional.hpp>
#include <boost/variant/get.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/// described in TS 103 097 v1.2.1 (2015-06), section 6.1
struct Certificate
{
SignerInfo signer_info;
SubjectInfo subject_info;
std::list<SubjectAttribute> subject_attributes;
std::list<ValidityRestriction> validity_restriction;
Signature signature;
// certificate version is two, for conformance with the present standard
uint8_t version() const { return 2; }
/**
* Get subject attribute of a certain type (if present)
* \param type of subject attribute
*/
const SubjectAttribute* get_attribute(SubjectAttributeType type) const;
/**
* Get validity restriction of a certain type (if present)
* \param type of validity restriction
*/
const ValidityRestriction* get_restriction(ValidityRestrictionType type) const;
/**
* Remove subject attribute of a certain type (if present)
* \param type of subject attribute
*/
void remove_attribute(SubjectAttributeType type);
/**
* Remove validity restriction of a certain type (if present)
* \param type of validity restriction
*/
void remove_restriction(ValidityRestrictionType type);
/**
* Add ITS-AID to certificate's subject attributes
* \param aid ITS-AID
*/
void add_permission(ItsAid aid);
/**
* Add ITS-AID along with SSP to certificate's subject attributes
* \param aid ITS-AID
* \param ssp Service Specific Permissions
*/
void add_permission(ItsAid aid, const ByteBuffer& ssp);
/**
* Get subject attribute by type
* \tparam T subject attribute type
* \return subject attribute, nullptr if not found
*/
template<SubjectAttributeType T>
const subject_attribute_type<T>* get_attribute() const
{
using type = subject_attribute_type<T>;
const SubjectAttribute* field = get_attribute(T);
return boost::get<type>(field);
}
/**
* Get validity restriction by type
* \tparam T validity restriction type
* \return validity restriction, nullptr if not found
*/
template<ValidityRestrictionType T>
const validity_restriction_type<T>* get_restriction() const
{
using type = validity_restriction_type<T>;
const ValidityRestriction* field = get_restriction(T);
return boost::get<type>(field);
}
};
/**
* \brief Calculates size of an certificate object
*
* \param cert
* \return number of octets needed to serialize the object
*/
size_t get_size(const Certificate&);
/**
* \brief Serializes an object into a binary archive
*
* \param ar archive to serialize in
* \param cert to serialize
*/
void serialize(OutputArchive&, const Certificate&);
/**
* \brief Deserializes an object from a binary archive
*
* \param ar archive with a serialized object at the beginning
* \param cert to deserialize
* \return size of the deserialized object
*/
size_t deserialize(InputArchive&, Certificate&);
/**
* \brief Serialize parts of a Certificate for signature calculation
*
* Uses version, signer_field, subject_info, subject_attributes (+ length),
* validity_restriction (+ length).
*
* \param cert certificate to be converted
* \return binary representation
*/
ByteBuffer convert_for_signing(const Certificate&);
/**
* \brief Sort lists in the certificate to be in the correct order for serialization
*
* \param cert certificate to sort
*/
void sort(Certificate& certificate);
/**
* \brief Extract public key from certificate
* \param cert Certificate
* \param backend Backend
* \return Uncompressed public key (if available)
*/
boost::optional<Uncompressed> get_uncompressed_public_key(const Certificate&, Backend& backend);
/**
* \brief Extract public ECDSA256 key from certificate
* \param cert Certificate
* \param backend Backend
* \return public key (if available)
*/
boost::optional<ecdsa256::PublicKey> get_public_key(const Certificate&, Backend& backend);
/**
* Calculate hash id of certificate
* \param cert Certificate
* \return hash
*/
HashedId8 calculate_hash(const Certificate&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CERTIFICATE_HPP_LWBWIAVL */
@@ -0,0 +1,110 @@
#include <vanetza/security/v2/certificate_cache.hpp>
#include <chrono>
namespace vanetza
{
namespace security
{
namespace v2
{
CertificateCache::CertificateCache(const Runtime& rt) : m_runtime(rt)
{
}
void CertificateCache::insert(const Certificate& certificate)
{
const HashedId8 id = calculate_hash(certificate);
// this may drop expired entries and extend some's lifetime
std::list<Certificate> certs = lookup(id, certificate.subject_info.subject_type);
// TODO: implement equality comparison for Certificate
if (certs.size()) {
const auto binary_insert = convert_for_signing(certificate);
for (auto& cert : certs) {
const auto binary_found = convert_for_signing(cert);
if (binary_insert == binary_found) {
return;
}
}
}
Clock::duration lifetime = Clock::duration::zero();
if (certificate.subject_info.subject_type == SubjectType::Authorization_Ticket) {
// section 7.1 in ETSI TS 103 097 v1.2.1
// there must be a CAM with the authorization ticket every one second
// we choose two seconds here to account for one missed message
lifetime = std::chrono::seconds(2);
} else if (certificate.subject_info.subject_type == SubjectType::Authorization_Authority) {
// section 7.1 in ETSI TS 103 097 v1.2.1
// chains are only sent upon request, there will probably only be a few authoritation authorities in use
// one hour is an arbitrarily choosen cache period for now
lifetime = std::chrono::seconds(3600);
}
if (lifetime > Clock::duration::zero()) {
CachedCertificate entry;
entry.certificate = certificate;
map_type::iterator stored = m_certificates.emplace(id, entry);
heap_type::handle_type& handle = stored->second.handle;
handle = m_expiries.push(Expiry { m_runtime.now() + lifetime, stored });
}
}
std::list<Certificate> CertificateCache::lookup(const HashedId8& id, SubjectType type)
{
drop_expired();
using iterator = std::multimap<HashedId8, CachedCertificate>::iterator;
std::pair<iterator, iterator> range = m_certificates.equal_range(id);
std::list<Certificate> matches;
for (auto item = range.first; item != range.second; ++item) {
const Certificate& cert = item->second.certificate;
auto subject_type = cert.subject_info.subject_type;
if (subject_type != type) {
continue;
}
matches.push_back(cert);
// renew cached certificate
if (subject_type == SubjectType::Authorization_Ticket) {
refresh(item->second.handle, std::chrono::seconds(2));
} else if (subject_type == SubjectType::Authorization_Authority) {
refresh(item->second.handle, std::chrono::seconds(3600));
}
}
return matches;
}
void CertificateCache::drop_expired()
{
while (!m_expiries.empty() && is_expired(m_expiries.top())) {
m_certificates.erase(m_expiries.top().certificate);
m_expiries.pop();
}
}
bool CertificateCache::is_expired(const Expiry& expiry) const
{
return m_runtime.now() > expiry;
}
void CertificateCache::refresh(heap_type::handle_type& handle, Clock::duration lifetime)
{
static_cast<Clock::time_point&>(*handle) = m_runtime.now() + lifetime;
m_expiries.update(handle);
}
CertificateCache::Expiry::Expiry(Clock::time_point expiry, map_type::iterator it) :
Clock::time_point(expiry), certificate(it)
{
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,80 @@
#ifndef VANETZA_CERTIFICATE_CACHE_HPP
#define VANETZA_CERTIFICATE_CACHE_HPP
#include <vanetza/common/clock.hpp>
#include <vanetza/common/runtime.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <boost/heap/binomial_heap.hpp>
#include <list>
#include <map>
namespace vanetza
{
namespace security
{
namespace v2
{
/**
* CertificateCache remembers validated certificates for some time.
* This is necessary for certificate lookup when only its digest is known.
*/
class CertificateCache
{
public:
CertificateCache(const Runtime& rt);
/**
* Puts a (validated) certificate into the cache.
*
* \param certificate certificate to add to the cache
*/
void insert(const Certificate& certificate);
/**
* Lookup certificates based on the passed HashedId8.
*
* \param id hash identifier of the certificate
* \param type type of certificate to lookup
* \return all stored certificates matching the passed identifier and type
*/
std::list<Certificate> lookup(const HashedId8& id, SubjectType type);
/**
* Number of currently stored certificates
* \return cache size
*/
std::size_t size() const { return m_certificates.size(); }
private:
struct CachedCertificate;
using map_type = std::multimap<HashedId8, CachedCertificate>;
struct Expiry : public Clock::time_point
{
Expiry(Clock::time_point, map_type::iterator);
const map_type::iterator certificate;
};
using heap_type = boost::heap::binomial_heap<Expiry, boost::heap::compare<std::greater<Expiry>>>;
struct CachedCertificate
{
Certificate certificate;
heap_type::handle_type handle;
};
const Runtime& m_runtime;
heap_type m_expiries;
map_type m_certificates;
void drop_expired();
bool is_expired(const Expiry&) const;
void refresh(heap_type::handle_type&, Clock::duration);
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* VANETZA_CERTIFICATE_CACHE_HPP */
@@ -0,0 +1,43 @@
#ifndef A137DCCA_FFB9_4D91_8441_D559E6F17C14
#define A137DCCA_FFB9_4D91_8441_D559E6F17C14
#include <vanetza/security/ecdsa256.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
class CertificateProvider
{
public:
/**
* Get own certificate to use for signing
* \return own certificate
*/
virtual const Certificate& own_certificate() = 0;
/**
* Get own certificate chain in root CA → AA → AT order, excluding the AT and root certificate
* \return own certificate chain
*/
virtual std::list<Certificate> own_chain() = 0;
/**
* Get private key associated with own certificate
* \return private key
*/
virtual const ecdsa256::PrivateKey& own_private_key() = 0;
virtual ~CertificateProvider() = default;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* A137DCCA_FFB9_4D91_8441_D559E6F17C14 */
@@ -0,0 +1,33 @@
#ifndef CERTIFICATE_VALIDATOR_HPP
#define CERTIFICATE_VALIDATOR_HPP
//#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/certificate_validity.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
struct Certificate;
class CertificateValidator
{
public:
/**
* Check validity of given certificate and consistency with parent certificates.
* \param certificate given certificate
* \return validity result
*/
virtual CertificateValidity check_certificate(const Certificate& certificate) = 0;
virtual ~CertificateValidator() = default;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif // CERTIFICATE_VALIDATOR_HPP
@@ -0,0 +1,250 @@
#include <vanetza/common/its_aid.hpp>
#include <vanetza/common/position_fix.hpp>
#include <vanetza/security/hashed_id.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/certificate_cache.hpp>
#include <vanetza/security/v2/default_certificate_validator.hpp>
#include <vanetza/security/v2/signature.hpp>
#include <vanetza/security/v2/trust_store.hpp>
#include <vanetza/security/v2/validity_restriction.hpp>
#include <algorithm>
#include <chrono>
namespace vanetza
{
namespace security
{
namespace v2
{
namespace
{
boost::optional<StartAndEndValidity> extract_validity_time(const Certificate& certificate)
{
boost::optional<StartAndEndValidity> restriction;
for (auto& validity_restriction : certificate.validity_restriction) {
ValidityRestrictionType type = get_type(validity_restriction);
if (type == ValidityRestrictionType::Time_Start_And_End) {
// reject more than one restriction
if (restriction) {
return boost::none;
}
restriction = boost::get<StartAndEndValidity>(validity_restriction);
// check if certificate validity restriction timestamps are logically correct
if (restriction->start_validity >= restriction->end_validity) {
return boost::none;
}
} else if (type == ValidityRestrictionType::Time_End) {
// must not be used, no certificate profile allows it
return boost::none;
} else if (type == ValidityRestrictionType::Time_Start_And_Duration) {
// must not be used, no certificate profile allows it
return boost::none;
}
}
return restriction;
}
bool check_time_consistency(const Certificate& certificate, const Certificate& signer)
{
boost::optional<StartAndEndValidity> certificate_time = extract_validity_time(certificate);
boost::optional<StartAndEndValidity> signer_time = extract_validity_time(signer);
if (!certificate_time || !signer_time) {
return false;
}
if (signer_time->start_validity > certificate_time->start_validity) {
return false;
}
if (signer_time->end_validity < certificate_time->end_validity) {
return false;
}
return true;
}
std::list<ItsAid> extract_application_identifiers(const Certificate& certificate)
{
std::list<ItsAid> aids;
auto certificate_type = certificate.subject_info.subject_type;
if (certificate_type == SubjectType::Authorization_Ticket) {
auto list = certificate.get_attribute<SubjectAttributeType::ITS_AID_SSP_List>();
if (list) {
for (auto& item : *list) {
aids.push_back(item.its_aid.get());
}
}
} else {
auto list = certificate.get_attribute<SubjectAttributeType::ITS_AID_List>();
if (list) {
for (auto& item : *list) {
aids.push_back(item.get());
}
}
}
return aids;
}
bool check_permission_consistency(const Certificate& certificate, const Certificate& signer)
{
auto certificate_aids = extract_application_identifiers(certificate);
auto signer_aids = extract_application_identifiers(signer);
auto compare = [](ItsAid a, ItsAid b) { return a < b; };
certificate_aids.sort(compare);
signer_aids.sort(compare);
return std::includes(signer_aids.begin(), signer_aids.end(), certificate_aids.begin(), certificate_aids.end());
}
bool check_subject_assurance_consistency(const Certificate& certificate, const Certificate& signer)
{
auto certificate_assurance = certificate.get_attribute<SubjectAttributeType::Assurance_Level>();
auto signer_assurance = signer.get_attribute<SubjectAttributeType::Assurance_Level>();
if (!certificate_assurance || !signer_assurance) {
return false;
}
// See TS 103 096-2 v1.3.1, section 5.2.7.11 + 5.3.5.17 and following
if (certificate_assurance->assurance() > signer_assurance->assurance()) {
return false;
} else if (certificate_assurance->assurance() == signer_assurance->assurance()) {
if (certificate_assurance->confidence() > signer_assurance->confidence()) {
return false;
}
}
return true;
}
bool check_region_consistency(const Certificate& certificate, const Certificate& signer)
{
auto certificate_region = certificate.get_restriction<ValidityRestrictionType::Region>();
auto signer_region = signer.get_restriction<ValidityRestrictionType::Region>();
if (!signer_region) {
return true;
}
if (!certificate_region) {
return false;
}
return is_within(*certificate_region, *signer_region);
}
bool check_consistency(const Certificate& certificate, const Certificate& signer)
{
if (!check_time_consistency(certificate, signer)) {
return false;
}
if (!check_permission_consistency(certificate, signer)) {
return false;
}
if (!check_subject_assurance_consistency(certificate, signer)) {
return false;
}
if (!check_region_consistency(certificate, signer)) {
return false;
}
return true;
}
} // namespace
DefaultCertificateValidator::DefaultCertificateValidator(Backend& backend, CertificateCache& cert_cache, const TrustStore& trust_store) :
m_crypto_backend(backend),
m_cert_cache(cert_cache),
m_trust_store(trust_store)
{
}
CertificateValidity DefaultCertificateValidator::check_certificate(const Certificate& certificate)
{
if (!extract_validity_time(certificate)) {
return CertificateInvalidReason::Broken_Time_Period;
}
if (!certificate.get_attribute<SubjectAttributeType::Assurance_Level>()) {
return CertificateInvalidReason::Missing_Subject_Assurance;
}
SubjectType subject_type = certificate.subject_info.subject_type;
// check if subject_name is empty if certificate is authorization ticket
if (subject_type == SubjectType::Authorization_Ticket && 0 != certificate.subject_info.subject_name.size()) {
return CertificateInvalidReason::Invalid_Name;
}
if (get_type(certificate.signer_info) != SignerInfoType::Certificate_Digest_With_SHA256) {
return CertificateInvalidReason::Invalid_Signer;
}
HashedId8 signer_hash = boost::get<HashedId8>(certificate.signer_info);
// try to extract ECDSA signature
boost::optional<EcdsaSignature> sig = extract_ecdsa_signature(certificate.signature);
if (!sig) {
return CertificateInvalidReason::Missing_Signature;
}
// create buffer of certificate
ByteBuffer binary_cert = convert_for_signing(certificate);
// authorization tickets may only be signed by authorization authorities
if (subject_type == SubjectType::Authorization_Ticket) {
for (auto& possible_signer : m_cert_cache.lookup(signer_hash, SubjectType::Authorization_Authority)) {
auto verification_key = get_public_key(possible_signer, m_crypto_backend);
if (!verification_key) {
continue;
}
if (m_crypto_backend.verify_data(verification_key.get(), binary_cert, sig.get())) {
if (!check_consistency(certificate, possible_signer)) {
return CertificateInvalidReason::Inconsistent_With_Signer;
}
return CertificateValidity::valid();
}
}
}
// authorization authorities may only be signed by root CAs
// Note: There's no clear specification about this, but there's a test for it in 5.2.7.12.4 of TS 103 096-2 V1.3.1
if (subject_type == SubjectType::Authorization_Authority) {
for (auto& possible_signer : m_trust_store.lookup(signer_hash)) {
auto verification_key = get_public_key(possible_signer, m_crypto_backend);
if (!verification_key) {
continue;
}
if (m_crypto_backend.verify_data(verification_key.get(), binary_cert, sig.get())) {
if (!check_consistency(certificate, possible_signer)) {
return CertificateInvalidReason::Inconsistent_With_Signer;
}
return CertificateValidity::valid();
}
}
}
return CertificateInvalidReason::Unknown_Signer;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,47 @@
#ifndef DEFAULT_CERTIFICATE_VALIDATOR_HPP_MTULFLKX
#define DEFAULT_CERTIFICATE_VALIDATOR_HPP_MTULFLKX
#include <vanetza/common/clock.hpp>
#include <vanetza/common/position_provider.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/v2/certificate_validator.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
// forward declaration
class TrustStore;
class CertificateCache;
/**
* \brief The default certificate validator
*
* This certificate validator is reasonably secure! It just doesn't implement revocation checks for CA certificates.
*/
class DefaultCertificateValidator : public CertificateValidator
{
public:
DefaultCertificateValidator(Backend&, CertificateCache&, const TrustStore&);
/**
* \brief check certificate
* \param certificate to verify
* \return certificate status
*/
CertificateValidity check_certificate(const Certificate& certificate) override;
private:
Backend& m_crypto_backend;
CertificateCache& m_cert_cache;
const TrustStore& m_trust_store;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* DEFAULT_CERTIFICATE_VALIDATOR_HPP_MTULFLKX */
@@ -0,0 +1,186 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/ecc_point.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
#include <cassert>
namespace vanetza
{
namespace security
{
namespace v2
{
size_t get_size(const EccPoint& point)
{
size_t size = sizeof(EccPointType);
struct ecc_point_visitor : public boost::static_visitor<size_t>
{
size_t operator()(X_Coordinate_Only coord)
{
return coord.x.size();
}
size_t operator()(Compressed_Lsb_Y_0 coord)
{
return coord.x.size();
}
size_t operator()(Compressed_Lsb_Y_1 coord)
{
return coord.x.size();
}
size_t operator()(Uncompressed coord)
{
return coord.x.size() + coord.y.size();
}
};
ecc_point_visitor visit;
boost::apply_visitor(visit, point);
size += boost::apply_visitor(visit, point);
return size;
}
EccPointType get_type(const EccPoint& point)
{
struct ecc_point_visitor : public boost::static_visitor<EccPointType>
{
EccPointType operator()(const X_Coordinate_Only&)
{
return EccPointType::X_Coordinate_Only;
}
EccPointType operator()(const Compressed_Lsb_Y_0&)
{
return EccPointType::Compressed_Lsb_Y_0;
}
EccPointType operator()(const Compressed_Lsb_Y_1&)
{
return EccPointType::Compressed_Lsb_Y_1;
}
EccPointType operator()(const Uncompressed&)
{
return EccPointType::Uncompressed;
}
};
ecc_point_visitor visit;
return boost::apply_visitor(visit, point);
}
void serialize(OutputArchive& ar, const EccPoint& point, PublicKeyAlgorithm algo)
{
struct ecc_point_visitor : public boost::static_visitor<>
{
ecc_point_visitor(OutputArchive& ar, PublicKeyAlgorithm algo) :
m_archive(ar), m_algo(algo)
{
}
void operator()(X_Coordinate_Only coord)
{
assert(coord.x.size() == field_size(m_algo));
for (auto byte : coord.x) {
m_archive << byte;
}
}
void operator()(Compressed_Lsb_Y_0 coord)
{
assert(coord.x.size() == field_size(m_algo));
for (auto byte : coord.x) {
m_archive << byte;
}
}
void operator()(Compressed_Lsb_Y_1 coord)
{
assert(coord.x.size() == field_size(m_algo));
for (auto byte : coord.x) {
m_archive << byte;
}
}
void operator()(Uncompressed coord)
{
assert(coord.x.size() == field_size(m_algo));
assert(coord.y.size() == field_size(m_algo));
for (auto byte : coord.x) {
m_archive << byte;
}
for (auto byte : coord.y) {
m_archive << byte;
}
}
OutputArchive& m_archive;
PublicKeyAlgorithm m_algo;
};
EccPointType type = get_type(point);
serialize(ar, type);
ecc_point_visitor visit(ar, algo);
boost::apply_visitor(visit, point);
}
void deserialize(InputArchive& ar, EccPoint& point, PublicKeyAlgorithm algo)
{
size_t size = field_size(algo);
uint8_t elem;
EccPointType type;
deserialize(ar, type);
switch (type) {
case EccPointType::X_Coordinate_Only: {
X_Coordinate_Only coord;
for (size_t c = 0; c < size; c++) {
ar >> elem;
coord.x.push_back(elem);
}
point = coord;
break;
}
case EccPointType::Compressed_Lsb_Y_0: {
Compressed_Lsb_Y_0 coord;
for (size_t c = 0; c < size; c++) {
ar >> elem;
coord.x.push_back(elem);
}
point = coord;
break;
}
case EccPointType::Compressed_Lsb_Y_1: {
Compressed_Lsb_Y_1 coord;
for (size_t c = 0; c < size; c++) {
ar >> elem;
coord.x.push_back(elem);
}
point = coord;
break;
}
case EccPointType::Uncompressed: {
Uncompressed coord;
for (size_t c = 0; c < size; c++) {
ar >> elem;
coord.x.push_back(elem);
}
for (size_t c = 0; c < size; c++) {
ar >> elem;
coord.y.push_back(elem);
}
point = coord;
break;
}
default:
throw deserialization_error("Unknown EccPointType");
}
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,62 @@
#ifndef ECC_POINT_HPP_XCESTUEB
#define ECC_POINT_HPP_XCESTUEB
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/security/ecc_point.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
/// forward declaration, see public_key.hpp
enum class PublicKeyAlgorithm: uint8_t;
/// EccPointType specified in TS 103 097 v1.2.1 in section 4.2.6
enum class EccPointType : uint8_t
{
X_Coordinate_Only = 0,
Compressed_Lsb_Y_0 = 2,
Compressed_Lsb_Y_1 = 3,
Uncompressed = 4
};
/**
* \brief Determines EccPointType to a given EccPoint
* \param ecc_point
* \return type
*/
EccPointType get_type(const EccPoint&);
/**
* \brief Serializes an EccPoint into a binary archive
* \param ar to serialize in
* \param ecc_point to serialize
* \param pka Public key algorithm used for EccPoint
*/
void serialize(OutputArchive&, const EccPoint&, PublicKeyAlgorithm);
/**
* \brief Deserializes an EccPoint from a binary archive
* \param ar with a serialized EccPoint at the beginning,
* \param ecc_point to deserialize
* \param pka to get field size of the encoded coordinates
*/
void deserialize(InputArchive&, EccPoint&, PublicKeyAlgorithm);
/**
* \brief Calculates size of an EccPoint
* \param ecc_point
* \return size_t containing the number of octets needed to serialize the EccPoint
*/
size_t get_size(const EccPoint&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* ECC_POINT_HPP_XCESTUEB */
@@ -0,0 +1,91 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/encryption_parameter.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
SymmetricAlgorithm get_type(const EncryptionParameter& param)
{
struct Encryption_visitor : public boost::static_visitor<SymmetricAlgorithm>
{
SymmetricAlgorithm operator()(const Nonce&)
{
return SymmetricAlgorithm::AES128_CCM;
}
};
Encryption_visitor visit;
return boost::apply_visitor(visit, param);
}
void serialize(OutputArchive& ar, const EncryptionParameter& param)
{
struct Encryption_visitor : public boost::static_visitor<>
{
Encryption_visitor(OutputArchive& ar) :
m_archive(ar)
{
}
void operator()(const Nonce& nonce)
{
for (auto& byte : nonce) {
m_archive << byte;
}
}
OutputArchive& m_archive;
};
SymmetricAlgorithm algo = get_type(param);
serialize(ar, algo);
Encryption_visitor visit(ar);
boost::apply_visitor(visit, param);
}
size_t get_size(const EncryptionParameter& param)
{
size_t size = sizeof(SymmetricAlgorithm);
struct Encryption_visitor : public boost::static_visitor<size_t>
{
size_t operator()(const Nonce& nonce)
{
return nonce.size();
}
};
Encryption_visitor visit;
size += boost::apply_visitor(visit, param);
return size;
}
size_t deserialize(InputArchive& ar, EncryptionParameter& param)
{
SymmetricAlgorithm algo;
deserialize(ar, algo);
switch (algo) {
case SymmetricAlgorithm::AES128_CCM: {
Nonce nonce;
for (size_t s = 0; s < nonce.size(); s++) {
ar >> nonce[s];
}
param = nonce;
break;
}
default:
throw deserialization_error("Unknown Symmetric Algorithm");
break;
}
return get_size(param);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,58 @@
#ifndef ENCRYPTION_PARAMETER_HPP_EIAWNAWY
#define ENCRYPTION_PARAMETER_HPP_EIAWNAWY
#include <vanetza/security/v2/serialization.hpp>
#include <boost/variant/variant.hpp>
#include <array>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
/// forward declaration, see public_key.hpp
enum class SymmetricAlgorithm : uint8_t;
/// Nonce specified in TS 103 097 v1.2.1, section 4.2.7
using Nonce = std::array<uint8_t, 12>;
/// EncryptionParameter specified in TS 103 097 v1.2.1, section 4.2.7
using EncryptionParameter = boost::variant<Nonce>;
/**
* \brief Determines SymmetricAlgorithm for an EncryptionParameter
* \param param
* \return SymmetricAlgorithm
*/
SymmetricAlgorithm get_type(const EncryptionParameter&);
/**
* \brief Serializes an EncryptionParameter into a binary archive
* \param ar to serialize in
* \param param to serialize
*/
void serialize(OutputArchive&, const EncryptionParameter&);
/**
* \brief Calculates size of an EncryptionParameter
* \param param
* \return number of octets needed to serialize the EncryptionParameter
*/
size_t get_size(const EncryptionParameter&);
/**
* \brief Deserializes an EncryptionParameter from a binary archive
* \param ar Input expected to start with an EncryptionParameter
* \param enc Deserialized encryption parameter
* \return size of deserialized EncryptionParameter
*/
size_t deserialize(InputArchive&, EncryptionParameter&);
} // namespace v2
} // namespace security
} // namespace vanetzta
#endif /* ENCRYPTION_PARAMETER_HPP_EIAWNAWY */
@@ -0,0 +1,290 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/header_field.hpp>
#include <boost/optional.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
HeaderFieldType get_type(const HeaderField& field)
{
struct HeaderFieldVisitor : public boost::static_visitor<HeaderFieldType>
{
HeaderFieldType operator()(const Time64&)
{
return HeaderFieldType::Generation_Time;
}
HeaderFieldType operator()(const Time64WithStandardDeviation&)
{
return HeaderFieldType::Generation_Time_Confidence;
}
HeaderFieldType operator()(const Time32&)
{
return HeaderFieldType::Expiration;
}
HeaderFieldType operator()(const ThreeDLocation&)
{
return HeaderFieldType::Generation_Location;
}
HeaderFieldType operator()(const std::list<HashedId3>&)
{
return HeaderFieldType::Request_Unrecognized_Certificate;
}
HeaderFieldType operator()(const IntX&)
{
return HeaderFieldType::Its_Aid;
}
HeaderFieldType operator()(const SignerInfo&)
{
return HeaderFieldType::Signer_Info;
}
HeaderFieldType operator()(const std::list<RecipientInfo>&)
{
return HeaderFieldType::Recipient_Info;
}
HeaderFieldType operator()(const EncryptionParameter&)
{
return HeaderFieldType::Encryption_Parameters;
}
};
HeaderFieldVisitor visit;
return boost::apply_visitor(visit, field);
}
size_t get_size(const HeaderField& field)
{
size_t size = sizeof(HeaderFieldType);
struct HeaderFieldVisitor : public boost::static_visitor<>
{
void operator()(const Time64&)
{
m_size = sizeof(Time64);
}
void operator()(const Time64WithStandardDeviation& time)
{
m_size = sizeof(time.time64);
m_size += sizeof(time.log_std_dev);
}
void operator()(const Time32&)
{
m_size = sizeof(Time32);
}
void operator()(const ThreeDLocation& loc)
{
m_size = get_size(loc);
}
void operator()(const std::list<HashedId3>& list)
{
m_size = 0;
for (auto& elem : list) {
m_size += elem.size();
}
m_size += length_coding_size(m_size);
}
void operator()(const IntX& itsAid)
{
m_size = get_size(itsAid);
}
void operator()(const SignerInfo& info)
{
m_size = get_size(info);
}
void operator()(const std::list<RecipientInfo>& list)
{
m_size = get_size(list);
m_size += length_coding_size(m_size);
}
void operator()(const EncryptionParameter& enc)
{
m_size = get_size(enc);
}
size_t m_size;
};
HeaderFieldVisitor visit;
boost::apply_visitor(visit, field);
size += visit.m_size;
return size;
}
void serialize(OutputArchive& ar, const HeaderField& field)
{
struct HeaderFieldVisitor : public boost::static_visitor<>
{
HeaderFieldVisitor(OutputArchive& ar) :
m_archive(ar)
{
}
void operator()(const Time64& time)
{
serialize(m_archive, host_cast(time));
}
void operator()(const Time64WithStandardDeviation& time)
{
serialize(m_archive, host_cast(time.time64));
serialize(m_archive, host_cast(time.log_std_dev));
}
void operator()(const Time32& time)
{
serialize(m_archive, host_cast(time));
}
void operator()(const ThreeDLocation& loc)
{
serialize(m_archive, loc);
}
void operator()(const std::list<HashedId3>& list)
{
size_t size = 0;
for (auto& elem : list) {
size += elem.size();
}
serialize_length(m_archive, size);
for (auto& elem : list) {
m_archive << elem[0];
m_archive << elem[1];
m_archive << elem[2];
}
}
void operator()(const IntX& itsAid)
{
serialize(m_archive, itsAid);
}
void operator()(const SignerInfo& info)
{
serialize(m_archive, info);
}
void operator()(const std::list<RecipientInfo>& list)
{
// TODO: only works until further symmetric algorithms are introduced
serialize(m_archive, list, SymmetricAlgorithm::AES128_CCM);
}
void operator()(const EncryptionParameter& param)
{
serialize(m_archive, param);
}
OutputArchive& m_archive;
};
HeaderFieldType type = get_type(field);
serialize(ar, type);
HeaderFieldVisitor visit(ar);
boost::apply_visitor(visit, field);
}
std::size_t deserialize(InputArchive& ar, std::list<HeaderField>& list)
{
static const std::size_t size_limit = 1024;
const std::size_t size = trim_size(deserialize_length(ar));
if (size > size_limit) {
ar.fail(InputArchive::ErrorCode::ExcessiveLength);
}
std::size_t read = 0;
boost::optional<SymmetricAlgorithm> sym_algo;
while (read < size && ar.is_good()) {
HeaderField field;
HeaderFieldType type;
deserialize(ar, type);
read += sizeof(HeaderFieldType);
switch (type) {
case HeaderFieldType::Generation_Time: {
Time64 time;
deserialize(ar, time);
field = time;
list.push_back(field);
read += sizeof(Time64);
break;
}
case HeaderFieldType::Generation_Time_Confidence: {
Time64WithStandardDeviation time;
deserialize(ar, time.time64);
deserialize(ar, time.log_std_dev);
field = time;
list.push_back(field);
read += sizeof(Time64);
read += sizeof(uint8_t);
break;
}
case HeaderFieldType::Expiration: {
Time32 time;
deserialize(ar, time);
field = time;
list.push_back(field);
read += sizeof(Time32);
break;
}
case HeaderFieldType::Generation_Location: {
ThreeDLocation loc;
read += deserialize(ar, loc);
field = loc;
list.push_back(field);
break;
}
case HeaderFieldType::Request_Unrecognized_Certificate: {
const std::size_t tmp_size = trim_size(deserialize_length(ar));
read += tmp_size;
std::list<HashedId3> hashedList;
for (std::size_t c = 0; c < tmp_size; c += 3) {
HashedId3 id;
ar >> id[0];
ar >> id[1];
ar >> id[2];
hashedList.push_back(id);
}
field = hashedList;
read += length_coding_size(tmp_size);
list.push_back(field);
break;
}
case HeaderFieldType::Its_Aid: {
IntX its_aid;
read += deserialize(ar, its_aid);
field = its_aid;
list.push_back(field);
break;
}
case HeaderFieldType::Signer_Info: {
SignerInfo info;
read += deserialize(ar, info);
field = info;
list.push_back(field);
break;
}
case HeaderFieldType::Recipient_Info: {
std::list<RecipientInfo> recipientList;
if (sym_algo) {
const size_t tmp_size = deserialize(ar, recipientList, sym_algo.get());
read += tmp_size;
read += length_coding_size(tmp_size);
field = recipientList;
list.push_back(field);
} else {
throw deserialization_error("HeaderFields: RecipientInfo read before EncryptionParameters: SymmetricAlgorithm still unknown");
}
break;
}
case HeaderFieldType::Encryption_Parameters: {
EncryptionParameter param;
read += deserialize(ar, param);
field = param;
sym_algo = get_type(param);
list.push_back(field);
break;
}
default:
throw deserialization_error("Unknown HeaderFieldType");
break;
}
}
return read;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,144 @@
#ifndef HEADER_FIELD_HPP_IHIAKD4K
#define HEADER_FIELD_HPP_IHIAKD4K
#include <vanetza/security/v2/basic_elements.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/encryption_parameter.hpp>
#include <vanetza/security/v2/int_x.hpp>
#include <vanetza/security/v2/recipient_info.hpp>
#include <vanetza/security/v2/region.hpp>
#include <vanetza/security/v2/signer_info.hpp>
#include <boost/variant/variant.hpp>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
/// HeaderFieldType specified in TS 103 097 v1.2.1, section 5.5
enum class HeaderFieldType : uint8_t
{
Generation_Time = 0, // Time64
Generation_Time_Confidence = 1, // Time64WithStandardDeviation
Expiration = 2, // Time32
Generation_Location = 3, // TreeDLocation
Request_Unrecognized_Certificate = 4, // std::list<HashedId3>
Its_Aid = 5, // IntX
Signer_Info = 128, // SignerInfo
Encryption_Parameters = 129, // EncryptionParameters
Recipient_Info = 130, // std::list<RecipientInfo>
};
/// HeaderField specified in TS 103 097 v1.2.1, section 5.4
using HeaderField = boost::variant<
Time64,
Time64WithStandardDeviation,
Time32,
ThreeDLocation,
std::list<HashedId3>,
IntX,
SignerInfo,
EncryptionParameter,
std::list<RecipientInfo>
>;
/**
* \brief Determines HeaderFieldType to a given HeaderField
* \param field
* \return type
*/
HeaderFieldType get_type(const HeaderField& field);
/**
* \brief Calculates size of a HeaderField
* \param field
* \return number of octets needed to serialize the HeaderField
*/
std::size_t get_size(const HeaderField& field);
/**
* \brief Serializes a HeaderField into a binary archive
* \note Serialization of HeaderField lists is provided by template
* \param ar to serialize in
* \param field to serialize
*/
void serialize(OutputArchive& ar, const HeaderField& field);
/**
* \brief Deserializes a list of HeaderFields from a binary archive
* \param ar with a serialized list of HeaderFields at the beginning
* \param list of HeaderFields to deserialize
* \return size of the deserialized list
*/
size_t deserialize(InputArchive& ar, std::list<HeaderField>& list);
/**
* \brief resolve type for matching HeaderFieldType
*
* This is kind of the reverse function of get_type(const HeaderField&)
*/
template<HeaderFieldType>
struct header_field_type;
template<>
struct header_field_type<HeaderFieldType::Generation_Time>
{
using type = Time64;
};
template<>
struct header_field_type<HeaderFieldType::Generation_Time_Confidence>
{
using type = Time64WithStandardDeviation;
};
template<>
struct header_field_type<HeaderFieldType::Expiration>
{
using type = Time32;
};
template<>
struct header_field_type<HeaderFieldType::Generation_Location>
{
using type = ThreeDLocation;
};
template<>
struct header_field_type<HeaderFieldType::Request_Unrecognized_Certificate>
{
using type = std::list<HashedId3>;
};
template<>
struct header_field_type<HeaderFieldType::Its_Aid>
{
using type = IntX;
};
template<>
struct header_field_type<HeaderFieldType::Signer_Info>
{
using type = SignerInfo;
};
template<>
struct header_field_type<HeaderFieldType::Encryption_Parameters>
{
using type = EncryptionParameter;
};
template<>
struct header_field_type<HeaderFieldType::Recipient_Info>
{
using type = std::list<RecipientInfo>;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* HEADER_FIELD_HPP_IHIAKD4K */
@@ -0,0 +1,51 @@
#include <vanetza/security/v2/int_x.hpp>
#include <vanetza/security/v2/length_coding.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void IntX::set(integer_type x)
{
m_value = x;
}
ByteBuffer IntX::encode() const
{
return encode_length(m_value);
}
boost::optional<IntX> IntX::decode(const ByteBuffer& buffer)
{
std::tuple<ByteBuffer::const_iterator, std::uintmax_t> decoded = decode_length(buffer);
if (std::get<0>(decoded) != buffer.begin()) {
IntX result;
result.set(std::get<1>(decoded));
return result;
}
return boost::none;
}
size_t get_size(IntX intx)
{
return length_coding_size(intx.get());
}
void serialize(OutputArchive& ar, const IntX& intx)
{
serialize_length(ar, intx.get());
}
size_t deserialize(InputArchive& ar, IntX& intx)
{
const auto size = deserialize_length(ar);
intx.set(size);
return get_size(intx);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,73 @@
#ifndef INT_X_HPP_RW3TJBBI
#define INT_X_HPP_RW3TJBBI
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <boost/operators.hpp>
#include <boost/optional.hpp>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
/// IntX specified in TS 103 097 v1.2.1, section 4.2.1
class IntX :
public boost::equality_comparable<IntX>,
public boost::equality_comparable<IntX, std::uintmax_t>
{
public:
using integer_type = std::uintmax_t;
constexpr IntX() : m_value(0) {}
constexpr explicit IntX(integer_type x) : m_value(x) {}
void set(integer_type x);
constexpr integer_type get() const { return m_value; }
constexpr bool operator==(const IntX& other) const
{
return m_value == other.m_value;
}
constexpr bool operator==(integer_type other) const
{
return m_value == other;
}
ByteBuffer encode() const;
static boost::optional<IntX> decode(const ByteBuffer&);
private:
integer_type m_value;
};
/**
* \brief Serializes an IntX into a binary archive
* \param ar to serialize in
* \param intx to serialize
*/
void serialize(OutputArchive&, const IntX&);
/**
* \brief Deserializes an IntX from a binary archive
* \param ar with a serialized IntX at the beginning
* \param intx to deserialize
* \return size of the deserialized IntX
*/
size_t deserialize(InputArchive&, IntX&);
/**
* \brief Calculates size of an IntX
* \param intx
* \return number of octets needed to serialize the IntX
*/
size_t get_size(IntX);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* INT_X_HPP_RW3TJBBI */
@@ -0,0 +1,94 @@
#include <vanetza/security/v2/length_coding.hpp>
#include <cassert>
#include <cmath>
#include <iterator>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
std::size_t count_leading_ones(uint8_t v)
{
std::size_t count = 0;
while ((v & 0x80) != 0) {
v <<= 1;
++count;
}
return count;
}
std::size_t length_coding_size(std::uintmax_t length) {
std::size_t size = 1;
while ((length & ~0x7f) != 0) {
// prefix enlongates by one additional leading "1" per shift
length >>= 7; // shift by 7
++size;
}
return size;
}
ByteBuffer encode_length(std::uintmax_t length)
{
static_assert(sizeof(std::uintmax_t) <= 8, "size of length type exceeds implementation capabilities");
std::list<uint8_t> length_info;
while (length != 0) {
length_info.push_front(static_cast<uint8_t>(length));
length >>= 8;
}
unsigned prefix_length = length_info.size();
if (prefix_length == 0) {
// Zero-size encoding
length_info.push_back(0x00);
}
else {
assert(prefix_length <= 8);
uint8_t prefix_mask = ~((1 << (8 - prefix_length)) - 1);
if ((length_info.front() & ~prefix_mask) != length_info.front()) {
// additional byte needed for prefix
length_info.push_front(prefix_mask);
}
else {
// enough free bits available for prefix
length_info.front() |= (prefix_mask << 1);
}
// Huge lengths have all bits set in leading prefix bytes
length_info.insert(length_info.begin(), prefix_length / 8, 0xff);
}
return ByteBuffer(length_info.begin(), length_info.end());
}
std::tuple<ByteBuffer::const_iterator, std::uintmax_t> decode_length(const ByteBuffer& buffer)
{
if (!buffer.empty()) {
std::size_t additional_prefix = count_leading_ones(buffer.front());
if (additional_prefix >= sizeof(std::uintmax_t)) {
// encoded length is wider than uintmax_t, we cannot represent this number
return std::make_tuple(buffer.begin(), 0);
} else if (buffer.size() > additional_prefix) {
uint8_t prefix_mask = (1 << (8 - additional_prefix)) - 1;
std::uintmax_t length = buffer.front() & prefix_mask;
for (std::size_t i = 1; i <= additional_prefix; ++i) {
length <<= 8;
length |= buffer[i];
}
auto start = buffer.begin();
std::advance(start, additional_prefix + 1);
return std::make_tuple(start, length);
}
}
return std::make_tuple(buffer.end(), 0);
}
} // namespace v2
} // namespace security
} // namespace vanextza
@@ -0,0 +1,49 @@
#ifndef LENGTH_CODING_HPP_UQ1OIDUN
#define LENGTH_CODING_HPP_UQ1OIDUN
#include <vanetza/common/byte_buffer.hpp>
#include <boost/range/iterator_range.hpp>
#include <cstdint>
#include <tuple>
namespace vanetza
{
namespace security
{
namespace v2
{
/**
* Calculate length coding for variable length fields
* \param length Data field length in bytes, e.g. size of a buffer
* \return byte buffer containing encoded length, prepend to data
*/
ByteBuffer encode_length(std::uintmax_t length);
/**
* Extract length information
* \param buffer Buffer with input data, shall start with first byte of encoded length
* \return iterator pointing at start of payload buffer (begin indicates error) and its length
*/
std::tuple<ByteBuffer::const_iterator, std::uintmax_t> decode_length(const ByteBuffer& buffer);
/**
* Count number of leading one bits
* \param a byte
* \return number of leadings ones in given byte
*/
std::size_t count_leading_ones(std::uint8_t);
/**
* Determines the number of bytes, needed to store a given size
* \param size
* \return number of bytes needed to store length
*/
std::size_t length_coding_size(std::uintmax_t);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* LENGTH_CODING_HPP_UQ1OIDUN */
@@ -0,0 +1,238 @@
#include <vanetza/common/its_aid.hpp>
//#include <vanetza/security/v2/basic_elements.hpp>
//#include <vanetza/security/v2/ecc_point.hpp>
#include <vanetza/security/v2/naive_certificate_provider.hpp>
//#include <vanetza/security/v2/payload.hpp>
//#include <vanetza/security/v2/secured_message.hpp>
//#include <vanetza/security/v2/signature.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
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()
{
// renew certificate if necessary
for (auto& validity_restriction : m_own_certificate.validity_restriction) {
auto start_and_end = boost::get<StartAndEndValidity>(&validity_restriction);
auto renewal_deadline = convert_time32(m_runtime.now() + std::chrono::hours(1));
if (start_and_end && start_and_end->end_validity < renewal_deadline) {
m_own_certificate = generate_authorization_ticket();
break;
}
}
return m_own_certificate;
}
std::list<Certificate> NaiveCertificateProvider::own_chain()
{
static const std::list<Certificate> chain = { aa_certificate() };
return chain;
}
const ecdsa256::PrivateKey& NaiveCertificateProvider::own_private_key()
{
return m_own_key_pair.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;
// section 6.1 in TS 103 097 v1.2.1
certificate.signer_info = calculate_hash(aa_certificate());
// section 6.3 in TS 103 097 v1.2.1
certificate.subject_info.subject_type = SubjectType::Authorization_Ticket;
// section 7.4.2 in TS 103 097 v1.2.1, subject_name implicit empty
// set assurance level
certificate.subject_attributes.push_back(SubjectAssurance(0x00));
certificate.add_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
certificate.add_permission(aid::DEN, ByteBuffer({ 1, 0xff, 0xff, 0xff}));
certificate.add_permission(aid::GN_MGMT, ByteBuffer({})); // required for beacons
certificate.add_permission(aid::IPV6_ROUTING, ByteBuffer({})); // required for routing tests
// section 7.4.1 in TS 103 097 v1.2.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());
EccPoint ecc_point = coordinates;
ecdsa_nistp256_with_sha256 ecdsa;
ecdsa.public_key = ecc_point;
VerificationKey verification_key;
verification_key.key = ecdsa;
certificate.subject_attributes.push_back(verification_key);
// section 6.7 in TS 103 097 v1.2.1
// set validity restriction
StartAndEndValidity start_and_end;
start_and_end.start_validity = convert_time32(m_runtime.now() - std::chrono::hours(1));
start_and_end.end_validity = convert_time32(m_runtime.now() + std::chrono::hours(23));
certificate.validity_restriction.push_back(start_and_end);
sign_authorization_ticket(certificate);
return certificate;
}
void NaiveCertificateProvider::sign_authorization_ticket(Certificate& certificate)
{
sort(certificate);
ByteBuffer data_buffer = convert_for_signing(certificate);
certificate.signature = m_crypto_backend->sign_data(aa_key_pair().private_key, data_buffer);
}
Certificate NaiveCertificateProvider::generate_aa_certificate(const std::string& subject_name)
{
// create certificate
Certificate certificate;
// section 6.1 in TS 103 097 v1.2.1
certificate.signer_info = calculate_hash(root_certificate());
// section 6.3 in TS 103 097 v1.2.1
certificate.subject_info.subject_type = SubjectType::Authorization_Authority;
// section 7.4.2 in TS 103 097 v1.2.1
std::vector<unsigned char> subject(subject_name.begin(), subject_name.end());
certificate.subject_info.subject_name = subject;
// section 6.6 in TS 103 097 v1.2.1 - levels currently undefined
certificate.subject_attributes.push_back(SubjectAssurance(0x00));
certificate.add_permission(aid::CA);
certificate.add_permission(aid::DEN);
certificate.add_permission(aid::GN_MGMT); // required for beacons
certificate.add_permission(aid::IPV6_ROUTING); // required for routing tests
// section 7.4.1 in TS 103 097 v1.2.1
// set subject attributes
// set the verification_key
Uncompressed coordinates;
coordinates.x.assign(aa_key_pair().public_key.x.begin(), aa_key_pair().public_key.x.end());
coordinates.y.assign(aa_key_pair().public_key.y.begin(), aa_key_pair().public_key.y.end());
EccPoint ecc_point = coordinates;
ecdsa_nistp256_with_sha256 ecdsa;
ecdsa.public_key = ecc_point;
VerificationKey verification_key;
verification_key.key = ecdsa;
certificate.subject_attributes.push_back(verification_key);
// section 6.7 in TS 103 097 v1.2.1
// set validity restriction
StartAndEndValidity start_and_end;
start_and_end.start_validity = convert_time32(m_runtime.now() - std::chrono::hours(1));
start_and_end.end_validity = convert_time32(m_runtime.now() + std::chrono::hours(23));
certificate.validity_restriction.push_back(start_and_end);
sort(certificate);
// set signature
ByteBuffer data_buffer = convert_for_signing(certificate);
certificate.signature = m_crypto_backend->sign_data(root_key_pair().private_key, data_buffer);
return certificate;
}
Certificate NaiveCertificateProvider::generate_root_certificate(const std::string& subject_name)
{
// create certificate
Certificate certificate;
// section 6.1 in TS 103 097 v1.2.1
certificate.signer_info = nullptr; /* self */
// section 6.3 in TS 103 097 v1.2.1
certificate.subject_info.subject_type = SubjectType::Root_CA;
// section 7.4.2 in TS 103 097 v1.2.1
std::vector<unsigned char> subject(subject_name.begin(), subject_name.end());
certificate.subject_info.subject_name = subject;
// section 6.6 in TS 103 097 v1.2.1 - levels currently undefined
certificate.subject_attributes.push_back(SubjectAssurance(0x00));
certificate.add_permission(aid::CA);
certificate.add_permission(aid::DEN);
certificate.add_permission(aid::GN_MGMT); // required for beacons
certificate.add_permission(aid::IPV6_ROUTING); // required for routing tests
// section 7.4.1 in TS 103 097 v1.2.1
// set subject attributes
// set the verification_key
Uncompressed coordinates;
coordinates.x.assign(root_key_pair().public_key.x.begin(), root_key_pair().public_key.x.end());
coordinates.y.assign(root_key_pair().public_key.y.begin(), root_key_pair().public_key.y.end());
EccPoint ecc_point = coordinates;
ecdsa_nistp256_with_sha256 ecdsa;
ecdsa.public_key = ecc_point;
VerificationKey verification_key;
verification_key.key = ecdsa;
certificate.subject_attributes.push_back(verification_key);
// section 6.7 in TS 103 097 v1.2.1
// set validity restriction
StartAndEndValidity start_and_end;
start_and_end.start_validity = convert_time32(m_runtime.now() - std::chrono::hours(1));
start_and_end.end_validity = convert_time32(m_runtime.now() + std::chrono::hours(365 * 24));
certificate.validity_restriction.push_back(start_and_end);
sort(certificate);
// set signature
ByteBuffer data_buffer = convert_for_signing(certificate);
certificate.signature = m_crypto_backend->sign_data(root_key_pair().private_key, data_buffer);
return certificate;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,110 @@
#ifndef NAIVE_CERTIFICATE_PROVIDER_HPP_MTULFLKX
#define NAIVE_CERTIFICATE_PROVIDER_HPP_MTULFLKX
#include <memory>
#include <string>
#include <vanetza/common/runtime.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/certificate_provider.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/**
* \brief A very simplistic certificate provider
*
* This certificate provider signs its certificates with a randomly generated root certificate. This means the
* signatures produced based on this certificate provider can't be verified by other parties.
*
* It's intended for experimenting with secured messages without validating signatures.
*/
class NaiveCertificateProvider : public CertificateProvider
{
public:
NaiveCertificateProvider(const Runtime&);
/**
* \brief get own certificate for signing
* \return own certificate
*/
const Certificate& own_certificate() override;
/**
* Get own certificate chain, excluding the leaf certificate and root CA
* \return own certificate chain
*/
std::list<Certificate> own_chain() override;
/**
* \brief get own private key
* \return private key
*/
const ecdsa256::PrivateKey& own_private_key() override;
/**
* \brief get ticket signer certificate (same for all instances)
* \return signing authorization authority certificate
*/
const Certificate& aa_certificate();
/**
* \brief get root certificate (same for all instances)
* \return signing root certificate
*/
const Certificate& root_certificate();
/**
* \brief generate an authorization ticket
* \return generated certificate
*/
Certificate generate_authorization_ticket();
/**
* \brief sign an authorization ticket
* \param certificate certificate to sign
*/
void sign_authorization_ticket(Certificate& certificate);
private:
/**
* \brief get root key (same for all instances)
* \return root key
*/
const ecdsa256::KeyPair& aa_key_pair();
/**
* \brief get root key (same for all instances)
* \return root key
*/
const ecdsa256::KeyPair& root_key_pair();
/**
* \brief generate a authorization authority certificate
*
* \return generated certificate
*/
Certificate generate_aa_certificate(const std::string& subject_name);
/**
* \brief generate a root certificate
*
* \return generated certificate
*/
Certificate generate_root_certificate(const std::string& subject_name);
std::unique_ptr<Backend> m_crypto_backend;
const Runtime& m_runtime;
const ecdsa256::KeyPair m_own_key_pair;
Certificate m_own_certificate;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* NAIVE_CERTIFICATE_PROVIDER_HPP_MTULFLKX */
@@ -0,0 +1,71 @@
#include <vanetza/security/v2/null_certificate_provider.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
VerificationKey create_null_verification_key()
{
Uncompressed coordinates;
coordinates.x.resize(32);
coordinates.y.resize(32);
EccPoint ecc_point = coordinates;
ecdsa_nistp256_with_sha256 ecdsa;
ecdsa.public_key = ecc_point;
VerificationKey verification_key;
verification_key.key = ecdsa;
return verification_key;
}
EcdsaSignature create_null_signature()
{
EcdsaSignature signature;
X_Coordinate_Only coordinate;
coordinate.x.resize(32);
signature.R = std::move(coordinate);
signature.s.resize(32);
return signature;
}
Certificate create_null_certificate()
{
Certificate cert;
cert.signer_info = HashedId8 {};
cert.subject_info.subject_type = SubjectType::Authorization_Ticket;
cert.subject_attributes.push_back(SubjectAssurance(0x00));
cert.subject_attributes.push_back(create_null_verification_key());
cert.validity_restriction.push_back(StartAndEndValidity {});
cert.signature = create_null_signature();
return cert;
}
NullCertificateProvider::NullCertificateProvider() { }
const Certificate& NullCertificateProvider::own_certificate()
{
return null_certificate();
}
std::list<Certificate> NullCertificateProvider::own_chain()
{
return std::list<Certificate> {};
}
const ecdsa256::PrivateKey& NullCertificateProvider::own_private_key()
{
static const ecdsa256::PrivateKey null_key {};
return null_key;
}
const Certificate& NullCertificateProvider::null_certificate()
{
static const Certificate null_certificate = create_null_certificate();
return null_certificate;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,33 @@
#ifndef NULL_CERTIFICATE_PROVIDER_HPP_3L9RJY2A
#define NULL_CERTIFICATE_PROVIDER_HPP_3L9RJY2A
#include <vanetza/security/v2/certificate_provider.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
class NullCertificateProvider : public CertificateProvider
{
public:
NullCertificateProvider();
const Certificate& own_certificate() override;
std::list<Certificate> own_chain() override;
const ecdsa256::PrivateKey& own_private_key() override;
/**
* Get static dummy certificate
* \return certificate filled with dummy values
*/
static const Certificate& null_certificate();
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* NULL_CERTIFICATE_PROVIDER_HPP_3L9RJY2A */
@@ -0,0 +1,26 @@
#include <vanetza/security/v2/null_certificate_validator.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
NullCertificateValidator::NullCertificateValidator() : m_check_result(CertificateInvalidReason::Unknown_Signer)
{
}
CertificateValidity NullCertificateValidator::check_certificate(const Certificate&)
{
return m_check_result;
}
void NullCertificateValidator::certificate_check_result(const CertificateValidity& result)
{
m_check_result = result;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,34 @@
#ifndef NULL_CERTIFICATE_VALIDATOR_HPP_3L9RJY2A
#define NULL_CERTIFICATE_VALIDATOR_HPP_3L9RJY2A
#include <vanetza/security/v2/certificate_validator.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
class NullCertificateValidator : public CertificateValidator
{
public:
NullCertificateValidator();
CertificateValidity check_certificate(const Certificate&) override;
/**
* Set predefined result of check_certificate() calls
* \param result predefined result
*/
void certificate_check_result(const CertificateValidity& result);
private:
CertificateValidity m_check_result;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* NULL_CERTIFICATE_VALIDATOR_HPP_3L9RJY2A */
@@ -0,0 +1,63 @@
#include <vanetza/security/v2/payload.hpp>
#include <vanetza/security/v2/length_coding.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
PayloadType get_type(const Payload& payload)
{
return payload.type;
}
size_t get_size(const Payload& payload)
{
size_t length = sizeof(PayloadType);
const size_t data = size(payload.data, OsiLayer::Network, max_osi_layer());
length += data;
length += length_coding_size(data);
return length;
}
size_t get_size(const ByteBuffer& buf)
{
size_t size = buf.size();
size += length_coding_size(size);
return size;
}
void serialize(OutputArchive& ar, const Payload& payload)
{
serialize(ar, payload.type);
serialize_length(ar, size(payload.data, OsiLayer::Network, max_osi_layer()));
serialize(ar, payload.data);
}
size_t deserialize(InputArchive& ar, Payload& payload)
{
size_t size = sizeof(PayloadType);
PayloadType type;
deserialize(ar, type);
payload.type = type;
static const std::size_t data_length_limit = 4096;
const auto data_length = deserialize_length(ar);
if (data_length <= data_length_limit) {
size += length_coding_size(data_length);
size += data_length;
ByteBuffer buf(data_length);
ar.load_binary(buf.data(), buf.size());
payload.data = CohesivePacket(std::move(buf), OsiLayer::Network);
} else {
ar.fail(InputArchive::ErrorCode::ExcessiveLength);
}
return size;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,66 @@
#ifndef PAYLOAD_HPP_R8IXQBSL
#define PAYLOAD_HPP_R8IXQBSL
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/net/packet.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
/// PayloadType specified in TS 103 097 v1.2.1, section 5.3
enum class PayloadType : uint8_t
{
Unsecured = 0,
Signed = 1,
Encrypted = 2,
Signed_External = 3,
Signed_And_Encrypted = 4,
};
/// Payload specified in TS 103 097 v1.2.1, section 5.2
struct Payload
{
PayloadType type;
PacketVariant data;
};
/**
* \brief Determines PayloadType to a given Payload
* \param payload
* \return type
*/
PayloadType get_type(const Payload&);
/**
* \brief Calculates size of Payload
* \param payload
* \return number of octets needed to serialize the Payload
*/
size_t get_size(const Payload&);
/**
* \brief Serializes Payload into a binary archive
* \param ar to serialize in
* \param payload to serialize
*/
void serialize(OutputArchive& ar, const Payload&);
/**
* \brief Deserializes Payload from a binary archive
* \param ar with serialized Payload at the beginning
* \param payload to deserialize
* \return size of the deserialized payload
*/
size_t deserialize(InputArchive& ar, Payload&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* PAYLOAD_HPP_R8IXQBSL */
@@ -0,0 +1,121 @@
#include <vanetza/common/serialization.hpp>
#include <vanetza/security/persistence.hpp>
#include <vanetza/security/v2/persistence.hpp>
#include <boost/variant/get.hpp>
#include <algorithm>
#include <fstream>
#include <stdexcept>
#ifdef VANETZA_WITH_OPENSSL
#include <vanetza/security/backend_openssl.hpp>
#endif
#ifdef VANETZA_WITH_CRYPTOPP
#include <vanetza/security/backend_cryptopp.hpp>
#endif
namespace vanetza
{
namespace security
{
namespace v2
{
ecdsa256::KeyPair load_private_key_from_file(const std::string& key_path)
{
const PrivateKey private_key = load_private_key_from_der_file(key_path);
if (private_key.type != KeyType::NistP256) {
// ETSI TS 103 097 v1.2.1 only defines NIST P-256 keys for v2 security.
throw std::runtime_error("v2 private key must use the NIST P-256 curve: " + key_path);
}
#if defined(VANETZA_WITH_OPENSSL)
const security::PublicKey public_key = openssl::derive_public_key(private_key);
#elif defined(VANETZA_WITH_CRYPTOPP)
const security::PublicKey public_key = cryptopp::derive_public_key(private_key);
#else
# warning "no crypto backend available for v2 private key loading"
const security::PublicKey public_key;
#endif
ecdsa256::KeyPair key_pair;
std::copy(private_key.key.begin(), private_key.key.end(), key_pair.private_key.key.begin());
std::copy(public_key.x.begin(), public_key.x.end(), key_pair.public_key.x.begin());
std::copy(public_key.y.begin(), public_key.y.end(), key_pair.public_key.y.begin());
return key_pair;
}
bool save_private_key_pkcs8_der(std::ostream& os, const ecdsa256::KeyPair& key_pair)
{
// PKCS#8 PrivateKeyInfo wrapping SEC 1 ECPrivateKey for secp256r1
static const uint8_t header[] = {
0x30, 0x81, 0x87, /*< SEQUENCE, length 135 */
0x02, 0x01, 0x00, /*< INTEGER 0 (version) */
0x30, 0x13, /*< SEQUENCE (AlgorithmIdentifier) */
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, /*< OID 1.2.840.10045.2.1 (ecPublicKey) */
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, /*< OID 1.2.840.10045.3.1.7 (secp256r1) */
0x04, 0x6d, /*< OCTET STRING, length 109 */
0x30, 0x6b, /*< SEQUENCE (ECPrivateKey), length 107 */
0x02, 0x01, 0x01, /*< INTEGER 1 (version) */
0x04, 0x20, /*< OCTET STRING, length 32 */
};
static const uint8_t pub_header[] = {
0xa1, 0x44, /*< [1] CONSTRUCTED, length 68 */
0x03, 0x42, /*< BIT STRING, length 66 */
0x00, /*< 0 unused bits */
0x04, /*< uncompressed point (x, y) */
};
os.write(reinterpret_cast<const char*>(header), sizeof(header));
os.write(reinterpret_cast<const char*>(key_pair.private_key.key.data()), key_pair.private_key.key.size());
os.write(reinterpret_cast<const char*>(pub_header), sizeof(pub_header));
os.write(reinterpret_cast<const char*>(key_pair.public_key.x.data()), key_pair.public_key.x.size());
os.write(reinterpret_cast<const char*>(key_pair.public_key.y.data()), key_pair.public_key.y.size());
return os.good();
}
PublicKey load_public_key_from_file(const std::string& key_path)
{
PublicKey public_key;
std::ifstream key_src;
key_src.open(key_path, std::ios::in | std::ios::binary);
vanetza::InputArchive key_archive(key_src);
deserialize(key_archive, public_key);
return public_key;
}
void save_public_key_to_file(const std::string& key_path, const PublicKey& public_key)
{
std::ofstream dest;
dest.open(key_path.c_str(), std::ios::out | std::ios::binary);
OutputArchive archive(dest);
serialize(archive, public_key);
}
Certificate load_certificate_from_file(const std::string& certificate_path)
{
Certificate certificate;
std::ifstream certificate_src;
certificate_src.open(certificate_path, std::ios::in | std::ios::binary);
vanetza::InputArchive certificate_archive(certificate_src);
deserialize(certificate_archive, certificate);
return certificate;
}
void save_certificate_to_file(const std::string& certificate_path, const Certificate& certificate)
{
std::ofstream dest;
dest.open(certificate_path.c_str(), std::ios::out | std::ios::binary);
OutputArchive archive(dest);
serialize(archive, certificate);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,63 @@
#ifndef VANETZA_SECURITY_PERSISTENCE_HPP
#define VANETZA_SECURITY_PERSISTENCE_HPP
#include <vanetza/security/ecdsa256.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <iosfwd>
namespace vanetza
{
namespace security
{
namespace v2
{
/**
* \brief Loads a private key from a file
* \param key_path file to load the key from
* \return loaded key
*/
ecdsa256::KeyPair load_private_key_from_file(const std::string& key_path);
/**
* \brief Save a private key pair to a stream in PKCS#8 DER format (secp256r1)
* \param os destination stream
* \param key_pair key pair to be stored
* \return true if successfully written
*/
bool save_private_key_pkcs8_der(std::ostream& os, const ecdsa256::KeyPair& key_pair);
/**
* \brief Loads a public key from a file
* \param key_path file to load the key from
* \return loaded key
*/
PublicKey load_public_key_from_file(const std::string& key_path);
/**
* \brief Saves a public key to a file
* \param key_path file to save the key to
* \param public_key key to save
*/
void save_public_key_to_file(const std::string& key_path, const PublicKey& public_key);
/**
* \brief Loads a certificate from a file
* \param certificate_path file to load the certificate from
* \return loaded certificate
*/
Certificate load_certificate_from_file(const std::string& certificate_path);
/**
* \brief Saves a certificate to a file
* \param certificate_path file to save the certificate to
* \param certificate certificate to save
*/
void save_certificate_to_file(const std::string& certificate_path, const Certificate& certificate);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* VANETZA_SECURITY_PERSISTENCE_HPP */
@@ -0,0 +1,137 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
PublicKeyAlgorithm get_type(const PublicKey& key)
{
struct public_key_visitor : public boost::static_visitor<PublicKeyAlgorithm>
{
PublicKeyAlgorithm operator()(const ecdsa_nistp256_with_sha256&)
{
return PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256;
}
PublicKeyAlgorithm operator()(const ecies_nistp256&)
{
return PublicKeyAlgorithm::ECIES_NISTP256;
}
};
public_key_visitor visit;
return boost::apply_visitor(visit, key);
}
void serialize(OutputArchive& ar, const PublicKey& key)
{
struct public_key_visitor : public boost::static_visitor<>
{
public_key_visitor(OutputArchive& ar, PublicKeyAlgorithm algo) :
m_archive(ar), m_algo(algo)
{
}
void operator()(const ecdsa_nistp256_with_sha256& ecdsa)
{
serialize(m_archive, ecdsa.public_key, m_algo);
}
void operator()(const ecies_nistp256& ecies)
{
serialize(m_archive, ecies.supported_symm_alg);
serialize(m_archive, ecies.public_key, m_algo);
}
OutputArchive& m_archive;
PublicKeyAlgorithm m_algo;
};
PublicKeyAlgorithm type = get_type(key);
serialize(ar, type);
public_key_visitor visit(ar, type);
boost::apply_visitor(visit, key);
}
std::size_t field_size(PublicKeyAlgorithm algo)
{
size_t size = 0;
switch (algo) {
case PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256:
size = 32;
break;
case PublicKeyAlgorithm::ECIES_NISTP256:
size = 32;
break;
}
return size;
}
std::size_t field_size(SymmetricAlgorithm algo)
{
size_t size = 0;
switch (algo) {
case SymmetricAlgorithm::AES128_CCM:
size = 16;
break;
default:
throw deserialization_error("Unknown SymmetricAlgorithm");
break;
}
return size;
}
size_t deserialize(InputArchive& ar, PublicKey& key)
{
PublicKeyAlgorithm type;
deserialize(ar, type);
switch (type) {
case PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256: {
ecdsa_nistp256_with_sha256 ecdsa;
deserialize(ar, ecdsa.public_key, PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
key = ecdsa;
break;
}
case PublicKeyAlgorithm::ECIES_NISTP256: {
ecies_nistp256 ecies;
deserialize(ar, ecies.supported_symm_alg);
deserialize(ar, ecies.public_key, PublicKeyAlgorithm::ECIES_NISTP256);
key = ecies;
break;
}
default:
throw deserialization_error("Unknown PublicKeyAlgorithm");
break;
}
return get_size(key);
}
size_t get_size(const PublicKey& key)
{
size_t size = sizeof(PublicKeyAlgorithm);
struct publicKey_visitor : public boost::static_visitor<size_t>
{
size_t operator()(ecdsa_nistp256_with_sha256 key)
{
return get_size(key.public_key);
}
size_t operator()(ecies_nistp256 key)
{
return get_size(key.public_key) + sizeof(key.supported_symm_alg);
}
};
publicKey_visitor visit;
size += boost::apply_visitor(visit, key);
return size;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,90 @@
#ifndef PUBLIC_KEY_HPP_DRZFSERF
#define PUBLIC_KEY_HPP_DRZFSERF
#include <vanetza/security/v2/ecc_point.hpp>
#include <boost/variant/variant.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/// SymmetricAlgorithm specified in TS 103 097 v1.2.1, section 4.2.3
enum class SymmetricAlgorithm : uint8_t
{
AES128_CCM = 0
};
/// PublicKeyAlgorithm specified in TS 103 097 v1.2.1, section 4.2.2
enum class PublicKeyAlgorithm : uint8_t
{
ECDSA_NISTP256_With_SHA256 = 0,
ECIES_NISTP256 = 1
};
/// ecdsa_nistp256_with_sha256 specified in TS 103 097 v1.2.1, section 4.2.4
struct ecdsa_nistp256_with_sha256
{
EccPoint public_key;
};
/// ecies_nistp256 specified in TS 103 097 v1.2.1, section 4.2.4
struct ecies_nistp256
{
SymmetricAlgorithm supported_symm_alg;
EccPoint public_key;
};
/// Profile specified in TS 103 097 v1.2.1, section 4.2.4
using PublicKey = boost::variant<ecdsa_nistp256_with_sha256, ecies_nistp256>;
/**
* \brief Determines PublicKeyAlgorithm to a given PublicKey
* \param public_key
* \return algorithm type
*/
PublicKeyAlgorithm get_type(const PublicKey&);
/**
* \brief Calculates size of a PublicKey
* \param public_key
* \return number of octets needed to serialize the PublicKey
*/
size_t get_size(const PublicKey&);
/**
* \brief Deserializes a PublicKey from a binary archive
* \param ar with a serialized PublicKey at the beginning
* \param public_key to save deserialized values in
* \return size of the deserialized publicKey
*/
size_t deserialize(InputArchive&, PublicKey&);
/**
* \brief Serializes a PublicKey into a binary archive
* \param ar to serialize in
* \param public_key to serialize
*/
void serialize(OutputArchive&, const PublicKey&);
/**
* \brief Determines field size related to algorithm
* \param public_key_algorithm
* \return required buffer size for related fields
* */
std::size_t field_size(PublicKeyAlgorithm);
/**
* \brief Determines field size related to algorithm
* \param symmetric_algorithm
* \return required buffer size for related fields
*/
std::size_t field_size(SymmetricAlgorithm);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* PUBLIC_KEY_HPP_DRZFSERF */
@@ -0,0 +1,156 @@
#include <vanetza/security/v2/recipient_info.hpp>
#include <vanetza/security/v2/length_coding.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/variant/apply_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
PublicKeyAlgorithm get_type(const Key& key)
{
struct key_visitor : public boost::static_visitor<PublicKeyAlgorithm>
{
PublicKeyAlgorithm operator()(const EciesEncryptedKey&)
{
return PublicKeyAlgorithm::ECIES_NISTP256;
}
PublicKeyAlgorithm operator()(const OpaqueKey&)
{
// TODO: could be anything except ECIES_NISTP256
return PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256;
}
};
key_visitor visitor;
return boost::apply_visitor(visitor, key);
}
PublicKeyAlgorithm RecipientInfo::pk_encryption() const
{
return get_type(enc_key);
}
size_t get_size(const RecipientInfo& info)
{
size_t size = info.cert_id.size();
size += sizeof(PublicKeyAlgorithm);
struct RecipientInfoKey_visitor : public boost::static_visitor<size_t>
{
size_t operator()(const EciesEncryptedKey& key)
{
return key.c.size() + key.t.size() + get_size(key.v);
}
size_t operator()(const OpaqueKey& key)
{
return length_coding_size(key.data.size()) + key.data.size();
}
};
RecipientInfoKey_visitor visit;
size += boost::apply_visitor(visit, info.enc_key);
return size;
}
void serialize(OutputArchive& ar, const RecipientInfo& info, SymmetricAlgorithm sym_algo)
{
struct key_visitor : public boost::static_visitor<>
{
key_visitor(OutputArchive& ar, SymmetricAlgorithm sym_algo, PublicKeyAlgorithm pk_algo) :
m_archive(ar), m_sym_algo(sym_algo), m_pk_algo(pk_algo)
{
}
void operator()(const EciesEncryptedKey& key)
{
assert(key.c.size() == field_size(m_sym_algo));
serialize(m_archive, key.v, m_pk_algo);
for (auto& byte : key.c) {
m_archive << byte;
}
for (auto& byte : key.t) {
m_archive << byte;
}
}
void operator()(const OpaqueKey& key)
{
serialize_length(m_archive, key.data.size());
for (auto byte : key.data) {
m_archive << byte;
}
}
OutputArchive& m_archive;
SymmetricAlgorithm m_sym_algo;
PublicKeyAlgorithm m_pk_algo;
};
for (auto& byte : info.cert_id) {
ar << byte;
}
const PublicKeyAlgorithm pk_algo = info.pk_encryption();
serialize(ar, pk_algo);
key_visitor visitor(ar, sym_algo, pk_algo);
boost::apply_visitor(visitor, info.enc_key);
}
EciesEncryptedKey deserialize_ecies(InputArchive& ar, const SymmetricAlgorithm& symAlgo)
{
EciesEncryptedKey ecies;
deserialize(ar, ecies.v, PublicKeyAlgorithm::ECIES_NISTP256);
const size_t fieldSize = field_size(symAlgo);
for (size_t c = 0; c < fieldSize; ++c) {
uint8_t tmp;
ar >> tmp;
ecies.c.push_back(tmp);
}
for (size_t c = 0; c < ecies.t.size(); ++c) {
uint8_t tmp;
ar >> tmp;
ecies.t[c] = tmp;
}
return ecies;
}
OpaqueKey deserialize_opaque(InputArchive& ar)
{
static const std::uintmax_t length_limit = 512;
const std::uintmax_t length = deserialize_length(ar);
if (length <= length_limit) {
ByteBuffer opaque(length);
for (std::uintmax_t i = 0; i < length; ++i) {
ar >> opaque[i];
}
return OpaqueKey { std::move(opaque) };
} else {
return OpaqueKey {};
}
}
size_t deserialize(InputArchive& ar, RecipientInfo& info, const SymmetricAlgorithm& symAlgo)
{
for (size_t c = 0; c < info.cert_id.size(); ++c) {
ar >> info.cert_id[c];
}
PublicKeyAlgorithm algo;
deserialize(ar, algo);
switch (algo) {
case PublicKeyAlgorithm::ECIES_NISTP256:
info.enc_key = deserialize_ecies(ar, symAlgo);
break;
default:
info.enc_key = deserialize_opaque(ar);
break;
}
return get_size(info);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,76 @@
#ifndef RECIPIENT_INFO_HPP_IENLXEUN
#define RECIPIENT_INFO_HPP_IENLXEUN
#include <vanetza/security/v2/basic_elements.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <boost/variant/variant.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/// EciesEncryptedKey specified in TS 103 097 v1.2.1, section 5.9
struct EciesEncryptedKey
{
EccPoint v;
ByteBuffer c;
std::array<uint8_t, 16> t;
};
/// OpaqueKey specified in TS 103 097 v1.2.1, section 5.8
struct OpaqueKey
{
ByteBuffer data;
};
/// Key specified in TS 103 097 v1.2.1, section 5.8 (in RecipientInfo)
typedef boost::variant<EciesEncryptedKey, OpaqueKey> Key;
/// RecipientInfo specified in TS 103 097 v1.2.1, section 5.8
struct RecipientInfo
{
HashedId8 cert_id;
Key enc_key;
PublicKeyAlgorithm pk_encryption() const;
};
/**
* \brief Determines applicable PublicKeyAlgorithm
* \param key Algorithm has to fit this kind of key
* \return PublicKeyAlgorithm
*/
PublicKeyAlgorithm get_type(const Key&);
/**
* Calculates size of a RecipientInfo
* \param info
* \return number of octets needed to serialize the RecipientInfo
*/
size_t get_size(const RecipientInfo&);
/**
* \brief Serializes a RecipientInfo into a binary archive
* \param ar Destination of serialized object
* \param info RecipientInfo to serialize
* \param sym Applicable symmetric algorithm
*/
void serialize(OutputArchive&, const RecipientInfo&, SymmetricAlgorithm);
/**
* \brief Deserialize a RecipientInfo
* \param ar Input starting with serialized RecipientInfo
* \param info Deserialized RecipientInfo
* \param sym Symmetric algorithm required to deserialize encrypted key
* \return size of the deserialized RecipientInfo in bytes
*/
size_t deserialize(InputArchive&, RecipientInfo&, const SymmetricAlgorithm&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* RECIPIENT_INFO_HPP_IENLXEUN */
@@ -0,0 +1,742 @@
#include <vanetza/common/annotation.hpp>
#include <vanetza/common/serialization.hpp>
#include <vanetza/geodesy/geodesy.hpp>
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/region.hpp>
#include <vanetza/units/angle.hpp>
#include <vanetza/units/length.hpp>
#include <boost/algorithm/clamp.hpp>
#include <boost/units/cmath.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <cmath>
namespace vanetza
{
namespace security
{
namespace v2
{
const ThreeDLocation::Elevation ThreeDLocation::unknown_elevation {{ 0xF0, 0x00 }};
const ThreeDLocation::Elevation ThreeDLocation::min_elevation {{ 0xF0, 0x01 }};
const ThreeDLocation::Elevation ThreeDLocation::max_elevation {{ 0xEF, 0xFF }};
RegionType get_type(const GeographicRegion& reg)
{
struct geograpical_region_visitor : public boost::static_visitor<RegionType>
{
RegionType operator()(const NoneRegion&)
{
return RegionType::None;
}
RegionType operator()(const CircularRegion&)
{
return RegionType::Circle;
}
RegionType operator()(const std::list<RectangularRegion>&)
{
return RegionType::Rectangle;
}
RegionType operator()(const PolygonalRegion&)
{
return RegionType::Polygon;
}
RegionType operator()(const IdentifiedRegion&)
{
return RegionType::ID;
}
};
geograpical_region_visitor visit;
return boost::apply_visitor(visit, reg);
}
bool TwoDLocation::operator==(const TwoDLocation& other) const
{
return this->latitude == other.latitude && this->longitude == other.longitude;
}
bool TwoDLocation::operator!=(const TwoDLocation& other) const
{
return !(*this == other);
}
bool ThreeDLocation::operator==(const ThreeDLocation& other) const
{
return this->latitude == other.latitude && this->longitude == other.longitude && this->elevation == other.elevation;
}
bool ThreeDLocation::operator!=(const ThreeDLocation& other) const
{
return !(*this == other);
}
bool NoneRegion::operator==(const NoneRegion&) const
{
return true;
}
bool NoneRegion::operator!=(const NoneRegion& other) const
{
return !(*this == other);
}
bool CircularRegion::operator==(const CircularRegion& other) const
{
return this->center == other.center && this->radius == other.radius;
}
bool CircularRegion::operator!=(const CircularRegion& other) const
{
return !(*this == other);
}
bool RectangularRegion::operator==(const RectangularRegion& other) const
{
return this->northwest == other.northwest && this->southeast == other.southeast;
}
bool RectangularRegion::operator!=(const RectangularRegion& other) const
{
return !(*this == other);
}
bool IdentifiedRegion::operator==(const IdentifiedRegion& other) const
{
return this->region_dictionary == other.region_dictionary
&& this->region_identifier == other.region_identifier
&& this->local_region == other.local_region;
}
bool IdentifiedRegion::operator!=(const IdentifiedRegion& other) const
{
return !(*this == other);
}
size_t get_size(const TwoDLocation& loc)
{
size_t size = 0;
size += sizeof(loc.latitude);
size += sizeof(loc.longitude);
return size;
}
size_t get_size(const ThreeDLocation& loc)
{
size_t size = 0;
size += sizeof(loc.latitude);
size += sizeof(loc.longitude);
size += loc.elevation.size();
return size;
}
size_t get_size(const CircularRegion& reg)
{
size_t size = 0;
size += get_size(reg.center);
size += sizeof(reg.radius);
return size;
}
size_t get_size(const RectangularRegion& reg)
{
size_t size = 0;
size += get_size(reg.northwest);
size += get_size(reg.southeast);
return size;
}
size_t get_size(const std::list<CircularRegion>& list)
{
size_t size = 0;
for (auto& circularRegion : list) {
size += get_size(circularRegion.center);
size += sizeof(circularRegion.radius);
}
return size;
}
size_t get_size(const std::list<RectangularRegion>& list)
{
size_t size = 0;
for (auto& rectangularRegion : list) {
size += get_size(rectangularRegion.northwest);
size += get_size(rectangularRegion.southeast);
}
return size;
}
size_t get_size(const PolygonalRegion& reg)
{
size_t size = 0;
for (auto& twoDLocation : reg) {
size += sizeof(twoDLocation.latitude);
size += sizeof(twoDLocation.longitude);
}
return size;
}
size_t get_size(const IdentifiedRegion& reg)
{
size_t size = 0;
size += sizeof(reg.region_dictionary);
size += sizeof(reg.region_identifier);
size += get_size(reg.local_region);
return size;
}
size_t get_size(const GeographicRegion& reg)
{
size_t size = sizeof(RegionType);
struct geograpical_region_visitor : public boost::static_visitor<>
{
void operator()(const NoneRegion&)
{
m_size = 0;
}
void operator()(const CircularRegion& reg)
{
m_size = get_size(reg);
}
void operator()(const std::list<RectangularRegion>& reg)
{
m_size = get_size(reg);
m_size += length_coding_size(m_size);
}
void operator()(const PolygonalRegion& reg)
{
m_size = get_size(reg);
m_size += length_coding_size(m_size);
}
void operator()(const IdentifiedRegion& reg)
{
m_size = get_size(reg);
}
size_t m_size;
};
geograpical_region_visitor visit;
boost::apply_visitor(visit, reg);
size += visit.m_size;
return size;
}
void serialize(OutputArchive& ar, const TwoDLocation& loc)
{
serialize(ar, loc.latitude);
serialize(ar, loc.longitude);
}
void serialize(OutputArchive& ar, const ThreeDLocation& loc)
{
serialize(ar, loc.latitude);
serialize(ar, loc.longitude);
ar << loc.elevation[0];
ar << loc.elevation[1];
}
void serialize(OutputArchive& ar, const CircularRegion& reg)
{
serialize(ar, reg.center);
serialize(ar, reg.radius);
}
void serialize(OutputArchive& ar, const RectangularRegion& reg)
{
serialize(ar, reg.northwest);
serialize(ar, reg.southeast);
}
void serialize(OutputArchive& ar, const std::list<RectangularRegion>& list)
{
size_t size;
size = get_size(list);
serialize_length(ar, size);
for (auto& rectangularRegion : list) {
serialize(ar, rectangularRegion);
}
}
void serialize(OutputArchive& ar, const PolygonalRegion& reg)
{
size_t size;
size = get_size(reg);
serialize_length(ar, size);
for (auto& twoDLocation : reg) {
serialize(ar, twoDLocation);
}
}
void serialize(OutputArchive& ar, const IdentifiedRegion& reg)
{
serialize(ar, reg.region_dictionary);
serialize(ar, host_cast(reg.region_identifier));
serialize(ar, reg.local_region);
}
void serialize(OutputArchive& ar, const GeographicRegion& reg)
{
struct geograpical_region_visitor : public boost::static_visitor<>
{
geograpical_region_visitor(OutputArchive& ar) :
m_archive(ar)
{
}
void operator()(const NoneRegion&)
{
// nothing to do
}
void operator()(const CircularRegion& reg)
{
serialize(m_archive, reg);
}
void operator()(const std::list<RectangularRegion>& reg)
{
serialize(m_archive, reg);
}
void operator()(const PolygonalRegion& reg)
{
serialize(m_archive, reg);
}
void operator()(const IdentifiedRegion& reg)
{
serialize(m_archive, reg);
}
OutputArchive& m_archive;
};
RegionType type = get_type(reg);
serialize(ar, type);
geograpical_region_visitor visit(ar);
boost::apply_visitor(visit, reg);
}
size_t deserialize(InputArchive& ar, TwoDLocation& loc)
{
deserialize(ar, loc.latitude);
deserialize(ar, loc.longitude);
return get_size(loc);
}
size_t deserialize(InputArchive& ar, ThreeDLocation& loc)
{
deserialize(ar, loc.latitude);
deserialize(ar, loc.longitude);
ar >> loc.elevation[0];
ar >> loc.elevation[1];
return get_size(loc);
}
size_t deserialize(InputArchive& ar, CircularRegion& reg)
{
size_t size = 0;
size += deserialize(ar, reg.center);
deserialize(ar, reg.radius);
size += sizeof(reg.radius);
return size;
}
size_t deserialize(InputArchive& ar, std::list<RectangularRegion>& list)
{
size_t size, ret_size;
size = deserialize_length(ar);
ret_size = size;
while (size > 0) {
RectangularRegion reg;
size -= deserialize(ar, reg.northwest);
size -= deserialize(ar, reg.southeast);
list.push_back(reg);
}
return ret_size;
}
size_t deserialize(InputArchive& ar, PolygonalRegion& reg)
{
size_t size, ret_size;
size = deserialize_length(ar);
ret_size = size;
while (size > 0) {
TwoDLocation loc;
size -= deserialize(ar, loc);
reg.push_back(loc);
}
return ret_size;
}
size_t deserialize(InputArchive& ar, IdentifiedRegion& reg)
{
size_t size = 0;
deserialize(ar, reg.region_dictionary);
size += sizeof(RegionDictionary);
deserialize(ar, reg.region_identifier);
size += sizeof(reg.region_identifier);
deserialize(ar, reg.local_region);
size += get_size(reg.local_region);
return size;
}
size_t deserialize(InputArchive& ar, GeographicRegion& reg)
{
RegionType type;
deserialize(ar, type);
size_t size = sizeof(RegionType);
switch (type) {
case RegionType::None:
NoneRegion none;
reg = none;
break;
case RegionType::Circle: {
CircularRegion circle;
size += deserialize(ar, circle);
reg = circle;
break;
}
case RegionType::Rectangle: {
std::list<RectangularRegion> list;
size += deserialize(ar, list);
size += length_coding_size(size);
reg = list;
break;
}
case RegionType::Polygon: {
PolygonalRegion polygon;
size += deserialize(ar, polygon);
size += length_coding_size(size);
reg = polygon;
break;
}
case RegionType::ID: {
IdentifiedRegion id;
size += deserialize(ar, id);
reg = id;
break;
}
default: {
throw deserialization_error("Unknown RegionType");
break;
}
}
return (size);
}
bool is_within(const TwoDLocation& position, const GeographicRegion& reg)
{
struct geograpical_region_visitor : public boost::static_visitor<bool>
{
geograpical_region_visitor(const TwoDLocation& position) :
m_position(position)
{
}
bool operator()(const NoneRegion&)
{
return true;
}
bool operator()(const CircularRegion& reg)
{
return is_within(m_position, reg);
}
bool operator()(const std::list<RectangularRegion>& reg)
{
return is_within(m_position, reg);
}
bool operator()(const PolygonalRegion& reg)
{
return is_within(m_position, reg);
}
bool operator()(const IdentifiedRegion& reg)
{
return is_within(m_position, reg);
}
const TwoDLocation& m_position;
};
geograpical_region_visitor visit(position);
return boost::apply_visitor(visit, reg);
}
bool is_within(const TwoDLocation& position, const CircularRegion& circular)
{
geodesy::GeodeticPosition pos(units::GeoAngle{position.latitude}, units::GeoAngle{position.longitude});
geodesy::GeodeticPosition center(units::GeoAngle{circular.center.latitude}, units::GeoAngle{circular.center.longitude});
auto dist = geodesy::distance(pos, center);
return dist <= circular.radius;
}
bool is_within(const TwoDLocation& position, const std::list<RectangularRegion>& rectangles)
{
static const unsigned max_rectangles = 6; /*< see TS 103 097 v1.2.1, section 4.2.20 */
if (rectangles.size() > max_rectangles) {
return false;
}
return std::any_of(rectangles.begin(), rectangles.end(),
[&position](const RectangularRegion& rect) { return is_within(position, rect); });
}
bool is_within(const TwoDLocation& position, const RectangularRegion& rectangle)
{
// basic coordinate checks according to TS 103 097 v1.2.1, 4.2.23 and IEEE 1609.2-2016, 6.4.20
// - northwest is truly north of southeast (never equal)
// - northwest is truly west of southeast (never equal)
if (rectangle.northwest.latitude <= rectangle.southeast.latitude) {
return false;
} else if (rectangle.northwest.longitude >= rectangle.southeast.longitude) {
return false;
}
if (rectangle.northwest.latitude < position.latitude) {
return false; // position is north of rectangle
} else if (rectangle.northwest.longitude > position.longitude) {
return false; // position is west of rectangle
} else if (rectangle.southeast.latitude > position.latitude) {
return false; // position is south of rectangle
} else if (rectangle.southeast.longitude < position.longitude) {
return false; // position is east of rectangle
}
return true;
}
bool is_within(const TwoDLocation&, const PolygonalRegion&)
{
// TODO: Add support for polygonal region, see TS 103 097 v1.2.1, section 4.2.24
return false;
}
bool is_within(const TwoDLocation&, const IdentifiedRegion&)
{
// TODO: Add support for identified region, see TS 103 097 v1.2.1, section 4.2.25
return false;
}
bool is_within(const GeographicRegion& inner, const GeographicRegion& outer)
{
struct outer_geograpical_region_visitor : public boost::static_visitor<bool>
{
outer_geograpical_region_visitor(const GeographicRegion& inner) :
inner(inner)
{
}
bool operator()(const NoneRegion&)
{
return true;
}
bool operator()(const CircularRegion& outer)
{
return is_within(inner, outer);
}
bool operator()(const std::list<RectangularRegion>& outer)
{
return is_within(inner, outer);
}
bool operator()(const PolygonalRegion& outer)
{
return is_within(inner, outer);
}
bool operator()(const IdentifiedRegion& outer)
{
return is_within(inner, outer);
}
const GeographicRegion& inner;
};
outer_geograpical_region_visitor visit(inner);
return boost::apply_visitor(visit, outer);
}
bool is_within(const GeographicRegion& inner, const CircularRegion& outer)
{
struct inner_geograpical_region_visitor : public boost::static_visitor<bool>
{
inner_geograpical_region_visitor(const CircularRegion& outer) :
outer(outer)
{
}
bool operator()(const NoneRegion&)
{
return false;
}
bool operator()(const CircularRegion& inner)
{
if (inner == outer) {
return true;
}
geodesy::GeodeticPosition inner_pos(units::GeoAngle{inner.center.latitude}, units::GeoAngle{inner.center.longitude});
geodesy::GeodeticPosition outer_pos(units::GeoAngle{outer.center.latitude}, units::GeoAngle{outer.center.longitude});
auto center_dist = geodesy::distance(inner_pos, outer_pos);
return center_dist + inner.radius <= outer.radius;
}
bool operator()(const std::list<RectangularRegion>&)
{
// TODO: Implement check whether reactangles are within the circle
/* Note: The rectangles can be converted to a polygon and its implementation be reused then.
* Note: Checking whether all corners of a rectangle are within the circle is NOT enough!
* Example: The rectangle here is spanning the earth except for a small part within the circle.
* ________
* / \
* _____/__ __\_____
* | | | |
* _____\__| |__/____
* \_________/
*/
return false;
}
bool operator()(const PolygonalRegion&)
{
// TODO: Implement check whether a polygon is within the circle.
// Note: Same thoughts as for rectangles applies.
return false;
}
bool operator()(const IdentifiedRegion&)
{
// TODO: Implement check whether an identified region is within the circle.
// Note: The identified region can be converted to a polygon and its implementation be reused then.
// Note: Same thoughts as for rectangles applies.
return false;
}
const CircularRegion& outer;
};
inner_geograpical_region_visitor visit(outer);
return boost::apply_visitor(visit, inner);
}
bool is_within(const GeographicRegion& inner, const std::list<RectangularRegion>& outer)
{
// Note: The rectangles cover an area combined, there's no need for the inner shape to be within a single one!
// TODO: Implement check whether inner is within the set of rectangles
// Note: The rectangles can be converted to a polygon and its implementation be reused then.
// Note: Only exact matches are implemented for now.
struct inner_geograpical_region_visitor : public boost::static_visitor<bool>
{
inner_geograpical_region_visitor(const std::list<RectangularRegion>& outer) :
outer(outer)
{
}
bool operator()(const NoneRegion&)
{
return false;
}
bool operator()(const CircularRegion&)
{
// TODO: Implement.
return false;
}
bool operator()(const std::list<RectangularRegion>& inner)
{
if (inner == outer) {
return true;
}
// TODO: Implement.
return false;
}
bool operator()(const PolygonalRegion&)
{
// TODO: Implement.
return false;
}
bool operator()(const IdentifiedRegion&)
{
// TODO: Implement.
return false;
}
const std::list<RectangularRegion>& outer;
};
inner_geograpical_region_visitor visit(outer);
return boost::apply_visitor(visit, inner);
}
bool is_within(const GeographicRegion& inner, const PolygonalRegion& outer)
{
// TODO: Implement check whether inner is within the polygon
mark_unused(inner);
mark_unused(outer);
return false;
}
bool is_within(const GeographicRegion& inner, const IdentifiedRegion& outer)
{
// TODO: Implement check whether inner is within the polygon identified by the outer region
// Note: The identified region can be converted to a polygon and its implementation be reused then.
mark_unused(inner);
mark_unused(outer);
return false;
}
ThreeDLocation::Elevation to_elevation(units::Length altitude)
{
using boost::units::isnan;
// Default to special value for NaN elevation
ThreeDLocation::Elevation elevation { ThreeDLocation::unknown_elevation };
if (!isnan(altitude)) {
using boost::algorithm::clamp;
// see TS 103 097 v1.2.1, section 4.2.19
double altitude_dm = std::round(10.0 * (altitude / vanetza::units::si::meter));
if (altitude_dm >= 0.0) {
altitude_dm = clamp(altitude_dm, 0.0, 61439.0);
auto altitude_int = static_cast<std::uint16_t>(altitude_dm);
elevation[0] = altitude_int >> 8;
elevation[1] = altitude_int & 0xFF;
} else {
altitude_dm = clamp(altitude_dm, -4095.0, -1.0);
auto altitude_int = static_cast<std::int16_t>(altitude_dm);
elevation[0] = altitude_int >> 8 | 0xF0;
elevation[1] = altitude_int & 0xFF;
}
}
return elevation;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,413 @@
#ifndef REGION_HPP_NUISLPMU
#define REGION_HPP_NUISLPMU
#include <vanetza/geonet/units.hpp>
#include <vanetza/security/v2/int_x.hpp>
#include <vanetza/units/angle.hpp>
#include <vanetza/units/length.hpp>
#include <boost/variant/variant.hpp>
#include <array>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
/// ThreeDLocation specified in TS 103 097 v1.2.1, section 4.2.19
struct ThreeDLocation
{
using Elevation = std::array<uint8_t, 2>;
static const Elevation unknown_elevation;
static const Elevation min_elevation;
static const Elevation max_elevation;
ThreeDLocation() = default;
ThreeDLocation(geonet::geo_angle_i32t latitude, geonet::geo_angle_i32t longitude) :
latitude(latitude), longitude(longitude), elevation(unknown_elevation) {}
ThreeDLocation(units::GeoAngle latitude, units::GeoAngle longitude) :
latitude(latitude), longitude(longitude), elevation(unknown_elevation) {}
ThreeDLocation(geonet::geo_angle_i32t latitude, geonet::geo_angle_i32t longitude, Elevation elevation) :
latitude(latitude), longitude(longitude), elevation(elevation) {}
ThreeDLocation(units::GeoAngle latitude, units::GeoAngle longitude, Elevation elevation) :
latitude(latitude), longitude(longitude), elevation(elevation) {}
geonet::geo_angle_i32t latitude;
geonet::geo_angle_i32t longitude;
Elevation elevation;
bool operator==(const ThreeDLocation&) const;
bool operator!=(const ThreeDLocation&) const;
};
/// TwoDLocation specified in TS 103 097 v1.2.1, section 4.2.18
struct TwoDLocation
{
TwoDLocation() = default;
TwoDLocation(geonet::geo_angle_i32t latitude, geonet::geo_angle_i32t longitude) :
latitude(latitude), longitude(longitude) {}
TwoDLocation(units::GeoAngle latitude, units::GeoAngle longitude) :
latitude(latitude), longitude(longitude) {}
explicit TwoDLocation(const ThreeDLocation& threeD) :
latitude(threeD.latitude), longitude(threeD.longitude) {}
geonet::geo_angle_i32t latitude;
geonet::geo_angle_i32t longitude;
bool operator==(const TwoDLocation&) const;
bool operator!=(const TwoDLocation&) const;
};
/// Specified in TS 103 097 v1.2.1, section 4.2.20
struct NoneRegion
{
// empty
bool operator==(const NoneRegion&) const;
bool operator!=(const NoneRegion&) const;
};
/// CircularRegion specified in TS 103 097 v1.2.1, section 4.2.22
struct CircularRegion
{
CircularRegion() = default;
CircularRegion(const TwoDLocation& center, geonet::distance_u16t radius) :
center(center), radius(radius) {}
CircularRegion(const TwoDLocation& center, units::Length radius) :
center(center), radius(radius) {}
TwoDLocation center;
geonet::distance_u16t radius;
bool operator==(const CircularRegion&) const;
bool operator!=(const CircularRegion&) const;
};
/// RectangularRegion specified in TS 103 097 v1.2.1, section 4.2.23
struct RectangularRegion
{
TwoDLocation northwest;
TwoDLocation southeast;
bool operator==(const RectangularRegion&) const;
bool operator!=(const RectangularRegion&) const;
};
/// PolygonalRegion specified in TS 103 097 v1.2.1, section 4.2.24
using PolygonalRegion = std::list<TwoDLocation>;
/// RegionDictionary specified in TS 103 097 v1.2.1, section 4.2.26
enum class RegionDictionary : uint8_t
{
ISO_3166_1 = 0,
UN_Stats = 1,
};
/// IdentifiedRegion specified in TS 103 097 v1.2.1, section 4.2.25
struct IdentifiedRegion
{
RegionDictionary region_dictionary;
int16_t region_identifier;
IntX local_region;
bool operator==(const IdentifiedRegion&) const;
bool operator!=(const IdentifiedRegion&) const;
};
/// RegionType specified in TS 103 097 v1.2.1, section 4.2.21
enum class RegionType : uint8_t
{
None = 0, // nothing
Circle = 1, // CircularRegion
Rectangle = 2, // std::list<RectangularRegion>
Polygon = 3, // PolygonalRegion
ID = 4, // IdentifiedRegion
};
/// GeographicRegion specified in TS 103 097 v1.2.1, section 4.2.20
using GeographicRegion = boost::variant<
NoneRegion,
CircularRegion,
std::list<RectangularRegion>,
PolygonalRegion,
IdentifiedRegion
>;
/**
* \brief Determines RegionType of a GeographicRegion
* \param region
* \return RegionType
*/
RegionType get_type(const GeographicRegion&);
/**
* \brief Calculates size of a TwoDLocation
* \param loc
* \return number of octets needed to serialize the TwoDLocation
*/
size_t get_size(const TwoDLocation&);
/**
* \brief Calculates size of a ThreeDLocation
* \param log
* \return number of octets needed to serialize the ThreeDLocation
*/
size_t get_size(const ThreeDLocation&);
/**
* \brief Calculates size of a CircularRegion
* \param reg
* \return number of octets needed to serialize the CiruclarRegion
*/
size_t get_size(const CircularRegion&);
/**
* \brief Calculates size of a RectangularRegion
* \param reg
* \return number of octets needed to serialize the RectangularRegion
*/
size_t get_size(const RectangularRegion&);
/**
* \brief Calculates size of a list of CircularRegion
* \param list
* \return number of octets needed to serialize the list of CircularRegion
*/
size_t get_size(const std::list<CircularRegion>&);
/**
* \brief Calculates size of a list of RectangularRegion
* \param list
* \return number of octets needed to serialize the list of RectangularRegion
*/
size_t get_size(const std::list<RectangularRegion>&);
/**
* \brief Calculates size of a PolygonalRegion
* \param reg
* \return number of octets needed to serialize the PolygonalRegion
*/
size_t get_size(const PolygonalRegion&);
/**
* \brief Calculates size of a GeographicRegion
* \param reg
* \return number of octets needed to serialize the GeographicRegion
*/
size_t get_size(const GeographicRegion&);
/**
* \brief Serializes a TwoDLocation into a binary archive
* \param ar to serialize in
* \param loc to serialize
*/
void serialize(OutputArchive&, const TwoDLocation&);
/**
* \brief Serializes a ThreeDLocation into a binary archive
* \param ar to serialize in
* \param loc to serialize
*/
void serialize(OutputArchive&, const ThreeDLocation&);
/**
* \brief Serializes a CiruclarRegion into a binary archive
* \param ar to serialize in
* \param reg to serialize
*/
void serialize(OutputArchive&, const CircularRegion&);
/**
* \brief Serializes a RectangularRegion into a binary archive
* \param ar to serialize in
* \param reg to serialize
*/
void serialize(OutputArchive&, const RectangularRegion&);
/**
* \brief Serializes a list of RectangularRegions into a binary archive
* \param ar to serialize in
* \param list to serialize
*/
void serialize(OutputArchive&, const std::list<RectangularRegion>&);
/**
* \brief Serializes a PolygonalRegion into a binary archive
* \param ar to serialize in
* \param reg to serialize
*/
void serialize(OutputArchive&, const PolygonalRegion&);
/**
* \brief Serializes an IdentifiedRegion into a binary archive
* \param ar to serialize in
* \param reg to serialize
*/
void serialize(OutputArchive&, const IdentifiedRegion&);
/**
* \brief Serializes a GeographicRegion into a binary archive
* \param ar to serialize in
* \param reg to serialize
*/
void serialize(OutputArchive&, const GeographicRegion&);
/**
* \brief Deserializes a TwoDLocation from a binary archive
* \param ar with a serialized TwoDLocation at the beginning
* \param loc to deserialize
* \return size of the deserialized TwoDLocation
*/
size_t deserialize(InputArchive&, TwoDLocation&);
/**
* \brief Deserializes a ThreeDLocation from a binary archive
* \param ar with a serialized ThreeDLocation at the beginning
* \param loc to deserialize
* \return size of the deserialized ThreeDLocation
*/
size_t deserialize(InputArchive&, ThreeDLocation&);
/**
* \brief Deserializes a CircularRegion from a binary archive
* \param ar with a serialized CiruclarRegion at the beginning
* \param reg to deserialize
* \return size of the deserialized CiruclarRegion
*/
size_t deserialize(InputArchive&, CircularRegion&);
/**
* \brief Deserializes a list of RectangularRegions from a binary archive
* \param ar with a serialized RectangularRegion list at the beginning
* \param list to deserialize
* \return size of the deserialized list
*/
size_t deserialize(InputArchive&, std::list<RectangularRegion>&);
/**
* \brief Deserializes a PolygonalRegion from a binary archive
* \param ar with a serialized PolygonalRegion at the beginning
* \param reg to deserialize
* \return size of the deserialized PolygonalRegion
*/
size_t deserialize(InputArchive&, PolygonalRegion&);
/**
* \brief Deserializes an IdentifiedRegion from a binary archive
* \param ar with a serialized IdentifiedRegion at the beginning
* \param reg to deserialize
* \return size of the deserialized IdentifiedRegion
*/
size_t deserialize(InputArchive&, IdentifiedRegion&);
/**
* \brief Deserializes a GeographicRegion from a binary archive
* \param ar with a serialized GeographicRegion at the beginning
* \param reg to deserialize
* \return size of the deserialized GeographicRegion
*/
size_t deserialize(InputArchive&, GeographicRegion&);
/**
* \brief Check if position is within geographic region
* \param pos position
* \param r region
* \true if pos is within region
*/
bool is_within(const TwoDLocation&, const GeographicRegion&);
/**
* \brief Check if position is within circular region
* \param pos position
* \param c cicrular region
* \true if pos is within region
*/
bool is_within(const TwoDLocation&, const CircularRegion&);
/**
* \brief Check if position is within set of rectangular regions
* \param pos position
* \param r rectangular regions
* \true if pos is within region
*/
bool is_within(const TwoDLocation&, const std::list<RectangularRegion>&);
/**
* \brief Check if position is within rectangular region
* \param pos position
* \param r rectangular region
* \true if pos is within region
*/
bool is_within(const TwoDLocation&, const RectangularRegion&);
/**
* \brief Check if position is within polygonal region
* \param pos position
* \param c cicrular region
* \true if pos is within region
*/
bool is_within(const TwoDLocation&, const PolygonalRegion&);
/**
* \brief Check if position is within identified region
* \param pos position
* \param i identified region
* \true if pos is within region
*/
bool is_within(const TwoDLocation&, const IdentifiedRegion&);
/**
* \brief Check if a region is within another geographic region
* \param reg region
* \param r region
* \true if pos is within region
*/
bool is_within(const GeographicRegion&, const GeographicRegion&);
/**
* \brief Check if a region is within a circular region
* \param reg region
* \param c cicrular region
* \true if pos is within region
*/
bool is_within(const GeographicRegion&, const CircularRegion&);
/**
* \brief Check if a region is within a set of rectangular regions
* \param reg region
* \param r rectangular regions
* \true if pos is within region
*/
bool is_within(const GeographicRegion&, const std::list<RectangularRegion>&);
/**
* \brief Check if a region is within a polygonal region
* \param reg region
* \param c cicrular region
* \true if pos is within region
*/
bool is_within(const GeographicRegion&, const PolygonalRegion&);
/**
* \brief Check if a region is within an identified region
* \param reg region
* \param i identified region
* \true if pos is within region
*/
bool is_within(const GeographicRegion&, const IdentifiedRegion&);
/**
* \brief Convert WGS84 altitude to elevation
* \see TS 103 097 v1.2.1, section 4.2.19
* \param altitude altitude above ellipsoid (accepts NaN)
* \return encoded elevation
*/
ThreeDLocation::Elevation to_elevation(units::Length);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* REGION_HPP_NUISLPMU */
@@ -0,0 +1,138 @@
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/byte_buffer_sink.hpp>
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <vanetza/security/v2/secured_message.hpp>
#include <boost/iostreams/stream.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
HeaderField* SecuredMessage::header_field(HeaderFieldType type)
{
HeaderField* match = nullptr;
for (auto& field : header_fields) {
if (get_type(field) == type) {
match = &field;
break;
}
}
return match;
}
const HeaderField* SecuredMessage::header_field(HeaderFieldType type) const
{
const HeaderField* match = nullptr;
for (auto& field : header_fields) {
if (get_type(field) == type) {
match = &field;
break;
}
}
return match;
}
TrailerField* SecuredMessage::trailer_field(TrailerFieldType type)
{
TrailerField* match = nullptr;
for (auto& field : trailer_fields) {
if (get_type(field) == type) {
match = &field;
break;
}
}
return match;
}
const TrailerField* SecuredMessage::trailer_field(TrailerFieldType type) const
{
const TrailerField* match = nullptr;
for (auto& field : trailer_fields) {
if (get_type(field) == type) {
match = &field;
break;
}
}
return match;
}
size_t get_size(const SecuredMessage& message)
{
size_t size = sizeof(uint8_t); // protocol version
size += get_size(message.header_fields);
size += length_coding_size(get_size(message.header_fields));
size += get_size(message.trailer_fields);
size += length_coding_size(get_size(message.trailer_fields));
size += get_size(message.payload);
return size;
}
void serialize(OutputArchive& ar, const SecuredMessage& message)
{
const uint8_t protocol_version = message.protocol_version();
ar << protocol_version;
serialize(ar, message.header_fields);
serialize(ar, message.payload);
serialize(ar, message.trailer_fields);
}
size_t deserialize(InputArchive& ar, SecuredMessage& message)
{
uint8_t protocol_version = 0;
ar >> protocol_version;
size_t length = sizeof(protocol_version);
if (protocol_version == 2) {
const size_t hdr_length = deserialize(ar, message.header_fields);
length += hdr_length + length_coding_size(hdr_length);
length += deserialize(ar, message.payload);
const size_t trlr_length = deserialize(ar, message.trailer_fields);
length += trlr_length + length_coding_size(trlr_length);
} else {
throw deserialization_error("Unsupported SecuredMessage protocol version");
}
return length;
}
ByteBuffer convert_for_signing(const SecuredMessage& message, const std::list<TrailerField>& trailer_fields)
{
ByteBuffer buf;
byte_buffer_sink sink(buf);
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
OutputArchive ar(stream);
const uint8_t protocol_version = message.protocol_version();
ar << protocol_version;
serialize(ar, message.header_fields);
serialize(ar, message.payload);
// Encode the total length, all trailer fields before the signature and the type of the signature
// (see TS 103 097 v1.2.1, section 5.6)
serialize_length(ar, get_size(trailer_fields));
for (auto& elem : trailer_fields) {
TrailerFieldType type = get_type(elem);
if (type == TrailerFieldType::Signature) {
serialize(ar, type);
break; // exclude fields after signature
} else {
serialize(ar, elem);
}
}
stream.close();
return buf;
}
ItsAid get_its_aid(const SecuredMessage& msg)
{
const IntX* raw = msg.header_field<HeaderFieldType::Its_Aid>();
return raw ? raw->get() : 0;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,128 @@
#ifndef SECURED_MESSAGE_HPP_MO3HBSXG
#define SECURED_MESSAGE_HPP_MO3HBSXG
#include <vanetza/common/its_aid.hpp>
#include <vanetza/security/v2/header_field.hpp>
#include <vanetza/security/v2/trailer_field.hpp>
#include <vanetza/security/v2/payload.hpp>
#include <cstdint>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
/// SecuredMessage as specified in TS 103 097 v1.2.1, section 5.1
struct SecuredMessage
{
std::list<HeaderField> header_fields;
std::list<TrailerField> trailer_fields;
Payload payload;
uint8_t protocol_version() const { return 2; }
/**
* Fetch pointer to first matching header field
* \param type HeaderField has to match given type
* \return matching HeaderField or nullptr
*/
HeaderField* header_field(HeaderFieldType);
/**
* Fetch read-only pointer to first machting header field
* \param type requested header field type
* \return matching header field or nullptr
*/
const HeaderField* header_field(HeaderFieldType type) const;
/**
* Fetch pointer to first matching trailer field
* \param type TrailerField has to match given type
* \return matching TrailerField or nullptr
*/
TrailerField* trailer_field(TrailerFieldType);
/**
* Fetch read-only pointer of first matching trailer field
* \param type request trailer field type
* \return matching trailer field or nullptr
*/
const TrailerField* trailer_field(TrailerFieldType type) const;
template<HeaderFieldType T>
typename header_field_type<T>::type* header_field()
{
using field_type = typename header_field_type<T>::type;
HeaderField* field = header_field(T);
return boost::get<field_type>(field);
}
template<HeaderFieldType T>
const typename header_field_type<T>::type* header_field() const
{
using field_type = typename header_field_type<T>::type;
const HeaderField* field = header_field(T);
return boost::get<field_type>(field);
}
template<TrailerFieldType T>
typename trailer_field_type<T>::type* trailer_field()
{
using field_type = typename trailer_field_type<T>::type;
TrailerField* field = trailer_field(T);
return boost::get<field_type>(field);
}
template<TrailerFieldType T>
const typename trailer_field_type<T>::type* trailer_field() const
{
using field_type = typename trailer_field_type<T>::type;
const TrailerField* field = trailer_field(T);
return boost::get<field_type>(field);
}
};
/**
* \brief Calculates size of a SecuredMessage object
* \return size_t containing the number of octets needed to serialize the object
*/
size_t get_size(const SecuredMessage&);
/**
* \brief Serializes a SecuredMessage into a binary archive
*/
void serialize(OutputArchive& ar, const SecuredMessage& message);
/**
* \brief Deserializes a SecuredMessage from a binary archive
* \return size of deserialized SecuredMessage
*/
size_t deserialize(InputArchive& ar, SecuredMessage& message);
/**
* \brief Create ByteBuffer equivalent of SecuredMessage suitable for signature creation
*
* ByteBuffer contains message's version, header_fields and payload.
* Additionally, the length of trailer fields and the type of the signature is appended.
*
* \param message
* \param trailer_fields only trailer fields up to signature will be included in byte buffer
* \return serialized data fields relevant for signature creation
*/
ByteBuffer convert_for_signing(const SecuredMessage& message, const std::list<TrailerField>& trailer_fields);
/**
* \brief Get ITS-AID from message
* \param msg secured message object
* \return found ITS-AID or 0
*/
ItsAid get_its_aid(const SecuredMessage& msg);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SECURED_MESSAGE_HPP_MO3HBSXG */
@@ -0,0 +1,65 @@
#include <vanetza/security/v2/length_coding.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <cassert>
#include <limits>
#include <stdexcept>
#include <type_traits>
namespace vanetza
{
namespace security
{
namespace v2
{
template<typename T>
std::size_t trim_size_impl(T in, typename std::enable_if<std::is_same<T, std::size_t>::value>::type* = nullptr)
{
return in;
}
template<typename T>
std::size_t trim_size_impl(T in, typename std::enable_if<!std::is_same<T, std::size_t>::value>::type* = nullptr)
{
if (in > std::numeric_limits<std::size_t>::max()) {
throw std::overflow_error("given size exceeds limits of std::size_t");
}
return static_cast<std::size_t>(in);
}
std::size_t trim_size(std::uintmax_t in)
{
return trim_size_impl(in);
}
void serialize_length(OutputArchive& ar, std::uintmax_t length)
{
ByteBuffer buf;
buf = encode_length(length);
for (auto it = buf.begin(); it != buf.end(); it++) {
ar << *it;
}
}
std::uintmax_t deserialize_length(InputArchive& ar)
{
ByteBuffer buf(1);
ar >> buf[0];
const size_t leading = count_leading_ones(buf[0]);
buf.resize(leading + 1);
for (size_t c = 1; c <= leading; ++c) {
ar >> buf[c];
}
auto tup = decode_length(buf);
if (std::get<0>(tup) != buf.begin()) {
return std::get<1>(tup);
} else {
ar.fail(InputArchive::ErrorCode::ConstraintViolation);
return 0;
}
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,121 @@
#ifndef SERIALIZATION_HPP_IENSIAL4
#define SERIALIZATION_HPP_IENSIAL4
#include <vanetza/common/serialization.hpp>
#include <vanetza/security/v2/length_coding.hpp>
#include <cassert>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
using vanetza::serialize;
using vanetza::deserialize;
/**
* \brief Serialize given length
* \param ar to serialize in
* \param size to encode
*/
void serialize_length(OutputArchive&, std::uintmax_t);
/**
* \brief Deserialize length from a given archive
* \param ar shall start with encoded length
* \return length deserialized from archive
*/
std::uintmax_t deserialize_length(InputArchive&);
/**
* \brief Calculate size of a list
*
* Sums up sizes of all list elements only, length itself is not included.
* Therefore, the returned length is suitable as argument for serialize_length.
*
* \tparam T list element type
* \param list
* \return accumulated elements' size
*/
template<class T>
size_t get_size(const std::list<T>& list)
{
using vanetza::security::v2::get_size;
size_t size = 0;
for (auto& elem : list) {
size += get_size(elem);
}
return size;
}
/**
* \brief Trim (possibly) wider size type safely
*
* This function throws an exception if size would be truncated.
*
* \param in wide size type
* \return same size using narrow type
*/
std::size_t trim_size(std::uintmax_t in);
/** \brief Serialize from any given list into given binary archive
* \tparam T the type of the list
* \tparam ARGS all additional arguments for the underlying functions
* \param ar to serialize in
* \param list
* \param args the additional arguments
*/
template<class T, typename... ARGS>
void serialize(OutputArchive& ar, const std::list<T>& list, ARGS&&... args)
{
using vanetza::security::v2::get_size;
using vanetza::security::v2::serialize;
size_t size = get_size(list);
serialize_length(ar, size);
for (auto& elem : list) {
serialize(ar, elem, std::forward<ARGS>(args)...);
}
}
/** \brief Deserialize a list from given archive
* \tparam T the type of the list
* \tparam ARGS all additional arguments for the underlying functions
* \param ar, shall start with the list
* \param args the additional arguments
* \return size of the deserialized list in bytes
*/
template<class T, typename... ARGS>
std::size_t deserialize(InputArchive& ar, std::list<T>& list, ARGS&&... args)
{
using vanetza::security::v2::deserialize;
static const std::size_t length_limit = 4096;
const auto length = trim_size(deserialize_length(ar));
if (length <= length_limit) {
std::size_t remainder = length;
while (remainder > 0) {
T t;
std::size_t size = deserialize(ar, t, std::forward<ARGS>(args)...);
if (size <= remainder && ar.is_good()) {
list.push_back(std::move(t));
remainder -= size;
} else {
ar.fail(InputArchive::ErrorCode::ConstraintViolation);
break;
}
}
} else {
ar.fail(InputArchive::ErrorCode::ExcessiveLength);
}
return length;
}
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SERIALIZATION_HPP_IENSIAL4 */
@@ -0,0 +1,99 @@
#include <vanetza/common/its_aid.hpp>
#include <vanetza/common/position_provider.hpp>
#include <vanetza/security/sign_service.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/certificate_provider.hpp>
#include <vanetza/security/v2/sign_header_policy.hpp>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
DefaultSignHeaderPolicy::DefaultSignHeaderPolicy(const Runtime& rt, PositionProvider& positioning) :
m_runtime(rt), m_positioning(positioning), m_cam_next_certificate(m_runtime.now()), m_cert_requested(false), m_chain_requested(false)
{
}
std::list<HeaderField> DefaultSignHeaderPolicy::prepare_header(const SignRequest& request, CertificateProvider& certificate_provider)
{
std::list<HeaderField> header_fields;
header_fields.push_back(convert_time64(m_runtime.now()));
header_fields.push_back(IntX(request.its_aid));
if (request.its_aid == aid::CA) {
// section 7.1 in TS 103 097 v1.2.1
if (m_chain_requested) {
std::list<Certificate> full_chain;
full_chain.splice(full_chain.end(), certificate_provider.own_chain());
full_chain.push_back(certificate_provider.own_certificate());
header_fields.push_back(SignerInfo { std::move(full_chain) });
m_cam_next_certificate = m_runtime.now() + std::chrono::seconds(1);
} else if (m_runtime.now() < m_cam_next_certificate && !m_cert_requested) {
header_fields.push_back(SignerInfo { calculate_hash(certificate_provider.own_certificate()) });
} else {
header_fields.push_back(SignerInfo { certificate_provider.own_certificate() });
m_cam_next_certificate = m_runtime.now() + std::chrono::seconds(1);
}
if (m_unknown_certificates.size() > 0) {
std::list<HashedId3> unknown_certificates(m_unknown_certificates.begin(), m_unknown_certificates.end());
header_fields.push_back(unknown_certificates);
m_unknown_certificates.clear();
}
m_cert_requested = false;
m_chain_requested = false;
} else {
auto position = m_positioning.position_fix();
if (position.altitude) {
header_fields.push_back(ThreeDLocation(position.latitude, position.longitude, to_elevation(position.altitude->value())));
} else {
header_fields.push_back(ThreeDLocation(position.latitude, position.longitude));
}
header_fields.push_back(SignerInfo { certificate_provider.own_certificate() });
}
// ensure correct serialization order, see TS 103 097 v1.2.1
header_fields.sort([](const HeaderField& a, const HeaderField& b) {
const HeaderFieldType type_a = get_type(a);
const HeaderFieldType type_b = get_type(b);
// signer_info must be encoded first in all profiles
if (type_a == HeaderFieldType::Signer_Info) {
// return false if both are signer_info fields
return type_b != HeaderFieldType::Signer_Info;
} else if (type_b == HeaderFieldType::Signer_Info) {
return false; // "signer info" @ b has precedence over "non-signer info" @ a
}
// all other fields must be encoded in ascending order
using enum_int = std::underlying_type<HeaderFieldType>::type;
return static_cast<enum_int>(type_a) < static_cast<enum_int>(type_b);
});
return header_fields;
}
void DefaultSignHeaderPolicy::request_unrecognized_certificate(HashedId8 id)
{
m_unknown_certificates.insert(truncate(id));
}
void DefaultSignHeaderPolicy::request_certificate()
{
m_cert_requested = true;
}
void DefaultSignHeaderPolicy::request_certificate_chain()
{
m_chain_requested = true;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,89 @@
#ifndef SIGN_HEADER_POLICY_HPP_KJIIEGCH
#define SIGN_HEADER_POLICY_HPP_KJIIEGCH
#include <vanetza/common/clock.hpp>
#include <vanetza/common/runtime.hpp>
#include <vanetza/security/hashed_id.hpp>
#include <vanetza/security/v2/header_field.hpp>
#include <set>
namespace vanetza
{
// forward declaration
class PositionProvider;
namespace security
{
// forward declarations
class CertificateProvider;
struct SignRequest;
namespace v2
{
/**
* SignHeaderPolicy is used while signing messages
*
* SignHeaderPolicy determines the header fields to be included in the secured message.
* Other components can influence the policy's behaviour by calling one of its "report" methods.
*/
class SignHeaderPolicy
{
public:
/**
* Prepare header fields for next secured message.
*
* \param req signing request (including ITS-AID for example)
* \param certprvd available certificates
* \return header fields
*/
virtual std::list<HeaderField> prepare_header(const SignRequest& req, CertificateProvider& certprvd) = 0;
/**
* Mark certificate as unrecognized in next secured message
* \param id hash of unknown certificate
*/
virtual void request_unrecognized_certificate(HashedId8 id) = 0;
/**
* Request a full certificate to be included in next secured message
*/
virtual void request_certificate() = 0;
/**
* Request a full certificate chain to be included in next secured message
*/
virtual void request_certificate_chain() = 0;
virtual ~SignHeaderPolicy() = default;
};
/**
* DefaultSignHeaderPolicy implements the default behaviour specified by ETSI TS 103 097 V1.2.1
*/
class DefaultSignHeaderPolicy : public SignHeaderPolicy
{
public:
DefaultSignHeaderPolicy(const Runtime&, PositionProvider& positioning);
std::list<HeaderField> prepare_header(const SignRequest& request, CertificateProvider& certificate_provider) override;
void request_unrecognized_certificate(HashedId8 id) override;
void request_certificate() override;
void request_certificate_chain() override;
private:
const Runtime& m_runtime;
PositionProvider& m_positioning;
Clock::time_point m_cam_next_certificate;
std::set<HashedId3> m_unknown_certificates;
bool m_cert_requested;
bool m_chain_requested;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SIGN_HEADER_POLICY_HPP_KJIIEGCH */
@@ -0,0 +1,108 @@
#include <vanetza/common/its_aid.hpp>
#include <vanetza/common/runtime.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/v2/certificate_provider.hpp>
#include <vanetza/security/v2/sign_header_policy.hpp>
#include <vanetza/security/v2/sign_service.hpp>
#include <vanetza/security/v2/signature.hpp>
#include <future>
namespace vanetza
{
namespace security
{
namespace v2
{
namespace
{
/**
* \brief signature used as placeholder until final signature is calculated
* \return placeholder containing dummy data
*/
EcdsaSignature signature_placeholder()
{
const auto size = field_size(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
EcdsaSignature ecdsa;
ecdsa.s.resize(size, 0x00);
X_Coordinate_Only coordinate;
coordinate.x.resize(size, 0x00);
ecdsa.R = std::move(coordinate);
return ecdsa;
}
} // namespace
StraightSignService::StraightSignService(CertificateProvider& provider, Backend& backend, SignHeaderPolicy& policy) :
m_certificates(provider), m_backend(backend), m_policy(policy)
{
}
SignConfirm StraightSignService::sign(SignRequest&& request)
{
SecuredMessage secured_message;
secured_message.payload.type = PayloadType::Signed;
secured_message.payload.data = std::move(request.plain_message);
secured_message.header_fields = m_policy.prepare_header(request, m_certificates);
const auto& private_key = m_certificates.own_private_key();
static const Signature placeholder = signature_placeholder();
static const std::list<TrailerField> trailer_fields = { placeholder };
ByteBuffer data_buffer = convert_for_signing(secured_message, trailer_fields);
TrailerField trailer_field = m_backend.sign_data(private_key, data_buffer);
secured_message.trailer_fields.push_back(trailer_field);
return SignConfirm::success(std::move(secured_message));
}
DeferredSignService::DeferredSignService(CertificateProvider& provider, Backend& backend, SignHeaderPolicy& policy) :
m_certificates(provider), m_backend(backend), m_policy(policy)
{
}
SignConfirm DeferredSignService::sign(SignRequest&& request)
{
SecuredMessage secured_message;
secured_message.payload.type = PayloadType::Signed;
secured_message.payload.data = std::move(request.plain_message);
secured_message.header_fields = m_policy.prepare_header(request, m_certificates);
const auto& private_key = m_certificates.own_private_key();
static const EcdsaSignature placeholder = signature_placeholder();
static const std::list<TrailerField> trailer_fields = { Signature { placeholder } };
auto future = std::async(std::launch::deferred, [this, secured_message, private_key]() {
ByteBuffer data = convert_for_signing(secured_message, trailer_fields);
return m_backend.sign_data(private_key, data);
});
EcdsaSignatureFuture signature(future.share(), placeholder);
secured_message.trailer_fields.push_back(Signature { std::move(signature) });
return SignConfirm::success(std::move(secured_message));
}
DummySignService::DummySignService(const Runtime& runtime, const SignerInfo& signer) :
m_runtime(runtime), m_signer_info(signer)
{
}
SignConfirm DummySignService::sign(SignRequest&& request)
{
static const Signature null_signature { signature_placeholder() };
SecuredMessage secured_message;
secured_message.payload.type = PayloadType::Signed;
secured_message.payload.data = std::move(request.plain_message);
secured_message.header_fields.push_back(convert_time64(m_runtime.now()));
secured_message.header_fields.push_back(request.its_aid);
secured_message.header_fields.push_back(m_signer_info);
secured_message.trailer_fields.push_back(null_signature);
return SignConfirm::success(std::move(secured_message));
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,75 @@
#ifndef AD03EF9D_246E_48D3_83F0_9983ADF0C454
#define AD03EF9D_246E_48D3_83F0_9983ADF0C454
#include <vanetza/security/sign_service.hpp>
#include <vanetza/security/v2/certificate_provider.hpp>
#include <vanetza/security/v2/secured_message.hpp>
#include <vanetza/security/v2/sign_header_policy.hpp>
namespace vanetza
{
namespace security
{
// forward declarations
class Backend;
namespace v2
{
// forward declarations
class CertificateProvider;
/**
* SignService immediately signing the message using given
*/
class StraightSignService : public SignService
{
public:
StraightSignService(CertificateProvider&, Backend&, SignHeaderPolicy&);
SignConfirm sign(SignRequest&&) override;
private:
CertificateProvider& m_certificates;
Backend& m_backend;
SignHeaderPolicy& m_policy;
};
/**
* SignService deferring actually signature calculation using EcdsaSignatureFuture
*/
class DeferredSignService : public SignService
{
public:
DeferredSignService(CertificateProvider&, Backend&, SignHeaderPolicy&);
SignConfirm sign(SignRequest&&) override;
private:
CertificateProvider& m_certificates;
Backend& m_backend;
SignHeaderPolicy& m_policy;
};
/**
* SignService without real cryptography but dummy signature
*/
class DummySignService : public SignService
{
public:
/**
* \param rt runtime for appropriate generation time
* \param si signer info attached to header fields of secured message
*/
DummySignService(const Runtime& rt, const SignerInfo& si);
SignConfirm sign(SignRequest&&) override;
private:
const Runtime& m_runtime;
SignerInfo m_signer_info;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* AD03EF9D_246E_48D3_83F0_9983ADF0C454 */
@@ -0,0 +1,161 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/signature.hpp>
#include <boost/iostreams/stream.hpp>
#include <cassert>
namespace vanetza
{
namespace security
{
namespace v2
{
PublicKeyAlgorithm get_type(const Signature& sig)
{
struct Signature_visitor : public boost::static_visitor<PublicKeyAlgorithm>
{
PublicKeyAlgorithm operator()(const EcdsaSignature&)
{
return PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256;
}
PublicKeyAlgorithm operator()(const EcdsaSignatureFuture&)
{
return PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256;
}
};
Signature_visitor visit;
return boost::apply_visitor(visit, sig.some_ecdsa);
}
size_t get_size(const EcdsaSignature& sig)
{
size_t size = sig.s.size();
size += get_size(sig.R);
return size;
}
size_t get_size(const EcdsaSignatureFuture& sig)
{
return sig.size();
}
size_t get_size(const Signature& sig)
{
size_t size = sizeof(PublicKeyAlgorithm);
struct Signature_visitor : public boost::static_visitor<size_t>
{
size_t operator()(const EcdsaSignature& sig)
{
return get_size(sig);
}
size_t operator()(const EcdsaSignatureFuture& sig)
{
return get_size(sig);
}
};
Signature_visitor visit;
size += boost::apply_visitor(visit, sig.some_ecdsa);
return size;
}
void serialize(OutputArchive& ar, const Signature& sig)
{
struct signature_visitor : public boost::static_visitor<>
{
signature_visitor(OutputArchive& ar) : m_archive(ar) {}
void operator()(const EcdsaSignature& sig)
{
serialize(m_archive, sig);
}
void operator()(const EcdsaSignatureFuture& sig)
{
serialize(m_archive, sig);
}
OutputArchive& m_archive;
};
PublicKeyAlgorithm algo = get_type(sig);
serialize(ar, algo);
signature_visitor visitor(ar);
boost::apply_visitor(visitor, sig.some_ecdsa);
}
void serialize(OutputArchive& ar, const EcdsaSignature& sig)
{
const PublicKeyAlgorithm algo = PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256;
assert(field_size(algo) == sig.s.size());
serialize(ar, sig.R, algo);
for (auto& byte : sig.s) {
ar << byte;
}
}
void serialize(OutputArchive& ar, const EcdsaSignatureFuture& sig)
{
auto& ecdsa = sig.get();
serialize(ar, ecdsa);
}
size_t deserialize(InputArchive& ar, EcdsaSignature& sig, const PublicKeyAlgorithm& algo)
{
EccPoint point;
ByteBuffer buf;
deserialize(ar, point, algo);
for (size_t i = 0; i < field_size(algo); i++) {
uint8_t byte;
ar >> byte;
buf.push_back(byte);
}
sig.R = point;
sig.s = buf;
return get_size(sig);
}
size_t deserialize(InputArchive& ar, Signature& sig)
{
PublicKeyAlgorithm algo;
size_t size = 0;
deserialize(ar, algo);
size += sizeof(algo);
switch (algo) {
case PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256: {
EcdsaSignature signature;
size += deserialize(ar, signature, algo);
sig = signature;
break;
}
default:
throw deserialization_error("Unknown PublicKeyAlgorithm");
}
return size;
}
boost::optional<EcdsaSignature> extract_ecdsa_signature(const Signature& sig)
{
struct signature_visitor : public boost::static_visitor<const EcdsaSignature*>
{
const EcdsaSignature* operator()(const EcdsaSignature& sig)
{
return &sig;
}
const EcdsaSignature* operator()(const EcdsaSignatureFuture& sig)
{
return &sig.get();
}
};
signature_visitor visitor;
const EcdsaSignature* ecdsa = boost::apply_visitor(visitor, sig.some_ecdsa);
return boost::optional<EcdsaSignature>(ecdsa != nullptr, *ecdsa);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,102 @@
#ifndef SIGNATURE_HPP_ZWPLNDVE
#define SIGNATURE_HPP_ZWPLNDVE
#include <vanetza/security/signature.hpp>
#include <vanetza/security/v2/ecc_point.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <boost/optional/optional.hpp>
#include <boost/variant/variant.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
struct Signature {
SomeEcdsaSignature some_ecdsa;
Signature() = default;
Signature(EcdsaSignature&& sig) : some_ecdsa(std::move(sig)) {}
Signature& operator=(EcdsaSignature&& sig) { some_ecdsa = std::move(sig); return *this; }
Signature(const EcdsaSignature& sig) : some_ecdsa(sig) {}
Signature& operator=(const EcdsaSignature& sig) { some_ecdsa = sig; return *this; }
Signature(EcdsaSignatureFuture&& sig) : some_ecdsa(std::move(sig)) {}
Signature& operator=(EcdsaSignatureFuture&& sig) { some_ecdsa = std::move(sig); return *this; }
Signature(SomeEcdsaSignature&& some) : some_ecdsa(std::move(some)) {}
Signature& operator=(SomeEcdsaSignature&& some) { this->some_ecdsa = std::move(some); return *this; }
};
/**
* brief Determines PublicKeyAlgorithm of a given Signature
* \param signature
* \return PublicKeyAlgorithm
*/
PublicKeyAlgorithm get_type(const Signature&);
/**
* \brief Calculates size of a EcdsaSignature
* \param signature
* \return number of octets needed for serialization
*/
size_t get_size(const EcdsaSignature&);
/**
* \brief Calculates size of a EcdsaSignatureFuture
* \param signature
* \return number of octets needed for serialization
*/
size_t get_size(const EcdsaSignatureFuture&);
/**
* \brief Calculates size of a Signature
* \param signature
* \return number of octets needed for serialization
*/
size_t get_size(const Signature&);
/**
* \brief Serializes a signature into a binary archive
* \param ar to serialize in
* \param signature
*/
void serialize(OutputArchive&, const Signature&);
void serialize(OutputArchive&, const EcdsaSignature&);
void serialize(OutputArchive&, const EcdsaSignatureFuture&);
/**
* \brief Deserializes an EcdsaSignature from a binary archive
* Requires PublicKeyAlgorithm for determining the signature size
* \param ar with a serialized EcdsaSignature at the beginning
* \param signature to deserialize
* \param public_key_algorithm to determine the size of the signature
* \return size of the deserialized EcdsaSignature
*/
size_t deserialize(InputArchive&, EcdsaSignature&, const PublicKeyAlgorithm&);
/**
* \brief Deserializes a Signature from a binary archive
* \param ar with a serialized Signature at the beginning
* \param signature to deserialize
* \return size of the deserialized Signature
*/
size_t deserialize(InputArchive&, Signature&);
/**
* Try to extract ECDSA signature from signature variant
* \param sig Signature variant (of some type)
* \return ECDSA signature (optionally)
*/
boost::optional<EcdsaSignature> extract_ecdsa_signature(const Signature& sig);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SIGNATURE_HPP_ZWPLNDVE */
@@ -0,0 +1,231 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/length_coding.hpp>
#include <vanetza/security/v2/signer_info.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
SignerInfoType get_type(const SignerInfo& info)
{
struct SignerInfo_visitor : public boost::static_visitor<SignerInfoType>
{
SignerInfoType operator()(const std::nullptr_t)
{
return SignerInfoType::Self;
}
SignerInfoType operator()(const HashedId8&)
{
return SignerInfoType::Certificate_Digest_With_SHA256;
}
SignerInfoType operator()(const Certificate&)
{
return SignerInfoType::Certificate;
}
SignerInfoType operator()(const std::list<Certificate>&)
{
return SignerInfoType::Certificate_Chain;
}
SignerInfoType operator()(const CertificateDigestWithOtherAlgorithm&)
{
return SignerInfoType::Certificate_Digest_With_Other_Algorithm;
}
};
SignerInfo_visitor visit;
return boost::apply_visitor(visit, info);
}
size_t get_size(const CertificateDigestWithOtherAlgorithm& cert)
{
size_t size = cert.digest.size();
size += sizeof(cert.algorithm);
return size;
}
size_t get_size(const SignerInfo& info)
{
size_t size = sizeof(SignerInfoType);
struct SignerInfo_visitor : public boost::static_visitor<size_t>
{
size_t operator()(const std::nullptr_t&)
{
return 0;
}
size_t operator()(const HashedId8& id)
{
return id.size();
}
size_t operator()(const Certificate& cert)
{
return get_size(cert);
}
size_t operator()(const std::list<Certificate>& list)
{
size_t size = get_size(list);
size += length_coding_size(size);
return size;
}
size_t operator()(const CertificateDigestWithOtherAlgorithm& cert)
{
return get_size(cert);
}
};
SignerInfo_visitor visit;
size += boost::apply_visitor(visit, info);
return size;
}
void serialize(OutputArchive& ar, const CertificateDigestWithOtherAlgorithm& cert)
{
serialize(ar, cert.algorithm);
for (auto& byte : cert.digest) {
ar << byte;
}
}
void serialize(OutputArchive& ar, const SignerInfo& info)
{
struct SignerInfo_visitor : public boost::static_visitor<>
{
SignerInfo_visitor(OutputArchive& ar) :
m_archive(ar)
{
}
void operator()(const std::nullptr_t)
{
// intentionally do nothing
}
void operator()(const HashedId8& id)
{
for (auto& byte : id) {
m_archive << byte;
}
}
void operator()(const Certificate& cert)
{
serialize(m_archive, cert);
}
void operator()(const std::list<Certificate>& list)
{
serialize(m_archive, list);
}
void operator()(const CertificateDigestWithOtherAlgorithm& cert)
{
serialize(m_archive, cert);
}
OutputArchive& m_archive;
};
SignerInfoType type = get_type(info);
serialize(ar, type);
SignerInfo_visitor visit(ar);
boost::apply_visitor(visit, info);
}
size_t deserialize(InputArchive& ar, CertificateDigestWithOtherAlgorithm& cert)
{
deserialize(ar, cert.algorithm);
for (size_t c = 0; c < 8; c++) {
ar >> cert.digest[c];
}
size_t size = cert.digest.size();
size += sizeof(cert.algorithm);
return size;
}
size_t deserialize(InputArchive& ar, SignerInfo& info)
{
SignerInfoType type;
size_t size = 0;
deserialize(ar, type);
size += sizeof(SignerInfoType);
switch (type) {
case SignerInfoType::Certificate: {
Certificate cert;
size += deserialize(ar, cert);
info = cert;
break;
}
case SignerInfoType::Certificate_Chain: {
std::list<Certificate> list;
size += deserialize(ar, list);
size += length_coding_size(size);
info = list;
break;
}
case SignerInfoType::Certificate_Digest_With_SHA256: {
HashedId8 cert;
for (size_t c = 0; c < 8; c++) {
ar >> cert[c];
}
info = cert;
size += sizeof(cert);
break;
}
case SignerInfoType::Certificate_Digest_With_Other_Algorithm: {
CertificateDigestWithOtherAlgorithm cert;
size += deserialize(ar, cert);
info = cert;
break;
}
case SignerInfoType::Self:
info = nullptr;
break;
default:
throw deserialization_error("Unknown SignerInfoType");
break;
}
return size;
}
size_t deserialize_certificate_signer(InputArchive& ar, SignerInfo& info)
{
SignerInfoType type;
size_t size = 0;
deserialize(ar, type);
size += sizeof(SignerInfoType);
switch (type) {
case SignerInfoType::Certificate:
case SignerInfoType::Certificate_Chain:
// TS 103 097 v1.2.1 clause 6.1 forbids these signer info types for certificates
throw deserialization_error("Illegal SignerInfo for Certificate");
break;
case SignerInfoType::Certificate_Digest_With_SHA256: {
HashedId8 cert;
for (size_t c = 0; c < 8; c++) {
ar >> cert[c];
}
info = cert;
size += sizeof(cert);
break;
}
case SignerInfoType::Certificate_Digest_With_Other_Algorithm: {
CertificateDigestWithOtherAlgorithm cert;
size += deserialize(ar, cert);
info = cert;
break;
}
case SignerInfoType::Self:
info = nullptr;
break;
default:
throw deserialization_error("Unknown SignerInfoType");
break;
}
return size;
}
} // ns v2
} // ns security
} // ns vanetza
@@ -0,0 +1,107 @@
#ifndef SIGNER_INFO_HPP_9K6GXK4R
#define SIGNER_INFO_HPP_9K6GXK4R
#include <vanetza/security/v2/basic_elements.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/variant/variant.hpp>
#include <cstddef>
#include <cstdint>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
struct Certificate;
/// described in TS 103 097 v1.2.1, section 4.2.11
enum class SignerInfoType : uint8_t
{
Self = 0, // nothing -> nullptr_t
Certificate_Digest_With_SHA256 = 1, // HashedId8
Certificate = 2, // Certificate
Certificate_Chain = 3, // std::list<Certificate>
Certificate_Digest_With_Other_Algorithm = 4 // CertificateDigestWithOtherAlgorithm
};
/// described in TS 103 097 v1.2.1, section 4.2.10
struct CertificateDigestWithOtherAlgorithm
{
PublicKeyAlgorithm algorithm;
HashedId8 digest;
};
/// described in TS 103 097 v1.2.1, section 4.2.10
using SignerInfo = boost::variant<
std::nullptr_t,
HashedId8,
boost::recursive_wrapper<Certificate>,
std::list<Certificate>,
CertificateDigestWithOtherAlgorithm
>;
/**
* \brief Determines SignerInfoType of SignerInfo
* \param SignerInfo
* \return SignerInfoType
*/
SignerInfoType get_type(const SignerInfo&);
/**
* \brief Calculates size of an CertificateDigestWithOtherAlgorithm
* \param CertificateDigestWithOtherAlgorithm
* \return number of octets needed to serialize the CertificateDigestWithOtherAlgorithm
*/
size_t get_size(const CertificateDigestWithOtherAlgorithm&);
/**
* \brief Calculates size of an SignerInfo
* \param SignerInfo
* \return number of octets needed to serialize the SignerInfo
*/
size_t get_size(const SignerInfo&);
/**
* \brief Serializes an CertificateDigestWithOtherAlgorithm into a binary archive
*/
void serialize(OutputArchive&, const CertificateDigestWithOtherAlgorithm&);
/**
* \brief Serializes an SignerInfo into a binary archive
*/
void serialize(OutputArchive&, const SignerInfo&);
/**
* \brief Deserializes an CertificateDigestWithOtherAlgorithm from a binary archive
* \param archive with a CertificateDigestWithOtherAlgorithm at the beginning
* \param CertificateDigestWithOtherAlgorithm to deserialize
* \return size of the deserialized CertificateDigestWithOtherAlgorithm
*/
size_t deserialize(InputArchive&, CertificateDigestWithOtherAlgorithm&);
/**
* \brief Deserializes an SignerInfo from a binary archive
* \param archive with a SignerInfo at the beginning
* \param SignerInfo to deserialize
* \return size of the deserialized SignerInfo
*/
size_t deserialize(InputArchive&, SignerInfo&);
/**
* \brief Deserialize SignerInfo of a Certificate from a binary archive
* This function rejects SignerInfo types which are invalid for certificates.
* \param ar archive with a (legal) SignerInfo at the beginning
* \param info SignerInfo to deserialize
* \return size of the deserialized SignerInfo
*/
size_t deserialize_certificate_signer(InputArchive& ar, SignerInfo& info);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SIGNER_INFO_HPP_9K6GXK4R */
@@ -0,0 +1,39 @@
#include <vanetza/security/v2/static_certificate_provider.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
StaticCertificateProvider::StaticCertificateProvider(const Certificate& authorization_ticket,
const ecdsa256::PrivateKey& authorization_ticket_key) :
StaticCertificateProvider(authorization_ticket, authorization_ticket_key, std::list<Certificate> {})
{
}
StaticCertificateProvider::StaticCertificateProvider(const Certificate& authorization_ticket,
const ecdsa256::PrivateKey& authorization_ticket_key, const std::list<Certificate>& chain) :
authorization_ticket(authorization_ticket), authorization_ticket_key(authorization_ticket_key), chain(chain)
{
}
const ecdsa256::PrivateKey& StaticCertificateProvider::own_private_key()
{
return authorization_ticket_key;
}
std::list<Certificate> StaticCertificateProvider::own_chain()
{
return chain;
}
const Certificate& StaticCertificateProvider::own_certificate()
{
return authorization_ticket;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,65 @@
#ifndef STATIC_CERTIFICATE_PROVIDER_HPP_MTULFLKX
#define STATIC_CERTIFICATE_PROVIDER_HPP_MTULFLKX
#include <vanetza/security/v2/certificate_provider.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/**
* \brief A simple certificate provider
*
* This certificate provider uses a static certificate and key pair that is pre-generated.
*/
class StaticCertificateProvider : public CertificateProvider
{
public:
/**
* Create static certificate provider with empty chain
* \param authorization_ticket
* \param ticket_key private key of given authorization ticket
*/
StaticCertificateProvider(const Certificate& authorization_ticket, const ecdsa256::PrivateKey& ticket_key);
/**
* Create static certificate provider with given chain
* \param authorization_ticket
* \param ticket_key private key of given authorization ticket
* \param chain own certificate chain
*/
StaticCertificateProvider(const Certificate& authorization_ticket, const ecdsa256::PrivateKey& ticket_key,
const std::list<Certificate>& chain);
/**
* Get own certificate to use for signing
* \return own certificate
*/
virtual const Certificate& own_certificate() override;
/**
* Get own certificate chain, excluding the leaf certificate and root CA
* \return own certificate chain
*/
virtual std::list<Certificate> own_chain() override;
/**
* Get private key associated with own certificate
* \return private key
*/
virtual const ecdsa256::PrivateKey& own_private_key() override;
private:
Certificate authorization_ticket;
ecdsa256::PrivateKey authorization_ticket_key;
std::list<Certificate> chain;
};
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* STATIC_CERTIFICATE_PROVIDER_HPP_MTULFLKX */
@@ -0,0 +1,222 @@
#include <vanetza/security/exception.hpp>
#include <vanetza/security/v2/subject_attribute.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
SubjectAttributeType get_type(const SubjectAttribute& sub)
{
struct subject_attribute_visitor : public boost::static_visitor<SubjectAttributeType>
{
SubjectAttributeType operator()(const VerificationKey&)
{
return SubjectAttributeType::Verification_Key;
}
SubjectAttributeType operator()(const EncryptionKey&)
{
return SubjectAttributeType::Encryption_Key;
}
SubjectAttributeType operator()(const SubjectAssurance&)
{
return SubjectAttributeType::Assurance_Level;
}
SubjectAttributeType operator()(const std::list<IntX>&)
{
return SubjectAttributeType::ITS_AID_List;
}
SubjectAttributeType operator()(const EccPoint&)
{
return SubjectAttributeType::Reconstruction_Value;
}
SubjectAttributeType operator()(const std::list<ItsAidSsp>&)
{
return SubjectAttributeType::ITS_AID_SSP_List;
}
};
subject_attribute_visitor visit;
return boost::apply_visitor(visit, sub);
}
void serialize(OutputArchive& ar, const ItsAidSsp& its_aid_ssp)
{
serialize(ar, its_aid_ssp.its_aid);
size_t size = its_aid_ssp.service_specific_permissions.size();
serialize_length(ar, size);
for (auto& byte : its_aid_ssp.service_specific_permissions) {
ar << byte;
}
}
size_t deserialize(InputArchive& ar, ItsAidSsp& its_aid_ssp)
{
size_t size = 0;
size += deserialize(ar, its_aid_ssp.its_aid);
static const std::uintmax_t buf_size_limit = 1024;
const std::uintmax_t buf_size = deserialize_length(ar);
if (buf_size <= buf_size_limit) {
its_aid_ssp.service_specific_permissions.resize(buf_size);
size += buf_size + length_coding_size(buf_size);
for (std::uintmax_t i = 0; i < buf_size; ++i) {
ar >> its_aid_ssp.service_specific_permissions[i];
}
} else {
ar.fail(InputArchive::ErrorCode::ExcessiveLength);
}
return size;
}
size_t get_size(const SubjectAssurance& assurance)
{
return sizeof(assurance.raw);
}
size_t get_size(const ItsAidSsp& its_aid_ssp)
{
size_t size = get_size(its_aid_ssp.its_aid);
size += its_aid_ssp.service_specific_permissions.size();
size += length_coding_size(its_aid_ssp.service_specific_permissions.size());
return size;
}
size_t get_size(const SubjectAttribute& sub)
{
size_t size = sizeof(SubjectAttributeType);
struct subject_attribute_visitor : public boost::static_visitor<size_t>
{
size_t operator()(const VerificationKey& key)
{
return get_size(key.key);
}
size_t operator()(const EncryptionKey& key)
{
return get_size(key.key);
}
size_t operator()(const SubjectAssurance& assurance)
{
return get_size(assurance);
}
size_t operator()(const std::list<IntX>& list)
{
size_t size = get_size(list);
size += length_coding_size(size);
return size;
}
size_t operator()(const EccPoint& ecc)
{
return get_size(ecc);
}
size_t operator()(const std::list<ItsAidSsp>& list)
{
size_t size = get_size(list);
size += length_coding_size(size);
return size;
}
};
subject_attribute_visitor visit;
size += boost::apply_visitor(visit, sub);
return size;
}
void serialize(OutputArchive& ar, const SubjectAttribute& subjectAttribute)
{
struct subject_attribute_visitor : public boost::static_visitor<>
{
subject_attribute_visitor(OutputArchive& ar) :
m_archive(ar)
{
}
void operator()(const VerificationKey& key)
{
serialize(m_archive, key.key);
}
void operator()(const EncryptionKey& key)
{
serialize(m_archive, key.key);
}
void operator()(const SubjectAssurance& assurance)
{
m_archive << assurance.raw;
}
void operator()(const std::list<IntX>& list)
{
serialize(m_archive, list);
}
void operator()(const EccPoint&)
{
// TODO: specification of corresponding public key algorithm is missing
throw serialization_error("unsupported serialization of SubjectAttribute with EccPoint");
}
void operator()(const std::list<ItsAidSsp>& list)
{
serialize(m_archive, list);
}
OutputArchive& m_archive;
};
SubjectAttributeType type = get_type(subjectAttribute);
serialize(ar, type);
subject_attribute_visitor visit(ar);
boost::apply_visitor(visit, subjectAttribute);
}
size_t deserialize(InputArchive& ar, SubjectAttribute& sub)
{
SubjectAttributeType type;
size_t size = 0;
deserialize(ar, type);
size += sizeof(type);
switch (type) {
case SubjectAttributeType::Assurance_Level: {
SubjectAssurance assurance;
ar >> assurance.raw;
size += get_size(assurance);
sub = assurance;
break;
}
case SubjectAttributeType::Verification_Key: {
VerificationKey key;
size += deserialize(ar, key.key);
sub = key;
break;
}
case SubjectAttributeType::Encryption_Key: {
EncryptionKey key;
size += deserialize(ar, key.key);
sub = key;
break;
}
case SubjectAttributeType::ITS_AID_List: {
std::list<IntX> intx_list;
size_t tmp_size = deserialize(ar, intx_list);
size += tmp_size;
size += length_coding_size(tmp_size);
sub = intx_list;
break;
}
case SubjectAttributeType::ITS_AID_SSP_List: {
std::list<ItsAidSsp> itsAidSsp_list;
size_t tmp_size = deserialize(ar, itsAidSsp_list);
size += tmp_size;
size += length_coding_size(tmp_size);
sub = itsAidSsp_list;
break;
}
case SubjectAttributeType::Reconstruction_Value:
throw deserialization_error("unsupported deserialization of SubjectAttribute with EccPoint");
break;
default:
throw deserialization_error("Unknown SubjectAttributeType");
}
return size;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,192 @@
#ifndef SUBJECT_ATTRIBUTE_HPP_IRZLEB7C
#define SUBJECT_ATTRIBUTE_HPP_IRZLEB7C
#include <vanetza/security/v2/int_x.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <boost/variant/variant.hpp>
#include <cstdint>
#include <list>
namespace vanetza
{
namespace security
{
namespace v2
{
/// SubjectAssurance specified in TS 103 097 v1.2.1 in section 6.6 and 7.4.1
struct SubjectAssurance
{
SubjectAssurance(uint8_t _raw = 0) : raw(_raw) {}
static constexpr uint8_t assurance_mask = 0xE0;
static constexpr uint8_t confidence_mask = 0x03;
uint8_t raw;
uint8_t assurance() const
{
return (raw & assurance_mask) >> 5;
}
uint8_t confidence() const
{
return raw & confidence_mask;
}
};
/// ItsAidSsp specified in TS 103 097 v1.2.1, section 6.9
struct ItsAidSsp
{
IntX its_aid;
ByteBuffer service_specific_permissions;
};
/// SubjectAttributeType specified in TS 103 097 v1.2.1, section 6.5
enum class SubjectAttributeType : uint8_t {
Verification_Key = 0, //VerificationKey
Encryption_Key = 1, //EncryptionKey
Assurance_Level = 2, //SubjectAssurance
Reconstruction_Value = 3, //EccPoint
ITS_AID_List = 32, //std::list<IntX>
ITS_AID_SSP_List = 33, //std::list<ItsAidSsp>
};
/// VerificationKey specified in TS 103 097 v1.2.1, section 6.4
struct VerificationKey
{
PublicKey key;
};
/// EncryptionKey specified in TS 103 097 v1.2.1, section 6.4
struct EncryptionKey
{
PublicKey key;
};
/// SubjectAttribute specified in TS 103 097 v1.2.1, section 6.4
using SubjectAttribute = boost::variant<
VerificationKey,
EncryptionKey,
SubjectAssurance,
EccPoint,
std::list<IntX>,
std::list<ItsAidSsp>
>;
/**
* \brief Determines SubjectAttributeType to a given SubjectAttribute
* \param attribute
* \return type
*/
SubjectAttributeType get_type(const SubjectAttribute&);
/**
* \brief Calculates size of a SubjectAttribute
* \param sub
* \return number of octets needed to serialize the SubjectAttribute
*/
size_t get_size(const SubjectAttribute&);
/**
* \brief Calculates size of a SubjectAssurance
* \param sub
* \return number of octets needed to serialize the SubjectAssurance
*/
size_t get_size(const SubjectAssurance&);
/**
* \brief Calculates size of an ItsAidSsp
* \param its_aid_ssp
* \return number of octets needed to serialize the ItsAidSsp
*/
size_t get_size(const ItsAidSsp&);
/**
* \brief Deserializes a SubjectAttribute from a binary archive
* \param ar with a serialized SubjectAttribute at the beginning
* \param sub to deserialize
* \return size of the deserialized SubjectAttribute
*/
size_t deserialize(InputArchive&, SubjectAttribute&);
/**
* \brief Deserializes an ItsAidSsp from a binary archive
* \param ar with a serialized ItsAidSsp at the beginning
* \param its_aid_ssp to deserialize
* \return size of the deserialized ItsAidSsp
*/
size_t deserialize(InputArchive&, ItsAidSsp&);
/**
* \brief Serializes a SubjectAttribute into a binary archive
* \param ar to serialize in
* \param sub to serialize
*/
void serialize(OutputArchive&, const SubjectAttribute&);
/**
* \brief Serializes an ItsAidSsp into a binary archive
* \param ar to serialize in
* \param its_aid_ssp to serialize
*/
void serialize(OutputArchive&, const ItsAidSsp&);
namespace detail
{
template<SubjectAttributeType>
struct subject_attribute_type;
template<>
struct subject_attribute_type<SubjectAttributeType::Verification_Key>
{
using type = VerificationKey;
};
template<>
struct subject_attribute_type<SubjectAttributeType::Encryption_Key>
{
using type = EncryptionKey;
};
template<>
struct subject_attribute_type<SubjectAttributeType::Assurance_Level>
{
using type = SubjectAssurance;
};
template<>
struct subject_attribute_type<SubjectAttributeType::Reconstruction_Value>
{
using type = EccPoint;
};
template<>
struct subject_attribute_type<SubjectAttributeType::ITS_AID_List>
{
using type = std::list<IntX>;
};
template<>
struct subject_attribute_type<SubjectAttributeType::ITS_AID_SSP_List>
{
using type = std::list<ItsAidSsp>;
};
} // namespace detail
/**
* \brief resolve type for matching SubjectAttributeType
*
* This is kind of the reverse function of get_type(const SubjectAttribute&)
*/
template<SubjectAttributeType T>
using subject_attribute_type = typename detail::subject_attribute_type<T>::type;
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SUBJECT_ATTRIBUTE_HPP_IRZLEB7C */
@@ -0,0 +1,43 @@
#include <vanetza/security/v2/subject_info.hpp>
#include <vanetza/security/v2/length_coding.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
size_t get_size(const SubjectInfo& sub)
{
size_t size = sizeof(sub.subject_type);
size += sub.subject_name.size();
size += length_coding_size(sub.subject_name.size());
return size;
}
void serialize(OutputArchive& ar, const SubjectInfo& sub)
{
serialize(ar, sub.subject_type);
size_t size = sub.subject_name.size();
serialize_length(ar, size);
for (auto& byte : sub.subject_name) {
ar << byte;
}
}
size_t deserialize(InputArchive& ar, SubjectInfo& sub)
{
deserialize(ar, sub.subject_type);
const std::uintmax_t size = deserialize_length(ar);
for (uintmax_t c = 0; c < size; ++c) {
uint8_t tmp;
ar >> tmp;
sub.subject_name.push_back(tmp);
}
return get_size(sub);
}
} // ns v2
} // ns security
} // ns vanetza
@@ -0,0 +1,58 @@
#ifndef SUBJECT_INFO_HPP_WCKSWSKY
#define SUBJECT_INFO_HPP_WCKSWSKY
#include <vanetza/security/v2/serialization.hpp>
#include <vanetza/common/byte_buffer.hpp>
#include <array>
#include <cstdint>
namespace vanetza
{
namespace security
{
namespace v2
{
/// described in TS 103 097 v1.2.1, section 6.3
enum class SubjectType : uint8_t
{
Enrollment_Credential = 0,
Authorization_Ticket = 1,
Authorization_Authority = 2,
Enrollment_Authority = 3,
Root_CA = 4,
CRL_Signer = 5
};
/// described in TS 103 097 v1.2.1, section 6.2
struct SubjectInfo
{
SubjectType subject_type;
ByteBuffer subject_name;
};
/**
* \brief Serializes a SubjectInfo into a binary archive
*/
void serialize(OutputArchive&, const SubjectInfo&);
/**
* \brief Deserializes a SubjectInfo from a binary archive
* \param archive with a serialized SubjectInfo at the beginning
* \param SubjectInfo
* \return size of the deserialized SubjectInfo
*/
size_t deserialize(InputArchive&, SubjectInfo&);
/**
* \brief Calculates size of a SubjectInfo
* \param SubjectInfo
* \return number of octets needed to serialize SubjectInfo
*/
size_t get_size(const SubjectInfo&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SUBJECT_INFO_HPP_WCKSWSKY */
@@ -0,0 +1,44 @@
include(UseGTest)
add_library(security_test_v2 STATIC
check_basic_elements.cpp
check_certificate.cpp
check_encryption_parameter.cpp
check_header_field.cpp
check_public_key.cpp
check_recipient_info.cpp
check_region.cpp
check_secured_message.cpp
check_signature.cpp
check_signer_info.cpp
check_subject_attribute.cpp
check_trailer_field.cpp
check_validity_restriction.cpp
)
target_include_directories(security_test_v2 PUBLIC $<TARGET_PROPERTY:security,INTERFACE_INCLUDE_DIRECTORIES>)
target_link_libraries(security_test_v2 PUBLIC ${GTest_LIBRARY})
configure_gtest_directory(LINK_LIBRARIES Boost::boost security security_test security_test_v2
COMPILE_DEFINITIONS ASSET_DIR="${SECURITY_TEST_ASSET_DIR}")
add_gtest(Certificate certificate.cpp)
add_gtest(CertificateCache certificate_cache.cpp)
add_gtest(DefaultCertificateValidator default_certificate_validator.cpp)
add_gtest(EccPoint ecc_point.cpp)
add_gtest(EncryptionParameter encryption_parameter.cpp)
add_gtest(HeaderField header_field.cpp)
add_gtest(IntX int_x.cpp)
add_gtest(LengthEncoding length_encoding.cpp)
add_gtest(NaiveCertificateProvider naive_certificate_provider.cpp)
add_gtest(Payload payload.cpp)
add_gtest(PersistenceV2 persistence.cpp COMPILE_DEFINITIONS WORK_DIR="${CMAKE_CURRENT_BINARY_DIR}")
add_gtest(PublicKeyV2 public_key.cpp)
add_gtest(RecipientInfo recipient_info.cpp)
add_gtest(Region region.cpp)
add_gtest(SecuredMessage secured_message.cpp)
add_gtest(SecurityEntity security_entity.cpp)
add_gtest(Signature signature.cpp)
add_gtest(SignerInfo signer_info.cpp)
add_gtest(SubjectAttribute subject_attribute.cpp)
add_gtest(SubjectInfo subject_info.cpp)
add_gtest(TrailerField trailer_field.cpp)
add_gtest(TrustStore trust_store.cpp)
add_gtest(ValidityRestriction validity_restriction.cpp)
@@ -0,0 +1,66 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/tests/check_certificate.hpp>
#include <vanetza/security/tests/serialization.hpp>
using namespace vanetza::security::v2;
using vanetza::security::deserialize_from_hexstring;
TEST(Certificate, WebValidator_RootCA_v2)
{
const char str[] =
"0200040C547275737465645F526F6F74808D000004F1817DD05116B855A853F80DB171A3A470D431"
"70EA7EEFD8EF392D66ECEFBE501CEBA19963C9B6447574424FFF1BB89485743F4D09A72B715FC73C"
"87E5F70A110101000441279A383B80C812B72B1A5F5C3C590E5041C634A1ADCC4CE58393CA046D3C"
"619717AEF634F7D80D5F6A29FA7F86EBF823ACE0097A71EE0DF0793034B0D3797C02E0200224250B"
"0114B12B03154E0D83030000007D12BADF99D7070BCB237ED1FA7A5D86FD47E6ABA8E616B35E95A2"
"856FC6E26A493E1215BCEE8BEA18B8ED52FB240716C4D4EC7D7C0167F0F032CBB87DF611D9";
Certificate c;
deserialize_from_hexstring(str, c);
check(c, serialize_roundtrip(c));
}
TEST(Certificate, WebValidator_AuthorizationAuthority1_v2)
{
const char str[] =
"0201F5425279310C0379020A547275737465645F4141808D00000432B9C37AC51D25863A7872EF40"
"5DB43DF37FA73411B2C0539FD39DF38828F86C946CB09039C0A9694A650D9104BA62C5A7588AEF8F"
"68935F0D170373968131CD01010004E6C956FBEDCC969935BE832E4DE599CBFD687D81495C58B3C1"
"2028F92489D4AF76B64D340BE2ACE8D7E2A789FE09A5F3B84F5E65BF54A07FAF74696131E762E302"
"E0200224250B0114B12B03154E0D8303000000CC6255F38BC8844FAC2A31DE3420E65F23DBC97DC8"
"66C840516328F27850B3520FC2A812A49DD989BFB0ECE408E53B375006974D1DA4EFD6FC5465B3F8"
"946183";
Certificate c;
deserialize_from_hexstring(str, c);
check(c, serialize_roundtrip(c));
}
TEST(Certificate, WebValidator_AuthorizationAuthority2_v2)
{
const char str[] =
"0201F5425279310C0379020A547275737465645F4141808D00000401418E994657434A71E034E530"
"B1E77A8AFAC37561132C83D45C442499228CA78573F14BE034A4958108A654CAC60F15BB35907E33"
"D0E97F8D7EAF64A1F43547010100047C5C8D8B86CF8A00A53F3CD23FCCF13D078555CC8EF27DC439"
"780EB8EF376237FF668055DA476CC82956F6FEBFA051A6D927E70D826DB0338E42819F026AEBAA02"
"E0200224250B0114B12B03154E0D83030000005145571104D52DD7094C577719C7CA430D59608D5F"
"EFD10DB3E61B7C5FD3E4716224F96ED5AB4EB7F860C15347B66E23EA12E0A186A1A80B96C6E5DE05"
"416A87";
Certificate c;
deserialize_from_hexstring(str, c);
check(c, serialize_roundtrip(c));
}
TEST(Certificate, WebValidator_AuthorizationTicket1_v2)
{
const char str[] =
"02015388DEC640C6E19E010052000004B27D4D442F58E065F8D500478929BC843940F3C34D46C547"
"5803C03594E35BD7E0132FD01634E86D4F50F7F2366988E12525232D00D03E98FC21CA8E5D0AF370"
"02E0210B24030100002504010000000B0114E9DB83154CBC0203000000553C8D2B8A4E53F3D84A88"
"37BEEBE83D5C7F68484AC5EFCEEFCC7B0BC5E9531754AAF58BF90790A10F2FD11796A85E13DFFAAC"
"6073D2068465DA733994CD0C71";
Certificate c;
deserialize_from_hexstring(str, c);
check(c, serialize_roundtrip(c));
}
@@ -0,0 +1,135 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/security/v2/certificate.hpp>
#include <vanetza/security/v2/certificate_cache.hpp>
#include <vanetza/security/tests/serialization.hpp>
using namespace vanetza;
using namespace vanetza::security;
using namespace vanetza::security::v2;
class CertificateCacheTest : public ::testing::Test
{
public:
CertificateCacheTest() :
runtime(Clock::at("2018-01-03 17:15")),
cache(runtime)
{
}
Certificate build_certificate(SubjectType subject_type, uint8_t id = 0)
{
Certificate cert;
cert.subject_info.subject_type = subject_type;
cert.signer_info = HashedId8 {{ id, id, id, id, id, id, id, id }};
EcdsaSignature signature;
X_Coordinate_Only x_only;;
x_only.x.insert(x_only.x.end(), 32, 0x22);
signature.R = std::move(x_only);
signature.s.insert(signature.s.end(), 32, 0x11);
cert.signature = std::move(signature);
return cert;
}
protected:
ManualRuntime runtime;
CertificateCache cache;
};
static const HashedId8 zero_id = {{ 0, 0, 0, 0, 0, 0, 0, 0 }};
TEST_F(CertificateCacheTest, lookup)
{
const Certificate cert = build_certificate(SubjectType::Authorization_Ticket);
const HashedId8 cert_id = calculate_hash(cert);
// empty cache
EXPECT_EQ(0, cache.lookup(cert_id, SubjectType::Authorization_Ticket).size());
cache.insert(cert);
// cache only contains 'cert' and must be able to find it
EXPECT_EQ(1, cache.lookup(cert_id, SubjectType::Authorization_Ticket).size());
// cache only contains 'cert' and must not return it for other types
EXPECT_EQ(0, cache.lookup(cert_id, SubjectType::Authorization_Authority).size());
// but nothing else
HashedId8 other_id = cert_id;
other_id[3] = cert_id[3] + 1;
EXPECT_EQ(0, cache.lookup(other_id, SubjectType::Authorization_Ticket).size());
}
TEST_F(CertificateCacheTest, insert_only_some_subject_type)
{
cache.insert(build_certificate(SubjectType::Enrollment_Credential));
EXPECT_EQ(0, cache.size());
cache.insert(build_certificate(SubjectType::Authorization_Ticket));
EXPECT_EQ(1, cache.size());
cache.insert(build_certificate(SubjectType::Authorization_Authority));
EXPECT_EQ(2, cache.size());
cache.insert(build_certificate(SubjectType::Enrollment_Authority));
EXPECT_EQ(2, cache.size());
cache.insert(build_certificate(SubjectType::Root_CA));
EXPECT_EQ(2, cache.size());
cache.insert(build_certificate(SubjectType::CRL_Signer));
EXPECT_EQ(2, cache.size());
}
TEST_F(CertificateCacheTest, drop_expired)
{
const Certificate cert1 = build_certificate(SubjectType::Authorization_Ticket); // 2 seconds
const Certificate cert2 = build_certificate(SubjectType::Authorization_Authority); // 1 hour
ASSERT_NE(calculate_hash(cert1), calculate_hash(cert2));
cache.insert(cert1);
cache.insert(cert2);
ASSERT_EQ(2, cache.size());
runtime.trigger(std::chrono::seconds(3));
EXPECT_EQ(2, cache.size());
cache.lookup(zero_id, SubjectType::Authorization_Ticket); // any lookup drops expired cache entries
EXPECT_EQ(1, cache.size());
runtime.trigger(std::chrono::minutes(60));
cache.lookup(zero_id, SubjectType::Authorization_Ticket);
EXPECT_EQ(0, cache.size());
}
TEST_F(CertificateCacheTest, lookup_match_extends_lifetime)
{
const Certificate cert1 = build_certificate(SubjectType::Authorization_Ticket);
const Certificate cert2 = build_certificate(SubjectType::Authorization_Authority);
const HashedId8 id_cert2 = calculate_hash(cert2);
cache.insert(cert1);
cache.insert(cert2);
EXPECT_EQ(2, cache.size());
for (unsigned i = 0; i < 3601; ++i) {
runtime.trigger(std::chrono::seconds(1));
cache.lookup(id_cert2, SubjectType::Authorization_Authority);
}
EXPECT_EQ(1, cache.size());
EXPECT_EQ(1, cache.lookup(id_cert2, SubjectType::Authorization_Authority).size());
}
TEST_F(CertificateCacheTest, insert_extends_lifetime)
{
const Certificate cert = build_certificate(SubjectType::Authorization_Ticket);
const HashedId8 id = calculate_hash(cert);
EXPECT_NE(zero_id, id);
cache.insert(cert);
EXPECT_EQ(1, cache.size());
runtime.trigger(std::chrono::seconds(1));
cache.insert(cert);
EXPECT_EQ(1, cache.size());
cache.lookup(zero_id, SubjectType::Authorization_Ticket);
EXPECT_EQ(1, cache.size());
runtime.trigger(std::chrono::seconds(2));
cache.lookup(id, SubjectType::Authorization_Ticket);
EXPECT_EQ(1, cache.size());
}
@@ -0,0 +1,23 @@
#include <vanetza/security/v2/tests/check_basic_elements.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const Time64WithStandardDeviation& expected, const Time64WithStandardDeviation& actual)
{
EXPECT_EQ(expected.time64, actual.time64);
EXPECT_EQ(expected.log_std_dev, actual.log_std_dev);
}
void check(const IntX& expected, const IntX& actual)
{
EXPECT_EQ(expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,46 @@
#ifndef CHECK_BASIC_ELEMENTS_HPP_XLHVYJ0S
#define CHECK_BASIC_ELEMENTS_HPP_XLHVYJ0S
#include <gtest/gtest.h>
#include <vanetza/security/v2/basic_elements.hpp>
#include <vanetza/security/v2/int_x.hpp>
#include <boost/format.hpp>
#include <type_traits>
namespace vanetza
{
namespace security
{
namespace v2
{
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
void check(const T& expected, const T& actual)
{
EXPECT_EQ(expected, actual);
}
template<typename T, size_t N>
void check(const std::array<T, N>& expected, const std::array<T, N>& actual)
{
SCOPED_TRACE("array<T, N>");
for (unsigned i = 0; i < N; ++i) {
SCOPED_TRACE(boost::format("element index #%1%") % i);
check(expected[i], actual[i]);
}
}
void check(const Time64WithStandardDeviation&, const Time64WithStandardDeviation&);
void check(const IntX&, const IntX&);
// explicit template instantiations
template void check<uint8_t, 3>(const HashedId3&, const HashedId3&);
template void check<uint8_t, 8>(const HashedId8&, const HashedId8&);
template void check<Time64>(const Time64&, const Time64&);
template void check<Time32>(const Time32&, const Time32&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_BASIC_ELEMENTS_HPP_XLHVYJ0S */
@@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_certificate.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
#include <vanetza/security/v2/tests/check_signature.hpp>
#include <vanetza/security/v2/tests/check_signer_info.hpp>
#include <vanetza/security/v2/tests/check_subject_attribute.hpp>
#include <vanetza/security/v2/tests/check_subject_info.hpp>
#include <vanetza/security/v2/tests/check_validity_restriction.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const Certificate& expected, const Certificate& actual)
{
// certificate version is static, no check required
check(expected.signer_info, actual.signer_info);
check(expected.subject_info, actual.subject_info);
check(expected.subject_attributes, actual.subject_attributes, "SubjectAttribute");
check(expected.validity_restriction, actual.validity_restriction, "ValidityRestriction");
check(expected.signature, actual.signature);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,20 @@
#ifndef CHECK_CERTIFICATE_HPP_ICIHS76C
#define CHECK_CERTIFICATE_HPP_ICIHS76C
#include <vanetza/security/v2/certificate.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const Certificate&, const Certificate&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_CERTIFICATE_HPP_ICIHS76C */
@@ -0,0 +1,69 @@
#ifndef CHECK_ECC_POINT_HPP_UQH2R8WK
#define CHECK_ECC_POINT_HPP_UQH2R8WK
#include <gtest/gtest.h>
#include <vanetza/security/v2/ecc_point.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
#include <boost/variant/apply_visitor.hpp>
namespace vanetza
{
namespace security
{
/**
* \brief check two X_Coordinate_Only
* \param expected the expected value
* \param actual the actual value
*/
inline void check(const X_Coordinate_Only& expected, const X_Coordinate_Only& actual)
{
EXPECT_EQ(expected.x, actual.x);
}
/**
* \brief check two Compressed_Lsb_Y_0
* \param expected the expected value
* \param actual the actual value
*/
inline void check(const Compressed_Lsb_Y_0& expected, const Compressed_Lsb_Y_0& actual)
{
EXPECT_EQ(expected.x, actual.x);
}
/**
* \brief check two Compressed_Lsb_Y_1
* \param expected the expected value
* \param actual the actual value
*/
inline void check(const Compressed_Lsb_Y_1& expected, const Compressed_Lsb_Y_1& actual)
{
EXPECT_EQ(expected.x, actual.x);
}
/**
* \brief check two Uncompressed
* \param expected the expected value
* \param actual the actual value
*/
inline void check(const Uncompressed& expected, const Uncompressed& actual)
{
EXPECT_EQ(expected.x, actual.x);
EXPECT_EQ(expected.y, actual.y);
}
/**
* \brief check two EccPoints
* \param expected the expected value
* \param actual the actual value
*/
inline void check(const EccPoint& expected, const EccPoint& actual)
{
ASSERT_EQ(v2::get_type(expected), v2::get_type(actual));
boost::apply_visitor(check_visitor<EccPoint>(), expected, actual);
}
} // namespace security
} // namespace vanetza
#endif /* CHECK_ECC_POINT_HPP_UQH2R8WK */
@@ -0,0 +1,21 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_encryption_parameter.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const EncryptionParameter& expected, const EncryptionParameter& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
EXPECT_EQ(get_size(expected), get_size(actual));
boost::apply_visitor(check_visitor<EncryptionParameter>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,25 @@
#ifndef CHECK_ENCRYPTION_PARAMETER_HPP_5UDSKSNK
#define CHECK_ENCRYPTION_PARAMETER_HPP_5UDSKSNK
#include <vanetza/security/v2/encryption_parameter.hpp>
#include <vanetza/security/v2/tests/check_basic_elements.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/**
* \brief check if the two EncryptionParameter are equal
* \param expected the expected value
* \param actual the actual value
*/
void check(const EncryptionParameter& expected, const EncryptionParameter& actual);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_ENCRYPTION_PARAMETER_HPP_5UDSKSNK */
@@ -0,0 +1,21 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_basic_elements.hpp>
#include <vanetza/security/v2/tests/check_header_field.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const HeaderField& expected, const HeaderField& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<HeaderField>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,26 @@
#ifndef CHECK_HEADER_FIELD_HPP_DNSZT9E2
#define CHECK_HEADER_FIELD_HPP_DNSZT9E2
#include <vanetza/security/v2/header_field.hpp>
#include <vanetza/security/v2/tests/check_basic_elements.hpp>
#include <vanetza/security/v2/tests/check_encryption_parameter.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
#include <vanetza/security/v2/tests/check_recipient_info.hpp>
#include <vanetza/security/v2/tests/check_region.hpp>
#include <vanetza/security/v2/tests/check_signer_info.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const HeaderField&, const HeaderField&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_HEADER_FIELD_HPP_DNSZT9E2 */
@@ -0,0 +1,35 @@
#ifndef CHECK_LIST_HPP_1ONCXSED
#define CHECK_LIST_HPP_1ONCXSED
#include <gtest/gtest.h>
#include <boost/format.hpp>
#include <list>
#include <typeinfo>
namespace vanetza
{
namespace security
{
namespace v2
{
template<typename T>
void check(std::list<T> expected, std::list<T> actual, const char* type = typeid(T).name())
{
SCOPED_TRACE(boost::format("list<%1%>") % type);
ASSERT_EQ(expected.size(), actual.size());
std::size_t i = 0;
while (!expected.empty() && !actual.empty()) {
SCOPED_TRACE(boost::format("%1% #%2%") % type % i);
check(expected.front(), actual.front());
expected.pop_front();
actual.pop_front();
++i;
}
}
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_LIST_HPP_1ONCXSED */
@@ -0,0 +1,34 @@
#ifndef CHECK_PAYLOAD_HPP_YNRGOKGC
#define CHECK_PAYLOAD_HPP_YNRGOKGC
#include <gtest/gtest.h>
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/serialization_buffer.hpp>
#include <vanetza/security/v2/payload.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
inline void check(const PacketVariant& expected, const PacketVariant& actual)
{
ByteBuffer expected_buf, actual_buf;
serialize_into_buffer(expected, expected_buf);
serialize_into_buffer(actual, actual_buf);
EXPECT_EQ(expected_buf, actual_buf);
}
inline void check(const Payload& expected, const Payload& actual)
{
EXPECT_EQ(expected.type, actual.type);
check(expected.data, actual.data);
}
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_PAYLOAD_HPP_YNRGOKGC */
@@ -0,0 +1,45 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/v2/tests/check_ecc_point.hpp>
#include <vanetza/security/v2/tests/check_public_key.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const ecdsa_nistp256_with_sha256& expected, const ecdsa_nistp256_with_sha256& actual)
{
check(expected.public_key, actual.public_key);
}
void check(const ecies_nistp256& expected, const ecies_nistp256& actual)
{
EXPECT_EQ(expected.supported_symm_alg, actual.supported_symm_alg);
check(expected.public_key, actual.public_key);
}
void check(const PublicKey& expected, const PublicKey& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<PublicKey>(), expected, actual);
}
PublicKey create_random_public_key(int seed)
{
const std::size_t size = field_size(PublicKeyAlgorithm::ECIES_NISTP256);
EccPoint point = Uncompressed { random_byte_sequence(size, seed),
random_byte_sequence(size, seed + 1) };
ecies_nistp256 ecies;
ecies.public_key = point;
ecies.supported_symm_alg = SymmetricAlgorithm::AES128_CCM;
return ecies;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,24 @@
#ifndef CHECK_PUBLIC_KEY_HPP_3HUSMPTE
#define CHECK_PUBLIC_KEY_HPP_3HUSMPTE
#include <vanetza/security/v2/public_key.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const ecdsa_nistp256_with_sha256&, const ecdsa_nistp256_with_sha256&);
void check(const ecies_nistp256&, const ecies_nistp256&);
void check(const PublicKey&, const PublicKey&);
PublicKey create_random_public_key(int seed = 0);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_PUBLIC_KEY_HPP_3HUSMPTE */
@@ -0,0 +1,39 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_ecc_point.hpp>
#include <vanetza/security/v2/tests/check_recipient_info.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const RecipientInfo& expected, const RecipientInfo& actual)
{
EXPECT_EQ(expected.cert_id, actual.cert_id);
check(expected.enc_key, actual.enc_key);
}
void check(const EciesEncryptedKey& expected, const EciesEncryptedKey& actual)
{
check(expected.v, actual.v);
EXPECT_EQ(expected.c, actual.c);
EXPECT_EQ(expected.t, actual.t);
}
void check(const OpaqueKey& expected, const OpaqueKey& actual)
{
EXPECT_EQ(expected.data, actual.data);
}
void check(const Key& expected, const Key& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<Key>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,22 @@
#ifndef CHECK_RECIPIENT_INFO_HPP_NMX7BFYV
#define CHECK_RECIPIENT_INFO_HPP_NMX7BFYV
#include <vanetza/security/v2/recipient_info.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const EciesEncryptedKey&, const EciesEncryptedKey&);
void check(const OpaqueKey&, const OpaqueKey&);
void check(const Key&, const Key&);
void check(const RecipientInfo&, const RecipientInfo&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_RECIPIENT_INFO_HPP_NMX7BFYV */
@@ -0,0 +1,87 @@
#include <gtest/gtest.h>
#include <vanetza/common/annotation.hpp>
#include <vanetza/security/v2/tests/check_region.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
#include <boost/format.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const ThreeDLocation& expected, const ThreeDLocation& actual)
{
EXPECT_EQ(expected.latitude, actual.latitude);
EXPECT_EQ(expected.longitude, actual.longitude);
EXPECT_EQ(expected.elevation, actual.elevation);
}
void check(const TwoDLocation& expected, const TwoDLocation& actual)
{
EXPECT_EQ(expected.longitude, actual.longitude);
EXPECT_EQ(expected.latitude, actual.latitude);
}
void check(const NoneRegion& expected, const NoneRegion& actual)
{
mark_unused(expected);
mark_unused(actual);
SCOPED_TRACE("None");
}
void check(const CircularRegion& expected, const CircularRegion& actual)
{
SCOPED_TRACE("CiruclarRegion");
check(expected.center, actual.center);
EXPECT_EQ(expected.radius, actual.radius);
}
void check(const RectangularRegion& expected, const RectangularRegion& actual)
{
SCOPED_TRACE("RectangularRegion");
check(expected.northwest, actual.northwest);
check(expected.southeast, actual.southeast);
}
void check(std::list<RectangularRegion> expected, std::list<RectangularRegion> actual)
{
SCOPED_TRACE("list<RectangularRegion>");
ASSERT_EQ(expected.size(), actual.size());
for (std::size_t i = 0, j = expected.size(); i < j; ++i) {
SCOPED_TRACE(boost::format("Rectangle #%1%") % i);
check(expected.front(), actual.front());
expected.pop_front();
actual.pop_front();
}
}
void check(PolygonalRegion expected, PolygonalRegion actual)
{
SCOPED_TRACE("PolygonalRegion");
ASSERT_EQ(expected.size(), actual.size());
for (std::size_t i = 0, j = expected.size(); i < j; ++i) {
SCOPED_TRACE(boost::format("Coordinate #%1%") % i);
check(expected.front(), actual.front());
expected.pop_front();
actual.pop_front();
}
}
void check(const IdentifiedRegion& expected, const IdentifiedRegion& actual)
{
EXPECT_EQ(expected.region_dictionary, actual.region_dictionary);
EXPECT_EQ(expected.region_identifier, actual.region_identifier);
EXPECT_EQ(expected.local_region, actual.local_region);
}
void check(const GeographicRegion& expected, const GeographicRegion& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<GeographicRegion>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,65 @@
#ifndef CHECK_REGION_HPP_UFSV2RZ5
#define CHECK_REGION_HPP_UFSV2RZ5
#include <vanetza/security/v2/region.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
/** \brief check two TwoDLocations
* \param expected the expected value
* \param actual the actual value
*/
void check(const TwoDLocation& expected, const TwoDLocation& actual);
/** \brief check two ThreeDLocations
* \param expected the expected value
* \param actual the actual value
*/
void check(const ThreeDLocation&, const ThreeDLocation&);
/** \brief check two CircularRegions
* \param expected the expected value
* \param actual the actual value
*/
void check(const CircularRegion& expected, const CircularRegion& actual);
/** \brief check two RectangularRegions
* \param expected the expected value
* \param actual the actual value
*/
void check(const RectangularRegion& expected, const RectangularRegion& actual);
/** \brief check two std::list<RectangularRegion>
* \param expected the expected list
* \param actual the actual value
*/
void check(std::list<RectangularRegion> expected, std::list<RectangularRegion> actual);
/** \brief check two PolygonalRegions
* \param expected the expected value
* \param actual the actual value
*/
void check(PolygonalRegion expected, PolygonalRegion actual);
/** \brief check two IdentifiedRegions
* \param expected the expected value
* \param actual the actual value
*/
void check(const IdentifiedRegion& expected, const IdentifiedRegion& actual);
/** \brief check two GeographicRegion
* \param expected the expected value
* \param actual the actual value
*/
void check(const GeographicRegion& expected, const GeographicRegion& actual);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_REGION_HPP_UFSV2RZ5 */
@@ -0,0 +1,25 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_header_field.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
#include <vanetza/security/v2/tests/check_payload.hpp>
#include <vanetza/security/v2/tests/check_secured_message.hpp>
#include <vanetza/security/v2/tests/check_trailer_field.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const SecuredMessage& expected, const SecuredMessage& actual)
{
SCOPED_TRACE("v2::SecuredMessage");
check(expected.header_fields, actual.header_fields);
check(expected.trailer_fields, actual.trailer_fields);
check(expected.payload, actual.payload);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,19 @@
#ifndef CHECK_SECURED_MESSAGE_HPP_1YPFNXQA
#define CHECK_SECURED_MESSAGE_HPP_1YPFNXQA
#include <vanetza/security/v2/secured_message.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const SecuredMessage&, const SecuredMessage&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_SECURED_MESSAGE_HPP_1YPFNXQA */
@@ -0,0 +1,45 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/signature.hpp>
#include <vanetza/security/v2/tests/check_ecc_point.hpp>
#include <vanetza/security/v2/tests/check_signature.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
#include <boost/variant/apply_visitor.hpp>
namespace vanetza
{
namespace security
{
void check(const EcdsaSignature& expected, const EcdsaSignature& actual)
{
check(expected.R, actual.R);
EXPECT_EQ(expected.s, actual.s);
}
void check(const EcdsaSignatureFuture& expected, const EcdsaSignatureFuture& actual)
{
check(expected.get(), actual.get());
}
EcdsaSignature create_random_ecdsa_signature(int seed)
{
const std::size_t field = v2::field_size(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
EcdsaSignature signature;
signature.s = random_byte_sequence(field, seed);
signature.R = X_Coordinate_Only { random_byte_sequence(field, ~seed) };
return signature;
}
namespace v2
{
void check(const Signature& expected, const Signature& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
// TODO boost::apply_visitor(check_visitor<Signature>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,30 @@
#ifndef CHECK_SIGNATURE_HPP_7RESWTUO
#define CHECK_SIGNATURE_HPP_7RESWTUO
#include <vanetza/security/v2/signature.hpp>
namespace vanetza
{
namespace security
{
void check(const EcdsaSignature&, const EcdsaSignature&);
void check(const EcdsaSignatureFuture&, const EcdsaSignatureFuture&);
/**
* \brief create a random EcdsaSignature
* \param seed the optional seed for the RNG
* \return created signature
*/
EcdsaSignature create_random_ecdsa_signature(int seed = 0);
namespace v2
{
void check(const Signature&, const Signature&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_SIGNATURE_HPP_7RESWTUO */
@@ -0,0 +1,38 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_basic_elements.hpp>
#include <vanetza/security/v2/tests/check_certificate.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
#include <vanetza/security/v2/tests/check_signer_info.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const std::nullptr_t&, const std::nullptr_t&)
{
}
void check(const CertificateDigestWithOtherAlgorithm& expected, const CertificateDigestWithOtherAlgorithm& actual)
{
EXPECT_EQ(expected.algorithm, actual.algorithm);
EXPECT_EQ(expected.digest, actual.digest);
}
void check(const boost::recursive_wrapper<Certificate>& expected, const boost::recursive_wrapper<Certificate>& actual)
{
check(expected.get(), actual.get());
}
void check(const SignerInfo& expected, const SignerInfo& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<SignerInfo>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,22 @@
#ifndef CHECK_SIGNER_INFO_HPP_S2AYJSYC
#define CHECK_SIGNER_INFO_HPP_S2AYJSYC
#include <vanetza/security/v2/signer_info.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const std::nullptr_t&, const std::nullptr_t&);
void check(const CertificateDigestWithOtherAlgorithm&, const CertificateDigestWithOtherAlgorithm&);
void check(const boost::recursive_wrapper<Certificate>&, const boost::recursive_wrapper<Certificate>&);
void check(const SignerInfo&, const SignerInfo&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_SIGNER_INFO_HPP_S2AYJSYC */
@@ -0,0 +1,76 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
#include <vanetza/security/v2/tests/check_public_key.hpp>
#include <vanetza/security/v2/tests/check_subject_attribute.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const VerificationKey& expected, const VerificationKey& actual)
{
SCOPED_TRACE("VerificationKey");
check(expected.key, actual.key);
}
void check(const EncryptionKey& expected, const EncryptionKey& actual)
{
SCOPED_TRACE("EncryptionKey");
check(expected.key, actual.key);
}
void check(const SubjectAssurance& expected, const SubjectAssurance& actual)
{
EXPECT_EQ(expected.raw, actual.raw);
}
void check(const ItsAidSsp& expected, const ItsAidSsp& actual)
{
SCOPED_TRACE("ItsAidSsp");
EXPECT_EQ(expected.its_aid, actual.its_aid);
EXPECT_EQ(expected.service_specific_permissions, actual.service_specific_permissions);
}
void check(const SubjectAttribute& expected, const SubjectAttribute& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<SubjectAttribute>(), expected, actual);
}
VerificationKey create_random_verification_key(int seed)
{
VerificationKey key;
key.key = create_random_public_key(seed);
return key;
}
EncryptionKey create_random_encryption_key(int seed)
{
EncryptionKey key;
key.key = create_random_public_key(seed);
return key;
}
IntX create_random_its_aid(int seed)
{
IntX result;
result.set((seed ^ (seed >> 23)) & 0xffffff);
return result;
}
ItsAidSsp create_random_its_aid_ssp(int seed)
{
ItsAidSsp result;
result.its_aid.set(~seed & 0xffffff);
result.service_specific_permissions = random_byte_sequence(10, seed);
return result;
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,32 @@
#ifndef CHECK_SUBJECT_ATTRIBUTE_HPP_40Z9HDV2
#define CHECK_SUBJECT_ATTRIBUTE_HPP_40Z9HDV2
#include <vanetza/security/v2/subject_attribute.hpp>
#include <vanetza/security/v2/tests/check_basic_elements.hpp>
#include <vanetza/security/v2/tests/check_ecc_point.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const VerificationKey&, const VerificationKey&);
void check(const EncryptionKey&, const EncryptionKey&);
void check(const SubjectAssurance&, const SubjectAssurance&);
void check(const ItsAidSsp&, const ItsAidSsp&);
void check(const SubjectAttribute&, const SubjectAttribute&);
VerificationKey create_random_verification_key(int seed = 0);
EncryptionKey create_random_encryption_key(int seed = 0);
IntX create_random_its_aid(int seed = 0);
ItsAidSsp create_random_its_aid_ssp(int seed = 0);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_SUBJECT_ATTRIBUTE_HPP_40Z9HDV2 */
@@ -0,0 +1,25 @@
#ifndef CHECK_SUBJECT_INFO_HPP_LCHGB2G3
#define CHECK_SUBJECT_INFO_HPP_LCHGB2G3
#include <gtest/gtest.h>
#include <vanetza/security/v2/subject_info.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
inline void check(const SubjectInfo& expected, const SubjectInfo& actual)
{
EXPECT_EQ(expected.subject_type, actual.subject_type);
EXPECT_EQ(expected.subject_name, actual.subject_name);
}
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_SUBJECT_INFO_HPP_LCHGB2G3 */
@@ -0,0 +1,24 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_signature.hpp>
#include <vanetza/security/v2/tests/check_trailer_field.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
#include <boost/mpl/size.hpp>
#include <boost/variant/get.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const TrailerField& expected, const TrailerField& actual)
{
static_assert(boost::mpl::size<typename TrailerField::types>::value == 1,
"Simple check works only for TrailerField variant with one possible type");
check(boost::get<Signature>(expected), boost::get<Signature>(actual));
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,19 @@
#ifndef CHECK_TRAILER_FIELD_HPP_5LY4T0VT
#define CHECK_TRAILER_FIELD_HPP_5LY4T0VT
#include <vanetza/security/v2/trailer_field.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(const TrailerField&, const TrailerField&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_TRAILER_FIELD_HPP_5LY4T0VT */
@@ -0,0 +1,42 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/tests/check_region.hpp>
#include <vanetza/security/v2/tests/check_validity_restriction.hpp>
#include <vanetza/security/v2/tests/check_visitor.hpp>
#include <boost/format.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(EndValidity expected, EndValidity actual)
{
SCOPED_TRACE("EndValidity");
EXPECT_EQ(expected, actual);
}
void check(const StartAndEndValidity& expected, const StartAndEndValidity& actual)
{
SCOPED_TRACE("StartAndEndValidity");
EXPECT_EQ(expected.start_validity, actual.start_validity);
EXPECT_EQ(expected.end_validity, actual.end_validity);
}
void check(const StartAndDurationValidity& expected, const StartAndDurationValidity& actual)
{
SCOPED_TRACE("StartAndDurationValidity");
EXPECT_EQ(expected.start_validity, actual.start_validity);
EXPECT_EQ(expected.duration.raw(), actual.duration.raw());
}
void check(const ValidityRestriction& expected, const ValidityRestriction& actual)
{
ASSERT_EQ(get_type(expected), get_type(actual));
boost::apply_visitor(check_visitor<ValidityRestriction>(), expected, actual);
}
} // namespace v2
} // namespace security
} // namespace vanetza
@@ -0,0 +1,23 @@
#ifndef CHECK_VALIDITY_RESTRICTION_HPP_W8OY9526
#define CHECK_VALIDITY_RESTRICTION_HPP_W8OY9526
#include <vanetza/security/v2/validity_restriction.hpp>
#include <vanetza/security/v2/tests/check_list.hpp>
namespace vanetza
{
namespace security
{
namespace v2
{
void check(EndValidity, EndValidity);
void check(const StartAndEndValidity&, const StartAndEndValidity&);
void check(const StartAndDurationValidity&, const StartAndDurationValidity&);
void check(const ValidityRestriction&, const ValidityRestriction&);
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* CHECK_VALIDITY_RESTRICTION_HPP_W8OY9526 */
@@ -0,0 +1,33 @@
#ifndef CHECK_VISITOR_HPP_YJ7UPXCB
#define CHECK_VISITOR_HPP_YJ7UPXCB
#include <gtest/gtest.h>
#include <boost/variant/static_visitor.hpp>
#include <typeinfo>
namespace vanetza
{
namespace security
{
template<class VARIANT>
struct check_visitor : public boost::static_visitor<>
{
template<typename R, typename S>
void operator()(const R&, const S&) const
{
FAIL() << typeid(R).name() << " differs from " << typeid(S).name();
}
template<typename R>
void operator()(const R& lhs, const R& rhs) const
{
using namespace vanetza::security::v2;
check(lhs, rhs);
}
};
} // namespace security
} // namespace vanetza
#endif /* CHECK_VISITOR_HPP_YJ7UPXCB */
@@ -0,0 +1,244 @@
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/security/v2/certificate_cache.hpp>
#include <vanetza/security/v2/default_certificate_validator.hpp>
#include <vanetza/security/v2/naive_certificate_provider.hpp>
#include <vanetza/security/v2/trust_store.hpp>
#include <vanetza/units/angle.hpp>
#include <boost/variant/get.hpp>
#include <gtest/gtest.h>
using namespace vanetza;
using namespace vanetza::security;
using namespace vanetza::security::v2;
class DefaultCertificateValidatorTest : public ::testing::Test
{
public:
DefaultCertificateValidatorTest() :
runtime(Clock::at("2016-08-01 00:00")),
backend(create_backend("default")),
cert_provider(runtime),
cert_cache(runtime),
cert_validator(*backend, cert_cache, trust_store)
{
trust_store.insert(cert_provider.root_certificate());
cert_cache.insert(cert_provider.aa_certificate());
}
protected:
ManualRuntime runtime;
std::unique_ptr<Backend> backend;
NaiveCertificateProvider cert_provider;
std::vector<Certificate> roots;
TrustStore trust_store;
CertificateCache cert_cache;
DefaultCertificateValidator cert_validator;
};
TEST_F(DefaultCertificateValidatorTest, invalid_signer_info)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.signer_info = cert_provider.own_chain().front();
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Invalid_Signer, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, missing_subject_assurance)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_attribute(SubjectAttributeType::Assurance_Level);
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Missing_Subject_Assurance, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, inconsistent_subject_assurance)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_attribute(SubjectAttributeType::Assurance_Level);
cert.subject_attributes.push_back(SubjectAssurance(0xE0)); // higher level
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Inconsistent_With_Signer, validity.reason());
cert.remove_attribute(SubjectAttributeType::Assurance_Level);
cert.subject_attributes.push_back(SubjectAssurance(0x03)); // same level, higher confidence
cert_provider.sign_authorization_ticket(cert);
validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Inconsistent_With_Signer, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, validity_time_no_constraint)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Broken_Time_Period, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, validity_time_start_and_end)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
StartAndEndValidity restriction;
restriction.start_validity = convert_time32(runtime.now() - std::chrono::hours(1));
restriction.end_validity = convert_time32(runtime.now() + std::chrono::hours(23));
cert.validity_restriction.push_back(restriction);
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_TRUE(validity);
}
TEST_F(DefaultCertificateValidatorTest, validity_time_start_and_duration)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
StartAndDurationValidity restriction;
restriction.start_validity = convert_time32(runtime.now() - std::chrono::hours(1));
restriction.duration = Duration(23, Duration::Units::Hours);
cert.validity_restriction.push_back(restriction);
cert_provider.sign_authorization_ticket(cert);
// all certificates must use time_start_and_end as restriction
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
ASSERT_EQ(CertificateInvalidReason::Broken_Time_Period, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, validity_time_end)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
EndValidity restriction = convert_time32(runtime.now() + std::chrono::hours(23));
cert.validity_restriction.push_back(restriction);
cert_provider.sign_authorization_ticket(cert);
// all certificates must use time_start_and_end as restriction
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
ASSERT_EQ(CertificateInvalidReason::Broken_Time_Period, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, validity_time_two_constraints)
{
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
// add first constraint
StartAndEndValidity start_and_end_validity;
start_and_end_validity.start_validity = convert_time32(runtime.now() - std::chrono::hours(1));
start_and_end_validity.end_validity = convert_time32(runtime.now() + std::chrono::hours(23));
cert.validity_restriction.push_back(start_and_end_validity);
// add second constraint
StartAndDurationValidity start_and_duration_validity;
start_and_duration_validity.start_validity = convert_time32(runtime.now() - std::chrono::hours(1));
start_and_duration_validity.duration = Duration(23, Duration::Units::Hours);
cert.validity_restriction.push_back(start_and_duration_validity);
// re-sign certificate
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Broken_Time_Period, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, validity_time_consistency_with_parent)
{
// The generated authorization ticket's start time is prior to the AA certificate's start time
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
StartAndEndValidity restriction;
restriction.start_validity = convert_time32(runtime.now() - std::chrono::hours(3));
restriction.end_validity = convert_time32(runtime.now() + std::chrono::hours(23));
cert.validity_restriction.push_back(restriction);
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Inconsistent_With_Signer, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, validity_time_consistency_start_and_end)
{
// The generated authorization ticket's start time is prior to the AA certificate's start time
Certificate cert = cert_provider.generate_authorization_ticket();
cert.remove_restriction(ValidityRestrictionType::Time_Start_And_End);
StartAndEndValidity restriction;
restriction.start_validity = convert_time32(runtime.now() + std::chrono::hours(3));
restriction.end_validity = convert_time32(runtime.now() - std::chrono::hours(23));
cert.validity_restriction.push_back(restriction);
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Broken_Time_Period, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, reject_additional_permissions)
{
Certificate cert = cert_provider.generate_authorization_ticket();
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_TRUE(validity);
cert.add_permission(16513 /* deprecated, so won't be used */, ByteBuffer({}));
cert_provider.sign_authorization_ticket(cert);
validity = cert_validator.check_certificate(cert);
ASSERT_FALSE(validity);
EXPECT_EQ(CertificateInvalidReason::Inconsistent_With_Signer, validity.reason());
}
TEST_F(DefaultCertificateValidatorTest, accept_permission_subset_permutation)
{
// We test both orders here, so we're not dependent on changes to the certificate provider order.
Certificate cert = cert_provider.generate_authorization_ticket();
// Order 1
cert.remove_attribute(SubjectAttributeType::ITS_AID_SSP_List);
cert.add_permission(aid::GN_MGMT, ByteBuffer({}));
cert.add_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
cert_provider.sign_authorization_ticket(cert);
CertificateValidity validity = cert_validator.check_certificate(cert);
ASSERT_TRUE(validity);
// Order 2
cert.remove_attribute(SubjectAttributeType::ITS_AID_SSP_List);
cert.add_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
cert.add_permission(aid::GN_MGMT, ByteBuffer({}));
cert_provider.sign_authorization_ticket(cert);
validity = cert_validator.check_certificate(cert);
ASSERT_TRUE(validity);
// Definite subset
cert.remove_attribute(SubjectAttributeType::ITS_AID_SSP_List);
cert.add_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
cert_provider.sign_authorization_ticket(cert);
validity = cert_validator.check_certificate(cert);
ASSERT_TRUE(validity);
}
@@ -0,0 +1,49 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/v2/ecc_point.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <vanetza/security/v2/tests/check_ecc_point.hpp>
using vanetza::ByteBuffer;
using namespace vanetza::security;
using namespace vanetza::security::v2;
using namespace vanetza;
using namespace std;
static const std::size_t length = field_size(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
EccPoint serialize_roundtrip(const EccPoint& point)
{
EccPoint outPoint;
std::stringstream stream;
OutputArchive oa(stream);
serialize(oa, point, PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
InputArchive ia(stream);
deserialize(ia, outPoint, PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
return outPoint;
}
TEST(EccPoint, uncompressed)
{
EccPoint point = Uncompressed { random_byte_sequence(length, 1), random_byte_sequence(length, 2) };
EccPoint outPoint = serialize_roundtrip(point);
check(point, outPoint);
EXPECT_EQ(EccPointType::Uncompressed, get_type(outPoint));
}
TEST(EccPoint, Compressed_Lsb_Y_0)
{
EccPoint point = Compressed_Lsb_Y_0 { random_byte_sequence(length, 3) };
EccPoint outPoint = serialize_roundtrip(point);
check(point, outPoint);
EXPECT_EQ(EccPointType::Compressed_Lsb_Y_0, get_type(outPoint));
}
TEST(EccPoint, X_Coordinate_Only)
{
EccPoint point = X_Coordinate_Only { random_byte_sequence(length, 4) };
EccPoint outPoint = serialize_roundtrip(point);
check(point, outPoint);
EXPECT_EQ(EccPointType::X_Coordinate_Only, get_type(outPoint));
}
@@ -0,0 +1,17 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/v2/encryption_parameter.hpp>
#include <vanetza/security/v2/tests/check_encryption_parameter.hpp>
#include <vanetza/security/tests/serialization.hpp>
#include <algorithm>
using namespace vanetza::security::v2;
TEST(EncryptionParameter, Nonce)
{
Nonce nonce;
auto random = vanetza::random_byte_sequence(nonce.size());
std::copy_n(random.begin(), nonce.size(), nonce.begin());
EncryptionParameter param = nonce;
check(param, serialize_roundtrip(param));
}
@@ -0,0 +1,94 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/v2/header_field.hpp>
#include <vanetza/security/v2/tests/check_header_field.hpp>
#include <vanetza/security/v2/tests/check_signature.hpp>
#include <vanetza/security/tests/serialization.hpp>
#include <algorithm>
using namespace vanetza;
using namespace vanetza::security;
using namespace vanetza::security::v2;
TEST(HeaderField, Serialize)
{
std::list<HeaderField> list;
std::list<Certificate> certificates;
for (unsigned i = 0; i < 2; ++i) {
auto rand_gen = random_byte_generator(i + 8 * 3);
Certificate cert;
HashedId8 cert_digest;
std::generate(cert_digest.begin(), cert_digest.end(), rand_gen);
cert.signer_info = cert_digest;
cert.subject_info = { SubjectType::Enrollment_Credential, random_byte_sequence(28, i) };
cert.signature = create_random_ecdsa_signature(i + 8);
certificates.push_back(cert);
}
list.push_back(SignerInfo { certificates });
list.push_back(Time64 { 983 });
Time64WithStandardDeviation time_dev;
time_dev.log_std_dev = 1;
time_dev.time64 = 2000;
list.push_back(time_dev);
list.push_back(Time32 { 434 });
ThreeDLocation loc;
loc.latitude.from_value(838);
loc.longitude.from_value(37);
loc.elevation = {{ 83, 17 }};
list.push_back(loc);
std::list<HashedId3> hashed;
for (unsigned i = 0; i < 3; ++i) {
HashedId3 id;
std::generate(id.begin(), id.end(), random_byte_generator(i + 8943));
hashed.push_back(id);
}
list.push_back(hashed);
list.push_back(IntX { 43 });
Nonce nonce;
std::generate(nonce.begin(), nonce.end(), random_byte_generator(22));
list.push_back(EncryptionParameter { nonce });
std::list<RecipientInfo> recipients;
for (unsigned i = 0; i < 2; ++i) {
const std::size_t length = field_size(PublicKeyAlgorithm::ECIES_NISTP256);
auto rand_gen = random_byte_generator(i + 93);
RecipientInfo info;
EciesEncryptedKey key;
std::generate(info.cert_id.begin(), info.cert_id.end(), rand_gen);
key.c = random_byte_sequence(field_size(SymmetricAlgorithm::AES128_CCM), rand_gen());
std::generate(key.t.begin(), key.t.end(), rand_gen);
key.v = Uncompressed {
random_byte_sequence(length, rand_gen()),
random_byte_sequence(length, rand_gen()) };
info.enc_key = key;
recipients.push_back(info);
}
list.push_back(recipients);
check(list, serialize_roundtrip(list));
}
TEST(HeaderField, WebValidator_SecuredMessage3_adapted)
{
const char str[] =
"810180020201A8ED6DF65B0E6D6A010080940000040209B0434163CCBAFDD34A45333E418FB96C"
"05BBE0E7E1D755D40D0B4BBE8DA508EC2F2723B7ADF0F27C39F3AECFF0783C196F9961F8821E6294"
"375D9294CD6A01000452113CE698DB081491675DF8FFE81C23EA5D0071B2D2BF0E0DA4ADA0CDA582"
"59CA5D999200B6565E194EDAB8BD3DCA863F2DDF39C13E7A0375ECE2566C5EB8C60200210AC04080"
"0101C0408101010F01099EB20109B1270003040100960000008DA1F3F9F35E04C3DE77D7438988A8"
"D57EBE44DAA021A4269E297C177C9CFE458E128EC290785D6631961625020943B6D87DAA54919A98"
"F7865709929A7C6E480000009373CF482D400502";
std::list<HeaderField> list;
const size_t deserialize_length = deserialize_from_hexstring(str, list);
EXPECT_EQ(257, deserialize_length);
EXPECT_EQ(257, get_size(list));
EXPECT_EQ(3, list.size());
}
@@ -0,0 +1,107 @@
#include <vanetza/security/v2/int_x.hpp>
#include <vanetza/security/v2/length_coding.hpp>
#include <vanetza/security/tests/serialization.hpp>
#include <gtest/gtest.h>
using vanetza::ByteBuffer;
using vanetza::security::v2::IntX;
TEST(IntX, set_and_get)
{
IntX a;
EXPECT_EQ(0, a.get());
a.set(static_cast<int8_t>(89));
EXPECT_EQ(89, a.get());
a.set(static_cast<uint32_t>(0x12345678));
EXPECT_EQ(0x12345678, a.get());
}
TEST(IntX, get_size)
{
IntX a;
EXPECT_EQ(1, get_size(a));
a.set(static_cast<int8_t>(89));
EXPECT_EQ(1, get_size(a));
a.set(127);
EXPECT_EQ(1, get_size(a));
a.set(128);
EXPECT_EQ(2, get_size(a));
a.set(0x23);
EXPECT_EQ(1, get_size(a));
a.set(static_cast<uint32_t>(0x00130033));
EXPECT_EQ(3, get_size(a));
a.set(static_cast<uint32_t>(0x00230033));
EXPECT_EQ(4, get_size(a));
a.set(static_cast<uint32_t>(0x33003300));
EXPECT_EQ(5, get_size(a));
}
TEST(IntX, encode)
{
IntX a;
EXPECT_EQ((ByteBuffer { 0x00 }), a.encode());
a.set(127);
EXPECT_EQ((ByteBuffer { 127 }), a.encode());
a.set(128);
EXPECT_EQ((ByteBuffer { 0x80, 128 }), a.encode());
a.set(0x00120034);
EXPECT_EQ((ByteBuffer { 0xd2, 0x00, 0x34 }), a.encode());
a.set(0x00320034);
EXPECT_EQ((ByteBuffer { 0xe0, 0x32, 0x00, 0x34 }), a.encode());
}
TEST(IntX, decode)
{
ByteBuffer buf { 0xe0, 0x32, 0x00, 0x34 };
auto decoded = IntX::decode(buf);
ASSERT_TRUE(!!decoded);
EXPECT_EQ(0x320034, decoded->get());
}
TEST(IntX, decode_one_null_byte)
{
ByteBuffer buf { 0x00 };
auto decoded = IntX::decode(buf);
ASSERT_TRUE(!!decoded);
EXPECT_EQ(0, decoded->get());
}
TEST(IntX, decode_empty)
{
ByteBuffer buf;
auto decoded = IntX::decode(buf);
EXPECT_FALSE(!!decoded);
}
TEST(IntX, decode_broken)
{
ByteBuffer buf { 0xff, 0xff };
auto decoded = IntX::decode(buf);
EXPECT_FALSE(!!decoded);
}
TEST(IntX, serialization)
{
IntX x;
x.set(0);
EXPECT_EQ(x, serialize_roundtrip(x));
x.set(255);
EXPECT_EQ(x, serialize_roundtrip(x));
x.set(0x123456);
EXPECT_EQ(x, serialize_roundtrip(x));
}
@@ -0,0 +1,128 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_buffer_sink.hpp>
#include <vanetza/common/byte_buffer_source.hpp>
#include <vanetza/security/v2/length_coding.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <boost/iostreams/stream_buffer.hpp>
using vanetza::ByteBuffer;
using vanetza::InputArchive;
using vanetza::OutputArchive;
using namespace vanetza::security::v2;
TEST(LengthEncoding, count_leading_ones)
{
EXPECT_EQ(0, count_leading_ones(0x00));
EXPECT_EQ(1, count_leading_ones(0x80));
EXPECT_EQ(1, count_leading_ones(0x81));
EXPECT_EQ(1, count_leading_ones(0xa0));
EXPECT_EQ(1, count_leading_ones(0xa1));
EXPECT_EQ(2, count_leading_ones(0xd3));
EXPECT_EQ(3, count_leading_ones(0xe8));
EXPECT_EQ(7, count_leading_ones(0xfe));
EXPECT_EQ(8, count_leading_ones(0xff));
}
TEST(LengthEncoding, encode_length)
{
EXPECT_EQ(ByteBuffer { 0x00 }, encode_length(0));
EXPECT_EQ(ByteBuffer { 5 }, encode_length(5));
EXPECT_EQ(ByteBuffer { 123 }, encode_length(123));
EXPECT_EQ(ByteBuffer { 127 }, encode_length(127));
EXPECT_EQ((ByteBuffer { 0x80, 128 }), encode_length(128));
EXPECT_EQ((ByteBuffer { 0xbf, 0xff }), encode_length(0x3fff));
EXPECT_EQ((ByteBuffer { 0x81, 0xff }), encode_length(0x01ff));
EXPECT_EQ((ByteBuffer { 0xdf, 0xff, 0xff }), encode_length(0x1fffff));
EXPECT_EQ((ByteBuffer { 0xe0, 0x20, 0x00, 0x00 }), encode_length(0x200000));
EXPECT_EQ(ByteBuffer { 0x0a }, encode_length(10));
EXPECT_EQ((ByteBuffer { 0x88, 0x88 }), encode_length(2184));
}
TEST(LengthEncoding, decode_length_empty_buffer)
{
ByteBuffer buffer;
auto decoded = decode_length(buffer);
EXPECT_EQ(buffer.end(), std::get<0>(decoded));
EXPECT_EQ(0, std::get<1>(decoded));
}
TEST(LengthEncoding, decode_length_zero_size)
{
ByteBuffer buffer { 0x00, 0xC0, 0xFF, 0xEE };
auto decoded = decode_length(buffer);
EXPECT_EQ(std::next(buffer.begin()), std::get<0>(decoded));
EXPECT_EQ(0, std::get<1>(decoded));
}
TEST(LengthEncoding, decode_length_prefix_too_long)
{
ByteBuffer buffer { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xba, 0xbe };
auto decoded = decode_length(buffer);
EXPECT_EQ(buffer.begin(), std::get<0>(decoded));
EXPECT_EQ(0, std::get<1>(decoded));
}
TEST(LengthEncoding, decode_length_buffer_too_short)
{
ByteBuffer buffer { 0x02, 0xde };
auto decoded_tuple = decode_length(buffer);
EXPECT_EQ(std::next(buffer.begin()), std::get<0>(decoded_tuple));
EXPECT_EQ(2, std::get<1>(decoded_tuple));
}
TEST(LengthEncoding, decode_length_good)
{
ByteBuffer buffer { 0xe0, 0x00, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde };
auto decoded_tuple = decode_length(buffer);
EXPECT_EQ(std::next(buffer.begin(), 4), std::get<0>(decoded_tuple));
EXPECT_EQ(4, std::get<1>(decoded_tuple));
}
TEST(LengthEncoding, serialize_length)
{
ByteBuffer buf;
auto serialize_length_into_buffer = [&buf](std::size_t length) {
vanetza::byte_buffer_sink sink(buf);
boost::iostreams::stream_buffer<vanetza::byte_buffer_sink> stream(sink);
OutputArchive oa(stream);
serialize_length(oa, length);
};
serialize_length_into_buffer(0x200000);
ASSERT_EQ(4, buf.size());
EXPECT_EQ(0xE0, buf[0]);
EXPECT_EQ(0x20, buf[1]);
EXPECT_EQ(0x00, buf[2]);
EXPECT_EQ(0x00, buf[3]);
buf.clear();
serialize_length_into_buffer(128);
ASSERT_EQ(2, buf.size());
EXPECT_EQ(0x80, buf[0]);
EXPECT_EQ(0x80, buf[1]);
}
TEST(LengthEncoding, length_coding_size)
{
EXPECT_EQ(1, length_coding_size(0x3f));
EXPECT_EQ(1, length_coding_size(0x20));
EXPECT_EQ(1, length_coding_size(0x7f));
EXPECT_EQ(1, length_coding_size(0x40));
EXPECT_EQ(2, length_coding_size(0x3fff));
EXPECT_EQ(2, length_coding_size(0x2000));
EXPECT_EQ(3, length_coding_size(0x7fff));
EXPECT_EQ(3, length_coding_size(0x4000));
EXPECT_EQ(3, length_coding_size(0x1fffff));
EXPECT_EQ(4, length_coding_size(0x3fffff));
}
TEST(LengthEncoding, WebValidator_length)
{
ByteBuffer buf {{ 0x81, 0x03 }};
vanetza::byte_buffer_source source(buf);
boost::iostreams::stream_buffer<vanetza::byte_buffer_source> stream(source);
InputArchive ia(stream);
size_t length = deserialize_length(ia);
EXPECT_EQ(259, length);
}
@@ -0,0 +1,91 @@
#include <vanetza/common/clock.hpp>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/security/v2/default_certificate_validator.hpp>
#include <vanetza/security/v2/naive_certificate_provider.hpp>
#include <boost/variant/get.hpp>
#include <gtest/gtest.h>
using namespace vanetza;
using namespace vanetza::security;
using namespace vanetza::security::v2;
using boost::get;
class NaiveCertificateProviderTest : public ::testing::Test
{
public:
NaiveCertificateProviderTest() : runtime(Clock::at("2016-08-01 00:00")), cert_provider(runtime)
{
}
protected:
ManualRuntime runtime;
NaiveCertificateProvider cert_provider;
};
TEST_F(NaiveCertificateProviderTest, own_certificate)
{
Certificate signed_certificate = cert_provider.own_certificate();
// Check signature
EXPECT_EQ(2 * field_size(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256),
extract_signature_buffer(signed_certificate.signature.some_ecdsa).size());
EXPECT_EQ(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256, get_type(signed_certificate.signature));
// Check signer_info and subject_info
EXPECT_EQ(2, signed_certificate.version());
EXPECT_EQ(SignerInfoType::Certificate_Digest_With_SHA256, get_type(signed_certificate.signer_info));
EXPECT_EQ(SubjectType::Authorization_Ticket, signed_certificate.subject_info.subject_type);
EXPECT_TRUE(signed_certificate.subject_info.subject_name.empty());
// Check subject attributes
int verification_key_counter = 0;
SubjectAssurance test_assurance_level;
int assurance_level_counter = 0;
using subject_type_int = std::underlying_type<SubjectAttributeType>::type;
subject_type_int last_subject_type = 0;
for (auto& subject_attribute : signed_certificate.subject_attributes) {
// Verify that fields are in ascending order
subject_type_int subject_type = static_cast<subject_type_int>(get_type(subject_attribute));
EXPECT_LE(last_subject_type, subject_type);
last_subject_type = subject_type;
if (SubjectAttributeType::Verification_Key == get_type(subject_attribute)) {
verification_key_counter++;
} else if (SubjectAttributeType::Assurance_Level == get_type(subject_attribute)) {
test_assurance_level = get<SubjectAssurance>(subject_attribute);
assurance_level_counter++;
} else if (SubjectAttributeType::ITS_AID_SSP_List == get_type(subject_attribute)) {
// TODO: check aid permissions
}
}
EXPECT_EQ(1, verification_key_counter);
ASSERT_EQ(1, assurance_level_counter);
EXPECT_EQ(0, test_assurance_level.raw);
// Check validity restrictions
Time32 start_time;
Time32 end_time;
using restriction_type_int = std::underlying_type<ValidityRestrictionType>::type;
restriction_type_int last_restriction_type = 0;
for (ValidityRestriction restriction : signed_certificate.validity_restriction) {
// Verify that fields are in ascending order
restriction_type_int restriction_type = static_cast<restriction_type_int>(get_type(restriction));
EXPECT_LE(last_restriction_type, restriction_type);
last_restriction_type = restriction_type;
if (ValidityRestrictionType::Time_Start_And_End == get_type(restriction)) {
StartAndEndValidity& time_validation = get<StartAndEndValidity>(restriction);
start_time = time_validation.start_validity;
end_time = time_validation.end_validity;
} else if (ValidityRestrictionType::Region == get_type(restriction)) {
// TODO: Region not specified yet
}
}
EXPECT_LT(start_time, end_time);
}
@@ -0,0 +1,38 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/payload.hpp>
#include <vanetza/security/v2/tests/check_payload.hpp>
#include <vanetza/security/tests/serialization.hpp>
using namespace vanetza;
using namespace vanetza::security::v2;
TEST(Payload, serialize_cohesive)
{
Payload p;
p.type = PayloadType::Unsecured;
p.data = CohesivePacket({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, OsiLayer::Application);
check(p, serialize_roundtrip(p));
}
TEST(Payload, serialize_chunk)
{
Payload p;
p.type = PayloadType::Encrypted;
ChunkPacket packet;
packet[OsiLayer::Network] = ByteBuffer { 1, 2, 3, 4 };
packet[OsiLayer::Transport] = ByteBuffer { 5, 6, 7, 8 };
packet[OsiLayer::Application] = ByteBuffer { 9, 10, 11, 12 };
p.data = packet;
check(p, serialize_roundtrip(p));
}
TEST(Payload, size)
{
Payload p;
p.type = PayloadType::Signed;
p.data = CohesivePacket({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, OsiLayer::Session);
EXPECT_EQ(1 /* type */ + 1 /* length coding */ + 10 /* bytes */, get_size(p));
}
@@ -0,0 +1,90 @@
#include <vanetza/security/v2/persistence.hpp>
#include <gtest/gtest.h>
#include <array>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <iterator>
#include <sstream>
using namespace vanetza::security;
#define WRITEABLE(path) WORK_DIR "/" path
#define ASSET(path) ASSET_DIR "/" path
namespace
{
const std::array<uint8_t, 32> expected_private_key = {
0x53, 0xb7, 0x7e, 0xb8, 0x48, 0x2e, 0x3c, 0x1a,
0xd3, 0x36, 0x70, 0xc0, 0xc6, 0xc4, 0x6b, 0xc0,
0x36, 0x90, 0xd4, 0x00, 0x59, 0xd3, 0xcb, 0xb5,
0x81, 0xb3, 0x36, 0xaf, 0x8a, 0x98, 0x93, 0xf4
};
const std::array<uint8_t, 32> expected_public_x = {
0x1c, 0x85, 0x0d, 0xc7, 0x45, 0x63, 0x29, 0x3c,
0xb0, 0xf3, 0xe5, 0x5e, 0xda, 0x7b, 0x10, 0xec,
0xb4, 0xe9, 0x74, 0x6f, 0x83, 0x6f, 0x84, 0x76,
0x96, 0xc3, 0x1e, 0xe8, 0x68, 0x4e, 0x37, 0x76
};
const std::array<uint8_t, 32> expected_public_y = {
0x28, 0xf1, 0x67, 0xfb, 0x64, 0xce, 0x7b, 0x79,
0xa6, 0x02, 0x06, 0x2a, 0xac, 0x11, 0x7f, 0x59,
0x6b, 0xac, 0x77, 0xc7, 0x1c, 0xd6, 0xf4, 0xca,
0xa7, 0x08, 0x7a, 0xcc, 0xcd, 0xab, 0x91, 0xab
};
void check_key_pair(const ecdsa256::KeyPair& kp)
{
EXPECT_EQ(kp.private_key.key, expected_private_key);
EXPECT_EQ(kp.public_key.x, expected_public_x);
EXPECT_EQ(kp.public_key.y, expected_public_y);
}
ecdsa256::KeyPair make_test_key_pair()
{
ecdsa256::KeyPair kp;
kp.private_key.key = expected_private_key;
kp.public_key.x = expected_public_x;
kp.public_key.y = expected_public_y;
return kp;
}
std::string read_file_bytes(const std::string& path)
{
std::ifstream file(path, std::ios::binary);
return {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
}
} // namespace
#if defined VANETZA_WITH_OPENSSL || defined VANETZA_WITH_CRYPTOPP
TEST(Persistence, load_private_key_from_file)
{
check_key_pair(v2::load_private_key_from_file(ASSET("test_key.der")));
}
TEST(Persistence, save_and_load_pkcs8_der)
{
auto kp = make_test_key_pair();
const std::string path = WRITEABLE("test_save.der");
std::ofstream ofs(path, std::ios::binary);
EXPECT_TRUE(v2::save_private_key_pkcs8_der(ofs, kp));
ofs.flush();
check_key_pair(v2::load_private_key_from_file(path));
std::remove(path.c_str());
}
TEST(Persistence, save_der_matches_reference_file)
{
auto kp = make_test_key_pair();
std::ostringstream oss(std::ios::binary);
EXPECT_TRUE(v2::save_private_key_pkcs8_der(oss, kp));
auto reference = read_file_bytes(ASSET("test_key.der"));
EXPECT_EQ(oss.str(), reference);
}
#endif /* VANETZA_WITH_OPENSSL || VANETZA_WITH_CRYPTOPP */
@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/public_key.hpp>
#include <vanetza/security/v2/public_key.hpp>
#include <vanetza/security/v2/tests/check_public_key.hpp>
using namespace vanetza;
using namespace vanetza::security;
using namespace std;
v2::PublicKey serialize(v2::PublicKey key)
{
std::stringstream stream;
OutputArchive oa(stream);
serialize(oa, key);
v2::PublicKey deKey;
InputArchive ia(stream);
deserialize(ia, deKey);
return deKey;
}
TEST(PublicKey, Field_Size)
{
EXPECT_EQ(32, field_size(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256));
EXPECT_EQ(32, field_size(v2::PublicKeyAlgorithm::ECIES_NISTP256));
}
TEST(PublicKey, ECIES_NISTP256)
{
v2::ecies_nistp256 ecies;
ecies.public_key = Uncompressed { random_byte_sequence(32, 1), random_byte_sequence(32, 2) };
ecies.supported_symm_alg = v2::SymmetricAlgorithm::AES128_CCM;
v2::PublicKey key = ecies;
v2::PublicKey deKey = serialize(key);
check(key, deKey);
EXPECT_EQ(v2::PublicKeyAlgorithm::ECIES_NISTP256, get_type(deKey));
EXPECT_EQ(67, get_size(deKey));
}
TEST(PublicKey, ECDSA_NISTP256_With_SHA256)
{
v2::ecdsa_nistp256_with_sha256 ecdsa;
ecdsa.public_key = X_Coordinate_Only { random_byte_sequence(32, 1) };
v2::PublicKey key = ecdsa;
v2::PublicKey deKey = serialize(key);
check(key, deKey);
EXPECT_EQ(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256, get_type(deKey));
EXPECT_EQ(34, get_size(deKey));
}
@@ -0,0 +1,25 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/v2/recipient_info.hpp>
#include <vanetza/security/v2/tests/check_recipient_info.hpp>
#include <vanetza/security/tests/serialization.hpp>
using namespace vanetza;
using namespace vanetza::security;
using namespace vanetza::security::v2;
TEST(RecipientInfo, Serialize)
{
EciesEncryptedKey ecies;
ecies.v = Compressed_Lsb_Y_0 { random_byte_sequence(field_size(PublicKeyAlgorithm::ECIES_NISTP256), 1337) };
auto cbuf = random_byte_sequence(field_size(SymmetricAlgorithm::AES128_CCM), 7331);
ecies.c = { cbuf.begin(), cbuf.end() };
auto tbuf = random_byte_sequence(ecies.t.size(), 1234);
std::copy_n(tbuf.begin(), ecies.t.size(), ecies.t.data());
RecipientInfo info;
info.enc_key = ecies;
auto certbuf = random_byte_sequence(info.cert_id.size(), 4321);
std::copy_n(certbuf.begin(), info.cert_id.size(), info.cert_id.data());
check(info, serialize_roundtrip(info, SymmetricAlgorithm::AES128_CCM));
}
@@ -0,0 +1,261 @@
#include <gtest/gtest.h>
#include <vanetza/security/v2/region.hpp>
#include <vanetza/security/v2/tests/check_region.hpp>
#include <vanetza/security/tests/serialization.hpp>
#include <vanetza/units/angle.hpp>
#include <vanetza/units/length.hpp>
#include <limits>
using namespace vanetza::security::v2;
using vanetza::geonet::distance_u16t;
using vanetza::geonet::geo_angle_i32t;
using vanetza::units::degrees;
using vanetza::units::si::meter;
TEST(Region, Serialize_CircularRegion)
{
CircularRegion reg;
reg.center.latitude = static_cast<geo_angle_i32t>(12564 * degrees);
reg.center.longitude = static_cast<geo_angle_i32t>(654321 * degrees);
reg.radius = static_cast<distance_u16t>(1337 * meter);
check(reg, serialize_roundtrip(reg));
}
TEST(Region, Serialize_IdentifiedRegion)
{
IdentifiedRegion reg;
reg.region_dictionary = RegionDictionary::ISO_3166_1;
reg.region_identifier = 12345;
reg.local_region.set(546);
check(reg, serialize_roundtrip(reg));
}
TEST(Region, Serialize_PolygonalRegion)
{
PolygonalRegion reg;
for (std::size_t i = 0; i < 3; ++i) {
reg.push_back(TwoDLocation {
geo_angle_i32t::from_value(25 + i),
geo_angle_i32t::from_value(26 + i)
});
}
check(reg, serialize_roundtrip(reg));
}
TEST(Region, Serialize_RectangularRegion_list)
{
std::list<RectangularRegion> reg;
for (std::size_t i = 0; i < 5; ++i) {
reg.push_back(RectangularRegion {
TwoDLocation {
geo_angle_i32t::from_value(1000000 + i),
geo_angle_i32t::from_value(1010000 + i)
},
TwoDLocation {
geo_angle_i32t::from_value(1020000 + i),
geo_angle_i32t::from_value(1030000 + i)
}
});
}
check(reg, serialize_roundtrip(reg));
}
TEST(Region, TwoDLocation_Within_Circle)
{
CircularRegion region;
region.radius = static_cast<distance_u16t>(400 * meter);
region.center = TwoDLocation {
geo_angle_i32t::from_value(490139190),
geo_angle_i32t::from_value(84044460)
};
EXPECT_TRUE(is_within(region.center, region));
EXPECT_TRUE(is_within(TwoDLocation {
geo_angle_i32t::from_value(490143170),
geo_angle_i32t::from_value(83995470)
}, region));
EXPECT_FALSE(is_within(TwoDLocation {
geo_angle_i32t::from_value(490145910),
geo_angle_i32t::from_value(83984740)
}, region));
EXPECT_FALSE(is_within(TwoDLocation {
geo_angle_i32t::from_value(490137060),
geo_angle_i32t::from_value(84120020)
}, region));
}
TEST(Region, Circle_Within_Circle)
{
CircularRegion outer;
outer.radius = static_cast<distance_u16t>(400 * meter);
outer.center = TwoDLocation {
geo_angle_i32t::from_value(490139190),
geo_angle_i32t::from_value(84044460)
};
CircularRegion inner;
inner.center = outer.center;
for (int i = 0; i <= 400; i += 10) {
inner.radius = static_cast<distance_u16t>(i * meter);
EXPECT_TRUE(is_within(inner, outer));
}
inner.radius = static_cast<distance_u16t>(401 * meter);
EXPECT_FALSE(is_within(inner, outer));
inner.center = TwoDLocation {
geo_angle_i32t::from_value(490143170),
geo_angle_i32t::from_value(83995470)
};
inner.radius = static_cast<distance_u16t>(38 * meter);
EXPECT_TRUE(is_within(inner, outer));
inner.radius = static_cast<distance_u16t>(40 * meter);
EXPECT_FALSE(is_within(inner, outer));
}
TEST(Region, TwoDLocation_Within_None)
{
NoneRegion region;
EXPECT_TRUE(is_within(TwoDLocation {
geo_angle_i32t::from_value(490143170),
geo_angle_i32t::from_value(83995470)
}, region));
}
TEST(Region, Circle_Within_None)
{
NoneRegion outer;
CircularRegion inner;
inner.radius = static_cast<distance_u16t>(400 * meter);
inner.center = TwoDLocation {
geo_angle_i32t::from_value(490139190),
geo_angle_i32t::from_value(84044460)
};
EXPECT_TRUE(is_within(inner, outer));
EXPECT_FALSE(is_within(outer, inner));
}
TEST(Region, TwoDLocation_Within_Rectangles)
{
TwoDLocation northwest {
static_cast<geo_angle_i32t>(20 * degrees),
static_cast<geo_angle_i32t>(10 * degrees)
};
TwoDLocation southeast {
static_cast<geo_angle_i32t>(10 * degrees),
static_cast<geo_angle_i32t>(20 * degrees)
};
RectangularRegion region { northwest, southeast };
std::list<RectangularRegion> regions({ region });
// inside
EXPECT_TRUE(is_within(TwoDLocation {
static_cast<geo_angle_i32t>(15 * degrees),
static_cast<geo_angle_i32t>(15 * degrees)
}, regions));
// outside - left
EXPECT_FALSE(is_within(TwoDLocation {
static_cast<geo_angle_i32t>(15 * degrees),
static_cast<geo_angle_i32t>(9 * degrees)
}, regions));
// outside - right
EXPECT_FALSE(is_within(TwoDLocation {
static_cast<geo_angle_i32t>(15 * degrees),
static_cast<geo_angle_i32t>(21 * degrees)
}, regions));
// outside - top
EXPECT_FALSE(is_within(TwoDLocation {
static_cast<geo_angle_i32t>(21 * degrees),
static_cast<geo_angle_i32t>(15 * degrees)
}, regions));
// outside - down
EXPECT_FALSE(is_within(TwoDLocation {
static_cast<geo_angle_i32t>(9 * degrees),
static_cast<geo_angle_i32t>(15 * degrees)
}, regions));
}
TEST(Region, Rectangles_Within_None)
{
TwoDLocation northwest {
static_cast<geo_angle_i32t>(20 * degrees),
static_cast<geo_angle_i32t>(10 * degrees)
};
TwoDLocation southeast {
static_cast<geo_angle_i32t>(10 * degrees),
static_cast<geo_angle_i32t>(20 * degrees)
};
RectangularRegion region { northwest, southeast };
std::list<RectangularRegion> regions({ region });
EXPECT_TRUE(is_within(regions, NoneRegion()));
EXPECT_FALSE(is_within(NoneRegion(), regions));
}
TEST(Region, Rectangle_Within_Rectangle_Exact)
{
TwoDLocation northwest_a {
static_cast<geo_angle_i32t>(10 * degrees),
static_cast<geo_angle_i32t>(10 * degrees)
};
TwoDLocation southeast_a {
static_cast<geo_angle_i32t>(20 * degrees),
static_cast<geo_angle_i32t>(20 * degrees)
};
TwoDLocation northwest_b {
static_cast<geo_angle_i32t>(10 * degrees),
static_cast<geo_angle_i32t>(10 * degrees)
};
TwoDLocation southeast_b {
static_cast<geo_angle_i32t>(20 * degrees),
static_cast<geo_angle_i32t>(20 * degrees)
};
RectangularRegion a { northwest_a, southeast_a };
RectangularRegion b { northwest_b, southeast_b };
std::list<RectangularRegion> region_a({ a });
std::list<RectangularRegion> region_b({ b });
EXPECT_TRUE(is_within(region_a, region_b));
EXPECT_TRUE(is_within(region_b, region_a));
}
TEST(Region, Altitude_To_Elevation)
{
using Elevation = ThreeDLocation::Elevation;
auto altitude_empty = std::numeric_limits<double>::quiet_NaN() * meter;
EXPECT_EQ(to_elevation(altitude_empty), ThreeDLocation::unknown_elevation);
auto altitude_positive = 2843.6 * meter;
EXPECT_EQ(to_elevation(altitude_positive), (Elevation { 0x6F, 0x14 }));
auto altitude_negative = -170.2 * meter;
EXPECT_EQ(to_elevation(altitude_negative), (Elevation { 0xF9, 0x5A }));
auto altitude_below_min = -420.0 * meter;
EXPECT_EQ(to_elevation(altitude_below_min), ThreeDLocation::min_elevation);
auto altitude_above_max = 6150.0 * meter;
EXPECT_EQ(to_elevation(altitude_above_max), ThreeDLocation::max_elevation);
// examples given in TS 103 097 V1.2.1 (section 4.2.19)
EXPECT_EQ(to_elevation(0.0 * meter), (Elevation { 0x00, 0x00 }));
EXPECT_EQ(to_elevation(100.0 * meter), (Elevation { 0x03, 0xe8 }));
EXPECT_EQ(to_elevation(-209.5 * meter), (Elevation { 0xf7, 0xd1 }));
}

Some files were not shown because too many files have changed in this diff Show More