Aracılığıyla paylaş


Android Belirtimi için PlayReady Eklentisi

1. Giriş

Bu belirtim, OEM'lerin Android'de PlayReady 4.0 tabanlı dijital hak yönetimi (DRM) eklentilerini uygulaması için rehberlik sağlar. Referans için bkz. https://developer.android.com/reference/android/media/MediaDrm.html.

Bu belirtim, eklenti API'lerinin PlayReady çağrılarıyla nasıl eş olduğu hakkında bilgi sağlar.

1.1. Değişiklik Geçmişi

Sürüm Değişiklik
Mayıs 2016 İlk sürüm

2. Arabirimler

PlayReadyDrmPlugin , DRM eklenti arabirimi için uygulamayı sağlar. PlayReadyDrmPlugin , DRM Yöneticisi API'lerini sarmalamaktan ve arabirim tarafından belirtilen parametreler için uygun çeviriyi PlayReady'nin üzerinde çalışabileceği bir biçime yapmaktan sorumludur.

PlayReadyDRMPlugin arabirimi

PlayReadyCryptoPlugin , örneklerin şifresini çözmekle sorumlu olan Şifreleme eklentisi arabiriminin uygulamasını sağlar. OEM, şifresi çözülen örneklerin güvenilir yürütme ortamından (TEE) asla ayrılmadığından emin olmalıdır.

PlayReadyCryptoPlugin arabirimi

3. İşlem

Aşağıdaki adımlarda basit bir oynatma senaryosu açıklanmaktadır.

  1. Uygulama MediaDrm nesnesini oluşturur ve bu da PlayReadyDrmPlugin örneğini oluşturur.

  2. Ardından openSession'ı çağırın; bu da DRM Yöneticisi'nin başlatılmasına neden olur.

  3. Uygulama daha sonra getKeyRequest'i çağırır ve içerikten ayıklanan içerik üst bilgisini initData parametresi olarak geçirir. Buna ek olarak, uygulama isteğe bağlı parametreler anahtar-değer vektöründeki lisans alma sınaması özelleşmiş verilerini de geçirebilir. Lisans alma sınaması özel verileri daha sonra Drm_Content_SetProperty çağrısı olarak DRM Yöneticisi'ne yayılmalıdır.

  4. Bu noktada uygulama, DRM Yöneticisi'nde eşdeğer (Drm_LicenseAcq_GenerateChallenge) / Drm_LicenseAcq_ProcessResponse) çağrıları üretecek olan (getKeyRequest / provideKeyResponse) işlemini yürütebilecektir.

  5. Uygulama daha sonra, Drm_Reader_Bind çağrısının geri dönmesiyle PlayReadyCryptoPlugin arabirimini (DRM_DECRYPT_CONTEXT saran) oluşturacak bir MediaCrypto nesnesi örneği oluşturabilir.

  6. Daha sonra tüm şifre çözme çağrıları PlayReadyCryptoPlugin::d ecrypt yöntemini kullanır ve bu yöntem şifresi çözülen örneklere bir tanıtıcı döndürür.

Basit Oynatma Akış Şeması

Basit Oynatma Katmanları

4. PlayReadyDRMPlugin

setPropertyString

Uygulamalar, eklenti API'lerinin geçerli tasarımı nedeniyle başka türlü mümkün olmayan parametreleri PlayReady'ye geçirmek için bu yöntemi kullanır.

  • DeviceStoreName — Uygulamanın bir oturumu açmadan önce cihaz deposunun konumunu özellik olarak ayarlaması gerekir. Ardından PlayReady, openSession çağrıları sırasında DRM Yöneticisi'ni başlatırken DeviceStoreName özelliğini arar. Cihaz deposunun yolu, uygulamanın özel veri dizini gibi uygulama tarafından erişilebilir olmalıdır. Bu özellik, HDS'yi barındırması gereken bir <yol veya dosya adına> işaret etmelidir.

  • LicenseChallengeCustomData — Uygulama isteğe bağlı olarak bu özelliği getKeyRequest çağrısından önce ayarlayabilir. Burada PlayReady özelliği arar ve bir lisans alma sınaması oluşturur ve özel verileri isteğe ekler.

  • SecureStopCustomData — Uygulama, Güvenli Durdurma sınamasını oluşturmak için bir çağrıdan önce isteğe bağlı olarak bu özelliği ayarlayabilir. PlayReady özelliği arar, Güvenli Durdurma sınamasını oluşturur ve özel verileri isteğe ekler.

  • SelectKID — Uygulamanın tek bir toplu işlemde birden çok bellek içi lisans aldığı durumlarda bağlama için base64 kodlu bir KID belirtebilir.

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

