PlayReady-Plug-In für Android Spezifikation
1. Einführung
Diese Spezifikation stellt Anleitungen für OEMs fest, um PlayReady 4.0 basierende digitale Rechteverwaltung (DRM)-Plug-Ins auf Android zu implementieren. Referenz finden Sie unter https://developer.android.com/reference/android/media/MediaDrm.html.
Diese Spezifikation enthält Informationen zur Zuordnung der Plug-In-APIs zu PlayReady-Aufrufen.
1.1. Änderungsverlauf
Version | Change |
---|---|
Mai 2016 | Ursprüngliche Version |
2. Schnittstellen
PlayReadyDrmPlugin stellt die Implementierung für die DRM-Plug-In-Schnittstelle bereit. PlayReadyDrmPlugin ist verantwortlich für das Umbruch der DRM-Manager-APIs und das Ausführen der richtigen Übersetzung für die Parameter, die von der Schnittstelle angegeben werden, in ein Format, auf dem PlayReady ausgeführt werden kann.
PlayReadyCryptoPlugin stellt die Implementierung für die Crypto-Plug-In-Schnittstelle bereit, die für die Entschlüsselung der Beispiele verantwortlich ist. Der OEM muss sicherstellen, dass die entschlüsselten Beispiele niemals die vertrauenswürdige Ausführungsumgebung (TEE) verlassen.
3. Vorgang
Die folgenden Schritte beschreiben ein einfaches Wiedergabeszenario:
Die App erstellt das MediaDrm-Objekt , das zu der Instanziierung von PlayReadyDrmPlugin führt.
Rufen Sie dann openSession auf, wodurch die Initialisierung des DRM-Managers erfolgt.
Die App ruft dann getKeyRequest auf und übergeben den Inhaltsheader aus dem Inhalt als InitData-Parameter . Darüber hinaus kann die App auch die benutzerdefinierten Daten der Lizenzkäufe in dem optionalParameters-Schlüsselwertvektor übergeben. Die Benutzerdefinierten Daten für die Lizenzkäufe sollten dann als Drm_Content_SetProperty Aufruf an den DRM-Manager weitergegeben werden.
An diesem Punkt kann die App die Aufrufe (getKeyRequest / provideKeyResponse) ausführen, die den äquivalenten Aufruf (Drm_LicenseAcq_GenerateChallenge) / Drm_LicenseAcq_ProcessResponse) des DRM-Managers erzeugen.
Die App kann dann ein MediaCrypto-Objekt instanziieren, das eine Instanz einer PlayReadyCryptoPlugin-Schnittstelle (Umbruch DRM_DECRYPT_CONTEXT) erstellt, wenn der Drm_Reader_Bind Aufruf zurückgibt.
Danach verwenden alle Entschlüsselungsaufrufe die PlayReadyCryptoPlugin::d ecrypt-Methode , die einen Handle an die entschlüsselten Beispiele zurückgibt.
4. PlayReadyDRMPlugin
setPropertyString
Apps verwenden diese Methode, um Parameter an PlayReady zu übergeben, die aufgrund des aktuellen Designs der Plug-In-APIs nicht andernfalls möglich sind.
DeviceStoreName – Die App muss den Speicherort des Gerätespeichers als Eigenschaft festlegen, bevor sie eine Sitzung öffnen. Dann wird PlayReady die DeviceStoreName-Eigenschaft nachschlagen, wenn sie den DRM-Manager während der Aufrufe zu openSession initialisieren. Der Pfad zum Gerätespeicher muss für die App wie das private Datenverzeichnis der App zugänglich sein. Die Eigenschaft sollte auf einen <Pfad/Dateinamen> verweisen, der die HDS enthalten soll.
LicenseChallengeCustomData – Die App kann diese Eigenschaft optional vor Aufrufen auf getKeyRequest festlegen, wobei PlayReady die Eigenschaft nachschlagen und eine Lizenzkauf-Herausforderung verfassen und die benutzerdefinierten Daten in die Anforderung einschließen kann.
SecureStopCustomData – Die App kann diese Eigenschaft optional vor einem Aufruf festlegen, um die Secure Stop-Herausforderung zu generieren. PlayReady wird die Eigenschaft nachschlagen und die Secure Stop-Herausforderung verfassen und die benutzerdefinierten Daten in die Anforderung einschließen.
SelectKID – In Situationen, in denen die App mehrere Speicherlizenzen in einem einzelnen Batch erworben hat, kann sie eine base64 codierte KID für die Bindung angeben.
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
Apps verwenden diese Methode, um Parameter an PlayReady zu übergeben, die aufgrund des aktuellen Designs der Plug-In-APIs nicht andernfalls möglich sind.
ContentHeader– Die App sollte den Inhaltsheader im Plug-In festlegen, bevor Sie versuchen, ein Crypto-Objekt zu erstellen. Der Inhaltsheader könnte sich in einem der folgenden Formate befinden:
Byte-Array mit dem Inhalt des PlayReady-Objekts.
Byte-Array mit dem Inhalt der V2, V2.4, V4, V4.1, V4.2-Header (Unicode XML).
Breites Zeichenarray, das die 24-Zeichen-KeyID angibt.
SecureStopPublisherCert – Die App sollte das Publisher Zertifikat einmal festlegen, bevor alle sicheren Aufrufe beendet werden.
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
Dieser Aufruf sollte dazu führen, dass der AppContext initialisiert wird, indem die Drm_Initialize Funktion des DRM-Managers aufgerufen wird.
Vor diesem Aufruf muss die App PropertyString auf dem Plug-In festlegen, um den Pfad zum Gerätespeicher bereitzustellen, der zum Speichern des HDS verwendet wird.
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
Das Schließen einer DRM-Sitzung sollte Drm_Uninitialize aufrufen, um den AppContext zu veröffentlichen.
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
Diese Methode generiert eine Anforderungsanforderungsanforderung.
ContentHeader– Die App kann entweder den Inhaltsheader im InitData-Parameter übergeben oder vor dem Aufrufen dieser Funktion die ContentHeader-Zeichenfolgeneigenschaft festlegen.
LicenseChallengeCustomData – Die App kann die Herausforderung benutzerdefinierte Daten im optionalParameters-Parameter mit dem Zeichenfolgenschlüssel "LicenseChallengeCustomData" übergeben. Alternativ kann die Methode die LicenseChallengeCustomData abrufen, wenn die App sie bereits als Eigenschaft festgelegt hat.
Die Methode wird die defaultUrl auffüllen, wenn sie im Inhaltsheader verfügbar ist, sowie die Anforderung, an den Lizenzserver gesendet zu werden.
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
Sobald eine KeyRequest (LicenseChallenge)
App an den Lizenzserver gesendet wurde, sollte die App die KeyResponse (LicenseResponse)
Ans-Plug-In übergeben.
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)
Apps können die getSecureStop-Methode aufrufen, um eine sichere Stopp-Herausforderung für die angegebene sichere Stopp-ID zu generieren. Alternativ kann die App getSecureStops aufrufen, um eine Herausforderung für alle vorhandenen Sicherstoppsitzungen zu generieren.
Bevor Sie die Herausforderung für den sicheren Stopp generieren, muss die App das Herausgeberzertifikat bereitstellen, indem Sie setPropertyByteArray aufrufen und SecureStopPublisherCert übergeben.
Optional kann die App auch SecureStopCustomData bereitstellen, um als Teil der Herausforderung für den sicheren Stopp enthalten zu werden.
Nachdem die Herausforderung für den sicheren Stopp erstellt wurde, sollte die App sie an den Server senden und die Antwort auf den sicheren Stopp an die ReleaseSecureStops-Methode zurückgeben.
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
Sobald die App die Antwort vom Server erhält, sollte die Nachricht an das Plug-In übergeben werden, um die sicheren Stoppinformationen aus dem Speicher zu entfernen.
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);
}
Nicht unterstützte APIs (Kein Vorgang)
Die folgende Liste der APIs verfügt nicht über einen entsprechenden Vorgang in PlayReady und ist nicht für die erfolgreiche Ausführung der unterstützten Szenarien erforderlich.
queryKeyStatus
status_t PlayReadyDrmPlugin::queryKeyStatus(
Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8=""> &infoMap) const
{ return _DR_TO_STATUS(DRM_E_NOTIMPL); }
getProvisionRequest
PlayReady unterstützt nur lokale Bereitstellung auf Android Geräten.
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 unterstützt nur lokale Bereitstellung auf Android Geräten.
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 unterstützt nur lokale Bereitstellung auf Android Geräten.
status_t PlayReadyDrmPlugin::unprovisionDevice() { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
setCipherAlgorithm
PlayReady verschlüsselt/entschlüsselt keine beliebigen Daten.
status_t PlayReadyDrmPlugin::setCipherAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
setMacAlgorithm
PlayReady signiert/überprüft keine beliebigen Daten.
status_t PlayReadyDrmPlugin::setMacAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
encrypt
PlayReady verschlüsselt/entschlüsselt keine beliebigen Daten.
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); }
Entschlüsseln
PlayReady verschlüsselt/entschlüsselt keine beliebigen Daten.
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); }
Signieren
PlayReady signiert/überprüft keine beliebigen Daten.
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); }
Überprüfen
PlayReady signiert/überprüft keine beliebigen Daten.
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 signiert/überprüft keine beliebigen Daten.
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 zum Erstellen eines PlayReadyCryptoPlugin-Objekts .
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;
}
erfordertSecureDecoderComponent
Eine sichere Decoderkomponente ist erforderlich, um verschlüsselte Inhalte von PlayReady zu verarbeiten.
bool PlayReadyCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
{
// Must always return true for PlayReady Content.
return true;
}
Entschlüsseln
Die von MediaCodec aufgerufene Entschlüsselung.
Da die Entschlüsselungsmethode keine klaren Beispiele ausgibt, sondern ein OEM-spezifischer Handle, muss der OEM sicherstellen, dass MediaCodec ordnungsgemäß auf diesem Handle funktionieren kann.
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
Der Krypto-Plug-In-Destruktor muss den Entschlüsselungskontext schließen.
~PlayReadyCryptoPlugin::PlayReadyCryptoPlugin()
{
Mutex::Autolock lock(mLock);
Drm_Reader_Close(&moDecryptContext);
}
6. PlayReadyDrmFactory und PlayReadyCryptoFactory
Die Implementierung der PlayReadyDrmFactory- und PlayReadyCryptoFactory-Schnittstellen ist für das Erstellen von Instanzen von PlayReadyDrmPlugin und PlayReadyCryptoPlugin erforderlich.
Beide Factoryklassen müssen aufrufende Benutzer angeben, dass sie das Erstellen von Objekten unterstützen, die PlayReady geschützte Inhalte nutzen können, indem sie die isCryptoSchemeSupported-API ordnungsgemäß implementieren.
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
Ein Singleton-Sitzungs-Manager muss implementiert werden, um die DRM_APP_CONTEXT zu halten und die Freigabe zwischen playReadyDrmPlugin und PlayReadyCryptoPlugin zu ermöglichen.
Andere Designs, die den gleichen Zweck erreichen, sind auch zulässig.
SessionManager |
---|
statische DRM_APP_CONTEXT soAppContext |
Statische Mutex-sLock |