From 68f9689234b5ded1eb9b69b212d7c32673a829ce Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Tue, 19 Nov 2024 19:06:03 +0100 Subject: [PATCH] Implement E2E Profile 7 (#11) --- CMakeLists.txt | 7 +- README.md | 2 +- doc/api.rst | 6 ++ src/e2e/__init__.py | 2 + src/e2e/p07.c | 247 ++++++++++++++++++++++++++++++++++++++++++++ src/e2e/p07.pyi | 11 ++ test/test_p07.py | 96 +++++++++++++++++ 7 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 src/e2e/p07.c create mode 100644 src/e2e/p07.pyi create mode 100644 test/test_p07.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 463f41d..cf27cf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,10 @@ python_add_library(p05 MODULE ${CMAKE_SOURCE_DIR}/src/e2e/p05.c ${PY_ABI_OPTIONS}) +python_add_library(p07 + MODULE + ${CMAKE_SOURCE_DIR}/src/e2e/p07.c + ${PY_ABI_OPTIONS}) # Add libraries add_library(crclib STATIC ${CMAKE_SOURCE_DIR}/src/e2e/crclib.c) @@ -49,5 +53,6 @@ target_link_libraries(p01 PRIVATE crclib) target_link_libraries(p02 PRIVATE crclib) target_link_libraries(p04 PRIVATE crclib util) target_link_libraries(p05 PRIVATE crclib util) +target_link_libraries(p07 PRIVATE crclib util) -install(TARGETS crc p01 p02 p04 p05 LIBRARY DESTINATION e2e) +install(TARGETS crc p01 p02 p04 p05 p07 LIBRARY DESTINATION e2e) diff --git a/README.md b/README.md index 30f8618..0170084 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The documentation is available [here](https://autosar-e2e.readthedocs.io/en/late This library provides fast C implementations of the E2E CRC algorithms and E2E profiles. Currently, all relevant CRC algorithms are available in module `e2e.crc` -but only E2E profiles 1, 2, 4 and 5 are available. +but only E2E profiles 1, 2, 4, 5 and 7 are available. If you provide example data for the other profiles I would try to implement them, too. ## Installation diff --git a/doc/api.rst b/doc/api.rst index 1e1b8ae..c82d34b 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -44,6 +44,12 @@ Profile 05 .. autofunction:: e2e.p05.e2e_p05_protect .. autofunction:: e2e.p05.e2e_p05_check +Profile 07 +"""""""""" + +.. autofunction:: e2e.p07.e2e_p07_protect +.. autofunction:: e2e.p07.e2e_p07_check + CRC Functions ^^^^^^^^^^^^^ diff --git a/src/e2e/__init__.py b/src/e2e/__init__.py index 10157c7..18a6a75 100644 --- a/src/e2e/__init__.py +++ b/src/e2e/__init__.py @@ -5,6 +5,7 @@ "p02", "p04", "p05", + "p07", ] from e2e import crc as crc @@ -12,4 +13,5 @@ from e2e import p02 as p02 from e2e import p04 as p04 from e2e import p05 as p05 +from e2e import p07 as p07 from e2e._version import __version__ diff --git a/src/e2e/p07.c b/src/e2e/p07.c new file mode 100644 index 0000000..87f2387 --- /dev/null +++ b/src/e2e/p07.c @@ -0,0 +1,247 @@ +/* SPDX-FileCopyrightText: 2022-present Artur Drogunow +# +# SPDX-License-Identifier: MIT */ + +#define PY_SSIZE_T_CLEAN +#include + +#include +#include + +#include "crclib.h" +#include "util.h" + +#define P07LENGTH_POS 8u +#define P07LENGTH_LEN 4u +#define P07COUNTER_POS 12u +#define P07COUNTER_LEN 4u +#define P07DATAID_POS 16u +#define P07DATAID_LEN 4u +#define P07CRC_POS 0u +#define P07CRC_LEN 8u + +#define P07HEADER_LEN (P07CRC_LEN+P07LENGTH_LEN+P07COUNTER_LEN+P07DATAID_LEN) + +uint64_t compute_p07_crc(uint8_t *data_ptr, + uint32_t length, + uint32_t offset) +{ + uint64_t crc; + + // bytes before crc bytes + uint32_t crc_offset = (uint32_t)(offset + P07CRC_POS); + crc = Crc_CalculateCRC64(data_ptr, crc_offset, CRC64_INITIAL_VALUE, true); + + // bytes after crc bytes, if any + if (offset + P07CRC_POS + P07CRC_LEN < length){ + uint32_t second_part_offset = offset + P07CRC_POS + P07CRC_LEN; + uint8_t *second_part_ptr = data_ptr + second_part_offset; + uint32_t second_part_len = length - (uint32_t)(offset + P07CRC_POS + P07CRC_LEN); + crc = Crc_CalculateCRC64(second_part_ptr, second_part_len, crc, false); + } + return crc; +} + +PyDoc_STRVAR(e2e_p07_protect_doc, + "e2e_p07_protect(data: bytearray, length: int, data_id: int, *, offset: int = 0, increment_counter: bool = True) -> None \n" + "Calculate CRC inplace according to AUTOSAR E2E Profile 7. \n" + "\n" + ":param bytearray data: \n" + " Mutable `bytes-like object `_.\n" + ":param int length: \n" + " Number of data bytes which are considered for CRC calculation. `length` must fulfill \n" + " the following condition: ``20 <= length <= len(data)`` \n" + ":param int data_id: \n" + " A unique identifier which is used to protect against masquerading. The `data_id` is a 32bit unsigned integer. \n" + ":param int offset: \n" + " Byte offset of the E2E header. \n" + ":param bool increment_counter: \n" + " If `True` the counter will be incremented before calculating the CRC. \n"); + +static PyObject * +py_e2e_p07_protect(PyObject *module, + PyObject *args, + PyObject *kwargs) +{ + Py_buffer data; + unsigned long length; + unsigned long data_id; + unsigned long offset = 0; + int increment = true; + + static char *kwlist[] = { + "data", + "length", + "data_id", + "offset", + "increment_counter", + NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*kk|$kp:e2e_p07_protect", + kwlist, &data, &length, &data_id, &offset, &increment)) + { + return NULL; + } + if (data.readonly) + { + PyErr_SetString(PyExc_ValueError, "\"data\" must be mutable. Use a bytearray or any object that implements the buffer protocol."); + goto error; + } + if (data.len < P07HEADER_LEN) + { + PyErr_SetString(PyExc_ValueError, "The length of bytearray \"data\" must be greater than or equal to 20."); + goto error; + } + if (length < P07HEADER_LEN || length > data.len) + { + PyErr_SetString(PyExc_ValueError, "Parameter \"length\" must fulfill the following condition: 20 <= length <= len(data)."); + goto error; + } + if (offset > data.len - P07HEADER_LEN) + { + PyErr_SetString(PyExc_ValueError, "Argument \"offset\" invalid."); + goto error; + } + + uint8_t *data_ptr = (uint8_t *)data.buf; + + // write length + uint32_to_bigendian(data_ptr + offset + P07LENGTH_POS, length); + + // increment counter + if (increment) { + uint32_t counter = bigendian_to_uint32(data_ptr + offset + P07COUNTER_POS); + counter += 1; + uint32_to_bigendian(data_ptr + offset + P07COUNTER_POS, counter); + } + + // write data_id + uint32_to_bigendian(data_ptr + offset + P07DATAID_POS, data_id); + + // calculate CRC + uint64_t crc = compute_p07_crc(data_ptr, length, offset); + uint64_to_bigendian(data_ptr + offset + P07CRC_POS, crc); + + PyBuffer_Release(&data); + + Py_RETURN_NONE; + +error: + PyBuffer_Release(&data); + return NULL; +} + +PyDoc_STRVAR(e2e_p07_check_doc, + "e2e_p07_check(data: bytes, length: int, data_id: int, *, offset: int = 0) -> bool \n" + "Return ``True`` if CRC is correct according to AUTOSAR E2E Profile 7. \n" + "\n" + ":param data: \n" + " `bytes-like object `_. \n" + ":param length: \n" + " Data byte count over which the CRC must be calculated. `length` must fulfill \n" + " the following condition: ``20 <= length <= len(data)`` \n" + ":param int data_id: \n" + " A unique identifier which is used to protect against masquerading. The `data_id` is a 32bit unsigned integer. \n" + ":param int offset: \n" + " Byte offset of the E2E header. \n" + ":return:\n" + " `True` if CRC is valid, otherwise return `False`"); + +static PyObject * +py_e2e_p07_check(PyObject *module, + PyObject *args, + PyObject *kwargs) +{ + Py_buffer data; + unsigned long length; + unsigned long data_id; + unsigned long offset = 0; + + static char *kwlist[] = { + "data", + "length", + "data_id", + "offset", + NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*kk|$k:e2e_p07_check", + kwlist, &data, &length, &data_id, &offset)) + { + return NULL; + } + if (data.len < P07HEADER_LEN) + { + PyErr_SetString(PyExc_ValueError, "The length of bytearray \"data\" must be greater or equal to 20."); + goto error; + } + if (length < P07HEADER_LEN || length > data.len) + { + PyErr_SetString(PyExc_ValueError, "Parameter \"length\" must fulfill the following condition: 20 <= length <= len(data)."); + goto error; + } + if (offset > data.len - P07HEADER_LEN) + { + PyErr_SetString(PyExc_ValueError, "Argument \"offset\" invalid."); + goto error; + } + + uint8_t *data_ptr = (uint8_t *)data.buf; + + // read length + uint32_t length_actual = bigendian_to_uint32(data_ptr + offset + P07LENGTH_POS); + + // read data_id + uint32_t data_id_actual = bigendian_to_uint32(data_ptr + offset + P07DATAID_POS); + + // read crc + uint64_t crc_actual = bigendian_to_uint64(data_ptr + offset + P07CRC_POS); + + // calculate CRC + uint64_t crc = compute_p07_crc(data_ptr, length, offset); + + PyBuffer_Release(&data); + + if ((length_actual == length) && (data_id_actual == data_id) && (crc_actual == crc)) + { + Py_RETURN_TRUE; + } + else + { + Py_RETURN_FALSE; + } + +error: + PyBuffer_Release(&data); + return NULL; +} + +static struct PyMethodDef methods[] = { + {"e2e_p07_protect", + (PyCFunction)py_e2e_p07_protect, + METH_VARARGS | METH_KEYWORDS, + e2e_p07_protect_doc}, + {"e2e_p07_check", + (PyCFunction)py_e2e_p07_check, + METH_VARARGS | METH_KEYWORDS, + e2e_p07_check_doc}, + {NULL} // sentinel +}; + +static PyModuleDef module = { + PyModuleDef_HEAD_INIT, + .m_name = "e2e.p07", + .m_doc = "", + .m_size = -1, + .m_methods = methods}; + +PyMODINIT_FUNC PyInit_p07(void) +{ + + PyObject *module_p; + module_p = PyModule_Create(&module); + + if (module_p == NULL) + return (NULL); + + return (module_p); +} diff --git a/src/e2e/p07.pyi b/src/e2e/p07.pyi new file mode 100644 index 0000000..1a73d7c --- /dev/null +++ b/src/e2e/p07.pyi @@ -0,0 +1,11 @@ +def e2e_p07_protect( + data: bytearray, + length: int, + data_id: int, + *, + offset: int = 0, + increment_counter: bool = True, +) -> None: ... +def e2e_p07_check( + data: bytes, length: int, data_id: int, *, offset: int = 0 +) -> bool: ... diff --git a/test/test_p07.py b/test/test_p07.py new file mode 100644 index 0000000..ac5140f --- /dev/null +++ b/test/test_p07.py @@ -0,0 +1,96 @@ +import e2e + + +def test_e2e_p07_protect(): + # short example + data = bytearray(b"\x00" * 24) + length = len(data) + data_id = 0x0a0b0c0d + + e2e.p07.e2e_p07_protect(data, length, data_id, increment_counter=False) + assert data == bytearray( + b"\x1f\xb2\xe7\x37\xfc\xed\xbc\xd9" + b"\x00\x00\x00\x18\x00\x00\x00\x00" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x00" + ), data.hex(sep=" ") + + e2e.p07.e2e_p07_protect(data, length, data_id, increment_counter=True) + assert data == bytearray( + b"\x7b\xde\x72\x68\xb8\xe9\xbc\x27" + b"\x00\x00\x00\x18\x00\x00\x00\x01" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x00" + ), data.hex(sep=" ") + + # long example (e.g. SOME/IP) + data = bytearray(b"\x00" * 32) + length = len(data) + data_id = 0x0a0b0c0d + offset = 8 # bytes + + e2e.p07.e2e_p07_protect( + data, length, data_id, offset=offset, increment_counter=False + ) + assert data == bytearray( + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x17\xf7\xc8\x17\x32\x38\x65\xa8" + b"\x00\x00\x00\x20\x00\x00\x00\x00" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x00" + ), data.hex(sep=" ") + + e2e.p07.e2e_p07_protect( + data, length, data_id, offset=offset, increment_counter=True + ) + assert data == bytearray( + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x73\x9b\x5d\x48\x76\x3c\x65\x56" + b"\x00\x00\x00\x20\x00\x00\x00\x01" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x00" + ), data.hex(sep=" ") + + +def test_e2e_p07_check(): + assert ( + e2e.p07.e2e_p07_check( + b"\x1f\xb2\xe7\x37\xfc\xed\xbc\xd9" + b"\x00\x00\x00\x18\x00\x00\x00\x00" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x00", + 24, + 0x0a0b0c0d, + ) + is True + ) + assert ( + e2e.p07.e2e_p07_check( + b"\x1f\xb2\xe7\x37\xfc\xed\xbc\xd9" + b"\x00\x00\x00\x18\x00\x00\x00\x00" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x01", + 24, + 0x0a0b0c0d, + ) + is False + ) + + assert ( + e2e.p07.e2e_p07_check( + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x17\xf7\xc8\x17\x32\x38\x65\xa8" + b"\x00\x00\x00\x20\x00\x00\x00\x00" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x00", + 32, + 0x0a0b0c0d, + offset=8, + ) + is True + ) + assert ( + e2e.p07.e2e_p07_check( + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x17\xf7\xc8\x17\x32\x38\x65\xa8" + b"\x00\x00\x00\x20\x00\x00\x00\x00" + b"\x0a\x0b\x0c\x0d\x00\x00\x00\x01", + 32, + 0x0a0b0c0d, + offset=8, + ) + is False + )