Skip to content

Commit 5dc9c38

Browse files
eddieramirezJohn Meigs
andauthored
Update for SPEKEv2 for AES 128 and Sample AES (#128)
* update code for cmaf sample aes workflow in speke v2 * remove fairplay hls signaling data inputs * insert iv for all fairplay in speke v2 * add iv for aes128 * fix clearkey path and simplify code * Update requirements.txt update zappa from 0.56 to 0.58 to support cryptography v35.0 and above --------- Co-authored-by: John Meigs <[email protected]>
1 parent 47cf29e commit 5dc9c38

File tree

3 files changed

+33
-35
lines changed

3 files changed

+33
-35
lines changed

cloudformation/speke_reference.json

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,6 @@
259259
},
260260
"WIDEVINE_HLS_SIGNALING_DATA_MASTER": {
261261
"Ref": "WidevineHlsSignalingDataMaster"
262-
},
263-
"FAIRPLAY_HLS_SIGNALING_DATA_MEDIA": {
264-
"Ref": "FairplayHlsSignalingDataMedia"
265-
},
266-
"FAIRPLAY_HLS_SIGNALING_DATA_MASTER": {
267-
"Ref": "FairplayHlsSignalingDataMaster"
268262
}
269263
}
270264
},
@@ -693,16 +687,6 @@
693687
"Default": "",
694688
"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",
695689
"Type": "String"
696-
},
697-
"FairplayHlsSignalingDataMedia": {
698-
"Default": "",
699-
"Description": "Encoded Fairplay HlsSignalingData for media (ext-x-key) that is included in the encrypted content and is supported in Speke V2.0 only",
700-
"Type": "String"
701-
},
702-
"FairplayHlsSignalingDataMaster": {
703-
"Default": "",
704-
"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",
705-
"Type": "String"
706690
}
707691
},
708692
"Conditions": {

requirements.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
argcomplete==3.1.1
32
asn1crypto==1.5.1
43
astroid==2.15.6
@@ -50,5 +49,5 @@ Werkzeug==2.3.7
5049
wrapt==1.15.0
5150
wsgi-request-logger==0.4.6
5251
yapf==0.40.1
53-
zappa==0.56.0
54-
zipp==3.16.2
52+
zappa==0.58.0
53+
zipp==3.16.2

src/key_server_common.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@
3434
HLS_AES_128_KEY_FORMAT_VERSIONS = '1' # '1'
3535
HLS_SAMPLE_AES_KEY_FORMAT = 'com.apple.streamingkeydelivery'
3636
HLS_SAMPLE_AES_KEY_FORMAT_VERSIONS = '1'
37-
# speke v2.0 settings for fairplay drm
38-
FAIRPLAY_HLS_SIGNALING_DATA_MEDIA = os.environ["FAIRPLAY_HLS_SIGNALING_DATA_MEDIA"]
39-
FAIRPLAY_HLS_SIGNALING_DATA_MASTER = os.environ["FAIRPLAY_HLS_SIGNALING_DATA_MASTER"]
4037

4138
# settings for widevine drm
4239
WIDEVINE_PSSH_BOX = os.environ["WIDEVINE_PSSH_BOX"]
@@ -75,6 +72,7 @@ def __init__(self, request_body, cache, generator):
7572
self.document_key = None
7673
self.hmac_key = None
7774
self.public_key = None
75+
self.init_vector = None
7876
self.use_playready_content_key = False
7977
element_tree.register_namespace("cpix", "urn:dashif:org:cpix")
8078
element_tree.register_namespace("pskc", "urn:ietf:params:xml:ns:keyprov:pskc")
@@ -188,10 +186,16 @@ def fill_request(self):
188186
else:
189187
print("CLEAR-RESPONSE")
190188

189+
for content_key in self.root.findall("./{urn:dashif:org:cpix}ContentKeyList/{urn:dashif:org:cpix}ContentKey"):
190+
self.init_vector = content_key.get("explicitIV")
191+
191192
for drm_system in self.root.findall("./{urn:dashif:org:cpix}DRMSystemList/{urn:dashif:org:cpix}DRMSystem"):
192193
kid = drm_system.get("kid")
193194
system_id = drm_system.get("systemId")
194195
system_ids[system_id] = kid
196+
# HLS SAMPLE AES and AES 128 Only
197+
if self.init_vector is None and (system_id == HLS_SAMPLE_AES_SYSTEM_ID or system_id == CLEAR_KEY_AES_128_SYSTEM_ID):
198+
self.init_vector = base64.b64encode(self.generator.key(content_id, kid)).decode('utf-8')
195199
print("SYSTEM-ID {}".format(system_id.lower()))
196200
self.fixup_document(drm_system, system_id, content_id, kid)
197201

@@ -200,9 +204,9 @@ def fill_request(self):
200204
init_vector = content_key.get("explicitIV")
201205
data = element_tree.SubElement(content_key, "{urn:dashif:org:cpix}Data")
202206
secret = element_tree.SubElement(data, "{urn:ietf:params:xml:ns:keyprov:pskc}Secret")
203-
# HLS SAMPLE AES Only
204-
if init_vector is None and system_ids.get(HLS_SAMPLE_AES_SYSTEM_ID, False) == kid:
205-
content_key.set('explicitIV', base64.b64encode(self.generator.key(content_id, kid)).decode('utf-8'))
207+
# HLS SAMPLE AES and AES 128 Only
208+
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):
209+
content_key.set('explicitIV', self.init_vector)
206210
# generate the key
207211
key_bytes = self.generator.key(content_id, kid)
208212
# store to the key in the cache
@@ -317,18 +321,30 @@ def fixup_document(self, drm_system, system_id, content_id, kid):
317321
self.safe_remove(drm_system, "{urn:dashif:org:cpix}ContentProtectionData")
318322
self.safe_remove(drm_system, "{urn:dashif:org:cpix}PSSH")
319323
self.safe_remove(drm_system, "{urn:dashif:org:cpix}SmoothStreamingProtectionHeaderData")
324+
ext_x_key_uri = self.cache.url(content_id, kid)
325+
method = "SAMPLE-AES"
326+
key_format = HLS_SAMPLE_AES_KEY_FORMAT
327+
key_format_versions = HLS_SAMPLE_AES_KEY_FORMAT_VERSIONS
328+
init_vector = hex(int.from_bytes(base64.b64decode(self.init_vector), byteorder="big"))
329+
330+
ext_x_session_key, ext_x_key = self.clearkey_hls_signaling_data(ext_x_key_uri, method, key_format, key_format_versions, init_vector)
331+
320332
hls_signalling_data_elems = drm_system.findall("{urn:dashif:org:cpix}HLSSignalingData")
321333
if hls_signalling_data_elems:
322-
drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='media']").text = FAIRPLAY_HLS_SIGNALING_DATA_MEDIA
323-
drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='master']").text = FAIRPLAY_HLS_SIGNALING_DATA_MASTER
334+
drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='media']").text = ext_x_key
335+
drm_system.find("{urn:dashif:org:cpix}HLSSignalingData[@playlist='master']").text = ext_x_session_key
324336

