Crypto: Add test infrastructure

This patch introduces changes to run regression testing
for both secure and non-secure domains for the Crypto
service. It also provides a basic set of tests for AES
symmetric ciphers in CFB and CBC mode.

Change-Id: Ib3f54056cd2b21a327c55539dd130d100aa23e45
Signed-off-by: Antonio de Angelis <antonio.deangelis@arm.com>
diff --git a/test/CMakeLists.inc b/test/CMakeLists.inc
index fdc59bf..97f3566 100644
--- a/test/CMakeLists.inc
+++ b/test/CMakeLists.inc
@@ -31,4 +31,5 @@
 include(${CMAKE_CURRENT_LIST_DIR}/suites/invert/CMakeLists.inc)
 include(${CMAKE_CURRENT_LIST_DIR}/suites/sst/CMakeLists.inc)
 include(${CMAKE_CURRENT_LIST_DIR}/suites/audit/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/crypto/CMakeLists.inc)
 include(${CMAKE_CURRENT_LIST_DIR}/test_services/CMakeLists.inc)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 1cf33e5..f3491ae 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -48,6 +48,7 @@
 
 set(ENABLE_SECURE_STORAGE_SERVICE_TESTS TRUE)
 set(ENABLE_AUDIT_LOGGING_SERVICE_TESTS TRUE)
+set(ENABLE_CRYPTO_SERVICE_TESTS TRUE)
 set(ENABLE_INVERT_SERVICE_TESTS TRUE)
 include(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.inc)
 
diff --git a/test/framework/non_secure_suites.c b/test/framework/non_secure_suites.c
index 68e2d20..7ba01f5 100644
--- a/test/framework/non_secure_suites.c
+++ b/test/framework/non_secure_suites.c
@@ -12,6 +12,7 @@
 /* Service specific includes */
 #include "test/suites/sst/non_secure/sst_ns_tests.h"
 #include "test/suites/audit/non_secure/audit_ns_tests.h"
+#include "test/suites/crypto/non_secure/crypto_ns_tests.h"
 #include "test/suites/invert/non_secure/invert_ns_tests.h"
 #include "test/suites/core/non_secure/core_ns_tests.h"
 
@@ -47,6 +48,9 @@
     /* Non-secure Audit Logging test cases */
     {&register_testsuite_ns_audit_interface, 0, 0, 0},
 
+    /* Non-secure Crypto test cases */
+    {&register_testsuite_ns_crypto_interface, 0, 0, 0},
+
 #ifdef TFM_PARTITION_TEST_CORE
     /* Non-secure invert test cases */
     /* Note: since this is sample code, only run if test services are enabled */
diff --git a/test/framework/secure_suites.c b/test/framework/secure_suites.c
index 89369cb..cb92f79 100644
--- a/test/framework/secure_suites.c
+++ b/test/framework/secure_suites.c
@@ -13,6 +13,8 @@
 #include "test/suites/sst/secure/sst_tests.h"
 #include "test/suites/audit/secure/audit_s_tests.h"
 #include "test/suites/invert/secure/invert_s_tests.h"
