881 changes: 881 additions & 0 deletions lib/tpm2_error.c

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions lib/tpm2_error.h
@@ -0,0 +1,119 @@
//**********************************************************************;
// Copyright (c) 2018, Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//**********************************************************************;

#ifndef LIB_TPM2_ERROR_H_
#define LIB_TPM2_ERROR_H_

#include <stdbool.h>

#include <sapi/tpm20.h>

/**
* Number of error layers
*/
#define TPM2_ERROR_TSS2_RC_LAYER_COUNT (TSS2_RC_LAYER_MASK >> TSS2_RC_LAYER_SHIFT)

/**
* A custom error handler prototype.
* @param rc
* The rc to decode with only the error bits set, ie no need to mask the
* layer bits out. Handlers will never be invoked with the error bits set
* to 0, as zero always indicates success.
* @return
* An error string describing the rc. If the handler cannot determine
* a valid response, it can return NULL indicating that the framework
* should just print the raw hexidecimal value of the error field of
* a tpm2_err_layer_rc.
* Note that this WILL NOT BE FREED by the caller,
* i.e. static.
*/
typedef const char *(*tpm2_error_handler)(TSS2_RC rc);

/**
* Register or unregister a custom layer error handler.
* @param layer
* The layer in which to register a handler for. It is an error
* to register for the following reserved layers:
* - TSS2_TPM_RC_LAYER - layer 0
* - TSS2_SYS_RC_LAYER - layer 8
* - TSS2_MU_RC_LAYER - layer 9
* - TSS2_TCTI_RC_LAYER - layer 10
* @param name
* A friendly layer name. It is an error for the name to be of
* length 0 or greater than 4.
* @param handler
* The handler function to register or NULL to unregister.
* @return
* True on success or False on error.
*/
bool tpm2_error_set_handler(UINT8 layer, const char *name,
tpm2_error_handler handler);

/**
* Given a TSS2_RC return code, provides a static error string in the format:
* <layer-name>:<layer-specific-msg>.
*
* The layer-name section will either be the friendly name, or if no layer
* handler is registered, the base10 layer number.
*
* The "layer-specific-msg" is layer specific and will contain details on the
* error that occurred or the error code if it couldn't look it up.
*
* Known layer specific substrings:
* TPM - The tpm layer produces 2 distinct format codes that allign with:
* - Section 6.6 of: https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
* - Section 39.4 of: https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf
*
* The two formats are format 0 and format 1.
* Format 0 string format:
* - "<error|warn>(<version>): <description>
* - Examples:
* - error(1.2): bad tag
* - warn(2.0): the 1st handle in the handle area references a transient object or session that is not loaded
*
* Format 1 string format:
* - <handle|session|parameter>(<index>):<description>
* - Examples:
* - handle(unk):value is out of range or is not correct for the context
* - tpm:handle(5):value is out of range or is not correct for the context
*
* Note that passing TPM2_RC_SUCCESS results in the layer specific message of "success".
*
* The System, TCTI and Marshaling (MU) layers, all define simple string
* returns analogous to strerror(3).
*
* Unknown layers will have the layer number in decimal and then a layer specific string of
* a hex value representing the error code. For example: 9:0x3
*
* @param rc
* The error code to decode.
* @return
* A human understandable error description string.
*/
const char *tpm2_error_str(TSS2_RC rc);

#endif /* LIB_TPM2_ERROR_H_ */
286 changes: 286 additions & 0 deletions test/unit/test_tpm2_error.c
@@ -0,0 +1,286 @@
//**********************************************************************;
// Copyright (c) 2018, Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//**********************************************************************;

#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>

#include <setjmp.h>
#include <cmocka.h>

#include <sapi/tpm20.h>

#include "tpm2_error.h"

#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0]))

#define assert_string_prefix(str, prefix) \
assert_memory_equal(str, prefix, strlen(prefix))

static void test_layers(void **state) {
(void) state;

static const char *known_layers[TPM2_ERROR_TSS2_RC_LAYER_COUNT] = {
"tpm:",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"fapi:",
"sys:",
"mu:",
"tcti:",
"rmt",
"rm",
"drvr",
};

UINT8 layer;
for (layer = 0; layer < TPM2_ERROR_TSS2_RC_LAYER_COUNT; layer++) {
TSS2_RC rc = TSS2_RC_LAYER(layer);

const char *got = tpm2_error_str(rc);

char buf[256];
snprintf(buf, sizeof(buf), "%u:", layer);

const char *expected = known_layers[layer] ? known_layers[layer] : buf;
assert_string_prefix(got, expected);
}
}

static void test_tpm_format_0_version2_0_error(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_SEQUENCE);
assert_string_equal(m, "tpm:error(2.0): improper use of a sequence"
" handle");
}

static void test_tpm_format_0_version2_0_warn(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_REFERENCE_H0);
assert_string_equal(m,
"tpm:warn(2.0): the 1st handle in the handle area references a"
" transient object or session that is not loaded");
}

static void test_tpm2_format_0_unkown(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_NOT_USED + 0x80);
assert_string_equal(m, "tpm:parameter(1):unknown error num: 0x3F");
}

