Udostępnij za pośrednictwem


Wtyczka PlayReady dla specyfikacji systemu Android

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

Interfejs PlayReadyDRMPlugin

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

PlayReadyCryptoPlugin, interfejs

3. Operacja

W poniższych krokach opisano prosty scenariusz odtwarzania:

  1. Aplikacja tworzy obiekt MediaDrm, co spowoduje utworzenie wystąpienia wtyczki PlayReadyDrmPlugin.

  2. Następnie wywołaj metodę openSession, co spowoduje zainicjowanie menedżera DRM.

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

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

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

  6. Następnie wszystkie wywołania odszyfrowywania będą korzystać z metody PlayReadyCryptoPlugin::decrypt, która zwróci uchwyt do odszyfrowanych próbek.

Prosty diagram przepływu odtwarzania

Proste warstwy odtwarzania

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