OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/formats/webm/webm_crypto_helpers.h" | 5 #include "media/formats/webm/webm_crypto_helpers.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/sys_byteorder.h" | 10 #include "base/sys_byteorder.h" |
11 #include "media/base/decrypt_config.h" | 11 #include "media/base/decrypt_config.h" |
12 #include "media/formats/webm/webm_constants.h" | 12 #include "media/formats/webm/webm_constants.h" |
13 | 13 |
14 namespace media { | 14 namespace media { |
15 namespace { | 15 namespace { |
16 | 16 |
17 // Generates a 16 byte CTR counter block. The CTR counter block format is a | 17 // Generates a 16 byte CTR counter block. The CTR counter block format is a |
18 // CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. | 18 // CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. |
19 // |iv_size| is the size of |iv| in btyes. Returns a string of | 19 // |iv_size| is the size of |iv| in btyes. Returns a string of |
20 // kDecryptionKeySize bytes. | 20 // kDecryptionKeySize bytes. |
21 std::string GenerateWebMCounterBlock(const uint8_t* iv, int iv_size) { | 21 std::string GenerateWebMCounterBlock(const uint8_t* iv, int iv_size) { |
22 std::string counter_block(reinterpret_cast<const char*>(iv), iv_size); | 22 std::string counter_block(reinterpret_cast<const char*>(iv), iv_size); |
23 counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0); | 23 counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0); |
24 return counter_block; | 24 return counter_block; |
25 } | 25 } |
26 | 26 |
| 27 uint32_t ReadInteger(const uint8_t* buf, int size) { |
| 28 // Read in the big-endian integer. |
| 29 uint32_t value = 0; |
| 30 for (int i = 0; i < size; ++i) |
| 31 value = (value << 8) | buf[i]; |
| 32 return value; |
| 33 } |
| 34 |
| 35 bool ExtractSubsamples(const uint8_t* buf, |
| 36 size_t frame_data_size, |
| 37 size_t num_partitions, |
| 38 std::vector<SubsampleEntry>* subsample_entries) { |
| 39 subsample_entries->clear(); |
| 40 uint32_t clear_bytes = 0; |
| 41 // Partition is the wall between alternating sections. Partition offsets are |
| 42 // relative to the start of the actual frame data. |
| 43 // Size of clear/cipher sections can be calculated from the difference between |
| 44 // adjacent partition offsets. |
| 45 // Here is an example with 4 partitions (5 sections): |
| 46 // "clear |1 cipher |2 clear |3 cipher |4 clear" |
| 47 // With the first and the last implicit partition included: |
| 48 // "|0 clear |1 cipher |2 clear |3 cipher |4 clear |5" |
| 49 // where partition_offset_0 = 0, partition_offset_5 = frame_data_size |
| 50 // There are three subsamples in the above example: |
| 51 // Subsample0.clear_bytes = partition_offset_1 - partition_offset_0 |
| 52 // Subsample0.cipher_bytes = partition_offset_2 - partition_offset_1 |
| 53 // ... |
| 54 // Subsample2.clear_bytes = partition_offset_5 - partition_offset_4 |
| 55 // Subsample2.cipher_bytes = 0 |
| 56 uint32_t partition_offset = 0; |
| 57 for (size_t i = 0, offset = 0; i <= num_partitions; ++i) { |
| 58 const uint32_t prev_partition_offset = partition_offset; |
| 59 partition_offset = |
| 60 (i == num_partitions) |
| 61 ? frame_data_size |
| 62 : ReadInteger(buf + offset, kWebMEncryptedFramePartitionOffsetSize); |
| 63 offset += kWebMEncryptedFramePartitionOffsetSize; |
| 64 if (partition_offset < prev_partition_offset) { |
| 65 DVLOG(1) << "Partition should not be decreasing " << prev_partition_offset |
| 66 << " " << partition_offset; |
| 67 return false; |
| 68 } |
| 69 |
| 70 uint32_t cipher_bytes = 0; |
| 71 bool new_subsample_entry = false; |
| 72 // Alternating clear and cipher sections. |
| 73 if ((i % 2) == 0) { |
| 74 clear_bytes = partition_offset - prev_partition_offset; |
| 75 // Generate a new subsample when finishing reading partition offsets. |
| 76 new_subsample_entry = i == num_partitions; |
| 77 } else { |
| 78 cipher_bytes = partition_offset - prev_partition_offset; |
| 79 // Generate a new subsample after seeing a cipher section. |
| 80 new_subsample_entry = true; |
| 81 } |
| 82 |
| 83 if (new_subsample_entry) { |
| 84 if (clear_bytes == 0 && cipher_bytes == 0) { |
| 85 DVLOG(1) << "Not expecting >2 partitions with the same offsets."; |
| 86 return false; |
| 87 } |
| 88 subsample_entries->push_back(SubsampleEntry(clear_bytes, cipher_bytes)); |
| 89 } |
| 90 } |
| 91 return true; |
| 92 } |
| 93 |
27 } // namespace anonymous | 94 } // namespace anonymous |
28 | 95 |
29 bool WebMCreateDecryptConfig(const uint8_t* data, | 96 bool WebMCreateDecryptConfig(const uint8_t* data, |
30 int data_size, | 97 int data_size, |
31 const uint8_t* key_id, | 98 const uint8_t* key_id, |
32 int key_id_size, | 99 int key_id_size, |
33 std::unique_ptr<DecryptConfig>* decrypt_config, | 100 std::unique_ptr<DecryptConfig>* decrypt_config, |
34 int* data_offset) { | 101 int* data_offset) { |
35 if (data_size < kWebMSignalByteSize) { | 102 if (data_size < kWebMSignalByteSize) { |
36 DVLOG(1) << "Got a block from an encrypted stream with no data."; | 103 DVLOG(1) << "Got a block from an encrypted stream with no data."; |
37 return false; | 104 return false; |
38 } | 105 } |
39 | 106 |
40 uint8_t signal_byte = data[0]; | 107 const uint8_t signal_byte = data[0]; |
41 int frame_offset = sizeof(signal_byte); | 108 int frame_offset = sizeof(signal_byte); |
42 | 109 |
43 // Setting the DecryptConfig object of the buffer while leaving the | 110 // Setting the DecryptConfig object of the buffer while leaving the |
44 // initialization vector empty will tell the decryptor that the frame is | 111 // initialization vector empty will tell the decryptor that the frame is |
45 // unencrypted. | 112 // unencrypted. |
46 std::string counter_block; | 113 std::string counter_block; |
| 114 std::vector<SubsampleEntry> subsample_entries; |
47 | 115 |
48 if (signal_byte & kWebMFlagEncryptedFrame) { | 116 if (signal_byte & kWebMFlagEncryptedFrame) { |
49 if (data_size < kWebMSignalByteSize + kWebMIvSize) { | 117 if (data_size < kWebMSignalByteSize + kWebMIvSize) { |
50 DVLOG(1) << "Got an encrypted block with not enough data " << data_size; | 118 DVLOG(1) << "Got an encrypted block with not enough data " << data_size; |
51 return false; | 119 return false; |
52 } | 120 } |
53 counter_block = GenerateWebMCounterBlock(data + frame_offset, kWebMIvSize); | 121 counter_block = GenerateWebMCounterBlock(data + frame_offset, kWebMIvSize); |
54 frame_offset += kWebMIvSize; | 122 frame_offset += kWebMIvSize; |
| 123 |
| 124 if (signal_byte & kWebMFlagEncryptedFramePartitioned) { |
| 125 if (data_size < frame_offset + kWebMEncryptedFrameNumPartitionsSize) { |
| 126 DVLOG(1) << "Got a partitioned encrypted block with not enough data " |
| 127 << data_size; |
| 128 return false; |
| 129 } |
| 130 |
| 131 const size_t num_partitions = data[frame_offset]; |
| 132 if (num_partitions == 0) { |
| 133 DVLOG(1) << "Got a partitioned encrypted block with 0 partitions."; |
| 134 return false; |
| 135 } |
| 136 frame_offset += kWebMEncryptedFrameNumPartitionsSize; |
| 137 const uint8_t* partition_data_start = data + frame_offset; |
| 138 frame_offset += kWebMEncryptedFramePartitionOffsetSize * num_partitions; |
| 139 if (data_size <= frame_offset) { |
| 140 DVLOG(1) << "Got a partitioned encrypted block with " << num_partitions |
| 141 << " partitions but not enough data " << data_size; |
| 142 return false; |
| 143 } |
| 144 const size_t frame_data_size = data_size - frame_offset; |
| 145 if (!ExtractSubsamples(partition_data_start, frame_data_size, |
| 146 num_partitions, &subsample_entries)) { |
| 147 return false; |
| 148 } |
| 149 } |
55 } | 150 } |
56 | 151 |
57 decrypt_config->reset(new DecryptConfig( | 152 decrypt_config->reset(new DecryptConfig( |
58 std::string(reinterpret_cast<const char*>(key_id), key_id_size), | 153 std::string(reinterpret_cast<const char*>(key_id), key_id_size), |
59 counter_block, | 154 counter_block, subsample_entries)); |
60 std::vector<SubsampleEntry>())); | |
61 *data_offset = frame_offset; | 155 *data_offset = frame_offset; |
62 | 156 |
63 return true; | 157 return true; |
64 } | 158 } |
65 | 159 |
66 } // namespace media | 160 } // namespace media |
OLD | NEW |