1. 소개
이 사양은 OEM이 Android에서 PlayReady 4.0 기반 DRM(디지털 권한 관리) 플러그 인을 구현하기 위한 지침을 설정합니다. 참조하세요 https://developer.android.com/reference/android/media/MediaDrm.html.
이 사양은 플러그 인 API가 PlayReady 호출에 매핑되는 방법에 대한 정보를 제공합니다.
1.1. 변경 기록
| 버전 | 변화 |
|---|---|
| 2016년 5월 | 초기 버전 |
2. 인터페이스
PlayReadyDrmPlugin 은 DRM 플러그 인 인터페이스에 대한 구현을 제공합니다. PlayReadyDrmPlugin 은 DRM 관리자 API를 래핑하고 인터페이스에 지정된 대로 매개 변수에 대한 적절한 변환을 PlayReady가 작동할 수 있는 형식으로 수행하는 일을 담당합니다.
PlayReadyCryptoPlugin 은 샘플의 암호 해독을 담당하는 Crypto 플러그 인 인터페이스에 대한 구현을 제공합니다. OEM은 암호 해독된 샘플이 신뢰할 수 있는 TEE(실행 환경)를 벗어나지 않도록 해야 합니다.
3. 작업
다음 단계에서는 간단한 재생 시나리오를 설명합니다.
앱은 MediaDrm 개체를 만들어 PlayReadyDrmPlugin을 인스턴스화합니다.
그런 다음 openSession을 호출하면 DRM 관리자가 초기화됩니다.
그러면 앱이 getKeyRequest를 호출하고 콘텐츠에서 추출된 콘텐츠 헤더를 initData 매개 변수로 전달합니다. 또한 앱은 선택적Parameters 키-값 벡터에서 라이선스 획득 챌린지 사용자 지정 데이터를 전달할 수도 있습니다. 라이선스 취득 챌린지 사용자 지정 데이터는 drM Manager에 Drm_Content_SetProperty 호출로 전파되어야 합니다.
이 시점에서 앱은 DRM 관리자에 해당하는(Drm_LicenseAcq_GenerateChallenge) /Drm_LicenseAcq_ProcessResponse) 호출을 생성하는 (getKeyRequest / provideKeyResponse)를 실행할 수 있습니다.
그런 다음 앱은 Drm_Reader_Bind 호출이 반환된 후 PlayReadyCryptoPlugin 인터페이스(래핑 DRM_DECRYPT_CONTEXT)의 인스턴스를 생성하는 MediaCrypto 객체를 인스턴스화할 수 있습니다.
그 후 모든 암호 해독 호출은 PlayReadyCryptoPlugin::d ecrypt 메서드를 활용합니다. 그러면 암호 해독된 샘플에 대한 핸들이 반환됩니다.
4. PlayReadyDRMPlugin
setPropertyString
앱은 이 메서드를 사용하여 플러그 인 API의 현재 디자인으로 인해 가능하지 않은 매개 변수를 PlayReady에 전달합니다.
DeviceStoreName - 앱은 세션을 열기 전에 디바이스 저장소의 위치를 속성으로 설정해야 합니다. 그런 다음 PlayReady는 openSession을 호출하는 동안 DRM 관리자를 초기화할 때 DeviceStoreName 속성을 조회합니다. 디바이스 저장소의 경로는 앱의 프라이빗 데이터 디렉터리처럼 앱에서 액세스할 수 있어야 합니다. 이 속성은 HDS를 <보유해야 하는 경로/파일 이름을> 가리킵니다.
LicenseChallengeCustomData - 앱은 선택적으로 getKeyRequest를 호출하기 전에 이 속성을 설정할 수 있습니다. 여기서 PlayReady는 속성을 조회하고 라이선스 취득 챌린지를 작성하고 요청에 사용자 지정 데이터를 포함합니다.
SecureStopCustomData - 앱은 필요에 따라 보안 중지 챌린지를 생성하기 위해 호출하기 전에 이 속성을 설정할 수 있습니다. PlayReady는 속성을 조회하고 보안 중지 챌린지를 작성하고 요청에 사용자 지정 데이터를 포함합니다.
SelectKID - 앱이 단일 일괄 처리로 여러 메모리 내 라이선스를 획득한 경우 바인딩을 위해 base64로 인코딩된 KID를 지정할 수 있습니다.
status_t PlayReadyDrmPlugin::setPropertyString(
String8 const &name,
String8 const &value)
{
DRM_RESULT dr = DRM_SUCCESS;
Mutex::Autolock lock(&SessionManager::sLock);
//
// Store the Property in the name/value KeyedVector
// for later usage.
//
ChkDR( mStringProperties.add(name, value));
if (name == "SelectKID")
{
DRM_SUBSTRING dasstrKID = DRM_EMPTY_DRM_SUBSTRING;
DRM_CONST_STRING dstrKID = DRM_EMPTY_DRM_STRING;
DRM_BYTE rgbKID[CCH_BASE64_EQUIV(CCH_BASE64_EQUIV(DRM_ID_SIZE)] = {0};
const char *pszhKID = value.string();
// Convert the ASCII KID to UNICODE
DRM_DSTR_FROM_PB(&dstrKID, rgbKID, sizeof(rgbKID));
DRM_UTL_PromoteASCIItoUNICODE(pszhKID, &dasstrKID, (DRM_STRING *)&dstrKID);
ChkDR(Drm_Content_SetProperty(
&SessionManager::soAppContext, // DRM_APP_CONTEXT pointer
DRM_CSP_SELECT_KID,
dstrKID.pwszString,
dstrKID.cchString * sizeof(DRM_WCHAR)));
}
ErrorExit:
return _DR_TO_STATUS(dr);
}
setPropertyByteArray
앱은 이 메서드를 사용하여 플러그 인 API의 현재 디자인으로 인해 가능하지 않은 매개 변수를 PlayReady에 전달합니다.
ContentHeader - 앱은 Crypto 개체를 만들기 전에 플러그 인에서 콘텐츠 헤더를 설정해야 합니다. 콘텐츠 헤더는 다음 형식 중 하나일 수 있습니다.
PlayReady 개체의 내용이 포함된 바이트 배열입니다.
V2, V2.4, V4, V4.1, V4.2 헤더(유니코드 XML)의 내용이 포함된 바이트 배열입니다.
24자 KeyID를 지정하는 와이드 문자 배열입니다.
SecureStopPublisherCert - 앱은 모든 보안 중지 관련 호출 전에 게시자 인증서를 한 번 설정해야 합니다.
status_t PlayReadyDrmPlugin::setPropertyByteArray(
String8 const &name,
Vector<uint8_t> const &value)
{
DRM_RESULT dr = DRM_SUCCESS;
Mutex::Autolock lock(&SessionManager::sLock);
// Add the name/value pair to a KeyedVector
mByteArrayProperties.add(name, value);
// If the provided property is a content header
// go ahead and set the property.
if (name == "ContentHeader")
{
ChkDR(Drm_Content_SetProperty(
&SessionManager::soAppContext, // DRM_APP_CONTEXT pointer
DRM_CSP_AUTODETECT_HEADER,
value.data(),
value.size()));
}
ErrorExit:
return _DR_TO_STATUS(dr);
}
세션 열기
이 호출은 DRM 관리자의 Drm_Initialize 함수를 호출하여 AppContext를 초기화해야 합니다.
이 호출 전에 앱은 플러그 인에서 PropertyString을 설정하여 HDS를 저장하는 데 사용할 디바이스 저장소에 대한 경로를 제공해야 합니다.
status_t PlayReadyDrmPlugin::openSession(Vector<luint8_t> &sessionId)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_CONST_STRING dstrDeviceStoreName = { 0 };
String8 deviceStoreName;
Mutex::Autolock lock(&SessionManager::sLock);
ChkMem(mpbOpaqueBuffer =
(DRM_BYTE*)Oem_MemAlloc(MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE));
//
// Application must call setPropertyString to set the DeviceStoreName
// before opening the session.
// So the call to getPropertyString with DeviceStoreName must return a value.
//
ChkDR(getPropertyString(String8("DeviceStoreName"), deviceStoreName));
// Convert the deviceStoreName to a DRM_CONST_STRING */
ChkDR(util_String8ToDrmConstString(deviceStoreName, &dstrDeviceStoreName));
// Initialize AppContext
ChkDR(Drm_Initialize(
&SessionManager::soAppContext,
NULL, // pOEMContext : OEM can initialize and pass if needed.
mpbOpaqueBuffer,
MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE,
&dstrDeviceStoreName));
ErrorExit:
return _DR_TO_STATUS(dr);
}
세션 종료
DRM 세션을 닫으면 Drm_Uninitialize 호출하여 AppContext를 해제해야 합니다.
status_t PlayReadyDrmPlugin::closeSession(Vector<uint8_t> const &sessionId)
{
Mutex::Autolock lock(&SessionManager::sLock);
// Clear the App Context
Drm_Uninitialize(&SessionManager::soAppContext);
SAFE_FREE(mpbOpaqueBuffer);
return OK;
}
getKeyRequest
이 메서드는 라이선스 요청 챌린지를 생성합니다.
ContentHeader - 앱은 initData 매개 변수에서 콘텐츠 헤더를 전달하거나, 이 함수를 호출하기 전에 앱 이 ContentHeader 문자열 속성을 설정해야 합니다.
LicenseChallengeCustomData - 앱은 문자열 키 "LicenseChallengeCustomData"를 사용하여 optionalParameters 매개 변수의 챌린지 사용자 지정 데이터를 전달할 수 있습니다. 또는 앱이 이미 속성으로 설정한 경우 메서드가 LicenseChallengeCustomData 를 선택할 수 있습니다.
이 메서드는 콘텐츠 헤더에서 사용할 수 있는 경우 defaultUrl 과 라이선스 서버로 전송할 요청을 채웁다.
status_t PlayReadyDrmPlugin::getKeyRequest(
Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &initData, s// ContentHeader
String8 const &mimeType,
KeyType keyType,
KeyedVector<String8, String8=""> const &optionalParameters, // ChallengeCustomData
Vector<uint8_t> &request, // Output Challenge
String8 &defaultUrl, // Output URL
KeyRequestType *keyRequestType)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_BYTE *pbContentProperty = NULL;
DRM_DWORD cbContentProperty = 0;
DRM_CHAR *pchCustomData = NULL;
DRM_DWORD cchCustomData = 0;
DRM_CHAR *pchSilentURL = NULL;
DRM_DWORD cchSilentURL = 0;
DRM_BYTE *pbChallenge = NULL;
DRM_DWORD cbChallenge = 0;
DRM_BOOL fHasUrl = FALSE;
Vector<uint8_t> contentHeader;
String8 customData;
Mutex::Autolock lock(&SessionManager::sLock);
if (getValue(optionalParameters, String8("LicenseChallengeCustomData"), customData) == OK)
{
//
// The Application can pass the custom data as a part of the optionalParameters.
// The key string would be "LicenseChallengeCustomData".
// If it is provided. The plug-in should use it when generating the license challenge.
//
pchCustomData = customData.data();
cchCustomData = customData.size();
}
else if (getPropertyString(String8("LicenseChallengeCustomData"), customData) == OK)
{
//
// Alternatively the Application could have provided customData for this operation
// via a previous call to setPropertyString.
// Try to retrieve it if available. Otherwise, skip without failing.
//
pchCustomData = customData.data();
cchCustomData = customData.size();
}
//
// The Application could choose to pass the content header as the initData
// If the initData is passed, use it to call Drm_Content_SetProperty
//
if (value.size() != 0)
{
ChkDR(Drm_Content_SetProperty(
&SessionManager::soAppContext, // DRM_APP_CONTEXT pointer
DRM_CSP_AUTODETECT_HEADER,
value.data(),
value.size()));
}
//
// First, try to retrieve the URL.
//
dr = Drm_LicenseAcq_GenerateChallenge(
&SessionManager::soAppContext,
NULL,
0,
NULL,
pchCustomData,
cchCustomData,
pchSilentURL,
&cchSilentURL, // Attempt to get the URL size.
NULL,
NULL,
pbChallenge,
&cbChallenge);
if (dr == DRM_E_BUFFERTOOSMALL)oi
{
fHasUrl = TRUE;
// ContentHeader has a URL. Allocate buffer for it.
ChkMem(pchSilentURL = (DRM_CHAR*)Oem_MemAlloc(cchSilentURL));
}
else if (dr == DRM_E_NO_URL)
{
// No Url in the content header, no need to fail.
// The Application can get the URL independently.
dr = DRM_SUCCESS;
}
else
{
ChkDR(dr);
}
//
// Second, get the challenge size.
//
dr = Drm_LicenseAcq_GenerateChallenge(
poAppContext,
NULL,
0,
NULL,
pchCustomData,
cchCustomData,
pchSilentURL,
fHasUrl ? &cchSilentURL : NULL,
NULL,
NULL,
pbChallenge,
&cbChallenge);
if (dr == DRM_E_BUFFERTOOSMALL)
{
// Allocate buffer that is sufficient
// to store the license acquisition challenge.
ChkMem(pbChallenge = (DRM_BYTE*)Oem_MemAlloc(cbChallenge));
}
else
{
ChkDR(dr);
}
//
// Finally, generate challenge.
//
ChkDR(Drm_LicenseAcq_GenerateChallenge(
&SessionManager::soAppContext,
NULL,
0,
NULL,
pchCustomData,
cchCustomData,
pchSilentURL,
fHasUrl ? &cchSilentURL : NULL,
NULL,
NULL,
pbChallenge,
&cbChallenge));
//
// Write the License Challenge to the request buffer.
//
request.appendArray(pbChallenge, cbChallenge);
if (fHasUrl)
{
defaultUrl.appendArray(pchSilentURL, cchSilentURL);
}
ErrorExit:
SAFE_OEM_FREE(pbChallenge);
SAFE_OEM_FREE(pchSilentURL);
return _DR_TO_STATUS(dr);
}
핵심 응답을 제공하다
앱에서 KeyRequest (LicenseChallenge)를 라이선스 서버로 보내면, 그 다음에 앱이 KeyResponse (LicenseResponse)을 플러그인에 전달해야 합니다.
status_t PlayReadyDrmPlugin::provideKeyResponse(
Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &response,
Vector<uint8_t> &keySetId)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_LICENSE_RESPONSE oLicenseResponse = { 0 };
DRM_DWORD idx = 0;
DRM_BOOL fExceedMaxLic = FALSE;
Mutex::Autolock lock(&SessionManager::sLock);
//
// Process the response.
//
dr = Drm_LicenseAcq_ProcessResponse(
&SessionManager::soAppContext,
0,
NULL,
NULL,
response.data(),
response.size(),
&oLicenseResponse);
if(dr == DRM_E_LICACQ_TOO_MANY_LICENSES)
{
//
// Received more licenses than DRM_MAX_LICENSE_ACK.
// Allocate space for that.
//
oLicenseResponse.m_cMaxAcks = oLicenseResponse.m_cAcks;
ChkMem(oLicenseResponse.m_pAcks=
(DRM_BYTE*)Oem_MemAlloc(sizeof(DRM_LICENSE_ACK)
* oLicenseResponse.m_cAcks ));
ChkDR(Drm_LicenseAcq_ProcessResponse(
&SessionManager::soAppContext,
0,
NULL,
NULL,
response.data(),
response.size(),
&oLicenseResponse);
fExceedMaxLic = TRUE;
}
ChkDR(dr);
//
// Ensure that all licenses were processed successfully.
//
if(fExceedMaxLic)
{
for (idx = 0; idx < oLicenseResponse.m_cAcks; idx++)
{
ChkDR(oLicenseResponse.m_pAcks[idx].m_dwResult);
}
}
else
{
for (idx = 0; idx < oLicenseResponse.m_cAcks; idx++)
{
ChkDR(oLicenseResponse.m_rgoAcks[idx].m_dwResult);
}
}
ErrorExit:
SAFE_FREE(oLicenseResponse.m_pAcks);
return _DR_TO_STATUS(dr);
}
getSecureStop(s) 가져오기
앱은 getSecureStop 메서드를 호출하여 지정된 보안 중지 ID에 대한 보안 중지 챌린지를 생성할 수 있습니다. 또는 앱에서 getSecureStops 를 호출하여 기존의 모든 보안 중지 세션에 대한 챌린지를 생성할 수 있습니다.
보안 중지 챌린지를 생성하기 전에 앱은 setPropertyByteArray 를 호출하고 SecureStopPublisherCert를 전달하여 게시자 인증서를 제공해야 합니다.
필요에 따라 앱은 보안 중지 챌린지의 일부로 포함할 SecureStopCustomData 를 제공할 수도 있습니다.
보안 중지 챌린지를 만든 후 앱은 이를 서버로 보내고 releaseSecureStops 메서드에 대한 보안 중지 응답을 다시 제공해야 합니다.
DRM_RESULT PlayReadyDrmPlugin::_getSecureStop(
DRM_ID *pIdSession,
DRM_BYTE **ppbChallenge,
DRM_DWORD *pcbChallenge)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_CHAR *pchCustomData = NULL;
DRM_DWORD cchCustomData = 0;
String8 customData;
Vector<uint8_t> publisherCert;
if (getPropertyString("SecureStopCustomData", customData) == OK)
{
// SecureStop CustomData is optional
pchCustomData = customData.data();
cchCustomData = customData.size();
}
// Application must set SecureStopPublisherCert before calling getSecureStop(s)
ChkDR(getPropertyByteArray("SecureStopPublisherCert", publisherCert));
ChkDR(Drm_SecureStop_GenerateChallenge(
&SessionManager::soAppContext,
pIdSession,
publisherCert.size(),
publisherCert.data(),
cchCustomData,
pchCustomData,
pcbChallenge,
ppbChallenge));
ErrorExit:
return dr;
}
status_t PlayReadyDrmPlugin::getSecureStop(
Vector<uint8_t> const &ssid, // In
Vector<uint8_t> &secureStop) // Out
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_ID idSecureStop = { 0 };
DRM_BYTE *pbChallenge = NULL;
DRM_DWORD cbChallenge = 0;
Mutex::Autolock lock(&SessionManager::sLock);
ChkArg(ssid.size() == sizeof(DRM_ID));
memcpy(&idSecureStop, ssid.data(), sizeof(DRM_ID));
ChkDR(_getSecureStop(
&idSecureStop,
&pbChallenge,
&cbChallenge));
secureStop.appendArray(pbChallenge, cbChallenge);
ErrorExit:
SAFE_FREE(pbChallenge);
return _DR_TO_STATUS(dr);
}
status_t PlayReadyDrmPlugin::getSecureStops(
List<Vector<uint8_t> > &secureStops)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_BYTE *pbChallenge = NULL;
DRM_DWORD cbChallenge = 0;
Vector<uint8_t> secureStopChallenge;
Mutex::Autolock lock(&SessionManager::sLock);
ChkDR(_getSecureStop(
NULL,
&pbChallenge,
&cbChallenge));
secureStopChallenge.appendArray(pbChallenge, cbChallenge);
secureStops.push_back(secureStopChallenge);
ErrorExit:
SAFE_FREE(pbChallenge);
return _DR_TO_STATUS(dr);
}
보안 정지 해제
앱이 서버에서 보안 중지 응답을 받으면 메시지를 플러그 인으로 전달하여 스토리지에서 보안 중지 정보를 제거해야 합니다.
status_t PlayReadyDrmPlugin::releaseSecureStops(
Vector<uint8_t> const &ssRelease)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_CHAR *pchCustomData = NULL;
DRM_DWORD cchCustomData = 0;
Vector<uint8_t> publisherCert;
Mutex::Autolock lock(&SessionManager::sLock);
// Application must set SecureStopPublisherCert before calling
// releaseSecureStops
ChkDR(getPropertyByteArray("SecureStopPublisherCert", publisherCert));
ChkDR(Drm_SecureStop_ProcessResponse(
&SessionManager::soAppContext,
NULL,
publisherCert.size(),
publisherCert.data(),
ssRelease.size(),
ssRelease.data(),
&cchCustomData,
&pchCustomData));
ErrorExit:
SAFE_FREE(pchCustomData);
return _DR_TO_STATUS(dr);
}
지원되지 않는 API(작업 없음)
다음 API 목록은 PlayReady에 해당 작업이 없으며 지원되는 시나리오를 성공적으로 실행하는 데 필요하지 않습니다.
키 상태 조회
status_t PlayReadyDrmPlugin::queryKeyStatus(
Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8=""> &infoMap) const
{ return _DR_TO_STATUS(DRM_E_NOTIMPL); }
프로비전 요청 가져오기
PlayReady는 Android 디바이스에서만 로컬 프로비저닝을 지원합니다.
status_t PlayReadyDrmPlugin::getProvisionRequest(
String8 const &certType,
String8 const &certAuthority,
Vector<uint8_t> &request,
String8 &defaultUrl) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
provideProvisionResponse
PlayReady는 Android 디바이스에서만 로컬 프로비저닝을 지원합니다.
status_t PlayReadyDrmPlugin::provideProvisionResponse(
Vector<uint8_t> const &response,
Vector<uint8_t> &certificate,
Vector<uint8_t> &wrappedKey) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
장치 프로비저닝 해제
PlayReady는 Android 디바이스에서만 로컬 프로비저닝을 지원합니다.
status_t PlayReadyDrmPlugin::unprovisionDevice() { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
암호 알고리즘 설정
PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.
status_t PlayReadyDrmPlugin::setCipherAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
Mac 알고리즘 설정
PlayReady는 임의의 데이터에 서명/확인하지 않습니다.
status_t PlayReadyDrmPlugin::setMacAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
암호화하다
PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.
status_t PlayReadyDrmPlugin::encrypt(
Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &keyId,
Vector<uint8_t> const &input,
Vector<uint8_t> const &iv,
Vector<uint8_t> &output) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
해독하다
PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.
status_t PlayReadyDrmPlugin::decrypt(
Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &keyId,
Vector<uint8_t> const &input,
Vector<uint8_t> const &iv,
Vector<uint8_t> &output) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
기호
PlayReady는 임의의 데이터에 서명/확인하지 않습니다.
status_t PlayReadyDrmPlugin::sign(
Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &keyId,
Vector<uint8_t> const &message,
Vector<uint8_t> &signature) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
확인
PlayReady는 임의의 데이터에 서명/확인하지 않습니다.
status_t PlayReadyDrmPlugin::verify(
Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &keyId,
Vector<uint8_t> const &message,
Vector<uint8_t> const &signature,
bool &match) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
signRSA
PlayReady는 임의의 데이터에 서명/확인하지 않습니다.
status_t PlayReadyDrmPlugin::signRSA(
Vector<uint8_t> const &sessionId,
String8 const &algorithm,
Vector<uint8_t> const &message,
Vector<uint8_t> const &wrappedKey,
Vector<uint8_t> &signature) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
5. PlayReadyCryptoPlugin
PlayReadyCryptoPlugin
PlayReadyCryptoPlugin 개체를 만드는 생성자입니다.
PlayReadyCryptoPlugin::PlayReadyCryptoPlugin(
const uint8_t sessionId[16],
const void * data,
size_t size);
{
DRM_RESULT dr = DRM_SUCCESS;
Mutex::Autolock lock(&SessionManager::sLock);
ChkDR(Drm_Reader_Bind(
&SessionManager::soAppContext,
NULL,
0,
NULL,
NULL,
&moDecryptContext));
ErrorExit:
// Cache the Bind Result and check for it on the first call to decrypt.
mdrBindResult = dr;
}
보안 디코더 구성 요소 필요
PlayReady 암호화된 콘텐츠를 처리하려면 보안 디코더 구성 요소가 필요합니다.
bool PlayReadyCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
{
// Must always return true for PlayReady Content.
return true;
}
해독하다
MediaCodec에서 호출하는 암호 해독입니다.
암호 해독 메서드는 명확한 샘플을 제공하지 않고 OEM 관련 핸들을 제공하므로 OEM은 MediaCodec 가 해당 핸들에서 올바르게 작동할 수 있는지 확인해야 합니다.
ssize_t PlayReadyCryptoPlugin::decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
Mode mode,
const void *srcPtr,
const SubSample *subSamples,
size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg)
{
DRM_RESULT dr = DRM_SUCCESS;
DRM_DWORD idx = 0;
DRM_DWORD idxSubSample = 0;
DRM_DWORD cbEncryptedContent = 0;
DRM_UINT64 ui64InitializationVector;
DRM_DWORD *pdwEncryptedRegionMappings = NULL;
DRM_DWORD cbOpaqueClearContentHandle = 0;
Mutex::Autolock lock(mLock);
// Only AES_CTR is supported
ChkBOOL(mode == kMode_AES_CTR, DRM_E_UNSUPPORTED_ALGORITHM);
ChkArg(secure == TRUE);
// Ensure that bind returned successfully in the constructor.
ChkDR( mdrBindResult );
// Allocate a list for the region mapping.
ChkMem(pdwEncryptedRegionMappings
= Oem_MemAlloc(sizeof(DRM_DWORD)* numSubSamples * 2));
// Fill the region mappings list with the information
// from the subSamples.
for (idxSubSample = 0; idxSubSample < numSubSamples; idxSubSample++)
{
pdwEncryptedRegionMappings[idx++]
= subSamples[idxSubSample].mNumBytesOfClearData;
pdwEncryptedRegionMappings[idx++]
= subSamples[idxSubSample].mNumBytesOfEncryptedData;
// Calculate the total number of bytes
cbEncryptedContent +=
(subSamples[idxSubSample].mNumBytesOfClearData
+ subSamples[idxSubSample].mNumBytesOfEncryptedData);
}
// Convert the IV from a uint8 array to a DRM_UINT64
// Endianess should be taken into consideration.
ChkStatus(initializeIV(iv, &ui64InitializationVector));
// Decrypt
ChkDR(Drm_Reader_DecryptOpaque(
&moDecryptContext,
numSubSamples * 2,
pdwEncryptedRegionMappings,
ui64InitializationVector,
cbEncryptedContent,
srcPtr,
&cbOpaqueClearContentHandle,
&dstPtr);
ErrorExit:
// Free the Region mappings list.
SAFE_FREE(pdwEncryptedRegionMappings);
if (DRM_FAILED(dr))
{
// If an error occurs, fill the errorMessage
// then return the DRM_RESULT
SetErrorDetails(errorDetailMsg, dr);
return _DR_TO_STATUS(dr);
}
// In case of success, return the size of the handle pointing
// to the clear content.
return cbOpaqueClearContentHandle;
}
~PlayReadyCryptoPlugin
암호화 플러그 인 소멸자는 암호 해독기 컨텍스트를 닫아야 합니다.
~PlayReadyCryptoPlugin::PlayReadyCryptoPlugin()
{
Mutex::Autolock lock(mLock);
Drm_Reader_Close(&moDecryptContext);
}
6. PlayReadyDrmFactory 및 PlayReadyCryptoFactory
PlayReadyDrmFactory 및 PlayReadyCryptoFactory 인터페이스의 구현은 각각 PlayReadyDrmPlugin 및 PlayReadyCryptoPlugin의 인스턴스를 만드는 데 필요합니다.
두 팩터리 클래스는 모두 호출자에게 isCryptoSchemeSupported API를 올바르게 구현하여 PlayReady로 보호된 콘텐츠를 사용할 수 있는 개체 만들기를 지원함을 나타내야 합니다.
const uint8_t playready_uuid[16] =
{0x79, 0xf0, 0x04, 0x9a, 0x40, 0x98, 0x86, 0x42, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95};
bool isCryptoSchemeSupported(const uint8_t uuid[16])
{
return (!memcmp(uuid, playready_uuid, sizeof(playready_uuid)));
}
7. SessionManager
DRM_APP_CONTEXT 유지하고 PlayReadyDrmPlugin과 PlayReadyCryptoPlugin 간에 공유할 수 있도록 싱글톤 세션 관리자를 구현해야 합니다.
동일한 목적을 달성하는 다른 디자인도 허용됩니다.
| SessionManager |
|---|
| static DRM_APP_CONTEXT soAppContext |
| static Mutex sLock |