Showing with 650 additions and 40 deletions.
  1. +7 −0 Makefile.am
  2. +201 −0 lib/conversion.c
  3. +85 −0 lib/conversion.h
  4. +54 −0 lib/tpm2_alg_util.c
  5. +14 −0 lib/tpm2_alg_util.h
  6. +6 −0 man/common/pubkey.md
  7. +10 −0 man/common/signature.md
  8. +8 −1 man/tpm2_quote.1.md
  9. +3 −1 man/tpm2_readpublic.1.md
  10. +2 −4 man/tpm2_sign.1.md
  11. +1 −1 test/system/test.sh
  12. +152 −0 test/system/test_output_formats.sh
  13. +76 −22 tools/tpm2_quote.c
  14. +17 −5 tools/tpm2_readpublic.c
  15. +14 −6 tools/tpm2_sign.c
7 changes: 7 additions & 0 deletions Makefile.am
Expand Up @@ -103,6 +103,7 @@ endif
noinst_LIBRARIES = $(LIB_COMMON)
lib_libcommon_a_SOURCES = \
$(tcti_src) \
lib/conversion.c \
lib/files.c \
lib/files.h \
lib/log.c \
Expand Down Expand Up @@ -282,7 +283,9 @@ MARKDOWN_COMMON_DEPS = \
man/common/object-alg.md \
man/common/options.md \
man/common/password.md \
man/common/pubkey.md \
man/common/sign-alg.md \
man/common/signature.md \
man/common/tcti.md

man/man1/%.1 : man/%.1.md $(MARKDOWN_COMMON_DEPS)
Expand All @@ -306,6 +309,10 @@ man/man1/%.1 : man/%.1.md $(MARKDOWN_COMMON_DEPS)
-e '/\[nv attributes\]/d' \
-e '/\[pcr bank specifiers\]/r man/common/pcr.md' \
-e '/\[pcr bank specifiers\]/d' \
-e '/\[pubkey options\]/r man/common/pubkey.md' \
-e '/\[pubkey options\]/d' \
-e '/\[signature options\]/r man/common/signature.md' \
-e '/\[signature options\]/d' \
< $< | pandoc -s -t man > $@

CLEANFILES = $(man1_MANS)
201 changes: 201 additions & 0 deletions lib/conversion.c
@@ -0,0 +1,201 @@
//**********************************************************************;
// Copyright (c) 2017, SUSE Linux GmbH
// 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.
//
// 3. Neither the name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// 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 <string.h>
#include <errno.h>

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bn.h>
#include <openssl/err.h>

#include "conversion.h"
#include "files.h"
#include "log.h"
#include "tpm2_alg_util.h"
#include "tpm2_util.h"

static bool tpm2_convert_pubkey_ssl(TPMT_PUBLIC *public, pubkey_format format, const char *path);

pubkey_format tpm2_parse_pubkey_format(const char *label) {
if (strcasecmp(label, "der") == 0) {
return pubkey_format_der;
}
else if (strcasecmp(label, "pem") == 0) {
return pubkey_format_pem;
}
else if (strcasecmp(label, "tss") == 0) {
return pubkey_format_tss;
}

LOG_ERR("Invalid public key output format '%s' specified", label);

return pubkey_format_err;
}

signature_format tpm2_parse_signature_format(const char *label) {
if (strcasecmp(label, "tss") == 0) {
return signature_format_tss;
}
else if (strcasecmp(label, "plain") == 0) {
return signature_format_plain;
}

LOG_ERR("Invalid signature output format '%s' specified", label);

return signature_format_err;
}

static void print_ssl_error(const char *failed_action) {
char errstr[256] = {0};
unsigned long errnum = ERR_get_error();

ERR_error_string_n(errnum, errstr, sizeof(errstr));
LOG_ERR("%s: %s", failed_action, errstr);
}

bool tpm2_convert_pubkey(TPM2B_PUBLIC *public, pubkey_format format, const char *path) {

if (format == pubkey_format_der || format == pubkey_format_pem) {
return tpm2_convert_pubkey_ssl(&public->t.publicArea, format, path);
}
else if (format == pubkey_format_tss) {
// save raw as is
return files_save_bytes_to_file(path, (UINT8 *)public, sizeof(*public));
}

LOG_ERR("Unsupported public key output format.");
return false;
}

static bool tpm2_convert_pubkey_ssl(TPMT_PUBLIC *public, pubkey_format format, const char *path) {
bool ret = false;
FILE *fp = NULL;
RSA *ssl_rsa_key = NULL;

// need this before the first SSL call for getting human readable error
// strings in print_ssl_error()
ERR_load_crypto_strings();

if (public->type != TPM_ALG_RSA) {
LOG_ERR("Unsupported key type for requested output format. Only RSA is supported.");
goto error;
}

UINT32 exponent = (public->parameters).rsaDetail.exponent;
if (exponent == 0) {
exponent = RSA_DEFAULT_PUBLIC_EXPONENT;
}

// OpenSSL expects this in network byte order
exponent = tpm2_util_hton_32(exponent);
ssl_rsa_key = RSA_new();
if (!ssl_rsa_key) {
print_ssl_error("Failed to allocate OpenSSL RSA structure");
goto error;
}

ssl_rsa_key->e = BN_bin2bn((void*)&exponent, sizeof(exponent), NULL);
ssl_rsa_key->n = BN_bin2bn(
(public->unique).rsa.t.buffer,
(public->unique).rsa.t.size,
NULL);

if (!ssl_rsa_key->n || !ssl_rsa_key->e) {
print_ssl_error("Failed to convert data to SSL internal format");
goto error;
}

fp = fopen(path, "wb");
if (!fp) {
LOG_ERR("Failed to open public key output file '%s': %s",
path, strerror(errno));
goto error;
}

int ssl_res = 0;

switch(format) {
case pubkey_format_pem:
ssl_res = PEM_write_RSA_PUBKEY(fp, ssl_rsa_key);
break;
case pubkey_format_der:
ssl_res = i2d_RSA_PUBKEY_fp(fp, ssl_rsa_key);
break;
default:
LOG_ERR("Invalid OpenSSL target format %d encountered", format);
goto error;
}

if (ssl_res <= 0) {
print_ssl_error("OpenSSL public key conversion failed");
goto error;
}

ret = true;

error:
if (fp) {
fclose(fp);
}
if (ssl_rsa_key) {
RSA_free(ssl_rsa_key);
}
ERR_free_strings();

return ret;
}

