Delen via


PlayReady-invoegtoepassing voor Android-specificatie

1. Inleiding

Deze specificatie stelt richtlijnen vast voor OEM's voor het implementeren van Op PlayReady 4.0 gebaseerde DRM-invoegtoepassingen (Digital Rights Management) op Android. Zie voor naslaginformatie https://developer.android.com/reference/android/media/MediaDrm.html.

Deze specificatie bevat informatie over hoe de plug-in API's worden toegewezen aan PlayReady-aanroepen.

1.1. Wijzigingsgeschiedenis

Versie Veranderen
Mei 2016 Eerste versie

2. Interfaces

PlayReadyDrmPlugin biedt de implementatie voor de DRM-invoegtoepassingsinterface. PlayReadyDrmPlugin is verantwoordelijk voor het verpakken van de DRM Manager-API's en het uitvoeren van de juiste vertaling voor de parameters zoals opgegeven door de interface in een indeling waarop PlayReady kan werken.

PlayReadyDRMPlugin-interface

PlayReadyCryptoPlugin biedt de implementatie voor de Crypto plug-in interface, die verantwoordelijk is voor het ontsleutelen van de voorbeelden. De OEM moet ervoor zorgen dat de ontsleutelde voorbeelden nooit de TEE (Trusted Execution Environment) verlaten.

PlayReadyCryptoPlugin-interface

3. Bewerking

In de volgende stappen wordt een eenvoudig afspeelscenario beschreven:

  1. De app maakt het MediaDrm-object , wat resulteert in de instantiëring van PlayReadyDrmPlugin.

  2. Roep vervolgens openSession aan, wat resulteert in de initialisatie van drm-beheer.

  3. De app zal vervolgens getKeyRequest aanroepen en de uit de inhoud geëxtraheerde contentheader als de initData-parameter doorgeven. Daarnaast kan de app ook de uitdaging van het verkrijgen van licenties doorgeven als aangepaste gegevens in de optionele parameters sleutel-waarde vector. Het proces voor het verkrijgen van licenties voor aangepaste gegevens moet vervolgens worden doorgegeven aan de DRM-manager als een Drm_Content_SetProperty-aanroep.

  4. Op dit moment kan de app de aanroepen (getKeyRequest / provideKeyResponse) uitvoeren, waarmee de equivalente aanroepen (Drm_LicenseAcq_GenerateChallenge) / Drm_LicenseAcq_ProcessResponse) in de DRM Manager worden geproduceerd.

  5. De app kan vervolgens een MediaCrypto-object instantiëren waarmee een exemplaar van een PlayReadyCryptoPlugin-interface (wrapping DRM_DECRYPT_CONTEXT) wordt gemaakt wanneer de Drm_Reader_Bind aanroep terugkeert.

  6. Daarna zullen alle ontsleutelingsaanroepen gebruikmaken van de PlayReadyCryptoPlugin::d ecrypt-methode , die een ingang retourneert naar de ontsleutelde voorbeelden.

Eenvoudig afspeelstroomdiagram

Eenvoudige afspeellagen

4. PlayReadyDRMPlugin

setPropertyString

Apps gebruiken deze methode om parameters door te geven aan PlayReady die anders niet mogelijk zijn vanwege het huidige ontwerp van de invoegtoepassing-API's.

  • DeviceStoreName — de app moet de locatie van de apparatenwinkel instellen als eigenschap voordat een sessie wordt geopend. Vervolgens zoekt PlayReady de eigenschap DeviceStoreName op bij het initialiseren van DRM Manager tijdens aanroepen omSession te openen. Het pad naar de apparaatopslag moet toegankelijk zijn voor de app, zoals de privégegevensmap van de app. De eigenschap moet verwijzen naar een <pad/bestandsnaam> die de HDS moet bevatten.

  • LicenseChallengeCustomData : de app kan deze eigenschap desgewenst instellen voordat aanroepen naar getKeyRequest worden aanroepen, waarbij PlayReady de eigenschap opzoekt en een vraag voor het verkrijgen van licenties opstelt en de aangepaste gegevens in de aanvraag opneemt.

  • SecureStopCustomData : de app kan deze eigenschap desgewenst instellen vóór een aanroep om de Secure Stop-uitdaging te genereren. PlayReady zoekt de eigenschap op en stelt de Secure Stop-uitdaging samen en neemt de aangepaste gegevens op in de aanvraag.

  • SelectKID : in situaties waarin de app meerdere in-memory licenties in één batch heeft verkregen, kan er een base64 gecodeerd KID voor binding worden opgegeven.

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 gebruiken deze methode om parameters door te geven aan PlayReady die anders niet mogelijk zijn vanwege het huidige ontwerp van de invoegtoepassings-API's.

  • ContentHeader: De app moet de header instellen op de plug-in voordat het probeert een Crypto-object te maken. De inhoudskoptekst kan een van de volgende indelingen hebben:

    • Bytematrix met de inhoud van het PlayReady-object.

    • Bytematrix met de inhoud van de headers V2, V2.4, V4.1, V4.2 (Unicode XML).

    • Brede tekenmatrix die de KeyID van 24 tekens aangeeft.

  • SecureStopPublisherCert: de app moet het uitgeverscertificaat eenmaal instellen voordat er enige beveiligde stop-gerelateerde oproepen worden gedaan.

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

sessie openen

