int vb2_fw_parse_gbb(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); struct vb2_gbb_header *gbb; struct vb2_workbuf wb; int rv; vb2_workbuf_from_ctx(ctx, &wb); /* Read GBB into next chunk of work buffer */ gbb = vb2_workbuf_alloc(&wb, sizeof(*gbb)); if (!gbb) return VB2_ERROR_GBB_WORKBUF; rv = vb2_read_gbb_header(ctx, gbb); if (rv) return rv; /* Extract the only things we care about at firmware time */ sd->gbb_flags = gbb->flags; sd->gbb_rootkey_offset = gbb->rootkey_offset; sd->gbb_rootkey_size = gbb->rootkey_size; memcpy(sd->gbb_hwid_digest, gbb->hwid_digest, VB2_GBB_HWID_DIGEST_SIZE); return VB2_SUCCESS; }
int vb2api_get_pcr_digest(struct vb2_context *ctx, enum vb2_pcr_digest which_digest, uint8_t *dest, uint32_t *dest_size) { const uint8_t *digest; uint32_t digest_size; switch (which_digest) { case BOOT_MODE_PCR: digest = vb2_get_boot_state_digest(ctx); digest_size = VB2_SHA1_DIGEST_SIZE; break; case HWID_DIGEST_PCR: digest = vb2_get_sd(ctx)->gbb_hwid_digest; digest_size = VB2_GBB_HWID_DIGEST_SIZE; break; default: return VB2_ERROR_API_PCR_DIGEST; } if (digest == NULL || *dest_size < digest_size) return VB2_ERROR_API_PCR_DIGEST_BUF; memcpy(dest, digest, digest_size); *dest_size = digest_size; return VB2_SUCCESS; }
static void reset_common_data(enum reset_type t) { struct vb2_keyblock *kb = &mock_vblock.k.kb; struct vb2_fw_preamble *pre = &mock_vblock.p.pre; memset(workbuf, 0xaa, sizeof(workbuf)); memset(&cc, 0, sizeof(cc)); cc.workbuf = workbuf; cc.workbuf_size = sizeof(workbuf); vb2_init_context(&cc); sd = vb2_get_sd(&cc); vb2_nv_init(&cc); vb2_secdata_create(&cc); vb2_secdata_init(&cc); mock_read_res_fail_on_call = 0; mock_unpack_key_retval = VB2_SUCCESS; mock_verify_keyblock_retval = VB2_SUCCESS; mock_verify_preamble_retval = VB2_SUCCESS; /* Set up mock data for verifying keyblock */ sd->fw_version_secdata = 0x20002; vb2_secdata_set(&cc, VB2_SECDATA_VERSIONS, sd->fw_version_secdata); sd->gbb_rootkey_offset = vb2_offset_of(&mock_gbb, &mock_gbb.rootkey); sd->gbb_rootkey_size = sizeof(mock_gbb.rootkey_data); sd->last_fw_result = VB2_FW_RESULT_SUCCESS; mock_gbb.rootkey.algorithm = 11; mock_gbb.rootkey.key_offset = vb2_offset_of(&mock_gbb.rootkey, &mock_gbb.rootkey_data); mock_gbb.rootkey.key_size = sizeof(mock_gbb.rootkey_data); kb->keyblock_size = sizeof(mock_vblock.k); kb->data_key.algorithm = 7; kb->data_key.key_version = 2; kb->data_key.key_offset = vb2_offset_of(&mock_vblock.k, &mock_vblock.k.data_key_data) - vb2_offset_of(&mock_vblock.k, &kb->data_key); kb->data_key.key_size = sizeof(mock_vblock.k.data_key_data); strcpy(mock_vblock.k.data_key_data, "data key data!!"); pre->preamble_size = sizeof(mock_vblock.p); pre->firmware_version = 2; /* If verifying preamble, verify keyblock first to set up data key */ if (t == FOR_PREAMBLE) vb2_load_fw_keyblock(&cc); };
int vb2_select_fw_slot(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); uint32_t tries; /* Get result of last boot */ sd->last_fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED); sd->last_fw_result = vb2_nv_get(ctx, VB2_NV_FW_RESULT); /* Save to the previous result fields in NV storage */ vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, sd->last_fw_slot); vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, sd->last_fw_result); /* Clear result, since we don't know what will happen this boot */ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN); /* Get slot to try */ sd->fw_slot = vb2_nv_get(ctx, VB2_NV_TRY_NEXT); /* Check try count */ tries = vb2_nv_get(ctx, VB2_NV_TRY_COUNT); if (sd->last_fw_result == VB2_FW_RESULT_TRYING && sd->last_fw_slot == sd->fw_slot && tries == 0) { /* * We used up our last try on the previous boot, so fall back * to the other slot this boot. */ sd->fw_slot = 1 - sd->fw_slot; vb2_nv_set(ctx, VB2_NV_TRY_NEXT, sd->fw_slot); } if (tries > 0) { /* Still trying this firmware */ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING); /* Decrement non-zero try count, unless told not to */ if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT)) vb2_nv_set(ctx, VB2_NV_TRY_COUNT, tries - 1); } /* Store the slot we're trying */ vb2_nv_set(ctx, VB2_NV_FW_TRIED, sd->fw_slot); /* Set context flag if we're using slot B */ if (sd->fw_slot) ctx->flags |= VB2_CONTEXT_FW_SLOT_B; /* Set status flag */ sd->status |= VB2_SD_STATUS_CHOSE_SLOT; return VB2_SUCCESS; }
int vb2api_check_hash(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); struct vb2_digest_context *dc = (struct vb2_digest_context *) (ctx->workbuf + sd->workbuf_hash_offset); struct vb2_workbuf wb; uint8_t *digest; uint32_t digest_size = vb2_digest_size(dc->hash_alg); const struct vb2_signature *sig; int rv; vb2_workbuf_from_ctx(ctx, &wb); /* Get signature pointer */ if (!sd->hash_tag) return VB2_ERROR_API_CHECK_HASH_TAG; sig = (const struct vb2_signature *)(ctx->workbuf + sd->hash_tag); /* Must have initialized hash digest work area */ if (!sd->workbuf_hash_size) return VB2_ERROR_API_CHECK_HASH_WORKBUF; /* Should have hashed the right amount of data */ if (sd->hash_remaining_size) return VB2_ERROR_API_CHECK_HASH_SIZE; /* Allocate the digest */ digest = vb2_workbuf_alloc(&wb, digest_size); if (!digest) return VB2_ERROR_API_CHECK_HASH_WORKBUF_DIGEST; /* Finalize the digest */ if (dc->using_hwcrypto) rv = vb2ex_hwcrypto_digest_finalize(digest, digest_size); else rv = vb2_digest_finalize(dc, digest, digest_size); if (rv) return rv; /* Compare with the signature */ if (vb2_safe_memcmp(digest, (const uint8_t *)sig + sig->sig_offset, digest_size)) return VB2_ERROR_API_CHECK_HASH_SIG; // TODO: the old check-hash function called vb2_fail() on any mismatch. // I don't think it should do that; the caller should. return VB2_SUCCESS; }
VbError_t VbGbbReadData(struct vb2_context *ctx, uint32_t offset, uint32_t size, void *buf) { struct vb2_shared_data *sd = vb2_get_sd(ctx); /* This is the old API, for backwards compatibility */ if (!sd->gbb) return VBERROR_INVALID_GBB; if (offset + size > sd->gbb_size) return VBERROR_INVALID_GBB; memcpy(buf, ((uint8_t *)sd->gbb) + offset, size); return VBERROR_SUCCESS; }
void vb2_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode) { struct vb2_shared_data *sd = vb2_get_sd(ctx); /* If NV data hasn't been initialized, initialize it now */ if (!(sd->status & VB2_SD_STATUS_NV_INIT)) vb2_nv_init(ctx); /* See if we were far enough in the boot process to choose a slot */ if (sd->status & VB2_SD_STATUS_CHOSE_SLOT) { /* Boot failed */ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE); /* Use up remaining tries */ vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 0); /* * Try the other slot next time. We'll alternate * between slots, which may help if one or both slots is * flaky. */ vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - sd->fw_slot); /* * If we didn't try the other slot last boot, or we tried it * and it didn't fail, try it next boot. */ if (sd->last_fw_slot != 1 - sd->fw_slot || sd->last_fw_result != VB2_FW_RESULT_FAILURE) return; } /* * If we're still here, we failed before choosing a slot, or both * this slot and the other slot failed in successive boots. So we * need to go to recovery. * * Set a recovery reason and subcode only if they're not already set. * If recovery is already requested, it's a more specific error code * than later code is providing and we shouldn't overwrite it. */ VB2_DEBUG("Need recovery, reason: %#x / %#x\n", reason, subcode); if (!vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST)) { vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, reason); vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, subcode); } }
static void reset_common_data(void) { memset(workbuf, 0xaa, sizeof(workbuf)); memset(&cc, 0, sizeof(cc)); cc.workbuf = workbuf; cc.workbuf_size = sizeof(workbuf); vb2_init_context(&cc); sd = vb2_get_sd(&cc); vb2_nv_init(&cc); vb2_secdata_create(&cc); vb2_secdata_init(&cc); mock_tpm_clear_called = 0; mock_tpm_clear_retval = VB2_SUCCESS; };
/* Reset mock data (for use before each test) */ static void ResetMocks(void) { int gbb_used; memset(gbb_data, 0, sizeof(gbb_data)); gbb->major_version = GBB_MAJOR_VER; gbb->minor_version = GBB_MINOR_VER; gbb->flags = 0; gbb_used = sizeof(GoogleBinaryBlockHeader); gbb->hwid_offset = gbb_used; strcpy(gbb_data + gbb->hwid_offset, "Test HWID"); gbb->hwid_size = strlen(gbb_data + gbb->hwid_offset) + 1; gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7; mock_localization_count = 3; mock_altfw_mask = 3 << 1; /* This mask selects 1 and 2 */ gbb->header_size = sizeof(*gbb); gbb->rootkey_offset = gbb_used; gbb->rootkey_size = 64; gbb_used += 64; gbb->recovery_key_offset = gbb_used; gbb->recovery_key_size = 64; gbb_used += 64; memset(&ctx, 0, sizeof(ctx)); ctx.workbuf = workbuf; ctx.workbuf_size = sizeof(workbuf); vb2_init_context(&ctx); vb2_nv_init(&ctx); sd = vb2_get_sd(&ctx); sd->vbsd = shared; sd->gbb = (struct vb2_gbb_header *)gbb_data; sd->gbb_size = sizeof(gbb_data); memset(&shared_data, 0, sizeof(shared_data)); VbSharedDataInit(shared, sizeof(shared_data)); *debug_info = 0; }
void vb2_check_recovery(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); uint32_t reason = vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST); uint32_t subcode = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE); VB2_DEBUG("Recovery reason from previous boot: %#x / %#x\n", reason, subcode); /* * Sets the current recovery request, unless there's already been a * failure earlier in the boot process. */ if (!sd->recovery_reason) sd->recovery_reason = reason; /* Clear request and subcode so we don't get stuck in recovery mode */ vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED); vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, VB2_RECOVERY_NOT_REQUESTED); if (ctx->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) { VB2_DEBUG("Recovery was requested manually\n"); if (subcode && !sd->recovery_reason) /* * Recovery was requested at 'broken' screen. * Promote subcode to reason. */ sd->recovery_reason = subcode; else /* Recovery was forced. Override recovery reason */ sd->recovery_reason = VB2_RECOVERY_RO_MANUAL; sd->flags |= VB2_SD_FLAG_MANUAL_RECOVERY; } /* If recovery reason is non-zero, tell caller we need recovery mode */ if (sd->recovery_reason) { ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; VB2_DEBUG("We have a recovery request: %#x / %#x\n", sd->recovery_reason, vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE)); } }
int vb2api_fw_phase2(struct vb2_context *ctx) { int rv; /* * Use the slot from the last boot if this is a resume. Do not set * VB2_SD_STATUS_CHOSE_SLOT so the try counter is not decremented on * failure as we are explicitly not attempting to boot from a new slot. */ if (ctx->flags & VB2_CONTEXT_S3_RESUME) { struct vb2_shared_data *sd = vb2_get_sd(ctx); /* Set the current slot to the last booted slot */ sd->fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED); /* Set context flag if we're using slot B */ if (sd->fw_slot) ctx->flags |= VB2_CONTEXT_FW_SLOT_B; return VB2_SUCCESS; } /* Always clear RAM when entering developer mode */ if (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ctx->flags |= VB2_CONTEXT_CLEAR_RAM; /* Check for explicit request to clear TPM */ rv = vb2_check_tpm_clear(ctx); if (rv) { vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv); return rv; } /* Decide which firmware slot to try this boot */ rv = vb2_select_fw_slot(ctx); if (rv) { vb2_fail(ctx, VB2_RECOVERY_FW_SLOT, rv); return rv; } return VB2_SUCCESS; }
int vb2_init_context(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); /* Don't do anything if the context has already been initialized */ if (ctx->workbuf_used) return VB2_SUCCESS; /* * Workbuf had better be big enough for our shared data struct and * aligned. Not much we can do if it isn't; we'll die before we can * store a recovery reason. */ if (ctx->workbuf_size < sizeof(*sd)) return VB2_ERROR_INITCTX_WORKBUF_SMALL; if (!vb2_aligned(ctx->workbuf, VB2_WORKBUF_ALIGN)) return VB2_ERROR_INITCTX_WORKBUF_ALIGN; /* Initialize the shared data at the start of the work buffer */ memset(sd, 0, sizeof(*sd)); ctx->workbuf_used = sizeof(*sd); return VB2_SUCCESS; }
int vb2api_extend_hash(struct vb2_context *ctx, const void *buf, uint32_t size) { struct vb2_shared_data *sd = vb2_get_sd(ctx); struct vb2_digest_context *dc = (struct vb2_digest_context *) (ctx->workbuf + sd->workbuf_hash_offset); /* Must have initialized hash digest work area */ if (!sd->workbuf_hash_size) return VB2_ERROR_API_EXTEND_HASH_WORKBUF; /* Don't extend past the data we expect to hash */ if (!size || size > sd->hash_remaining_size) return VB2_ERROR_API_EXTEND_HASH_SIZE; sd->hash_remaining_size -= size; if (dc->using_hwcrypto) return vb2ex_hwcrypto_digest_extend(buf, size); else return vb2_digest_extend(dc, buf, size); }
VbError_t VbGbbReadHWID(struct vb2_context *ctx, char *hwid, uint32_t max_size) { struct vb2_shared_data *sd = vb2_get_sd(ctx); if (!max_size) return VBERROR_INVALID_PARAMETER; *hwid = '\0'; StrnAppend(hwid, "{INVALID}", max_size); if (!ctx) return VBERROR_INVALID_GBB; if (0 == sd->gbb->hwid_size) { VB2_DEBUG("VbHWID(): invalid hwid size\n"); return VBERROR_SUCCESS; /* oddly enough! */ } if (sd->gbb->hwid_size > max_size) { VB2_DEBUG("VbDisplayDebugInfo(): invalid hwid offset/size\n"); return VBERROR_INVALID_PARAMETER; } return VbGbbReadData(ctx, sd->gbb->hwid_offset, sd->gbb->hwid_size, hwid); }
int vb2api_init_hash2(struct vb2_context *ctx, const struct vb2_guid *guid, uint32_t *size) { struct vb2_shared_data *sd = vb2_get_sd(ctx); const struct vb2_fw_preamble *pre; const struct vb2_signature *sig = NULL; struct vb2_digest_context *dc; struct vb2_workbuf wb; uint32_t hash_offset; int i, rv; vb2_workbuf_from_ctx(ctx, &wb); /* Get preamble pointer */ if (!sd->workbuf_preamble_size) return VB2_ERROR_API_INIT_HASH_PREAMBLE; pre = (const struct vb2_fw_preamble *) (ctx->workbuf + sd->workbuf_preamble_offset); /* Find the matching signature */ hash_offset = pre->hash_offset; for (i = 0; i < pre->hash_count; i++) { sig = (const struct vb2_signature *) ((uint8_t *)pre + hash_offset); if (!memcmp(guid, &sig->guid, sizeof(*guid))) break; hash_offset += sig->c.total_size; } if (i >= pre->hash_count) return VB2_ERROR_API_INIT_HASH_GUID; /* No match */ /* Allocate workbuf space for the hash */ if (sd->workbuf_hash_size) { dc = (struct vb2_digest_context *) (ctx->workbuf + sd->workbuf_hash_offset); } else { uint32_t dig_size = sizeof(*dc); dc = vb2_workbuf_alloc(&wb, dig_size); if (!dc) return VB2_ERROR_API_INIT_HASH_WORKBUF; sd->workbuf_hash_offset = vb2_offset_of(ctx->workbuf, dc); sd->workbuf_hash_size = dig_size; ctx->workbuf_used = sd->workbuf_hash_offset + dig_size; } sd->hash_tag = vb2_offset_of(ctx->workbuf, sig); sd->hash_remaining_size = sig->data_size; if (size) *size = sig->data_size; if (!(pre->flags & VB2_FIRMWARE_PREAMBLE_DISALLOW_HWCRYPTO)) { rv = vb2ex_hwcrypto_digest_init(sig->hash_alg, sig->data_size); if (!rv) { VB2_DEBUG("Using HW crypto engine for hash_alg %d\n", sig->hash_alg); dc->hash_alg = sig->hash_alg; dc->using_hwcrypto = 1; return VB2_SUCCESS; } if (rv != VB2_ERROR_EX_HWCRYPTO_UNSUPPORTED) return rv; VB2_DEBUG("HW crypto for hash_alg %d not supported, using SW\n", sig->hash_alg); } else { VB2_DEBUG("HW crypto forbidden by preamble, using SW\n"); } return vb2_digest_init(dc, sig->hash_alg); }
int vb2_check_dev_switch(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); uint32_t flags = 0; uint32_t old_flags; int is_dev = 0; int use_secdata = 1; int rv; /* Read secure flags */ rv = vb2_secdata_get(ctx, VB2_SECDATA_FLAGS, &flags); if (rv) { if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) { /* * Recovery mode needs to check other ways developer * mode can be enabled, so don't give up yet. But * since we can't read secdata, assume dev mode was * disabled. */ use_secdata = 0; flags = 0; } else { /* Normal mode simply fails */ return rv; } } old_flags = flags; /* Handle dev disable request */ if (use_secdata && vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST)) { flags &= ~VB2_SECDATA_FLAG_DEV_MODE; /* Clear the request */ vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 0); } /* * Check if we've been asked by the caller to disable dev mode. Note * that hardware switch and GBB flag will take precedence over this. */ if (ctx->flags & VB2_DISABLE_DEVELOPER_MODE) flags &= ~VB2_SECDATA_FLAG_DEV_MODE; /* Check virtual dev switch */ if (flags & VB2_SECDATA_FLAG_DEV_MODE) is_dev = 1; /* Handle forcing dev mode via physical switch */ if (ctx->flags & VB2_CONTEXT_FORCE_DEVELOPER_MODE) is_dev = 1; /* Check if GBB is forcing dev mode */ if (sd->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON) is_dev = 1; /* Handle whichever mode we end up in */ if (is_dev) { /* Developer mode */ sd->flags |= VB2_SD_DEV_MODE_ENABLED; ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; flags |= VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER; } else { /* Normal mode */ flags &= ~VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER; /* * Disable dev_boot_* flags. This ensures they will be * initially disabled if the user later transitions back into * developer mode. */ vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 0); vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 0); vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 0); vb2_nv_set(ctx, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP, 0); vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, 0); vb2_nv_set(ctx, VB2_NV_FASTBOOT_UNLOCK_IN_FW, 0); } if (ctx->flags & VB2_CONTEXT_FORCE_WIPEOUT_MODE) vb2_nv_set(ctx, VB2_NV_REQ_WIPEOUT, 1); if (flags != old_flags) { /* * Just changed dev mode state. Clear TPM owner. This must be * done here instead of simply passing a flag to * vb2_check_tpm_clear(), because we don't want to update * last_boot_developer and then fail to clear the TPM owner. * * Note that we do this even if we couldn't read secdata, since * the TPM owner and secdata may be independent, and we want * the owner to be cleared if *this boot* is different than the * last one (perhaps due to GBB or hardware override). */ rv = vb2ex_tpm_clear_owner(ctx); if (use_secdata) { /* Check for failure to clear owner */ if (rv) { /* * Note that this truncates rv to 8 bit. Which * is not as useful as the full error code, but * we don't have NVRAM space to store the full * 32-bit code. */ vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv); return rv; } /* Save new flags */ rv = vb2_secdata_set(ctx, VB2_SECDATA_FLAGS, flags); if (rv) return rv; } } return VB2_SUCCESS; }
static void nv_storage_test(void) { struct nv_field *vnf; uint8_t goodcrc; uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE] __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); struct vb2_context c = { .flags = 0, .workbuf = workbuf, .workbuf_size = sizeof(workbuf), }; struct vb2_shared_data *sd = vb2_get_sd(&c); memset(c.nvdata, 0xA6, sizeof(c.nvdata)); vb2_init_context(&c); /* Init with invalid data should set defaults and regenerate CRC */ vb2_nv_init(&c); TEST_EQ(c.nvdata[0], 0x70, "vb2_nv_init() reset header byte"); TEST_NEQ(c.nvdata[15], 0, "vb2_nv_init() CRC"); TEST_EQ(sd->status, VB2_SD_STATUS_NV_INIT | VB2_SD_STATUS_NV_REINIT, "vb2_nv_init() status changed"); test_changed(&c, 1, "vb2_nv_init() reset changed"); goodcrc = c.nvdata[15]; TEST_SUCC(vb2_nv_check_crc(&c), "vb2_nv_check_crc() good"); /* Another init should not cause further changes */ c.flags = 0; sd->status = 0; vb2_nv_init(&c); test_changed(&c, 0, "vb2_nv_init() didn't re-reset"); TEST_EQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC same"); TEST_EQ(sd->status, VB2_SD_STATUS_NV_INIT, "vb2_nv_init() status same"); /* Perturbing the header should force defaults */ c.nvdata[0] ^= 0x40; TEST_EQ(vb2_nv_check_crc(&c), VB2_ERROR_NV_HEADER, "vb2_nv_check_crc() bad header"); vb2_nv_init(&c); TEST_EQ(c.nvdata[0], 0x70, "vb2_nv_init() reset header byte again"); test_changed(&c, 1, "vb2_nv_init() corrupt changed"); TEST_EQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC same again"); /* So should perturbing some other byte */ TEST_EQ(c.nvdata[11], 0, "Kernel byte starts at 0"); c.nvdata[11] = 12; TEST_EQ(vb2_nv_check_crc(&c), VB2_ERROR_NV_CRC, "vb2_nv_check_crc() bad CRC"); vb2_nv_init(&c); TEST_EQ(c.nvdata[11], 0, "vb2_nv_init() reset kernel byte"); test_changed(&c, 1, "vb2_nv_init() corrupt elsewhere changed"); TEST_EQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC same again"); /* Clear the kernel and firmware flags */ vb2_nv_init(&c); TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET), 1, "Firmware settings are reset"); vb2_nv_set(&c, VB2_NV_FIRMWARE_SETTINGS_RESET, 0); TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET), 0, "Firmware settings are clear"); TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET), 1, "Kernel settings are reset"); vb2_nv_set(&c, VB2_NV_KERNEL_SETTINGS_RESET, 0); TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET), 0, "Kernel settings are clear"); TEST_EQ(c.nvdata[0], 0x40, "Header byte now just has the header bit"); /* That should have changed the CRC */ TEST_NEQ(c.nvdata[15], goodcrc, "vb2_nv_init() CRC changed due to flags clear"); /* Test explicitly setting the reset flags again */ vb2_nv_init(&c); vb2_nv_set(&c, VB2_NV_FIRMWARE_SETTINGS_RESET, 1); TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET), 1, "Firmware settings forced reset"); vb2_nv_set(&c, VB2_NV_FIRMWARE_SETTINGS_RESET, 0); vb2_nv_set(&c, VB2_NV_KERNEL_SETTINGS_RESET, 1); TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET), 1, "Kernel settings forced reset"); vb2_nv_set(&c, VB2_NV_KERNEL_SETTINGS_RESET, 0); /* Get/set an invalid field */ vb2_nv_init(&c); vb2_nv_set(&c, -1, 1); TEST_EQ(vb2_nv_get(&c, -1), 0, "Get invalid setting"); /* Test other fields */ vb2_nv_init(&c); for (vnf = nvfields; vnf->desc; vnf++) { TEST_EQ(vb2_nv_get(&c, vnf->param), vnf->default_value, vnf->desc); vb2_nv_set(&c, vnf->param, vnf->test_value); TEST_EQ(vb2_nv_get(&c, vnf->param), vnf->test_value, vnf->desc); vb2_nv_set(&c, vnf->param, vnf->test_value2); TEST_EQ(vb2_nv_get(&c, vnf->param), vnf->test_value2, vnf->desc); } /* None of those changes should have caused a reset to defaults */ vb2_nv_init(&c); TEST_EQ(vb2_nv_get(&c, VB2_NV_FIRMWARE_SETTINGS_RESET), 0, "Firmware settings are still clear"); TEST_EQ(vb2_nv_get(&c, VB2_NV_KERNEL_SETTINGS_RESET), 0, "Kernel settings are still clear"); /* Writing identical settings doesn't cause the CRC to regenerate */ c.flags = 0; vb2_nv_init(&c); test_changed(&c, 0, "No regen CRC on open"); for (vnf = nvfields; vnf->desc; vnf++) vb2_nv_set(&c, vnf->param, vnf->test_value2); test_changed(&c, 0, "No regen CRC if data not changed"); /* Test out-of-range fields mapping to defaults or failing */ vb2_nv_init(&c); vb2_nv_set(&c, VB2_NV_TRY_COUNT, 16); TEST_EQ(vb2_nv_get(&c, VB2_NV_TRY_COUNT), 15, "Try b count out of range"); vb2_nv_set(&c, VB2_NV_RECOVERY_REQUEST, 0x101); TEST_EQ(vb2_nv_get(&c, VB2_NV_RECOVERY_REQUEST), VB2_RECOVERY_LEGACY, "Recovery request out of range"); vb2_nv_set(&c, VB2_NV_LOCALIZATION_INDEX, 0x102); TEST_EQ(vb2_nv_get(&c, VB2_NV_LOCALIZATION_INDEX), 0, "Localization index out of range"); vb2_nv_set(&c, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN + 1); vb2_nv_set(&c, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN + 100); TEST_EQ(vb2_nv_get(&c, VB2_NV_FW_RESULT), VB2_FW_RESULT_UNKNOWN, "Firmware result out of range"); } int main(int argc, char* argv[]) { nv_storage_test(); return gTestSuccess ? 0 : 255; }
VbError_t VbGbbReadRecoveryKey(struct vb2_context *ctx, VbPublicKey **keyp) { struct vb2_shared_data *sd = vb2_get_sd(ctx); return VbGbbReadKey(ctx, sd->gbb->recovery_key_offset, keyp); }
int vb2api_fw_phase1(struct vb2_context *ctx) { int rv; struct vb2_shared_data *sd; /* Initialize the vboot context if it hasn't been yet */ vb2_init_context(ctx); sd = vb2_get_sd(ctx); /* Initialize NV context */ vb2_nv_init(ctx); /* * Handle caller-requested reboot due to secdata. Do this before we * even look at secdata. If we fail because of a reboot loop we'll be * the first failure so will get to set the recovery reason. */ if (!(ctx->flags & VB2_CONTEXT_SECDATA_WANTS_REBOOT)) { /* No reboot requested */ vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 0); } else if (vb2_nv_get(ctx, VB2_NV_TPM_REQUESTED_REBOOT)) { /* * Reboot requested... again. Fool me once, shame on you. * Fool me twice, shame on me. Fail into recovery to avoid * a reboot loop. */ vb2_fail(ctx, VB2_RECOVERY_RO_TPM_REBOOT, 0); } else { /* Reboot requested for the first time */ vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 1); return VB2_ERROR_API_PHASE1_SECDATA_REBOOT; } /* Initialize secure data */ rv = vb2_secdata_init(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_SECDATA_INIT, rv); /* Load and parse the GBB header */ rv = vb2_fw_parse_gbb(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_GBB_HEADER, rv); /* * Check for recovery. Note that this function returns void, since any * errors result in requesting recovery. That's also why we don't * return error from failures in the preceding two steps; those * failures simply cause us to detect recovery mode here. */ vb2_check_recovery(ctx); /* Check for dev switch */ rv = vb2_check_dev_switch(ctx); if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { /* * Error in dev switch processing, and we weren't already * headed for recovery mode. Reboot into recovery mode, since * it's too late to handle those errors this boot, and we need * to take a different path through the dev switch checking * code in that case. */ vb2_fail(ctx, VB2_RECOVERY_DEV_SWITCH, rv); return rv; } /* * Check for possible reasons to ask the firmware to make display * available. sd->recovery_reason may have been set above by * vb2_check_recovery. VB2_SD_FLAG_DEV_MODE_ENABLED may have been set * above by vb2_check_dev_switch. */ if (!(ctx->flags & VB2_CONTEXT_DISPLAY_INIT) && (vb2_nv_get(ctx, VB2_NV_OPROM_NEEDED) || sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED || sd->recovery_reason)) ctx->flags |= VB2_CONTEXT_DISPLAY_INIT; /* Mark display as available for downstream vboot and vboot callers. */ if (ctx->flags & VB2_CONTEXT_DISPLAY_INIT) sd->flags |= VB2_SD_FLAG_DISPLAY_AVAILABLE; /* Return error if recovery is needed */ if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) { /* Always clear RAM when entering recovery mode */ ctx->flags |= VB2_CONTEXT_CLEAR_RAM; return VB2_ERROR_API_PHASE1_RECOVERY; } return VB2_SUCCESS; }
static void reset_common_data(enum reset_type t) { struct vb2_packed_key *k; memset(workbuf, 0xaa, sizeof(workbuf)); memset(&cc, 0, sizeof(cc)); cc.workbuf = workbuf; cc.workbuf_size = sizeof(workbuf); vb2_workbuf_from_ctx(&cc, &wb); vb2_init_context(&cc); sd = vb2_get_sd(&cc); vb2_nv_init(&cc); vb2_secdatak_create(&cc); vb2_secdatak_init(&cc); vb2_secdatak_set(&cc, VB2_SECDATAK_VERSIONS, 0x20002); mock_read_res_fail_on_call = 0; mock_unpack_key_retval = VB2_SUCCESS; mock_read_gbb_header_retval = VB2_SUCCESS; mock_load_kernel_keyblock_retval = VB2_SUCCESS; mock_load_kernel_preamble_retval = VB2_SUCCESS; /* Recovery key in mock GBB */ mock_gbb.recovery_key.algorithm = 11; mock_gbb.recovery_key.key_offset = vb2_offset_of(&mock_gbb.recovery_key, &mock_gbb.recovery_key_data); mock_gbb.recovery_key.key_size = sizeof(mock_gbb.recovery_key_data); strcpy(mock_gbb.recovery_key_data, "The recovery key"); mock_gbb.h.recovery_key_offset = vb2_offset_of(&mock_gbb, &mock_gbb.recovery_key); mock_gbb.h.recovery_key_size = mock_gbb.recovery_key.key_offset + mock_gbb.recovery_key.key_size; if (t == FOR_PHASE1) { uint8_t *kdata; /* Create mock firmware preamble in the context */ sd->workbuf_preamble_offset = cc.workbuf_used; fwpre = (struct vb2_fw_preamble *) (cc.workbuf + sd->workbuf_preamble_offset); k = &fwpre->kernel_subkey; kdata = (uint8_t *)fwpre + sizeof(*fwpre); memcpy(kdata, fw_kernel_key_data, sizeof(fw_kernel_key_data)); k->algorithm = 7; k->key_offset = vb2_offset_of(k, kdata); k->key_size = sizeof(fw_kernel_key_data); sd->workbuf_preamble_size = sizeof(*fwpre) + k->key_size; cc.workbuf_used += sd->workbuf_preamble_size; } else if (t == FOR_PHASE2) { struct vb2_signature *sig; struct vb2_digest_context dc; uint8_t *sdata; /* Create mock kernel data key */ sd->workbuf_data_key_offset = cc.workbuf_used; kdkey = (struct vb2_packed_key *) (cc.workbuf + sd->workbuf_data_key_offset); kdkey->algorithm = VB2_ALG_RSA2048_SHA256; sd->workbuf_data_key_size = sizeof(*kdkey); cc.workbuf_used += sd->workbuf_data_key_size; /* Create mock kernel preamble in the context */ sd->workbuf_preamble_offset = cc.workbuf_used; kpre = (struct vb2_kernel_preamble *) (cc.workbuf + sd->workbuf_preamble_offset); sdata = (uint8_t *)kpre + sizeof(*kpre); sig = &kpre->body_signature; sig->data_size = sizeof(kernel_data); sig->sig_offset = vb2_offset_of(sig, sdata); sig->sig_size = VB2_SHA512_DIGEST_SIZE; vb2_digest_init(&dc, VB2_HASH_SHA256); vb2_digest_extend(&dc, (const uint8_t *)kernel_data, sizeof(kernel_data)); vb2_digest_finalize(&dc, sdata, sig->sig_size); sd->workbuf_preamble_size = sizeof(*kpre) + sig->sig_size; sd->vblock_preamble_offset = 0x10000 - sd->workbuf_preamble_size; cc.workbuf_used += sd->workbuf_preamble_size; } else { /* Set flags and versions for roll-forward */ sd->kernel_version = 0x20004; sd->kernel_version_secdatak = 0x20002; sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED; cc.flags |= VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD; } };