7 #include <packager/media/base/playready_pssh_generator.h>
14 #include <absl/log/check.h>
15 #include <absl/log/log.h>
16 #include <absl/strings/escaping.h>
17 #include <mbedtls/cipher.h>
19 #include <packager/macros/compiler.h>
20 #include <packager/macros/crypto.h>
21 #include <packager/macros/logging.h>
22 #include <packager/media/base/buffer_writer.h>
23 #include <packager/media/base/protection_system_ids.h>
29 const uint8_t kPlayReadyPsshBoxVersion = 0;
32 const std::string kPlayHeaderObject_4_0 =
34 "xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
35 "version=\"4.0.0.0\"><DATA>"
36 "<PROTECTINFO><KEYLEN>16</KEYLEN><ALGID>AESCTR</ALGID></PROTECTINFO>"
37 "<KID>$0</KID><CHECKSUM>$1</CHECKSUM>"
38 "$2</DATA></WRMHEADER>";
41 const std::string kPlayHeaderObject_4_3 =
43 "xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
44 "version=\"4.3.0.0\"><DATA><PROTECTINFO><KIDS>"
45 "<KID ALGID=\"AESCBC\" VALUE=\"$0\"></KID>"
46 "</KIDS></PROTECTINFO>$1</DATA></WRMHEADER>";
49 std::vector<uint8_t> ConvertGuidEndianness(
const std::vector<uint8_t>& input) {
50 std::vector<uint8_t> output = input;
51 if (output.size() > 7) {
65 void ReplaceString(std::string* str,
66 const std::string& from,
67 const std::string& to) {
68 size_t location = str->find(from);
69 if (location != std::string::npos) {
70 str->replace(location, from.size(), to);
74 void AesEcbEncrypt(
const std::vector<uint8_t>& key,
75 const std::vector<uint8_t>& plaintext,
76 std::vector<uint8_t>* ciphertext) {
77 CHECK_EQ(plaintext.size() % AES_BLOCK_SIZE, 0u);
78 ciphertext->resize(plaintext.size());
80 mbedtls_cipher_context_t ctx;
81 mbedtls_cipher_init(&ctx);
83 const mbedtls_cipher_info_t* cipher_info =
84 mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);
87 CHECK_EQ(mbedtls_cipher_setup(&ctx, cipher_info), 0) <<
"Cipher setup failed";
89 CHECK_EQ(key.size(), 16u);
91 mbedtls_cipher_setkey(&ctx, key.data(), 8 * key.size(), MBEDTLS_ENCRYPT),
93 <<
"Failed to set encryption key";
95 size_t output_size = 0;
96 CHECK_EQ(mbedtls_cipher_crypt(&ctx, NULL, 0,
97 plaintext.data(), plaintext.size(),
98 ciphertext->data(), &output_size),
101 mbedtls_cipher_free(&ctx);
107 Status GeneratePlayReadyPsshData(
const std::vector<uint8_t>& key_id,
108 const std::vector<uint8_t>& key,
109 const std::string& extra_header_data,
110 const FourCC protection_scheme,
111 std::vector<uint8_t>* output) {
113 std::vector<uint8_t> key_id_converted = ConvertGuidEndianness(key_id);
115 std::vector<uint8_t> encrypted_key_id;
116 AesEcbEncrypt(key, key_id_converted, &encrypted_key_id);
118 std::string checksum =
119 std::string(encrypted_key_id.begin(), encrypted_key_id.end())
121 std::string base64_checksum;
122 absl::Base64Escape(checksum, &base64_checksum);
123 std::string base64_key_id;
125 std::string(key_id_converted.begin(), key_id_converted.end()),
128 std::string playready_header;
130 switch (protection_scheme) {
131 case kAppleSampleAesProtectionScheme:
134 playready_header = kPlayHeaderObject_4_3;
135 ReplaceString(&playready_header,
"$0", base64_key_id);
136 ReplaceString(&playready_header,
"$1", extra_header_data);
141 playready_header = kPlayHeaderObject_4_0;
142 ReplaceString(&playready_header,
"$0", base64_key_id);
143 ReplaceString(&playready_header,
"$1", base64_checksum);
144 ReplaceString(&playready_header,
"$2", extra_header_data);
148 return Status(error::INVALID_ARGUMENT,
149 "The provided protection scheme is not supported.");
155 std::vector<uint16_t> record_value =
156 std::vector<uint16_t>(playready_header.begin(), playready_header.end());
158 uint16_t record_type =
160 uint16_t record_length = record_value.size() * 2;
161 writer_pr_record.
AppendInt(
static_cast<uint8_t
>(record_type & 0xff));
162 writer_pr_record.
AppendInt(
static_cast<uint8_t
>((record_type >> 8) & 0xff));
163 writer_pr_record.
AppendInt(
static_cast<uint8_t
>(record_length & 0xff));
164 writer_pr_record.
AppendInt(
static_cast<uint8_t
>((record_length >> 8) & 0xff));
165 for (
auto record_item : record_value) {
166 writer_pr_record.
AppendInt(
static_cast<uint8_t
>(record_item & 0xff));
167 writer_pr_record.
AppendInt(
static_cast<uint8_t
>((record_item >> 8) & 0xff));
174 uint32_t playready_header_length = writer_pr_record.Size() + 4 + 2;
175 uint16_t record_count = 1;
177 static_cast<uint8_t
>(playready_header_length & 0xff));
179 static_cast<uint8_t
>((playready_header_length >> 8) & 0xff));
181 static_cast<uint8_t
>((playready_header_length >> 16) & 0xff));
183 static_cast<uint8_t
>((playready_header_length >> 24) & 0xff));
184 writer_pr_header_object.
AppendInt(
static_cast<uint8_t
>(record_count & 0xff));
186 static_cast<uint8_t
>((record_count >> 8) & 0xff));
187 writer_pr_header_object.AppendBuffer(writer_pr_record);
188 *output = std::vector<uint8_t>(
189 writer_pr_header_object.
Buffer(),
190 writer_pr_header_object.
Buffer() + writer_pr_header_object.Size());
195 PlayReadyPsshGenerator::PlayReadyPsshGenerator(
196 const std::string& extra_header_data,
197 FourCC protection_scheme)
198 : PsshGenerator(std::vector<uint8_t>(std::begin(kPlayReadySystemId),
199 std::end(kPlayReadySystemId)),
200 kPlayReadyPsshBoxVersion),
201 extra_header_data_(extra_header_data),
202 protection_scheme_(protection_scheme) {}
204 PlayReadyPsshGenerator::~PlayReadyPsshGenerator() {}
206 bool PlayReadyPsshGenerator::SupportMultipleKeys() {
210 std::optional<std::vector<uint8_t>>
211 PlayReadyPsshGenerator::GeneratePsshDataFromKeyIdAndKey(
212 const std::vector<uint8_t>& key_id,
213 const std::vector<uint8_t>& key)
const {
214 std::vector<uint8_t> pssh_data;
215 Status status = GeneratePlayReadyPsshData(key_id, key, extra_header_data_,
216 protection_scheme_, &pssh_data);
218 LOG(ERROR) << status.ToString();
225 std::optional<std::vector<uint8_t>>
226 PlayReadyPsshGenerator::GeneratePsshDataFromKeyIds(
227 const std::vector<std::vector<uint8_t>>& key_ids)
const {
All the methods that are virtual are virtual for mocking.