7 #include "packager/media/base/playready_key_source.h"
11 #include "packager/base/base64.h"
12 #include "packager/base/logging.h"
13 #include "packager/base/strings/string_number_conversions.h"
14 #include "packager/base/strings/string_util.h"
15 #include "packager/media/base/buffer_writer.h"
16 #include "packager/media/base/http_key_fetcher.h"
17 #include "packager/media/base/key_source.h"
18 #include "packager/media/base/protection_system_ids.h"
19 #include "packager/status_macros.h"
26 const uint32_t kHttpFetchTimeout = 60;
27 const std::string kAcquireLicenseRequest =
28 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
29 "<soap:Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" "
30 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
31 "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
32 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
34 "<AcquirePackagingData "
35 "xmlns=\"http://schemas.microsoft.com/DRM/2007/03/protocols\">"
37 "xmlns=\"http://schemas.microsoft.com/DRM"
38 "/2007/03/protocols/AcquirePackagingData/v1.0\">"
40 "<ProtectionSystemId>9A04F079-9840-4286-AB92-E65BE0885F95"
41 "</ProtectionSystemId>"
42 "</ProtectionSystems>"
43 "<StreamProtectionRequests>"
45 "<ProgramIdentifier>$0</ProgramIdentifier>"
46 "<OffsetFromProgramStart>P0S</OffsetFromProgramStart>"
47 "</StreamInformation>"
48 "</StreamProtectionRequests>"
50 "</AcquirePackagingData>"
54 bool Base64StringToBytes(
const std::string& base64_string,
55 std::vector<uint8_t>* bytes) {
58 if (!base::Base64Decode(base64_string, &str))
60 bytes->assign(str.begin(), str.end());
68 : generate_playready_protection_system_(
74 server_url_(server_url) {}
76 PlayReadyKeySource::~PlayReadyKeySource() =
default;
78 Status RetrieveTextInXMLElement(
const std::string& element,
79 const std::string& xml,
81 std::string start_tag =
"<" + element +
">";
82 std::string end_tag =
"</" + element +
">";
83 std::size_t start_pos = xml.find(start_tag);
84 if (start_pos == std::string::npos) {
85 return Status(error::SERVER_ERROR,
86 "Unable to find tag: " + start_tag);
88 start_pos += start_tag.size();
89 std::size_t end_pos = xml.find(end_tag);
90 if (end_pos == std::string::npos) {
91 return Status(error::SERVER_ERROR,
92 "Unable to find tag: " + end_tag);
94 if (start_pos > end_pos) {
95 return Status(error::SERVER_ERROR,
"Invalid positions");
97 std::size_t segment_len = end_pos - start_pos;
98 *value = xml.substr(start_pos, segment_len);
102 Status SetKeyInformationFromServerResponse(
103 const std::string& response,
104 bool generate_playready_protection_system,
105 EncryptionKey* encryption_key) {
110 std::string key_id_hex;
111 RETURN_IF_ERROR(RetrieveTextInXMLElement(
"KeyId", response, &key_id_hex));
113 std::remove(key_id_hex.begin(), key_id_hex.end(),
'-'), key_id_hex.end());
114 if (!base::HexStringToBytes(key_id_hex, &encryption_key->key_id)) {
115 LOG(ERROR) <<
"Cannot parse key_id_hex, " << key_id_hex;
116 return Status(error::SERVER_ERROR,
"Cannot parse key_id_hex.");
119 std::string key_data_b64;
120 RETURN_IF_ERROR(RetrieveTextInXMLElement(
"KeyData", response, &key_data_b64));
121 if (!Base64StringToBytes(key_data_b64, &encryption_key->key)) {
122 LOG(ERROR) <<
"Cannot parse key, " << key_data_b64;
123 return Status(error::SERVER_ERROR,
"Cannot parse key.");
125 encryption_key->key_ids.emplace_back(encryption_key->key_id);
127 if (generate_playready_protection_system) {
128 std::string pssh_data_b64;
129 RETURN_IF_ERROR(RetrieveTextInXMLElement(
"Data", response, &pssh_data_b64));
130 std::vector<uint8_t> pssh_data;
131 if (!Base64StringToBytes(pssh_data_b64, &pssh_data)) {
132 LOG(ERROR) <<
"Cannot parse pssh data, " << pssh_data_b64;
133 return Status(error::SERVER_ERROR,
"Cannot parse pssh.");
136 PsshBoxBuilder pssh_builder;
137 pssh_builder.add_key_id(encryption_key->key_id);
138 pssh_builder.set_system_id(kPlayReadySystemId,
139 arraysize(kPlayReadySystemId));
140 pssh_builder.set_pssh_data(pssh_data);
141 encryption_key->key_system_info.push_back(
142 {pssh_builder.system_id(), pssh_builder.CreateBox()});
147 Status PlayReadyKeySource::FetchKeysWithProgramIdentifier(
148 const std::string& program_identifier) {
149 std::unique_ptr<EncryptionKey> encryption_key(
new EncryptionKey);
150 HttpKeyFetcher key_fetcher(kHttpFetchTimeout);
152 std::string acquire_license_request = kAcquireLicenseRequest;
153 base::ReplaceFirstSubstringAfterOffset(
154 &acquire_license_request, 0,
"$0", program_identifier);
155 std::string acquire_license_response;
156 Status status = key_fetcher.FetchKeys(server_url_, acquire_license_request,
157 &acquire_license_response);
158 VLOG(1) <<
"Server response: " << acquire_license_response;
159 RETURN_IF_ERROR(status);
161 RETURN_IF_ERROR(SetKeyInformationFromServerResponse(
162 acquire_license_response, generate_playready_protection_system_,
163 encryption_key.get()));
166 encryption_key_ = std::move(encryption_key);
171 const std::vector<uint8_t>& init_data) {
182 DCHECK(encryption_key_);
183 *key = *encryption_key_;
192 DCHECK(encryption_key_);
193 *key = *encryption_key_;
198 uint32_t crypto_period_duration_in_seconds,
199 const std::string& stream_label,
202 *key = *encryption_key_;
All the methods that are virtual are virtual for mocking.