diff options
author | Daisuke Nojiri <dnojiri@chromium.org> | 2017-07-07 09:49:36 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-07-13 19:45:57 -0700 |
commit | 4ec4975d90713b58557beca7ed2a94745d7476e3 (patch) | |
tree | b6f5cf7601f271944de4793cfd2392b5954659e5 | |
parent | 7630636a0fe8ceb2dbba2b175564a17900d175cf (diff) | |
download | chrome-ec-4ec4975d90713b58557beca7ed2a94745d7476e3.tar.gz |
vboot: Move common code under common/vboot
This patch moves the code which can be shared with other data
verification schemes (e.g. RWSIG) under common/vboot. It also
adds unit tests for it.
BUG=b:38462249
BRANCH=none
TEST=make run-vboot. Verify verification succeeds on Fizz.
Change-Id: Icab4d96dd2c154a12b01c41ebe9b46286b4b590e
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/563463
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | common/build.mk | 5 | ||||
-rw-r--r-- | common/vboot/common.c | 60 | ||||
-rw-r--r-- | common/vboot/vb21_lib.c | 44 | ||||
-rw-r--r-- | common/vboot/vboot.c (renamed from common/vboot.c) | 105 | ||||
-rw-r--r-- | include/vboot.h | 46 | ||||
-rw-r--r-- | power/intel_x86.c | 2 | ||||
-rw-r--r-- | test/build.mk | 2 | ||||
-rw-r--r-- | test/test_config.h | 20 | ||||
-rw-r--r-- | test/vboot.c | 142 | ||||
-rw-r--r-- | test/vboot.tasklist | 17 |
11 files changed, 347 insertions, 97 deletions
@@ -196,6 +196,7 @@ $(eval $(call get_sources,ro)) dirs=core/$(CORE) chip/$(CHIP) $(BDIR) common power test cts/common cts/$(CTS_MODULE) dirs+= private $(PDIR) +dirs+=$(shell find common -type d) dirs+=$(shell find driver -type d) common_dirs=util diff --git a/common/build.mk b/common/build.mk index 7907a87e30..9ceef96bc1 100644 --- a/common/build.mk +++ b/common/build.mk @@ -87,7 +87,8 @@ common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o common-$(CONFIG_RMA_AUTH)+=rma_auth.o common-$(CONFIG_RSA)+=rsa.o common-$(CONFIG_ROLLBACK)+=rollback.o -common-$(CONFIG_RWSIG)+=rwsig.o +common-$(CONFIG_RWSIG)+=rwsig.o vboot/common.o +common-$(CONFIG_RWSIG_TYPE_RWSIG)+=vboot/vb21_lib.o common-$(CONFIG_MATH_UTIL)+=math_util.o common-$(CONFIG_SHA1)+= sha1.o common-$(CONFIG_SHA256)+=sha256.o @@ -112,7 +113,7 @@ common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o usb_pd_policy.o common-$(CONFIG_USB_PD_LOGGING)+=pd_log.o common-$(CONFIG_USB_PD_TCPC)+=usb_pd_tcpc.o common-$(CONFIG_USB_UPDATE)+=usb_update.o update_fw.o -common-$(CONFIG_VBOOT_EC)+=vboot.o +common-$(CONFIG_VBOOT_EC)+=vboot/vboot.o common-$(CONFIG_VBOOT_HASH)+=sha256.o vboot_hash.o common-$(CONFIG_VSTORE)+=vstore.o common-$(CONFIG_WIRELESS)+=wireless.o diff --git a/common/vboot/common.c b/common/vboot/common.c new file mode 100644 index 0000000000..3a75a297e6 --- /dev/null +++ b/common/vboot/common.c @@ -0,0 +1,60 @@ +/* Copyright 2017 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. + */ + +#include "common.h" +#include "console.h" +#include "rsa.h" +#include "sha256.h" +#include "shared_mem.h" +#include "vboot.h" + +#define CPRINTS(format, args...) cprints(CC_VBOOT, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_VBOOT, format, ## args) + +int vboot_is_padding_valid(const uint8_t *data, uint32_t start, uint32_t end) +{ + const uint32_t *data32 = (const uint32_t *)data; + int i; + + if (start > end) + return EC_ERROR_INVAL; + + if (start % 4 || end % 4) + return EC_ERROR_INVAL; + + for (i = start / 4; i < end / 4; i++) { + if (data32[i] != 0xffffffff) + return EC_ERROR_INVAL; + } + + return EC_SUCCESS; +} + +int vboot_verify(const uint8_t *data, int len, + const struct rsa_public_key *key, const uint8_t *sig) +{ + struct sha256_ctx ctx; + uint8_t *hash; + uint32_t *workbuf; + int err = EC_SUCCESS; + + if (shared_mem_acquire(3 * RSANUMBYTES, (char **)&workbuf)) { + CPRINTS("Failed to allocate memory"); + return EC_ERROR_UNKNOWN; + } + + /* Compute hash of the RW firmware */ + SHA256_init(&ctx); + SHA256_update(&ctx, data, len); + hash = SHA256_final(&ctx); + + /* Verify the data */ + if (rsa_verify(key, sig, hash, workbuf) != 1) + err = EC_ERROR_INVAL; + + shared_mem_release(workbuf); + + return err; +} diff --git a/common/vboot/vb21_lib.c b/common/vboot/vb21_lib.c new file mode 100644 index 0000000000..11242a3038 --- /dev/null +++ b/common/vboot/vb21_lib.c @@ -0,0 +1,44 @@ +/* Copyright 2017 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. + */ + +/* + * Common utility APIs for vboot 2.1 + */ + +#include "common.h" +#include "rsa.h" +#include "rwsig.h" +#include "vb21_struct.h" +#include "vboot.h" + +int vb21_is_packed_key_valid(const struct vb21_packed_key *key) +{ + if (key->c.magic != VB21_MAGIC_PACKED_KEY) + return EC_ERROR_INVAL; + if (key->key_size != sizeof(struct rsa_public_key)) + return EC_ERROR_INVAL; + return EC_SUCCESS; +} + +int vb21_is_signature_valid(const struct vb21_signature *sig, + const struct vb21_packed_key *key) +{ + if (sig->c.magic != VB21_MAGIC_SIGNATURE) + return EC_ERROR_INVAL; + if (sig->sig_size != RSANUMBYTES) + return EC_ERROR_INVAL; + if (key->sig_alg != sig->sig_alg) + return EC_ERROR_INVAL; + if (key->hash_alg != sig->hash_alg) + return EC_ERROR_INVAL; + /* Sanity check signature offset and data size. */ + if (sig->sig_offset < sizeof(*sig)) + return EC_ERROR_INVAL; + if (sig->sig_offset + RSANUMBYTES > CONFIG_RW_SIG_SIZE) + return EC_ERROR_INVAL; + if (sig->data_size > CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE) + return EC_ERROR_INVAL; + return EC_SUCCESS; +} diff --git a/common/vboot.c b/common/vboot/vboot.c index 1e92572c5b..27c57b27c2 100644 --- a/common/vboot.c +++ b/common/vboot/vboot.c @@ -22,6 +22,7 @@ #include "vb21_struct.h" #define CPRINTS(format, args...) cprints(CC_VBOOT, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_VBOOT, format, ## args) enum vboot_ec_slot { VBOOT_EC_SLOT_A, @@ -47,93 +48,14 @@ static int is_low_power_ap_boot_supported(void) return 0; } -static int is_vb21_packed_key_valid(const struct vb21_packed_key *key) -{ - if (key->c.magic != VB21_MAGIC_PACKED_KEY) - return EC_ERROR_INVAL; - if (key->key_size != sizeof(struct rsa_public_key)) - return EC_ERROR_INVAL; - return EC_SUCCESS; -} - -static int is_vb21_signature_valid(const struct vb21_signature *sig, - const struct vb21_packed_key *key) -{ - if (sig->c.magic != VB21_MAGIC_SIGNATURE) - return EC_ERROR_INVAL; - if (sig->sig_size != RSANUMBYTES) - return EC_ERROR_INVAL; - if (key->sig_alg != sig->sig_alg) - return EC_ERROR_INVAL; - if (key->hash_alg != sig->hash_alg) - return EC_ERROR_INVAL; - /* Sanity check signature offset and data size. */ - if (sig->sig_offset < sizeof(*sig)) - return EC_ERROR_INVAL; - if (sig->sig_offset + RSANUMBYTES > CONFIG_RW_SIG_SIZE) - return EC_ERROR_INVAL; - if (sig->data_size > CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE) - return EC_ERROR_INVAL; - return EC_SUCCESS; -} - -static int is_padding_valid(const uint8_t *data, unsigned int start, int len) -{ - const uint32_t *data32 = (const uint32_t *)data; - int i; - - if (len < 0) - return EC_ERROR_INVAL; - - if ((start % 4) != 0 || (len % 4) != 0) - return EC_ERROR_INVAL; - - for (i = start/4; i < len/4; i++) { - if (data32[i] != 0xffffffff) - return EC_ERROR_INVAL; - } - - return EC_SUCCESS; -} - -static int vboot_verify(const uint8_t *data, int len, - const struct rsa_public_key *key, const uint8_t *sig) -{ - struct sha256_ctx ctx; - uint8_t *hash; - uint32_t *workbuf; - int err = EC_SUCCESS; - - if (shared_mem_acquire(3 * RSANUMBYTES, (char **)&workbuf)) { - CPRINTS("Failed to allocate memory"); - return EC_ERROR_UNKNOWN; - } - - /* Compute hash of the RW firmware */ - SHA256_init(&ctx); - SHA256_update(&ctx, data, len); - hash = SHA256_final(&ctx); - - /* Verify the data */ - if (rsa_verify(key, sig, hash, workbuf) != 1) { - CPRINTS("Invalid data"); - err = EC_ERROR_INVAL; - } - - shared_mem_release(workbuf); - - return err; -} - static int verify_slot(int slot) { - const uint8_t *data; const struct vb21_packed_key *vb21_key; const struct vb21_signature *vb21_sig; const struct rsa_public_key *key; const uint8_t *sig; + const uint8_t *data; int len; - int rv = EC_ERROR_INVAL; CPRINTS("Verifying RW_%c", slot == VBOOT_EC_SLOT_A ? 'A' : 'B'); @@ -141,9 +63,9 @@ static int verify_slot(int slot) CONFIG_MAPPED_STORAGE_BASE + CONFIG_EC_PROTECTED_STORAGE_OFF + CONFIG_RO_PUBKEY_STORAGE_OFF); - if (is_vb21_packed_key_valid(vb21_key)) { + if (vb21_is_packed_key_valid(vb21_key)) { CPRINTS("Invalid key"); - goto exit; + return EC_ERROR_INVAL; } key = (const struct rsa_public_key *) ((const uint8_t *)vb21_key + vb21_key->key_offset); @@ -166,28 +88,25 @@ static int verify_slot(int slot) CONFIG_RW_B_SIGN_STORAGE_OFF); } - if (is_vb21_signature_valid(vb21_sig, vb21_key)) { + if (vb21_is_signature_valid(vb21_sig, vb21_key)) { CPRINTS("Invalid signature"); - goto exit; + return EC_ERROR_INVAL; } sig = (const uint8_t *)vb21_sig + vb21_sig->sig_offset; len = vb21_sig->data_size; - if (is_padding_valid(data, len, - CONFIG_RW_SIZE - len - CONFIG_RW_SIG_SIZE)) { + if (vboot_is_padding_valid(data, len, + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)) { CPRINTS("Invalid padding"); - goto exit; + return EC_ERROR_INVAL; } if (vboot_verify(data, len, key, sig)) { CPRINTS("Invalid data"); - goto exit; + return EC_ERROR_INVAL; } - rv = EC_SUCCESS; - -exit: - return rv; + return EC_SUCCESS; } static int verify_and_jump(void) @@ -238,7 +157,7 @@ static int is_manual_recovery(void) return host_get_events() & EC_HOST_EVENT_KEYBOARD_RECOVERY; } -void vboot_ec(void) +void vboot_main(void) { int port = charge_manager_get_active_charge_port(); diff --git a/include/vboot.h b/include/vboot.h index ba13328544..14f7a8f13c 100644 --- a/include/vboot.h +++ b/include/vboot.h @@ -3,6 +3,50 @@ * found in the LICENSE file. */ +#include "common.h" +#include "vb21_struct.h" +#include "rsa.h" + +/** + * Validate key contents. + * + * @param key + * @return EC_SUCCESS or EC_ERROR_* + */ +int vb21_is_packed_key_valid(const struct vb21_packed_key *key); + +/** + * Validate signature contents. + * + * @param sig Signature to be validated. + * @param key Key to be used for validating <sig>. + * @return EC_SUCCESS or EC_ERROR_* + */ +int vb21_is_signature_valid(const struct vb21_signature *sig, + const struct vb21_packed_key *key); + +/** + * Check data region is filled with ones + * + * @param data Data to be validated. + * @param start Offset where validation starts. + * @param end Offset where validation ends. data[end] won't be checked. + * @return EC_SUCCESS or EC_ERROR_* + */ +int vboot_is_padding_valid(const uint8_t *data, uint32_t start, uint32_t end); + +/** + * Verify data by RSA signature + * + * @param data Data to be verified. + * @param len Number of bytes in <data>. + * @param key Key to be used for verification. + * @param sig Signature of <data> + * @return EC_SUCCESS or EC_ERROR_* + */ +int vboot_verify(const uint8_t *data, int len, + const struct rsa_public_key *key, const uint8_t *sig); + /** * Verify RW image and jump to it * @@ -12,4 +56,4 @@ * 3. Returns, requesting more power * 4. Returns, requesting recovery */ -void vboot_ec(void); +void vboot_main(void); diff --git a/power/intel_x86.c b/power/intel_x86.c index 5d08397912..90a8682d7c 100644 --- a/power/intel_x86.c +++ b/power/intel_x86.c @@ -280,7 +280,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state) */ { if (!system_can_boot_ap()) { - vboot_ec(); + vboot_main(); while (!system_can_boot_ap()) /* LED blinks as HOOK_TICK events trigger. * We can print percent & power as they diff --git a/test/build.mk b/test/build.mk index 2a1102748d..c54627d03c 100644 --- a/test/build.mk +++ b/test/build.mk @@ -79,6 +79,7 @@ test-list-host += usb_pd test-list-host += usb_pd_giveback test-list-host += utils test-list-host += utils_str +test-list-host += vboot test-list-host += x25519 endif @@ -130,4 +131,5 @@ usb_pd-y=usb_pd.o usb_pd_giveback-y=usb_pd.o utils-y=utils.o utils_str-y=utils_str.o +vboot-y=vboot.o x25519-y=x25519.o diff --git a/test/test_config.h b/test/test_config.h index b90aac3966..5c4807fd66 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -225,6 +225,26 @@ enum nvmem_vars { #define CONFIG_FLASH_NVMEM_VARS_USER_SIZE 600 #endif /* TEST_NVMEM_VARS */ +#ifdef TEST_VBOOT +#define CONFIG_RWSIG +#define CONFIG_SHA256 +#define CONFIG_RSA +#define CONFIG_RWSIG_TYPE_RWSIG +#define CONFIG_RW_B +#define CONFIG_RW_B_MEM_OFF CONFIG_RO_MEM_OFF +#undef CONFIG_RO_SIZE +#define CONFIG_RO_SIZE (CONFIG_FLASH_SIZE / 4) +#undef CONFIG_RW_SIZE +#define CONFIG_RW_SIZE CONFIG_RO_SIZE +#define CONFIG_RW_A_STORAGE_OFF CONFIG_RW_STORAGE_OFF +#define CONFIG_RW_B_STORAGE_OFF (CONFIG_RW_A_STORAGE_OFF + \ + CONFIG_RW_SIZE) +#define CONFIG_RW_A_SIGN_STORAGE_OFF (CONFIG_RW_A_STORAGE_OFF + \ + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE) +#define CONFIG_RW_B_SIGN_STORAGE_OFF (CONFIG_RW_B_STORAGE_OFF + \ + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE) +#endif + #ifdef TEST_X25519 #define CONFIG_CURVE25519 #endif /* TEST_X25519 */ diff --git a/test/vboot.c b/test/vboot.c new file mode 100644 index 0000000000..3d4be10308 --- /dev/null +++ b/test/vboot.c @@ -0,0 +1,142 @@ +/* Copyright 2017 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. + * + * Test vboot + */ + +#include <stdlib.h> +#include "common.h" +#include "rsa.h" +#include "test_util.h" +#include "vboot.h" +#include "rsa2048-3.h" +#include "rwsig.h" + +struct vboot_key { + struct vb21_packed_key vb21_key; + struct rsa_public_key key_data; +}; + +struct vboot_sig { + struct vb21_signature vb21_sig; + uint8_t sig_data[RSANUMBYTES]; +}; + +static void reset_data(struct vboot_key *k, struct vboot_sig *s) +{ + k->vb21_key.c.magic = VB21_MAGIC_PACKED_KEY; + k->vb21_key.key_offset = sizeof(struct vb21_packed_key); + k->vb21_key.key_size = sizeof(rsa_data); + memcpy(&k->key_data, rsa_data, sizeof(rsa_data)); + + s->vb21_sig.c.magic = VB21_MAGIC_SIGNATURE; + s->vb21_sig.sig_size = RSANUMBYTES; + s->vb21_sig.sig_offset = sizeof(struct vb21_signature); + s->vb21_sig.sig_alg = k->vb21_key.sig_alg; + s->vb21_sig.hash_alg = k->vb21_key.hash_alg; + s->vb21_sig.data_size = CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE - 32; + memcpy(s->sig_data, sig, sizeof(s->sig_data)); +} + +static int test_vboot(void) +{ + struct vboot_key k; + struct vboot_sig s; + uint8_t data[CONFIG_RW_SIZE]; + int len; + int err; + + /* Success */ + reset_data(&k, &s); + memset(data, 0xff, CONFIG_RW_SIZE); + err = vb21_is_packed_key_valid(&k.vb21_key); + TEST_ASSERT(err == EC_SUCCESS); + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_SUCCESS); + len = s.vb21_sig.data_size; + err = vboot_is_padding_valid(data, len, + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE); + TEST_ASSERT(err == EC_SUCCESS); + + /* Invalid magic */ + reset_data(&k, &s); + k.vb21_key.c.magic = VB21_MAGIC_SIGNATURE; + err = vb21_is_packed_key_valid(&k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid key size */ + reset_data(&k, &s); + k.vb21_key.key_size--; + err = vb21_is_packed_key_valid(&k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid magic */ + reset_data(&k, &s); + s.vb21_sig.c.magic = VB21_MAGIC_PACKED_KEY; + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid sig size */ + reset_data(&k, &s); + s.vb21_sig.sig_size--; + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Sig algorithm mismatch */ + reset_data(&k, &s); + s.vb21_sig.sig_alg++; + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Hash algorithm mismatch */ + reset_data(&k, &s); + s.vb21_sig.hash_alg++; + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid sig_offset */ + reset_data(&k, &s); + s.vb21_sig.sig_offset--; + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid data size */ + reset_data(&k, &s); + s.vb21_sig.data_size = CONFIG_RW_SIZE; + err = vb21_is_signature_valid(&s.vb21_sig, &k.vb21_key); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid padding */ + reset_data(&k, &s); + len = s.vb21_sig.data_size; + data[len] = 0; + err = vboot_is_padding_valid(data, len, + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Invalid padding size */ + reset_data(&k, &s); + len = s.vb21_sig.data_size + 1; + err = vboot_is_padding_valid(data, len, + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE); + TEST_ASSERT(err == EC_ERROR_INVAL); + + /* Padding size is too large */ + reset_data(&k, &s); + len = s.vb21_sig.data_size + 64; + err = vboot_is_padding_valid(data, len, + CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE); + TEST_ASSERT(err == EC_ERROR_INVAL); + + return EC_SUCCESS; +} + +void run_test(void) +{ + test_reset(); + + RUN_TEST(test_vboot); + + test_print_result(); +} diff --git a/test/vboot.tasklist b/test/vboot.tasklist new file mode 100644 index 0000000000..e241aab4bb --- /dev/null +++ b/test/vboot.tasklist @@ -0,0 +1,17 @@ +/* Copyright 2017 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST /* No test task */ |