static void test_tpm_format_1_unk_handle(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_HASH);
assert_string_equal(m,
"tpm:handle(unk):hash algorithm not supported or not appropriate");
}

static void test_tpm_format_1_unk_parameter(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_HASH + TPM2_RC_P);
assert_string_equal(m,
"tpm:parameter(unk):hash algorithm not supported or not appropriate");
}

static void test_tpm_format_1_unk_session(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_HASH + TPM2_RC_S);
assert_string_equal(m,
"tpm:session(unk):hash algorithm not supported or not appropriate");
}

static void test_tpm_format_1_5_handle(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_HASH + TPM2_RC_5);
assert_string_equal(m,
"tpm:handle(5):hash algorithm not supported or not appropriate");
}

static void test_tpm2_format_1_unkown(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_NOT_USED + 0x80);
assert_string_equal(m, "tpm:parameter(1):unknown error num: 0x3F");
}

static void test_tpm2_format_1_success(void **state) {
(void) state;

const char *m = tpm2_error_str(TPM2_RC_SUCCESS);
assert_string_equal(m, "tpm:success");
}

static const char *
custom_err_handler(TSS2_RC rc) {

static const char *err_map[] = { "error 1", "error 2", "error 3" };

if (rc - 1u >= ARRAY_LEN(err_map)) {
return NULL;
}

return err_map[rc - 1];
}

static void test_custom_handler(void **state) {
(void) state;

/*
* Test registering a custom handler
*/
bool res = tpm2_error_set_handler(1, "cstm", custom_err_handler);
assert_true(res);

/*
* Test getting error strings
*/
unsigned i;
for (i = 1; i < 4; i++) {
// Make a layer 1 error with an error number of i.
TSS2_RC rc = TSS2_RC_LAYER(1) | i;
char buf[256];
snprintf(buf, sizeof(buf), "cstm:error %u", i);

const char *e = tpm2_error_str(rc);
assert_string_equal(e, buf);
}

TSS2_RC rc = TSS2_RC_LAYER(1) | 42;

/*
* Test an unknown error
*/
const char *e = tpm2_error_str(rc);
assert_string_equal(e, "cstm:0x2A");

/*
* Test clearing a handler
*/
res = tpm2_error_set_handler(1, "cstm", NULL);
assert_true(res);

/*
* Test an unknown layer
*/
e = tpm2_error_str(rc);
assert_string_equal(e, "1:0x2A");
}

static void test_zero_length_name(void **state) {
(void) state;

bool res = tpm2_error_set_handler(TSS2_TPM_RC_LAYER, "",
custom_err_handler);
assert_false(res);
}

static void test_over_length_name(void **state) {
(void) state;

bool res = tpm2_error_set_handler(1, "way to long", custom_err_handler);
assert_false(res);
}

static void test_reserved_handler(void **state) {
(void) state;

bool res = tpm2_error_set_handler(TSS2_TPM_RC_LAYER, "nope",
custom_err_handler);
assert_false(res);
}

static void test_null_name(void **state) {
(void) state;

bool res = tpm2_error_set_handler(TSS2_TPM_RC_LAYER,
NULL, custom_err_handler);
assert_false(res);
}

static void test_sys(void **state) {
(void) state;

const char *e = tpm2_error_str(TSS2_SYS_RC_ABI_MISMATCH);
assert_string_equal(e,
"sys:Passed in ABI version doesn't match called module's ABI version");
}

static void test_mu(void **state) {
(void) state;

const char *e = tpm2_error_str(TSS2_MU_RC_BAD_REFERENCE);
assert_string_equal(e,
"mu:A pointer is NULL that isn't allowed to be NULL.");

}

static void test_tcti(void **state) {
(void) state;

const char *e = tpm2_error_str(TSS2_TCTI_RC_NO_CONNECTION);
assert_string_equal(e, "tcti:Fails to connect to next lower layer");
}

int main(int argc, char* argv[]) {
(void) argc;
(void) argv;

const struct CMUnitTest tests[] = {
/* Layer tests */
cmocka_unit_test(test_layers),
cmocka_unit_test(test_tpm_format_0_version2_0_error),
cmocka_unit_test(test_tpm_format_0_version2_0_warn),
cmocka_unit_test(test_tpm2_format_0_unkown),
cmocka_unit_test(test_tpm_format_1_unk_handle),
cmocka_unit_test(test_tpm_format_1_unk_parameter),
cmocka_unit_test(test_tpm_format_1_unk_session),
cmocka_unit_test(test_tpm_format_1_5_handle),
cmocka_unit_test(test_tpm2_format_1_unkown),
cmocka_unit_test(test_tpm2_format_1_success),
cmocka_unit_test(test_custom_handler),
cmocka_unit_test(test_zero_length_name),
cmocka_unit_test(test_over_length_name),
cmocka_unit_test(test_reserved_handler),
cmocka_unit_test(test_null_name),
cmocka_unit_test(test_sys),
cmocka_unit_test(test_mu),
cmocka_unit_test(test_tcti),
};

return cmocka_run_group_tests(tests, NULL, NULL);
}