Message ID | 1366628805-29964-2-git-send-email-eibach@gdsys.de |
---|---|
State | Superseded |
Delegated to: | Tom Rini |
Headers | show |
Acked-by: Che-Liang Chiou <clchiou@chromium.org> On Mon, Apr 22, 2013 at 4:06 AM, Dirk Eibach <eibach@gdsys.de> wrote: > From: Reinhard Pfau <pfau@gdsys.de> > > Extend the tpm library with support for single authorized (AUTH1) commands > as specified in the TCG Main Specification 1.2. (The internally used helper > functions are implemented in a way that they could also be used for double > authorized commands if someone needs it.) > > Provide enums with the return codes from the TCG Main specification. > > For now only a single OIAP session is supported. > > OIAP authorized version of the commands TPM_LoadKey2 and TPM_GetPubKey are > provided. Both features are available using the 'tpm' command, too. > > Authorized commands are enabled with CONFIG_TPM_AUTH_SESSIONS. (Note that > this also requires CONFIG_SHA1 to be enabled.) > > Signed-off-by: Reinhard Pfau <pfau@gdsys.de> > > Signed-off-by: Dirk Eibach <eibach@gdsys.de> > --- > README | 14 +++ > common/cmd_tpm.c | 93 +++++++++++++++ > include/tpm.h | 174 +++++++++++++++++++++++++++ > lib/tpm.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++++++++- > 4 files changed, 628 insertions(+), 1 deletion(-) > > diff --git a/README b/README > index 0bc0af5..58b2ee5 100644 > --- a/README > +++ b/README > @@ -1210,6 +1210,20 @@ The following options need to be configured: > to. Contemporary x86 systems usually map it at > 0xfed40000. > > + CONFIG_CMD_TPM > + Add tpm monitor functions. > + Requires CONFIG_TPM. If CONFIG_TPM_AUTH_SESSIONS is set, also > + provides monitor access to authorized functions. > + > + CONFIG_TPM > + Define this to enable the TPM support library which provides > + functional interfaces to some TPM commands. > + Requires support for a TPM device. > + > + CONFIG_TPM_AUTH_SESSIONS > + Define this to enable authorized functions in the TPM library. > + Requires CONFIG_TPM and CONFIG_SHA1. > + > - USB Support: > At the moment only the UHCI host controller is > supported (PIP405, MIP405, MPC5200); define > diff --git a/common/cmd_tpm.c b/common/cmd_tpm.c > index 46fae18..c8767a4 100644 > --- a/common/cmd_tpm.c > +++ b/common/cmd_tpm.c > @@ -546,6 +546,72 @@ static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag, > return convert_return_code(err); > } > > +#ifdef CONFIG_TPM_AUTH_SESSIONS > + > +static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) > +{ > + uint32_t auth_handle, err; > + > + err = tpm_oiap(&auth_handle); > + > + return convert_return_code(err); > +} > + > +static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) > +{ > + uint32_t parent_handle, key_len, key_handle, err; > + uint8_t usage_auth[20]; > + void *key; > + > + if (argc < 5) > + return CMD_RET_USAGE; > + > + parent_handle = simple_strtoul(argv[1], NULL, 0); > + key = (void *)simple_strtoul(argv[2], NULL, 0); > + key_len = simple_strtoul(argv[3], NULL, 0); > + if (strlen(argv[4]) != 40) > + return CMD_RET_FAILURE; > + parse_byte_string(argv[4], usage_auth, NULL); > + > + err = tpm_load_key2_oiap(parent_handle, key, key_len, usage_auth, > + &key_handle); > + if (!err) > + printf("Key handle is 0x%x\n", key_handle); > + > + return convert_return_code(err); > +} > + > +static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) > +{ > + uint32_t key_handle, err; > + uint8_t usage_auth[20]; > + uint8_t pub_key_buffer[288]; > + size_t pub_key_len = sizeof(pub_key_buffer); > + > + if (argc < 3) > + return CMD_RET_USAGE; > + > + key_handle = simple_strtoul(argv[1], NULL, 0); > + if (strlen(argv[2]) != 40) > + return CMD_RET_FAILURE; > + parse_byte_string(argv[2], usage_auth, NULL); > + > + err = tpm_get_pub_key_oiap(key_handle, usage_auth, > + pub_key_buffer, &pub_key_len); > + if (!err) { > + printf("dump of received pub key structure:\n"); > + print_byte_string(pub_key_buffer, pub_key_len); > + } > + return convert_return_code(err); > +} > + > +TPM_COMMAND_NO_ARG(tpm_end_oiap) > + > +#endif /* CONFIG_TPM_AUTH_SESSIONS */ > + > #define MAKE_TPM_CMD_ENTRY(cmd) \ > U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "") > > @@ -590,6 +656,16 @@ static cmd_tbl_t tpm_commands[] = { > do_tpm_nv_read, "", ""), > U_BOOT_CMD_MKENT(nv_write, 0, 1, > do_tpm_nv_write, "", ""), > +#ifdef CONFIG_TPM_AUTH_SESSIONS > + U_BOOT_CMD_MKENT(oiap, 0, 1, > + do_tpm_oiap, "", ""), > + U_BOOT_CMD_MKENT(end_oiap, 0, 1, > + do_tpm_end_oiap, "", ""), > + U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1, > + do_tpm_load_key2_oiap, "", ""), > + U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1, > + do_tpm_get_pub_key_oiap, "", ""), > +#endif /* CONFIG_TPM_AUTH_SESSIONS */ > }; > > static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) > @@ -638,6 +714,16 @@ U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, > " get_capability cap_area sub_cap addr count\n" > " - Read <count> bytes of TPM capability indexed by <cap_area> and\n" > " <sub_cap> to memory address <addr>.\n" > +#ifdef CONFIG_TPM_AUTH_SESSIONS > +"Storage functions\n" > +" loadkey2_oiap parent_handle key_addr key_len usage_auth\n" > +" - loads a key data from memory address <key_addr>, <key_len> bytes\n" > +" into TPM using the parent key <parent_handle> with authorization\n" > +" <usage_auth> (20 bytes hex string).\n" > +" get_pub_key_oiap key_handle usage_auth\n" > +" - get the public key portion of a loaded key <key_handle> using\n" > +" authorization <usage auth> (20 bytes hex string)\n" > +#endif /* CONFIG_TPM_AUTH_SESSIONS */ > "Endorsement Key Handling Commands:\n" > " read_pubek addr count\n" > " - Read <count> bytes of the public endorsement key to memory\n" > @@ -648,6 +734,13 @@ U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, > " <digest_hex_string>\n" > " pcr_read index addr count\n" > " - Read <count> bytes from PCR <index> to memory address <addr>.\n" > +#ifdef CONFIG_TPM_AUTH_SESSIONS > +"Authorization Sessions\n" > +" oiap\n" > +" - setup an OIAP session\n" > +" end_oiap\n" > +" - terminates an active OIAP session\n" > +#endif /* CONFIG_TPM_AUTH_SESSIONS */ > "Non-volatile Storage Commands:\n" > " nv_define_space index permission size\n" > " - Establish a space at index <index> with <permission> of <size> bytes.\n" > diff --git a/include/tpm.h b/include/tpm.h > index 7219b73..5e9f832 100644 > --- a/include/tpm.h > +++ b/include/tpm.h > @@ -1,5 +1,6 @@ > /* > * Copyright (c) 2013 The Chromium OS Authors. > + * Coypright (c) 2013 Guntermann & Drunck GmbH > * > * See file CREDITS for list of people who contributed to this > * project. > @@ -54,6 +55,120 @@ enum tpm_nv_index { > }; > > /** > + * TPM return codes as defined in the TCG Main specification > + * (TPM Main Part 2 Structures; Specification version 1.2) > + */ > +enum tpm_return_code { > + TPM_BASE = 0x00000000, > + TPM_NON_FATAL = 0x00000800, > + TPM_SUCCESS = TPM_BASE, > + /* TPM-defined fatal error codes */ > + TPM_AUTHFAIL = TPM_BASE + 1, > + TPM_BADINDEX = TPM_BASE + 2, > + TPM_BAD_PARAMETER = TPM_BASE + 3, > + TPM_AUDITFAILURE = TPM_BASE + 4, > + TPM_CLEAR_DISABLED = TPM_BASE + 5, > + TPM_DEACTIVATED = TPM_BASE + 6, > + TPM_DISABLED = TPM_BASE + 7, > + TPM_DISABLED_CMD = TPM_BASE + 8, > + TPM_FAIL = TPM_BASE + 9, > + TPM_BAD_ORDINAL = TPM_BASE + 10, > + TPM_INSTALL_DISABLED = TPM_BASE + 11, > + TPM_INVALID_KEYHANDLE = TPM_BASE + 12, > + TPM_KEYNOTFOUND = TPM_BASE + 13, > + TPM_INAPPROPRIATE_ENC = TPM_BASE + 14, > + TPM_MIGRATE_FAIL = TPM_BASE + 15, > + TPM_INVALID_PCR_INFO = TPM_BASE + 16, > + TPM_NOSPACE = TPM_BASE + 17, > + TPM_NOSRK = TPM_BASE + 18, > + TPM_NOTSEALED_BLOB = TPM_BASE + 19, > + TPM_OWNER_SET = TPM_BASE + 20, > + TPM_RESOURCES = TPM_BASE + 21, > + TPM_SHORTRANDOM = TPM_BASE + 22, > + TPM_SIZE = TPM_BASE + 23, > + TPM_WRONGPCRVAL = TPM_BASE + 24, > + TPM_BAD_PARAM_SIZE = TPM_BASE + 25, > + TPM_SHA_THREAD = TPM_BASE + 26, > + TPM_SHA_ERROR = TPM_BASE + 27, > + TPM_FAILEDSELFTEST = TPM_BASE + 28, > + TPM_AUTH2FAIL = TPM_BASE + 29, > + TPM_BADTAG = TPM_BASE + 30, > + TPM_IOERROR = TPM_BASE + 31, > + TPM_ENCRYPT_ERROR = TPM_BASE + 32, > + TPM_DECRYPT_ERROR = TPM_BASE + 33, > + TPM_INVALID_AUTHHANDLE = TPM_BASE + 34, > + TPM_NO_ENDORSEMENT = TPM_BASE + 35, > + TPM_INVALID_KEYUSAGE = TPM_BASE + 36, > + TPM_WRONG_ENTITYTYPE = TPM_BASE + 37, > + TPM_INVALID_POSTINIT = TPM_BASE + 38, > + TPM_INAPPROPRIATE_SIG = TPM_BASE + 39, > + TPM_BAD_KEY_PROPERTY = TPM_BASE + 40, > + TPM_BAD_MIGRATION = TPM_BASE + 41, > + TPM_BAD_SCHEME = TPM_BASE + 42, > + TPM_BAD_DATASIZE = TPM_BASE + 43, > + TPM_BAD_MODE = TPM_BASE + 44, > + TPM_BAD_PRESENCE = TPM_BASE + 45, > + TPM_BAD_VERSION = TPM_BASE + 46, > + TPM_NO_WRAP_TRANSPORT = TPM_BASE + 47, > + TPM_AUDITFAIL_UNSUCCESSFUL = TPM_BASE + 48, > + TPM_AUDITFAIL_SUCCESSFUL = TPM_BASE + 49, > + TPM_NOTRESETABLE = TPM_BASE + 50, > + TPM_NOTLOCAL = TPM_BASE + 51, > + TPM_BAD_TYPE = TPM_BASE + 52, > + TPM_INVALID_RESOURCE = TPM_BASE + 53, > + TPM_NOTFIPS = TPM_BASE + 54, > + TPM_INVALID_FAMILY = TPM_BASE + 55, > + TPM_NO_NV_PERMISSION = TPM_BASE + 56, > + TPM_REQUIRES_SIGN = TPM_BASE + 57, > + TPM_KEY_NOTSUPPORTED = TPM_BASE + 58, > + TPM_AUTH_CONFLICT = TPM_BASE + 59, > + TPM_AREA_LOCKED = TPM_BASE + 60, > + TPM_BAD_LOCALITY = TPM_BASE + 61, > + TPM_READ_ONLY = TPM_BASE + 62, > + TPM_PER_NOWRITE = TPM_BASE + 63, > + TPM_FAMILY_COUNT = TPM_BASE + 64, > + TPM_WRITE_LOCKED = TPM_BASE + 65, > + TPM_BAD_ATTRIBUTES = TPM_BASE + 66, > + TPM_INVALID_STRUCTURE = TPM_BASE + 67, > + TPM_KEY_OWNER_CONTROL = TPM_BASE + 68, > + TPM_BAD_COUNTER = TPM_BASE + 69, > + TPM_NOT_FULLWRITE = TPM_BASE + 70, > + TPM_CONTEXT_GAP = TPM_BASE + 71, > + TPM_MAXNVWRITES = TPM_BASE + 72, > + TPM_NOOPERATOR = TPM_BASE + 73, > + TPM_RESOURCEMISSING = TPM_BASE + 74, > + TPM_DELEGATE_LOCK = TPM_BASE + 75, > + TPM_DELEGATE_FAMILY = TPM_BASE + 76, > + TPM_DELEGATE_ADMIN = TPM_BASE + 77, > + TPM_TRANSPORT_NOTEXCLUSIVE = TPM_BASE + 78, > + TPM_OWNER_CONTROL = TPM_BASE + 79, > + TPM_DAA_RESOURCES = TPM_BASE + 80, > + TPM_DAA_INPUT_DATA0 = TPM_BASE + 81, > + TPM_DAA_INPUT_DATA1 = TPM_BASE + 82, > + TPM_DAA_ISSUER_SETTINGS = TPM_BASE + 83, > + TPM_DAA_TPM_SETTINGS = TPM_BASE + 84, > + TPM_DAA_STAGE = TPM_BASE + 85, > + TPM_DAA_ISSUER_VALIDITY = TPM_BASE + 86, > + TPM_DAA_WRONG_W = TPM_BASE + 87, > + TPM_BAD_HANDLE = TPM_BASE + 88, > + TPM_BAD_DELEGATE = TPM_BASE + 89, > + TPM_BADCONTEXT = TPM_BASE + 90, > + TPM_TOOMANYCONTEXTS = TPM_BASE + 91, > + TPM_MA_TICKET_SIGNATURE = TPM_BASE + 92, > + TPM_MA_DESTINATION = TPM_BASE + 93, > + TPM_MA_SOURCE = TPM_BASE + 94, > + TPM_MA_AUTHORITY = TPM_BASE + 95, > + TPM_PERMANENTEK = TPM_BASE + 97, > + TPM_BAD_SIGNATURE = TPM_BASE + 98, > + TPM_NOCONTEXTSPACE = TPM_BASE + 99, > + /* TPM-defined non-fatal errors */ > + TPM_RETRY = TPM_BASE + TPM_NON_FATAL, > + TPM_NEEDS_SELFTEST = TPM_BASE + TPM_NON_FATAL + 1, > + TPM_DOING_SELFTEST = TPM_BASE + TPM_NON_FATAL + 2, > + TPM_DEFEND_LOCK_RUNNING = TPM_BASE + TPM_NON_FATAL + 3, > +}; > + > +/** > * Initialize TPM device. It must be called before any TPM commands. > * > * @return 0 on success, non-0 on error. > @@ -201,4 +316,63 @@ uint32_t tpm_physical_set_deactivated(uint8_t state); > uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap, > void *cap, size_t count); > > +/** > + * Issue a TPM_FlushSpecific command for a AUTH ressource. > + * > + * @param auth_handle handle of the auth session > + * @return return code of the operation > + */ > +uint32_t tpm_terminate_auth_session(uint32_t auth_handle); > + > +/** > + * Issue a TPM_OIAP command to setup an object independant authorization > + * session. > + * Information about the session is stored internally. > + * If there was already an OIAP session active it is terminated and a new > + * session is set up. > + * > + * @param auth_handle pointer to the (new) auth handle or NULL. > + * @return return code of the operation > + */ > +uint32_t tpm_oiap(uint32_t *auth_handle); > + > +/** > + * Ends an active OIAP session. > + * > + * @return return code of the operation > + */ > +uint32_t tpm_end_oiap(void); > + > +/** > + * Issue a TPM_LoadKey2 (Auth1) command using an OIAP session for authenticating > + * the usage of the parent key. > + * > + * @param parent_handle handle of the parent key. > + * @param key pointer to the key structure (TPM_KEY or TPM_KEY12). > + * @param key_length size of the key structure > + * @param parent_key_usage_auth usage auth for the parent key > + * @param key_handle pointer to the key handle > + * @return return code of the operation > + */ > +uint32_t tpm_load_key2_oiap(uint32_t parent_handle, > + const void *key, size_t key_length, > + const void *parent_key_usage_auth, > + uint32_t *key_handle); > + > +/** > + * Issue a TPM_GetPubKey (Auth1) command using an OIAP session for > + * authenticating the usage of the key. > + * > + * @param key_handle handle of the key > + * @param usage_auth usage auth for the key > + * @param pubkey pointer to the pub key buffer; may be NULL if the pubkey > + * should not be stored. > + * @param pubkey_len pointer to the pub key buffer len. On entry: the size of > + * the provided pubkey buffer. On successful exit: the size > + * of the stored TPM_PUBKEY structure (iff pubkey != NULL). > + * @return return code of the operation > + */ > +uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth, > + void *pubkey, size_t *pubkey_len); > + > #endif /* __TPM_H */ > diff --git a/lib/tpm.c b/lib/tpm.c > index 42c9bea..87bf188 100644 > --- a/lib/tpm.c > +++ b/lib/tpm.c > @@ -1,5 +1,6 @@ > /* > * Copyright (c) 2013 The Chromium OS Authors. > + * Coypright (c) 2013 Guntermann & Drunck GmbH > * > * See file CREDITS for list of people who contributed to this > * project. > @@ -22,6 +23,7 @@ > > #include <common.h> > #include <stdarg.h> > +#include <sha1.h> > #include <tpm.h> > #include <asm/unaligned.h> > > @@ -35,8 +37,31 @@ enum { > TPM_REQUEST_HEADER_LENGTH = 10, > TPM_RESPONSE_HEADER_LENGTH = 10, > PCR_DIGEST_LENGTH = 20, > + DIGEST_LENGTH = 20, > + TPM_REQUEST_AUTH_LENGTH = 45, > + TPM_RESPONSE_AUTH_LENGTH = 41, > + /* some max lengths, valid for RSA keys <= 2048 bits */ > + TPM_KEY12_MAX_LENGTH = 618, > + TPM_PUBKEY_MAX_LENGTH = 288, > }; > > +#ifdef CONFIG_TPM_AUTH_SESSIONS > + > +#ifndef CONFIG_SHA1 > +#error "TPM_AUTH_SESSIONS require SHA1 to be configured, too" > +#endif /* !CONFIG_SHA1 */ > + > +struct session_data { > + int valid; > + uint32_t handle; > + uint8_t nonce_even[DIGEST_LENGTH]; > + uint8_t nonce_odd[DIGEST_LENGTH]; > +}; > + > +static struct session_data oiap_session = {0, }; > + > +#endif /* CONFIG_TPM_AUTH_SESSIONS */ > + > /** > * Pack data into a byte string. The data types are specified in > * the format string: 'b' means unsigned byte, 'w' unsigned word, > @@ -235,7 +260,7 @@ static uint32_t tpm_sendrecv_command(const void *command, > response, &response_length); > if (err) > return TPM_LIB_ERROR; > - if (response) > + if (size_ptr) > *size_ptr = response_length; Thanks for catching this! > > return tpm_return_code(response); > @@ -579,3 +604,324 @@ uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap, > > return 0; > } > + > +#ifdef CONFIG_TPM_AUTH_SESSIONS > + > +/** > + * Fill an authentication block in a request. > + * This func can create the first as well as the second auth block (for > + * double authorized commands). > + * > + * @param request pointer to the request (w/ uninitialised auth data) > + * @param request_len0 length of the request without auth data > + * @param handles_len length of the handles area in request > + * @param auth_session pointer to the (valid) auth session to be used > + * @param request_auth pointer to the auth block of the request to be filled > + * @param auth authentication data (HMAC key) > + */ > +static uint32_t create_request_auth(const void *request, size_t request_len0, > + size_t handles_len, > + struct session_data *auth_session, > + void *request_auth, const void *auth) > +{ > + uint8_t hmac_data[DIGEST_LENGTH * 3 + 1]; > + sha1_context hash_ctx; > + const size_t command_code_offset = 6; > + const size_t auth_nonce_odd_offset = 4; > + const size_t auth_continue_offset = 24; > + const size_t auth_auth_offset = 25; > + > + if (!auth_session || !auth_session->valid) > + return TPM_LIB_ERROR; > + > + sha1_starts(&hash_ctx); > + sha1_update(&hash_ctx, request + command_code_offset, 4); > + if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len) > + sha1_update(&hash_ctx, > + request + TPM_REQUEST_HEADER_LENGTH + handles_len, > + request_len0 - TPM_REQUEST_HEADER_LENGTH - handles_len); > + sha1_finish(&hash_ctx, hmac_data); > + > + sha1_starts(&hash_ctx); > + sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH); > + sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data)); > + sha1_finish(&hash_ctx, auth_session->nonce_odd); > + > + if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb", > + 0, auth_session->handle, > + auth_nonce_odd_offset, auth_session->nonce_odd, > + DIGEST_LENGTH, > + auth_continue_offset, 1)) > + return TPM_LIB_ERROR; > + if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss", > + DIGEST_LENGTH, > + auth_session->nonce_even, > + DIGEST_LENGTH, > + 2 * DIGEST_LENGTH, > + request_auth + auth_nonce_odd_offset, > + DIGEST_LENGTH + 1)) > + return TPM_LIB_ERROR; > + sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data), > + request_auth + auth_auth_offset); > + > + return TPM_SUCCESS; > +} > + > +/** > + * Verify an authentication block in a response. > + * Since this func updates the nonce_even in the session data it has to be > + * called when receiving a succesfull AUTH response. > + * This func can verify the first as well as the second auth block (for > + * double authorized commands). > + * > + * @param command_code command code of the request > + * @param response pointer to the request (w/ uninitialised auth data) > + * @param handles_len length of the handles area in response > + * @param auth_session pointer to the (valid) auth session to be used > + * @param response_auth pointer to the auth block of the response to be verified > + * @param auth authentication data (HMAC key) > + */ > +static uint32_t verify_response_auth(uint32_t command_code, > + const void *response, size_t response_len0, > + size_t handles_len, > + struct session_data *auth_session, > + const void *response_auth, const void *auth) > +{ > + uint8_t hmac_data[DIGEST_LENGTH * 3 + 1]; > + uint8_t computed_auth[DIGEST_LENGTH]; > + sha1_context hash_ctx; > + const size_t return_code_offset = 6; > + const size_t auth_continue_offset = 20; > + const size_t auth_auth_offset = 21; > + uint8_t auth_continue; > + > + if (!auth_session || !auth_session->valid) > + return TPM_AUTHFAIL; > + if (pack_byte_string(hmac_data, sizeof(hmac_data), "d", > + 0, command_code)) > + return TPM_LIB_ERROR; > + if (response_len0 < TPM_RESPONSE_HEADER_LENGTH) > + return TPM_LIB_ERROR; > + > + sha1_starts(&hash_ctx); > + sha1_update(&hash_ctx, response + return_code_offset, 4); > + sha1_update(&hash_ctx, hmac_data, 4); > + if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len) > + sha1_update(&hash_ctx, > + response + TPM_RESPONSE_HEADER_LENGTH + handles_len, > + response_len0 - TPM_RESPONSE_HEADER_LENGTH > + - handles_len); > + sha1_finish(&hash_ctx, hmac_data); > + > + memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH); > + auth_continue = ((uint8_t *)response_auth)[auth_continue_offset]; > + if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb", > + DIGEST_LENGTH, > + response_auth, > + DIGEST_LENGTH, > + 2 * DIGEST_LENGTH, > + auth_session->nonce_odd, > + DIGEST_LENGTH, > + 3 * DIGEST_LENGTH, > + auth_continue)) > + return TPM_LIB_ERROR; > + > + sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data), > + computed_auth); > + > + if (memcmp(computed_auth, response_auth + auth_auth_offset, > + DIGEST_LENGTH)) > + return TPM_AUTHFAIL; > + > + return TPM_SUCCESS; > +} > + > + > +uint32_t tpm_terminate_auth_session(uint32_t auth_handle) > +{ > + const uint8_t command[18] = { > + 0x00, 0xc1, /* TPM_TAG */ > + 0x00, 0x00, 0x00, 0x00, /* parameter size */ > + 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */ > + 0x00, 0x00, 0x00, 0x00, /* TPM_HANDLE */ > + 0x00, 0x00, 0x00, 0x02, /* TPM_RESSOURCE_TYPE */ > + }; > + const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH; > + uint8_t request[COMMAND_BUFFER_SIZE]; > + > + if (pack_byte_string(request, sizeof(request), "sd", > + 0, command, sizeof(command), > + req_handle_offset, auth_handle)) > + return TPM_LIB_ERROR; > + if (oiap_session.valid && oiap_session.handle == auth_handle) > + oiap_session.valid = 0; > + > + return tpm_sendrecv_command(request, NULL, NULL); > +} > + > +uint32_t tpm_end_oiap(void) > +{ > + uint32_t err = TPM_SUCCESS; > + if (oiap_session.valid) > + err = tpm_terminate_auth_session(oiap_session.handle); > + return err; > +} > + > +uint32_t tpm_oiap(uint32_t *auth_handle) > +{ > + const uint8_t command[10] = { > + 0x00, 0xc1, /* TPM_TAG */ > + 0x00, 0x00, 0x00, 0x0a, /* parameter size */ > + 0x00, 0x00, 0x00, 0x0a, /* TPM_COMMAND_CODE */ > + }; > + const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH; > + const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4; > + uint8_t response[COMMAND_BUFFER_SIZE]; > + size_t response_length = sizeof(response); > + uint32_t err; > + > + if (oiap_session.valid) > + tpm_terminate_auth_session(oiap_session.handle); > + > + err = tpm_sendrecv_command(command, response, &response_length); > + if (err) > + return err; > + if (unpack_byte_string(response, response_length, "ds", > + res_auth_handle_offset, &oiap_session.handle, > + res_nonce_even_offset, &oiap_session.nonce_even, > + (uint32_t)DIGEST_LENGTH)) > + return TPM_LIB_ERROR; > + oiap_session.valid = 1; > + if (auth_handle) > + *auth_handle = oiap_session.handle; > + return 0; > +} > + > +uint32_t tpm_load_key2_oiap(uint32_t parent_handle, > + const void *key, size_t key_length, > + const void *parent_key_usage_auth, > + uint32_t *key_handle) > +{ > + const uint8_t command[14] = { > + 0x00, 0xc2, /* TPM_TAG */ > + 0x00, 0x00, 0x00, 0x00, /* parameter size */ > + 0x00, 0x00, 0x00, 0x41, /* TPM_COMMAND_CODE */ > + 0x00, 0x00, 0x00, 0x00, /* parent handle */ > + }; > + const size_t req_size_offset = 2; > + const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH; > + const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4; > + const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH; > + uint8_t request[sizeof(command) + TPM_KEY12_MAX_LENGTH > + + TPM_REQUEST_AUTH_LENGTH]; > + uint8_t response[COMMAND_BUFFER_SIZE]; > + size_t response_length = sizeof(response); > + uint32_t err; > + > + if (!oiap_session.valid) { > + err = tpm_oiap(NULL); > + if (err) > + return err; > + } > + if (pack_byte_string(request, sizeof(request), "sdds", > + 0, command, sizeof(command), > + req_size_offset, > + sizeof(command) + key_length + TPM_REQUEST_AUTH_LENGTH, > + req_parent_handle_offset, parent_handle, > + req_key_offset, key, key_length > + )) > + return TPM_LIB_ERROR; > + > + err = create_request_auth(request, sizeof(command) + key_length, 4, > + &oiap_session, > + request + sizeof(command) + key_length, > + parent_key_usage_auth); > + if (err) > + return err; > + err = tpm_sendrecv_command(request, response, &response_length); > + if (err) { > + if (err == TPM_AUTHFAIL) > + oiap_session.valid = 0; > + return err; > + } > + > + err = verify_response_auth(0x00000041, response, > + response_length - TPM_RESPONSE_AUTH_LENGTH, > + 4, &oiap_session, > + response + response_length - TPM_RESPONSE_AUTH_LENGTH, > + parent_key_usage_auth); > + if (err) > + return err; > + > + if (key_handle) { > + if (unpack_byte_string(response, response_length, "d", > + res_handle_offset, key_handle)) > + return TPM_LIB_ERROR; > + } > + > + return 0; > +} > + > +uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth, > + void *pubkey, size_t *pubkey_len) > +{ > + const uint8_t command[14] = { > + 0x00, 0xc2, /* TPM_TAG */ > + 0x00, 0x00, 0x00, 0x00, /* parameter size */ > + 0x00, 0x00, 0x00, 0x21, /* TPM_COMMAND_CODE */ > + 0x00, 0x00, 0x00, 0x00, /* key handle */ > + }; > + const size_t req_size_offset = 2; > + const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH; > + const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH; > + uint8_t request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH]; > + uint8_t response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH > + + TPM_RESPONSE_AUTH_LENGTH]; > + size_t response_length = sizeof(response); > + uint32_t err; > + > + if (!oiap_session.valid) { > + err = tpm_oiap(NULL); > + if (err) > + return err; > + } > + if (pack_byte_string(request, sizeof(request), "sdd", > + 0, command, sizeof(command), > + req_size_offset, > + (uint32_t)(sizeof(command) + TPM_REQUEST_AUTH_LENGTH), > + req_key_handle_offset, key_handle > + )) > + return TPM_LIB_ERROR; > + err = create_request_auth(request, sizeof(command), 4, &oiap_session, > + request + sizeof(command), usage_auth); > + if (err) > + return err; > + err = tpm_sendrecv_command(request, response, &response_length); > + if (err) { > + if (err == TPM_AUTHFAIL) > + oiap_session.valid = 0; > + return err; > + } > + err = verify_response_auth(0x00000021, response, > + response_length - TPM_RESPONSE_AUTH_LENGTH, > + 0, &oiap_session, > + response + response_length - TPM_RESPONSE_AUTH_LENGTH, > + usage_auth); > + if (err) > + return err; > + > + if (pubkey) { > + if ((response_length - TPM_RESPONSE_HEADER_LENGTH > + - TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len) > + return TPM_LIB_ERROR; > + *pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH > + - TPM_RESPONSE_AUTH_LENGTH; > + memcpy(pubkey, response + res_pubkey_offset, > + response_length - TPM_RESPONSE_HEADER_LENGTH > + - TPM_RESPONSE_AUTH_LENGTH); > + } > + > + return 0; > +} > + > +#endif /* CONFIG_TPM_AUTH_SESSIONS */ > -- > 1.7.10.4 > >
On Mon, Apr 22, 2013 at 01:06:40PM +0200, Dirk Eibach wrote: > From: Reinhard Pfau <pfau@gdsys.de> > > Extend the tpm library with support for single authorized (AUTH1) commands > as specified in the TCG Main Specification 1.2. (The internally used helper > functions are implemented in a way that they could also be used for double > authorized commands if someone needs it.) > > Provide enums with the return codes from the TCG Main specification. > > For now only a single OIAP session is supported. > > OIAP authorized version of the commands TPM_LoadKey2 and TPM_GetPubKey are > provided. Both features are available using the 'tpm' command, too. > > Authorized commands are enabled with CONFIG_TPM_AUTH_SESSIONS. (Note that > this also requires CONFIG_SHA1 to be enabled.) > > Signed-off-by: Reinhard Pfau <pfau@gdsys.de> > > Signed-off-by: Dirk Eibach <eibach@gdsys.de> I'm glad to see this as it helps further the TPM related discussions. [snip] > +#ifdef CONFIG_TPM_AUTH_SESSIONS > + > +static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) checkpatch warning. Please run ./tools/checkpatch.pl and fix everything. [snip] > + uint8_t usage_auth[20]; [snip] > + if (strlen(argv[4]) != 40) > + return CMD_RET_FAILURE; [snip] > + uint8_t usage_auth[20]; > + uint8_t pub_key_buffer[288]; These are just a few examples of array sizes that clearly have a specific meaning (288 == TPM_PUBKEY_MAX_LENGTH I think), that we should be using the name for, for clarity. [snip] > /** > + * TPM return codes as defined in the TCG Main specification > + * (TPM Main Part 2 Structures; Specification version 1.2) > + */ > +enum tpm_return_code { > + TPM_BASE = 0x00000000, > + TPM_NON_FATAL = 0x00000800, > + TPM_SUCCESS = TPM_BASE, > + /* TPM-defined fatal error codes */ > + TPM_AUTHFAIL = TPM_BASE + 1, [snip] > + TPM_BADINDEX = TPM_BASE + 2, I don't like this form, and it's not what we usually use. It should be, roughly: enum tpm_return_code { TPM_SUCCESS = 0, /* TPM-defined fatal error codes. */ TPM_BAD_PARAMETER, TPM_AUDITFAILURE, ... /* TPM-defined non-fatal error codes. */ TPM_RETRY = 0x800, TPM_NEEDS_SELFTEST, ... }
Hi, > -----Original Message----- > From: u-boot-bounces@lists.denx.de > [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Tom Rini > Sent: Monday, April 22, 2013 8:37 PM > To: Eibach, Dirk > Cc: u-boot@lists.denx.de > Subject: (Mixed security state) Re: [U-Boot] [PATCH 1/6] tpm: > add AUTH1 cmds for LoadKey2 and GetPubKey > > On Mon, Apr 22, 2013 at 01:06:40PM +0200, Dirk Eibach wrote: > > > From: Reinhard Pfau <pfau@gdsys.de> > > > > Extend the tpm library with support for single authorized > (AUTH1) commands > > as specified in the TCG Main Specification 1.2. (The > internally used helper > > functions are implemented in a way that they could also be > used for double > > authorized commands if someone needs it.) > > > > Provide enums with the return codes from the TCG Main specification. > > > > For now only a single OIAP session is supported. > > > > OIAP authorized version of the commands TPM_LoadKey2 and > TPM_GetPubKey are > > provided. Both features are available using the 'tpm' command, too. > > > > Authorized commands are enabled with > CONFIG_TPM_AUTH_SESSIONS. (Note that > > this also requires CONFIG_SHA1 to be enabled.) > > > > Signed-off-by: Reinhard Pfau <pfau@gdsys.de> > > > > Signed-off-by: Dirk Eibach <eibach@gdsys.de> > > I'm glad to see this as it helps further the TPM related discussions. > > [snip] > > +#ifdef CONFIG_TPM_AUTH_SESSIONS > > + > > +static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, > > + int argc, char * const argv[]) > > checkpatch warning. Please run ./tools/checkpatch.pl and fix > everything. OK; will be done. > > [snip] > > + uint8_t usage_auth[20]; > [snip] > > + if (strlen(argv[4]) != 40) > > + return CMD_RET_FAILURE; > [snip] > > + uint8_t usage_auth[20]; > > + uint8_t pub_key_buffer[288]; > > These are just a few examples of array sizes that clearly have a > specific meaning (288 == TPM_PUBKEY_MAX_LENGTH I think), that > we should > be using the name for, for clarity. Like we did in lib/tpm.c; sounds like a good idea. Will be done, too. > > [snip] > > /** > > + * TPM return codes as defined in the TCG Main specification > > + * (TPM Main Part 2 Structures; Specification version 1.2) > > + */ > > +enum tpm_return_code { > > + TPM_BASE = 0x00000000, > > + TPM_NON_FATAL = 0x00000800, > > + TPM_SUCCESS = TPM_BASE, > > + /* TPM-defined fatal error codes */ > > + TPM_AUTHFAIL = TPM_BASE + 1, > [snip] > > + TPM_BADINDEX = TPM_BASE + 2, > > I don't like this form, and it's not what we usually use. It > should be, > roughly: > enum tpm_return_code { > TPM_SUCCESS = 0, > /* TPM-defined fatal error codes. */ > TPM_BAD_PARAMETER, > TPM_AUDITFAILURE, > ... > /* TPM-defined non-fatal error codes. */ > TPM_RETRY = 0x800, > TPM_NEEDS_SELFTEST, > ... > } Well, the way I wrote the constants is intentionally since the return codes are defined like this in the TCG specification. In the spec the return codes are found in a table with name and value; and the value is expressed as sum based on TPM_BASE. (See TCG published spec: "TPM Main Part 2 TPM Structures; Specification version 1.2" chapter 16 ("Return Codes").) This way it might be easier to keep the constants in sync with (future) versions of the TCG spec :-) So I would like to keep it as it is. Greetings, Reinhard.
On Tue, Apr 23, 2013 at 02:12:52PM +0200, Pfau, Reinhard wrote: > > Hi, > > > -----Original Message----- > > From: u-boot-bounces@lists.denx.de > > [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Tom Rini > > Sent: Monday, April 22, 2013 8:37 PM > > To: Eibach, Dirk > > Cc: u-boot@lists.denx.de > > Subject: (Mixed security state) Re: [U-Boot] [PATCH 1/6] tpm: > > add AUTH1 cmds for LoadKey2 and GetPubKey > > > > On Mon, Apr 22, 2013 at 01:06:40PM +0200, Dirk Eibach wrote: > > > > > From: Reinhard Pfau <pfau@gdsys.de> [snip] > > [snip] > > > /** > > > + * TPM return codes as defined in the TCG Main specification > > > + * (TPM Main Part 2 Structures; Specification version 1.2) > > > + */ > > > +enum tpm_return_code { > > > + TPM_BASE = 0x00000000, > > > + TPM_NON_FATAL = 0x00000800, > > > + TPM_SUCCESS = TPM_BASE, > > > + /* TPM-defined fatal error codes */ > > > + TPM_AUTHFAIL = TPM_BASE + 1, > > [snip] > > > + TPM_BADINDEX = TPM_BASE + 2, > > > > I don't like this form, and it's not what we usually use. It > > should be, > > roughly: > > enum tpm_return_code { > > TPM_SUCCESS = 0, > > /* TPM-defined fatal error codes. */ > > TPM_BAD_PARAMETER, > > TPM_AUDITFAILURE, > > ... > > /* TPM-defined non-fatal error codes. */ > > TPM_RETRY = 0x800, > > TPM_NEEDS_SELFTEST, > > ... > > } > > Well, the way I wrote the constants is intentionally since the return > codes are defined like this in the TCG specification. > In the spec the return codes are found in a table with name and value; > and the value is expressed as sum based on TPM_BASE. > (See TCG published spec: "TPM Main Part 2 TPM Structures; Specification > version 1.2" chapter 16 ("Return Codes").) > > This way it might be easier to keep the constants in sync with (future) > versions of the TCG spec :-) > So I would like to keep it as it is. OK, I pulled up the doc and I see what you mean. Yes, it's OK to do that in this case here. Thanks!
diff --git a/README b/README index 0bc0af5..58b2ee5 100644 --- a/README +++ b/README @@ -1210,6 +1210,20 @@ The following options need to be configured: to. Contemporary x86 systems usually map it at 0xfed40000. + CONFIG_CMD_TPM + Add tpm monitor functions. + Requires CONFIG_TPM. If CONFIG_TPM_AUTH_SESSIONS is set, also + provides monitor access to authorized functions. + + CONFIG_TPM + Define this to enable the TPM support library which provides + functional interfaces to some TPM commands. + Requires support for a TPM device. + + CONFIG_TPM_AUTH_SESSIONS + Define this to enable authorized functions in the TPM library. + Requires CONFIG_TPM and CONFIG_SHA1. + - USB Support: At the moment only the UHCI host controller is supported (PIP405, MIP405, MPC5200); define diff --git a/common/cmd_tpm.c b/common/cmd_tpm.c index 46fae18..c8767a4 100644 --- a/common/cmd_tpm.c +++ b/common/cmd_tpm.c @@ -546,6 +546,72 @@ static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag, return convert_return_code(err); } +#ifdef CONFIG_TPM_AUTH_SESSIONS + +static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t auth_handle, err; + + err = tpm_oiap(&auth_handle); + + return convert_return_code(err); +} + +static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t parent_handle, key_len, key_handle, err; + uint8_t usage_auth[20]; + void *key; + + if (argc < 5) + return CMD_RET_USAGE; + + parent_handle = simple_strtoul(argv[1], NULL, 0); + key = (void *)simple_strtoul(argv[2], NULL, 0); + key_len = simple_strtoul(argv[3], NULL, 0); + if (strlen(argv[4]) != 40) + return CMD_RET_FAILURE; + parse_byte_string(argv[4], usage_auth, NULL); + + err = tpm_load_key2_oiap(parent_handle, key, key_len, usage_auth, + &key_handle); + if (!err) + printf("Key handle is 0x%x\n", key_handle); + + return convert_return_code(err); +} + +static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + uint32_t key_handle, err; + uint8_t usage_auth[20]; + uint8_t pub_key_buffer[288]; + size_t pub_key_len = sizeof(pub_key_buffer); + + if (argc < 3) + return CMD_RET_USAGE; + + key_handle = simple_strtoul(argv[1], NULL, 0); + if (strlen(argv[2]) != 40) + return CMD_RET_FAILURE; + parse_byte_string(argv[2], usage_auth, NULL); + + err = tpm_get_pub_key_oiap(key_handle, usage_auth, + pub_key_buffer, &pub_key_len); + if (!err) { + printf("dump of received pub key structure:\n"); + print_byte_string(pub_key_buffer, pub_key_len); + } + return convert_return_code(err); +} + +TPM_COMMAND_NO_ARG(tpm_end_oiap) + +#endif /* CONFIG_TPM_AUTH_SESSIONS */ + #define MAKE_TPM_CMD_ENTRY(cmd) \ U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "") @@ -590,6 +656,16 @@ static cmd_tbl_t tpm_commands[] = { do_tpm_nv_read, "", ""), U_BOOT_CMD_MKENT(nv_write, 0, 1, do_tpm_nv_write, "", ""), +#ifdef CONFIG_TPM_AUTH_SESSIONS + U_BOOT_CMD_MKENT(oiap, 0, 1, + do_tpm_oiap, "", ""), + U_BOOT_CMD_MKENT(end_oiap, 0, 1, + do_tpm_end_oiap, "", ""), + U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1, + do_tpm_load_key2_oiap, "", ""), + U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1, + do_tpm_get_pub_key_oiap, "", ""), +#endif /* CONFIG_TPM_AUTH_SESSIONS */ }; static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -638,6 +714,16 @@ U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, " get_capability cap_area sub_cap addr count\n" " - Read <count> bytes of TPM capability indexed by <cap_area> and\n" " <sub_cap> to memory address <addr>.\n" +#ifdef CONFIG_TPM_AUTH_SESSIONS +"Storage functions\n" +" loadkey2_oiap parent_handle key_addr key_len usage_auth\n" +" - loads a key data from memory address <key_addr>, <key_len> bytes\n" +" into TPM using the parent key <parent_handle> with authorization\n" +" <usage_auth> (20 bytes hex string).\n" +" get_pub_key_oiap key_handle usage_auth\n" +" - get the public key portion of a loaded key <key_handle> using\n" +" authorization <usage auth> (20 bytes hex string)\n" +#endif /* CONFIG_TPM_AUTH_SESSIONS */ "Endorsement Key Handling Commands:\n" " read_pubek addr count\n" " - Read <count> bytes of the public endorsement key to memory\n" @@ -648,6 +734,13 @@ U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, " <digest_hex_string>\n" " pcr_read index addr count\n" " - Read <count> bytes from PCR <index> to memory address <addr>.\n" +#ifdef CONFIG_TPM_AUTH_SESSIONS +"Authorization Sessions\n" +" oiap\n" +" - setup an OIAP session\n" +" end_oiap\n" +" - terminates an active OIAP session\n" +#endif /* CONFIG_TPM_AUTH_SESSIONS */ "Non-volatile Storage Commands:\n" " nv_define_space index permission size\n" " - Establish a space at index <index> with <permission> of <size> bytes.\n" diff --git a/include/tpm.h b/include/tpm.h index 7219b73..5e9f832 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 The Chromium OS Authors. + * Coypright (c) 2013 Guntermann & Drunck GmbH * * See file CREDITS for list of people who contributed to this * project. @@ -54,6 +55,120 @@ enum tpm_nv_index { }; /** + * TPM return codes as defined in the TCG Main specification + * (TPM Main Part 2 Structures; Specification version 1.2) + */ +enum tpm_return_code { + TPM_BASE = 0x00000000, + TPM_NON_FATAL = 0x00000800, + TPM_SUCCESS = TPM_BASE, + /* TPM-defined fatal error codes */ + TPM_AUTHFAIL = TPM_BASE + 1, + TPM_BADINDEX = TPM_BASE + 2, + TPM_BAD_PARAMETER = TPM_BASE + 3, + TPM_AUDITFAILURE = TPM_BASE + 4, + TPM_CLEAR_DISABLED = TPM_BASE + 5, + TPM_DEACTIVATED = TPM_BASE + 6, + TPM_DISABLED = TPM_BASE + 7, + TPM_DISABLED_CMD = TPM_BASE + 8, + TPM_FAIL = TPM_BASE + 9, + TPM_BAD_ORDINAL = TPM_BASE + 10, + TPM_INSTALL_DISABLED = TPM_BASE + 11, + TPM_INVALID_KEYHANDLE = TPM_BASE + 12, + TPM_KEYNOTFOUND = TPM_BASE + 13, + TPM_INAPPROPRIATE_ENC = TPM_BASE + 14, + TPM_MIGRATE_FAIL = TPM_BASE + 15, + TPM_INVALID_PCR_INFO = TPM_BASE + 16, + TPM_NOSPACE = TPM_BASE + 17, + TPM_NOSRK = TPM_BASE + 18, + TPM_NOTSEALED_BLOB = TPM_BASE + 19, + TPM_OWNER_SET = TPM_BASE + 20, + TPM_RESOURCES = TPM_BASE + 21, + TPM_SHORTRANDOM = TPM_BASE + 22, + TPM_SIZE = TPM_BASE + 23, + TPM_WRONGPCRVAL = TPM_BASE + 24, + TPM_BAD_PARAM_SIZE = TPM_BASE + 25, + TPM_SHA_THREAD = TPM_BASE + 26, + TPM_SHA_ERROR = TPM_BASE + 27, + TPM_FAILEDSELFTEST = TPM_BASE + 28, + TPM_AUTH2FAIL = TPM_BASE + 29, + TPM_BADTAG = TPM_BASE + 30, + TPM_IOERROR = TPM_BASE + 31, + TPM_ENCRYPT_ERROR = TPM_BASE + 32, + TPM_DECRYPT_ERROR = TPM_BASE + 33, + TPM_INVALID_AUTHHANDLE = TPM_BASE + 34, + TPM_NO_ENDORSEMENT = TPM_BASE + 35, + TPM_INVALID_KEYUSAGE = TPM_BASE + 36, + TPM_WRONG_ENTITYTYPE = TPM_BASE + 37, + TPM_INVALID_POSTINIT = TPM_BASE + 38, + TPM_INAPPROPRIATE_SIG = TPM_BASE + 39, + TPM_BAD_KEY_PROPERTY = TPM_BASE + 40, + TPM_BAD_MIGRATION = TPM_BASE + 41, + TPM_BAD_SCHEME = TPM_BASE + 42, + TPM_BAD_DATASIZE = TPM_BASE + 43, + TPM_BAD_MODE = TPM_BASE + 44, + TPM_BAD_PRESENCE = TPM_BASE + 45, + TPM_BAD_VERSION = TPM_BASE + 46, + TPM_NO_WRAP_TRANSPORT = TPM_BASE + 47, + TPM_AUDITFAIL_UNSUCCESSFUL = TPM_BASE + 48, + TPM_AUDITFAIL_SUCCESSFUL = TPM_BASE + 49, + TPM_NOTRESETABLE = TPM_BASE + 50, + TPM_NOTLOCAL = TPM_BASE + 51, + TPM_BAD_TYPE = TPM_BASE + 52, + TPM_INVALID_RESOURCE = TPM_BASE + 53, + TPM_NOTFIPS = TPM_BASE + 54, + TPM_INVALID_FAMILY = TPM_BASE + 55, + TPM_NO_NV_PERMISSION = TPM_BASE + 56, + TPM_REQUIRES_SIGN = TPM_BASE + 57, + TPM_KEY_NOTSUPPORTED = TPM_BASE + 58, + TPM_AUTH_CONFLICT = TPM_BASE + 59, + TPM_AREA_LOCKED = TPM_BASE + 60, + TPM_BAD_LOCALITY = TPM_BASE + 61, + TPM_READ_ONLY = TPM_BASE + 62, + TPM_PER_NOWRITE = TPM_BASE + 63, + TPM_FAMILY_COUNT = TPM_BASE + 64, + TPM_WRITE_LOCKED = TPM_BASE + 65, + TPM_BAD_ATTRIBUTES = TPM_BASE + 66, + TPM_INVALID_STRUCTURE = TPM_BASE + 67, + TPM_KEY_OWNER_CONTROL = TPM_BASE + 68, + TPM_BAD_COUNTER = TPM_BASE + 69, + TPM_NOT_FULLWRITE = TPM_BASE + 70, + TPM_CONTEXT_GAP = TPM_BASE + 71, + TPM_MAXNVWRITES = TPM_BASE + 72, + TPM_NOOPERATOR = TPM_BASE + 73, + TPM_RESOURCEMISSING = TPM_BASE + 74, + TPM_DELEGATE_LOCK = TPM_BASE + 75, + TPM_DELEGATE_FAMILY = TPM_BASE + 76, + TPM_DELEGATE_ADMIN = TPM_BASE + 77, + TPM_TRANSPORT_NOTEXCLUSIVE = TPM_BASE + 78, + TPM_OWNER_CONTROL = TPM_BASE + 79, + TPM_DAA_RESOURCES = TPM_BASE + 80, + TPM_DAA_INPUT_DATA0 = TPM_BASE + 81, + TPM_DAA_INPUT_DATA1 = TPM_BASE + 82, + TPM_DAA_ISSUER_SETTINGS = TPM_BASE + 83, + TPM_DAA_TPM_SETTINGS = TPM_BASE + 84, + TPM_DAA_STAGE = TPM_BASE + 85, + TPM_DAA_ISSUER_VALIDITY = TPM_BASE + 86, + TPM_DAA_WRONG_W = TPM_BASE + 87, + TPM_BAD_HANDLE = TPM_BASE + 88, + TPM_BAD_DELEGATE = TPM_BASE + 89, + TPM_BADCONTEXT = TPM_BASE + 90, + TPM_TOOMANYCONTEXTS = TPM_BASE + 91, + TPM_MA_TICKET_SIGNATURE = TPM_BASE + 92, + TPM_MA_DESTINATION = TPM_BASE + 93, + TPM_MA_SOURCE = TPM_BASE + 94, + TPM_MA_AUTHORITY = TPM_BASE + 95, + TPM_PERMANENTEK = TPM_BASE + 97, + TPM_BAD_SIGNATURE = TPM_BASE + 98, + TPM_NOCONTEXTSPACE = TPM_BASE + 99, + /* TPM-defined non-fatal errors */ + TPM_RETRY = TPM_BASE + TPM_NON_FATAL, + TPM_NEEDS_SELFTEST = TPM_BASE + TPM_NON_FATAL + 1, + TPM_DOING_SELFTEST = TPM_BASE + TPM_NON_FATAL + 2, + TPM_DEFEND_LOCK_RUNNING = TPM_BASE + TPM_NON_FATAL + 3, +}; + +/** * Initialize TPM device. It must be called before any TPM commands. * * @return 0 on success, non-0 on error. @@ -201,4 +316,63 @@ uint32_t tpm_physical_set_deactivated(uint8_t state); uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap, void *cap, size_t count); +/** + * Issue a TPM_FlushSpecific command for a AUTH ressource. + * + * @param auth_handle handle of the auth session + * @return return code of the operation + */ +uint32_t tpm_terminate_auth_session(uint32_t auth_handle); + +/** + * Issue a TPM_OIAP command to setup an object independant authorization + * session. + * Information about the session is stored internally. + * If there was already an OIAP session active it is terminated and a new + * session is set up. + * + * @param auth_handle pointer to the (new) auth handle or NULL. + * @return return code of the operation + */ +uint32_t tpm_oiap(uint32_t *auth_handle); + +/** + * Ends an active OIAP session. + * + * @return return code of the operation + */ +uint32_t tpm_end_oiap(void); + +/** + * Issue a TPM_LoadKey2 (Auth1) command using an OIAP session for authenticating + * the usage of the parent key. + * + * @param parent_handle handle of the parent key. + * @param key pointer to the key structure (TPM_KEY or TPM_KEY12). + * @param key_length size of the key structure + * @param parent_key_usage_auth usage auth for the parent key + * @param key_handle pointer to the key handle + * @return return code of the operation + */ +uint32_t tpm_load_key2_oiap(uint32_t parent_handle, + const void *key, size_t key_length, + const void *parent_key_usage_auth, + uint32_t *key_handle); + +/** + * Issue a TPM_GetPubKey (Auth1) command using an OIAP session for + * authenticating the usage of the key. + * + * @param key_handle handle of the key + * @param usage_auth usage auth for the key + * @param pubkey pointer to the pub key buffer; may be NULL if the pubkey + * should not be stored. + * @param pubkey_len pointer to the pub key buffer len. On entry: the size of + * the provided pubkey buffer. On successful exit: the size + * of the stored TPM_PUBKEY structure (iff pubkey != NULL). + * @return return code of the operation + */ +uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth, + void *pubkey, size_t *pubkey_len); + #endif /* __TPM_H */ diff --git a/lib/tpm.c b/lib/tpm.c index 42c9bea..87bf188 100644 --- a/lib/tpm.c +++ b/lib/tpm.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 The Chromium OS Authors. + * Coypright (c) 2013 Guntermann & Drunck GmbH * * See file CREDITS for list of people who contributed to this * project. @@ -22,6 +23,7 @@ #include <common.h> #include <stdarg.h> +#include <sha1.h> #include <tpm.h> #include <asm/unaligned.h> @@ -35,8 +37,31 @@ enum { TPM_REQUEST_HEADER_LENGTH = 10, TPM_RESPONSE_HEADER_LENGTH = 10, PCR_DIGEST_LENGTH = 20, + DIGEST_LENGTH = 20, + TPM_REQUEST_AUTH_LENGTH = 45, + TPM_RESPONSE_AUTH_LENGTH = 41, + /* some max lengths, valid for RSA keys <= 2048 bits */ + TPM_KEY12_MAX_LENGTH = 618, + TPM_PUBKEY_MAX_LENGTH = 288, }; +#ifdef CONFIG_TPM_AUTH_SESSIONS + +#ifndef CONFIG_SHA1 +#error "TPM_AUTH_SESSIONS require SHA1 to be configured, too" +#endif /* !CONFIG_SHA1 */ + +struct session_data { + int valid; + uint32_t handle; + uint8_t nonce_even[DIGEST_LENGTH]; + uint8_t nonce_odd[DIGEST_LENGTH]; +}; + +static struct session_data oiap_session = {0, }; + +#endif /* CONFIG_TPM_AUTH_SESSIONS */ + /** * Pack data into a byte string. The data types are specified in * the format string: 'b' means unsigned byte, 'w' unsigned word, @@ -235,7 +260,7 @@ static uint32_t tpm_sendrecv_command(const void *command, response, &response_length); if (err) return TPM_LIB_ERROR; - if (response) + if (size_ptr) *size_ptr = response_length; return tpm_return_code(response); @@ -579,3 +604,324 @@ uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap, return 0; } + +#ifdef CONFIG_TPM_AUTH_SESSIONS + +/** + * Fill an authentication block in a request. + * This func can create the first as well as the second auth block (for + * double authorized commands). + * + * @param request pointer to the request (w/ uninitialised auth data) + * @param request_len0 length of the request without auth data + * @param handles_len length of the handles area in request + * @param auth_session pointer to the (valid) auth session to be used + * @param request_auth pointer to the auth block of the request to be filled + * @param auth authentication data (HMAC key) + */ +static uint32_t create_request_auth(const void *request, size_t request_len0, + size_t handles_len, + struct session_data *auth_session, + void *request_auth, const void *auth) +{ + uint8_t hmac_data[DIGEST_LENGTH * 3 + 1]; + sha1_context hash_ctx; + const size_t command_code_offset = 6; + const size_t auth_nonce_odd_offset = 4; + const size_t auth_continue_offset = 24; + const size_t auth_auth_offset = 25; + + if (!auth_session || !auth_session->valid) + return TPM_LIB_ERROR; + + sha1_starts(&hash_ctx); + sha1_update(&hash_ctx, request + command_code_offset, 4); + if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len) + sha1_update(&hash_ctx, + request + TPM_REQUEST_HEADER_LENGTH + handles_len, + request_len0 - TPM_REQUEST_HEADER_LENGTH - handles_len); + sha1_finish(&hash_ctx, hmac_data); + + sha1_starts(&hash_ctx); + sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH); + sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data)); + sha1_finish(&hash_ctx, auth_session->nonce_odd); + + if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb", + 0, auth_session->handle, + auth_nonce_odd_offset, auth_session->nonce_odd, + DIGEST_LENGTH, + auth_continue_offset, 1)) + return TPM_LIB_ERROR; + if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss", + DIGEST_LENGTH, + auth_session->nonce_even, + DIGEST_LENGTH, + 2 * DIGEST_LENGTH, + request_auth + auth_nonce_odd_offset, + DIGEST_LENGTH + 1)) + return TPM_LIB_ERROR; + sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data), + request_auth + auth_auth_offset); + + return TPM_SUCCESS; +} + +/** + * Verify an authentication block in a response. + * Since this func updates the nonce_even in the session data it has to be + * called when receiving a succesfull AUTH response. + * This func can verify the first as well as the second auth block (for + * double authorized commands). + * + * @param command_code command code of the request + * @param response pointer to the request (w/ uninitialised auth data) + * @param handles_len length of the handles area in response + * @param auth_session pointer to the (valid) auth session to be used + * @param response_auth pointer to the auth block of the response to be verified + * @param auth authentication data (HMAC key) + */ +static uint32_t verify_response_auth(uint32_t command_code, + const void *response, size_t response_len0, + size_t handles_len, + struct session_data *auth_session, + const void *response_auth, const void *auth) +{ + uint8_t hmac_data[DIGEST_LENGTH * 3 + 1]; + uint8_t computed_auth[DIGEST_LENGTH]; + sha1_context hash_ctx; + const size_t return_code_offset = 6; + const size_t auth_continue_offset = 20; + const size_t auth_auth_offset = 21; + uint8_t auth_continue; + + if (!auth_session || !auth_session->valid) + return TPM_AUTHFAIL; + if (pack_byte_string(hmac_data, sizeof(hmac_data), "d", + 0, command_code)) + return TPM_LIB_ERROR; + if (response_len0 < TPM_RESPONSE_HEADER_LENGTH) + return TPM_LIB_ERROR; + + sha1_starts(&hash_ctx); + sha1_update(&hash_ctx, response + return_code_offset, 4); + sha1_update(&hash_ctx, hmac_data, 4); + if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len) + sha1_update(&hash_ctx, + response + TPM_RESPONSE_HEADER_LENGTH + handles_len, + response_len0 - TPM_RESPONSE_HEADER_LENGTH + - handles_len); + sha1_finish(&hash_ctx, hmac_data); + + memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH); + auth_continue = ((uint8_t *)response_auth)[auth_continue_offset]; + if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb", + DIGEST_LENGTH, + response_auth, + DIGEST_LENGTH, + 2 * DIGEST_LENGTH, + auth_session->nonce_odd, + DIGEST_LENGTH, + 3 * DIGEST_LENGTH, + auth_continue)) + return TPM_LIB_ERROR; + + sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data), + computed_auth); + + if (memcmp(computed_auth, response_auth + auth_auth_offset, + DIGEST_LENGTH)) + return TPM_AUTHFAIL; + + return TPM_SUCCESS; +} + + +uint32_t tpm_terminate_auth_session(uint32_t auth_handle) +{ + const uint8_t command[18] = { + 0x00, 0xc1, /* TPM_TAG */ + 0x00, 0x00, 0x00, 0x00, /* parameter size */ + 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */ + 0x00, 0x00, 0x00, 0x00, /* TPM_HANDLE */ + 0x00, 0x00, 0x00, 0x02, /* TPM_RESSOURCE_TYPE */ + }; + const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH; + uint8_t request[COMMAND_BUFFER_SIZE]; + + if (pack_byte_string(request, sizeof(request), "sd", + 0, command, sizeof(command), + req_handle_offset, auth_handle)) + return TPM_LIB_ERROR; + if (oiap_session.valid && oiap_session.handle == auth_handle) + oiap_session.valid = 0; + + return tpm_sendrecv_command(request, NULL, NULL); +} + +uint32_t tpm_end_oiap(void) +{ + uint32_t err = TPM_SUCCESS; + if (oiap_session.valid) + err = tpm_terminate_auth_session(oiap_session.handle); + return err; +} + +uint32_t tpm_oiap(uint32_t *auth_handle) +{ + const uint8_t command[10] = { + 0x00, 0xc1, /* TPM_TAG */ + 0x00, 0x00, 0x00, 0x0a, /* parameter size */ + 0x00, 0x00, 0x00, 0x0a, /* TPM_COMMAND_CODE */ + }; + const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH; + const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4; + uint8_t response[COMMAND_BUFFER_SIZE]; + size_t response_length = sizeof(response); + uint32_t err; + + if (oiap_session.valid) + tpm_terminate_auth_session(oiap_session.handle); + + err = tpm_sendrecv_command(command, response, &response_length); + if (err) + return err; + if (unpack_byte_string(response, response_length, "ds", + res_auth_handle_offset, &oiap_session.handle, + res_nonce_even_offset, &oiap_session.nonce_even, + (uint32_t)DIGEST_LENGTH)) + return TPM_LIB_ERROR; + oiap_session.valid = 1; + if (auth_handle) + *auth_handle = oiap_session.handle; + return 0; +} + +uint32_t tpm_load_key2_oiap(uint32_t parent_handle, + const void *key, size_t key_length, + const void *parent_key_usage_auth, + uint32_t *key_handle) +{ + const uint8_t command[14] = { + 0x00, 0xc2, /* TPM_TAG */ + 0x00, 0x00, 0x00, 0x00, /* parameter size */ + 0x00, 0x00, 0x00, 0x41, /* TPM_COMMAND_CODE */ + 0x00, 0x00, 0x00, 0x00, /* parent handle */ + }; + const size_t req_size_offset = 2; + const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH; + const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4; + const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH; + uint8_t request[sizeof(command) + TPM_KEY12_MAX_LENGTH + + TPM_REQUEST_AUTH_LENGTH]; + uint8_t response[COMMAND_BUFFER_SIZE]; + size_t response_length = sizeof(response); + uint32_t err; + + if (!oiap_session.valid) { + err = tpm_oiap(NULL); + if (err) + return err; + } + if (pack_byte_string(request, sizeof(request), "sdds", + 0, command, sizeof(command), + req_size_offset, + sizeof(command) + key_length + TPM_REQUEST_AUTH_LENGTH, + req_parent_handle_offset, parent_handle, + req_key_offset, key, key_length + )) + return TPM_LIB_ERROR; + + err = create_request_auth(request, sizeof(command) + key_length, 4, + &oiap_session, + request + sizeof(command) + key_length, + parent_key_usage_auth); + if (err) + return err; + err = tpm_sendrecv_command(request, response, &response_length); + if (err) { + if (err == TPM_AUTHFAIL) + oiap_session.valid = 0; + return err; + } + + err = verify_response_auth(0x00000041, response, + response_length - TPM_RESPONSE_AUTH_LENGTH, + 4, &oiap_session, + response + response_length - TPM_RESPONSE_AUTH_LENGTH, + parent_key_usage_auth); + if (err) + return err; + + if (key_handle) { + if (unpack_byte_string(response, response_length, "d", + res_handle_offset, key_handle)) + return TPM_LIB_ERROR; + } + + return 0; +} + +uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth, + void *pubkey, size_t *pubkey_len) +{ + const uint8_t command[14] = { + 0x00, 0xc2, /* TPM_TAG */ + 0x00, 0x00, 0x00, 0x00, /* parameter size */ + 0x00, 0x00, 0x00, 0x21, /* TPM_COMMAND_CODE */ + 0x00, 0x00, 0x00, 0x00, /* key handle */ + }; + const size_t req_size_offset = 2; + const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH; + const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH; + uint8_t request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH]; + uint8_t response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH + + TPM_RESPONSE_AUTH_LENGTH]; + size_t response_length = sizeof(response); + uint32_t err; + + if (!oiap_session.valid) { + err = tpm_oiap(NULL); + if (err) + return err; + } + if (pack_byte_string(request, sizeof(request), "sdd", + 0, command, sizeof(command), + req_size_offset, + (uint32_t)(sizeof(command) + TPM_REQUEST_AUTH_LENGTH), + req_key_handle_offset, key_handle + )) + return TPM_LIB_ERROR; + err = create_request_auth(request, sizeof(command), 4, &oiap_session, + request + sizeof(command), usage_auth); + if (err) + return err; + err = tpm_sendrecv_command(request, response, &response_length); + if (err) { + if (err == TPM_AUTHFAIL) + oiap_session.valid = 0; + return err; + } + err = verify_response_auth(0x00000021, response, + response_length - TPM_RESPONSE_AUTH_LENGTH, + 0, &oiap_session, + response + response_length - TPM_RESPONSE_AUTH_LENGTH, + usage_auth); + if (err) + return err; + + if (pubkey) { + if ((response_length - TPM_RESPONSE_HEADER_LENGTH + - TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len) + return TPM_LIB_ERROR; + *pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH + - TPM_RESPONSE_AUTH_LENGTH; + memcpy(pubkey, response + res_pubkey_offset, + response_length - TPM_RESPONSE_HEADER_LENGTH + - TPM_RESPONSE_AUTH_LENGTH); + } + + return 0; +} + +#endif /* CONFIG_TPM_AUTH_SESSIONS */