다음을 통해 공유


Android 사양용 PlayReady 플러그 인

1. 소개

이 사양은 OEM이 Android PlayReady 4.0 기반 DRM(디지털 권한 관리) 플러그 인을 구현하기 위한 지침을 설정합니다. 참조는 을 참조하세요 https://developer.android.com/reference/android/media/MediaDrm.html.

이 사양은 플러그 인 API가 PlayReady 호출에 매핑되는 방법에 대한 정보를 제공합니다.

1.1. 변경 내용

버전 변경
2016년 5월 초기 버전

2. 인터페이스

PlayReadyDrmPlugin 은 DRM 플러그 인 인터페이스에 대한 구현을 제공합니다. PlayReadyDrmPlugin 은 DRM 관리자 API를 래핑하고 인터페이스에서 지정한 매개 변수에 대해 PlayReady가 작동할 수 있는 형식으로 적절히 변환하는 작업을 담당합니다.

PlayReadyDRMPlugin interface

PlayReadyCryptoPlugin 은 샘플 암호 해독을 담당하는 Crypto 플러그 인 인터페이스에 대한 구현을 제공합니다. OEM은 암호 해독된 샘플이 신뢰할 수 있는 TEE(실행 환경)를 벗어나지 않도록 해야 합니다.

PlayReadyCryptoPlugin interface

3. 작업

다음 단계에서는 간단한 재생 시나리오를 설명합니다.

  1. 앱은 MediaDrm 개체를 만들어 PlayReadyDrmPlugin을 인스턴스화합니다.

  2. 그런 다음 openSession을 호출하면 DRM 관리자가 초기화됩니다.

  3. 그러면 앱은 getKeyRequest 를 호출하고 콘텐츠에서 추출된 콘텐츠 헤더를 initData 매개 변수로 전달합니다. 또한 앱은 선택적Parameters 키-값 벡터에서 라이선스 획득 챌린지 사용자 지정 데이터를 전달할 수도 있습니다. 라이선스 획득 챌린지 사용자 지정 데이터는 drM Manager에 Drm_Content_SetProperty 호출로 전파되어야 합니다.

  4. 이 시점에서 앱은 DRM 관리자에서 동등한(Drm_LicenseAcq_GenerateChallenge) /Drm_LicenseAcq_ProcessResponse) 호출을 생성하는 (getKeyRequest / provideKeyResponse)를 실행할 수 있습니다.

  5. 그런 다음 앱은 Drm_Reader_Bind 호출이 반환될 때 PlayReadyCryptoPlugin 인터페이스(DRM_DECRYPT_CONTEXT 래핑)의 인스턴스를 만드는 MediaCrypto 개체를 인스턴스화할 수 있습니다.

  6. 그 후 모든 암호 해독 호출은 PlayReadyCryptoPlugin::d ecrypt 메서드를 활용합니다. 그러면 암호 해독된 샘플에 대한 핸들이 반환됩니다.

Simple Playback Flowchart

Simple Playback Layers

4. PlayReadyDRMPlugin

setPropertyString

앱은 이 메서드를 사용하여 플러그 인 API의 현재 디자인으로 인해 가능하지 않은 매개 변수를 PlayReady에 전달합니다.

  • DeviceStoreName - 앱은 세션을 열기 전에 디바이스 저장소의 위치를 속성으로 설정해야 합니다. 그런 다음 PlayReady는 openSession 호출 중에 DRM 관리자를 초기화할 때 DeviceStoreName 속성을 조회합니다. 디바이스 저장소의 경로는 앱의 프라이빗 데이터 디렉터리처럼 앱에 액세스할 수 있어야 합니다. 이 속성은 HDS를 <보유해야 하는 경로/파일 이름을> 가리킵니다.

  • LicenseChallengeCustomData - 앱은 선택적으로 getKeyRequest 호출 전에 이 속성을 설정할 수 있습니다. 여기서 PlayReady는 속성을 조회하고 라이선스 취득 챌린지를 작성하고 요청에 사용자 지정 데이터를 포함합니다.

  • SecureStopCustomData - 앱은 필요에 따라 호출 전에 이 속성을 설정하여 보안 중지 챌린지를 생성할 수 있습니다. PlayReady는 속성을 조회하고 보안 중지 챌린지를 구성하고 요청에 사용자 지정 데이터를 포함합니다.

  • SelectKID - 앱이 단일 일괄 처리로 여러 메모리 내 라이선스를 획득한 경우 바인딩을 위해 base64로 인코딩된 KID를 지정할 수 있습니다.

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

앱은 이 메서드를 사용하여 플러그 인 API의 현재 디자인으로 인해 가능하지 않은 매개 변수를 PlayReady에 전달합니다.

  • ContentHeader - 앱은 Crypto 개체를 만들기 전에 플러그 인에서 콘텐츠 헤더를 설정해야 합니다. 콘텐츠 헤더는 다음 형식 중 하나일 수 있습니다.

    • PlayReady 개체의 내용이 포함된 바이트 배열입니다.

    • V2, V2.4, V4, V4.1, V4.2 헤더(유니코드 XML)의 내용이 포함된 바이트 배열입니다.

    • 24자 KeyID를 지정하는 와이드 문자 배열입니다.

  • SecureStopPublisherCert - 앱은 모든 보안 중지 관련 호출 전에 Publisher 인증서를 한 번 설정해야 합니다.

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

