Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
1. Wprowadzenie
Ta specyfikacja ustanawia wskazówki dla OEM w celu zaimplementowania wtyczek do zarządzania prawami cyfrowymi (DRM) opartych na technologii PlayReady 4.0 w systemie Android. Aby uzyskać informacje, zobacz https://developer.android.com/reference/android/media/MediaDrm.html.
Specyfikacja dostarcza informacje na temat tego, jak interfejsy API wtyczek są odwzorowywane w wywołania PlayReady.
1.1. Historia zmian
wersja | Zmiana |
---|---|
Maj 2016 r. | Wersja początkowa |
2. Interfejsy
Element PlayReadyDrmPlugin udostępnia implementację interfejsu wtyczki DRM. PlayReadyDrmPlugin jest odpowiedzialny za opakowywanie interfejsów API menedżera DRM i wykonanie odpowiedniego tłumaczenia parametrów określonych przez interfejs na format, w którym PlayReady może działać.
PlayReadyCryptoPlugin udostępnia implementację interfejsu wtyczki Crypto, który jest odpowiedzialny za odszyfrowywanie przykładów. OEM musi zapewnić, że odszyfrowane próbki nigdy nie opuszczają zaufanego środowiska wykonawczego (TEE).
3. Operacja
W poniższych krokach opisano prosty scenariusz odtwarzania:
Aplikacja tworzy obiekt MediaDrm, co spowoduje utworzenie wystąpienia wtyczki PlayReadyDrmPlugin.
Następnie wywołaj metodę openSession, co spowoduje zainicjowanie menedżera DRM.
Następnie aplikacja wywoła metodę getKeyRequest i przekaże nagłówek zawartości wyodrębniony z zawartości jako parametr initData . Ponadto aplikacja może również przekazać niestandardowe dane dotyczące wyzwania w pozyskaniu licencji w wektorze key-value optionalParameters. Dane niestandardowe dotyczące wyzwania pozyskiwania licencji powinny być następnie przekazywane do menedżera DRM jako wywołanie Drm_Content_SetProperty.
W tym momencie aplikacja będzie mogła wykonać wywołania (getKeyRequest / provideKeyResponse), które będą generować równoważne wywołania (Drm_LicenseAcq_GenerateChallenge) / Drm_LicenseAcq_ProcessResponse) na Menedżerze DRM.
Następnie aplikacja może utworzyć wystąpienie obiektu MediaCrypto, który utworzy wystąpienie interfejsu PlayReadyCryptoPlugin (opakowującego DRM_DECRYPT_CONTEXT), gdy zostanie zwrócone wywołanie Drm_Reader_Bind.
Następnie wszystkie wywołania odszyfrowywania będą korzystać z metody PlayReadyCryptoPlugin::decrypt, która zwróci uchwyt do odszyfrowanych próbek.
4. PlayReadyDRMPlugin
setPropertyString
Aplikacje będą używać tej metody do przekazywania parametrów do PlayReady, co nie jest możliwe w inny sposób ze względu na bieżący projekt interfejsów API wtyczek.
DeviceStoreName — aplikacja musi ustawić lokalizację magazynu urządzeń jako właściwość przed otwarciem sesji. Następnie PlayReady wyszuka właściwość o nazwie DeviceStoreName podczas inicjowania menedżera DRM przy wywołaniach funkcji openSession. Ścieżka do sklepu urządzeń musi być dostępna dla aplikacji w sposób podobny do katalogu danych prywatnych aplikacji. Właściwość powinna wskazywać ścieżkę/nazwę pliku<, które powinny zawierać usługę HDS>.
LicenseChallengeCustomData — aplikacja może opcjonalnie ustawić tę właściwość przed wywołaniem getKeyRequest, gdzie PlayReady wyszuka właściwość, utworzy wyzwanie uzyskania licencji i uwzględni dane niestandardowe w żądaniu.
SecureStopCustomData — aplikacja może opcjonalnie ustawić tę właściwość przed wywołaniem, by wygenerować zapytanie Secure Stop. PlayReady wyszuka właściwość, utworzy wyzwanie Secure Stop i uwzględni dane niestandardowe w żądaniu.
SelectKID — w sytuacjach, w których aplikacja uzyskała wiele licencji w pamięci w jednej partii, może określić kodowany base64 KID do powiązania.
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
Aplikacje będą używać tej metody do przekazywania parametrów do PlayReady, które nie są możliwe w inny sposób z powodu aktualnego projektu interfejsów API wtyczek.
ContentHeader — aplikacja powinna ustawić nagłówek zawartości przy użyciu wtyczki przed próbą utworzenia obiektu Crypto. Nagłówek zawartości może mieć jeden z następujących formatów:
Tablica bajtów z zawartością obiektu PlayReady.
Tablica bajtów z zawartością nagłówków V2, V2.4, V4, V4.1, V4.2 (unicode XML).
Szeroka tablica znaków określająca 24-znakowy identyfikator KeyID.
SecureStopPublisherCert — aplikacja powinna ustawić certyfikat wydawcy jednokrotnie przed wszelkimi wywołaniami związanymi z funkcją bezpiecznego zatrzymania.
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);
}
otwórzSesję
To wywołanie powinno spowodować zainicjowanie obiektu AppContext przez wywołanie funkcji Drm_Initialize menedżera DRM.
Przed wykonaniem tego wywołania aplikacja musi ustawić PropertyString na wtyczkę, aby podać ścieżkę do magazynu urządzeń, który będzie używany do przechowywania usługi 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);
}
zamknijSesję
Zamknięcie sesji DRM powinno wywołać funkcję Drm_Uninitialize, aby zwolnić 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
Ta metoda spowoduje wygenerowanie żądania licencji.
ContentHeader — aplikacja może przekazać nagłówek zawartości w parametrze initData lub przed wywołaniem tej funkcji aplikacja musi ustawić właściwość ciągu ContentHeader .
LicenseChallengeCustomData — aplikacja może przekazać dane niestandardowe wyzwania w opcjonalnym parametrzeParameters z kluczem ciągu "LicenseChallengeCustomData". Alternatywnie metoda może wykorzystać właściwość LicenseChallengeCustomData jeśli aplikacja już ją ustawiła jako właściwość.
Metoda wypełni wartość defaultUrl , jeśli jest dostępna w nagłówku zawartości, a także żądanie, które ma zostać wysłane do serwera licencji.
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);
}
dostarcz odpowiedź kluczową
Po wysłaniu przez aplikację KeyRequest (LicenseChallenge)
do serwera licencji, aplikacja powinna przekazać KeyResponse (LicenseResponse)
do wtyczki.
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)
Aplikacje mogą wywoływać metodę getSecureStop, aby wygenerować wyzwanie związane z bezpiecznym zatrzymaniem dla określonego identyfikatora bezpiecznego zatrzymania. Alternatywnie aplikacja może wywołać funkcję getSecureStops, aby wygenerować wyzwanie dla wszystkich istniejących sesji bezpiecznego zatrzymania.
Przed wygenerowaniem wyzwania bezpiecznego zatrzymania aplikacja musi podać certyfikat wydawcy przez wywołanie polecenia setPropertyByteArray i przekazanie certyfikatu SecureStopPublisherCert.
Opcjonalnie aplikacja może również dostarczyć SecureStopCustomData, który ma zostać uwzględniony jako część wyzwania związanego z bezpiecznym zatrzymaniem.
Po utworzeniu wyzwania bezpiecznego zatrzymania aplikacja powinna wysłać ją do serwera i przekazać z powrotem bezpieczną odpowiedź zatrzymania do metody 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
Kiedy aplikacja odbierze odpowiedź bezpiecznego zatrzymania z serwera, wiadomość powinna zostać przekazana do wtyczki, aby usunąć informacje o bezpiecznym zatrzymaniu z przechowywania.
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);
}
Nieobsługiwane interfejsy API (brak operacji)
Poniższa lista interfejsów API nie ma odpowiedniej operacji w usłudze PlayReady i nie jest wymagana do pomyślnego wykonania obsługiwanych scenariuszy.
queryKeyStatus
status_t PlayReadyDrmPlugin::queryKeyStatus(
Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8=""> &infoMap) const
{ return _DR_TO_STATUS(DRM_E_NOTIMPL); }
PobierzWniosekZaopatrzenia
Usługa PlayReady obsługuje aprowizację lokalną tylko na urządzeniach z systemem 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
Usługa PlayReady obsługuje aprowizację lokalną tylko na urządzeniach z systemem 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
Usługa PlayReady obsługuje aprowizację lokalną tylko na urządzeniach z systemem Android.
status_t PlayReadyDrmPlugin::unprovisionDevice() { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
setCipherAlgorithm
PlayReady nie szyfruje ani nie odszyfrowuje dowolnych danych.
status_t PlayReadyDrmPlugin::setCipherAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
setMacAlgorithm
PlayReady nie podpisuje ani weryfikuje dowolnych danych.
status_t PlayReadyDrmPlugin::setMacAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
szyfrować
Element PlayReady nie szyfruje ani nie odszyfrowuje dowolnych danych.
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); }
odszyfrować
Element PlayReady nie szyfruje/odszyfrowuje jakichkolwiek danych.
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); }
znak
Element PlayReady nie podpisuje ani nie weryfikuje dowolnie wybranych danych.
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); }
zweryfikować
PlayReady nie podpisuje ani nie weryfikuje dowolnych danych.
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 (funkcja podpisu RSA)
Element PlayReady nie podpisuje/nie weryfikuje dowolnych danych.
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
Konstruktor do utworzenia obiektu 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;
}
wymaga bezpiecznego komponentu dekodera
Składnik bezpiecznego dekodera jest wymagany do przetwarzania zaszyfrowanej zawartości PlayReady.
bool PlayReadyCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
{
// Must always return true for PlayReady Content.
return true;
}
odszyfrować
Odszyfrowywanie wywoływane przez program MediaCodec.
Ponieważ metoda odszyfrowywania nie daje przejrzystych próbek, ale raczej uchwyt specyficzny dla producenta OEM, producent musi upewnić się, że MediaCodec może działać poprawnie na tym uchwycie.
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
Destruktor wtyczki kryptograficznej musi zamknąć kontekst odszyfrowywania.
~PlayReadyCryptoPlugin::PlayReadyCryptoPlugin()
{
Mutex::Autolock lock(mLock);
Drm_Reader_Close(&moDecryptContext);
}
6. PlayReadyDrmFactory i PlayReadyCryptoFactory
Implementacja interfejsów PlayReadyDrmFactory i PlayReadyCryptoFactory jest wymagana do tworzenia wystąpień elementów PlayReadyDrmPlugin i PlayReadyCryptoPlugin .
Obie klasy fabryczne muszą wskazywać wywołującym, że obsługują tworzenie obiektów, które mogą korzystać z zawartości chronionej przez PlayReady, przez prawidłowe zaimplementowanie interfejsu API isCryptoSchemeSupported.
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
Menedżer sesji pojedynczej należy zaimplementować w celu przechowywania DRM_APP_CONTEXT i zezwalania na udostępnianie go między elementami PlayReadyDrmPlugin i PlayReadyCryptoPlugin.
Inne projekty, które osiągną ten sam cel, są również dopuszczalne.
SessionManager |
---|
static DRM_APP_CONTEXT soAppContext |
statyczny Mutex sLock |