Exemple #1
0
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;
}
Exemple #2
0
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;
	}
};
Exemple #4
0
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");
}