bool tpm2_convert_signature(TPMT_SIGNATURE *signature, signature_format format, const char *path) {

switch(format) {
case signature_format_tss:
/* TODO fix serialization */
return files_save_bytes_to_file(path, (UINT8 *)signature,
sizeof(*signature));
case signature_format_plain: {
UINT8 *buffer;
UINT16 size;

buffer = tpm2_extract_plain_signature(&size, signature);
if (buffer == NULL) {
return false;
}

bool ret = files_save_bytes_to_file(path, buffer, size);
free(buffer);
return ret;
}
default:
LOG_ERR("Unsupported signature output format.");
return false;
}
}
85 changes: 85 additions & 0 deletions lib/conversion.h
@@ -0,0 +1,85 @@
//**********************************************************************;
// Copyright (c) 2017, SUSE GmbH
// 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 CONVERSION_H
#define CONVERSION_H

#include <stdbool.h>

#include <sapi/tpm20.h>

typedef enum pubkey_format pubkey_format;

enum pubkey_format {
pubkey_format_tss, pubkey_format_pem, pubkey_format_der, pubkey_format_err
};

typedef enum signature_format signature_format;

enum signature_format {
signature_format_tss, signature_format_plain, signature_format_err
};

/**
* Parses the given command line public key format option string and returns
* the corresponding pubkey_format enum value.
*
* LOG_ERR is used to communicate errors.
*
* @return
* On error pubkey_format_err is returned.
*/
pubkey_format tpm2_parse_pubkey_format(const char *label);

/**
* Converts the given public key structure into the requested target format
* and writes the result to the given file system path.
*
* LOG_ERR is used to communicate errors.
*/
bool tpm2_convert_pubkey(TPM2B_PUBLIC *public, pubkey_format format, const char *path);

/**
* Parses the given command line signature format option string and returns
* the corresponding signature_format enum value.
*
* LOG_ERR is used to communicate errors.
*
* @return
* On error signature_format_err is returned.
*/
signature_format tpm2_parse_signature_format(const char *label);

/**
* Converts the given signature data into the requested target format and
* writes the result to the given file system path.
*
* LOG_ERR is used to communicate errors.
*/
bool tpm2_convert_signature(TPMT_SIGNATURE *signature, signature_format format, const char *path);

#endif /* CONVERSION_H */
54 changes: 54 additions & 0 deletions lib/tpm2_alg_util.c
Expand Up @@ -329,3 +329,57 @@ bool pcr_parse_digest_list(char **argv, int len,

return true;
}

UINT8* tpm2_extract_plain_signature(UINT16 *size, TPMT_SIGNATURE *signature) {

UINT8 *buffer = NULL;
*size = 0;

switch (signature->sigAlg) {
case TPM_ALG_RSASSA:
*size = sizeof(signature->signature.rsassa.sig.t.buffer);
buffer = malloc(*size);
if (!buffer) {
goto nomem;
}
memcpy(buffer, signature->signature.rsassa.sig.t.buffer, *size);
break;
case TPM_ALG_HMAC: {
TPMU_HA *hmac_sig = &(signature->signature.hmac.digest);
*size = tpm2_alg_util_get_hash_size(signature->signature.hmac.hashAlg);
buffer = malloc(*size);
if (!buffer) {
goto nomem;
}
memcpy(buffer, &hmac_sig->na, *size);
break;
}
case TPM_ALG_ECDSA: {
const size_t ECC_PAR_LEN = sizeof(TPM2B_ECC_PARAMETER);
*size = ECC_PAR_LEN * 2;
buffer = malloc(*size);
if (!buffer) {
goto nomem;
}
memcpy(buffer,
(UINT8*)&(signature->signature.ecdsa.signatureR),
ECC_PAR_LEN
);
memcpy(buffer + ECC_PAR_LEN,
(UINT8*)&(signature->signature.ecdsa.signatureS),
ECC_PAR_LEN
);
break;
}
default:
LOG_ERR("%s: unknown signature scheme: 0x%x", __func__,
signature->sigAlg);
return NULL;
}

return buffer;
nomem:
LOG_ERR("%s: couldn't allocate memory", __func__);
return NULL;
}

14 changes: 14 additions & 0 deletions lib/tpm2_alg_util.h
Expand Up @@ -160,4 +160,18 @@ bool pcr_parse_digest_list(char **argv, int len,
*/
UINT16 tpm2_alg_util_get_hash_size(TPMI_ALG_HASH id);

/**
* Extracts the plain signature data without any headers
*
* Communicates errors via LOG_ERR.
*
* @param size
* Will receive the number of bytes stored in buffer.
* @signature The actual signature struct to extract the plain signature from.
* @return
* Returns a buffer filled with the extracted signature or NULL on error.
* Needs to be free()'d by the caller.
*/
UINT8* tpm2_extract_plain_signature(UINT16 *size, TPMT_SIGNATURE *signature);

#endif /* LIB_TPM2_ALG_UTIL_H_ */