Keep the colleague's microbu-esp32c5 tree in this repository

obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but
that tree was gitignored, so a clone of this repository could not build the
firmware it ships. It is now committed here as ordinary files in its own
folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP
bridge's signature verification (--trust) used on the bench. Nothing is
fetched from or pushed to the colleague's repository; this repository and
its remotes carry everything. The folder's own .gitignore keeps build output,
downloaded components and private key material out, as it did there; the
committed file set is identical to that repository's tracked files.

The ESP32-C5 is still flashed from obu-firmware/, which only takes
vanetza-idf from microbu-esp32c5/, so the two stay separate folders.
FLASHING.md says how to take a newer version of the colleague's tree (copy
it over the folder, rebuild, test, commit).
This commit is contained in:
Ashin Walpola
2026-09-23 17:46:40 +02:00
parent 2f60623e18
commit 0e9525162d
9881 changed files with 1582523 additions and 17 deletions
@@ -0,0 +1,21 @@
include(UseGTest)
add_library(security_test STATIC
serialization.cpp
)
target_include_directories(security_test PUBLIC $<TARGET_PROPERTY:security,INTERFACE_INCLUDE_DIRECTORIES>)
target_link_libraries(security_test PUBLIC ${GTest_LIBRARY})
configure_gtest_directory(LINK_LIBRARIES Boost::boost security security_test
COMPILE_DEFINITIONS ASSET_DIR="${SECURITY_TEST_ASSET_DIR}")
add_gtest(Backend backend.cpp)
add_gtest(CamServiceSpecificPermissions cam_ssp.cpp)
add_gtest(CertificateV3 certificate_v3.cpp)
add_gtest(DecapService decap_service.cpp)
add_gtest(DummyVerifyService dummy_verify_service.cpp)
add_gtest(EccPointDecompression ecc_point_decompression.cpp)
add_gtest(Geometry geometry.cpp)
add_gtest(Hmac hmac.cpp)
add_gtest(PeerRequestTracker peer_request_tracker.cpp)
add_gtest(Persistence persistence.cpp)
add_gtest(PublicKey public_key.cpp)
add_gtest(SecuredMessageV3 secured_message_v3.cpp)
@@ -0,0 +1,394 @@
# FOKUS WebValidator for TS 103 097
Vanetza unit tests for SecuredMessage serialization use some third-party test data available online: [FOKUS WebValidator](https://werkzeug.dcaiti.tu-berlin.de/etsi/ts103097/)
Unfortunately, the provided SecuredMessages are for protocol version 1 whereas Vanetza currently follows version 2. The original messages from FOKUS WebValidator are pasted into this document for reference because changes were necessary to adapt these for the recent protocol version.
## SecuredMessage/v1 (1)
``
010181038002010901A8ED6DF65B0E6D6A0100809400000418929DB6A9E452223062C52028E956BF9874E0A40D21D5F9F56564F39C5DD187C922F2E5F0630373879A43393373B9F6205BF01FBD9C1F113165C291C376F535010004EABA91A915D81807E910FD292D99DF8B401EED88CF7F031412D5ED9905F9996469798C412FC8F7237A3AB3469795E2DEF5E1B783EA4F6B6A2359D21772B2EA9D0200210AC040800101C0408101010F01099EB20109B1270003040100960000004B2E6D0D0EE9BC4AD9CD087B601E9AF06031995443D652763455FBB794B33982889260740EF64CFA8C6808A58F98E06CE42A1E9C22A0785D7242647F7895ABFC0000009373931CD7580500021E011C983E690E5F6D755BD4871578A9427E7BC383903DC7DA3B560384013643010000FE8566BEA87B39E6411F80226E792D6E01E77B598F2BB1FCE7F2DD441185C07CEF0573FBFB9876B99FE811486F6F5D499E6114FC0724A67F8D71D2A897A7EB34
``
struct SecuredMessage {
uint8 protocol_version: 1
uint8 security_profile: 1
HeaderField<259> header_fields {
struct HeaderField {
HeaderFieldType type: signer_info (128)
struct SignerInfo signer {
SignerInfoType type: certificate (2)
struct Certificate certificate {
uint8 version: 1
SignerInfo<9> signer_info_v1 {
struct SignerInfo {
SignerInfoType type: certificate_digest_with_sha256 (1)
HashedId8 digest: A8ED6DF65B0E6D6A
}
}
struct SubjectInfo subject_info {
SubjectType subject_type: authorization_ticket (1)
opaque<0> subject_name:
}
SubjectAttribute<148> subject_attributes {
struct SubjectAttribute {
SubjectAttributeType type: verification_key (0)
struct PublicKey key {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EccPoint public_key {
EccPointType type: uncompressed (4)
opaque[32] x: 18929DB6A9E452223062C52028E956BF9874E0A40D21D5F9F56564F39C5DD187
opaque[32] y: C922F2E5F0630373879A43393373B9F6205BF01FBD9C1F113165C291C376F535
}
}
}
struct SubjectAttribute {
SubjectAttributeType type: encryption_key (1)
struct PublicKey key {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EccPoint public_key {
EccPointType type: uncompressed (4)
opaque[32] x: EABA91A915D81807E910FD292D99DF8B401EED88CF7F031412D5ED9905F99964
opaque[32] y: 69798C412FC8F7237A3AB3469795E2DEF5E1B783EA4F6B6A2359D21772B2EA9D
}
}
}
struct SubjectAttribute {
SubjectAttributeType type: assurance_level (2)
SubjectAssurance assurance_level: assurance level = 0, confidence = 0 (bitmask = 0)
}
struct SubjectAttribute {
SubjectAttributeType type: its_aid_ssp_list (33)
ItsAidSsp<10> its_aid_ssp_list {
struct ItsAidSsp {
IntX its_aid: 16512
opaque<1> service_specific_permissions: 01
}
struct ItsAidSsp {
IntX its_aid: 16513
opaque<1> service_specific_permissions: 01
}
}
}
}
ValidityRestriction<15> validity_restrictions {
struct ValidityRestriction {
ValidityRestrictionType type: time_start_and_end (1)
Time32 start_validity: 2015-02-12 00:00:00 UTC
Time32 end_validity: 2015-02-25 23:59:59 UTC
}
struct ValidityRestriction {
ValidityRestrictionType type: region (3)
struct GeographicRegion region {
RegionType region_type: id (4)
struct IdentifiedRegion id_region {
RegionDictionary region_dictionary: un_stats (1)
uint16 region_identifier: 150
IntX local_region: 0
}
}
}
}
struct Signature {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EcdsaSignature ecdsa_signature {
struct EccPoint R {
EccPointType type: x_coordinate_only (0)
opaque[32] x: 4B2E6D0D0EE9BC4AD9CD087B601E9AF06031995443D652763455FBB794B33982
}
opaque[32] s: 889260740EF64CFA8C6808A58F98E06CE42A1E9C22A0785D7242647F7895ABFC
}
}
}
}
}
struct HeaderField {
HeaderFieldType type: generation_time (0)
Time64 generation_time: 2015-02-20 10:36:37.663 UTC
}
struct HeaderField {
HeaderFieldType type: message_type (5)
uint16 message_type: 2
}
}
Payload<30> payload_fields {
struct Payload {
PayloadType type: signed (1)
opaque<28> data: 983E690E5F6D755BD4871578A9427E7BC383903DC7DA3B5603840136
}
}
TrailerField<67> trailer_fields {
struct TrailerField {
TrailerFieldType type: signature (1)
struct Signature signature {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EcdsaSignature ecdsa_signature {
struct EccPoint R {
EccPointType type: x_coordinate_only (0)
opaque[32] x: FE8566BEA87B39E6411F80226E792D6E01E77B598F2BB1FCE7F2DD441185C07C
}
opaque[32] s: EF0573FBFB9876B99FE811486F6F5D499E6114FC0724A67F8D71D2A897A7EB34
}
}
}
}
}
## SecuredMessage/v1 (2)
``
010181038002010901A8ED6DF65B0E6D6A01008094000004C4EC137145DD4F450145DE530CCA36E73AB3D87FC8275847CDAD8248C1CD20879BD6A8CB54EA9E05D3B41376CE2F24789AEF82836CA818D568ADF4A140E96E48010004D6C268EE68B5B8B387B2312B7E1D21CE0C366D251A32431508B96EB6A3479CCF96A8738F30ED451F00DA8DDE84367C7EB16727D14FF14F5DD8F9791FE0A12A640200210AC040800101C0408101010F01099EB20109B1270003040100960000001EB035FE8E51DCDD8558DE0BE9B87895B36B420583A5C6B2B8B2EAB7F3D3C99163638FA025A0033D4BD80BBA02B8E3DE1B55766459D494677AF24917E51B80AC0000009373CC5F22C8050002220120F29384759027349075829034707ABABABABABAABAB98437985739845783974954301000081E7CDB6D2C741C1700822305C39E8E809622AF9FCA1C0786F762D08E80580C42F1FCC1D5499577210834C390BB4613E102DECB14F575A2820743DC9A66BBD7A
``
struct SecuredMessage {
uint8 protocol_version: 1
uint8 security_profile: 1
HeaderField<259> header_fields {
struct HeaderField {
HeaderFieldType type: signer_info (128)
struct SignerInfo signer {
SignerInfoType type: certificate (2)
struct Certificate certificate {
uint8 version: 1
SignerInfo<9> signer_info_v1 {
struct SignerInfo {
SignerInfoType type: certificate_digest_with_sha256 (1)
HashedId8 digest: A8ED6DF65B0E6D6A
}
}
struct SubjectInfo subject_info {
SubjectType subject_type: authorization_ticket (1)
opaque<0> subject_name:
}
SubjectAttribute<148> subject_attributes {
struct SubjectAttribute {
SubjectAttributeType type: verification_key (0)
struct PublicKey key {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EccPoint public_key {
EccPointType type: uncompressed (4)
opaque[32] x: C4EC137145DD4F450145DE530CCA36E73AB3D87FC8275847CDAD8248C1CD2087
opaque[32] y: 9BD6A8CB54EA9E05D3B41376CE2F24789AEF82836CA818D568ADF4A140E96E48
}
}
}
struct SubjectAttribute {
SubjectAttributeType type: encryption_key (1)
struct PublicKey key {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EccPoint public_key {
EccPointType type: uncompressed (4)
opaque[32] x: D6C268EE68B5B8B387B2312B7E1D21CE0C366D251A32431508B96EB6A3479CCF
opaque[32] y: 96A8738F30ED451F00DA8DDE84367C7EB16727D14FF14F5DD8F9791FE0A12A64
}
}
}
struct SubjectAttribute {
SubjectAttributeType type: assurance_level (2)
SubjectAssurance assurance_level: assurance level = 0, confidence = 0 (bitmask = 0)
}
struct SubjectAttribute {
SubjectAttributeType type: its_aid_ssp_list (33)
ItsAidSsp<10> its_aid_ssp_list {
struct ItsAidSsp {
IntX its_aid: 16512
opaque<1> service_specific_permissions: 01
}
struct ItsAidSsp {
IntX its_aid: 16513
opaque<1> service_specific_permissions: 01
}
}
}
}
ValidityRestriction<15> validity_restrictions {
struct ValidityRestriction {
ValidityRestrictionType type: time_start_and_end (1)
Time32 start_validity: 2015-02-12 00:00:00 UTC
Time32 end_validity: 2015-02-25 23:59:59 UTC
}
struct ValidityRestriction {
ValidityRestrictionType type: region (3)
struct GeographicRegion region {
RegionType region_type: id (4)
struct IdentifiedRegion id_region {
RegionDictionary region_dictionary: un_stats (1)
uint16 region_identifier: 150
IntX local_region: 0
}
}
}
}
struct Signature {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EcdsaSignature ecdsa_signature {
struct EccPoint R {
EccPointType type: x_coordinate_only (0)
opaque[32] x: 1EB035FE8E51DCDD8558DE0BE9B87895B36B420583A5C6B2B8B2EAB7F3D3C991
}
opaque[32] s: 63638FA025A0033D4BD80BBA02B8E3DE1B55766459D494677AF24917E51B80AC
}
}
}
}
}
struct HeaderField {
HeaderFieldType type: generation_time (0)
Time64 generation_time: 2015-02-20 10:52:38.309 UTC
}
struct HeaderField {
HeaderFieldType type: message_type (5)
uint16 message_type: 2
}
}
Payload<34> payload_fields {
struct Payload {
PayloadType type: signed (1)
opaque<32> data: F29384759027349075829034707ABABABABABAABAB9843798573984578397495
}
}
TrailerField<67> trailer_fields {
struct TrailerField {
TrailerFieldType type: signature (1)
struct Signature signature {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EcdsaSignature ecdsa_signature {
struct EccPoint R {
EccPointType type: x_coordinate_only (0)
opaque[32] x: 81E7CDB6D2C741C1700822305C39E8E809622AF9FCA1C0786F762D08E80580C4
}
opaque[32] s: 2F1FCC1D5499577210834C390BB4613E102DECB14F575A2820743DC9A66BBD7A
}
}
}
}
}
## SecuredMessage/v1 (3)
``
010181038002010901A8ED6DF65B0E6D6A010080940000040209B0434163CCBAFDD34A45333E418FB96C05BBE0E7E1D755D40D0B4BBE8DA508EC2F2723B7ADF0F27C39F3AECFF0783C196F9961F8821E6294375D9294CD6A01000452113CE698DB081491675DF8FFE81C23EA5D0071B2D2BF0E0DA4ADA0CDA58259CA5D999200B6565E194EDAB8BD3DCA863F2DDF39C13E7A0375ECE2566C5EB8C60200210AC040800101C0408101010F01099EB20109B1270003040100960000008DA1F3F9F35E04C3DE77D7438988A8D57EBE44DAA021A4269E297C177C9CFE458E128EC290785D6631961625020943B6D87DAA54919A98F7865709929A7C6E480000009373CF482D400500020A01080123456789ABCDEF43010000371423BBA0902D8AF2FB2226D73A7781D4D6B6772650A8BEE5A1AF198CEDABA2C9BF57540C629E6A1E629B8812AEBDDDBCAF472F6586F16C14B3DEFBE9B6ADB2
``
struct SecuredMessage {
uint8 protocol_version: 1
uint8 security_profile: 1
HeaderField<259> header_fields {
struct HeaderField {
HeaderFieldType type: signer_info (128)
struct SignerInfo signer {
SignerInfoType type: certificate (2)
struct Certificate certificate {
uint8 version: 1
SignerInfo<9> signer_info_v1 {
struct SignerInfo {
SignerInfoType type: certificate_digest_with_sha256 (1)
HashedId8 digest: A8ED6DF65B0E6D6A
}
}
struct SubjectInfo subject_info {
SubjectType subject_type: authorization_ticket (1)
opaque<0> subject_name:
}
SubjectAttribute<148> subject_attributes {
struct SubjectAttribute {
SubjectAttributeType type: verification_key (0)
struct PublicKey key {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EccPoint public_key {
EccPointType type: uncompressed (4)
opaque[32] x: 0209B0434163CCBAFDD34A45333E418FB96C05BBE0E7E1D755D40D0B4BBE8DA5
opaque[32] y: 08EC2F2723B7ADF0F27C39F3AECFF0783C196F9961F8821E6294375D9294CD6A
}
}
}
struct SubjectAttribute {
SubjectAttributeType type: encryption_key (1)
struct PublicKey key {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EccPoint public_key {
EccPointType type: uncompressed (4)
opaque[32] x: 52113CE698DB081491675DF8FFE81C23EA5D0071B2D2BF0E0DA4ADA0CDA58259
opaque[32] y: CA5D999200B6565E194EDAB8BD3DCA863F2DDF39C13E7A0375ECE2566C5EB8C6
}
}
}
struct SubjectAttribute {
SubjectAttributeType type: assurance_level (2)
SubjectAssurance assurance_level: assurance level = 0, confidence = 0 (bitmask = 0)
}
struct SubjectAttribute {
SubjectAttributeType type: its_aid_ssp_list (33)
ItsAidSsp<10> its_aid_ssp_list {
struct ItsAidSsp {
IntX its_aid: 16512
opaque<1> service_specific_permissions: 01
}
struct ItsAidSsp {
IntX its_aid: 16513
opaque<1> service_specific_permissions: 01
}
}
}
}
ValidityRestriction<15> validity_restrictions {
struct ValidityRestriction {
ValidityRestrictionType type: time_start_and_end (1)
Time32 start_validity: 2015-02-12 00:00:00 UTC
Time32 end_validity: 2015-02-25 23:59:59 UTC
}
struct ValidityRestriction {
ValidityRestrictionType type: region (3)
struct GeographicRegion region {
RegionType region_type: id (4)
struct IdentifiedRegion id_region {
RegionDictionary region_dictionary: un_stats (1)
uint16 region_identifier: 150
IntX local_region: 0
}
}
}
}
struct Signature {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EcdsaSignature ecdsa_signature {
struct EccPoint R {
EccPointType type: x_coordinate_only (0)
opaque[32] x: 8DA1F3F9F35E04C3DE77D7438988A8D57EBE44DAA021A4269E297C177C9CFE45
}
opaque[32] s: 8E128EC290785D6631961625020943B6D87DAA54919A98F7865709929A7C6E48
}
}
}
}
}
struct HeaderField {
HeaderFieldType type: generation_time (0)
Time64 generation_time: 2015-02-20 10:53:27.136 UTC
}
struct HeaderField {
HeaderFieldType type: message_type (5)
uint16 message_type: 2
}
}
Payload<10> payload_fields {
struct Payload {
PayloadType type: signed (1)
opaque<8> data: 0123456789ABCDEF
}
}
TrailerField<67> trailer_fields {
struct TrailerField {
TrailerFieldType type: signature (1)
struct Signature signature {
PublicKeyAlgorithm algorithm: ecdsa_nistp256_with_sha256 (0)
struct EcdsaSignature ecdsa_signature {
struct EccPoint R {
EccPointType type: x_coordinate_only (0)
opaque[32] x: 371423BBA0902D8AF2FB2226D73A7781D4D6B6772650A8BEE5A1AF198CEDABA2
}
opaque[32] s: C9BF57540C629E6A1E629B8812AEBDDDBCAF472F6586F16C14B3DEFBE9B6ADB2
}
}
}
}
}
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgU7d+uEguPBrTNnDA
xsRrwDaQ1ABZ08u1gbM2r4qYk/ShRANCAAQchQ3HRWMpPLDz5V7aexDstOl0b4Nv
hHaWwx7oaE43dijxZ/tkznt5pgIGKqwRf1lrrHfHHNb0yqcIeszNq5Gr
-----END PRIVATE KEY-----
@@ -0,0 +1,212 @@
#include <vanetza/security/backend.hpp>
#ifdef VANETZA_WITH_OPENSSL
#include <vanetza/security/backend_openssl.hpp>
#endif
#ifdef VANETZA_WITH_CRYPTOPP
#include <vanetza/security/backend_cryptopp.hpp>
#endif
#include <gtest/gtest.h>
#include <algorithm>
#include <list>
#include "serialization.hpp"
using namespace vanetza;
using namespace vanetza::security;
class BackendTest : public ::testing::TestWithParam<std::string>
{
public:
void SetUp() override
{
backend = create_backend(GetParam());
ASSERT_NE(backend.get(), nullptr);
}
ByteBuffer buffer_from_string(const std::string& s)
{
ByteBuffer b;
std::copy(s.begin(), s.end(), std::back_inserter(b));
return b;
}
std::unique_ptr<Backend> backend;
};
TEST_P(BackendTest, sha256sum)
{
const ByteBuffer input = buffer_from_string("All your ITS stations are belong to us");
const ByteBuffer expected = buffer_from_hexstring("dcb61edffc5f536aeb80c7e61fc943239a28b16edad52f4a0bc6e83f2df0fdc8");
EXPECT_EQ(backend->calculate_hash(HashAlgorithm::SHA256, input), expected);
}
TEST_P(BackendTest, sha384sum)
{
const ByteBuffer input = buffer_from_string("All your ITS stations are belong to us");
const ByteBuffer expected = buffer_from_hexstring("4dfdccefa8612f3285aa4e909c644eb841e0347132465a733cbc99b46437d9ea0886c96a25fd9d51389585431b069651");
EXPECT_EQ(backend->calculate_hash(HashAlgorithm::SHA384, input), expected);
}
TEST_P(BackendTest, sign_and_verify_nistp256)
{
const ByteBuffer digest = buffer_from_hexstring("33e57499804cebc409407d6bf4ade70b49b58d0d4bae9ca57d507b260a676685");
PrivateKey private_key;
private_key.type = KeyType::NistP256;
private_key.key = buffer_from_hexstring("f4ce0e4b48829aae85abd2124a2574dba44388eea94ebd373f9203ad39719a30");
PublicKey public_key;
public_key.type = KeyType::NistP256;
public_key.compression = KeyCompression::NoCompression;
public_key.x = buffer_from_hexstring("8e65e0ab7d4cd66860be693e29bf747fe796ebfe3942416b1f9c7ecf7fdb5797");
public_key.y = buffer_from_hexstring("49ce27c4acfc53c34867420a35b999e7deb3aeabec388f0d08d7fe0edf54ba62");
Signature sig = backend->sign_digest(private_key, digest);
EXPECT_TRUE(backend->verify_digest(public_key, digest, sig));
}
TEST_P(BackendTest, sign_and_verify_brainpoolp256r1)
{
const ByteBuffer digest = buffer_from_hexstring("33e57499804cebc409407d6bf4ade70b49b58d0d4bae9ca57d507b260a676685");
PrivateKey private_key;
private_key.type = KeyType::BrainpoolP256r1;
private_key.key = buffer_from_hexstring("38f72493269d99c77b6e6488de05aea60bc707a35b464b0286665463ecc2883d");
PublicKey public_key;
public_key.type = KeyType::BrainpoolP256r1;
public_key.compression = KeyCompression::NoCompression;
public_key.x = buffer_from_hexstring("2cdb98f1053bb69fb4879101946d0a49aba965c8c4ffad5ef64356b0cff0bcf8");
public_key.y = buffer_from_hexstring("17828cc0e33f68cbf9cb1d5419597464aef62efea8503676403177a9074cf86e");
Signature sig = backend->sign_digest(private_key, digest);
EXPECT_TRUE(backend->verify_digest(public_key, digest, sig));
}
TEST_P(BackendTest, sign_and_verify_brainpoolp384r1)
{
const ByteBuffer digest = buffer_from_hexstring("33e57499804cebc409407d6bf4ade70b49b58d0d4bae9ca57d507b260a676685");
PrivateKey private_key;
private_key.type = KeyType::BrainpoolP384r1;
private_key.key = buffer_from_hexstring("29267526c4511103f094f4d9ef1e8a57e2e6429642188939b756fe6738db49744363ea4081601e5acebe091d258bcedd");
PublicKey public_key;
public_key.type = KeyType::BrainpoolP384r1;
public_key.compression = KeyCompression::NoCompression;
public_key.x = buffer_from_hexstring("271dd92e47814e1f6b39c29488a80a720ae18153597380f41b2079cca4d92373b058850af280e920b10993bf925bdc4a");
public_key.y = buffer_from_hexstring("388b971cb0ad878842de6825e5f1a6e359c1c4cddd65593781af6179fa743c21a056577de9cca2631e107edc28dc2ef7");
Signature sig = backend->sign_digest(private_key, digest);
EXPECT_TRUE(backend->verify_digest(public_key, digest, sig));
}
TEST_P(BackendTest, verify_nistp256)
{
Signature sig;
sig.type = KeyType::NistP256;
sig.r = buffer_from_hexstring("de99edf601b3682d90e6458ab0e5588fcdef54d679852e38fc85e7e16ad02074");
sig.s = buffer_from_hexstring("552962f3572898a05dd99b179124d6e1d1a672a3f407083b59a1fe2dc62e8161");
PublicKey public_key;
public_key.type = KeyType::NistP256;
public_key.compression = KeyCompression::NoCompression;
public_key.x = buffer_from_hexstring("8e65e0ab7d4cd66860be693e29bf747fe796ebfe3942416b1f9c7ecf7fdb5797");
public_key.y = buffer_from_hexstring("49ce27c4acfc53c34867420a35b999e7deb3aeabec388f0d08d7fe0edf54ba62");
ByteBuffer digest = buffer_from_hexstring("33e57499804cebc409407d6bf4ade70b49b58d0d4bae9ca57d507b260a676685");
EXPECT_TRUE(backend->verify_digest(public_key, digest, sig));
ByteBuffer false_digest = digest;
false_digest[0] ^= 0x01;
EXPECT_FALSE(backend->verify_digest(public_key, false_digest, sig));
}
TEST_P(BackendTest, verify_brainpoolp256r1)
{
Signature sig;
sig.type = KeyType::BrainpoolP256r1;
sig.r = buffer_from_hexstring("28c9b30e37b05388c2f04be921dbc79480d972f1c782ff8c5ade76c40e136d5a");
sig.s = buffer_from_hexstring("77cf1e91d0204d2a58847b543df055605ff968cd6120c4ac9c751be08d782518");
PublicKey public_key;
public_key.type = KeyType::BrainpoolP256r1;
public_key.compression = KeyCompression::NoCompression;
public_key.x = buffer_from_hexstring("2cdb98f1053bb69fb4879101946d0a49aba965c8c4ffad5ef64356b0cff0bcf8");
public_key.y = buffer_from_hexstring("17828cc0e33f68cbf9cb1d5419597464aef62efea8503676403177a9074cf86e");
ByteBuffer digest = buffer_from_hexstring("33e57499804cebc409407d6bf4ade70b49b58d0d4bae9ca57d507b260a676685");
EXPECT_TRUE(backend->verify_digest(public_key, digest, sig));
ByteBuffer false_digest = digest;
false_digest[0] ^= 0x01;
EXPECT_FALSE(backend->verify_digest(public_key, false_digest, sig));
}
TEST_P(BackendTest, verify_brainpoolp384r1)
{
Signature sig;
sig.type = KeyType::BrainpoolP384r1;
sig.r = buffer_from_hexstring("2b51c205ac9598ff245cbed8df5c8f3e1d9d3c7d6901a08d45e790784457900a84c903167f5d6b124ee193881ff93950");
sig.s = buffer_from_hexstring("81c06d7f5fdf4e825b3cd8545956e6b1b587be4dcc66ce3007ca8b6966f90bc94efc8d88c70b5bd3bd44739ec34da54f");
PublicKey public_key;
public_key.type = KeyType::BrainpoolP384r1;
public_key.compression = KeyCompression::NoCompression;
public_key.x = buffer_from_hexstring("271dd92e47814e1f6b39c29488a80a720ae18153597380f41b2079cca4d92373b058850af280e920b10993bf925bdc4a");
public_key.y = buffer_from_hexstring("388b971cb0ad878842de6825e5f1a6e359c1c4cddd65593781af6179fa743c21a056577de9cca2631e107edc28dc2ef7");
ByteBuffer digest = buffer_from_hexstring("33e57499804cebc409407d6bf4ade70b49b58d0d4bae9ca57d507b260a676685");
EXPECT_TRUE(backend->verify_digest(public_key, digest, sig));
ByteBuffer false_digest = digest;
false_digest[0] ^= 0x01;
EXPECT_FALSE(backend->verify_digest(public_key, false_digest, sig));
}
#if defined VANETZA_WITH_OPENSSL || defined VANETZA_WITH_CRYPTOPP
namespace
{
// Check that every built backend derives the known public key from the private key.
void check_derive_public_key(KeyType type, const char* priv_hex, const char* x_hex, const char* y_hex)
{
PrivateKey priv;
priv.type = type;
priv.key = buffer_from_hexstring(priv_hex);
const ByteBuffer x = buffer_from_hexstring(x_hex);
const ByteBuffer y = buffer_from_hexstring(y_hex);
#ifdef VANETZA_WITH_OPENSSL
EXPECT_EQ(openssl::derive_public_key(priv).x, x);
EXPECT_EQ(openssl::derive_public_key(priv).y, y);
#endif
#ifdef VANETZA_WITH_CRYPTOPP
EXPECT_EQ(cryptopp::derive_public_key(priv).x, x);
EXPECT_EQ(cryptopp::derive_public_key(priv).y, y);
#endif
}
} // namespace
TEST(Backend, derive_public_key_nistp256)
{
check_derive_public_key(KeyType::NistP256,
"f4ce0e4b48829aae85abd2124a2574dba44388eea94ebd373f9203ad39719a30",
"8e65e0ab7d4cd66860be693e29bf747fe796ebfe3942416b1f9c7ecf7fdb5797",
"49ce27c4acfc53c34867420a35b999e7deb3aeabec388f0d08d7fe0edf54ba62");
}
TEST(Backend, derive_public_key_brainpoolp384r1)
{
check_derive_public_key(KeyType::BrainpoolP384r1,
"29267526c4511103f094f4d9ef1e8a57e2e6429642188939b756fe6738db49744363ea4081601e5acebe091d258bcedd",
"271dd92e47814e1f6b39c29488a80a720ae18153597380f41b2079cca4d92373b058850af280e920b10993bf925bdc4a",
"388b971cb0ad878842de6825e5f1a6e359c1c4cddd65593781af6179fa743c21a056577de9cca2631e107edc28dc2ef7");
}
#endif /* VANETZA_WITH_OPENSSL || VANETZA_WITH_CRYPTOPP */
std::list<std::string> available_backends()
{
std::list<std::string> backends;
#ifdef VANETZA_WITH_OPENSSL
backends.emplace_back("OpenSSL");
#endif
#ifdef VANETZA_WITH_CRYPTOPP
backends.emplace_back("CryptoPP");
#endif
return backends;
}
INSTANTIATE_TEST_SUITE_P(BackendImplementations, BackendTest, ::testing::ValuesIn(available_backends()));
@@ -0,0 +1,133 @@
#include <vanetza/security/cam_ssp.hpp>
#include <gtest/gtest.h>
#include <algorithm>
#include <set>
using vanetza::ByteBuffer;
using namespace vanetza::security;
static const std::set<CamPermission> all {{
CamPermission::CEN_DSRC_Tolling_Zone,
CamPermission::Public_Transport,
CamPermission::Special_Transport,
CamPermission::Dangerous_Goods,
CamPermission::Roadwork,
CamPermission::Rescue,
CamPermission::Emergency,
CamPermission::Safety_Car,
CamPermission::Closed_Lanes,
CamPermission::Request_For_Right_Of_Way,
CamPermission::Request_For_Free_Crossing_At_Traffic_Light,
CamPermission::No_Passing,
CamPermission::No_Passing_For_Trucks,
CamPermission::Speed_Limit,
}};
TEST(CamSsp, empty)
{
CamPermissions empty;
EXPECT_TRUE(std::none_of(all.begin(), all.end(), [empty](CamPermission cp) { return empty.has(cp); }));
EXPECT_TRUE(empty.none());
}
TEST(CamSsp, single)
{
CamPermissions single(CamPermission::Safety_Car);
EXPECT_FALSE(single.none());
EXPECT_TRUE(single.has(CamPermission::Safety_Car));
std::set<CamPermission> rest = all;
rest.erase(CamPermission::Safety_Car);
EXPECT_TRUE(std::none_of(rest.begin(), rest.end(), [single](CamPermission cp) { return single.has(cp); }));
}
TEST(CamSsp, multiple)
{
CamPermissions multiple({CamPermission::Roadwork, CamPermission::Public_Transport, CamPermission::Speed_Limit});
EXPECT_TRUE(multiple.has(CamPermission::Roadwork));
EXPECT_TRUE(multiple.has(CamPermission::Public_Transport));
EXPECT_TRUE(multiple.has(CamPermission::Speed_Limit));
EXPECT_TRUE(multiple.has({CamPermission::Roadwork, CamPermission::Public_Transport, CamPermission::Speed_Limit}));
}
TEST(CamSsp, manipulation)
{
CamPermissions ssp({CamPermission::No_Passing, CamPermission::Rescue});
EXPECT_TRUE(ssp.has({CamPermission::Rescue, CamPermission::No_Passing}));
ssp.remove(CamPermission::Rescue);
EXPECT_FALSE(ssp.has({CamPermission::Rescue, CamPermission::No_Passing}));
EXPECT_TRUE(ssp.has(CamPermission::No_Passing));
ssp.remove(CamPermission::Speed_Limit);
EXPECT_FALSE(ssp.has(CamPermission::Speed_Limit));
EXPECT_TRUE(ssp.has(CamPermission::No_Passing));
ssp.add(CamPermission::Closed_Lanes);
EXPECT_TRUE(ssp.has({CamPermission::Closed_Lanes, CamPermission::No_Passing}));
ssp.remove(CamPermission::No_Passing).remove(CamPermission::Closed_Lanes);
EXPECT_TRUE(ssp.none());
}
TEST(CamSsp, serialization)
{
CamPermissions ssp;
const auto empty_ssp_buffer = ssp.encode();
EXPECT_EQ(ByteBuffer({0x01, 0x00, 0x00}), empty_ssp_buffer);
ssp.add(CamPermission::CEN_DSRC_Tolling_Zone);
const auto dsrc_ssp_buffer = ssp.encode();
EXPECT_EQ(ByteBuffer({0x01, 0x80, 0x00}), dsrc_ssp_buffer);
ssp.add(CamPermission::Speed_Limit);
const auto extremes_ssp_buffer = ssp.encode();
EXPECT_EQ(ByteBuffer({0x01, 0x80, 0x04}), extremes_ssp_buffer);
CamPermissions decoded = CamPermissions::decode(extremes_ssp_buffer);
EXPECT_EQ(extremes_ssp_buffer, decoded.encode());
EXPECT_TRUE(decoded.has({CamPermission::Speed_Limit, CamPermission::CEN_DSRC_Tolling_Zone}));
decoded.remove(CamPermission::Speed_Limit).remove(CamPermission::CEN_DSRC_Tolling_Zone);
EXPECT_TRUE(decoded.none());
CamPermissions decoded_empty = CamPermissions::decode(ByteBuffer {});
EXPECT_TRUE(decoded_empty.none());
CamPermissions decoded_testing = CamPermissions::decode(ByteBuffer {0x00});
EXPECT_TRUE(decoded_testing.none());
CamPermissions decoded_version = CamPermissions::decode(ByteBuffer {0xff, 0x80, 0x30});
EXPECT_TRUE(decoded_version.none());
CamPermissions decoded_short = CamPermissions::decode(ByteBuffer {0x01, 0x40});
EXPECT_TRUE(decoded_short.none());
CamPermissions decoded_long = CamPermissions::decode(ByteBuffer {0x01, 0x80, 0x04, 0x00});
EXPECT_TRUE(decoded_long.none());
CamPermissions decoded_reserved = CamPermissions::decode(ByteBuffer {0x01, 0x02, 0x07});
EXPECT_FALSE(decoded_reserved.none());
EXPECT_TRUE(decoded_reserved.has(CamPermission::Speed_Limit));
decoded_reserved.remove(CamPermission::Speed_Limit).remove(CamPermission::Emergency);
EXPECT_TRUE(std::none_of(all.begin(), all.end(),
[decoded_reserved](CamPermission cp) { return decoded_reserved.has(cp); }));
EXPECT_FALSE(decoded_reserved.none());
}
TEST(CamSsp, permissions)
{
CamPermissions ssp { CamPermission::No_Passing, CamPermission::Speed_Limit };
std::set<CamPermission> expected { CamPermission::No_Passing, CamPermission::Speed_Limit };
EXPECT_EQ(expected, ssp.permissions());
// works also for reserved bits
const CamPermission reserved = static_cast<CamPermission>(0x0100);
ssp.add(reserved);
expected.insert(reserved);
ASSERT_EQ(3, expected.size());
EXPECT_EQ(expected, ssp.permissions());
}
TEST(CamSsp, stringify)
{
EXPECT_EQ("Safety Car", stringify(CamPermission::Safety_Car));
EXPECT_EQ("Reserved (0x0200)", stringify(static_cast<CamPermission>(0x0200)));
}
@@ -0,0 +1,58 @@
#include <gtest/gtest.h>
#include <vanetza/security/v3/certificate.hpp>
#include <vanetza/security/v3/certificate_cache.hpp>
#include <vanetza/security/sha.hpp>
using namespace vanetza;
using namespace vanetza::security;
// implicit certificate (IEEE 1609.2 clause 6.4.5) with a dummy reconstruction value
inline v3::Certificate fake_implicit_certificate()
{
v3::Certificate cert;
cert->version = 3;
cert->type = Vanetza_Security_CertificateType_implicit;
cert->issuer.present = Vanetza_Security_IssuerIdentifier_PR_sha256AndDigest;
std::array<char, 8> issuer_digest = { 1, 2, 3, 4, 5, 6, 7, 8 };
OCTET_STRING_fromBuf(&cert->issuer.choice.sha256AndDigest, issuer_digest.data(), issuer_digest.size());
cert->toBeSigned.id.present = Vanetza_Security_CertificateId_PR_none;
std::array<char, 3> craca_id = { 0, 0, 0 };
OCTET_STRING_fromBuf(&cert->toBeSigned.cracaId, craca_id.data(), craca_id.size());
cert->toBeSigned.crlSeries = 0;
cert->toBeSigned.validityPeriod.start = 0;
cert->toBeSigned.validityPeriod.duration.present = Vanetza_Security_Duration_PR_minutes;
cert->toBeSigned.validityPeriod.duration.choice.minutes = 10080;
cert->toBeSigned.verifyKeyIndicator.present = Vanetza_Security_VerificationKeyIndicator_PR_reconstructionValue;
cert->toBeSigned.verifyKeyIndicator.choice.reconstructionValue.present = Vanetza_Security_EccP256CurvePoint_PR_compressed_y_0;
std::array<char, 32> dummy_point {};
OCTET_STRING_fromBuf(
&cert->toBeSigned.verifyKeyIndicator.choice.reconstructionValue.choice.compressed_y_0,
dummy_point.data(), dummy_point.size()
);
cert.add_app_permission(aid::CA, ByteBuffer({ 1, 0, 0 }));
return cert;
}
TEST(CertificateV3, cache)
{
v3::CertificateCache cache;
cache.store(v3::fake_certificate());
}
TEST(CertificateV3, implicit_certificate_digest)
{
v3::Certificate cert = fake_implicit_certificate();
ASSERT_TRUE(v3::is_canonical(*cert));
// curve of a reconstructed key is unknown without the issuer certificate
EXPECT_EQ(KeyType::Unspecified, cert.get_verification_key_type());
// ECQV public key reconstruction is not supported
EXPECT_FALSE(v3::get_public_key(*cert));
// IEEE 1609.2 clause 5.3.2: SHA-256 over the canonical encoding
const ByteBuffer encoded = cert.encode();
const HashedId8 expected = create_hashed_id8(calculate_sha256_digest(encoded.data(), encoded.size()));
auto digest = cert.calculate_digest();
ASSERT_TRUE(digest);
EXPECT_EQ(expected, *digest);
}
@@ -0,0 +1,33 @@
#include <gtest/gtest.h>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/security/decap_service.hpp>
#include <vanetza/security/v3/secured_message.hpp>
#include "printer.hpp"
using namespace vanetza;
using namespace vanetza::security;
TEST(DecapService, confirm_from_verify_confirm)
{
v3::SecuredMessage v3_msg = v3::SecuredMessage::with_signed_data();
v3_msg.set_payload(ByteBuffer { 0xca, 0xfe });
SecuredMessage msg { std::move(v3_msg) };
const HashedId8 signer { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
VerifyConfirm verify;
verify.report = VerificationReport::Success;
verify.its_aid = aid::CA;
verify.permissions = ByteBuffer { 0x01, 0x02 };
verify.certificate_validity = CertificateValidity::valid();
verify.certificate_id = signer;
DecapConfirm decap = DecapConfirm::from(std::move(verify), SecuredMessageView { msg });
EXPECT_EQ(VerificationReport::Success, decap.report);
EXPECT_EQ(aid::CA, decap.its_aid);
EXPECT_EQ(ByteBuffer({ 0x01, 0x02 }), decap.permissions);
EXPECT_TRUE(decap.certificate_validity);
ASSERT_TRUE(decap.certificate_id);
EXPECT_EQ(signer, *decap.certificate_id);
EXPECT_EQ(2, boost::apply_visitor([](const auto& packet) { return packet.size(); }, decap.plaintext_payload));
}
@@ -0,0 +1,153 @@
#include <gtest/gtest.h>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/security/verify_service.hpp>
#include <vanetza/security/v3/certificate.hpp>
#include <vanetza/security/v3/certificate_cache.hpp>
#include <vanetza/security/v3/secured_message.hpp>
#include <memory>
using namespace vanetza;
using namespace vanetza::security;
TEST(DummyVerifyServiceTest, lookup)
{
std::unique_ptr<VerifyService> dummy { new DummyVerifyService {
VerificationReport::Invalid_Timestamp, CertificateValidity::valid() }};
SecuredMessage message;
VerifyRequest req(SecuredMessageView { message });
auto confirm = dummy->verify(std::move(req));
EXPECT_EQ(VerificationReport::Invalid_Timestamp, confirm.report);
EXPECT_TRUE(confirm.certificate_validity);
EXPECT_EQ(0, confirm.its_aid);
EXPECT_EQ(vanetza::ByteBuffer({}), confirm.permissions);
EXPECT_FALSE(confirm.certificate_id);
}
TEST(DummyVerifyServiceTest, signer_with_full_certificate)
{
DummyVerifyService dummy { VerificationReport::Success, CertificateValidity::valid() };
v3::Certificate cert = v3::fake_certificate();
auto digest = cert.calculate_digest();
ASSERT_TRUE(digest);
v3::SecuredMessage msg = v3::SecuredMessage::with_signed_data();
msg.set_its_aid(aid::CA);
msg.set_signer_identifier(cert);
auto confirm = dummy.verify(VerifyRequest { SecuredMessageView { std::move(msg) } });
EXPECT_EQ(aid::CA, confirm.its_aid);
ASSERT_TRUE(confirm.certificate_id);
EXPECT_EQ(*digest, *confirm.certificate_id);
EXPECT_EQ(ByteBuffer({ 1, 0, 0 }), confirm.permissions);
}
TEST(DummyVerifyServiceTest, signer_with_digest_no_cache)
{
DummyVerifyService dummy { VerificationReport::Success, CertificateValidity::valid() };
HashedId8 digest = {{ 1, 2, 3, 4, 5, 6, 7, 8 }};
v3::SecuredMessage msg = v3::SecuredMessage::with_signed_data();
msg.set_its_aid(aid::CA);
msg.set_signer_identifier(digest);
auto confirm = dummy.verify(VerifyRequest { SecuredMessageView { std::move(msg) } });
ASSERT_TRUE(confirm.certificate_id);
EXPECT_EQ(digest, *confirm.certificate_id);
EXPECT_TRUE(confirm.permissions.empty());
}
TEST(DummyVerifyServiceTest, signer_with_digest_cache_hit)
{
DummyVerifyService dummy { VerificationReport::Success, CertificateValidity::valid() };
v3::Certificate cert = v3::fake_certificate();
auto digest = cert.calculate_digest();
ASSERT_TRUE(digest);
v3::CertificateCache cache;
cache.store(std::move(cert));
dummy.use_certificate_cache(&cache);
v3::SecuredMessage msg = v3::SecuredMessage::with_signed_data();
msg.set_its_aid(aid::CA);
msg.set_signer_identifier(*digest);
auto confirm = dummy.verify(VerifyRequest { SecuredMessageView { std::move(msg) } });
ASSERT_TRUE(confirm.certificate_id);
EXPECT_EQ(*digest, *confirm.certificate_id);
EXPECT_EQ(ByteBuffer({ 1, 0, 0 }), confirm.permissions);
}
TEST(DummyVerifyServiceTest, signer_with_digest_cache_miss)
{
DummyVerifyService dummy { VerificationReport::Success, CertificateValidity::valid() };
v3::Certificate cert = v3::fake_certificate();
auto digest = cert.calculate_digest();
ASSERT_TRUE(digest);
v3::CertificateCache cache;
// certificate is not added
dummy.use_certificate_cache(&cache);
v3::SecuredMessage msg = v3::SecuredMessage::with_signed_data();
msg.set_its_aid(aid::CA);
msg.set_signer_identifier(*digest);
auto confirm = dummy.verify(VerifyRequest { SecuredMessageView { std::move(msg) } });
ASSERT_TRUE(confirm.certificate_id);
EXPECT_EQ(*digest, *confirm.certificate_id);
EXPECT_TRUE(confirm.permissions.empty());
}
TEST(DummyVerifyServiceTest, full_certificate_populates_cache)
{
DummyVerifyService dummy { VerificationReport::Success, CertificateValidity::valid() };
v3::CertificateCache cache;
dummy.use_certificate_cache(&cache);
v3::Certificate cert = v3::fake_certificate();
auto digest = cert.calculate_digest();
ASSERT_TRUE(digest);
v3::SecuredMessage msg = v3::SecuredMessage::with_signed_data();
msg.set_its_aid(aid::CA);
msg.set_signer_identifier(cert);
EXPECT_EQ(nullptr, cache.lookup(*digest));
auto confirm = dummy.verify(VerifyRequest { SecuredMessageView { std::move(msg) } });
EXPECT_EQ(ByteBuffer({ 1, 0, 0 }), confirm.permissions);
EXPECT_EQ(1u, cache.size());
EXPECT_NE(nullptr, cache.lookup(*digest));
}
TEST(DummyVerifyServiceTest, permissions_empty_for_unrelated_aid)
{
DummyVerifyService dummy { VerificationReport::Success, CertificateValidity::valid() };
v3::Certificate cert = v3::fake_certificate();
auto digest = cert.calculate_digest();
ASSERT_TRUE(digest);
v3::SecuredMessage msg = v3::SecuredMessage::with_signed_data();
msg.set_its_aid(aid::DEN);
msg.set_signer_identifier(cert);
auto confirm = dummy.verify(VerifyRequest { SecuredMessageView { std::move(msg) } });
EXPECT_EQ(aid::DEN, confirm.its_aid);
ASSERT_TRUE(confirm.certificate_id);
EXPECT_EQ(*digest, *confirm.certificate_id);
EXPECT_TRUE(confirm.permissions.empty());
}
@@ -0,0 +1,70 @@
#include <gtest/gtest.h>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/ecc_point.hpp>
#include <memory>
using namespace vanetza::security;
using namespace vanetza;
class EccPointDecompressionTest: public ::testing::TestWithParam<const char*>
{
protected:
std::unique_ptr<Backend> backend;
void SetUp() override
{
backend = create_backend(GetParam());
ASSERT_TRUE(backend);
}
};
TEST_P(EccPointDecompressionTest, LSB_Y_0)
{
const ByteBuffer x = {
0x21, 0x31, 0xB5, 0x19, 0x4C, 0xE6, 0xEE, 0x9F,
0x47, 0xB6, 0xB4, 0x5F, 0xF8, 0x46, 0xC1, 0x79,
0x65, 0x1A, 0x4A, 0x15, 0x63, 0x5A, 0x09, 0x87,
0xCC, 0xC3, 0x8F, 0x3E, 0x34, 0x4D, 0xCD, 0x77 };
const ByteBuffer y_expected = {
0xD0, 0x12, 0x39, 0xBF, 0x92, 0x7F, 0x16, 0xA3,
0xAD, 0xDB, 0x58, 0x2F, 0x94, 0x9B, 0xA7, 0x25,
0x8F, 0xAC, 0x53, 0xFE, 0xD2, 0xE7, 0x26, 0xDB,
0x9C, 0x62, 0xC3, 0x2C, 0xAC, 0x8D, 0xBA, 0x5A };
const EccPoint point = Compressed_Lsb_Y_0 { x };
auto decompressed = backend->decompress_point(point);
ASSERT_TRUE(decompressed);
EXPECT_EQ(x, decompressed->x);
EXPECT_EQ(y_expected, decompressed->y);
}
TEST_P(EccPointDecompressionTest, LSB_Y_1)
{
const ByteBuffer x = {
0x7F, 0x92, 0xF5, 0xBA, 0x25, 0xE0, 0x2C, 0x7F,
0x7C, 0xF9, 0x82, 0x2D, 0x74, 0x6F, 0x28, 0x05,
0x65, 0xD6, 0xA1, 0x4A, 0x64, 0x38, 0x40, 0x61,
0x2E, 0x08, 0x63, 0x4D, 0x6E, 0x85, 0x7F, 0x13 };
const ByteBuffer y_expected = {
0x32, 0xD9, 0x85, 0xB6, 0x1D, 0x0E, 0x70, 0x51,
0x51, 0x03, 0x88, 0xF9, 0x08, 0x2E, 0x24, 0x05,
0x0B, 0x1A, 0xFE, 0xB6, 0x56, 0x49, 0x2D, 0x17,
0x29, 0xB2, 0x8F, 0x9B, 0x58, 0xCE, 0xCB, 0xAF };
const EccPoint point = Compressed_Lsb_Y_1 { x };
auto decompressed = backend->decompress_point(point);
ASSERT_TRUE(decompressed);
EXPECT_EQ(x, decompressed->x);
EXPECT_EQ(y_expected, decompressed->y);
}
#if defined VANETZA_WITH_OPENSSL && defined VANETZA_WITH_CRYPTOPP
static auto backends = ::testing::Values("OpenSSL", "CryptoPP");
#elif defined VANETZA_WITH_OPENSSL
static auto backends = ::testing::Values("OpenSSL");
#elif defined VANETZA_WITH_CRYPTOPP
static auto backends = ::testing::Values("CryptoPP");
#endif
INSTANTIATE_TEST_SUITE_P(EccPointDecompression, EccPointDecompressionTest, backends);
@@ -0,0 +1,104 @@
#include <gtest/gtest.h>
#include <vanetza/asn1/security_profile.hpp>
#include VANETZA_ASN1_SECURITY_HEADER(CircularRegion.h)
#include VANETZA_ASN1_SECURITY_HEADER(PolygonalRegion.h)
#include VANETZA_ASN1_SECURITY_HEADER(RectangularRegion.h)
#include <vanetza/common/position_fix.hpp>
#include <vanetza/security/v3/geometry.hpp>
#include <vanetza/units/angle.hpp>
#include <array>
using namespace vanetza::security::v3;
namespace units = vanetza::units;
TEST(Geometry, location_is_valid)
{
asn1::TwoDLocation location;
location.latitude = Vanetza_Security_NinetyDegreeInt_unknown;
location.longitude = Vanetza_Security_OneEightyDegreeInt_unknown;
EXPECT_FALSE(is_valid(location));
location.latitude = 0;
EXPECT_FALSE(is_valid(location));
location.longitude = 0;
EXPECT_TRUE(is_valid(location));
}
TEST(Geometry, is_inside_circular)
{
vanetza::PositionFix fix;
fix.latitude = 48.0 * units::degree;
fix.longitude = 11.0 * units::degree;
asn1::CircularRegion region;
region.center.latitude = 480010000; /*< 1 degree equals 111km */
region.center.longitude = 110000000;
region.radius = 100; /*< 100 is not enough */
EXPECT_FALSE(is_inside(fix, region));
region.radius = 120; /*< 120 exceeds 111m */
EXPECT_TRUE(is_inside(fix, region));
}
TEST(Geometry, is_inside_rectangular)
{
asn1::RectangularRegion region;
region.northWest.latitude = 485000000;
region.northWest.longitude = 110000000;
region.southEast.latitude = 480000000;
region.southEast.longitude = 115000000;
auto build_posfix = [](double lat, double lon) {
vanetza::PositionFix fix;
fix.latitude = lat * units::degree;
fix.longitude = lon * units::degree;
return fix;
};
EXPECT_TRUE(is_inside(build_posfix(48.001, 11.001), region));
EXPECT_FALSE(is_inside(build_posfix(47.999, 11.001), region));
EXPECT_FALSE(is_inside(build_posfix(48.501, 11.001), region));
EXPECT_FALSE(is_inside(build_posfix(48.001, 10.999), region));
EXPECT_FALSE(is_inside(build_posfix(48.001, 11.501), region));
std::swap(region.northWest, region.southEast);
EXPECT_FALSE(is_inside(build_posfix(48.001, 11.001), region));
}
TEST(Geometry, is_inside_polygon)
{
asn1::PolygonalRegion region;
std::array<asn1::TwoDLocation, 4> locations = {{
{ 100000000, 0 },
{ 100000000, 100000000 },
{ 0, 100000000 },
{ 0, 0}
}};
std::array<asn1::TwoDLocation*, 4> location_ptrs = {
&locations[0], &locations[1], &locations[2], &locations[3]
};
region.list.array = location_ptrs.data();
region.list.count = location_ptrs.size();
region.list.size = location_ptrs.size();
asn1::TwoDLocation point_b = { -10000000, -10000000 };
EXPECT_FALSE(is_inside(&point_b, &region));
asn1::TwoDLocation point_a = { 50000000, 50000000 };
EXPECT_TRUE(is_inside(&point_a, &region));
// also reversed order of region points
location_ptrs = { &locations[3], &locations[2], &locations[1], &locations[0] };
EXPECT_TRUE(is_inside(&point_a, &region));
vanetza::PositionFix fix;
fix.latitude = -1 * units::degree;
fix.longitude = -1 * units::degree;
EXPECT_FALSE(is_inside(fix, region));
fix.latitude = 5 * units::degree;
fix.longitude = 5 * units::degree;
EXPECT_TRUE(is_inside(fix, region));
}
@@ -0,0 +1,77 @@
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/security/hmac.hpp>
#include <gtest/gtest.h>
using namespace vanetza::security;
using vanetza::ByteBuffer;
class HmacTest : public ::testing::Test
{
protected:
ByteBuffer data = {0x01, 0x02, 0x03, 0x04, 0x05};
HmacKey key = {{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
}};
};
#if defined VANETZA_WITH_OPENSSL || defined VANETZA_WITH_CRYPTOPP
TEST_F(HmacTest, create_hmac_tag)
{
KeyTag tag = create_hmac_tag(data, key);
KeyTag zero = {};
EXPECT_NE(tag, zero);
}
TEST_F(HmacTest, deterministic)
{
KeyTag tag1 = create_hmac_tag(data, key);
KeyTag tag2 = create_hmac_tag(data, key);
EXPECT_EQ(tag1, tag2);
}
TEST_F(HmacTest, different_data_different_tag)
{
ByteBuffer other_data = {0x05, 0x04, 0x03, 0x02, 0x01};
KeyTag tag1 = create_hmac_tag(data, key);
KeyTag tag2 = create_hmac_tag(other_data, key);
EXPECT_NE(tag1, tag2);
}
TEST_F(HmacTest, different_key_different_tag)
{
HmacKey other_key = {};
KeyTag tag1 = create_hmac_tag(data, key);
KeyTag tag2 = create_hmac_tag(data, other_key);
EXPECT_NE(tag1, tag2);
}
#endif
#if defined VANETZA_WITH_OPENSSL
TEST_F(HmacTest, openssl)
{
KeyTag tag = create_hmac_tag_openssl(data, key);
KeyTag zero = {};
EXPECT_NE(tag, zero);
}
#endif
#if defined VANETZA_WITH_CRYPTOPP
TEST_F(HmacTest, cryptopp)
{
KeyTag tag = create_hmac_tag_cryptopp(data, key);
KeyTag zero = {};
EXPECT_NE(tag, zero);
}
#endif
#if defined VANETZA_WITH_OPENSSL && defined VANETZA_WITH_CRYPTOPP
TEST_F(HmacTest, openssl_and_cryptopp_match)
{
KeyTag tag_openssl = create_hmac_tag_openssl(data, key);
KeyTag tag_cryptopp = create_hmac_tag_cryptopp(data, key);
EXPECT_EQ(tag_openssl, tag_cryptopp);
}
#endif
@@ -0,0 +1,90 @@
#include <gtest/gtest.h>
#include <vanetza/security/peer_request_tracker.hpp>
#include "printer.hpp"
using namespace vanetza::security;
HashedId3 create_id(uint32_t id)
{
HashedId3 hid;
hid[0] = id & 0xFF;
hid[1] = (id >> 8) & 0xFF;
hid[2] = (id >> 16) & 0xFF;
return hid;
}
TEST(PeerRequestTracker, is_pending)
{
PeerRequestTracker tracker;
EXPECT_FALSE(tracker.is_pending(create_id(42)));
tracker.add_request(create_id(42));
EXPECT_TRUE(tracker.is_pending(create_id(42)));
tracker.discard_request(create_id(42));
EXPECT_FALSE(tracker.is_pending(create_id(42)));
}
TEST(PeerRequestTracker, bounded_capacity)
{
PeerRequestTracker tracker(2);
tracker.add_request(create_id(1));
tracker.add_request(create_id(2));
EXPECT_TRUE(tracker.is_pending(create_id(1)));
EXPECT_TRUE(tracker.is_pending(create_id(2)));
tracker.add_request(create_id(3));
EXPECT_TRUE(tracker.is_pending(create_id(3)));
EXPECT_TRUE(tracker.is_pending(create_id(2)));
// dropped oldest pending request
EXPECT_FALSE(tracker.is_pending(create_id(1)));
}
TEST(PeerRequestTracker, keep_order)
{
PeerRequestTracker tracker(4);
tracker.add_request(create_id(1));
tracker.add_request(create_id(2));
tracker.add_request(create_id(3));
tracker.add_request(create_id(4));
tracker.add_request(create_id(3));
tracker.add_request(create_id(2));
std::list<HashedId3> expected = { create_id(1), create_id(2), create_id(3), create_id(4) };
EXPECT_EQ(expected, tracker.all());
// nothing left in tracker
EXPECT_FALSE(tracker.next_one());
}
TEST(PeerRequestTracker, next_one)
{
PeerRequestTracker tracker(3);
tracker.add_request(create_id(0xc0));
tracker.add_request(create_id(0xff));
tracker.add_request(create_id(0xee));
tracker.add_request(create_id(0x42));
EXPECT_EQ(tracker.next_one(), create_id(0xff));
EXPECT_EQ(tracker.next_one(), create_id(0xee));
EXPECT_EQ(tracker.next_one(), create_id(0x42));
EXPECT_FALSE(tracker.next_one());
}
TEST(PeerRequestTracker, next_n)
{
PeerRequestTracker tracker(6);
tracker.add_request(create_id(0x01));
tracker.add_request(create_id(0x02));
tracker.add_request(create_id(0x03));
tracker.add_request(create_id(0x04));
tracker.add_request(create_id(0x05));
std::list<HashedId3> expected = { create_id(0x01), create_id(0x02), create_id(0x03) };
EXPECT_EQ(expected, tracker.next_n(3));
expected = { create_id(0x04), create_id(0x05) };
EXPECT_EQ(expected, tracker.next_n(8));
expected = { };
EXPECT_EQ(expected, tracker.next_n(2));
}
@@ -0,0 +1,84 @@
#include <vanetza/security/persistence.hpp>
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <cstdint>
using namespace vanetza::security;
#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
};
void check_private_key(const PrivateKey& key)
{
EXPECT_EQ(key.type, KeyType::NistP256);
ASSERT_EQ(key.key.size(), expected_private_key.size());
EXPECT_TRUE(std::equal(key.key.begin(), key.key.end(), expected_private_key.begin()));
}
} // namespace
#if defined VANETZA_WITH_OPENSSL || defined VANETZA_WITH_CRYPTOPP
TEST(Persistence, load_pem)
{
check_private_key(load_private_key_from_pem_file(ASSET("test_key.pem")));
}
TEST(Persistence, load_der)
{
check_private_key(load_private_key_from_der_file(ASSET("test_key.der")));
}
TEST(Persistence, pem_and_der_load_same_key)
{
auto pem = load_private_key_from_pem_file(ASSET("test_key.pem"));
auto der = load_private_key_from_der_file(ASSET("test_key.der"));
EXPECT_EQ(pem.type, der.type);
EXPECT_EQ(pem.key, der.key);
}
TEST(Persistence, load_pem_nonexistent_file)
{
EXPECT_THROW(load_private_key_from_pem_file("nonexistent.pem"), std::runtime_error);
}
TEST(Persistence, load_der_nonexistent_file)
{
EXPECT_THROW(load_private_key_from_der_file("nonexistent.der"), std::runtime_error);
}
#endif /* VANETZA_WITH_OPENSSL || VANETZA_WITH_CRYPTOPP */
#ifdef VANETZA_WITH_OPENSSL
TEST(Persistence, openssl_load_pem)
{
check_private_key(load_private_key_from_pem_file_openssl(ASSET("test_key.pem")));
}
TEST(Persistence, openssl_load_der)
{
check_private_key(load_private_key_from_der_file_openssl(ASSET("test_key.der")));
}
#endif /* VANETZA_WITH_OPENSSL */
#ifdef VANETZA_WITH_CRYPTOPP
TEST(Persistence, cryptopp_load_pem)
{
check_private_key(load_private_key_from_pem_file_cryptopp(ASSET("test_key.pem")));
}
TEST(Persistence, cryptopp_load_der)
{
check_private_key(load_private_key_from_der_file_cryptopp(ASSET("test_key.der")));
}
#endif /* VANETZA_WITH_CRYPTOPP */
@@ -0,0 +1,52 @@
#pragma once
#include <boost/optional/optional_io.hpp>
#include <vanetza/security/decap_service.hpp>
#include <vanetza/security/hashed_id.hpp>
#include <ostream>
namespace vanetza
{
namespace security
{
void PrintTo(const DecapReport& report, std::ostream* out)
{
struct Printer : boost::static_visitor<>
{
Printer(std::ostream* out) : out(out) {}
void operator()(const VerificationReport& report) const
{
*out << "DecapReport(VerificationReport(" << static_cast<int>(report) << "))";
}
void operator()(const boost::blank&) const
{
*out << "DecapReport(None)";
}
std::ostream* out;
};
boost::apply_visitor(Printer(out), report);
}
} // namespace security
} // namespace vanetza
namespace std {
/* HashedId3 and HashedId8 are mere type aliases of std::array<> */
std::ostream& operator<<(std::ostream& os, const vanetza::security::HashedId3& id)
{
os << vanetza::security::to_string(id);
return os;
}
std::ostream& operator<<(std::ostream& os, const vanetza::security::HashedId8& id)
{
os << vanetza::security::to_string(id);
return os;
}
} // namespace std
@@ -0,0 +1,80 @@
#include <gtest/gtest.h>
#include <vanetza/security/public_key.hpp>
using namespace vanetza;
using namespace vanetza::security;
using namespace std;
TEST(PublicKey, canonical_hexstring_y0)
{
PublicKey key;
key.type = KeyType::NistP256;
key.compression = KeyCompression::Y0;
key.x = ByteBuffer(32, 0x00);
EXPECT_EQ("02" + std::string(64, '0'), canonical_hexstring(key));
}
TEST(PublicKey, canonical_hexstring_y1)
{
PublicKey key;
key.type = KeyType::BrainpoolP384r1;
key.compression = KeyCompression::Y1;
key.x = ByteBuffer(48, 0x00);
EXPECT_EQ("03" + std::string(96, '0'), canonical_hexstring(key));
}
TEST(PublicKey, canonical_hexstring_encoding)
{
PublicKey key;
key.type = KeyType::NistP256;
key.compression = KeyCompression::Y0;
key.x = {
0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd,
0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
0x10, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba,
0xbe, 0x00, 0xff, 0x11, 0xee, 0x22, 0xdd, 0x33
};
auto expected = "02"
"000123456789ABCD"
"EFFEDCBA98765432"
"10DEADBEEFCAFEBA"
"BE00FF11EE22DD33";
EXPECT_EQ(expected, canonical_hexstring(key));
}
TEST(PublicKey, canonical_hexstring_uncompressed_parity)
{
PublicKey key;
key.type = KeyType::NistP256;
key.compression = KeyCompression::NoCompression;
key.x = ByteBuffer(32, 0xab);
key.y = ByteBuffer(32, 0x04);
EXPECT_EQ("02", canonical_hexstring(key).substr(0, 2));
key.y = ByteBuffer(32, 0x05);
EXPECT_EQ("03", canonical_hexstring(key).substr(0, 2));
}
TEST(PublicKey, canonical_hexstring_rejects_malformed)
{
PublicKey key;
key.compression = KeyCompression::Y0;
key.type = KeyType::Unspecified;
key.x = ByteBuffer(32, 0xab);
EXPECT_TRUE(canonical_hexstring(key).empty());
key.type = KeyType::NistP256;
key.x.clear();
EXPECT_TRUE(canonical_hexstring(key).empty());
key.x = ByteBuffer(31, 0xab);
EXPECT_TRUE(canonical_hexstring(key).empty());
key.x = ByteBuffer(32, 0xab);
key.compression = KeyCompression::NoCompression;
EXPECT_TRUE(canonical_hexstring(key).empty());
key.y = ByteBuffer(31, 0x04);
EXPECT_TRUE(canonical_hexstring(key).empty());
}
@@ -0,0 +1,116 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <vanetza/common/stored_position_provider.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/straight_verify_service.hpp>
#include <vanetza/security/v3/naive_certificate_provider.hpp>
#include <vanetza/security/v3/secured_message.hpp>
#include <vanetza/security/v3/sign_service.hpp>
#include "printer.hpp"
using namespace vanetza;
using namespace vanetza::security;
TEST(SecuredMessageV3, deserialize)
{
const ByteBuffer encoded_secured_msg = {
0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x56, 0x20, 0x50, 0x02, 0x80, 0x00,
0x32, 0x01, 0x00, 0x14, 0x00, 0xd2, 0xfb, 0x6a, 0x0c, 0x62, 0xd2, 0xbf,
0x6c, 0x54, 0x53, 0x1f, 0x44, 0xef, 0xf5, 0x06, 0x66, 0x36, 0x09, 0x84,
0x6a, 0x06, 0xc8, 0x00, 0x00, 0xa0, 0x00, 0x07, 0xd1, 0x00, 0x00, 0x02,
0x02, 0x6a, 0x0c, 0x62, 0xd2, 0x57, 0x41, 0x00, 0x5a, 0x9d, 0x3a, 0xbf,
0x6e, 0x36, 0x01, 0x20, 0x22, 0x34, 0x23, 0x00, 0xfc, 0x35, 0x96, 0xd4,
0x58, 0x69, 0x40, 0xe2, 0x4e, 0x03, 0x02, 0x96, 0x8a, 0x9b, 0x34, 0x3d,
0x82, 0x09, 0x9e, 0x10, 0x41, 0xc0, 0x14, 0xb9, 0x80, 0x40, 0x01, 0x24,
0x00, 0x01, 0xc8, 0x0b, 0xbf, 0x35, 0x4b, 0x4f, 0x80, 0x0b, 0xa2, 0xd2,
0xfb, 0x6a, 0x0c, 0x62, 0xd2, 0x80, 0x82, 0xea, 0x71, 0xef, 0xf9, 0xc6,
0xbb, 0x15, 0x7b, 0x8e, 0x16, 0x66, 0x44, 0x12, 0x0d, 0x7f, 0x98, 0xf2,
0x52, 0x5e, 0x73, 0x8f, 0x6f, 0x41, 0xd7, 0xfd, 0xfa, 0xc7, 0xd0, 0xd8,
0x8a, 0xa3, 0x9b, 0x10, 0x96, 0xfa, 0xb3, 0xfb, 0x2a, 0x0a, 0x92, 0x2a,
0x3b, 0x5f, 0xeb, 0x91, 0xf9, 0xf5, 0x22, 0xd0, 0x06, 0x1f, 0x3b, 0x9c,
0xa7, 0xa4, 0x6f, 0x7c, 0x7b, 0xd3, 0xef, 0x9d, 0x3a, 0x84, 0xbc
};
v3::SecuredMessage msg;
EXPECT_TRUE(msg.decode(encoded_secured_msg));
EXPECT_EQ(3, msg.protocol_version());
}
TEST(SecuredMessageV3, verify)
{
// a CAM signed by a VW Golf 8 including a full signer certificate
const ByteBuffer encoded_secured_msg = {
0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x56, 0x20, 0x50, 0x02, 0x80, 0x00,
0x32, 0x01, 0x00, 0x14, 0x00, 0xfe, 0x38, 0x4c, 0xe0, 0xb8, 0x90, 0xbf,
0x6b, 0x2f, 0x5b, 0x1f, 0x45, 0x28, 0x58, 0x06, 0x64, 0x0a, 0x6d, 0x80,
0xe8, 0x03, 0xbe, 0x00, 0x00, 0xa0, 0x00, 0x07, 0xd1, 0x00, 0x00, 0x02,
0x02, 0x4c, 0xe0, 0xb8, 0x90, 0x30, 0xbc, 0x00, 0x5a, 0x9d, 0x42, 0x2a,
0x0e, 0x35, 0xbb, 0xa0, 0x22, 0x44, 0x23, 0x86, 0xda, 0x35, 0x96, 0xd4,
0x58, 0x3b, 0xe1, 0x20, 0xa1, 0x03, 0x02, 0x96, 0x8a, 0xcf, 0x33, 0xe7,
0x81, 0xff, 0x82, 0x10, 0x3f, 0xe0, 0x14, 0x19, 0x80, 0x40, 0x01, 0x24,
0x00, 0x01, 0xc8, 0x0b, 0xba, 0xb6, 0xc8, 0x15, 0x81, 0x01, 0x01, 0x80,
0x03, 0x00, 0x80, 0x56, 0xdf, 0xd6, 0xd6, 0x27, 0xa3, 0x62, 0xdc, 0x10,
0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xdf, 0xf7, 0xb5, 0x84, 0x00,
0xa8, 0x01, 0x02, 0x80, 0x01, 0x24, 0x81, 0x04, 0x03, 0x01, 0x00, 0x00,
0x80, 0x01, 0x25, 0x81, 0x05, 0x04, 0x01, 0x90, 0x1a, 0x25, 0x80, 0x80,
0x82, 0x04, 0x27, 0xbb, 0x27, 0xc9, 0x98, 0xc1, 0xec, 0xa2, 0xb1, 0x0e,
0x71, 0x07, 0x98, 0x02, 0x44, 0x51, 0x8b, 0x3c, 0x50, 0xa3, 0xa3, 0x27,
0xb5, 0xb1, 0x90, 0xd0, 0x90, 0xf1, 0x45, 0x1f, 0x3d, 0x80, 0x80, 0x83,
0xc2, 0xf3, 0xca, 0xeb, 0xc7, 0xfa, 0x35, 0x94, 0x5c, 0x03, 0x0a, 0x5a,
0xe0, 0x1a, 0x41, 0x7a, 0xdf, 0x6d, 0xff, 0xd5, 0x41, 0xcc, 0xd2, 0xd9,
0x2b, 0xfe, 0xb6, 0x3d, 0xc1, 0x56, 0x89, 0xcb, 0xd6, 0xb8, 0xe3, 0x2b,
0xd5, 0xe8, 0x66, 0xd9, 0xfa, 0xa2, 0xfe, 0x55, 0x95, 0xe2, 0xdb, 0xb9,
0xbe, 0x3e, 0x96, 0x5a, 0x70, 0x94, 0x25, 0x8b, 0x4a, 0x24, 0x9d, 0xfb,
0x75, 0x8a, 0x07, 0x80, 0x82, 0x73, 0x7a, 0x94, 0x51, 0x6c, 0x56, 0xf8,
0x85, 0x26, 0x2f, 0xd4, 0xd2, 0xac, 0x77, 0x5e, 0xba, 0xa1, 0x46, 0x84,
0xeb, 0xf6, 0x59, 0x39, 0x66, 0xef, 0x7d, 0x30, 0x84, 0x07, 0x8e, 0xdd,
0xd0, 0xf4, 0xfe, 0x94, 0x06, 0x04, 0x2b, 0x1d, 0x1a, 0x92, 0xb7, 0x0a,
0x0c, 0xce, 0x8d, 0x7d, 0xe7, 0xe9, 0xb6, 0xfe, 0x13, 0xfb, 0x26, 0x9a,
0x5a, 0x67, 0x57, 0x31, 0x61, 0x58, 0x9e, 0x2a, 0x79
};
v3::SecuredMessage msg;
EXPECT_TRUE(msg.decode(encoded_secured_msg));
EXPECT_EQ(3, msg.protocol_version());
StoredPositionProvider position_provider;
ManualRuntime runtime { Clock::at("2019-11-21 11:30") };
auto backend = create_backend("default");
StraightVerifyService verify_service { runtime, *backend, position_provider };
VerifyConfirm confirm = verify_service.verify(msg);
EXPECT_EQ(confirm.report, VerificationReport::Success);
EXPECT_EQ(confirm.its_aid, aid::CA);
ByteBuffer ca_ssp {0x01, 0x00, 0x00};
EXPECT_EQ(confirm.permissions, ca_ssp);
HashedId8 digest { 0x12, 0x7c, 0xff, 0x38, 0x4c, 0xe0, 0xb8, 0x90};
EXPECT_EQ(confirm.certificate_id, digest);
// AT issuer digest = 56dfd6d627a362dc
}
TEST(SecuredMessageV3, sign_and_verify)
{
StoredPositionProvider position_provider;
ManualRuntime runtime { Clock::at("2024-08-12 11:30") };
auto backend = create_backend("default");
v3::NaiveCertificateProvider cert_provider { runtime };
v3::DefaultSignHeaderPolicy sign_header_policy { runtime, position_provider, cert_provider };
v3::NullCertificateValidator cert_validator;
v3::StraightSignService sign_service { cert_provider, *backend, sign_header_policy, cert_validator };
SignRequest request;
ChunkPacket packet;
packet[OsiLayer::Application] = ByteBuffer {0x01, 0x02, 0x03};
request.plain_message = std::move(packet);
request.its_aid = aid::DEN;
SignConfirm sign_confirm = sign_service.sign(std::move(request));
ASSERT_TRUE(sign_confirm.secured_message);
StraightVerifyService verify_service { runtime, *backend, position_provider };
VerifyRequest verify_request { SecuredMessageView { *sign_confirm.secured_message } };
VerifyConfirm verify_confirm = verify_service.verify(verify_request);
EXPECT_EQ(verify_confirm.report, VerificationReport::Success);
EXPECT_TRUE(verify_confirm.certificate_validity.valid());
}
@@ -0,0 +1,17 @@
#include <vanetza/security/tests/serialization.hpp>
#include <boost/algorithm/hex.hpp>
namespace vanetza
{
namespace security
{
ByteBuffer buffer_from_hexstring(const char* string)
{
ByteBuffer buf;
boost::algorithm::unhex(string, back_inserter(buf));
return buf;
}
} // namespace security
} // namespace vanetza
@@ -0,0 +1,73 @@
#ifndef SERIALIZATION_HPP_ZWGI3RCG
#define SERIALIZATION_HPP_ZWGI3RCG
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/byte_buffer_source.hpp>
#include <vanetza/security/v2/serialization.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <sstream>
namespace vanetza
{
namespace security
{
/**
* \brief create a ByteBuffer from griven hex string
* \param string hex string
* \return equivalent byte buffer
*/
ByteBuffer buffer_from_hexstring(const char*);
/**
* \brief Deserialize any class from given hex string
* \tparam T the target type of deserialization
* \tparam ARGS additional arguments passed to deserialize function
* \param string hex string
* \param result deserialize into this object
* \param args additional arguments for deserialization
*/
template<typename T, typename... ARGS>
size_t deserialize_from_hexstring(const char* string, T& result, ARGS&&... args)
{
auto buffer = buffer_from_hexstring(string);
byte_buffer_source source(buffer);
boost::iostreams::stream_buffer<byte_buffer_source> stream(source);
InputArchive ar(stream);
return deserialize(ar, result, std::forward<ARGS>(args)...);
}
namespace v2
{
/**
* \brief Serialize and deserialize an object
*
* Source object is serialized and deserialized
* object form this binary representation is returned.
*
* \tparam T the type of the result
* \tparam ARGS additional arguments passed to underlying functions
* \param source serialize from this object
* \param args additional arguments
* \return deserialized object (should be equal to source)
*/
template<typename T, typename... ARGS>
T serialize_roundtrip(const T& source, ARGS&&... args)
{
std::stringstream stream;
OutputArchive oa(stream);
serialize(oa, source, std::forward<ARGS>(args)...);
T result;
InputArchive ia(stream);
deserialize(ia, result, std::forward<ARGS>(args)...);
return result;
}
} // namespace v2
} // namespace security
} // namespace vanetza
#endif /* SERIALIZATION_HPP_ZWGI3RCG */