이 호출은 DRM Manager의 Drm_Initialize 함수를 호출하여 AppContext를 초기화해야 합니다.

이 호출 전에 앱은 플러그 인에서 PropertyString을 설정하여 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);
}

closeSession

DRM 세션을 닫으면 Drm_Uninitialize 호출하여 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

이 메서드는 라이선스 요청 챌린지를 생성합니다.

ContentHeader - 앱은 initData 매개 변수에서 콘텐츠 헤더를 전달하거나, 이 함수를 호출하기 전에 앱 에서 ContentHeader 문자열 속성을 설정해야 합니다.

LicenseChallengeCustomData - 앱은 문자열 키 "LicenseChallengeCustomData"를 사용하여 optionalParameters 매개 변수의 챌린지 사용자 지정 데이터를 전달할 수 있습니다. 또는 앱이 이미 속성으로 설정한 경우 메서드가 LicenseChallengeCustomData 를 선택할 수 있습니다.

이 메서드는 콘텐츠 헤더에서 사용할 수 있는 경우 defaultUrl 과 라이선스 서버로 전송할 요청을 채웁습니다.

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

앱에서 KeyRequest (LicenseChallenge) 라이선스 서버로 보낸 후에는 앱이 플러그 인에 KeyResponse (LicenseResponse) 전달해야 합니다.

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

앱은 getSecureStop 메서드를 호출하여 지정된 보안 중지 ID에 대한 보안 중지 챌린지를 생성할 수 있습니다. 또는 앱에서 getSecureStops 를 호출하여 기존의 모든 보안 중지 세션에 대한 챌린지를 생성할 수 있습니다.

보안 중지 챌린지를 생성하기 전에 앱은 setPropertyByteArray 를 호출하고 SecureStopPublisherCert를 전달하여 게시자 인증서를 제공해야 합니다.

필요에 따라 앱은 보안 중지 챌린지의 일부로 포함할 SecureStopCustomData 를 제공할 수도 있습니다.

보안 중지 챌린지를 만든 후 앱은 서버로 보내고 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

앱이 서버에서 보안 중지 응답을 받으면 메시지를 플러그 인으로 전달하여 스토리지에서 보안 중지 정보를 제거해야 합니다.

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

지원되지 않는 API(작업 없음)

다음 API 목록은 PlayReady에 해당 작업이 없으며 지원되는 시나리오를 성공적으로 실행하는 데 필요하지 않습니다.

queryKeyStatus

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

getProvisionRequest

PlayReady는 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

PlayReady는 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

PlayReady는 Android 디바이스에서만 로컬 프로비저닝을 지원합니다.

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

setCipherAlgorithm

PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.

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

setMacAlgorithm

PlayReady는 임의의 데이터에 서명/확인하지 않습니다.

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

encrypt

PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.

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

decrypt

PlayReady는 임의의 데이터를 암호화/암호 해독하지 않습니다.

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

sign

PlayReady는 임의의 데이터에 서명/확인하지 않습니다.

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

verify

PlayReady는 임의의 데이터에 서명/확인하지 않습니다.

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는 임의의 데이터에 서명/확인하지 않습니다.

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 개체를 만드는 생성자입니다.

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

requiresSecureDecoderComponent

PlayReady 암호화된 콘텐츠를 처리하려면 보안 디코더 구성 요소가 필요합니다.

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

decrypt

MediaCodec에서 호출한 암호 해독입니다.

암호 해독 메서드는 명확한 샘플을 제공하지 않고 OEM 관련 핸들을 제공하므로 OEM은 MediaCodec 가 해당 핸들에서 올바르게 작동할 수 있는지 확인해야 합니다.

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

암호화 플러그 인 소멸자는 암호 해독기 컨텍스트를 닫아야 합니다.

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

6. PlayReadyDrmFactory 및 PlayReadyCryptoFactory

PlayReadyDrmFactoryPlayReadyCryptoFactory 인터페이스의 구현은 각각 PlayReadyDrmPluginPlayReadyCryptoPlugin의 인스턴스를 만드는 데 필요합니다.

두 팩터리 클래스는 모두 호출자에게 isCryptoSchemeSupported API를 올바르게 구현하여 PlayReady 보호된 콘텐츠를 사용할 수 있는 개체 만들기를 지원함을 나타내야 합니다.


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 유지하고 PlayReadyDrmPlugin과 PlayReadyCryptoPlugin 간에 공유할 수 있도록 싱글톤 세션 관리자 구현해야 합니다.

동일한 목적을 달성하는 다른 디자인도 허용됩니다.

SessionManager
static DRM_APP_CONTEXT soAppContext
static Mutex sLock