Add a playready_experiment.py to play with ProtectionHeader
[smooth-dl.git] / playready_experiment.py
1 #!/usr/bin/env python3
2 #
3 # playready_experiment.py - Example of SmoothStreaming PlayReady authentication
4 #
5 # Copyright (C) 2016  Antonio Ospite <ao2@ao2.it>
6 #
7 # This program is free software: you can redistribute it and/or modify it under
8 # the terms of the GNU General Public License as published by the Free Software
9 # Foundation, either version 3 of the License, or (at your option) any later
10 # version.
11 #
12 # This program is distributed in the hope that it will be useful, but WITHOUT
13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15 # details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 #
21 # Experiment on how to use the ProtectionHeader element in SmoothSTreaming
22 # manifest files.
23 #
24 # The code is just a demonstration of how to decode the ProtectionHeader element
25 # content, it is not supposed to do nothing actually useful.
26 #
27 # Based on the PlayReady Header Object specification:
28 # http://download.microsoft.com/download/1/4/7/1475C862-6B67-44B2-AF31-ABC83EA68292/PlayReady%20Header%20Object%20(7-2-2012).docx
29
30 import base64
31 import struct
32 import sys
33 import xml.etree.ElementTree as etree
34
35 from pysimplesoap.client import SoapClient
36 from pysimplesoap.simplexml import SimpleXMLElement
37
38
39 def get_right_management_header(protection_header_text):
40     play_ready_header_objects = base64.b64decode(protection_header_text)
41
42     offset = 0
43
44     field_size = 4
45     total_size = struct.unpack("<L", play_ready_header_objects[offset:offset + field_size])[0]
46     offset += field_size
47
48     if total_size != len(play_ready_header_objects):
49         sys.stderr.write("Error, unexpected size.\n")
50         sys.exit(1)
51
52     field_size = 2
53     count = struct.unpack("<H", play_ready_header_objects[offset:offset + field_size])[0]
54     offset += field_size
55
56     for i in range(count):
57         field_size = 2
58         record_type = struct.unpack("<H", play_ready_header_objects[offset:offset + field_size])[0]
59         offset += field_size
60
61         field_size = 2
62         record_length = struct.unpack("<H", play_ready_header_objects[offset:offset + field_size])[0]
63         offset += field_size
64
65         if record_type == 1:
66             right_management_header = play_ready_header_objects[offset:offset + record_length].decode("UTF-16")
67             return right_management_header
68         else:
69             sys.stderr.write("Error, unsupported record type. Skipping.\n")
70             offset += record_length
71
72
73 def main():
74     if len(sys.argv) != 2:
75         sys.stderr.write("usage: %s <manifest>\n" % sys.argv[0])
76         sys.exit(1)
77
78     manifest_filename = sys.argv[1]
79
80     manifest = etree.parse(manifest_filename)
81     protection_header = manifest.find(".//Protection/ProtectionHeader")
82
83     challenge = get_right_management_header(protection_header.text)
84
85     challenge_xml = etree.fromstring(challenge)
86     la_url = challenge_xml.find(".//{http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader}LA_URL").text
87
88     client = SoapClient(
89         location=la_url,
90         action='http://schemas.microsoft.com/DRM/2007/03/protocols/',
91         soap_ns='soap', trace=True, ns=False)
92
93     params = SimpleXMLElement("""<AcquireLicense xmlns="http://schemas.microsoft.com/DRM/2007/03/protocols"><challenge>""" +
94                               challenge + 
95                               """</challenge></AcquireLicense>""")
96
97     response = client.call('AcquireLicense', params)
98     print(response)
99
100 if __name__ == "__main__":
101     main()