Chromium Code Reviews| 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) { | |
|
tinskip1
2016/08/17 19:28:25
Does not exists elsewhere?
inline?
kqyang
2016/08/18 19:39:53
There is a BufferReader in src/media/formats/mp4/b
| |
| 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 |