325337
elif system_id.lower() == CLEAR_KEY_AES_128_SYSTEM_ID.lower():
326-
ext_x_key_uri = self.cache.url(content_id, kid)
327338
self.safe_remove(drm_system, "{urn:dashif:org:cpix}ContentProtectionData")
328339
self.safe_remove(drm_system, "{urn:dashif:org:cpix}PSSH")
329340
self.safe_remove(drm_system, "{urn:dashif:org:cpix}SmoothStreamingProtectionHeaderData")
341+
ext_x_key_uri = self.cache.url(content_id, kid)
342+
method = "AES-128"
343+
key_format = HLS_AES_128_KEY_FORMAT
344+
key_format_versions = HLS_AES_128_KEY_FORMAT_VERSIONS
345+
init_vector = hex(int.from_bytes(base64.b64decode(self.init_vector), byteorder="big"))
330346

331-
ext_x_session_key, ext_x_key = self.clearkey_aes_128_hls_signaling_data(ext_x_key_uri)
347+
ext_x_session_key, ext_x_key = self.clearkey_hls_signaling_data(ext_x_key_uri, method, key_format, key_format_versions, init_vector)
332348

333349
hls_signalling_data_elems = drm_system.findall("{urn:dashif:org:cpix}HLSSignalingData")
334350
if hls_signalling_data_elems:
@@ -356,15 +372,14 @@ def get_response(self):
356372
"body": element_tree.tostring(self.root).decode('utf-8')
357373
}
358374

359-
def clearkey_aes_128_hls_signaling_data(self, ext_x_key_uri):
360-
method = "AES-128"
361-
uri = ext_x_key_uri
362-
key_format = HLS_AES_128_KEY_FORMAT
363-
key_format_versions = HLS_AES_128_KEY_FORMAT_VERSIONS
364-
375+
def clearkey_hls_signaling_data(self, uri, method, key_format, key_format_versions, init_vector=None):
365376
# need to fix
366377
ext_x_session_key = '#EXT-X-SESSION-KEY:METHOD={},URI="{}",KEYFORMAT="{}",KEYFORMATVERSIONS="{}"'.format(method, uri, key_format, key_format_versions)
367378
ext_x_key = '#EXT-X-KEY:METHOD={},URI="{}",KEYFORMAT="{}",KEYFORMATVERSIONS="{}"'.format(method, uri, key_format, key_format_versions)
379+
if init_vector is not None:
380+
ext_x_session_key = '{},IV={}'.format(ext_x_session_key, init_vector)
381+
ext_x_key = '{},IV={}'.format(ext_x_key, init_vector)
382+
368383

369384
encoded_session_key = base64.b64encode(ext_x_session_key.encode('utf-8')).decode('utf-8')
370385
encoded_key = base64.b64encode(ext_x_key.encode('utf-8')).decode('utf-8')

0 commit comments

Comments
 (0)