Deze aanroep moet resulteren in het initialiseren van de AppContext door de Drm_Initialize-functie van DRM Manager aan te roepen.

Vóór deze aanroep moet de app PropertyString instellen op de invoegtoepassing om het pad naar de apparaatopslag op te geven dat wordt gebruikt om de HDS op te slaan.

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

sluitSessie

Als u een DRM-sessie sluit, moet Drm_Uninitialize worden aangeroepen om de AppContext vrij te geven.

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

Met deze methode wordt een licentieaanvraagvraag gegenereerd.

ContentHeader: de app kan de inhoudskop doorgeven in de parameter initData of, voordat deze functie wordt aangeroepen, moet de app de eigenschap ContentHeader-tekenreeks instellen.

LicenseChallengeCustomData: de app kan de aangepaste vraag doorgeven in de parameter optionalParameters met de tekenreekssleutel LicenseChallengeCustomData. De methode kan ook de LicenseChallengeCustomData ophalen als de app deze al heeft ingesteld als een eigenschap.

De methode vult de defaultUrl in, indien beschikbaar in de inhoudskoptekst en de aanvraag die naar de licentieserver moet worden verzonden.

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

geefBelangrijkeReactie

Zodra een KeyRequest (LicenseChallenge) door de app naar de licentieserver is verzonden, moet de app de KeyResponse (LicenseResponse) doorgeven aan de invoegtoepassing.

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 kunnen de getSecureStop-methode aanroepen om een secure stop-uitdaging te genereren voor de opgegeven secure stop-id. De app kan ook getSecureStops aanroepen om een uitdaging te genereren voor alle bestaande beveiligde stopsessies.

Voordat de secure stop-uitdaging wordt gegenereerd, moet de app het uitgeverscertificaat opgeven door setPropertyByteArray aan te roepen en SecureStopPublisherCert door te geven.

De app kan ook optioneel SecureStopCustomData leveren om als onderdeel van de secure stop-uitdaging te worden opgenomen.

Nadat de secure stop-uitdaging is gemaakt, moet de app deze naar de server verzenden en de reactie van de secure stop terugsturen naar de releaseSecureStops-methode .

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

Zodra de app het antwoord van de server over de beveiligde stop ontvangt, moet het bericht worden doorgegeven aan de plug-in om de beveiligde stopinformatie uit de opslag te verwijderen.

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

Niet-ondersteunde API's (geen bewerking)

De volgende lijst met API's heeft geen bijbehorende bewerking in PlayReady en is niet vereist voor een geslaagde uitvoering van de ondersteunde scenario's.

vraag sleutelstatus op

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

krijgVoorzieningsVerzoek

PlayReady biedt alleen ondersteuning voor lokale inrichting op Android-apparaten.

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 biedt alleen ondersteuning voor lokale inrichting op Android-apparaten.

status_t PlayReadyDrmPlugin::provideProvisionResponse(
        Vector<uint8_t>          const &response,
        Vector<uint8_t>                &certificate,
        Vector<uint8_t>                &wrappedKey) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

inrichting ongedaan makenDevice

PlayReady biedt alleen ondersteuning voor lokale inrichting op Android-apparaten.

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

setCipherAlgorithm

PlayReady versleutelt/ontsleutelt willekeurige gegevens niet.

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

setMacAlgorithm

PlayReady ondertekent/verifieert geen willekeurige gegevens.

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

versleutelen

PlayReady versleutelt/ontsleutelt willekeurige gegevens niet.

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

ontsleutelen

PlayReady versleutelt/ontsleutelt willekeurige gegevens niet.

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

teken

PlayReady ondertekent/verifieert geen willekeurige gegevens.

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

verifiëren

PlayReady ondertekent/verifieert geen willekeurige gegevens.

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

RSA-ondertekenen

PlayReady ondertekent/verifieert geen willekeurige gegevens.

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

Constructor voor het maken van een PlayReadyCryptoPlugin-object .

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

vereist beveiligde decodercomponent

Een beveiligd decoderonderdeel is vereist voor het verwerken van versleutelde PlayReady-inhoud.

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

ontsleutelen

De decryptie die door MediaCodec wordt aangeroepen.

Omdat de ontsleutelingsmethode geen duidelijke voorbeelden geeft, maar eerder een OEM-specifieke ingang, moet de OEM ervoor zorgen dat MediaCodec correct op die ingang kan werken.

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

De crypto-plug-in destructor moet de ontsleutelingscontext sluiten.

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

6. PlayReadyDrmFactory en PlayReadyCryptoFactory

Implementatie van de PlayReadyDrmFactory - en PlayReadyCryptoFactory-interfaces is vereist voor het maken van exemplaren van zowel PlayReadyDrmPlugin als PlayReadyCryptoPlugin .

Beide factoryklassen moeten tegen de aanroepen aangeven dat ze ondersteuning bieden voor het maken van objecten die PlayReady-beveiligde inhoud kunnen gebruiken door de isCryptoSchemeSupported-API correct te implementeren.


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

Er moet een singleton session manager worden geïmplementeerd om de DRM_APP_CONTEXT vast te houden en deze te kunnen delen tussen de PlayReadyDrmPlugin en PlayReadyCryptoPlugin.

Andere ontwerpen die hetzelfde doel bereiken, zijn ook acceptabel.

SessionManager
statische DRM_APP_CONTEXT soAppContext
statische Mutex sLock