Add packed-c protocol support for crypto service
To provide a lightweight parameter encoding that is aligned to
conventions used by SCMI, the packed-c parameter serialization has
been added to the crypto service. This builds on generic
components that allow other packed-c service access protocols
to be added easily. Service level tests have been extended to
use both protobuf and packed-c clients.
Signed-off-by: julhal01 <julian.hall@arm.com>
Change-Id: I9279b0814bcc9cf6c4aa4e30629e2f46f2df4c23
diff --git a/components/common/tlv/component.cmake b/components/common/tlv/component.cmake
new file mode 100644
index 0000000..6fafa16
--- /dev/null
+++ b/components/common/tlv/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/tlv.c"
+ )
diff --git a/components/common/tlv/test/component.cmake b/components/common/tlv/test/component.cmake
new file mode 100644
index 0000000..4394d30
--- /dev/null
+++ b/components/common/tlv/test/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/tlv_tests.cpp"
+ )
diff --git a/components/common/tlv/test/tlv_tests.cpp b/components/common/tlv/test/tlv_tests.cpp
new file mode 100644
index 0000000..661a810
--- /dev/null
+++ b/components/common/tlv/test/tlv_tests.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include <common/tlv/tlv.h>
+#include <CppUTest/TestHarness.h>
+
+TEST_GROUP(TlvTests)
+{
+
+};
+
+TEST(TlvTests, decodeGoodRecords)
+{
+ struct tlv_const_iterator iter;
+ struct tlv_record decoded_record;
+
+ /* Case 1: Valid single zero length record */
+ const uint8_t case_1[] = {
+ 0x77, 0x31, 0x00, 0x00
+ };
+
+ tlv_const_iterator_begin(&iter, case_1, sizeof(case_1));
+ CHECK_TRUE(tlv_decode(&iter, &decoded_record));
+ UNSIGNED_LONGS_EQUAL(0x7731, decoded_record.tag);
+ UNSIGNED_LONGS_EQUAL(0, decoded_record.length);
+ CHECK_FALSE(tlv_decode(&iter, &decoded_record));
+
+ /* Case 2: Two valid records */
+ const uint8_t case_2[] = {
+ 0x01, 0x10, 0x00, 0x01, 0x50,
+ 0x02, 0x11, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04
+ };
+
+ tlv_const_iterator_begin(&iter, case_2, sizeof(case_2));
+ CHECK_TRUE(tlv_decode(&iter, &decoded_record));
+ UNSIGNED_LONGS_EQUAL(0x0110, decoded_record.tag);
+ UNSIGNED_LONGS_EQUAL(1, decoded_record.length);
+ UNSIGNED_LONGS_EQUAL(0x50, decoded_record.value[0]);
+ CHECK_TRUE(tlv_decode(&iter, &decoded_record));
+ UNSIGNED_LONGS_EQUAL(0x0211, decoded_record.tag);
+ UNSIGNED_LONGS_EQUAL(4, decoded_record.length);
+ UNSIGNED_LONGS_EQUAL(0x01, decoded_record.value[0]);
+ UNSIGNED_LONGS_EQUAL(0x02, decoded_record.value[1]);
+ UNSIGNED_LONGS_EQUAL(0x03, decoded_record.value[2]);
+ UNSIGNED_LONGS_EQUAL(0x04, decoded_record.value[3]);
+ CHECK_FALSE(tlv_decode(&iter, &decoded_record));
+}
+
+TEST(TlvTests, findAndDecode)
+{
+ struct tlv_const_iterator iter;
+ struct tlv_record decoded_record;
+
+ /*
+ * Checks use of the tlv_find_decode method to extract known records
+ * from a sequence of records, encoded in ascending tag order, that includes
+ * ones that the receiver isn't interested in.
+ */
+ const uint8_t encoded_records[] = {
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x03, 0x88, 0x88, 0x88,
+ 0x00, 0x07, 0x00, 0x02, 0x77, 0x77,
+ 0x00, 0x09, 0x00, 0x01, 0x77
+ };
+
+ tlv_const_iterator_begin(&iter, encoded_records, sizeof(encoded_records));
+ CHECK_TRUE(tlv_find_decode(&iter, 0x0001, &decoded_record));
+ UNSIGNED_LONGS_EQUAL(0, decoded_record.length);
+ CHECK_TRUE(tlv_find_decode(&iter, 0x0007, &decoded_record));
+ UNSIGNED_LONGS_EQUAL(2, decoded_record.length);
+ CHECK_TRUE(tlv_find_decode(&iter, 0x0009, &decoded_record));
+ UNSIGNED_LONGS_EQUAL(1, decoded_record.length);
+}
+
+TEST(TlvTests, decodeBadRecords)
+{
+ struct tlv_const_iterator iter;
+ struct tlv_record decoded_record;
+
+ /* Case 1: Too short to accommodate a valid header */
+ const uint8_t case_1[] = {
+ 0x77, 0x31, 0x00
+ };
+
+ tlv_const_iterator_begin(&iter, case_1, sizeof(case_1));
+ CHECK_FALSE(tlv_decode(&iter, &decoded_record));
+
+ /* Case 1: A complete record followed by a truncated one */
+ const uint8_t case_2[] = {
+ 0x77, 0x31, 0x00, 0x00,
+ 0x03, 0x21, 0x00, 0x03, 0xaa
+ };
+
+ tlv_const_iterator_begin(&iter, case_2, sizeof(case_2));
+ CHECK_TRUE(tlv_decode(&iter, &decoded_record));
+ CHECK_FALSE(tlv_decode(&iter, &decoded_record));
+}
+
+TEST(TlvTests, encodeRecords)
+{
+ struct tlv_iterator iter;
+ struct tlv_record record_to_encode;
+ size_t required_space;
+
+ /* Case 1: Encode zero length record */
+ const uint8_t case_1_expected[] = {
+ 0x66, 0x77, 0x00, 0x00
+ };
+ record_to_encode.tag = 0x6677;
+ record_to_encode.length = 0;
+ record_to_encode.value = NULL;
+ required_space = tlv_required_space(record_to_encode.length);
+ uint8_t case_1_actual[required_space];
+ tlv_iterator_begin(&iter, case_1_actual, required_space);
+ CHECK_TRUE(tlv_encode(&iter, &record_to_encode));
+ MEMCMP_EQUAL(case_1_expected, case_1_actual, required_space);
+
+ /* Case 2: Encode two records */
+ const uint8_t case_2_expected[] = {
+ 0xa8, 0xa9, 0x00, 0x01, 0x88,
+ 0xa8, 0xaa, 0x00, 0x02, 0x01, 0x02
+ };
+
+ required_space = tlv_required_space(1) + tlv_required_space(2);
+ uint8_t case_2_actual[required_space];
+ tlv_iterator_begin(&iter, case_2_actual, required_space);
+
+ record_to_encode.tag = 0xa8a9;
+ record_to_encode.length = 1;
+ record_to_encode.value = &case_2_expected[4];
+ CHECK_TRUE(tlv_encode(&iter, &record_to_encode));
+
+ record_to_encode.tag = 0xa8aa;
+ record_to_encode.length = 2;
+ record_to_encode.value = &case_2_expected[9];
+ CHECK_TRUE(tlv_encode(&iter, &record_to_encode));
+
+ MEMCMP_EQUAL(case_2_expected, case_2_actual, required_space);
+
+ /* Check that you can't encode beyond the limit of the buffer */
+ CHECK_FALSE(tlv_encode(&iter, &record_to_encode));
+}
+
+TEST(TlvTests, encodeInsufficientSpace)
+{
+ struct tlv_iterator iter;
+ struct tlv_record record_to_encode;
+ size_t required_space;
+
+ /* Case 1: Encode record into buffer that isn't big enough */
+ const uint8_t case_1_expected[] = {
+ 0x66, 0x77, 0x00, 0x03, 0x01, 0x02, 0x03
+ };
+ record_to_encode.tag = 0x6677;
+ record_to_encode.length = 3;
+ record_to_encode.value = &case_1_expected[4];
+ required_space = tlv_required_space(record_to_encode.length) - 1;
+ uint8_t case_1_actual[required_space];
+ tlv_iterator_begin(&iter, case_1_actual, required_space);
+ CHECK_FALSE(tlv_encode(&iter, &record_to_encode));
+}
+
+TEST(TlvTests, encodeWrongOrder)
+{
+ struct tlv_iterator iter;
+ struct tlv_record record_to_encode;
+ size_t required_space;
+
+ /* Check defence against encoding successive records
+ * out of tag value order. Encoding rules require
+ * records to be in ascending tag value order.
+ */
+ const uint8_t record_value[] = {
+ 0x11, 0x22, 0x33, 0x44
+ };
+
+ /* Attemps to encode 4 records, the first 3 obey order
+ * rule, the last one doesn't
+ */
+ required_space = tlv_required_space(sizeof(record_value)) * 4;
+ uint8_t encode_buffer[required_space];
+ tlv_iterator_begin(&iter, encode_buffer, required_space);
+
+ record_to_encode.tag = 1;
+ record_to_encode.length = sizeof(record_value);
+ record_to_encode.value = record_value;
+ CHECK_TRUE(tlv_encode(&iter, &record_to_encode));
+
+ record_to_encode.tag = 2;
+ record_to_encode.length = sizeof(record_value);
+ record_to_encode.value = record_value;
+ CHECK_TRUE(tlv_encode(&iter, &record_to_encode));
+
+ record_to_encode.tag = 2;
+ record_to_encode.length = sizeof(record_value);
+ record_to_encode.value = record_value;
+ CHECK_TRUE(tlv_encode(&iter, &record_to_encode));
+
+ record_to_encode.tag = 1;
+ record_to_encode.length = sizeof(record_value);
+ record_to_encode.value = record_value;
+ CHECK_FALSE(tlv_encode(&iter, &record_to_encode));
+}
diff --git a/components/common/tlv/tlv.c b/components/common/tlv/tlv.c
new file mode 100644
index 0000000..1308832
--- /dev/null
+++ b/components/common/tlv/tlv.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "tlv.h"
+#include <string.h>
+
+size_t tlv_required_space(size_t length)
+{
+ return TLV_HDR_LEN + length;
+}
+
+void tlv_iterator_begin(struct tlv_iterator *iter, uint8_t *buf, size_t bufsize)
+{
+ iter->pos = buf;
+ iter->limit = &buf[bufsize];
+
+ /* Defend against overflow */
+ if (iter->limit < buf) iter->limit = buf;
+
+ /* Used to enforce ascending tag order when encoding */
+ iter->prev_tag = 0;
+}
+
+void tlv_const_iterator_begin(struct tlv_const_iterator *iter, const uint8_t *buf, size_t bufsize)
+{
+ iter->pos = buf;
+ iter->limit = &buf[bufsize];
+
+ /* Defend against overflow */
+ if (iter->limit < buf) iter->limit = buf;
+}
+
+bool tlv_encode(struct tlv_iterator *iter, const struct tlv_record *input)
+{
+ bool success = false;
+ size_t required_space = tlv_required_space(input->length);
+ size_t available_space = iter->limit - iter->pos;
+
+ if (required_space <= available_space && input->tag >= iter->prev_tag) {
+
+ iter->pos[TLV_TAG_OFFSET + 0] = (uint8_t)(input->tag >> 8);
+ iter->pos[TLV_TAG_OFFSET + 1] = (uint8_t)(input->tag);
+ iter->pos[TLV_LENGTH_OFFSET + 0] = (uint8_t)(input->length >> 8);
+ iter->pos[TLV_LENGTH_OFFSET + 1] = (uint8_t)(input->length);
+
+ memcpy(&iter->pos[TLV_VALUE_OFFSET], input->value, input->length);
+
+ iter->pos += required_space;
+ iter->prev_tag = input->tag;
+ success = true;
+ }
+
+ return success;
+}
+
+bool tlv_decode(struct tlv_const_iterator *iter, struct tlv_record *output)
+{
+ bool success = false;
+ size_t max_space = iter->limit - iter->pos;
+
+ if (max_space >= TLV_HDR_LEN) {
+
+ size_t record_len;
+ output->tag = (iter->pos[TLV_TAG_OFFSET + 0] << 8) | iter->pos[TLV_TAG_OFFSET + 1];
+ output->length = (iter->pos[TLV_LENGTH_OFFSET + 0] << 8) | iter->pos[TLV_LENGTH_OFFSET + 1];
+ output->value = &iter->pos[TLV_VALUE_OFFSET];
+
+ record_len = output->length + TLV_HDR_LEN;
+
+ if (record_len <= max_space) {
+
+ iter->pos += record_len;
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool tlv_find_decode(struct tlv_const_iterator *iter, uint16_t tag, struct tlv_record *output)
+{
+ while (tlv_decode(iter, output)) {
+
+ if (output->tag == tag) {
+ /* Found a record */
+ return true;
+ }
+ else if (output->tag > tag) {
+ /* Iterated beyond the expected parameter */
+ return false;
+ }
+ }
+
+ /* Reached the end of the buffer without finding a record with the requested tag */
+ return false;
+}
diff --git a/components/common/tlv/tlv.h b/components/common/tlv/tlv.h
new file mode 100644
index 0000000..64233a3
--- /dev/null
+++ b/components/common/tlv/tlv.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TLV_H
+#define TLV_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TLV provides a general purpose serialization for variable length
+ * objects, identified by a tag. A serialized TLV record has the following
+ * structure:
+ * | Tag | Length | Value |
+ * | (16-bits) | (16-bits) | (Length bytes) |
+ *
+ * No assumptions are made about the alignment of the start of a serialized record.
+ * Tag and Length fields are encoded in Big Endian byte order.
+ */
+#define TLV_TAG_WIDTH (2)
+#define TLV_LENGTH_WIDTH (2)
+#define TLV_HDR_LEN (TLV_TAG_WIDTH + TLV_LENGTH_WIDTH)
+#define TLV_TAG_OFFSET (0)
+#define TLV_LENGTH_OFFSET TLV_TAG_WIDTH
+#define TLV_VALUE_OFFSET TLV_HDR_LEN
+
+/*
+ * TLV record structure provides access to a serialized record.
+ */
+struct tlv_record
+{
+ uint16_t tag;
+ uint16_t length;
+ const uint8_t *value;
+};
+
+/*
+ * Iterator state for interating over serialized tlv records when encoding.
+ */
+struct tlv_iterator
+{
+ uint8_t *pos;
+ uint8_t *limit;
+ uint16_t prev_tag;
+};
+
+/*
+ * Iterator state for interating over serialized tlv records when decoding.
+ */
+struct tlv_const_iterator
+{
+ const uint8_t *pos;
+ const uint8_t *limit;
+};
+
+/*
+ * Return the space required in bytes for a serialized record with the
+ * specified value length.
+ */
+size_t tlv_required_space(size_t length);
+
+/*
+ * Initializes a TLV iterator to the start of a buffer. Used when writing
+ * records to a buffer when encoding.
+ */
+void tlv_iterator_begin(struct tlv_iterator *iter, uint8_t *buf, size_t bufsize);
+
+/*
+ * Initializes a TLV const iterator to the start of a buffer. Used when reading
+ * records from a buffer when decoding.
+ */
+void tlv_const_iterator_begin(struct tlv_const_iterator *iter, const uint8_t *buf, size_t bufsize);
+
+/*
+ * Encode a serialized record and advance the iterator, ready to encode the next
+ * record. Returns true if successful, false if insufficient room.
+ */
+bool tlv_encode(struct tlv_iterator *iter, const struct tlv_record *input);
+
+/*
+ * Decode a serialized record and advance the iterator, ready to decode the next
+ * record (if there is one). Returns true if successful, false there is no serialized record
+ * or an incomplete one.
+ */
+bool tlv_decode(struct tlv_const_iterator *iter, struct tlv_record *output);
+
+/*
+ * Advances the iterator until a record with the specified tag is found. If
+ * it's found, it's decoded and the iterator is advanced to the next position.
+ * This can be used when decoding an expected set of records, encoded in ascending
+ * tag order. Any unrecognised records are skipped over. This is the typical
+ * decoding pattern.
+ */
+bool tlv_find_decode(struct tlv_const_iterator *iter, uint16_t tag, struct tlv_record *output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TLV_H */