diff options
author | Yicheng Li <yichengli@chromium.org> | 2019-09-24 16:18:38 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-10-09 21:30:42 +0000 |
commit | cb860c94d5b562c6f6833dd60bcf2920713bb9b9 (patch) | |
tree | ed5d2f024083a672773d64bfbba4a902281abbdd | |
parent | 18bc5eba805ae2bb140373ba763acd75e20f8805 (diff) | |
download | chrome-ec-cb860c94d5b562c6f6833dd60bcf2920713bb9b9.tar.gz |
fpsensor: Enable positive match secret and positive match salt on enrollment.
On enrollment success, generate new positive_match_salt and send it as part
of the encrypted blob. Also enable positive match secret to be read.
The positive_match_salt is used to derive positive_match_secret and
is different from the encryption salt for encrypting the templates.
The positive_match_salt needs to be sent to biod and stored with templates
because it needs to be re-uploaded to FPMCU the next time the user logs in.
The positive match secret needs to be sent to biod so that it knows what to
compare against at a match.
BRANCH=nocturne
BUG=chromium:927095
TEST=make -j buildall
TEST=tested enrollment, matching and multifinger on DUT nocturne
Change-Id: I3e44a972ee17c5a93bddd52340f8f2249836463a
Signed-off-by: Yicheng Li <yichengli@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1828058
Reviewed-by: Nicolas Norvez <norvez@chromium.org>
Reviewed-by: Tom Hughes <tomhughes@chromium.org>
-rw-r--r-- | common/fpsensor/fpsensor.c | 87 | ||||
-rw-r--r-- | common/fpsensor/fpsensor_crypto.c | 3 | ||||
-rw-r--r-- | common/fpsensor/fpsensor_state.c | 2 | ||||
-rw-r--r-- | include/ec_commands.h | 9 | ||||
-rw-r--r-- | include/fpsensor_state.h | 4 | ||||
-rw-r--r-- | test/fpsensor.c | 6 |
6 files changed, 88 insertions, 23 deletions
diff --git a/common/fpsensor/fpsensor.c b/common/fpsensor/fpsensor.c index 5377b526b6..486d2f4d12 100644 --- a/common/fpsensor/fpsensor.c +++ b/common/fpsensor/fpsensor.c @@ -97,6 +97,10 @@ static uint32_t fp_process_enroll(void) int percent = 0; int res; + if (template_newly_enrolled != FP_NO_SUCH_TEMPLATE) + CPRINTS("Warning: previously enrolled template has not been " + "read yet."); + /* begin/continue enrollment */ CPRINTS("[%d]Enrolling ...", templ_valid); res = fp_finger_enroll(fp_buffer, &percent); @@ -110,10 +114,9 @@ static uint32_t fp_process_enroll(void) if (res) { res = EC_MKBP_FP_ERR_ENROLL_INTERNAL; } else { - init_trng(); - rand_bytes(fp_positive_match_salt[templ_valid], - FP_POSITIVE_MATCH_SALT_BYTES); - exit_trng(); + template_newly_enrolled = templ_valid; + fp_enable_positive_match_secret(templ_valid, + &positive_match_secret_state); templ_valid++; } sensor_mode &= ~FP_MODE_ENROLL_SESSION; @@ -407,6 +410,13 @@ static enum ec_status fp_command_frame(struct host_cmd_handler_args *args) if (!offset) { /* Host has requested the first chunk, do the encryption. */ timestamp_t now = get_time(); + /* Encrypted template is after the metadata. */ + uint8_t *encrypted_template = fp_enc_buffer + sizeof(*enc_info); + /* Positive match salt is after the template. */ + uint8_t *positive_match_salt = + encrypted_template + sizeof(fp_template[0]); + size_t encrypted_blob_size = sizeof(fp_template[0]) + + sizeof(fp_positive_match_salt[0]); /* b/114160734: Not more than 1 encrypted message per second. */ if (!timestamp_expired(encryption_deadline, &now)) @@ -414,23 +424,50 @@ static enum ec_status fp_command_frame(struct host_cmd_handler_args *args) encryption_deadline.val = now.val + (1 * SECOND); memset(fp_enc_buffer, 0, sizeof(fp_enc_buffer)); - /* The beginning of the buffer contains nonce/salt/tag. */ + /* + * The beginning of the buffer contains nonce, encryption_salt + * and tag. + */ enc_info = (void *)fp_enc_buffer; enc_info->struct_version = FP_TEMPLATE_FORMAT_VERSION; init_trng(); rand_bytes(enc_info->nonce, FP_CONTEXT_NONCE_BYTES); - rand_bytes(enc_info->salt, FP_CONTEXT_SALT_BYTES); + rand_bytes(enc_info->encryption_salt, + FP_CONTEXT_ENCRYPTION_SALT_BYTES); exit_trng(); - ret = derive_encryption_key(key, enc_info->salt); + if (fgr == template_newly_enrolled) { + /* + * Newly enrolled templates need new positive match + * salt, new positive match secret and new validation + * value. + */ + template_newly_enrolled = FP_NO_SUCH_TEMPLATE; + init_trng(); + rand_bytes(fp_positive_match_salt[fgr], + FP_POSITIVE_MATCH_SALT_BYTES); + exit_trng(); + } + + ret = derive_encryption_key(key, enc_info->encryption_salt); if (ret != EC_SUCCESS) { CPRINTS("fgr%d: Failed to derive key", fgr); return EC_RES_UNAVAILABLE; } - ret = aes_gcm_encrypt(key, SBP_ENC_KEY_LEN, fp_template[fgr], - fp_enc_buffer + sizeof(*enc_info), - sizeof(fp_template[0]), + /* + * Copy the payload to |fp_enc_buffer| where it will be + * encrypted in-place. + */ + memcpy(encrypted_template, fp_template[fgr], + sizeof(fp_template[0])); + memcpy(positive_match_salt, fp_positive_match_salt[fgr], + sizeof(fp_positive_match_salt[0])); + + /* Encrypt the secret blob in-place. */ + ret = aes_gcm_encrypt(key, SBP_ENC_KEY_LEN, encrypted_template, + encrypted_template, + encrypted_blob_size, enc_info->nonce, FP_CONTEXT_NONCE_BYTES, enc_info->tag, FP_CONTEXT_TAG_BYTES); always_memset(key, 0, sizeof(key)); @@ -471,6 +508,10 @@ DECLARE_HOST_COMMAND(EC_CMD_FP_STATS, fp_command_stats, EC_VER_MASK(0)); static int validate_template_format( struct ec_fp_template_encryption_metadata *enc_info) { + if (enc_info->struct_version == 3 && FP_TEMPLATE_FORMAT_VERSION == 4) + /* The host requested migration to v4. */ + return EC_RES_SUCCESS; + if (enc_info->struct_version != FP_TEMPLATE_FORMAT_VERSION) { CPRINTS("Invalid template format %d", enc_info->struct_version); return EC_RES_INVALID_PARAM; @@ -503,27 +544,39 @@ static enum ec_status fp_command_template(struct host_cmd_handler_args *args) memcpy(&fp_enc_buffer[offset], params->data, size); if (xfer_complete) { + /* Encrypted template is after the metadata. */ + uint8_t *encrypted_template = fp_enc_buffer + sizeof(*enc_info); + /* Positive match salt is after the template. */ + uint8_t *positive_match_salt = + encrypted_template + sizeof(fp_template[0]); + size_t encrypted_blob_size = sizeof(fp_template[0]) + + sizeof(fp_positive_match_salt[0]); + /* * The complete encrypted template has been received, start * decryption. */ fp_clear_finger_context(idx); - /* The beginning of the buffer contains nonce/salt/tag. */ + /* + * The beginning of the buffer contains nonce, encryption_salt + * and tag. + */ enc_info = (void *)fp_enc_buffer; ret = validate_template_format(enc_info); if (ret != EC_RES_SUCCESS) { CPRINTS("fgr%d: Template format not supported", idx); return EC_RES_INVALID_PARAM; } - ret = derive_encryption_key(key, enc_info->salt); + ret = derive_encryption_key(key, enc_info->encryption_salt); if (ret != EC_SUCCESS) { CPRINTS("fgr%d: Failed to derive key", idx); return EC_RES_UNAVAILABLE; } - ret = aes_gcm_decrypt(key, SBP_ENC_KEY_LEN, fp_template[idx], - fp_enc_buffer + sizeof(*enc_info), - sizeof(fp_template[0]), + /* Decrypt the secret blob in-place. */ + ret = aes_gcm_decrypt(key, SBP_ENC_KEY_LEN, encrypted_template, + encrypted_template, + encrypted_blob_size, enc_info->nonce, FP_CONTEXT_NONCE_BYTES, enc_info->tag, FP_CONTEXT_TAG_BYTES); always_memset(key, 0, sizeof(key)); @@ -534,6 +587,10 @@ static enum ec_status fp_command_template(struct host_cmd_handler_args *args) return EC_RES_UNAVAILABLE; } templ_valid++; + memcpy(fp_template[idx], encrypted_template, + sizeof(fp_template[0])); + memcpy(fp_positive_match_salt[idx], positive_match_salt, + sizeof(fp_positive_match_salt[0])); } return EC_RES_SUCCESS; diff --git a/common/fpsensor/fpsensor_crypto.c b/common/fpsensor/fpsensor_crypto.c index 4a2b31c411..73d7aca681 100644 --- a/common/fpsensor/fpsensor_crypto.c +++ b/common/fpsensor/fpsensor_crypto.c @@ -200,7 +200,8 @@ int derive_encryption_key(uint8_t *out_key, const uint8_t *salt) } /* "Extract step of HKDF. */ - hkdf_extract(prk, salt, FP_CONTEXT_SALT_BYTES, ikm, sizeof(ikm)); + hkdf_extract(prk, salt, FP_CONTEXT_ENCRYPTION_SALT_BYTES, ikm, + sizeof(ikm)); always_memset(ikm, 0, sizeof(ikm)); /* diff --git a/common/fpsensor/fpsensor_state.c b/common/fpsensor/fpsensor_state.c index 82a548b8af..ec4ddb4fd9 100644 --- a/common/fpsensor/fpsensor_state.c +++ b/common/fpsensor/fpsensor_state.c @@ -38,6 +38,8 @@ struct positive_match_secret_state positive_match_secret_state = { .deadline.val = 0, }; +/* Index of the last enrolled but not retrieved template. */ +int8_t template_newly_enrolled = FP_NO_SUCH_TEMPLATE; /* Number of used templates */ uint32_t templ_valid; /* Bitmap of the templates with local modifications */ diff --git a/include/ec_commands.h b/include/ec_commands.h index f28b9ea3e2..6e5e3b6990 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -5916,15 +5916,18 @@ struct ec_response_fp_info { #define FP_FRAME_OFFSET_MASK 0x0FFFFFFF /* Version of the format of the encrypted templates. */ -#define FP_TEMPLATE_FORMAT_VERSION 3 +#define FP_TEMPLATE_FORMAT_VERSION 4 /* Constants for encryption parameters */ #define FP_CONTEXT_NONCE_BYTES 12 #define FP_CONTEXT_USERID_WORDS (32 / sizeof(uint32_t)) #define FP_CONTEXT_TAG_BYTES 16 -#define FP_CONTEXT_SALT_BYTES 16 +#define FP_CONTEXT_ENCRYPTION_SALT_BYTES 16 #define FP_CONTEXT_TPM_BYTES 32 +/* Constants for positive match parameters. */ +#define FP_POSITIVE_MATCH_SALT_BYTES 16 + struct ec_fp_template_encryption_metadata { /* * Version of the structure format (N=3). @@ -5937,7 +5940,7 @@ struct ec_fp_template_encryption_metadata { * a different one is used for every message. */ uint8_t nonce[FP_CONTEXT_NONCE_BYTES]; - uint8_t salt[FP_CONTEXT_SALT_BYTES]; + uint8_t encryption_salt[FP_CONTEXT_ENCRYPTION_SALT_BYTES]; uint8_t tag[FP_CONTEXT_TAG_BYTES]; }; diff --git a/include/fpsensor_state.h b/include/fpsensor_state.h index 624318837d..4047fca74d 100644 --- a/include/fpsensor_state.h +++ b/include/fpsensor_state.h @@ -38,8 +38,8 @@ #define SBP_ENC_KEY_LEN 16 #define FP_ALGORITHM_ENCRYPTED_TEMPLATE_SIZE \ (FP_ALGORITHM_TEMPLATE_SIZE + \ + FP_POSITIVE_MATCH_SALT_BYTES + \ sizeof(struct ec_fp_template_encryption_metadata)) -#define FP_POSITIVE_MATCH_SALT_BYTES 16 /* Events for the FPSENSOR task */ #define TASK_EVENT_SENSOR_IRQ TASK_EVENT_CUSTOM_BIT(0) @@ -63,6 +63,8 @@ extern uint8_t fp_enc_buffer[FP_ALGORITHM_ENCRYPTED_TEMPLATE_SIZE]; /* Salt used in derivation of positive match secret. */ extern uint8_t fp_positive_match_salt [FP_MAX_FINGER_COUNT][FP_POSITIVE_MATCH_SALT_BYTES]; +/* Index of the last enrolled but not retrieved template. */ +extern int8_t template_newly_enrolled; /* Number of used templates */ extern uint32_t templ_valid; /* Bitmap of the templates with local modifications */ diff --git a/test/fpsensor.c b/test/fpsensor.c index 962295afbf..81441295ae 100644 --- a/test/fpsensor.c +++ b/test/fpsensor.c @@ -276,7 +276,7 @@ test_static int test_hkdf_expand(void) test_static int test_derive_encryption_key_failure_seed_not_set(void) { static uint8_t unused_key[SBP_ENC_KEY_LEN]; - static const uint8_t unused_salt[FP_CONTEXT_SALT_BYTES] = { 0 }; + static const uint8_t unused_salt[FP_CONTEXT_ENCRYPTION_SALT_BYTES]; /* GIVEN that the TPM seed is not set. */ if (fp_tpm_seed_is_set()) { @@ -373,7 +373,7 @@ test_static int test_derive_encryption_key(void) test_static int test_derive_encryption_key_failure_rollback_fail(void) { static uint8_t unused_key[SBP_ENC_KEY_LEN]; - static const uint8_t unused_salt[FP_CONTEXT_SALT_BYTES] = { 0 }; + static const uint8_t unused_salt[FP_CONTEXT_ENCRYPTION_SALT_BYTES]; /* GIVEN that reading the rollback secret will fail. */ rollback_should_fail = 1; @@ -473,7 +473,7 @@ test_static int test_derive_positive_match_secret_fail_salt_trivial(void) { static uint8_t output[FP_POSITIVE_MATCH_SECRET_BYTES]; /* GIVEN that the salt is trivial. */ - static const uint8_t salt[FP_CONTEXT_SALT_BYTES] = { 0 }; + static const uint8_t salt[FP_CONTEXT_ENCRYPTION_SALT_BYTES] = { 0 }; /* THEN deriving positive match secret will fail. */ TEST_ASSERT(derive_positive_match_secret(output, salt) |