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_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; }
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; } };
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); }
static void init_context_tests(void) { /* Use our own context struct so we can re-init it */ struct vb2_context c = { .workbuf = workbuf, .workbuf_size = sizeof(workbuf), }; reset_common_data(); TEST_SUCC(vb2_init_context(&c), "Init context good"); TEST_EQ(c.workbuf_used, sizeof(struct vb2_shared_data), "Init vbsd"); /* Don't re-init if used is non-zero */ c.workbuf_used = 200; TEST_SUCC(vb2_init_context(&c), "Re-init context good"); TEST_EQ(c.workbuf_used, 200, "Didn't re-init"); /* Handle workbuf errors */ c.workbuf_used = 0; c.workbuf_size = sizeof(struct vb2_shared_data) - 1; TEST_EQ(vb2_init_context(&c), VB2_ERROR_INITCTX_WORKBUF_SMALL, "Init too small"); c.workbuf_size = sizeof(workbuf); /* Handle workbuf unaligned */ c.workbuf++; TEST_EQ(vb2_init_context(&c), VB2_ERROR_INITCTX_WORKBUF_ALIGN, "Init unaligned"); } static void misc_tests(void) { struct vb2_workbuf wb; reset_common_data(); cc.workbuf_used = 16; vb2_workbuf_from_ctx(&cc, &wb); TEST_PTR_EQ(wb.buf, workbuf + 16, "vb_workbuf_from_ctx() buf"); TEST_EQ(wb.size, cc.workbuf_size - 16, "vb_workbuf_from_ctx() size"); } static void gbb_tests(void) { struct vb2_gbb_header gbb = { .signature = {'$', 'G', 'B', 'B'}, .major_version = VB2_GBB_MAJOR_VER, .minor_version = VB2_GBB_MINOR_VER, .header_size = sizeof(struct vb2_gbb_header), .flags = 0x1234, .rootkey_offset = 240, .rootkey_size = 1040, }; struct vb2_gbb_header gbbdest; TEST_EQ(sizeof(struct vb2_gbb_header), EXPECTED_VB2_GBB_HEADER_SIZE, "sizeof(struct vb2_gbb_header)"); reset_common_data(); /* Good contents */ mock_resource_index = VB2_RES_GBB; mock_resource_ptr = &gbb; mock_resource_size = sizeof(gbb); TEST_SUCC(vb2_read_gbb_header(&cc, &gbbdest), "read gbb header good"); TEST_SUCC(memcmp(&gbb, &gbbdest, sizeof(gbb)), "read gbb contents"); mock_resource_index = VB2_RES_GBB + 1; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_EX_READ_RESOURCE_INDEX, "read gbb header missing"); mock_resource_index = VB2_RES_GBB; gbb.signature[0]++; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_MAGIC, "read gbb header bad magic"); gbb.signature[0]--; gbb.major_version = VB2_GBB_MAJOR_VER + 1; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_VERSION, "read gbb header major version"); gbb.major_version = VB2_GBB_MAJOR_VER; gbb.minor_version = VB2_GBB_MINOR_VER + 1; TEST_SUCC(vb2_read_gbb_header(&cc, &gbbdest), "read gbb header minor++"); gbb.minor_version = 1; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_TOO_OLD, "read gbb header 1.1 fails"); gbb.minor_version = 0; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_TOO_OLD, "read gbb header 1.0 fails"); gbb.minor_version = VB2_GBB_MINOR_VER; gbb.header_size--; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_HEADER_SIZE, "read gbb header size"); TEST_EQ(vb2_fw_parse_gbb(&cc), VB2_ERROR_GBB_HEADER_SIZE, "parse gbb failure"); gbb.header_size++; /* Parse GBB */ TEST_SUCC(vb2_fw_parse_gbb(&cc), "parse gbb"); TEST_EQ(sd->gbb_flags, gbb.flags, "gbb flags"); TEST_EQ(sd->gbb_rootkey_offset, gbb.rootkey_offset, "rootkey offset"); TEST_EQ(sd->gbb_rootkey_size, gbb.rootkey_size, "rootkey size"); /* Workbuf failure */ reset_common_data(); cc.workbuf_used = cc.workbuf_size - 4; TEST_EQ(vb2_fw_parse_gbb(&cc), VB2_ERROR_GBB_WORKBUF, "parse gbb no workbuf"); } static void fail_tests(void) { /* Early fail (before even NV init) */ reset_common_data(); sd->status &= ~VB2_SD_STATUS_NV_INIT; vb2_fail(&cc, 1, 2); TEST_NEQ(sd->status & VB2_SD_STATUS_NV_INIT, 0, "vb2_fail inits NV"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 1, "vb2_fail request"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE), 2, "vb2_fail subcode"); /* Repeated fail doesn't overwrite the error code */ vb2_fail(&cc, 3, 4); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 1, "vb2_fail repeat"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE), 2, "vb2_fail repeat2"); /* Fail with other slot good doesn't trigger recovery */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3); vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN); sd->status |= VB2_SD_STATUS_CHOSE_SLOT; sd->fw_slot = 0; sd->last_fw_slot = 1; sd->last_fw_result = VB2_FW_RESULT_UNKNOWN; vb2_fail(&cc, 5, 6); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0, "vb2_failover"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_FAILURE, "vb2_fail this fw"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 0, "vb2_fail use up tries"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "vb2_fail try other slot"); /* Fail with other slot already failing triggers recovery */ reset_common_data(); sd->status |= VB2_SD_STATUS_CHOSE_SLOT; sd->fw_slot = 1; sd->last_fw_slot = 0; sd->last_fw_result = VB2_FW_RESULT_FAILURE; vb2_fail(&cc, 7, 8); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 7, "vb2_fail both slots bad"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_FAILURE, "vb2_fail this fw"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 0, "vb2_fail try other slot"); }