You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
342 lines
12 KiB
342 lines
12 KiB
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "HlsSampleDecryptor"
|
|
|
|
#include "HlsSampleDecryptor.h"
|
|
|
|
#include <media/stagefright/foundation/ABuffer.h>
|
|
#include <media/stagefright/foundation/ADebug.h>
|
|
#include <media/stagefright/Utils.h>
|
|
|
|
|
|
namespace android {
|
|
|
|
HlsSampleDecryptor::HlsSampleDecryptor()
|
|
: mValidKeyInfo(false) {
|
|
}
|
|
|
|
HlsSampleDecryptor::HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem)
|
|
: mValidKeyInfo(false) {
|
|
|
|
signalNewSampleAesKey(sampleAesKeyItem);
|
|
}
|
|
|
|
void HlsSampleDecryptor::signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem) {
|
|
|
|
if (sampleAesKeyItem == NULL) {
|
|
mValidKeyInfo = false;
|
|
ALOGW("signalNewSampleAesKey: sampleAesKeyItem is NULL");
|
|
return;
|
|
}
|
|
|
|
sp<ABuffer> keyDataBuffer, initVecBuffer;
|
|
sampleAesKeyItem->findBuffer("keyData", &keyDataBuffer);
|
|
sampleAesKeyItem->findBuffer("initVec", &initVecBuffer);
|
|
|
|
if (keyDataBuffer != NULL && keyDataBuffer->size() == AES_BLOCK_SIZE &&
|
|
initVecBuffer != NULL && initVecBuffer->size() == AES_BLOCK_SIZE) {
|
|
|
|
ALOGV("signalNewSampleAesKey: Key: %s IV: %s",
|
|
aesBlockToStr(keyDataBuffer->data()).c_str(),
|
|
aesBlockToStr(initVecBuffer->data()).c_str());
|
|
|
|
uint8_t KeyData[AES_BLOCK_SIZE];
|
|
memcpy(KeyData, keyDataBuffer->data(), AES_BLOCK_SIZE);
|
|
memcpy(mAESInitVec, initVecBuffer->data(), AES_BLOCK_SIZE);
|
|
|
|
mValidKeyInfo = (AES_set_decrypt_key(KeyData, 8*AES_BLOCK_SIZE/*128*/, &mAesKey) == 0);
|
|
if (!mValidKeyInfo) {
|
|
ALOGE("signalNewSampleAesKey: failed to set AES decryption key.");
|
|
}
|
|
|
|
} else {
|
|
// Media scanner might try extract/parse the TS files without knowing the key.
|
|
// Otherwise, shouldn't get here (unless an invalid playlist has swaped SAMPLE-AES with
|
|
// NONE method while still sample-encrypted stream is parsed).
|
|
|
|
mValidKeyInfo = false;
|
|
ALOGE("signalNewSampleAesKey Can't decrypt; keyDataBuffer: %p(%zu) initVecBuffer: %p(%zu)",
|
|
keyDataBuffer.get(), (keyDataBuffer.get() == NULL)? -1 : keyDataBuffer->size(),
|
|
initVecBuffer.get(), (initVecBuffer.get() == NULL)? -1 : initVecBuffer->size());
|
|
}
|
|
}
|
|
|
|
size_t HlsSampleDecryptor::processNal(uint8_t *nalData, size_t nalSize) {
|
|
|
|
unsigned nalType = nalData[0] & 0x1f;
|
|
if (!mValidKeyInfo) {
|
|
ALOGV("processNal[%d]: (%p)/%zu Skipping due to invalid key", nalType, nalData, nalSize);
|
|
return nalSize;
|
|
}
|
|
|
|
bool isEncrypted = (nalSize > VIDEO_CLEAR_LEAD + AES_BLOCK_SIZE);
|
|
ALOGV("processNal[%d]: (%p)/%zu isEncrypted: %d", nalType, nalData, nalSize, isEncrypted);
|
|
|
|
if (isEncrypted) {
|
|
// Encrypted NALUs have extra start code emulation prevention that must be
|
|
// stripped out before we can decrypt it.
|
|
size_t newSize = unescapeStream(nalData, nalSize);
|
|
|
|
ALOGV("processNal:unescapeStream[%d]: %zu -> %zu", nalType, nalSize, newSize);
|
|
nalSize = newSize;
|
|
|
|
//Encrypted_nal_unit () {
|
|
// nal_unit_type_byte // 1 byte
|
|
// unencrypted_leader // 31 bytes
|
|
// while (bytes_remaining() > 0) {
|
|
// if (bytes_remaining() > 16) {
|
|
// encrypted_block // 16 bytes
|
|
// }
|
|
// unencrypted_block // MIN(144, bytes_remaining()) bytes
|
|
// }
|
|
//}
|
|
|
|
size_t offset = VIDEO_CLEAR_LEAD;
|
|
size_t remainingBytes = nalSize - VIDEO_CLEAR_LEAD;
|
|
|
|
// a copy of initVec as decryptBlock updates it
|
|
unsigned char AESInitVec[AES_BLOCK_SIZE];
|
|
memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
|
|
|
|
while (remainingBytes > 0) {
|
|
// encrypted_block: protected block uses 10% skip encryption
|
|
if (remainingBytes > AES_BLOCK_SIZE) {
|
|
uint8_t *encrypted = nalData + offset;
|
|
status_t ret = decryptBlock(encrypted, AES_BLOCK_SIZE, AESInitVec);
|
|
if (ret != OK) {
|
|
ALOGE("processNal failed with %d", ret);
|
|
return nalSize; // revisit this
|
|
}
|
|
|
|
offset += AES_BLOCK_SIZE;
|
|
remainingBytes -= AES_BLOCK_SIZE;
|
|
}
|
|
|
|
// unencrypted_block
|
|
size_t clearBytes = std::min(remainingBytes, (size_t)(9 * AES_BLOCK_SIZE));
|
|
|
|
offset += clearBytes;
|
|
remainingBytes -= clearBytes;
|
|
} // while
|
|
|
|
} else { // isEncrypted == false
|
|
ALOGV("processNal[%d]: Unencrypted NALU (%p)/%zu", nalType, nalData, nalSize);
|
|
}
|
|
|
|
return nalSize;
|
|
}
|
|
|
|
void HlsSampleDecryptor::processAAC(size_t adtsHdrSize, uint8_t *data, size_t size) {
|
|
|
|
if (!mValidKeyInfo) {
|
|
ALOGV("processAAC: (%p)/%zu Skipping due to invalid key", data, size);
|
|
return;
|
|
}
|
|
|
|
// ADTS header is included in the size
|
|
if (size < adtsHdrSize) {
|
|
ALOGV("processAAC: size (%zu) < adtsHdrSize (%zu)", size, adtsHdrSize);
|
|
android_errorWriteLog(0x534e4554, "128433933");
|
|
return;
|
|
}
|
|
size_t offset = adtsHdrSize;
|
|
size_t remainingBytes = size - adtsHdrSize;
|
|
|
|
bool isEncrypted = (remainingBytes >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
|
|
ALOGV("processAAC: header: %zu data: %p(%zu) isEncrypted: %d",
|
|
adtsHdrSize, data, size, isEncrypted);
|
|
|
|
//Encrypted_AAC_Frame () {
|
|
// ADTS_Header // 7 or 9 bytes
|
|
// unencrypted_leader // 16 bytes
|
|
// while (bytes_remaining() >= 16) {
|
|
// encrypted_block // 16 bytes
|
|
// }
|
|
// unencrypted_trailer // 0-15 bytes
|
|
//}
|
|
|
|
// with lead bytes
|
|
if (remainingBytes >= AUDIO_CLEAR_LEAD) {
|
|
offset += AUDIO_CLEAR_LEAD;
|
|
remainingBytes -= AUDIO_CLEAR_LEAD;
|
|
|
|
// encrypted_block
|
|
if (remainingBytes >= AES_BLOCK_SIZE) {
|
|
|
|
size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
|
|
unsigned char AESInitVec[AES_BLOCK_SIZE];
|
|
memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
|
|
|
|
// decrypting all blocks at once
|
|
uint8_t *encrypted = data + offset;
|
|
status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
|
|
if (ret != OK) {
|
|
ALOGE("processAAC: decryptBlock failed with %d", ret);
|
|
return;
|
|
}
|
|
|
|
offset += encryptedBytes;
|
|
remainingBytes -= encryptedBytes;
|
|
} // encrypted
|
|
|
|
// unencrypted_trailer
|
|
size_t clearBytes = remainingBytes;
|
|
if (clearBytes > 0) {
|
|
CHECK(clearBytes < AES_BLOCK_SIZE);
|
|
}
|
|
|
|
} else { // without lead bytes
|
|
ALOGV("processAAC: Unencrypted frame (without lead bytes) size %zu = %zu (hdr) + %zu (rem)",
|
|
size, adtsHdrSize, remainingBytes);
|
|
}
|
|
|
|
}
|
|
|
|
void HlsSampleDecryptor::processAC3(uint8_t *data, size_t size) {
|
|
|
|
if (!mValidKeyInfo) {
|
|
ALOGV("processAC3: (%p)/%zu Skipping due to invalid key", data, size);
|
|
return;
|
|
}
|
|
|
|
bool isEncrypted = (size >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
|
|
ALOGV("processAC3 %p(%zu) isEncrypted: %d", data, size, isEncrypted);
|
|
|
|
//Encrypted_AC3_Frame () {
|
|
// unencrypted_leader // 16 bytes
|
|
// while (bytes_remaining() >= 16) {
|
|
// encrypted_block // 16 bytes
|
|
// }
|
|
// unencrypted_trailer // 0-15 bytes
|
|
//}
|
|
|
|
if (size >= AUDIO_CLEAR_LEAD) {
|
|
// unencrypted_leader
|
|
size_t offset = AUDIO_CLEAR_LEAD;
|
|
size_t remainingBytes = size - AUDIO_CLEAR_LEAD;
|
|
|
|
if (remainingBytes >= AES_BLOCK_SIZE) {
|
|
|
|
size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
|
|
|
|
// encrypted_block
|
|
unsigned char AESInitVec[AES_BLOCK_SIZE];
|
|
memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
|
|
|
|
// decrypting all blocks at once
|
|
uint8_t *encrypted = data + offset;
|
|
status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
|
|
if (ret != OK) {
|
|
ALOGE("processAC3: decryptBlock failed with %d", ret);
|
|
return;
|
|
}
|
|
|
|
offset += encryptedBytes;
|
|
remainingBytes -= encryptedBytes;
|
|
} // encrypted
|
|
|
|
// unencrypted_trailer
|
|
size_t clearBytes = remainingBytes;
|
|
if (clearBytes > 0) {
|
|
CHECK(clearBytes < AES_BLOCK_SIZE);
|
|
}
|
|
|
|
} else {
|
|
ALOGV("processAC3: Unencrypted frame (without lead bytes) size %zu", size);
|
|
}
|
|
}
|
|
|
|
// Unescapes data replacing occurrences of [0, 0, 3] with [0, 0] and returns the new size
|
|
size_t HlsSampleDecryptor::unescapeStream(uint8_t *data, size_t limit) const {
|
|
Vector<size_t> scratchEscapePositions;
|
|
size_t position = 0;
|
|
|
|
while (position < limit) {
|
|
position = findNextUnescapeIndex(data, position, limit);
|
|
if (position < limit) {
|
|
scratchEscapePositions.add(position);
|
|
position += 3;
|
|
}
|
|
}
|
|
|
|
size_t scratchEscapeCount = scratchEscapePositions.size();
|
|
size_t escapedPosition = 0; // The position being read from.
|
|
size_t unescapedPosition = 0; // The position being written to.
|
|
for (size_t i = 0; i < scratchEscapeCount; i++) {
|
|
size_t nextEscapePosition = scratchEscapePositions[i];
|
|
//TODO: add 2 and get rid of the later = 0 assignments
|
|
size_t copyLength = nextEscapePosition - escapedPosition;
|
|
memmove(data+unescapedPosition, data+escapedPosition, copyLength);
|
|
unescapedPosition += copyLength;
|
|
data[unescapedPosition++] = 0;
|
|
data[unescapedPosition++] = 0;
|
|
escapedPosition += copyLength + 3;
|
|
}
|
|
|
|
size_t unescapedLength = limit - scratchEscapeCount;
|
|
size_t remainingLength = unescapedLength - unescapedPosition;
|
|
memmove(data+unescapedPosition, data+escapedPosition, remainingLength);
|
|
|
|
return unescapedLength;
|
|
}
|
|
|
|
size_t HlsSampleDecryptor::findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const {
|
|
for (size_t i = offset; i < limit - 2; i++) {
|
|
//TODO: speed
|
|
if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x03) {
|
|
return i;
|
|
}
|
|
}
|
|
return limit;
|
|
}
|
|
|
|
status_t HlsSampleDecryptor::decryptBlock(uint8_t *buffer, size_t size,
|
|
uint8_t AESInitVec[AES_BLOCK_SIZE]) {
|
|
if (size == 0) {
|
|
return OK;
|
|
}
|
|
|
|
if ((size % AES_BLOCK_SIZE) != 0) {
|
|
ALOGE("decryptBlock: size (%zu) not a multiple of block size", size);
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
ALOGV("decryptBlock: %p (%zu)", buffer, size);
|
|
|
|
AES_cbc_encrypt(buffer, buffer, size, &mAesKey, AESInitVec, AES_DECRYPT);
|
|
|
|
return OK;
|
|
}
|
|
|
|
AString HlsSampleDecryptor::aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]) {
|
|
AString result;
|
|
|
|
if (block == NULL) {
|
|
result = AString("null");
|
|
} else {
|
|
result = AStringPrintf("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
|
block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
|
|
block[8], block[9], block[10], block[11], block[12], block[13], block[14], block[15]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
} // namespace android
|