[igt-dev] [PATCH i-g-t 3/4] kms_content_protection: srm and downstream_info test

Ramalingam C ramalingam.c at intel.com
Tue Feb 26 07:23:24 UTC 2019


Retrieve the downstream info and use that to stitch a srm and
verify the revocation process.

Adds two more connector properties called CP_SRM and CP_Downstream_Info

SRM stitched here is polluted. Kernel trusts the userspace to validate
the SRM integrity through DCP signature. Actually this test exploits
that trust to test SRM and downstream_info features.

HDCP1.4 BKSV and HDCP2.2 receiver id of a HDCP sink is different. So we
need to force the KMD to use a single HDCP version across
downstream_info gathering and SRM setting and verifying teh revocation
process. So Type 1 is used.

Signed-off-by: Ramalingam C <ramalingam.c at intel.com>
---
 lib/igt_kms.c                  |   2 +
 lib/igt_kms.h                  |   2 +
 tests/kms_content_protection.c | 201 ++++++++++++++++++++++++++++++++++++++---
 3 files changed, 191 insertions(+), 14 deletions(-)

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index b3d1c3456157..528cfe454816 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -200,6 +200,8 @@ const char * const igt_connector_prop_names[IGT_NUM_CONNECTOR_PROPS] = {
 	[IGT_CONNECTOR_CONTENT_PROTECTION] = "Content Protection",
 	[IGT_CONNECTOR_VRR_CAPABLE] = "vrr_capable",
 	[IGT_CONNECTOR_CP_CONTENT_TYPE] = "CP_Content_Type",
+	[IGT_CONNECTOR_CP_SRM] = "CP_SRM",
+	[IGT_CONNECTOR_CP_DOWNSTREAM_INFO] = "CP_Downstream_Info",
 };
 
 /*
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index e152b6a2ed55..f7fb88d7e5e6 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -124,6 +124,8 @@ enum igt_atomic_connector_properties {
        IGT_CONNECTOR_CONTENT_PROTECTION,
        IGT_CONNECTOR_VRR_CAPABLE,
        IGT_CONNECTOR_CP_CONTENT_TYPE,
+       IGT_CONNECTOR_CP_SRM,
+       IGT_CONNECTOR_CP_DOWNSTREAM_INFO,
        IGT_NUM_CONNECTOR_PROPS
 };
 
diff --git a/tests/kms_content_protection.c b/tests/kms_content_protection.c
index 4563a6067a32..c41e9684deb6 100644
--- a/tests/kms_content_protection.c
+++ b/tests/kms_content_protection.c
@@ -55,6 +55,109 @@ struct data {
 #define FLIP_EVENT_POLLING_TIMEOUT_MSEC		1000
 
 
+#define DRM_MODE_HDCP_KSV_LEN			5
+#define DRM_MODE_HDCP_MAX_DEVICE_CNT		127
+
+#define DRM_MODE_HDCP14_IN_FORCE		(1 << 0)
+#define DRM_MODE_HDCP22_IN_FORCE		(1 << 1)
+
+struct cp_downstream_info {
+	/* HDCP ver in force */
+	__u32 ver_in_force;
+	__u8 content_type;
+
+	/* KSV of immediate HDCP Sink. In Little-Endian Format. */
+	char bksv[DRM_MODE_HDCP_KSV_LEN];
+
+	/* Whether Immediate HDCP sink is a repeater? */
+	bool is_repeater;
+
+	/* Depth received from immediate downstream repeater */
+	__u8 depth;
+
+	/* Device count received from immediate downstream repeater */
+	__u32 device_count;
+
+	/*
+	 * Max buffer required to hold ksv list received from immediate
+	 * repeater. In this array first device_count * DRM_MODE_HDCP_KSV_LEN
+	 * will hold the valid ksv bytes.
+	 * If authentication specification is
+	 *      HDCP1.4 - each KSV's Bytes will be in Little-Endian format.
+	 *      HDCP2.2 - each KSV's Bytes will be in Big-Endian format.
+	 */
+	char ksv_list[DRM_MODE_HDCP_KSV_LEN * DRM_MODE_HDCP_MAX_DEVICE_CNT];
+};
+
+__u8 facsimile_srm[] = {
+	0x80, 0x0, 0x0, 0x05, 0x01, 0x0, 0x0, 0x36, 0x02, 0x51, 0x1E, 0xF2,
+	0x1A, 0xCD, 0xE7, 0x26, 0x97, 0xF4, 0x01, 0x97, 0x10, 0x19, 0x92, 0x53,
+	0xE9, 0xF0, 0x59, 0x95, 0xA3, 0x7A, 0x3B, 0xFE, 0xE0, 0x9C, 0x76, 0xDD,
+	0x83, 0xAA, 0xC2, 0x5B, 0x24, 0xB3, 0x36, 0x84, 0x94, 0x75, 0x34, 0xDB,
+	0x10, 0x9E, 0x3B, 0x23, 0x13, 0xD8, 0x7A, 0xC2, 0x30, 0x79, 0x84};
+
+static void parse_downstream_info(struct cp_downstream_info *ds_info)
+{
+	char *ksvs;
+	int i;
+
+	if (ds_info->ver_in_force & DRM_MODE_HDCP14_IN_FORCE)
+		igt_debug("HDCP1.4 is Enabled\n");
+	else if (ds_info->ver_in_force & DRM_MODE_HDCP22_IN_FORCE)
+		igt_debug("HDCP2.2 is Enabled. Type%d\n",
+			  ds_info->content_type & CP_TYPE_1 ? 1 : 0);
+	else
+		return;
+
+	igt_debug("\tReceiver ID: %#04x %#04x %#04x %#04x %#04x\n",
+		  ds_info->bksv[0] & 0xFF, ds_info->bksv[1] & 0xFF,
+		  ds_info->bksv[2] & 0xFF, ds_info->bksv[3] & 0xFF,
+		  ds_info->bksv[4] & 0xFF);
+
+	if (ds_info->is_repeater) {
+		igt_debug("\tHDCP sink is a Repeater\n");
+
+		igt_debug("\tDepth: %d, Device count: %d\n", ds_info->depth,
+			  ds_info->device_count);
+		ksvs = ds_info->ksv_list;
+
+		for (i = 0; i < ds_info->device_count; i++) {
+			igt_debug("\tksv-%d: %#04x %#04x %#04x %#04x %#04x\n", i,
+				  ksvs[0] & 0xFF, ksvs[1] & 0xFF,
+				  ksvs[2] & 0xFF, ksvs[3] & 0xFF,
+				  ksvs[4] & 0xFF);
+			ksvs += DRM_MODE_HDCP_KSV_LEN;
+		}
+	} else {
+		igt_debug("\tHDCP sink is a Receiver\n");
+	}
+}
+
+static void retrieve_downstream_info_prepare_srm(igt_output_t *output)
+{
+	drmModePropertyBlobRes *ds_info_prop = NULL;
+	uint64_t downstream_blob_id;
+	struct cp_downstream_info *ds_info;
+	int i;
+
+	igt_debug("CP_downstream_info property is attached\n");
+
+	downstream_blob_id =
+		igt_output_get_prop(output, IGT_CONNECTOR_CP_DOWNSTREAM_INFO);
+
+	igt_assert_f(downstream_blob_id, "Invalid downstream blob id\n");
+	ds_info_prop = drmModeGetPropertyBlob(data.drm_fd, downstream_blob_id);
+
+	igt_assert(ds_info_prop);
+	igt_assert_eq(ds_info_prop->length, sizeof(struct cp_downstream_info));
+	ds_info = ds_info_prop->data;
+
+	parse_downstream_info(ds_info);
+
+	for (i = 0; i < 5; i++)
+		facsimile_srm[i + 9] = ds_info->bksv[i];
+}
+
 static void flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
 			 unsigned int tv_usec, void *_data)
 {
@@ -164,7 +267,7 @@ static void modeset_with_fb(const enum pipe pipe, igt_output_t *output,
 }
 
 static bool test_cp_enable(igt_output_t *output, enum igt_commit_style s,
-			   int content_type)
+			   int content_type, bool test_srm)
 {
 	igt_display_t *display = &data.display;
 	igt_plane_t *primary;
@@ -172,6 +275,25 @@ static bool test_cp_enable(igt_output_t *output, enum igt_commit_style s,
 
 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
 
+	if (output->props[IGT_CONNECTOR_CP_SRM]) {
+		if (test_srm) {
+			igt_debug("CP_SRM property is updated with SRM\n");
+
+			/* Set SRM Blob to CR_SRM property */
+			igt_output_replace_prop_blob(output,
+						     IGT_CONNECTOR_CP_SRM,
+						     facsimile_srm,
+						     sizeof(facsimile_srm));
+		} else if (output->values[IGT_CONNECTOR_CP_SRM]) {
+			igt_debug("CP_SRM property is cleared\n");
+
+			/* Set SRM Blob to CR_SRM property */
+			igt_output_replace_prop_blob(output,
+						     IGT_CONNECTOR_CP_SRM,
+						     NULL, 0);
+		}
+	}
+
 	igt_output_set_prop_value(output,
 				  IGT_CONNECTOR_CONTENT_PROTECTION, CP_DESIRED);
 	if (output->props[IGT_CONNECTOR_CP_CONTENT_TYPE])
@@ -214,13 +336,14 @@ static void test_cp_disable(igt_output_t *output, enum igt_commit_style s)
 
 static void test_cp_enable_with_retry(igt_output_t *output,
 				      enum igt_commit_style s, int retry,
-				      int content_type, bool expect_failure)
+				      int content_type, bool expect_failure,
+				      bool test_srm)
 {
 	bool ret;
 
 	do {
 		test_cp_disable(output, s);
-		ret = test_cp_enable(output, s, content_type);
+		ret = test_cp_enable(output, s, content_type, test_srm);
 
 		if (!ret && --retry)
 			igt_debug("Retry (%d/2) ...\n", 3 - retry);
@@ -229,6 +352,9 @@ static void test_cp_enable_with_retry(igt_output_t *output,
 	if (expect_failure)
 		igt_assert_f(!ret,
 			     "CP Enabled. Though it is expected to fail\n");
+	else if (test_srm)
+		igt_assert_f(!ret,
+			     "CP Enabled. Though ID is revoked through SRM\n");
 	else
 		igt_assert_f(ret, "Content Protection not enabled\n");
 }
@@ -256,12 +382,12 @@ static void test_cp_lic(igt_output_t *output)
 static void
 test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
 				  bool dpms_test, int content_type,
-				  bool mei_reload_test)
+				  bool mei_reload_test, bool test_srm)
 {
 	igt_display_t *display = &data.display;
 	igt_plane_t *primary;
 	enum pipe pipe;
-	bool ret;
+	bool ret, srm_modified = false;
 
 	for_each_pipe(display, pipe) {
 		if (!igt_pipe_connector_valid(pipe, output))
@@ -276,8 +402,10 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
 		if (!igt_pipe_is_free(display, pipe))
 			continue;
 
+		srm_modified = false;
 		modeset_with_fb(pipe, output, s);
-		test_cp_enable_with_retry(output, s, 3, content_type, false);
+		test_cp_enable_with_retry(output, s, 3, content_type, false,
+					  false);
 
 		if (mei_reload_test) {
 			igt_assert_f(!igt_kmod_unload("mei_hdcp", 0),
@@ -297,6 +425,23 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
 
 		test_cp_lic(output);
 
+		if (output->props[IGT_CONNECTOR_CP_DOWNSTREAM_INFO] &&
+		    output->props[IGT_CONNECTOR_CP_SRM] &&
+		    test_srm) {
+			retrieve_downstream_info_prepare_srm(output);
+			srm_modified = true;
+		}
+
+		if (test_srm && srm_modified) {
+			test_cp_disable(output, s);
+			test_cp_enable_with_retry(output, s, 3, content_type,
+						  false, test_srm);
+
+			/* Destroy the SRM Blob */
+			test_cp_enable_with_retry(output, s, 1, content_type,
+						  false, false);
+		}
+
 		if (dpms_test) {
 			igt_pipe_set_prop_value(display, pipe,
 						IGT_CRTC_ACTIVE, 0);
@@ -310,7 +455,8 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
 						  KERNEL_AUTH_TIME_ALLOWED_MSEC);
 			if (!ret)
 				test_cp_enable_with_retry(output, s, 2,
-							  content_type, false);
+							  content_type, false,
+							  false);
 		}
 
 		test_cp_disable(output, s);
@@ -375,7 +521,8 @@ static bool sink_hdcp2_capable(igt_output_t *output)
 
 static void
 test_content_protection(enum igt_commit_style s, bool dpms_test,
-			int content_type, bool mei_reload_test)
+			int content_type, bool mei_reload_test,
+			bool test_srm)
 {
 	igt_display_t *display = &data.display;
 	igt_output_t *output;
@@ -407,7 +554,7 @@ test_content_protection(enum igt_commit_style s, bool dpms_test,
 
 		test_content_protection_on_output(output, s, dpms_test,
 						  content_type,
-						  mei_reload_test);
+						  mei_reload_test, test_srm);
 		valid_tests++;
 	}
 
@@ -426,30 +573,56 @@ igt_main
 
 	igt_subtest("legacy")
 		test_content_protection(COMMIT_LEGACY, false, CP_TYPE_0,
-					false);
+					false, false);
 
 	igt_subtest("atomic") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_0,
-					false);
+					false, false);
 	}
 
 	igt_subtest("atomic-dpms") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, true, CP_TYPE_0,