Uygulamalar, Eklenti API'lerinin geçerli tasarımı nedeniyle başka türlü mümkün olmayan parametreleri PlayReady'ye geçirmek için bu yöntemi kullanır.

  • ContentHeader— Uygulama, Bir Crypto nesnesi oluşturmaya çalışmadan önce eklentideki içerik üst bilgisini ayarlamalıdır. İçerik üst bilgisi aşağıdaki biçimlerden birinde olabilir:

    • PlayReady Nesnesinin içeriğiyle bayt dizisi.

    • V2, V2.4, V4, V4.1, V4.2 üst bilgilerinin (unicode XML) içeriğine sahip bayt dizisi.

    • 24 karakterlik KeyID değerini belirten geniş karakter dizisi.

  • SecureStopPublisherCert— Uygulama, tüm güvenli durdurma ile ilgili çağrılardan önce Yayımcı Sertifikası'nı bir kez ayarlamalıdır.

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);
}

oturumAç

Bu çağrı, DRM Yöneticisi'nin Drm_Initialize işlevini çağırarak AppContext'in başlatılmasına neden olmalıdır.

Bu çağrıdan önce uygulamanın, HDS'yi depolamak için kullanılacak cihaz deposunun yolunu sağlamak için eklentide PropertyString'i ayarlaması gerekir.

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);
}

oturumu kapat

Bir DRM Oturumunu kapatmak, AppContext'i serbest bırakmak için Drm_Uninitialize'ı çağırmalıdır.

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;
}

anahtar isteği al

Bu yöntem bir lisans isteği sınaması oluşturur.

ContentHeader— Uygulama, initData parametresindeki içerik üst bilgisini geçirebilir veya bu işlevi çağırmadan önce uygulamanın ContentHeader dize özelliğini ayarlaması gerekir.

LicenseChallengeCustomData—Uygulama optionalParameters parametresinde izin sınama özel verilerini "LicenseChallengeCustomData" dize anahtarıyla geçirebilir. Yöntem, alternatif olarak, uygulama zaten onu bir özellik olarak ayarlamışsa, LicenseChallengeCustomData'yı alabilir.

yöntemi, hem içerik üst bilgisinde hem de Lisans Sunucusu'na gönderilme isteğinde varsa defaultUrl dosyasını doldurur.

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);
}

anahtarYanıtVer

Uygulama tarafından Lisans Sunucusu'na bir KeyRequest (LicenseChallenge) gönderildikten sonra, KeyResponse (LicenseResponse) eklentiye iletilmelidir.

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)

Uygulamalar, belirtilen güvenli durdurma kimliği için güvenli bir durdurma sınaması oluşturmak üzere getSecureStop yöntemini çağırabilir. Alternatif olarak, uygulama mevcut tüm güvenli durdurma oturumları için bir sınama oluşturmak üzere getSecureStops çağrısı yapabilir.

Güvenli durdurma sınamasını oluşturmadan önce uygulamanın setPropertyByteArray çağrısı yaparak ve SecureStopPublisherCert geçirerek yayımcı sertifikası sağlaması gerekir.

İsteğe bağlı olarak uygulama, güvenli durdurma sınamasının bir parçası olarak eklenmesi için SecureStopCustomData da sağlayabilir.

Güvenli durdurma sınaması oluşturulduktan sonra uygulama bunu Sunucu'ya göndermeli ve releaseSecureStops yöntemine güvenli durdurma yanıtını geri sağlamalıdır.

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

Uygulama Sunucudan güvenli durdurma yanıtını aldıktan sonra, güvenli durdurma bilgilerini depolama alanından kaldırmak için ileti eklentiye geçirilmelidir.

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);
}

Desteklenmeyen API'ler (İşlem Yok)

Aşağıdaki API'lerin listesinde PlayReady'de karşılık gelen bir işlem yoktur ve desteklenen senaryoların başarıyla yürütülmesi için gerekli değildir.

