昨日の続き。スクランブルの掛かったストリームはmParser->mCasManagerに任せていました。mCasManagerはATSParser::CasManagerでしたので、実装を見てみます。
//frameworks/av/media/libstagefright/mpeg2ts/CasManager.cpp
bool ATSParser::CasManager::parsePID(ABitReader *br, unsigned pid) {
ssize_t index = mCAPidToSessionIdMap.indexOfKey(pid);
if (index < 0) {
return false;
}
hidl_vec<uint8_t> ecm;
ecm.setToExternal((uint8_t*)br->data(), br->numBitsLeft() / 8);
auto returnStatus = mICas->processEcm(mCAPidToSessionIdMap[index], ecm); //★★★★processEcm()
if (!returnStatus.isOk() || (Status) returnStatus != Status::OK) {
ALOGE("Failed to process ECM: trans=%s, status=%d",
returnStatus.description().c_str(), (Status) returnStatus);
}
return true; // handled
}
謎のmICasがどこから来るかは、後で調べるとして、関数名processEcm() で探してみると、HALの方にコードがあります。
//hardware/interfaces/cas/1.0/default/CasImpl.cpp
Return<Status> CasImpl::processEcm(
const HidlCasSessionId &sessionId, const HidlCasData& ecm) {
ALOGV("%s: sessionId=%s", __FUNCTION__,
sessionIdToString(sessionId).string());
std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder); //★★★★CasPlugin
if (holder.get() == nullptr) {
return toStatus(INVALID_OPERATION);
}
return toStatus(holder->processEcm(sessionId, ecm));
}
想像するにCasPluginというクラスを派生させて処理を実装するのでしょう。探してみるとframeworks/av/drm/mediacas/plugins以下にclearkeyとmockという実装があります。
//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
class ClearKeyCasPlugin : public CasPlugin {
...
//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
status_t ClearKeyCasPlugin::processEcm(
const CasSessionId &sessionId, const CasEcm& ecm) {
ALOGV("processEcm: sessionId=%s", sessionIdToString(sessionId).string());
sp<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
if (session == NULL) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
Mutex::Autolock lock(mKeyFetcherLock);
return session->updateECM(mKeyFetcher.get(), (void*)ecm.data(), ecm.size()); //★★★★mKeyFetcher
}
status_t ClearKeyCasSession::updateECM(
KeyFetcher *keyFetcher, void *ecm, size_t size) {
//...
uint64_t asset_id;
std::vector<KeyFetcher::KeyInfo> keys;
status_t err = keyFetcher->ObtainKey(mEcmBuffer, &asset_id, &keys); //★★★★keyFetcher
if (err != OK) {
ALOGE("updateECM: failed to obtain key (err=%d)", err);
return err;
}
ALOGV("updateECM: %zu key(s) found", keys.size());
for (size_t keyIndex = 0; keyIndex < keys.size(); keyIndex++) {
String8 str;
const sp<ABuffer>& keyBytes = keys[keyIndex].key_bytes;
CHECK(keyBytes->size() == kUserKeyLength);
int result = AES_set_decrypt_key(
reinterpret_cast<const uint8_t*>(keyBytes->data()),
AES_BLOCK_SIZE * 8, &mKeyInfo[keyIndex].contentKey); //★★★★libsslの関数に渡して鍵を生成している?ようだ
//...
//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
status_t ClearKeyFetcher::ObtainKey(const sp<ABuffer>& buffer,
uint64_t* asset_id, std::vector<KeyInfo>* keys) {
//...
引数に渡しているmKeyFetcher(とget() が返すkeyFetcherも同様に)はKeyFetcher型のポインタでした。KeyFetcherを継承したClearKeyFetcher型のオブジェクトが格納されていました。
ClearKeyの仕組みは詳しく知りませんが、ClearKeyCasSession::updateECM() でAESの復号などをしていることと、AESの復号鍵はClearKeyFetcher::ObtainKey() がECMを読んで復号鍵を取得してくれるように見えました。
AndroidでECMの解読を行っている箇所が見つけられました。エレメンタリストリームのデスクランブルはどこで行っているのでしょうね…??
< | 2018 | > | ||||
<< | < | 07 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | - | - | - | - |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)