Keep vanetza-idf in obu-firmware, so a plain clone builds the firmware
obu-firmware builds against the vanetza-idf C-ITS library, which until now came from the colleague's microbu-esp32c5 tree beside the repository and was not tracked here, so a clone of this repository could not build the firmware it ships. The library alone is now part of obu-firmware, as obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from there by default; -DVANETZA_IDF_DIR still points the build elsewhere. The rest of the colleague's tree (their own VAM firmware, PKI tooling, station-link Python tools, the V2X2MAP bridge) stays out of this repository and gitignored; nothing is pushed to their repository. NOTES.md, docs/06, TODO.md and the pcap verifier's usage line point at the new location.
This commit is contained in:
@@ -0,0 +1,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 */
|
||||
+43
@@ -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 */
|
||||
+33
@@ -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
|
||||
+250
@@ -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
|
||||
+47
@@ -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 */
|
||||
+91
@@ -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
|
||||
+58
@@ -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 */
|
||||
|
||||
+238
@@ -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
|
||||
+110
@@ -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 */
|
||||
+71
@@ -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
|
||||
+33
@@ -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 */
|
||||
+26
@@ -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
|
||||
+34
@@ -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 */
|
||||
+39
@@ -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
|
||||
+65
@@ -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));
|
||||
}
|
||||
|
||||
+135
@@ -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());
|
||||
}
|
||||
+23
@@ -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
|
||||
+46
@@ -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 */
|
||||
+29
@@ -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
|
||||
+20
@@ -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 */
|
||||
|
||||
+69
@@ -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 */
|
||||
+21
@@ -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
|
||||
+25
@@ -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 */
|
||||
+21
@@ -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
|
||||
+26
@@ -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 */
|
||||
+34
@@ -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 */
|
||||
+45
@@ -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
|
||||
+24
@@ -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 */
|
||||
|
||||
+39
@@ -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
|
||||
+22
@@ -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 */
|
||||
+25
@@ -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
|
||||
+19
@@ -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 */
|
||||
+45
@@ -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
|
||||
+30
@@ -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 */
|
||||
+38
@@ -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
|
||||
+22
@@ -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 */
|
||||
+76
@@ -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
|
||||
+32
@@ -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 */
|
||||
|
||||
+25
@@ -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 */
|
||||
|
||||
+24
@@ -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
|
||||
+19
@@ -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 */
|
||||
+42
@@ -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
|
||||
+23
@@ -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 */
|
||||
+33
@@ -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 */
|
||||
Vendored
+244
@@ -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));
|
||||
}
|
||||
|
||||
+17
@@ -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));
|
||||
}
|
||||
+128
@@ -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);
|
||||
}
|
||||
+91
@@ -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));
|
||||
}
|
||||
+25
@@ -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
Reference in New Issue
Block a user