queryKeyStatus

status_t PlayReadyDrmPlugin::queryKeyStatus(
        Vector<uint8_t>                  const &sessionId,
        KeyedVector<String8, String8="">       &infoMap) const
{ return _DR_TO_STATUS(DRM_E_NOTIMPL); }

getProvisionRequest

PlayReady yalnızca Android cihazlarda yerel sağlamayı destekler.

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 yalnızca Android cihazlarda yerel sağlamayı destekler.

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 yalnızca Android cihazlarda yerel sağlamayı destekler.

status_t PlayReadyDrmPlugin::unprovisionDevice() { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

şifreleme algoritması ayarla

PlayReady rastgele verileri şifrelemez/şifresini çözmez.

status_t PlayReadyDrmPlugin::setCipherAlgorithm(
        Vector<uint8_t>       const &sessionId,
        String8               const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

MacAlgoritmasiniAyarla

PlayReady rastgele verileri imzalamaz/doğrulamaz.

status_t PlayReadyDrmPlugin::setMacAlgorithm(
        Vector<uint8_t>       const &sessionId,
        String8               const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

şifrelemek

PlayReady rastgele verileri şifrelemez/şifresini çözmez.

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); }

Şifresini çöz

PlayReady rastgele verileri şifrelemez/şifresini çözmez.

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); }

işaret

PlayReady rastgele verileri imzalamaz/doğrulamaz.

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); }

doğrulamak

PlayReady, rasgele verileri imzalama veya doğrulama işlevini yerine getirmez.

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 rastgele verileri imzalamaz/doğrulamaz.

status_t PlayReadyDrmPlugin::signRSA(
        Vector<uint8_t> const &sessionId,
        String8         const &algorithm,
        Vector<uint8_t> const &message,
        Vector<uint8_t> const &wrappedKey,
        Vector<uint8_t>       &signature) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

5. PlayReadyCryptoPlugin

PlayReadyCryptoPlugin

PlayReadyCryptoPlugin nesnesi oluşturmak için oluşturucu.

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;
}

güvenli kod çözücü bileşeni gerektirir

PlayReady şifreli içeriği işlemek için güvenli bir kod çözücü bileşeni gereklidir.

bool PlayReadyCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
{
  // Must always return true for PlayReady Content.
  return true;
}

Şifresini çöz

MediaCodec tarafından çağrılan şifre çözme.

Şifre çözme yöntemi net örnekler vermeyeceğinden, OEM'e özgü bir tanıtıcı sağlayacağından, OEM MediaCodec'un bu tanıtıcı üzerinde doğru şekilde çalışabildiğinden emin olmalıdır.

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

Şifreleme eklentisi yıkıcısının şifre çözücü bağlamını kapatması gerekir.

~PlayReadyCryptoPlugin::PlayReadyCryptoPlugin()
{
  Mutex::Autolock lock(mLock);
  Drm_Reader_Close(&moDecryptContext);
}

6. PlayReadyDrmFactory ve PlayReadyCryptoFactory

Sırasıyla PlayReadyDrmPlugin ve PlayReadyCryptoPlugin örneklerini oluşturmak için PlayReadyDrmFactory ve PlayReadyCryptoFactory arabirimlerinin uygulanması gerekir.

Her iki fabrika sınıfı da çağıranlara isCryptoSchemeSupported API'sini düzgün bir şekilde uygulayarak PlayReady korumalı içerik kullanabilen nesneler oluşturmayı desteklediklerini belirtmelidir.


const uint8_t playready_uuid[16] =
    {0x79, 0xf0, 0x04, 0x9a, 0x40, 0x98, 0x86, 0x42, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95};

bool isCryptoSchemeSupported(const uint8_t uuid[16])
{
  return (!memcmp(uuid, playready_uuid, sizeof(playready_uuid)));
}

7. SessionManager

DRM_APP_CONTEXT tutmak ve PlayReadyDrmPlugin ile PlayReadyCryptoPlugin arasında paylaşılması için tek bir oturum yöneticisinin uygulanması gerekir.

Aynı amaca ulaşan diğer tasarımlar da kabul edilebilir.

SessionManager
static DRM_APP_CONTEXT soAppContext
statik Mutex sLock