diff --git a/cloudformation/speke_reference.json b/cloudformation/speke_reference.json index fbb957b..1279d84 100644 --- a/cloudformation/speke_reference.json +++ b/cloudformation/speke_reference.json @@ -259,12 +259,6 @@ }, "WIDEVINE_HLS_SIGNALING_DATA_MASTER": { "Ref": "WidevineHlsSignalingDataMaster" - }, - "FAIRPLAY_HLS_SIGNALING_DATA_MEDIA": { - "Ref": "FairplayHlsSignalingDataMedia" - }, - "FAIRPLAY_HLS_SIGNALING_DATA_MASTER": { - "Ref": "FairplayHlsSignalingDataMaster" } } }, @@ -693,16 +687,6 @@ "Default": "", "Description": "Encoded Widevine HlsSignalingData for master (ext-x-session-key) that is included in the encrypted content and is supported in Speke V2.0 only", "Type": "String" - }, - "FairplayHlsSignalingDataMedia": { - "Default": "", - "Description": "Encoded Fairplay HlsSignalingData for media (ext-x-key) that is included in the encrypted content and is supported in Speke V2.0 only", - "Type": "String" - }, - "FairplayHlsSignalingDataMaster": { - "Default": "", - "Description": "Encoded Fairplay HlsSignalingData for master (ext-x-session-key) that is included in the encrypted content and is supported in Speke V2.0 only", - "Type": "String" } }, "Conditions": { diff --git a/requirements.txt b/requirements.txt index a3266b7..e54f749 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ - argcomplete==3.1.1 asn1crypto==1.5.1 astroid==2.15.6 @@ -50,5 +49,5 @@ Werkzeug==2.3.7 wrapt==1.15.0 wsgi-request-logger==0.4.6 yapf==0.40.1 -zappa==0.56.0 -zipp==3.16.2 \ No newline at end of file +zappa==0.58.0 +zipp==3.16.2 diff --git a/src/key_server_common.py b/src/key_server_common.py index f6db8b8..740f54e 100755 --- a/src/key_server_common.py +++ b/src/key_server_common.py @@ -34,9 +34,6 @@ HLS_AES_128_KEY_FORMAT_VERSIONS = '1' # '1' HLS_SAMPLE_AES_KEY_FORMAT = 'com.apple.streamingkeydelivery' HLS_SAMPLE_AES_KEY_FORMAT_VERSIONS = '1' -# speke v2.0 settings for fairplay drm -FAIRPLAY_HLS_SIGNALING_DATA_MEDIA = os.environ["FAIRPLAY_HLS_SIGNALING_DATA_MEDIA"] -FAIRPLAY_HLS_SIGNALING_DATA_MASTER = os.environ["FAIRPLAY_HLS_SIGNALING_DATA_MASTER"] # settings for widevine drm WIDEVINE_PSSH_BOX = os.environ["WIDEVINE_PSSH_BOX"] @@ -75,6 +72,7 @@ def __init__(self, request_body, cache, generator): self.document_key = None self.hmac_key = None self.public_key = None + self.init_vector = None self.use_playready_content_key = False element_tree.register_namespace("cpix", "urn:dashif:org:cpix") element_tree.register_namespace("pskc", "urn:ietf:params:xml:ns:keyprov:pskc") @@ -188,10 +186,16 @@ def fill_request(self): else: print("CLEAR-RESPONSE") + for content_key in self.root.findall("./{urn:dashif:org:cpix}ContentKeyList/{urn:dashif:org:cpix}ContentKey"): + self.init_vector = content_key.get("explicitIV") + for drm_system in self.root.findall("./{urn:dashif:org:cpix}DRMSystemList/{urn:dashif:org:cpix}DRMSystem"): kid = drm_system.get("kid") system_id = drm_system.get("systemId") system_ids[system_id] = kid + # HLS SAMPLE AES and AES 128 Only + if self.init_vector is None and (system_id == HLS_SAMPLE_AES_SYSTEM_ID or system_id == CLEAR_KEY_AES_128_SYSTEM_ID): + self.init_vector = base64.b64encode(self.generator.key(content_id, kid)).decode('utf-8') print("SYSTEM-ID {}".format(system_id.lower())) self.fixup_document(drm_system, system_id, content_id, kid) @@ -200,9 +204,9 @@ def fill_request(self): init_vector = content_key.get("explicitIV") data = element_tree.SubElement(content_key, "{urn:dashif:org:cpix}Data") secret = element_tree.SubElement(data, "{urn:ietf:params:xml:ns:keyprov:pskc}Secret") - # HLS SAMPLE AES Only - if init_vector is None and system_ids.get(HLS_SAMPLE_AES_SYSTEM_ID, False) == kid: - content_key.set('explicitIV', base64.b64encode(self.generator.key(content_id, kid)).decode('utf-8')) + # HLS SAMPLE AES and AES 128 Only + if init_vector is None and (system_ids.get(HLS_SAMPLE_AES_SYSTEM_ID, False) == kid or system_ids.get(CLEAR_KEY_AES_128_SYSTEM_ID, False) == kid): + content_key.set('explicitIV', self.init_vector) # generate the key key_bytes = self.generator.key(content_id, kid) # store to the key in the cache @@ -317,18 +321,30 @@ def fixup_document(self, drm_system, system_id, content_id, kid): self.safe_remove(drm_system, "{urn:dashif:org:cpix}ContentProtectionData") self.safe_remove(drm_system, "{urn:dashif:org:cpix}PSSH") self.safe_remove(drm_system, "{urn:dashif:org:cpix}SmoothStreamingProtectionHeaderData") + ext_x_key_uri = self.cache.url(content_id, kid) + method = "SAMPLE-AES" + key_format = HLS_SAMPLE_AES_KEY_FORMAT + key_format_versions = HLS_SAMPLE_AES_KEY_FORMAT_VERSIONS + init_vector = hex(int.from_bytes(base64.b64decode(self.init_vector), byteorder="big")) + + ext_x_session_key, ext_x_key = self.clearkey_hls_signaling_data(ext_x_key_uri, method, key_format, key_format_versions, init_vector) + hls_signalling_data_elems = drm_system.findall("{urn:dashif:org:cpix}HLSSignalingData") if hls_signalling_data_elems: - drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='media']").text = FAIRPLAY_HLS_SIGNALING_DATA_MEDIA - drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='master']").text = FAIRPLAY_HLS_SIGNALING_DATA_MASTER + drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='media']").text = ext_x_key + drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='master']").text = ext_x_session_key elif system_id.lower() == CLEAR_KEY_AES_128_SYSTEM_ID.lower(): - ext_x_key_uri = self.cache.url(content_id, kid) self.safe_remove(drm_system, "{urn:dashif:org:cpix}ContentProtectionData") self.safe_remove(drm_system, "{urn:dashif:org:cpix}PSSH") self.safe_remove(drm_system, "{urn:dashif:org:cpix}SmoothStreamingProtectionHeaderData") + ext_x_key_uri = self.cache.url(content_id, kid) + method = "AES-128" + key_format = HLS_AES_128_KEY_FORMAT + key_format_versions = HLS_AES_128_KEY_FORMAT_VERSIONS + init_vector = hex(int.from_bytes(base64.b64decode(self.init_vector), byteorder="big")) - ext_x_session_key, ext_x_key = self.clearkey_aes_128_hls_signaling_data(ext_x_key_uri) + ext_x_session_key, ext_x_key = self.clearkey_hls_signaling_data(ext_x_key_uri, method, key_format, key_format_versions, init_vector) hls_signalling_data_elems = drm_system.findall("{urn:dashif:org:cpix}HLSSignalingData") if hls_signalling_data_elems: @@ -356,15 +372,14 @@ def get_response(self): "body": element_tree.tostring(self.root).decode('utf-8') } - def clearkey_aes_128_hls_signaling_data(self, ext_x_key_uri): - method = "AES-128" - uri = ext_x_key_uri - key_format = HLS_AES_128_KEY_FORMAT - key_format_versions = HLS_AES_128_KEY_FORMAT_VERSIONS - + def clearkey_hls_signaling_data(self, uri, method, key_format, key_format_versions, init_vector=None): # need to fix ext_x_session_key = '#EXT-X-SESSION-KEY:METHOD={},URI="{}",KEYFORMAT="{}",KEYFORMATVERSIONS="{}"'.format(method, uri, key_format, key_format_versions) ext_x_key = '#EXT-X-KEY:METHOD={},URI="{}",KEYFORMAT="{}",KEYFORMATVERSIONS="{}"'.format(method, uri, key_format, key_format_versions) + if init_vector is not None: + ext_x_session_key = '{},IV={}'.format(ext_x_session_key, init_vector) + ext_x_key = '{},IV={}'.format(ext_x_key, init_vector) + encoded_session_key = base64.b64encode(ext_x_session_key.encode('utf-8')).decode('utf-8') encoded_key = base64.b64encode(ext_x_key.encode('utf-8')).decode('utf-8')