diff --git a/CMakeLists.txt b/CMakeLists.txt index 945292818..7b8c568db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,11 @@ if(BUILD_BENCHMARK) add_subdirectory(tools/benchmark) endif() +option(BUILD_FUZZ "Build fuzz harness" OFF) +if(BUILD_FUZZ) + add_subdirectory(tools/fuzz-harness) +endif() + # interface library for convenience get_property(_components GLOBAL PROPERTY VANETZA_COMPONENTS) add_library(vanetza INTERFACE) diff --git a/docs/tools/fuzz-harness.md b/docs/tools/fuzz-harness.md new file mode 100644 index 000000000..d1557eb40 --- /dev/null +++ b/docs/tools/fuzz-harness.md @@ -0,0 +1,27 @@ +# fuzz-harness + +The fuzz harness for Vanetza is a testing tool designed to identify bugs, vulnerabilities, and unexpected behaviors within Vanetza's codebase. Fuzz testing, also known as fuzzing, involves providing invalid, unexpected, or random data as input to a program to uncover errors or security issues. + +## Requirements + +You will need AFL++ installed on your system. +Please refer to the [AFL++ documentation](https://aflplus.plus/docs/install/) for installation instructions. +Alternatively, you can use the scripts located at *tools/fuzz-harness* for running fuzz tests in a Docker container. + +## Usage + +Running the script *fuzz-harness/docker.sh* will build a suitable Docker container based on the official *aflplusplus/aflplusplus* image. +As soon as the container is ready, the script launches the built container and maps your local user and some Vanetza directories into it. + +Within the container, you can compile the *fuzz-harness* using the AFL++ toolchain by invoking the *compile.sh* script. +The *fuzz.sh* script is a convenient way to run the built harness with *afl-fuzz*. +If it crashes immediately, try again a few times. + +### Analyse + +Fuzzing is executing the *fuzzing-persistent* executable. +You can use its sibling *fuzzing-run* to investigate a particular crash and get more information about the possible problems. +The address sanitizer is enabled by default. If you're not interested in memory leaks, make sure to disable the leak sanatizer by setting the environment variable `ASAN_OPTIONS=detect_leaks=0`. + +You may also want to classify the found issues using *casr-afl*: `casr-afl -i output -o output/casr` +The classification process also eliminates duplicate issues. diff --git a/tools/benchmark/cases/security/validation.cpp b/tools/benchmark/cases/security/validation.cpp index 90dce2226..c89d2be64 100644 --- a/tools/benchmark/cases/security/validation.cpp +++ b/tools/benchmark/cases/security/validation.cpp @@ -55,7 +55,7 @@ int SecurityValidationCase::execute() std::vector> providers; std::vector> entities; - std::vector secured_messages(identities); + std::vector secured_messages(identities); for (unsigned i = 0; i < identities; i++) { providers.emplace_back(new v2::NaiveCertificateProvider(runtime)); @@ -83,9 +83,9 @@ int SecurityValidationCase::execute() encap_request.its_aid = aid::CA; EncapConfirm encap_confirm = entities[i]->encapsulate_packet(std::move(encap_request)); - secured_messages.push_back(boost::get(encap_confirm.sec_packet)); - auto signer_info = secured_messages[i].header_field(); - + auto v2_sec_msg = boost::get(encap_confirm.sec_packet); + auto signer_info = v2_sec_msg.header_field(); + if (signer_info_type == "hash") { assert(signer_info && get_type(*signer_info) == v2::SignerInfoType::Certificate_Digest_With_SHA256); } else if (signer_info_type == "certificate") { @@ -93,6 +93,8 @@ int SecurityValidationCase::execute() } else if (signer_info_type == "chain") { assert(signer_info && get_type(*signer_info) == v2::SignerInfoType::Certificate_Chain); } + + secured_messages.push_back(v2_sec_msg); } std::mt19937 gen(0); @@ -101,8 +103,8 @@ int SecurityValidationCase::execute() std::cout << "Starting benchmark for messages ... "; for (unsigned i = 0; i < messages; i++) { - SecuredMessage copy = secured_messages[dis(gen)]; - auto decap_confirm = security_entity.decapsulate_packet(std::move(copy)); + DecapRequest decap_request { SecuredMessageView { secured_messages[dis(gen)] }}; + auto decap_confirm = security_entity.decapsulate_packet(std::move(decap_request)); assert(decap_confirm.report == DecapReport::Success); } diff --git a/tools/fuzz-harness/.gitignore b/tools/fuzz-harness/.gitignore new file mode 100644 index 000000000..ea1472ec1 --- /dev/null +++ b/tools/fuzz-harness/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/tools/fuzz-harness/CMakeLists.txt b/tools/fuzz-harness/CMakeLists.txt new file mode 100644 index 000000000..a31c5ea3f --- /dev/null +++ b/tools/fuzz-harness/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(fuzzing-persistent router_fuzzing_context.cpp persistent.cpp) +target_link_libraries(fuzzing-persistent PUBLIC vanetza) + +add_executable(fuzzing-run router_fuzzing_context.cpp run.cpp) +target_link_libraries(fuzzing-run PUBLIC vanetza) diff --git a/tools/fuzz-harness/Dockerfile b/tools/fuzz-harness/Dockerfile new file mode 100644 index 000000000..663b39f30 --- /dev/null +++ b/tools/fuzz-harness/Dockerfile @@ -0,0 +1,15 @@ +ARG VERSION=latest +FROM aflplusplus/aflplusplus:${VERSION} + +# install build dependencies for Vanetza +RUN apt-get update && apt-get install --no-install-recommends -y \ + libboost-all-dev libcrypto++-dev libgeographic-dev libssl-dev + +# install casr-afl tool +RUN cargo install --root /usr/local casr + +# set up "fuzz" user and mapping of host user +RUN useradd -m -s /bin/bash fuzz +RUN cp /root/.bashrc /home/fuzz/.bashrc && chown fuzz:fuzz /home/fuzz/.bashrc +COPY docker-entrypoint.sh /docker-entrypoint.sh +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/tools/fuzz-harness/compile.sh b/tools/fuzz-harness/compile.sh new file mode 100755 index 000000000..bde757ca5 --- /dev/null +++ b/tools/fuzz-harness/compile.sh @@ -0,0 +1,24 @@ +#!/bin/bash -eu + +if [[ ! -d "/AFLplusplus" ]] ; then + echo "This script shall be run inside the AFL++ container" + exit 1 +fi + +cd /home/fuzz + +export CC=${CC:=afl-clang-lto} +export CXX=${CXX:=afl-clang-lto++} + +export AFL_LLVM_CMPLOG=1 +mkdir -p build/cmplog +cmake -S source -B build/cmplog -G Ninja -DBUILD_FUZZ=ON +cmake --build build/cmplog +unset AFL_LLVM_CMPLOG + +# see https://aflplus.plus/docs/env_variables/ for supported environment variables +export AFL_USE_ASAN=1 +export AFL_USE_UBSAN=1 +mkdir -p build/asan +cmake -S source -B build/asan -G Ninja -DBUILD_FUZZ=ON +cmake --build build/asan diff --git a/tools/fuzz-harness/docker-entrypoint.sh b/tools/fuzz-harness/docker-entrypoint.sh new file mode 100755 index 000000000..504682e10 --- /dev/null +++ b/tools/fuzz-harness/docker-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/bash -eu +afl-system-config + +usermod -u ${HOST_USER_ID} -g ${HOST_GROUP_ID} fuzz +ln -sf /source /home/fuzz/source +ln -sf /input /home/fuzz/input +ln -sf /output /home/fuzz/output +ln -sf /source/tools/fuzz-harness/compile.sh /home/fuzz/compile.sh +ln -sf /source/tools/fuzz-harness/fuzz.sh /home/fuzz/fuzz.sh +cd /home/fuzz +su fuzz diff --git a/tools/fuzz-harness/docker.sh b/tools/fuzz-harness/docker.sh new file mode 100755 index 000000000..c65e34a41 --- /dev/null +++ b/tools/fuzz-harness/docker.sh @@ -0,0 +1,15 @@ +#!/bin/bash -eu +HARNESS_DIR=$(realpath $(dirname $0)) +SOURCE_DIR=$HARNESS_DIR/../.. + +docker build $HARNESS_DIR +IMAGE=$(docker build -q $HARNESS_DIR) + +mkdir -p $HARNESS_DIR/output +docker run --rm -it \ + --security-opt seccomp=unconfined \ + -v$SOURCE_DIR:/source:ro \ + -v$HARNESS_DIR/input:/input:ro \ + -v$HARNESS_DIR/output:/output \ + -e HOST_USER_ID=$(id -u) -e HOST_GROUP_ID=$(id -g) \ + $IMAGE \ No newline at end of file diff --git a/tools/fuzz-harness/fuzz.sh b/tools/fuzz-harness/fuzz.sh new file mode 100755 index 000000000..7fc72e95a --- /dev/null +++ b/tools/fuzz-harness/fuzz.sh @@ -0,0 +1,5 @@ +#!/bin/bash -eu +: ${FUZZ_INPUT:="$HOME/input"} +: ${FUZZ_OUTPUT:="$HOME/output"} +: ${FUZZ_BUILD:="$HOME/build"} +afl-fuzz -i $FUZZ_INPUT -o $FUZZ_OUTPUT -c $FUZZ_BUILD/cmplog/bin/fuzzing-persistent -m none -- $FUZZ_BUILD/asan/bin/fuzzing-persistent diff --git a/tools/fuzz-harness/input/cam_v3_certificate.dat b/tools/fuzz-harness/input/cam_v3_certificate.dat new file mode 100644 index 000000000..273b9d3e7 Binary files /dev/null and b/tools/fuzz-harness/input/cam_v3_certificate.dat differ diff --git a/tools/fuzz-harness/persistent.cpp b/tools/fuzz-harness/persistent.cpp new file mode 100644 index 000000000..2e484cc65 --- /dev/null +++ b/tools/fuzz-harness/persistent.cpp @@ -0,0 +1,34 @@ +#include "router_fuzzing_context.hpp" +#include +#include + +#ifndef __AFL_FUZZ_TESTCASE_LEN +ssize_t fuzz_len; +#define __AFL_FUZZ_TESTCASE_LEN fuzz_len +unsigned char fuzz_buf[1024000]; +#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf +#define __AFL_FUZZ_INIT() void sync(void); +#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) +#define __AFL_INIT() sync() +#endif + +__AFL_FUZZ_INIT(); + +int main() +{ +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + + vanetza::RouterFuzzingContext context; + + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; + while (__AFL_LOOP(10000)) { + int len = __AFL_FUZZ_TESTCASE_LEN; + vanetza::ByteBuffer buffer { buf, buf + len }; + context.initialize(); + context.indicate(std::move(buffer)); + } + + return 0; +} diff --git a/tools/fuzz-harness/router_fuzzing_context.cpp b/tools/fuzz-harness/router_fuzzing_context.cpp new file mode 100644 index 000000000..a2205781c --- /dev/null +++ b/tools/fuzz-harness/router_fuzzing_context.cpp @@ -0,0 +1,44 @@ +#include "router_fuzzing_context.hpp" + +namespace vanetza +{ + +class FuzzingRequestInterface : public dcc::RequestInterface +{ + void request(const dcc::DataRequest&, std::unique_ptr) override {} +}; + +class FuzzingTransportInterface : public geonet::TransportInterface +{ + void indicate(const geonet::DataIndication&, std::unique_ptr) override {} +}; + +RouterFuzzingContext::RouterFuzzingContext() : + runtime(vanetza::Clock::at("2010-12-23 18:29")), security(runtime), + req_ifc(std::make_unique()), + ind_ifc(std::make_unique()) +{ + initialize(); +} + +void RouterFuzzingContext::initialize() +{ + router = std::make_unique(runtime, mib); + router->set_security_entity(&security.entity()); + router->set_access_interface(req_ifc.get()); + router->set_transport_handler(geonet::UpperProtocol::BTP_B, ind_ifc.get()); + + geonet::Address gn_addr; + gn_addr.mid(MacAddress{0, 0, 0, 0, 0, 1}); + router->set_address(gn_addr); +} + +void RouterFuzzingContext::indicate(ByteBuffer&& buffer) +{ + MacAddress source { 0, 0, 0, 0, 0, 2 }; + MacAddress destination { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + auto packet = std::make_unique(CohesivePacket { std::move(buffer), OsiLayer::Network }); + router->indicate(std::move(packet), source, destination); +} + +} // namespace vanetza diff --git a/tools/fuzz-harness/router_fuzzing_context.hpp b/tools/fuzz-harness/router_fuzzing_context.hpp new file mode 100644 index 000000000..929e2aeb1 --- /dev/null +++ b/tools/fuzz-harness/router_fuzzing_context.hpp @@ -0,0 +1,30 @@ +#ifndef VANETZA_ROUTER_FUZZING_CONTEXT_HPP +#define VANETZA_ROUTER_FUZZING_CONTEXT_HPP + +#include +#include +#include +#include +#include + +namespace vanetza +{ + +class RouterFuzzingContext { +public: + RouterFuzzingContext(); + void initialize(); + void indicate(ByteBuffer&& buffer); + +private: + ManualRuntime runtime; + geonet::ManagementInformationBase mib; + std::unique_ptr router; + std::unique_ptr req_ifc; + std::unique_ptr ind_ifc; + SecurityContext security; +}; + +} // namespace vanetza + +#endif //VANETZA_ROUTER_FUZZING_CONTEXT_HPP diff --git a/tools/fuzz-harness/run.cpp b/tools/fuzz-harness/run.cpp new file mode 100644 index 000000000..c66b3170d --- /dev/null +++ b/tools/fuzz-harness/run.cpp @@ -0,0 +1,42 @@ +#include "router_fuzzing_context.hpp" +#include +#include + +vanetza::ByteBuffer readFileIntoBuffer(const std::string &filename) +{ + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + std::cerr << "Error opening file: " << filename << std::endl; + return {}; + } + + const std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + vanetza::ByteBuffer buffer(size); + if (!file.read(reinterpret_cast(buffer.data()), size)) { + std::cerr << "Error reading file: " << filename << std::endl; + return {}; + } + + return buffer; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + const std::string filename = argv[1]; + vanetza::ByteBuffer buffer = readFileIntoBuffer(filename); + + if (buffer.empty()) { + return 1; + } + + vanetza::RouterFuzzingContext context; + context.indicate(std::move(buffer)); + return 0; +} diff --git a/vanetza/asn1/CMakeLists.txt b/vanetza/asn1/CMakeLists.txt index 00d2832fc..517715e4e 100644 --- a/vanetza/asn1/CMakeLists.txt +++ b/vanetza/asn1/CMakeLists.txt @@ -206,6 +206,7 @@ function(add_asn1_component NAME) endfunction() add_asn1_component(support) +target_sources(asn1_support PRIVATE memory.c) add_asn1_component(its) add_asn1_component(security) add_asn1_component(pki) diff --git a/vanetza/asn1/memory.c b/vanetza/asn1/memory.c new file mode 100644 index 000000000..77922b778 --- /dev/null +++ b/vanetza/asn1/memory.c @@ -0,0 +1,27 @@ +#include + +static size_t max_size = 4096; + +void* vanetza_asn1c_malloc(size_t size) +{ + if (size <= max_size) + return malloc(size); + else + return NULL; +} + +void* vanetza_asn1c_calloc(size_t nmemb, size_t size) +{ + if (nmemb * size <= max_size) + return calloc(nmemb, size); + else + return NULL; +} + +void* vanetza_asn1c_realloc(void* ptr, size_t size) +{ + if (size <= max_size) + return realloc(ptr, size); + else + return NULL; +} diff --git a/vanetza/asn1/memory.h b/vanetza/asn1/memory.h new file mode 100644 index 000000000..9bb682055 --- /dev/null +++ b/vanetza/asn1/memory.h @@ -0,0 +1,10 @@ +#ifndef VANETZA_ASN1C_MEMORY +#define VANETZA_ASN1C_MEMORY + +#include + +void* vanetza_asn1c_malloc(size_t size); +void* vanetza_asn1c_calloc(size_t nmemb, size_t size); +void* vanetza_asn1c_realloc(void* ptr, size_t size); + +#endif /* VANETZA_ASN1C_MEMORY */ \ No newline at end of file diff --git a/vanetza/asn1/support/asn_internal.h b/vanetza/asn1/support/asn_internal.h index 86984b679..f9413e99c 100644 --- a/vanetza/asn1/support/asn_internal.h +++ b/vanetza/asn1/support/asn_internal.h @@ -34,9 +34,10 @@ extern "C" { #define ASN1C_ENVIRONMENT_VERSION 923 /* Compile-time version */ int get_asn1c_environment_version(void); /* Run-time version */ -#define CALLOC(nmemb, size) calloc(nmemb, size) -#define MALLOC(size) malloc(size) -#define REALLOC(oldptr, size) realloc(oldptr, size) +#include +#define CALLOC(nmemb, size) vanetza_asn1c_calloc(nmemb, size) +#define MALLOC(size) vanetza_asn1c_malloc(size) +#define REALLOC(oldptr, size) vanetza_asn1c_realloc(oldptr, size) #define FREEMEM(ptr) free(ptr) #define asn_debug_indent 0 diff --git a/vanetza/common/archives.cpp b/vanetza/common/archives.cpp index ab5fd0841..348f4e4fb 100644 --- a/vanetza/common/archives.cpp +++ b/vanetza/common/archives.cpp @@ -22,6 +22,7 @@ void InputArchive::load_binary(char* data, std::size_t len) { std::size_t read_bytes = m_stream_buffer->sgetn(data, len); if (read_bytes != len) { + fail(ErrorCode::IncompleteData); throw Exception("incomplete read"); } } @@ -30,12 +31,31 @@ char InputArchive::peek_byte() { auto got = m_stream_buffer->sgetc(); if (got == StreamBuffer::traits_type::eof()) { + fail(ErrorCode::IncompleteData); throw Exception("impossible peek at end of stream"); } else { return StreamBuffer::traits_type::to_char_type(got); } } +bool InputArchive::is_good() const +{ + return m_error_code == ErrorCode::Ok; +} + +InputArchive::ErrorCode InputArchive::error_code() const +{ + return m_error_code; +} + +void InputArchive::fail(ErrorCode error_code) +{ + // do not overwrite prior error code except "ok" + if (m_error_code == ErrorCode::Ok) { + m_error_code = error_code; + } +} + std::size_t InputArchive::remaining_bytes() { return m_stream_buffer->in_avail(); diff --git a/vanetza/common/archives.hpp b/vanetza/common/archives.hpp index 832d60566..086f53bcb 100644 --- a/vanetza/common/archives.hpp +++ b/vanetza/common/archives.hpp @@ -22,6 +22,13 @@ class InputArchive using std::runtime_error::runtime_error; }; + enum class ErrorCode { + Ok, + IncompleteData, + ExcessiveLength, + ConstraintViolation, + }; + InputArchive(InputStream& is); InputArchive(StreamBuffer& buf); @@ -39,8 +46,13 @@ class InputArchive char peek_byte(); std::size_t remaining_bytes(); + bool is_good() const; + ErrorCode error_code() const; + void fail(ErrorCode error_code); + private: StreamBuffer* m_stream_buffer; + ErrorCode m_error_code = ErrorCode::Ok; }; /** diff --git a/vanetza/geonet/indication_context.cpp b/vanetza/geonet/indication_context.cpp index 02d162a0d..c13cda2e1 100644 --- a/vanetza/geonet/indication_context.cpp +++ b/vanetza/geonet/indication_context.cpp @@ -28,10 +28,10 @@ const CommonHeader* IndicationContextDeserialize::parse_common() const IndicationContext::SecuredMessage* IndicationContextDeserialize::parse_secured() { - boost::optional tmp; + IndicationContext::SecuredMessage tmp; auto bytes = m_parser.parse_secured(tmp); - if (bytes > 0 && tmp) { - pdu().secured(std::move(*tmp)); + if (bytes > 0) { + pdu().secured(std::move(tmp)); return pdu().secured(); } else { return nullptr; diff --git a/vanetza/geonet/parser.cpp b/vanetza/geonet/parser.cpp index 8769ebc7c..70d448f09 100644 --- a/vanetza/geonet/parser.cpp +++ b/vanetza/geonet/parser.cpp @@ -48,25 +48,26 @@ std::size_t Parser::parse_common(CommonHeader& common) return bytes; } -std::size_t Parser::parse_secured(boost::optional& secured) +std::size_t Parser::parse_secured(security::SecuredMessage& secured) { std::size_t bytes = 0; - try { - std::uint8_t sec_first_byte = m_archive.peek_byte(); - if (sec_first_byte < 3) { - security::v2::SecuredMessage msg; - bytes = security::v2::deserialize(m_archive, msg); - secured = std::move(msg); - } else if (sec_first_byte == 3) { - security::v3::SecuredMessage msg; - bytes = security::v3::deserialize(m_archive, msg); - secured = std::move(msg); + if (m_archive.is_good()) { + try { + std::uint8_t sec_first_byte = m_archive.peek_byte(); + if (sec_first_byte < 3) { + security::v2::SecuredMessage msg; + bytes = security::v2::deserialize(m_archive, msg); + secured = std::move(msg); + } else if (sec_first_byte == 3) { + security::v3::SecuredMessage msg; + bytes = security::v3::deserialize(m_archive, msg); + secured = std::move(msg); + } + } catch (...) { } - } catch (InputArchive::Exception&) { - } catch (security::deserialization_error&) { + m_read_bytes += bytes; } - m_read_bytes += bytes; return bytes; } diff --git a/vanetza/geonet/parser.hpp b/vanetza/geonet/parser.hpp index f5e97bdbf..ca9116160 100644 --- a/vanetza/geonet/parser.hpp +++ b/vanetza/geonet/parser.hpp @@ -27,7 +27,7 @@ class Parser std::size_t parse_basic(BasicHeader&); std::size_t parse_common(CommonHeader&); - std::size_t parse_secured(boost::optional&); + std::size_t parse_secured(security::SecuredMessage&); std::size_t parse_extended(HeaderVariant&, HeaderType); std::size_t parsed_bytes() const; diff --git a/vanetza/geonet/router.cpp b/vanetza/geonet/router.cpp index 83539fe8f..6e87262f7 100644 --- a/vanetza/geonet/router.cpp +++ b/vanetza/geonet/router.cpp @@ -363,38 +363,26 @@ DataConfirm Router::request(const TsbDataRequest&, DownPacketPtr) void Router::indicate(UpPacketPtr packet, const MacAddress& sender, const MacAddress& destination) { assert(packet); - - struct indication_visitor : public boost::static_visitor<> - { - indication_visitor(Router& router, const IndicationContext::LinkLayer& link_layer, UpPacketPtr packet) : - m_router(router), m_link_layer(link_layer), m_packet(std::move(packet)) - { - } - - void operator()(CohesivePacket& packet) - { - IndicationContextDeserialize ctx(std::move(m_packet), packet, m_link_layer); - m_router.indicate_basic(ctx); - } - - void operator()(ChunkPacket& packet) - { - IndicationContextCast ctx(std::move(m_packet), packet, m_link_layer); - m_router.indicate_basic(ctx); + const auto size_limit = m_mib.itsGnMaxSduSize + m_mib.itsGnMaxGeoNetworkingHeaderSize; + if (size(*packet) <= size_limit) { + IndicationContext::LinkLayer link_layer; + link_layer.sender = sender; + link_layer.destination = destination; + + if (auto cohesive = boost::get(packet.get())) { + IndicationContextDeserialize ctx(std::move(packet), *cohesive, link_layer); + indicate_basic(ctx); + + } else if (auto chunk = boost::get(packet.get())) { + IndicationContextCast ctx(std::move(packet), *chunk, link_layer); + indicate_basic(ctx); + } else { + packet_dropped(PacketDropReason::Internal_Error); } - - Router& m_router; - const IndicationContext::LinkLayer& m_link_layer; - UpPacketPtr m_packet; - }; - - IndicationContext::LinkLayer link_layer; - link_layer.sender = sender; - link_layer.destination = destination; - - UpPacket* packet_ptr = packet.get(); - indication_visitor visitor(*this, link_layer, std::move(packet)); - boost::apply_visitor(visitor, *packet_ptr); + } else { + packet_dropped(PacketDropReason::Packet_Size); + return; + } } void Router::indicate_basic(IndicationContextBasic& ctx) @@ -498,7 +486,7 @@ void Router::indicate_secured(IndicationContextBasic& ctx, const BasicHeader& ba } else if (m_security_entity) { // Decap packet using namespace vanetza::security; - DecapConfirm decap_confirm = m_security_entity->decapsulate_packet(DecapRequest(*secured_message)); + DecapConfirm decap_confirm = m_security_entity->decapsulate_packet(SecuredMessageView { *secured_message }); ctx.service_primitive().security_report = decap_confirm.report; ctx.service_primitive().its_aid = decap_confirm.its_aid; ctx.service_primitive().permissions = decap_confirm.permissions; diff --git a/vanetza/geonet/router.hpp b/vanetza/geonet/router.hpp index d5c84a418..af0f55b39 100644 --- a/vanetza/geonet/router.hpp +++ b/vanetza/geonet/router.hpp @@ -92,7 +92,9 @@ class Router Decap_Unsuccessful_Strict, Hop_Limit, Payload_Size, - Security_Entity_Missing + Security_Entity_Missing, + Packet_Size, + Internal_Error, }; // Reason for stopping packet forwarding diff --git a/vanetza/geonet/variant_pdu.cpp b/vanetza/geonet/variant_pdu.cpp index 808643c89..b24148242 100644 --- a/vanetza/geonet/variant_pdu.cpp +++ b/vanetza/geonet/variant_pdu.cpp @@ -81,6 +81,11 @@ void VariantPdu::secured(const SecuredMessage& smsg) m_secured = smsg; } +void VariantPdu::secured(SecuredMessage&& smsg) +{ + m_secured.emplace(std::move(smsg)); +} + std::unique_ptr VariantPdu::clone() const { return std::unique_ptr { new VariantPdu(*this) }; diff --git a/vanetza/geonet/variant_pdu.hpp b/vanetza/geonet/variant_pdu.hpp index 06f6731d0..1976f3564 100644 --- a/vanetza/geonet/variant_pdu.hpp +++ b/vanetza/geonet/variant_pdu.hpp @@ -33,6 +33,7 @@ class VariantPdu : public Pdu SecuredMessage* secured() override; const SecuredMessage* secured() const override; void secured(const SecuredMessage&) override; + void secured(SecuredMessage&&); std::unique_ptr clone() const override; private: diff --git a/vanetza/security/decap_request.hpp b/vanetza/security/decap_request.hpp index 675eb0fab..78af9e9f6 100644 --- a/vanetza/security/decap_request.hpp +++ b/vanetza/security/decap_request.hpp @@ -15,10 +15,11 @@ namespace security */ struct DecapRequest { - DecapRequest(SecuredMessage secmsg) : sec_packet(std::move(secmsg)) {} - SecuredMessage sec_packet; + DecapRequest(SecuredMessageView sec_msg_view) : sec_packet(sec_msg_view) {} + SecuredMessageView sec_packet; }; } // namespace security } // namespace vanetza + #endif // DECAP_REQUEST_HPP_WH8O09MB diff --git a/vanetza/security/delegating_security_entity.cpp b/vanetza/security/delegating_security_entity.cpp index c543b6d0a..c7c64b24e 100644 --- a/vanetza/security/delegating_security_entity.cpp +++ b/vanetza/security/delegating_security_entity.cpp @@ -32,13 +32,15 @@ EncapConfirm DelegatingSecurityEntity::encapsulate_packet(EncapRequest&& encap_r DecapConfirm DelegatingSecurityEntity::decapsulate_packet(DecapRequest&& decap_request) { - VerifyConfirm verify_confirm = m_verify_service->verify(VerifyRequest { decap_request.sec_packet }); DecapConfirm decap_confirm; + + VerifyConfirm verify_confirm = m_verify_service->verify(VerifyRequest { decap_request.sec_packet }); decap_confirm.plaintext_payload = get_payload_copy(decap_request.sec_packet); decap_confirm.report = static_cast(verify_confirm.report); decap_confirm.certificate_validity = verify_confirm.certificate_validity; decap_confirm.its_aid = verify_confirm.its_aid; decap_confirm.permissions = verify_confirm.permissions; + return decap_confirm; } diff --git a/vanetza/security/secured_message.cpp b/vanetza/security/secured_message.cpp index e26dc2b14..11c806f34 100644 --- a/vanetza/security/secured_message.cpp +++ b/vanetza/security/secured_message.cpp @@ -7,22 +7,32 @@ namespace vanetza namespace security { -ItsAid get_its_aid(const SecuredMessage& msg) +SecuredMessageView::SecuredMessageView(const SecuredMessage& msg) : + m_variant(msg) +{ +} + +struct ItsAidVisitor : boost::static_visitor { - struct Visitor : boost::static_visitor + ItsAid operator()(const v2::SecuredMessage& msg) const { - ItsAid operator()(const v2::SecuredMessage& msg) const - { - return get_its_aid(msg); - } + return get_its_aid(msg); + } - ItsAid operator()(const v3::SecuredMessage& msg) const - { - return msg.its_aid(); - } - }; + ItsAid operator()(const v3::SecuredMessage& msg) const + { + return msg.its_aid(); + } +}; - return boost::apply_visitor(Visitor(), msg); +ItsAid get_its_aid(const SecuredMessage& msg) +{ + return boost::apply_visitor(ItsAidVisitor(), msg); +} + +ItsAid get_its_aid(const SecuredMessageView& msg) +{ + return boost::apply_visitor(ItsAidVisitor(), msg); } std::size_t get_size(const SecuredMessage& msg) @@ -43,25 +53,31 @@ std::size_t get_size(const SecuredMessage& msg) return boost::apply_visitor(Visitor(), msg); } -void serialize(OutputArchive& ar, const SecuredMessage& msg) +struct SerializeVisitor : boost::static_visitor { - struct Visitor : boost::static_visitor + OutputArchive& m_archive; + SerializeVisitor(OutputArchive& ar) : m_archive(ar) {} + + void operator()(const v2::SecuredMessage& msg) { - OutputArchive& m_archive; - Visitor(OutputArchive& ar) : m_archive(ar) {} + serialize(m_archive, msg); + } - void operator()(const v2::SecuredMessage& msg) - { - serialize(m_archive, msg); - } + void operator()(const v3::SecuredMessage& msg) + { + serialize(m_archive, msg); + } +}; - void operator()(const v3::SecuredMessage& msg) - { - serialize(m_archive, msg); - } - }; +void serialize(OutputArchive& ar, const SecuredMessage& msg) +{ + SerializeVisitor visitor { ar }; + boost::apply_visitor(visitor, msg); +} - Visitor visitor(ar); +void serialize(OutputArchive& ar, const SecuredMessageView& msg) +{ + SerializeVisitor visitor { ar }; boost::apply_visitor(visitor, msg); } @@ -87,22 +103,27 @@ std::size_t deserialize(InputArchive& ar, SecuredMessage& msg) return boost::apply_visitor(visitor, msg); } -PacketVariant get_payload_copy(const SecuredMessage& msg) +struct PayloadCopyVisitor : boost::static_visitor { - struct Visitor : boost::static_visitor + PacketVariant operator()(const v2::SecuredMessage& msg) const { - PacketVariant operator()(const v2::SecuredMessage& msg) const - { - return msg.payload.data; - } + return msg.payload.data; + } - PacketVariant operator()(const v3::SecuredMessage& msg) const - { - return msg.payload(); - } - }; + PacketVariant operator()(const v3::SecuredMessage& msg) const + { + return msg.payload(); + } +}; - return boost::apply_visitor(Visitor {}, msg); +PacketVariant get_payload_copy(const SecuredMessage& msg) +{ + return boost::apply_visitor(PayloadCopyVisitor {}, msg); +} + +PacketVariant get_payload_copy(const SecuredMessageView& msg) +{ + return boost::apply_visitor(PayloadCopyVisitor {}, msg); } } // namespace security diff --git a/vanetza/security/secured_message.hpp b/vanetza/security/secured_message.hpp index aa10d8a04..3f0217f45 100644 --- a/vanetza/security/secured_message.hpp +++ b/vanetza/security/secured_message.hpp @@ -15,15 +15,34 @@ namespace security using SecuredMessage = boost::variant; +class SecuredMessageView +{ +public: + explicit SecuredMessageView(const SecuredMessage& msg); + + template + typename Visitor::result_type apply_visitor(Visitor& visitor) const + { + return m_variant.apply_visitor(visitor); + } + +private: + boost::variant m_variant; +}; + ItsAid get_its_aid(const SecuredMessage&); +ItsAid get_its_aid(const SecuredMessageView&); std::size_t get_size(const SecuredMessage& msg); +std::size_t get_size(const SecuredMessageView& msg); void serialize(OutputArchive& ar, const SecuredMessage& msg); +void serialize(OutputArchive& ar, const SecuredMessageView& msg); std::size_t deserialize(InputArchive& ar, SecuredMessage&); PacketVariant get_payload_copy(const SecuredMessage&); +PacketVariant get_payload_copy(const SecuredMessageView&); } // namespace security } // namespace vanetza diff --git a/vanetza/security/straight_verify_service.cpp b/vanetza/security/straight_verify_service.cpp index 9ad4f2c43..8b9368638 100644 --- a/vanetza/security/straight_verify_service.cpp +++ b/vanetza/security/straight_verify_service.cpp @@ -76,7 +76,7 @@ void StraightVerifyService::use_certificate_cache(v3::CertificateCache* cache) m_context_v3.m_cert_cache = cache; } -VerifyConfirm StraightVerifyService::verify(VerifyRequest&& request) +VerifyConfirm StraightVerifyService::verify(const VerifyRequest& request) { struct visitor : public boost::static_visitor { @@ -97,8 +97,7 @@ VerifyConfirm StraightVerifyService::verify(VerifyRequest&& request) StraightVerifyService* m_service = nullptr; } visitor(this); - const SecuredMessage& secured_message = request.secured_message; - return boost::apply_visitor(visitor, secured_message); + return boost::apply_visitor(visitor, request.secured_message); } VerifyConfirm StraightVerifyService::verify(const v2::SecuredMessage& secured_message) @@ -432,8 +431,16 @@ VerifyConfirm StraightVerifyService::verify(const v3::SecuredMessage& msg) return confirm; } + ByteBuffer encoded_cert; + try { + encoded_cert = asn1::encode_oer(asn_DEF_CertificateBase, certificate); + } catch (...) { + confirm.report = VerificationReport::Invalid_Certificate; + return confirm; + } + ByteBuffer data_hash = m_backend.calculate_hash(public_key->type, msg.signing_payload()); - ByteBuffer cert_hash = m_backend.calculate_hash(public_key->type, asn1::encode_oer(asn_DEF_CertificateBase, certificate)); + ByteBuffer cert_hash = m_backend.calculate_hash(public_key->type, encoded_cert); ByteBuffer concat_hash = data_hash; concat_hash.insert(concat_hash.end(), cert_hash.begin(), cert_hash.end()); ByteBuffer msg_hash = m_backend.calculate_hash(public_key->type, concat_hash); diff --git a/vanetza/security/straight_verify_service.hpp b/vanetza/security/straight_verify_service.hpp index db738b3c8..8640c1fa9 100644 --- a/vanetza/security/straight_verify_service.hpp +++ b/vanetza/security/straight_verify_service.hpp @@ -48,7 +48,7 @@ class StraightVerifyService : public VerifyService void use_certificate_cache(v3::CertificateCache*); - VerifyConfirm verify(VerifyRequest&&) override; + VerifyConfirm verify(const VerifyRequest&) override; VerifyConfirm verify(const v2::SecuredMessage&); VerifyConfirm verify(const v3::SecuredMessage&); diff --git a/vanetza/security/tests/dummy_verify_service.cpp b/vanetza/security/tests/dummy_verify_service.cpp index 14c2faffa..396a22e33 100644 --- a/vanetza/security/tests/dummy_verify_service.cpp +++ b/vanetza/security/tests/dummy_verify_service.cpp @@ -9,8 +9,8 @@ TEST(DummyVerifyServiceTest, lookup) std::unique_ptr dummy { new DummyVerifyService { VerificationReport::Invalid_Timestamp, CertificateValidity::valid() }}; - v2::SecuredMessage message; - VerifyRequest req(message); + SecuredMessage message; + VerifyRequest req(SecuredMessageView { message }); auto confirm = dummy->verify(std::move(req)); diff --git a/vanetza/security/tests/security_entity.cpp b/vanetza/security/tests/security_entity.cpp index 534be0d7c..ff9c7a83f 100644 --- a/vanetza/security/tests/security_entity.cpp +++ b/vanetza/security/tests/security_entity.cpp @@ -177,7 +177,7 @@ TEST_F(SecurityEntityTest, mutual_acceptance) verify->use_sign_header_policy(&sign_header_policy); DelegatingSecurityEntity other_security(std::move(sign), std::move(verify)); EncapConfirm encap_confirm = other_security.encapsulate_packet(create_encap_request()); - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); } @@ -219,12 +219,12 @@ TEST_F(SecurityEntityTest, mutual_acceptance_impl) // OpenSSL to Crypto++ EncapConfirm encap_confirm = openssl_security.encapsulate_packet(create_encap_request()); - DecapConfirm decap_confirm = cryptopp_security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + DecapConfirm decap_confirm = cryptopp_security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); // Crypto++ to OpenSSL encap_confirm = cryptopp_security.encapsulate_packet(create_encap_request()); - decap_confirm = openssl_security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + decap_confirm = openssl_security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); } #endif @@ -244,8 +244,9 @@ TEST_F(SecurityEntityTest, captured_acceptance) "765b6f5366837cda248d22f66da7d806e740810de221c6bd389c060bd02c48a9a574f32ec5a193ed2de21ef6d86de9e7c313d364f8" "91398776"; - v2::SecuredMessage message; - deserialize_from_hexstring(secured_cam, message); + v2::SecuredMessage v2_message; + deserialize_from_hexstring(secured_cam, v2_message); + security::SecuredMessage message = v2_message; runtime.reset(Clock::at("2018-02-15 16:28:30")); @@ -255,7 +256,7 @@ TEST_F(SecurityEntityTest, captured_acceptance) DelegatingSecurityEntity dummy_security(create_sign_service(), std::move(verify)); // We only care about the message signature here to be valid, the certificate isn't validated. - DecapConfirm decap_confirm = dummy_security.decapsulate_packet(DecapRequest { message }); + DecapConfirm decap_confirm = dummy_security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); } @@ -315,9 +316,8 @@ TEST_F(SecurityEntityTest, expected_payload) TEST_F(SecurityEntityTest, verify_message) { // build valid message - auto secured_message = create_secured_message(); - DecapRequest decap_request(secured_message); - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + security::SecuredMessage secured_message = create_secured_message(); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::Success, decap_confirm.report); @@ -330,14 +330,13 @@ TEST_F(SecurityEntityTest, verify_message) TEST_F(SecurityEntityTest, verify_message_modified_message_type) { // build message with wrong ITS-AID - auto secured_message = create_secured_message(); - IntX* its_aid = secured_message.header_field(); + auto v2_secured_message = create_secured_message(); + IntX* its_aid = v2_secured_message.header_field(); ASSERT_TRUE(its_aid); its_aid->set(42); // verify message - DecapRequest decap_request(std::move(secured_message)); - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { v2_secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::False_Signature, decap_confirm.report); } @@ -349,7 +348,8 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_name) certificate.subject_info.subject_name = {42}; // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Invalid_Name, decap_confirm.certificate_validity.reason()); } @@ -362,7 +362,8 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_signer_info) certificate.signer_info = faulty_hash; // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Unknown_Signer, decap_confirm.certificate_validity.reason()); } @@ -374,7 +375,8 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_subject_info) certificate.subject_info.subject_type = SubjectType::Root_CA; // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Invalid_Signer, decap_confirm.certificate_validity.reason()); } @@ -391,7 +393,8 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_subject_assurance } // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Unknown_Signer, decap_confirm.certificate_validity.reason()); } @@ -409,7 +412,8 @@ TEST_F(SecurityEntityTest, verify_message_outdated_certificate) certificate_provider->sign_authorization_ticket(certificate); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Off_Time_Period, decap_confirm.certificate_validity.reason()); @@ -428,7 +432,8 @@ TEST_F(SecurityEntityTest, verify_message_premature_certificate) certificate_provider->sign_authorization_ticket(certificate); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Off_Time_Period, decap_confirm.certificate_validity.reason()); @@ -448,7 +453,8 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_validity_restrict } // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Broken_Time_Period, decap_confirm.certificate_validity.reason()); } @@ -459,7 +465,8 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_signature) certificate.signature = create_random_ecdsa_signature(0); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Unknown_Signer, decap_confirm.certificate_validity.reason()); } @@ -467,16 +474,15 @@ TEST_F(SecurityEntityTest, verify_message_modified_certificate_signature) TEST_F(SecurityEntityTest, verify_message_modified_signature) { // hamper with signature - auto secured_message = create_secured_message(); - v2::Signature* signature = secured_message.trailer_field(); + auto v2_secured_message = create_secured_message(); + v2::Signature* signature = v2_secured_message.trailer_field(); ASSERT_TRUE(signature); ASSERT_EQ(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256, get_type(*signature)); EcdsaSignature& ecdsa_signature = boost::get(signature->some_ecdsa); ecdsa_signature.s = {8, 15, 23}; // verify message - DecapRequest decap_request(std::move(secured_message)); - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { v2_secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::False_Signature, decap_confirm.report); } @@ -484,12 +490,11 @@ TEST_F(SecurityEntityTest, verify_message_modified_signature) TEST_F(SecurityEntityTest, verify_message_modified_payload_type) { // change the payload type (should break signature) - auto secured_message = create_secured_message(); - secured_message.payload.type = PayloadType::Unsecured; + auto v2_secured_message = create_secured_message(); + v2_secured_message.payload.type = PayloadType::Unsecured; // verify message - DecapRequest decap_request(std::move(secured_message)); - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { v2_secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::Unsigned_Message, decap_confirm.report); } @@ -497,12 +502,11 @@ TEST_F(SecurityEntityTest, verify_message_modified_payload_type) TEST_F(SecurityEntityTest, verify_message_modified_payload) { // modify payload buffer - auto secured_message = create_secured_message(); - secured_message.payload.data = CohesivePacket({42, 42, 42}, OsiLayer::Session); + auto v2_secured_message = create_secured_message(); + v2_secured_message.payload.data = CohesivePacket({42, 42, 42}, OsiLayer::Session); // verify message - DecapRequest decap_request(std::move(secured_message)); - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { v2_secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::False_Signature, decap_confirm.report); } @@ -510,13 +514,13 @@ TEST_F(SecurityEntityTest, verify_message_modified_payload) TEST_F(SecurityEntityTest, verify_message_generation_time_before_current_time) { // prepare decap request - DecapRequest decap_request(create_secured_message()); + security::SecuredMessage secured_message = create_secured_message(); // change the time, so the generation time of SecuredMessage is before current time runtime.trigger(std::chrono::hours(12)); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::Invalid_Timestamp, decap_confirm.report); } @@ -527,23 +531,22 @@ TEST_F(SecurityEntityTest, verify_message_generation_time_after_current_time) runtime.trigger(std::chrono::hours(12)); // prepare decap request - auto secured_message = create_secured_message(); - DecapRequest decap_request(std::move(secured_message)); + security::SecuredMessage secured_message = create_secured_message(); // change the time, so the current time is before generation time of SecuredMessage runtime.reset(runtime.now() - std::chrono::hours(12)); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { secured_message }); // check if verify was successful EXPECT_EQ(DecapReport::Invalid_Timestamp, decap_confirm.report); } TEST_F(SecurityEntityTest, verify_message_without_signer_info) { - auto secured_message = create_secured_message(); + auto v2_secured_message = create_secured_message(); // iterate through all header_fields - auto& header_fields = secured_message.header_fields; + auto& header_fields = v2_secured_message.header_fields; for (auto field = header_fields.begin(); field != header_fields.end(); ++field) { // modify certificate if (HeaderFieldType::Signer_Info == get_type(*field)) { @@ -553,8 +556,7 @@ TEST_F(SecurityEntityTest, verify_message_without_signer_info) } // verify message - DecapRequest decap_request(std::move(secured_message)); - DecapConfirm decap_confirm = security.decapsulate_packet(std::move(decap_request)); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { v2_secured_message}); // check if verify was successful EXPECT_EQ(DecapReport::Signer_Certificate_Not_Found, decap_confirm.report); } @@ -781,7 +783,8 @@ TEST_F(SecurityEntityTest, verify_message_without_position_and_with_restriction) position_provider.position_fix(unknown); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Off_Region, decap_confirm.certificate_validity.reason()); @@ -794,7 +797,8 @@ TEST_F(SecurityEntityTest, verify_message_without_position_and_without_restricti position_provider.position_fix(unknown); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message() }); + security::SecuredMessage sec_msg = create_secured_message(); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); ASSERT_TRUE(decap_confirm.certificate_validity); } @@ -804,7 +808,8 @@ TEST_F(SecurityEntityTest, verify_message_with_insufficient_aid) its_aid = 42; // some random value not present in the certificate // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message() }); + security::SecuredMessage sec_msg = create_secured_message(); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Insufficient_ITS_AID, decap_confirm.certificate_validity.reason()); @@ -827,7 +832,8 @@ TEST_F(SecurityEntityTest, verify_non_cam_generation_location_ok) certificate_provider->sign_authorization_ticket(certificate); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); ASSERT_TRUE(decap_confirm.certificate_validity); } @@ -849,7 +855,8 @@ TEST_F(SecurityEntityTest, verify_non_cam_generation_location_fail) certificate_provider->sign_authorization_ticket(certificate); // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { create_secured_message(certificate) }); + security::SecuredMessage sec_msg = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { sec_msg }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Off_Region, decap_confirm.certificate_validity.reason()); @@ -857,31 +864,34 @@ TEST_F(SecurityEntityTest, verify_non_cam_generation_location_fail) TEST_F(SecurityEntityTest, verify_message_without_signature) { - auto message = create_secured_message(); - message.trailer_fields.clear(); + auto v2_message = create_secured_message(); + v2_message.trailer_fields.clear(); + security::SecuredMessage message = v2_message; // verify message - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { message }); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Unsigned_Message, decap_confirm.report); } TEST_F(SecurityEntityTest, verify_message_with_signer_info_hash) { - auto message_a = create_secured_message(); - auto message_b = create_secured_message(); - - auto signer_info = message_b.header_field(); - ASSERT_EQ(get_type(*signer_info), SignerInfoType::Certificate_Digest_With_SHA256); + auto message_with_cert = create_secured_message(); + auto signer_info_cert = message_with_cert.header_field(); + ASSERT_EQ(get_type(*signer_info_cert), SignerInfoType::Certificate); + auto message_with_digest = create_secured_message(); + auto signer_info_digest = message_with_digest.header_field(); + ASSERT_EQ(get_type(*signer_info_digest), SignerInfoType::Certificate_Digest_With_SHA256); + security::SecuredMessage message = message_with_digest; // verify message - hash unknown - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { message_b }); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Signer_Certificate_Not_Found, decap_confirm.report); EXPECT_EQ(cert_cache.size(), 1); cert_cache.insert(certificate_provider->own_certificate()); // verify message - certificate now known - decap_confirm = security.decapsulate_packet(DecapRequest { message_b }); + decap_confirm = security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); EXPECT_EQ(cert_cache.size(), 2); } @@ -890,13 +900,13 @@ TEST_F(SecurityEntityTest, verify_message_with_signer_info_chain) { sign_header_policy.request_certificate_chain(); - auto message = create_secured_message(); - - auto signer_info = message.header_field(); + auto v2_message = create_secured_message(); + auto signer_info = v2_message.header_field(); ASSERT_EQ(get_type(*signer_info), SignerInfoType::Certificate_Chain); // verify message - hash unknown - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { message }); + security::SecuredMessage message = v2_message; + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Success, decap_confirm.report); EXPECT_EQ(cert_cache.size(), 2); } @@ -914,9 +924,8 @@ TEST_F(SecurityEntityTest, verify_message_without_time_and_dummy_certificate_ver certificate.remove_restriction(ValidityRestrictionType::Time_Start_And_End); certificate_provider->sign_authorization_ticket(certificate); - auto message = create_secured_message(certificate); - - DecapConfirm decap_confirm = other_security.decapsulate_packet(DecapRequest { message }); + security::SecuredMessage message = create_secured_message(certificate); + DecapConfirm decap_confirm = other_security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Off_Time_Period, decap_confirm.certificate_validity.reason()); @@ -928,9 +937,8 @@ TEST_F(SecurityEntityTest, verify_message_without_public_key_in_certificate) certificate.remove_attribute(SubjectAttributeType::Verification_Key); certificate_provider->sign_authorization_ticket(certificate); - auto message = create_secured_message(certificate); - - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { message }); + security::SecuredMessage message = create_secured_message(certificate); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { message }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Missing_Public_Key, decap_confirm.certificate_validity.reason()); @@ -963,7 +971,7 @@ TEST_F(SecurityEntityTest, verify_message_certificate_requests) ASSERT_EQ(get_type(signer_info(msg(encap_confirm))), SignerInfoType::Certificate_Digest_With_SHA256); // Unknown certificate hash incoming from other - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); EXPECT_EQ(DecapReport::Signer_Certificate_Not_Found, decap_confirm.report); // Security entity does request certificate from other @@ -975,7 +983,7 @@ TEST_F(SecurityEntityTest, verify_message_certificate_requests) ASSERT_EQ(get_type(signer_info(msg(encap_confirm))), SignerInfoType::Certificate_Digest_With_SHA256); // Other receives certificate request and sends certificate with next message - decap_confirm = other_security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + decap_confirm = other_security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); encap_confirm = other_security.encapsulate_packet(create_encap_request()); ASSERT_EQ(get_type(signer_info(msg(encap_confirm))), SignerInfoType::Certificate); } @@ -1011,7 +1019,7 @@ TEST_F(SecurityEntityTest, verify_denm_without_generation_location) its_aid = aid::DEN; EncapConfirm encap_confirm = other_security.encapsulate_packet(create_encap_request()); - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); EXPECT_EQ(DecapReport::Invalid_Certificate, decap_confirm.report); ASSERT_FALSE(decap_confirm.certificate_validity); EXPECT_EQ(CertificateInvalidReason::Off_Region, decap_confirm.certificate_validity.reason()); @@ -1042,7 +1050,7 @@ TEST_F(SecurityEntityTest, verify_message_without_its_aid) auto msg = boost::get(encap_confirm.sec_packet); ASSERT_EQ(nullptr, msg.header_field()); - DecapConfirm decap_confirm = security.decapsulate_packet(DecapRequest { encap_confirm.sec_packet }); + DecapConfirm decap_confirm = security.decapsulate_packet(SecuredMessageView { encap_confirm.sec_packet }); EXPECT_EQ(DecapReport::Incompatible_Protocol, decap_confirm.report); } diff --git a/vanetza/security/v2/header_field.cpp b/vanetza/security/v2/header_field.cpp index dd1a04663..1fc965698 100644 --- a/vanetza/security/v2/header_field.cpp +++ b/vanetza/security/v2/header_field.cpp @@ -179,10 +179,15 @@ void serialize(OutputArchive& ar, const HeaderField& field) std::size_t deserialize(InputArchive& ar, std::list& 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 sym_algo; - while (read < size) { + while (read < size && ar.is_good()) { HeaderField field; HeaderFieldType type; deserialize(ar, type); @@ -277,7 +282,7 @@ std::size_t deserialize(InputArchive& ar, std::list& list) break; } } - return size; + return read; } } // namespace v2 diff --git a/vanetza/security/v2/payload.cpp b/vanetza/security/v2/payload.cpp index c79fe6fa3..73433576d 100644 --- a/vanetza/security/v2/payload.cpp +++ b/vanetza/security/v2/payload.cpp @@ -43,12 +43,17 @@ size_t deserialize(InputArchive& ar, Payload& payload) deserialize(ar, type); payload.type = type; + static const std::size_t data_length_limit = 4096; const auto data_length = deserialize_length(ar); - 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); + 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; } diff --git a/vanetza/security/v2/recipient_info.cpp b/vanetza/security/v2/recipient_info.cpp index dbaa2e59f..6428b2c66 100644 --- a/vanetza/security/v2/recipient_info.cpp +++ b/vanetza/security/v2/recipient_info.cpp @@ -99,6 +99,40 @@ void serialize(OutputArchive& ar, const RecipientInfo& info, SymmetricAlgorithm 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) { @@ -107,32 +141,12 @@ size_t deserialize(InputArchive& ar, RecipientInfo& info, const SymmetricAlgorit PublicKeyAlgorithm algo; deserialize(ar, algo); switch (algo) { - case PublicKeyAlgorithm::ECIES_NISTP256: { - 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; - } - info.enc_key = std::move(ecies); + case PublicKeyAlgorithm::ECIES_NISTP256: + info.enc_key = deserialize_ecies(ar, symAlgo); break; - } - default: { - const std::uintmax_t length = deserialize_length(ar); - ByteBuffer opaque(length); - for (std::uintmax_t i = 0; i < length; ++i) { - ar >> opaque[i]; - } - info.enc_key = OpaqueKey { std::move(opaque) }; + default: + info.enc_key = deserialize_opaque(ar); break; - } } return get_size(info); } diff --git a/vanetza/security/v2/serialization.cpp b/vanetza/security/v2/serialization.cpp index 9cb6bf24e..10f30fc9e 100644 --- a/vanetza/security/v2/serialization.cpp +++ b/vanetza/security/v2/serialization.cpp @@ -52,8 +52,12 @@ std::uintmax_t deserialize_length(InputArchive& ar) ar >> buf[c]; } auto tup = decode_length(buf); - assert(std::get<0>(tup) != buf.begin()); - return std::get<1>(tup); + if (std::get<0>(tup) != buf.begin()) { + return std::get<1>(tup); + } else { + ar.fail(InputArchive::ErrorCode::ConstraintViolation); + return 0; + } } } // namespace v2 diff --git a/vanetza/security/v2/serialization.hpp b/vanetza/security/v2/serialization.hpp index e9aa464bd..9e72acf52 100644 --- a/vanetza/security/v2/serialization.hpp +++ b/vanetza/security/v2/serialization.hpp @@ -91,14 +91,26 @@ template std::size_t deserialize(InputArchive& ar, std::list& list, ARGS&&... args) { using vanetza::security::v2::deserialize; + static const std::size_t length_limit = 4096; + const auto length = trim_size(deserialize_length(ar)); - std::intmax_t remainder = length; - while (remainder > 0) { - T t; - remainder -= deserialize(ar, t, std::forward(args)...); - list.push_back(std::move(t)); + if (length <= length_limit) { + std::intmax_t remainder = length; + while (remainder > 0) { + T t; + std::size_t size = deserialize(ar, t, std::forward(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); } - assert(remainder == 0); + return length; } diff --git a/vanetza/security/v2/subject_attribute.cpp b/vanetza/security/v2/subject_attribute.cpp index ed0903cbf..9ac5f46c5 100644 --- a/vanetza/security/v2/subject_attribute.cpp +++ b/vanetza/security/v2/subject_attribute.cpp @@ -56,11 +56,16 @@ 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); - 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]; + 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; } diff --git a/vanetza/security/verify_service.cpp b/vanetza/security/verify_service.cpp index 28794b38c..aef162cf9 100644 --- a/vanetza/security/verify_service.cpp +++ b/vanetza/security/verify_service.cpp @@ -9,7 +9,7 @@ DummyVerifyService::DummyVerifyService(VerificationReport report, CertificateVal { } -VerifyConfirm DummyVerifyService::verify(VerifyRequest&& request) +VerifyConfirm DummyVerifyService::verify(const VerifyRequest& request) { VerifyConfirm confirm; confirm.report = m_report; diff --git a/vanetza/security/verify_service.hpp b/vanetza/security/verify_service.hpp index c97a4a420..1dcda05de 100644 --- a/vanetza/security/verify_service.hpp +++ b/vanetza/security/verify_service.hpp @@ -34,8 +34,8 @@ enum class VerificationReport // mandatory parameters of SN-VERIFY.request (TS 102 723-8 V1.1.1) struct VerifyRequest { - VerifyRequest(const SecuredMessage& msg) : secured_message(msg) {} - const SecuredMessage& secured_message; /*< contains security header and payload */ + VerifyRequest(SecuredMessageView msg) : secured_message(msg) {} + SecuredMessageView secured_message; /*< contains security header and payload */ }; // parameters of SN-VERIFY.confirm (TS 102 723-8 V1.1.1) @@ -55,7 +55,7 @@ class VerifyService { public: virtual ~VerifyService() = default; - virtual VerifyConfirm verify(VerifyRequest&&) = 0; + virtual VerifyConfirm verify(const VerifyRequest&) = 0; }; @@ -70,7 +70,7 @@ class DummyVerifyService : public VerifyService * \param validity predefined certificate validity result */ DummyVerifyService(VerificationReport report, CertificateValidity validity); - VerifyConfirm verify(VerifyRequest&&) override; + VerifyConfirm verify(const VerifyRequest&) override; private: VerificationReport m_report;