| /* Copyright 2018 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* Shared types between Cr50 and the AP side code. */ |
| |
| #ifndef __CROS_EC_INCLUDE_PINWEAVER_TYPES_H |
| #define __CROS_EC_INCLUDE_PINWEAVER_TYPES_H |
| |
| #include <stdint.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #define PW_PACKED __packed |
| |
| #define PW_PROTOCOL_VERSION 1 |
| #define PW_LEAF_MAJOR_VERSION 0 |
| /* The change from version zero is the addition of valid_pcr_value metadata */ |
| #define PW_LEAF_MINOR_VERSION 1 |
| |
| #define PW_MAX_MESSAGE_SIZE (2048 - 12 /* sizeof(struct tpm_cmd_header) */) |
| |
| /* The block size of encryption used for wrapped_leaf_data_t. */ |
| #define PW_WRAP_BLOCK_SIZE 16 |
| |
| #define PW_ALIGN_TO_WRD __aligned(4) |
| |
| #define PW_ALIGN_TO_BLK __aligned(PW_WRAP_BLOCK_SIZE) |
| |
| enum pw_error_codes_enum { |
| PW_ERR_VERSION_MISMATCH = 0x10000, /* EC_ERROR_INTERNAL_FIRST */ |
| PW_ERR_TREE_INVALID, |
| PW_ERR_LENGTH_INVALID, |
| PW_ERR_TYPE_INVALID, |
| PW_ERR_BITS_PER_LEVEL_INVALID, |
| PW_ERR_HEIGHT_INVALID, |
| PW_ERR_LABEL_INVALID, |
| PW_ERR_DELAY_SCHEDULE_INVALID, |
| PW_ERR_PATH_AUTH_FAILED, |
| PW_ERR_LEAF_VERSION_MISMATCH, |
| PW_ERR_HMAC_AUTH_FAILED, |
| PW_ERR_LOWENT_AUTH_FAILED, |
| PW_ERR_RESET_AUTH_FAILED, |
| PW_ERR_CRYPTO_FAILURE, |
| PW_ERR_RATE_LIMIT_REACHED, |
| PW_ERR_ROOT_NOT_FOUND, |
| PW_ERR_NV_EMPTY, |
| PW_ERR_NV_LENGTH_MISMATCH, |
| PW_ERR_NV_VERSION_MISMATCH, |
| PW_ERR_PCR_NOT_MATCH, |
| }; |
| |
| /* Represents the log2(fan out) of a tree. */ |
| struct PW_PACKED bits_per_level_t { |
| uint8_t v; |
| }; |
| |
| /* Represent the height of a tree. */ |
| struct PW_PACKED height_t { |
| uint8_t v; |
| }; |
| |
| /* Represents a child index of a node in a tree. */ |
| struct PW_PACKED index_t { |
| uint8_t v; |
| }; |
| |
| /* Represents the child index for each level of a tree along a path to a leaf. |
| * It is a Little-endian unsigned integer with the following value (MSB->LSB) |
| * | Zero padding | 1st level index | ... | leaf index |, |
| * where each index is represented by bits_per_level bits. |
| */ |
| struct PW_PACKED label_t { |
| uint64_t v; |
| }; |
| |
| /* Represents a count of failed login attempts. This is capped at UINT32_MAX. */ |
| struct PW_PACKED attempt_count_t { |
| uint32_t v; |
| }; |
| |
| /* Represents a notion of time. */ |
| struct PW_PACKED pw_timestamp_t { |
| /* Number of boots. This is used to track if Cr50 has rebooted since |
| * timer_value was recorded. |
| */ |
| uint32_t boot_count; |
| /* Seconds since boot. */ |
| uint64_t timer_value; |
| }; |
| |
| /* Represents a time interval in seconds. |
| * |
| * This only needs to be sufficiently large to represent the longest time |
| * between allowed attempts. |
| */ |
| struct PW_PACKED time_diff_t { |
| uint32_t v; |
| }; |
| #define PW_BLOCK_ATTEMPTS UINT32_MAX |
| |
| /* Number of bytes required for a hash or hmac value in the merkle tree. */ |
| #define PW_HASH_SIZE 32 |
| |
| /* Represents a single entry in a delay schedule table. */ |
| struct PW_PACKED delay_schedule_entry_t { |
| struct attempt_count_t attempt_count; |
| struct time_diff_t time_diff; |
| }; |
| |
| /* Represents a set of PCR values hashed into a single digest. This is a |
| * criterion that can be added to a leaf. A leaf is valid only if at least one |
| * of the valid_pcr_value_t criteria it contains is satisfied. |
| */ |
| struct PW_PACKED valid_pcr_value_t { |
| /* The set of PCR indexes that have to pass the validation. */ |
| uint8_t bitmask[2]; |
| /* The hash digest of the PCR values contained in the bitmask */ |
| uint8_t digest[32]; |
| }; |
| |
| /* Represents the number of entries in the delay schedule table which can be |
| * used to determine the next time an authentication attempt can be made. |
| */ |
| #define PW_SCHED_COUNT 16 |
| |
| /* Represents the maximum number of criteria for valid PCR values. |
| */ |
| #define PW_MAX_PCR_CRITERIA_COUNT 2 |
| |
| /* Number of bytes required to store a secret. |
| */ |
| #define PW_SECRET_SIZE 32 |
| |
| struct PW_PACKED leaf_version_t { |
| /* minor comes first so this struct will be compatibile with uint32_t |
| * comparisons for little endian to make version comparisons easier. |
| * |
| * Changes to minor versions are allowed to add new fields, but not |
| * remove existing fields, and they are allowed to be interpreted by |
| * previous versions---any extra fields are truncated. |
| * |
| * Leafs will reject future major versions assuming they are |
| * incompatible, so fields in struct leaf_public_data_t and |
| * struct leaf_sensitive_data_t may be removed for new major versions. |
| * Upgrades across major versions will require explicit logic to |
| * map the old struct to the new struct or vice versa. |
| */ |
| uint16_t minor; |
| uint16_t major; |
| }; |
| |
| /* Do not change this within the same PW_LEAF_MAJOR_VERSION. */ |
| struct PW_PACKED leaf_header_t { |
| /* Always have leaf_version at the beginning of |
| * struct wrapped_leaf_data_t to maintain preditable behavior across |
| * versions. |
| */ |
| struct leaf_version_t leaf_version; |
| uint16_t pub_len; |
| uint16_t sec_len; |
| }; |
| |
| /* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */ |
| /* Unencrypted part of the leaf data. |
| */ |
| struct PW_PACKED leaf_public_data_t { |
| struct label_t label; |
| struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT]; |
| |
| /* State used to rate limit. */ |
| struct pw_timestamp_t timestamp; |
| struct attempt_count_t attempt_count; |
| struct valid_pcr_value_t valid_pcr_criteria[PW_MAX_PCR_CRITERIA_COUNT]; |
| }; |
| |
| /* Represents a struct of unknown length to be imported to process a request. */ |
| struct PW_PACKED unimported_leaf_data_t { |
| /* This is first so that head.leaf_version will be the first field |
| * in the struct to make handling different struct versions easier. |
| */ |
| struct leaf_header_t head; |
| /* Covers .head, .iv, and .payload (excluding path_hashes) */ |
| uint8_t hmac[PW_HASH_SIZE]; |
| uint8_t iv[PW_WRAP_BLOCK_SIZE]; |
| /* This field is treated as having a zero size by the compiler so the |
| * actual size needs to be added to the size of this struct. This allows |
| * for forward compatibility using the pub_len and sec_len fields in the |
| * header. |
| * |
| * Has following layout: |
| * Required: |
| * uint8_t pub_data[head.pub_len]; |
| * uint8_t ciphter_text[head.sec_len]; |
| * |
| * For Requests only: |
| * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE]; |
| */ |
| uint8_t payload[]; |
| }; |
| |
| /******************************************************************************/ |
| /* Message structs |
| * |
| * The message format is a pw_request_header_t followed by the data |
| */ |
| |
| enum pw_message_type_enum { |
| PW_MT_INVALID = 0, |
| |
| /* Request / "Question" types. */ |
| PW_RESET_TREE = 1, |
| PW_INSERT_LEAF, |
| PW_REMOVE_LEAF, |
| PW_TRY_AUTH, |
| PW_RESET_AUTH, |
| PW_GET_LOG, |
| PW_LOG_REPLAY, |
| }; |
| |
| struct PW_PACKED pw_message_type_t { |
| uint8_t v; |
| }; |
| |
| struct PW_PACKED pw_request_header_t { |
| uint8_t version; |
| struct pw_message_type_t type; |
| uint16_t data_length; |
| }; |
| |
| struct PW_PACKED pw_response_header_t { |
| uint8_t version; |
| uint16_t data_length; /* Does not include the header. */ |
| uint32_t result_code; |
| uint8_t root[PW_HASH_SIZE]; |
| }; |
| |
| struct PW_PACKED pw_request_reset_tree_t { |
| struct bits_per_level_t bits_per_level; |
| struct height_t height; |
| }; |
| |
| /* This is only used for parsing incoming data of version 0:0 */ |
| struct PW_PACKED pw_request_insert_leaf00_t { |
| struct label_t label; |
| struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT]; |
| uint8_t low_entropy_secret[PW_SECRET_SIZE]; |
| uint8_t high_entropy_secret[PW_SECRET_SIZE]; |
| uint8_t reset_secret[PW_SECRET_SIZE]; |
| /* This is a variable length field because it size is determined at |
| * runtime based on the chosen tree parameters. Its size is treated as |
| * zero by the compiler so the computed size needs to be added to the |
| * size of this struct in order to determine the actual size. This field |
| * has the form: |
| * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE]; |
| */ |
| uint8_t path_hashes[][PW_HASH_SIZE]; |
| }; |
| |
| struct PW_PACKED pw_request_insert_leaf_t { |
| struct label_t label; |
| struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT]; |
| uint8_t low_entropy_secret[PW_SECRET_SIZE]; |
| uint8_t high_entropy_secret[PW_SECRET_SIZE]; |
| uint8_t reset_secret[PW_SECRET_SIZE]; |
| struct valid_pcr_value_t valid_pcr_criteria[PW_MAX_PCR_CRITERIA_COUNT]; |
| /* This is a variable length field because it size is determined at |
| * runtime based on the chosen tree parameters. Its size is treated as |
| * zero by the compiler so the computed size needs to be added to the |
| * size of this struct in order to determine the actual size. This field |
| * has the form: |
| * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE]; |
| */ |
| uint8_t path_hashes[][PW_HASH_SIZE]; |
| }; |
| |
| struct PW_PACKED pw_response_insert_leaf_t { |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_request_remove_leaf_t { |
| struct label_t leaf_location; |
| uint8_t leaf_hmac[PW_HASH_SIZE]; |
| /* See (struct pw_request_insert_leaf_t).path_hashes. */ |
| uint8_t path_hashes[][PW_HASH_SIZE]; |
| }; |
| |
| struct PW_PACKED pw_request_try_auth_t { |
| uint8_t low_entropy_secret[PW_SECRET_SIZE]; |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| /* This is only used to send response data of version 0:0 */ |
| struct PW_PACKED pw_response_try_auth00_t { |
| /* Valid for the PW_ERR_RATE_LIMIT_REACHED return code only. */ |
| struct time_diff_t seconds_to_wait; |
| /* Valid for the EC_SUCCESS return code only. */ |
| uint8_t high_entropy_secret[PW_SECRET_SIZE]; |
| /* Valid for the PW_ERR_LOWENT_AUTH_FAILED and EC_SUCCESS return codes. |
| */ |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_response_try_auth_t { |
| /* Valid for the PW_ERR_RATE_LIMIT_REACHED return code only. */ |
| struct time_diff_t seconds_to_wait; |
| /* Valid for the EC_SUCCESS return code only. */ |
| uint8_t high_entropy_secret[PW_SECRET_SIZE]; |
| /* Valid for the EC_SUCCESS return code only. */ |
| uint8_t reset_secret[PW_SECRET_SIZE]; |
| /* Valid for the PW_ERR_LOWENT_AUTH_FAILED and EC_SUCCESS return codes. |
| */ |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_request_reset_auth_t { |
| uint8_t reset_secret[PW_SECRET_SIZE]; |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_response_reset_auth_t { |
| uint8_t high_entropy_secret[PW_SECRET_SIZE]; |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_request_get_log_t { |
| /* The root on the CrOS side that needs to be brought back in sync with |
| * the root on Cr50. If this doesn't match a log entry, the entire log |
| * is returned. |
| */ |
| uint8_t root[PW_HASH_SIZE]; |
| }; |
| |
| struct PW_PACKED pw_request_log_replay_t { |
| /* The root hash after the desired log event. |
| * The log entry that matches this hash contains all the necessary |
| * data to update wrapped_leaf_data |
| */ |
| uint8_t log_root[PW_HASH_SIZE]; |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_response_log_replay_t { |
| struct unimported_leaf_data_t unimported_leaf_data; |
| }; |
| |
| struct PW_PACKED pw_get_log_entry_t { |
| /* The root hash after this operation. */ |
| uint8_t root[PW_HASH_SIZE]; |
| /* The label of the leaf that was operated on. */ |
| struct label_t label; |
| /* The type of operation. This should be one of |
| * PW_INSERT_LEAF, |
| * PW_REMOVE_LEAF, |
| * PW_TRY_AUTH. |
| * |
| * Successful PW_RESET_AUTH events are included |
| */ |
| struct pw_message_type_t type; |
| /* Type specific fields. */ |
| union { |
| /* PW_INSERT_LEAF */ |
| uint8_t leaf_hmac[PW_HASH_SIZE]; |
| /* PW_REMOVE_LEAF */ |
| /* PW_TRY_AUTH */ |
| struct PW_PACKED { |
| struct pw_timestamp_t timestamp; |
| int32_t return_code; |
| }; |
| }; |
| }; |
| |
| struct PW_PACKED pw_request_t { |
| struct pw_request_header_t header; |
| union { |
| struct pw_request_reset_tree_t reset_tree; |
| struct pw_request_insert_leaf00_t insert_leaf00; |
| struct pw_request_insert_leaf_t insert_leaf; |
| struct pw_request_remove_leaf_t remove_leaf; |
| struct pw_request_try_auth_t try_auth; |
| struct pw_request_reset_auth_t reset_auth; |
| struct pw_request_get_log_t get_log; |
| struct pw_request_log_replay_t log_replay; |
| } data; |
| }; |
| |
| struct PW_PACKED pw_response_t { |
| struct pw_response_header_t header; |
| union { |
| |
| struct pw_response_insert_leaf_t insert_leaf; |
| struct pw_response_try_auth00_t try_auth00; |
| struct pw_response_try_auth_t try_auth; |
| struct pw_response_reset_auth_t reset_auth; |
| /* An array with as many entries as are present in the log up to |
| * the present time or will fit in the message. |
| */ |
| uint8_t get_log[0]; |
| struct pw_response_log_replay_t log_replay; |
| } data; |
| }; |
| |
| /* An explicit limit is set because struct unimported_leaf_data_t can have more |
| * than one variable length field so the max length for these fields needs to be |
| * defined so that meaningful parameter limits can be set to validate the tree |
| * parameters. |
| * |
| * 1024 was chosen because it is 1/2 of 2048 and allows for a maximum tree |
| * height of 10 for the default fan-out of 4. |
| */ |
| #define PW_MAX_PATH_SIZE 1024 |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* __CROS_EC_INCLUDE_PINWEAVER_TYPES_H */ |