Freigeben über


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.

PlayReadyDRMPlugin interface

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.

PlayReadyCryptoPlugin interface

3. Vorgang

Die folgenden Schritte beschreiben ein einfaches Wiedergabeszenario:

  1. Die App erstellt das MediaDrm-Objekt , das zu der Instanziierung von PlayReadyDrmPlugin führt.

  2. Rufen Sie dann openSession auf, wodurch die Initialisierung des DRM-Managers erfolgt.

  3. 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.

  4. 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.

  5. 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.

  6. Danach verwenden alle Entschlüsselungsaufrufe die PlayReadyCryptoPlugin::d ecrypt-Methode , die einen Handle an die entschlüsselten Beispiele zurückgibt.

Simple Playback Flowchart

Simple Playback Layers

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