+#include "test/suites/crypto/secure/crypto_s_tests.h"
+
 #include "secure_fw/services/secure_storage/sst_object_system.h"
 
 static struct test_suite_t test_suites[] = {
@@ -36,6 +38,9 @@
     /* Secure Audit Logging test cases */
     {&register_testsuite_s_audit_interface, 0, 0, 0},
 
+    /* Crypto test cases */
+    {&register_testsuite_s_crypto_interface, 0, 0, 0},
+
 #ifdef TFM_PARTITION_TEST_CORE
     /* Secure invert test cases */
     /* Note: since this is sample code, only run if test services are enabled */
diff --git a/test/suites/crypto/CMakeLists.inc b/test/suites/crypto/CMakeLists.inc
new file mode 100644
index 0000000..12a37a8
--- /dev/null
+++ b/test/suites/crypto/CMakeLists.inc
@@ -0,0 +1,37 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "Crypto test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CRYPTO_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+  message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_CRYPTO_SERVICE_TESTS)
+  message(FATAL_ERROR "Incomplete build configuration: ENABLE_CRYPTO_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_CRYPTO_SERVICE_TESTS)
+  list(APPEND ALL_SRC_C_S "${CRYPTO_TEST_DIR}/secure/crypto_sec_interface_testsuite.c")
+  list(APPEND ALL_SRC_C_NS "${CRYPTO_TEST_DIR}/non_secure/crypto_ns_interface_testsuite.c")
+
+  #Setting include directories
+  embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+  embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+
+endif()
diff --git a/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c b/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c
new file mode 100644
index 0000000..40d49ad
--- /dev/null
+++ b/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_api.h"
+#include "psa_crypto.h"
+
+#define BIT_SIZE_TEST_KEY (128)
+#define BYTE_SIZE_TEST_KEY (BIT_SIZE_TEST_KEY/8)
+#define BYTE_SIZE_CHUNK (16)
+#define ENC_DEC_BUFFER_SIZE (32)
+#define CIPHER_CFB_DECRYPT_LENGTH (32)
+#define CIPHER_CBC_DECRYPT_LENGTH (16)
+#define CIPHER_CFB_FLUSH_LENGTH (0)
+#define CIPHER_CBC_FLUSH_LENGTH (16)
+
+/* List of tests */
+static void tfm_crypto_test_6001(struct test_result_t *ret);
+static void tfm_crypto_test_6002(struct test_result_t *ret);
+static void tfm_crypto_test_6003(struct test_result_t *ret);
+
+static struct test_t crypto_veneers_tests[] = {
+    {&tfm_crypto_test_6001, "TFM_CRYPTO_TEST_6001",
+     "Non Secure Key management interface", {0} },
+    {&tfm_crypto_test_6002, "TFM_CRYPTO_TEST_6002",
+     "Non Secure Symmetric encryption (AES-128-CBC) interface", {0} },
+    {&tfm_crypto_test_6003, "TFM_CRYPTO_TEST_6003",
+     "Non Secure Symmetric encryption (AES-128-CFB) interface", {0} },
+};
+
+void register_testsuite_ns_crypto_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(crypto_veneers_tests) /
+                 sizeof(crypto_veneers_tests[0]));
+
+    set_testsuite("Crypto non-secure interface test (TFM_CRYPTO_TEST_6XXX)",
+                  crypto_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Non-secure interface test for Crypto
+ *
+ * \details The scope of this set of tests is to functionally verify
+ *          the interfaces specified by psa_crypto.h are working
+ *          as expected. This is not meant to cover all possible
+ *          scenarios and corner cases.
+ *
+ */
+static void tfm_crypto_test_6001(struct test_result_t *ret)
+{
+    psa_status_t status = PSA_SUCCESS;
+    uint32_t i = 0;
+    const psa_key_slot_t slot = 0;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    psa_key_type_t type = PSA_KEY_TYPE_NONE;
+    size_t bits = 0;
+    uint8_t exported_data[sizeof(data)] = {0};
+    size_t exported_data_size = 0;
+
+    status = psa_import_key(slot, PSA_KEY_TYPE_AES, data, sizeof(data));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    status = psa_get_key_information(slot, &type, &bits);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        return;
+    }
+
+    if (bits != BIT_SIZE_TEST_KEY) {
+        TEST_FAIL("The number of key bits is different from expected");
+        return;
+    }
+
+    if (type != PSA_KEY_TYPE_AES) {
+        TEST_FAIL("The type of the key is different from expected");
+        return;
+    }
+
+    status = psa_export_key(slot,
+                            exported_data,
+                            sizeof(data),
+                            &exported_data_size);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error exporting a key");
+        return;
+    }
+
+    if (exported_data_size != BYTE_SIZE_TEST_KEY) {
+        TEST_FAIL("Number of bytes of exported key different from expected");
+        return;
+    }
+
+    /* Check that the exported key is the same as the imported one */
+    for (i=0; i<exported_data_size; i++) {
+        if (exported_data[i] != data[i]) {
+            TEST_FAIL("Exported key doesn't match the imported key");
+            return;
+        }
+    }
+
+    status = psa_destroy_key(slot);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying the key");
+        return;
+    }
+
+    status = psa_get_key_information(slot, &type, &bits);
+    if (status != PSA_ERROR_EMPTY_SLOT) {
+        TEST_FAIL("Key slot should be empty now");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+static uint32_t compare_buffers(const uint8_t *p1,
+                                const uint8_t *p2,
+                                uint32_t s1,
+                                uint32_t s2)
+{
+    uint32_t i = 0, comp_size = (s1 < s2) ? s1 : s2;
+
+    for (i=0; i<comp_size; i++) {
+        if (p1[i] != p2[i]) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void psa_cipher_test(const psa_algorithm_t alg,
+                            struct test_result_t *ret)
+{
+    psa_cipher_operation_t handle, handle_dec;
+    psa_status_t status = PSA_SUCCESS;
+    const psa_key_slot_t slot = 0;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    psa_key_type_t type = PSA_KEY_TYPE_NONE;
+    size_t bits = 0;
+    const size_t iv_length = 16;
+    const uint8_t iv[] = "012345678901234";
+    const uint8_t plain_text[BYTE_SIZE_CHUNK] = "Sixteen bytes!!";
+    uint8_t decrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+    size_t output_length = 0, expected_output_length = 0;
+    uint8_t encrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+    uint32_t comp_result;
+
+    /* Import a key on slot 0 */
+    status = psa_import_key(slot, PSA_KEY_TYPE_AES, data, sizeof(data));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    status = psa_get_key_information(slot, &type, &bits);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        return;
+    }
+
+    if (bits != BIT_SIZE_TEST_KEY) {
+        TEST_FAIL("The number of key bits is different from expected");
+        return;
+    }
+
+    if (type != PSA_KEY_TYPE_AES) {
+        TEST_FAIL("The type of the key is different from expected");
+        return;
+    }
+
+    /* Setup the encryption object */
+    status = psa_encrypt_setup(&handle, slot, alg);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting up cipher operation object");
+        return;
+    }
+
+    /* Set the IV */
+    status = psa_encrypt_set_iv(&handle, iv, iv_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting the IV on the cypher operation object");
+        return;
+    }
+
+    /* Encrypt one chunk of information */
+    status = psa_cipher_update(&handle, plain_text, BYTE_SIZE_CHUNK,
+                               encrypted_data, ENC_DEC_BUFFER_SIZE,
+                               &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error encrypting one chunk of information");
+        return;
+    }
+
+    if (output_length != BYTE_SIZE_CHUNK) {
+        TEST_FAIL("Expected encrypted data length is different from expected");
+        return;
+    }
+
+    /* Finalise the cipher operation */
+    status = psa_cipher_finish(&handle, &encrypted_data[output_length],
+                               ENC_DEC_BUFFER_SIZE - output_length,
+                               &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error finalising the cipher operation");
+        return;
+    }
+
+    if (output_length != 0) {
+        TEST_FAIL("Unexpected output length after finalisation");
+        return;
+    }
+
+    /* Setup the decryption object */
+    if (alg == PSA_ALG_CFB_BASE) {
+        /* In CFB mode the object is always in encryption mode */
+        status = psa_encrypt_setup(&handle_dec, slot, alg);
+    } else {
+        status = psa_decrypt_setup(&handle_dec, slot, alg);
+    }
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting up cipher operation object");
+        return;
+    }
+
+    /* Set the IV for decryption */
+    if (alg == PSA_ALG_CFB_BASE) {
+        /*  In CFB mode the object is in encryption mode, so follow the
+         *  encryption flow.
+         */
+        status = psa_encrypt_set_iv(&handle_dec, iv, iv_length);
+    } else {
+        status = psa_cipher_update(&handle_dec, iv, iv_length,
+                                   encrypted_data, ENC_DEC_BUFFER_SIZE,
+                                   &output_length);
+    }
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting the IV for decryption");
+        return;
+    }
+
+    if (alg != PSA_ALG_CFB_BASE) {
+        if (output_length != 0) {
+            TEST_FAIL("Expected output length is different from expected");
+            return;
+        }
+    }
+
+    /* Decrypt */
+    status = psa_cipher_update(&handle_dec, encrypted_data, ENC_DEC_BUFFER_SIZE,
+                               decrypted_data, BYTE_SIZE_CHUNK, &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error during decryption");
+        return;
+    }
+
+    if (alg == PSA_ALG_CFB_BASE) {
+        expected_output_length = CIPHER_CFB_DECRYPT_LENGTH;
+    } else {
+        expected_output_length = CIPHER_CBC_DECRYPT_LENGTH;
+    }
+
+    if (output_length != expected_output_length) {
+        TEST_FAIL("Decrypted length is different from expected one");
+        return;
+    }
+
+    /* Check that the plain text matches the decrypted data */
+    comp_result = compare_buffers(plain_text, decrypted_data,
+                                  sizeof(plain_text), sizeof(decrypted_data));
+    if (comp_result != 0) {
+        TEST_FAIL("Decrypted data doesn't match with plain text");
+        return;
+    }
+
+    /* Finalise the cipher operation for decryption (destroys decrypted data) */
+    status = psa_cipher_finish(&handle_dec, decrypted_data, BYTE_SIZE_CHUNK,
+                               &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error finalising the cipher operation");
+        return;
+    }
+
+    if (alg == PSA_ALG_CFB_BASE) {
+        expected_output_length = CIPHER_CFB_FLUSH_LENGTH;
+    } else {
+        expected_output_length = CIPHER_CBC_FLUSH_LENGTH;
+    }
+
+    if (output_length != expected_output_length) {
+        TEST_FAIL("After finalising, unexpected decrypted length");
+        return;
+    }
+
+    /* Destroy the key on slot 0 */
+    status = psa_destroy_key(slot);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying a key");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+static void tfm_crypto_test_6002(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_ALG_CBC_BASE, ret);
+}
+
+static void tfm_crypto_test_6003(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_ALG_CFB_BASE, ret);
+}
diff --git a/test/suites/crypto/non_secure/crypto_ns_tests.h b/test/suites/crypto/non_secure/crypto_ns_tests.h
new file mode 100644
index 0000000..aba8c87
--- /dev/null
+++ b/test/suites/crypto/non_secure/crypto_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_NS_TESTS_H__
+#define __CRYPTO_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for Crypto non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_crypto_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_NS_TESTS_H__ */
diff --git a/test/suites/crypto/secure/crypto_s_tests.h b/test/suites/crypto/secure/crypto_s_tests.h
new file mode 100644
index 0000000..4a49844
--- /dev/null
+++ b/test/suites/crypto/secure/crypto_s_tests.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_S_TESTS_H__
+#define __CRYPTO_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+void register_testsuite_s_crypto_interface(struct test_suite_t *p_test_suite);
+
+#endif /* __CRYPTO_S_TESTS_H__ */
diff --git a/test/suites/crypto/secure/crypto_sec_interface_testsuite.c b/test/suites/crypto/secure/crypto_sec_interface_testsuite.c
new file mode 100644
index 0000000..c39d205
--- /dev/null
+++ b/test/suites/crypto/secure/crypto_sec_interface_testsuite.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_crypto_veneers.h"
+
+#define BIT_SIZE_TEST_KEY (128)
+#define BYTE_SIZE_TEST_KEY (BIT_SIZE_TEST_KEY/8)
+
+/* List of tests */
+static void tfm_crypto_test_5001(struct test_result_t *ret);
+
+static struct test_t crypto_tests[] = {
+    {&tfm_crypto_test_5001, "TFM_CRYPTO_TEST_5001",
+     "Secure Key management interface", {0}},
+};
+
+void register_testsuite_s_crypto_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(crypto_tests) / sizeof(crypto_tests[0]));
+
+    set_testsuite("Crypto secure interface tests (TFM_CRYPTO_TEST_5XXX)",
+                  crypto_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Secure interface test for Crypto
+ *
+ * \details The scope of this test is to perform a basic validation of
+ *          the secure interface for the Key management module.
+ */
+static void tfm_crypto_test_5001(struct test_result_t *ret)
+{
+    enum tfm_crypto_err_t err;
+    uint32_t i = 0;
+    const psa_key_slot_t slot = 0;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    psa_key_type_t type = PSA_KEY_TYPE_NONE;
+    size_t bits = 0;
+    uint8_t exported_data[sizeof(data)] = {0};
+    size_t exported_data_size = 0;
+
+    err = tfm_crypto_veneer_import_key(slot,
+                                       PSA_KEY_TYPE_AES,
+                                       data,
+                                       sizeof(data));
+
+    if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    err = tfm_crypto_veneer_get_key_information(slot, &type, &bits);
+    if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        return;
+    }
+
+    if (bits != BIT_SIZE_TEST_KEY) {
+        TEST_FAIL("The number of key bits is different from expected");
+        return;
+    }
+
+    if (type != PSA_KEY_TYPE_AES) {
+        TEST_FAIL("The type of the key is different from expected");
+        return;
+    }
+
+    err = tfm_crypto_veneer_export_key(slot,
+                                       exported_data,
+                                       sizeof(data),
+                                       &exported_data_size);
+
+    if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
+        TEST_FAIL("Error exporting a key");
+        return;
+    }
+
+    if (exported_data_size != BYTE_SIZE_TEST_KEY) {
+        TEST_FAIL("Number of bytes of exported key different from expected");
+        return;
+    }
+
+    /* Check that the exported key is the same as the imported one */
+    for (i=0; i<exported_data_size; i++) {
+        if (exported_data[i] != data[i]) {
+            TEST_FAIL("Exported key doesn't match the imported key");
+            return;
+        }
+    }
+
+    err = tfm_crypto_veneer_destroy_key(slot);
+    if (err != TFM_CRYPTO_ERR_PSA_SUCCESS) {
+        TEST_FAIL("Error destroying the key");
+        return;
+    }
+
+    err = tfm_crypto_veneer_get_key_information(slot, &type, &bits);
+    if (err != TFM_CRYPTO_ERR_PSA_ERROR_EMPTY_SLOT) {
+        TEST_FAIL("Key slot should be empty now");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}