Android 사양용 PlayReady 플러그 인
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 - 앱은 모든 보안 중지 관련 호출 전에 Publisher 인증서를 한 번 설정해야 합니다.
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);
}
openSession
이 호출은 DRM Manager의 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);
}
closeSession
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);
}
provideKeyResponse
앱에서 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
앱은 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);
}
releaseSecureStops
앱이 서버에서 보안 중지 응답을 받으면 메시지를 플러그 인으로 전달하여 스토리지에서 보안 중지 정보를 제거해야 합니다.
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에 해당 작업이 없으며 지원되는 시나리오를 성공적으로 실행하는 데 필요하지 않습니다.
queryKeyStatus
status_t PlayReadyDrmPlugin::queryKeyStatus(
Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8=""> &infoMap) const
{ return _DR_TO_STATUS(DRM_E_NOTIMPL); }
getProvisionRequest
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); }
unprovisionDevice
PlayReady는 Android 디바이스에서만 로컬 프로비저닝을 지원합니다.
status_t PlayReadyDrmPlugin::unprovisionDevice() { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
setCipherAlgorithm
PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.
status_t PlayReadyDrmPlugin::setCipherAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
setMacAlgorithm
PlayReady는 임의의 데이터에 서명/확인하지 않습니다.
status_t PlayReadyDrmPlugin::setMacAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
encrypt
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); }
decrypt
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); }
sign
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); }
verify
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;
}
requiresSecureDecoderComponent
PlayReady 암호화된 콘텐츠를 처리하려면 보안 디코더 구성 요소가 필요합니다.
bool PlayReadyCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
{
// Must always return true for PlayReady Content.
return true;
}
decrypt
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 |