-					false);
+					false, false);
 	}
 
 	igt_subtest("Type1") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_1,
-					false);
+					false, false);
 	}
 
 	igt_subtest("type1_mei_interface") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_1,
-					true);
+					true, false);
+	}
+
+	/*
+	 * SRM subtest perform the HDCP authentication, and then retrieve the
+	 * receiver id through downstream info.
+	 *
+	 * Using the receiver ID, facsimile SRM table is modified with
+	 * receiver ID retrieved from the downstream info. After modification
+	 * SRM table is not intact as per DCP Signature.
+	 *
+	 * But Kernel believes userspace and doesn't verify the DCP signature.
+	 * So we can exploite that trust to test the SRM and downstream info
+	 * features.
+	 *
+	 * So when modified SRM is applied Authentication will fail due to
+	 * receiver ID revocation.
+	 *
+	 * And Kernel attempts HDCP2.2 always and on failure of it HDCP1.4
+	 * will be attempted. But their ID of the sink varies between 1.4 and
+	 * 2.2 versions. So we need to stick to one version. Hence HDCP2.2 is
+	 * chosen using Type 1.
+	 */
+	igt_subtest("srm") {
+		igt_require(data.display.is_atomic);
+		test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_1,
+					false, true);
 	}
 
 	igt_fixture
-- 
2.7.4



More information about the igt-dev mailing list