diff --git a/CMakeLists.txt b/CMakeLists.txt index 2094644..0eaeffd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,54 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 2.8) project(DRMPlayready) -find_package(Thunder) +if (DEFINED MAKE_VERBOSE) + set(CMAKE_VERBOSE_MAKEFILE TRUE) +endif() + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +set(DRM_PLUGIN_NAME "Playready") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -w -Wno-psabi ") + +if(NOT CMAKE_CROSSCOMPILING) + set(STAGING_DIR "${CMAKE_INSTALL_PREFIX}") +else() + set(STAGING_DIR "${CMAKE_FIND_ROOT_PATH}") +endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +if(DEFINED USE_PLAYREADY_CMAKE) + find_package(PlayReady REQUIRED) +endif() -set(MODULE_NAME Playready) +find_package(OpenSSL REQUIRED) +file(GLOB DRM_PLUGIN_INCLUDES *.h) -find_package(${NAMESPACE}Core REQUIRED) -find_package(PlayReady REQUIRED) +if(DEFINED USE_PLAYREADY_CMAKE) +set(DRM_PLUGIN_LIBS + ${PLAYREADY_LIBRARIES}) +endif() -add_library(${MODULE_NAME} SHARED - MediaSession.cpp +set(DRM_PLUGIN_SOURCES + MediaSession.cpp MediaSystem.cpp MediaSessionExt.cpp) -set_target_properties(${MODULE_NAME} PROPERTIES - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED YES) -target_compile_definitions(${MODULE_NAME} - PRIVATE - BSTD_CPU_ENDIAN=BSTD_ENDIAN_LITTLE - DRM_BUILD_PROFILE=900 -) +# add the library +add_library(${DRM_PLUGIN_NAME} SHARED ${DRM_PLUGIN_SOURCES}) + +if(DEFINED USE_PLAYREADY_CMAKE) +target_compile_definitions(${DRM_PLUGIN_NAME} PRIVATE ${PLAYREADY_FLAGS}) +target_include_directories(${DRM_PLUGIN_NAME} PRIVATE ${PLAYREADY_INCLUDE_DIRS}) +target_link_libraries(${DRM_PLUGIN_NAME} ${DRM_PLUGIN_LIBS}) +endif() -target_include_directories(${MODULE_NAME} - PRIVATE - "${CMAKE_SYSROOT}/usr/include" - "${CMAKE_SYSROOT}/usr/include/${NAMESPACE}" - ${PLAYREADY_INCLUDE_DIRS}) +set_target_properties(${DRM_PLUGIN_NAME} PROPERTIES SUFFIX ".drm") +set_target_properties(${DRM_PLUGIN_NAME} PROPERTIES PREFIX "") -target_link_libraries(${MODULE_NAME} - ${NAMESPACE}Core::${NAMESPACE}Core - ${PLAYREADY_LIBRARIES}) +# openssl library added +target_link_libraries(${DRM_PLUGIN_NAME} OpenSSL::SSL) + +if(DEFINED PLAYREADY_REALTEK) + target_link_libraries(${DRM_PLUGIN_NAME} PRIVATE playreadypk) + message(STATUS "PLAYREADY_REALTEK is ON") +elseif(DEFINED PLAYREADY_BROADCOM) + #target_link_libraries(${DRM_PLUGIN_NAME} PRIVATE NEXUS::NEXUS NXCLIENT::NXCLIENT NexusWidevine::NexusWidevine) + #message(STATUS "PLAYREADY_BROADCOM is ON") +endif() + +# Enable SVP. +#if("${RDK_SVP}" STREQUAL "ENABLED") + message(STATUS "Using RDK_SVP") + add_definitions (-DUSE_SVP) + target_include_directories(${DRM_PLUGIN_NAME} PRIVATE ${CMAKE_SYSROOT}/usr/include/gstreamer-1.0) + target_include_directories(${DRM_PLUGIN_NAME} PRIVATE ${CMAKE_SYSROOT}/usr/include/glib-2.0) + target_include_directories(${DRM_PLUGIN_NAME} PRIVATE ${CMAKE_SYSROOT}/usr/lib/glib-2.0/include) + target_include_directories(${DRM_PLUGIN_NAME} PRIVATE ${CMAKE_SYSROOT}/usr/include/WPEFramework) + target_link_libraries(${DRM_PLUGIN_NAME} gstsvpext) +#endif() -# Library installation section -set_target_properties(${MODULE_NAME} PROPERTIES SUFFIX ".drm") -set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "") +if("${DRM_ERROR_NAME_SUPPORT}" STREQUAL "ON") + add_definitions( -DDRM_ERROR_NAME_SUPPORT ) + message(STATUS "DRM_ERROR_NAME_SUPPORT is ON") +else() + message(STATUS "DRM_ERROR_NAME_SUPPORT is OFF") +endif() -install(TARGETS ${MODULE_NAME} - PERMISSIONS OWNER_READ GROUP_READ - DESTINATION ${CMAKE_INSTALL_PREFIX}/share/${NAMESPACE}/OCDM) +if("${DRM_ANTI_ROLLBACK_CLOCK_SUPPORT}" STREQUAL "ON") + add_definitions( -DDRM_ANTI_ROLLBACK_CLOCK_SUPPORT ) + message(STATUS "DRM_ANTI_ROLLBACK_CLOCK_SUPPORT is ON") +else() + message(STATUS "DRM_ANTI_ROLLBACK_CLOCK_SUPPORT is OFF") +endif() -if(NOT "${CMAKE_FIND_ROOT_PATH}" STREQUAL "") - install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink /etc/playready ${CMAKE_CURRENT_BINARY_DIR}/playready)") - install(DIRECTORY DESTINATION ${CMAKE_INSTALL_PREFIX}/../${PERSISTENT_PATH}/OCDM) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/playready DESTINATION ${CMAKE_INSTALL_PREFIX}/../${PERSISTENT_PATH}/OCDM/) +if("${NO_PERSISTENT_LICENSE_CHECK}" STREQUAL "ON") + add_definitions( -DNO_PERSISTENT_LICENSE_CHECK=1 ) + message(STATUS "NO_PERSISTENT_LICENSE_CHECK is ON") +else() + message(STATUS "NO_PERSISTENT_LICENSE_CHECK is OFF") endif() + +if("${TEE_CONFIG_NEED}" STREQUAL "ON") + add_definitions( -DTEE_CONFIG_NEED=1 ) + message(STATUS "TEE_CONFIG_NEED is ON") +else() + message(STATUS "TEE_CONFIG_NEED is OFF") +endif() + +install(TARGETS ${DRM_PLUGIN_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/WPEFramework/OCDM) \ No newline at end of file diff --git a/MediaSession.cpp b/MediaSession.cpp index 7804783..6f88811 100644 --- a/MediaSession.cpp +++ b/MediaSession.cpp @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "MediaSession.h" #include #include @@ -23,11 +22,14 @@ #include #include #include -#include +#include + +#ifdef USE_SVP +#include "gst_svp_meta.h" +#endif -using namespace Thunder; -using SafeCriticalSection = Core::SafeSyncType; -extern Core::CriticalSection drmAppContextMutex_; +extern WPEFramework::Core::CriticalSection drmAppContextMutex_; +extern DRM_CONST_STRING g_dstrCDMDrmStoreName; #define NYI_KEYSYSTEM "keysystem-placeholder" @@ -47,28 +49,165 @@ extern Core::CriticalSection drmAppContextMutex_; #define NO_OF DRM_NO_OF #endif -MODULE_NAME_DECLARATION(BUILD_REFERENCE); +#define DEVCERT_WAIT_SECS 30 +#define DEVCERT_RETRY_MAX 4 +#define EXPECTED_AES_CTR_IVDATA_SIZE (8) +#define EXPECTED_AES_CBC_IVDATA_SIZE (16) using namespace std; +KeyId::KeyId( const DRM_BYTE *f_pBytes , KeyIdOrder keyOrder) +{ + m_hexStr.clear(); + m_base64Str.clear(); + ZEROMEM( m_bytes, DRM_ID_SIZE ); + keyIdOrder = KEYID_ORDER_UNKNOWN; + memcpy( m_bytes, f_pBytes, DRM_ID_SIZE ); + keyIdOrder = keyOrder; +} + +DRM_RESULT KeyId::keyDecode( const DRM_CONST_STRING &f_pdstrB64 ){ + + DRM_DWORD cBytes = DRM_ID_SIZE; + DRM_RESULT dr = DRM_B64_DecodeW( &f_pdstrB64, &cBytes, getmBytes(), 0 ); + if ( dr != DRM_SUCCESS ) + { + fprintf(stderr, "\n[keyDecode] DRM_B64_DecodeW Failed"); + } + return dr; +} + +void KeyId::setKeyIdOrder(KeyIdOrder keyOrder) +{ + keyIdOrder = keyOrder; +} + +KeyId::KeyIdOrder KeyId::getKeyIdOrder() +{ + return keyIdOrder; +} + +const DRM_BYTE* KeyId::getmBytes() +{ + return m_bytes; +} + +KeyId& KeyId::ToggleFormat() +{ + DRM_BYTE tmp; + + if ( keyIdOrder != KEYID_ORDER_UNKNOWN ) + { + tmp = m_bytes[3]; + m_bytes[3] = m_bytes[0]; + m_bytes[0] = tmp; + tmp = m_bytes[2]; + m_bytes[2] = m_bytes[1]; + m_bytes[1] = tmp; + tmp = m_bytes[5]; + m_bytes[5] = m_bytes[4]; + m_bytes[4] = tmp; + tmp = m_bytes[7]; + m_bytes[7] = m_bytes[6]; + m_bytes[6] = tmp; + + if ( keyIdOrder == KEYID_ORDER_GUID_LE ) + keyIdOrder = KEYID_ORDER_UUID_BE; + else + keyIdOrder = KEYID_ORDER_GUID_LE; + } + m_hexStr.clear(); + m_base64Str.clear(); + + return *this; +} + +bool KeyId::operator< ( const KeyId &keyId ) const +{ + if ( memcmp(keyId.m_bytes, m_bytes, DRM_ID_SIZE) < 0 ) + return true; + return false; +} + +bool KeyId::operator== ( const KeyId &keyId ) +{ + bool areEqual = false; + + if ( memcmp(&m_bytes[8], &(keyId.m_bytes[8]), 8) == 0 ) + { + if ( memcmp(keyId.m_bytes, m_bytes, 8) == 0 ) + { + areEqual = true; + } + else + { + ToggleFormat(); + areEqual = ( memcmp(keyId.m_bytes, m_bytes, DRM_ID_SIZE ) == 0 ); + ToggleFormat(); + } + } + + return areEqual; +} + +const char* KeyId::HexStr() +{ + if ( m_hexStr.empty() ) + { + char hex[64]; + ::memset(hex, 0, 64); + for (int i = 0; i < DRM_ID_SIZE; i++) + { + hex[i * 2] = "0123456789abcdef"[m_bytes[i] >> 4]; + hex[i * 2 + 1] = "0123456789abcdef"[m_bytes[i] & 0x0F]; + } + m_hexStr = hex; + } + + return m_hexStr.c_str(); +} + +const char* KeyId::B64Str() +{ + DRM_RESULT dr = DRM_SUCCESS; + if ( m_base64Str.empty() ) + { + char b64[64]; + DRM_DWORD cbB64 = 64; + ::memset( b64, 0, 64 ); + PR4ChkDR( DRM_B64_EncodeA( m_bytes, DRM_ID_SIZE, b64, &cbB64, 0 ) ); + + m_base64Str = b64; + } + + ErrorExit: + + return m_base64Str.c_str(); +} + namespace CDMi { -// The default location of CDM DRM store. -// /tmp/drmstore.dat +namespace { -const DRM_WCHAR g_rgwchCDMDrmStoreName[] = {WCHAR_CAST('/'), WCHAR_CAST('t'), WCHAR_CAST('m'), WCHAR_CAST('p'), WCHAR_CAST('/'), - WCHAR_CAST('d'), WCHAR_CAST('r'), WCHAR_CAST('m'), WCHAR_CAST('s'), WCHAR_CAST('t'), - WCHAR_CAST('o'), WCHAR_CAST('r'), WCHAR_CAST('e'), WCHAR_CAST('.'), WCHAR_CAST('d'), - WCHAR_CAST('a'), WCHAR_CAST('t'), WCHAR_CAST('\0')}; +void Swap(uint8_t& lhs, uint8_t& rhs) +{ + uint8_t tmp =lhs; + lhs = rhs; + rhs = tmp; +} -const DRM_CONST_STRING g_dstrCDMDrmStoreName = CREATE_DRM_STRING(g_rgwchCDMDrmStoreName); +} const DRM_CONST_STRING *g_rgpdstrRights[1] = {&g_dstrDRM_RIGHT_PLAYBACK}; -// Parse out the first PlayReady initialization header found in the concatenated -// block of headers in _initData_. -// If a PlayReady header is found, this function returns true and the header -// contents are stored in _output_. -// Otherwise, returns false and _output_ is not touched. + +uint64_t MediaKeySession::mMaxResDecodePixels = 0; +bool MediaKeySession::mMaxResDecodeSet = false; + +WPEFramework::Core::CriticalSection prPlatformMutex_; +WPEFramework::Core::CriticalSection prSessionMutex_; +DRM_DWORD CPRDrmPlatform::m_dwInitRefCount = 0; + +/*Parsing the first playready init header from _initData_. In success case the header will be stored in _output_*/ bool parsePlayreadyInitializationData(const std::string& initData, std::string* output) { BufferReader input(reinterpret_cast(initData.data()), initData.length()); @@ -76,23 +215,12 @@ bool parsePlayreadyInitializationData(const std::string& initData, std::string* static const uint8_t playreadySystemId[] = { 0x9A, 0x04, 0xF0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xAB, 0x92, 0xE6, 0x5B, 0xE0, 0x88, 0x5F, 0x95, + }; - // one PSSH box consists of: - // 4 byte size of the atom, inclusive. (0 means the rest of the buffer.) - // 4 byte atom type, "pssh". - // (optional, if size == 1) 8 byte size of the atom, inclusive. - // 1 byte version, value 0 or 1. (skip if larger.) - // 3 byte flags, value 0. (ignored.) - // 16 byte system id. - // (optional, if version == 1) 4 byte key ID count. (K) - // (optional, if version == 1) K * 16 byte key ID. - // 4 byte size of PSSH data, exclusive. (N) - // N byte PSSH data. while (!input.IsEOF()) { size_t startPosition = input.pos(); - // The atom size, used for skipping. uint64_t atomSize; if (!input.Read4Into8(&atomSize)) { @@ -126,26 +254,22 @@ bool parsePlayreadyInitializationData(const std::string& initData, std::string* if (version > 1) { - // unrecognized version - skip. if (!input.SkipBytes(atomSize - (input.pos() - startPosition))) { return false; } continue; } - // flags if (!input.SkipBytes(3)) { return false; } - // system id std::vector systemId; if (!input.ReadVec(&systemId, sizeof(playreadySystemId))) { return false; } if (memcmp(&systemId[0], playreadySystemId, sizeof(playreadySystemId))) { - // skip non-Playready PSSH boxes. if (!input.SkipBytes(atomSize - (input.pos() - startPosition))) { return false; } @@ -153,7 +277,6 @@ bool parsePlayreadyInitializationData(const std::string& initData, std::string* } if (version == 1) { - // v1 has additional fields for key IDs. We can skip them. uint32_t numKeyIds; if (!input.Read4(&numKeyIds)) { return false; @@ -164,7 +287,6 @@ bool parsePlayreadyInitializationData(const std::string& initData, std::string* } } - // size of PSSH data uint32_t dataLength; if (!input.Read4(&dataLength)) { return false; @@ -178,112 +300,342 @@ bool parsePlayreadyInitializationData(const std::string& initData, std::string* return true; } - // we did not find a matching record return false; } -MediaKeySession::MediaKeySession(const uint8_t *f_pbInitData, uint32_t f_cbInitData, const uint8_t *f_pbCDMData, uint32_t f_cbCDMData, DRM_APP_CONTEXT * poAppContext, bool initWithLast15, bool initiateChallengeGeneration /* = false */) - : m_pbOpaqueBuffer(nullptr) - , m_cbOpaqueBuffer(0) - , m_pbRevocationBuffer(nullptr) - , m_eKeyState(KEY_INIT) +/* + * f_pContext(input) : It could be NULL or Valid pointer + */ +DRM_RESULT CPRDrmPlatform::DrmPlatformInitialize( void *f_pContext ) +{ + DRM_RESULT dr = DRM_SUCCESS; + SafeCriticalSection systemLock(prPlatformMutex_); + + if ( ++m_dwInitRefCount == 1 ) + { + DRM_RESULT dr = DRM_SUCCESS; + DRM_DWORD cAttempts = 0; + + while( ( dr=Drm_Platform_Initialize( f_pContext ) ) == DRM_E_DEPRECATED_DEVCERT_READ_ERROR) { + + Drm_Platform_Uninitialize( (void *)nullptr ); + + if ( cAttempts >= DEVCERT_RETRY_MAX ){ + ChkDR( DRM_E_DEPRECATED_DEVCERT_READ_ERROR); + } + sleep( DEVCERT_WAIT_SECS ); + ++cAttempts; + } + } + + ErrorExit: + + if ( DRM_FAILED( dr ) ) + { + --m_dwInitRefCount; + fprintf(stderr, "[%s:%d] failed. 0x%X",__FUNCTION__,__LINE__,dr); + } + + return dr; +} + +DRM_RESULT CPRDrmPlatform::DrmPlatformInitialize() +{ + void *pPlatformInitData = NULL; + svpGetDrmPlatformInitData( &pPlatformInitData); + return DrmPlatformInitialize( (void *)pPlatformInitData ); +} + +DRM_RESULT CPRDrmPlatform::DrmPlatformUninitialize() +{ + DRM_RESULT dr = DRM_SUCCESS; + DRM_VOID *pDrmOemContext = NULL; + + SafeCriticalSection systemLock(prPlatformMutex_); + + if ( m_dwInitRefCount == 0 ) + { + fprintf(stderr, "[%s:%d] ref count is already 0",__FUNCTION__,__LINE__); + ChkDR( DRM_E_FAIL ); + } + else if ( --m_dwInitRefCount == 0 ) + { + svpGetDrmOEMContext(&pDrmOemContext); + + if ( DRM_FAILED( (dr=Drm_Platform_Uninitialize( (void *)pDrmOemContext ) ) ) ) + { + fprintf(stderr, "[%s:%d] Drm_Platform_Uninitialize failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + goto ErrorExit; + } + } + + ErrorExit: + + if ( DRM_FAILED( dr ) ) + { + fprintf(stderr, "[%s:%d] failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + } + + return dr; + +} + +/*Get the version and list of keyids from the header*/ +DRM_RESULT Header_GetInfo( + const DRM_CONST_STRING *f_pdstrWRMHEADER, + eDRM_HEADER_VERSION *f_pHeaderVersion, + DRM_CONST_STRING **f_ppdstrKIDs, + DRM_DWORD *f_pcbKIDs) +{ + DRM_RESULT dr = DRM_SUCCESS; + DRM_DWORD cKIDs = 0; + DRM_CONST_STRING *pdstrKIDs = NULL; + + PR4ChkDR( DRM_HDR_GetHeaderVersion( f_pdstrWRMHEADER, f_pHeaderVersion ) ); + + PR4ChkDR( DRM_HDR_GetAttribute( + f_pdstrWRMHEADER, + NULL, + DRM_HEADER_ATTRIB_KIDS, + NULL, + &cKIDs, + &pdstrKIDs, + 0 ) ); + + *f_ppdstrKIDs = pdstrKIDs; + *f_pcbKIDs = cKIDs; + +ErrorExit: + return dr; +} + +PlayreadySession::PlayreadySession() + : m_poAppContext(nullptr) + , m_pbPROpaqueBuf(nullptr) + , m_cbPROpaqueBuf(0) + , m_bInitCalled(false) +{ + void *pPlatformInitData = NULL; + svpGetDrmPlatformInitData( &pPlatformInitData); + + if ( DRM_FAILED( CPRDrmPlatform::DrmPlatformInitialize( pPlatformInitData ) ) ) + { + fprintf(stderr, "[%s:%d] DrmPlatformInitialize failed.",__FUNCTION__,__LINE__); + } +} + +PlayreadySession::~PlayreadySession() +{ + SafeCriticalSection systemLock(prSessionMutex_); + + if ( IsPlayreadySessionInit() ) + { + SAFE_OEM_FREE(m_pbPROpaqueBuf); + m_cbPROpaqueBuf = 0; + + if (m_poAppContext != nullptr) + { + Drm_Uninitialize(m_poAppContext); + SAFE_OEM_FREE(m_poAppContext); + m_poAppContext = nullptr; + } + } + + if (DRM_FAILED(CPRDrmPlatform::DrmPlatformUninitialize())) + { + fprintf(stderr, "[%s:%d] DrmPlatformUninitialize failed.",__FUNCTION__,__LINE__); + } + +} + +DRM_APP_CONTEXT * PlayreadySession::InitializeDRM(const DRM_CONST_STRING * pDRMStoreName) +{ + DRM_RESULT dr = DRM_SUCCESS; + DRM_VOID *pDrmOemContext = nullptr; + + SafeCriticalSection systemLock(prSessionMutex_); + + m_bInitCalled = true; + + if (m_poAppContext == nullptr) + { + ChkMem( m_pbPROpaqueBuf = (DRM_BYTE *)Oem_MemAlloc(MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE) ); + ZEROMEM(m_pbPROpaqueBuf, MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE); + m_cbPROpaqueBuf = MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE; + + ChkMem( m_poAppContext = (DRM_APP_CONTEXT * )Oem_MemAlloc( sizeof(DRM_APP_CONTEXT) ) ); + ZEROMEM( m_poAppContext, sizeof(DRM_APP_CONTEXT) ); + + svpGetDrmOEMContext(&pDrmOemContext); + dr = Drm_Initialize(m_poAppContext, pDrmOemContext, m_pbPROpaqueBuf, m_cbPROpaqueBuf, pDRMStoreName); + if (dr != DRM_SUCCESS) + { + ChkDR(Drm_Initialize(m_poAppContext, pDrmOemContext, m_pbPROpaqueBuf, m_cbPROpaqueBuf, pDRMStoreName)); + } + } + else + { + DRM_RESULT err = Drm_Reinitialize(m_poAppContext); + if (DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] Drm_Reinitialize failed. 0x%lX - %s",__FUNCTION__,__LINE__,(long )err,DRM_ERR_NAME(err)); + } + } + + return m_poAppContext; + +ErrorExit: + if (DRM_FAILED(dr)) { + fprintf(stderr, "[%s:%d] failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + } + return nullptr; +} + +MediaKeySession::MediaKeySession(const uint8_t *f_pbInitData, uint32_t f_cbInitData, const uint8_t *f_pbCDMData, uint32_t f_cbCDMData, DRM_APP_CONTEXT * poAppContext, bool initiateChallengeGeneration /* = false */) + : m_pbRevocationBuffer(nullptr) + , m_eKeyState(KEY_CLOSED) , m_pbChallenge(nullptr) , m_cbChallenge(0) - , m_pchSilentURL(nullptr) + , m_pchSilentURL(nullptr) , m_customData(reinterpret_cast(f_pbCDMData), f_cbCDMData) , m_piCallback(nullptr) , mSessionId(0) - , mInitWithLast15(initWithLast15) - , mInitiateChallengeGeneration(initiateChallengeGeneration) - , m_fCommit(false) + , mInitiateChallengeGeneration(initiateChallengeGeneration) + , m_cHeaderKIDs(0) + , m_pdstrHeaderKIDs( nullptr ) + , m_eHeaderVersion( DRM_HEADER_VERSION_UNKNOWN ) + , m_oBatchID( DRM_ID_EMPTY ) + , m_currentDecryptContext( nullptr ) +#ifdef USE_SVP + , m_pSVPContext(nullptr) + , m_rpcID(0) +#endif + , m_fCommit(FALSE) , m_poAppContext(poAppContext) - , m_oDecryptContext(nullptr) , m_decryptInited(false) + , m_bDRMInitializedLocally(false) { - memset(&levels_, 0, sizeof(levels_)); - DRM_RESULT dr = DRM_SUCCESS; + DRM_RESULT dr = DRM_SUCCESS; + DRM_ID oSessionID = DRM_ID_EMPTY; + DRM_CONST_STRING dstrWRMHEADER = DRM_EMPTY_DRM_STRING; - if (!initiateChallengeGeneration) { - mLicenseResponse = std::unique_ptr(new LicenseResponse()); - mSecureStopId.clear(); + DRM_DWORD cchEncodedSessionID = SIZEOF(m_rgchSessionID); - // TODO: can we do this nicer? - mDrmHeader.resize(f_cbCDMData); - memcpy(&mDrmHeader[0], f_pbCDMData, f_cbCDMData); - } else { - m_oDecryptContext = new DRM_DECRYPT_CONTEXT; - - DRM_ID oSessionID; +#ifdef USE_SVP + gst_svp_ext_get_context(&m_pSVPContext, Client, 0); - DRM_DWORD cchEncodedSessionID = SIZEOF(m_rgchSessionID); + m_stSecureBuffInfo.bCreateSecureMemRegion = true; + m_stSecureBuffInfo.SecureMemRegionSize = 512 * 1024; - // FIXME: Change the interface of this method? Not sure why the win32 bondage is still so popular. - std::string initData(reinterpret_cast(f_pbInitData), f_cbInitData); - std::string playreadyInitData; + if( 0 != svp_allocate_secure_buffers(m_pSVPContext, (void**)&m_stSecureBuffInfo, nullptr, nullptr, m_stSecureBuffInfo.SecureMemRegionSize)) + { + /* No need to break here */ + m_stSecureBuffInfo.SecureMemRegionSize = 0; + } +#endif - printf("Constructing PlayReady Session [%p]\n", this); + std::string initData(reinterpret_cast(f_pbInitData), f_cbInitData); + std::string playreadyInitData; - ChkMem(m_pbOpaqueBuffer = (DRM_BYTE *)Oem_MemAlloc(MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE)); - m_cbOpaqueBuffer = MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE; + ChkBOOL(m_eKeyState == KEY_CLOSED, DRM_E_INVALIDARG); - ChkMem(m_poAppContext = (DRM_APP_CONTEXT *)Oem_MemAlloc(SIZEOF(DRM_APP_CONTEXT))); + mMaxResDecodePixels = 0; + mMaxResDecodeSet = false; - // Initialize DRM app context. - ChkDR(Drm_Initialize(m_poAppContext, - nullptr, - m_pbOpaqueBuffer, - m_cbOpaqueBuffer, - &g_dstrCDMDrmStoreName)); + if (m_poAppContext == nullptr) { + m_poAppContext = InitializeDRM(&g_dstrCDMDrmStoreName); + } - if (DRM_REVOCATION_IsRevocationSupported()) { - ChkMem(m_pbRevocationBuffer = (DRM_BYTE *)Oem_MemAlloc(REVOCATION_BUFFER_SIZE)); + if (DRM_REVOCATION_IsRevocationSupported()) { + ChkMem(m_pbRevocationBuffer = (DRM_BYTE *)Oem_MemAlloc(REVOCATION_BUFFER_SIZE)); - ChkDR(Drm_Revocation_SetBuffer(m_poAppContext, - m_pbRevocationBuffer, - REVOCATION_BUFFER_SIZE)); - } + ChkDR(Drm_Revocation_SetBuffer(m_poAppContext, + m_pbRevocationBuffer, + REVOCATION_BUFFER_SIZE)); + } + + ChkDR(Oem_Random_GetBytes(nullptr, (DRM_BYTE *)&oSessionID, SIZEOF(oSessionID))); + ZEROMEM(m_rgchSessionID, SIZEOF(m_rgchSessionID)); + + ChkDR(DRM_B64_EncodeA((DRM_BYTE *)&oSessionID, + SIZEOF(oSessionID), + m_rgchSessionID, + &cchEncodedSessionID, + 0)); + + if (!parsePlayreadyInitializationData(initData, &playreadyInitData)) { + playreadyInitData = initData; + } - //temporary hack to allow time based licenses - ( DRM_REINTERPRET_CAST( DRM_APP_CONTEXT_INTERNAL, m_poAppContext ) )->fClockSet = TRUE; - - // Generate a random media session ID. - ChkDR(Oem_Random_GetBytes(nullptr, (DRM_BYTE *)&oSessionID, SIZEOF(oSessionID))); - ZEROMEM(m_rgchSessionID, SIZEOF(m_rgchSessionID)); - - // Store the generated media session ID in base64 encoded form. - ChkDR(DRM_B64_EncodeA((DRM_BYTE *)&oSessionID, - SIZEOF(oSessionID), - m_rgchSessionID, - &cchEncodedSessionID, - 0)); - - // The current state MUST be KEY_INIT otherwise error out. - ChkBOOL(m_eKeyState == KEY_INIT, DRM_E_INVALIDARG); - - if (!parsePlayreadyInitializationData(initData, &playreadyInitData)) { - playreadyInitData = initData; + mDrmHeader.resize( playreadyInitData.size() ); + ::memcpy( &mDrmHeader[ 0 ], + reinterpret_cast(playreadyInitData.data()), + playreadyInitData.size() ); + + ChkDR(Drm_Content_SetProperty(m_poAppContext, + DRM_CSP_AUTODETECT_HEADER, + &mDrmHeader[ 0 ], + mDrmHeader.size()) ); + + DRM_CONST_DSTR_FROM_PB( &dstrWRMHEADER, &mDrmHeader[ 0 ], mDrmHeader.size() ); + + ChkDR( Header_GetInfo( &dstrWRMHEADER, + &m_eHeaderVersion, + &m_pdstrHeaderKIDs, + &m_cHeaderKIDs ) ); + + for( DRM_DWORD idx = 0; idx < m_cHeaderKIDs; idx++ ) + { + KeyId kid , kid2; + DRM_DWORD cBytes = DRM_ID_SIZE; + DRM_DWORD cBytes2 = DRM_ID_SIZE; + + DRM_RESULT dr = DRM_B64_DecodeW( &m_pdstrHeaderKIDs[ idx ], &cBytes, kid.getmBytes(), 0 ); + if ( dr == DRM_SUCCESS ) + { + kid.setKeyIdOrder(KeyId::KEYID_ORDER_GUID_LE); + } + + DRM_RESULT dr2 = DRM_B64_DecodeW( &m_pdstrHeaderKIDs[ idx ], &cBytes2, kid2.getmBytes(), 0 ); + if ( dr2 == DRM_SUCCESS ) + { + kid2.setKeyIdOrder(KeyId::KEYID_ORDER_GUID_LE); + } } - ChkDR(Drm_Content_SetProperty(m_poAppContext, - DRM_CSP_AUTODETECT_HEADER, - reinterpret_cast(playreadyInitData.data()), - playreadyInitData.size())); - // The current state MUST be KEY_INIT otherwise error out. - ChkBOOL(m_eKeyState == KEY_INIT, DRM_E_INVALIDARG); - return; - } + m_eKeyState = KEY_INIT; ErrorExit: - if (DRM_FAILED(dr)) { - const DRM_CHAR* description; - DRM_ERR_GetErrorNameFromCode(dr, &description); - printf("playready error: %s\n", description); - } + + if (DRM_FAILED(dr)) + { + m_eKeyState = KEY_ERROR; + fprintf(stderr, "[%s:%d] Playready initialization error: 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + } + + return; +} + +MediaKeySession::~MediaKeySession(void) +{ + mMaxResDecodePixels = 0; + mMaxResDecodeSet = false; + Close(); + } -MediaKeySession::~MediaKeySession(void) { - Close(); - printf("Destructing PlayReady Session [%p]\n", this); +const char* MediaKeySession::printGuid(KeyId &keyId) +{ + if (keyId.getKeyIdOrder() == KeyId::KEYID_ORDER_UUID_BE) + keyId.ToggleFormat(); + return keyId.B64Str(); +} + +const char* MediaKeySession::printUuid(KeyId &keyId) +{ + if (keyId.getKeyIdOrder() == KeyId::KEYID_ORDER_GUID_LE) + keyId.ToggleFormat(); + return keyId.B64Str(); } const char *MediaKeySession::GetSessionId(void) const { @@ -291,25 +643,63 @@ const char *MediaKeySession::GetSessionId(void) const { } const char *MediaKeySession::GetKeySystem(void) const { - return NYI_KEYSYSTEM; // FIXME : replace with keysystem and test. + return NYI_KEYSYSTEM; } DRM_RESULT DRM_CALL MediaKeySession::_PolicyCallback( - VARIABLE_IS_NOT_USED const DRM_VOID *f_pvOutputLevelsData, - VARIABLE_IS_NOT_USED DRM_POLICY_CALLBACK_TYPE f_dwCallbackType, - VARIABLE_IS_NOT_USED const DRM_KID *f_pKID, - VARIABLE_IS_NOT_USED const DRM_LID *f_pLID, - VARIABLE_IS_NOT_USED const DRM_VOID *f_pv) { - return DRM_SUCCESS; + const DRM_VOID *f_pvOutputLevelsData, + DRM_POLICY_CALLBACK_TYPE f_dwCallbackType, + const DRM_KID *f_pKID, + const DRM_LID *f_pLID, + const DRM_VOID *f_pv) { + DRM_RESULT res = DRM_SUCCESS; + + switch (f_dwCallbackType) + { + case DRM_PLAY_OPL_CALLBACK: + { + const DRM_PLAY_OPL_LATEST * const opl = static_cast(f_pvOutputLevelsData); + assert(opl->dwVersion == VER_DRM_PLAY_OPL_LATEST); + + /* MaxResDecode */ + const DRM_DIGITAL_VIDEO_OUTPUT_PROTECTION_IDS_LATEST &dvopi = opl->dvopi; + assert(dvopi.dwVersion == VER_DRM_DIGITAL_VIDEO_OUTPUT_PROTECTION_IDS_LATEST); + for (size_t i = 0; i < dvopi.cEntries; ++i) + { + const DRM_OUTPUT_PROTECTION_LATEST &entry = dvopi.rgVop[i]; + if (DRM_IDENTICAL_GUIDS(&entry.guidId, &g_guidMaxResDecode)) + { + assert(entry.dwVersion == VER_DRM_DIGITAL_VIDEO_OUTPUT_PROTECTION_LATEST); + + uint32_t mrdWidth = (uint32_t)(entry.rgbConfigData[0] << 24 | entry.rgbConfigData[1] << 16 | entry.rgbConfigData[2] << 8 | entry.rgbConfigData[3]); + uint32_t mrdHeight = (uint32_t)(entry.rgbConfigData[4] << 24 | entry.rgbConfigData[5] << 16 | entry.rgbConfigData[6] << 8 | entry.rgbConfigData[7]); + + + mMaxResDecodePixels = mrdWidth*mrdHeight; + mMaxResDecodeSet = true; + res = DRM_SUCCESS; + break; + } + } + break; + } + default: + // ignored + res = DRM_SUCCESS; + break; + } + + return res; } void MediaKeySession::Run(const IMediaKeySessionCallback *f_piMediaKeySessionCallback) { - if (f_piMediaKeySessionCallback) { m_piCallback = const_cast(f_piMediaKeySessionCallback); if (mInitiateChallengeGeneration) { - playreadyGenerateKeyRequest(); + if ( CDMi_SUCCESS != PersistentLicenseCheck() ) { + playreadyGenerateKeyRequest(); + } } } else { m_piCallback = nullptr; @@ -317,51 +707,71 @@ void MediaKeySession::Run(const IMediaKeySessionCallback *f_piMediaKeySessionCal } bool MediaKeySession::playreadyGenerateKeyRequest() { - - DRM_RESULT dr = DRM_SUCCESS; + + DRM_RESULT dr = DRM_SUCCESS; DRM_DWORD cchSilentURL = 0; + SAFE_OEM_FREE( m_pbChallenge ); + SAFE_OEM_FREE( m_pchSilentURL ); + + m_cbChallenge = 0; - dr = Drm_Reader_Bind(m_poAppContext, - g_rgpdstrRights, - DRM_NO_OF(g_rgpdstrRights), - _PolicyCallback, - nullptr, - m_oDecryptContext); + ChkDR(Drm_Content_SetProperty(m_poAppContext, + DRM_CSP_AUTODETECT_HEADER, + &mDrmHeader[ 0 ], + mDrmHeader.size()) ); - // FIXME : Check add case Play rights already acquired - // Try to figure out the size of the license acquisition - // challenge to be returned. dr = Drm_LicenseAcq_GenerateChallenge(m_poAppContext, g_rgpdstrRights, sizeof(g_rgpdstrRights) / sizeof(DRM_CONST_STRING *), - NULL, + nullptr, !m_customData.empty() ? m_customData.c_str() : nullptr, m_customData.size(), - NULL, + nullptr, &cchSilentURL, - NULL, - NULL, + nullptr, + nullptr, m_pbChallenge, &m_cbChallenge, - NULL); + &m_oBatchID ); - if (dr == DRM_E_BUFFERTOOSMALL) { - if (cchSilentURL > 0) { - ChkMem(m_pchSilentURL = (DRM_CHAR *)Oem_MemAlloc(cchSilentURL + 1)); - ZEROMEM(m_pchSilentURL, cchSilentURL + 1); - } + if ( dr == DRM_E_NO_URL ) + { + dr = Drm_LicenseAcq_GenerateChallenge(m_poAppContext, + g_rgpdstrRights, + sizeof(g_rgpdstrRights) / sizeof(DRM_CONST_STRING *), + nullptr, + !m_customData.empty() ? m_customData.c_str() : nullptr, + m_customData.size() , + nullptr, + nullptr, // null pointer to buffer size + nullptr, + nullptr, + m_pbChallenge, + &m_cbChallenge, + &m_oBatchID ); + } - // Allocate buffer that is sufficient to store the license acquisition - // challenge. - if (m_cbChallenge > 0) - ChkMem(m_pbChallenge = (DRM_BYTE *)Oem_MemAlloc(m_cbChallenge)); + if (dr == DRM_E_BUFFERTOOSMALL) + { + if (cchSilentURL > 0) + { + ChkMem( m_pchSilentURL = (DRM_CHAR * )Oem_MemAlloc(cchSilentURL + 1)); + ZEROMEM( m_pchSilentURL, cchSilentURL + 1 ); + } + + if ( m_cbChallenge > 0 ) + { + ChkMem( m_pbChallenge = (DRM_BYTE * )Oem_MemAlloc( m_cbChallenge + 1 ) ); + ZEROMEM( m_pbChallenge, m_cbChallenge + 1 ); + } dr = DRM_SUCCESS; - } else { + } + else + { ChkDR(dr); } - // Supply a buffer to receive the license acquisition challenge. ChkDR(Drm_LicenseAcq_GenerateChallenge(m_poAppContext, g_rgpdstrRights, sizeof(g_rgpdstrRights) / sizeof(DRM_CONST_STRING *), @@ -369,272 +779,950 @@ bool MediaKeySession::playreadyGenerateKeyRequest() { !m_customData.empty() ? m_customData.c_str() : nullptr, m_customData.size(), m_pchSilentURL, - &cchSilentURL, + cchSilentURL ? &cchSilentURL : nullptr, nullptr, nullptr, m_pbChallenge, &m_cbChallenge, - nullptr)); + &m_oBatchID ) ); m_eKeyState = KEY_PENDING; + if (m_piCallback) - m_piCallback->OnKeyMessage((const uint8_t *) m_pbChallenge, m_cbChallenge, (char *)m_pchSilentURL); - return true; + m_piCallback->OnKeyMessage((const uint8_t *) m_pbChallenge, m_cbChallenge, + m_pchSilentURL != NULL ? (char *)m_pchSilentURL : "" ); ErrorExit: if (DRM_FAILED(dr)) { - const DRM_CHAR* description; - DRM_ERR_GetErrorNameFromCode(dr, &description); - printf("playready error: %s\n", description); - if (m_piCallback) - m_piCallback->OnKeyMessage((const uint8_t *) "", 0, ""); + fprintf(stderr, "[%s:%d] failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + + if(m_piCallback) + { + m_piCallback->OnError( 0, CDMi_S_FALSE, "KeyError" ); + m_piCallback->OnKeyStatusUpdate(MapDrToKeyMessage(dr), nullptr, 0); + m_piCallback->OnKeyStatusesUpdated(); + } + m_eKeyState = KEY_ERROR; } - return false; + + return ( dr == DRM_SUCCESS ); } CDMi_RESULT MediaKeySession::Load(void) { return CDMi_S_FALSE; } -void MediaKeySession::Update(const uint8_t *m_pbKeyMessageResponse, uint32_t m_cbKeyMessageResponse) { +/*Set KeyId property which will be used by the Reader_Bind during license searching*/ +CDMi_RESULT MediaKeySession::SetKeyIdProperty( const DRM_WCHAR *f_rgwchEncodedKid, DRM_DWORD f_cchEncodedKid ){ + DRM_RESULT err = Drm_Content_SetProperty( + m_poAppContext, + DRM_CSP_AUTODETECT_HEADER, + (DRM_BYTE*)f_rgwchEncodedKid, + f_cchEncodedKid * sizeof( DRM_WCHAR ) ); + + if (DRM_FAILED(err)) { + fprintf(stderr, "[%s:%d] Drm_Content_SetProperty DRM_CSP_AUTODETECT_HEADER failed. 0x%08X - %s",__FUNCTION__,__LINE__,static_cast(err),DRM_ERR_NAME(err)); + return CDMi_FAIL; + } - DRM_RESULT dr = DRM_SUCCESS; -PUSH_WARNING(DISABLE_WARNING_MISSING_FIELD_INITIALIZERS) - DRM_LICENSE_RESPONSE oLicenseResponse = {eUnknownProtocol, 0}; -POP_WARNING() - - ChkArg(m_pbKeyMessageResponse && m_cbKeyMessageResponse > 0); - - ChkDR(Drm_LicenseAcq_ProcessResponse(m_poAppContext, - DRM_PROCESS_LIC_RESPONSE_SIGNATURE_NOT_REQUIRED, - const_cast(m_pbKeyMessageResponse), - m_cbKeyMessageResponse, - &oLicenseResponse)); - - ChkDR(Drm_Reader_Bind(m_poAppContext, - g_rgpdstrRights, - NO_OF(g_rgpdstrRights), - _PolicyCallback, - nullptr, - m_oDecryptContext)); - - m_eKeyState = KEY_READY; - - if (m_eKeyState == KEY_READY) { - if (m_piCallback) { - for (uint32_t i = 0; i < oLicenseResponse.m_cAcks; ++i) { - if (DRM_SUCCEEDED(oLicenseResponse.m_rgoAcks[i].m_dwResult)) { - m_piCallback->OnKeyStatusUpdate("KeyUsable", oLicenseResponse.m_rgoAcks[i].m_oKID.rgb, DRM_ID_SIZE); + return CDMi_SUCCESS; +} + +/*Converting KeyId into base64-encoded format*/ +CDMi_RESULT MediaKeySession::SetKeyIdProperty( KeyId & f_rKeyId ){ + DRM_WCHAR rgwchEncodedKid[CCH_BASE64_EQUIV(DRM_ID_SIZE)]= {0}; + DRM_DWORD cchEncodedKid = CCH_BASE64_EQUIV(DRM_ID_SIZE); + + if ( f_rKeyId.getKeyIdOrder() == KeyId::KEYID_ORDER_UUID_BE ) + { + f_rKeyId.ToggleFormat(); + } + + DRM_RESULT err = DRM_B64_EncodeW( f_rKeyId.getmBytes(), DRM_ID_SIZE, + rgwchEncodedKid, &cchEncodedKid, 0); + + if (DRM_FAILED(err)) { + fprintf(stderr, "[%s:%d] DRM_B64_EncodeW failed. 0x%08X - %s",__FUNCTION__,__LINE__,static_cast(err),DRM_ERR_NAME(err)); + return CDMi_FAIL; + } + return SetKeyIdProperty( rgwchEncodedKid, cchEncodedKid ); +} + +/*handles all the licenses in the response using Drm_LicenseAcq_ProcessResponse().*/ +DRM_RESULT MediaKeySession::ProcessLicenseResponse( + DRM_PROCESS_LIC_RESPONSE_FLAG f_eResponseFlag, + const DRM_BYTE *f_pbResponse, + DRM_DWORD f_cbResponse, + DRM_LICENSE_RESPONSE *f_pLiceneResponse ) { + DRM_RESULT dr = DRM_SUCCESS; + + dr = Drm_LicenseAcq_ProcessResponse( + m_poAppContext, + f_eResponseFlag, + f_pbResponse, + f_cbResponse, + f_pLiceneResponse ); + + if ( dr == DRM_E_LICACQ_TOO_MANY_LICENSES ) + { + DRM_DWORD cLicenses = f_pLiceneResponse->m_cAcks; + f_pLiceneResponse->m_pAcks = ( DRM_LICENSE_ACK * )Oem_MemAlloc( cLicenses * sizeof( DRM_LICENSE_ACK ) ); + f_pLiceneResponse->m_cMaxAcks = cLicenses; + + dr = Drm_LicenseAcq_ProcessResponse( + m_poAppContext, + f_eResponseFlag, + f_pbResponse, + f_cbResponse, + f_pLiceneResponse ); + } + return dr; +} + +/*Wrapper function for Drm_Reader_Bind()*/ +DRM_RESULT MediaKeySession::ReaderBind( + const DRM_CONST_STRING *f_rgpdstrRights[], + DRM_DWORD f_cRights, + DRMPFNPOLICYCALLBACK f_pfnPolicyCallback, + const DRM_VOID *f_pv, + DRM_DECRYPT_CONTEXT *f_pDecryptContext ) { + DRM_RESULT dr = DRM_SUCCESS; + DRM_BYTE *newOpaqueBuffer = nullptr; + + while( (dr=Drm_Reader_Bind( + m_poAppContext, + f_rgpdstrRights, + f_cRights, + f_pfnPolicyCallback, + f_pv, + f_pDecryptContext ) ) == DRM_E_BUFFERTOOSMALL ){ + + + DRM_BYTE *pbOldBuf = nullptr; + DRM_DWORD cbOldBuf = 0; + + if ( m_cbPROpaqueBuf == 0 ) + m_cbPROpaqueBuf = MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE; + + m_cbPROpaqueBuf *= 2; + + if ( m_cbPROpaqueBuf > MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE * 64 ){ + ChkDR( DRM_E_OUTOFMEMORY ); + } + + ChkMem( newOpaqueBuffer = ( DRM_BYTE* )Oem_MemAlloc( m_cbPROpaqueBuf ) ); + + dr = Drm_GetOpaqueBuffer( m_poAppContext, &pbOldBuf, &cbOldBuf ); + if ( DRM_FAILED( dr ) ){ + fprintf(stderr, "[%s:%d] Drm_GetOpaqueBuffer failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + SAFE_OEM_FREE( newOpaqueBuffer ); + ChkDR( dr ); + } + + dr = Drm_ResizeOpaqueBuffer( m_poAppContext, newOpaqueBuffer, m_cbPROpaqueBuf ); + if ( DRM_FAILED( dr ) ){ + fprintf(stderr, "[%s:%d] Drm_ResizeOpaqueBuffer failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + SAFE_OEM_FREE( newOpaqueBuffer ); + ChkDR( dr ); + } + + if ( m_pbPROpaqueBuf != nullptr && m_pbPROpaqueBuf == pbOldBuf ){ + SAFE_OEM_FREE( pbOldBuf ); + m_pbPROpaqueBuf = newOpaqueBuffer; + }else{ + SAFE_OEM_FREE( pbOldBuf ); } - } - m_piCallback->OnKeyStatusesUpdated(); } - } - return; + ErrorExit: + if ( DRM_FAILED( dr ) ){ + fprintf(stderr, "[%s:%d] failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + } + + return dr; +} + +CDMi_RESULT MediaKeySession::PersistentLicenseCheck() { +#ifdef NO_PERSISTENT_LICENSE_CHECK + // DELIA-51437: The Webkit EME implementation used by OTT apps + // such as Amazon and YouTube fails when the key is usable from + // just the init data. Webkit is expecting a license request + // message and the lack of this message prevents the session from + // loading correctly. + // + // The EME concept of a persistent session uses the Session Id to + // reload a session, not the raw Key ID. We do not current + // support that type of session in the OCDM. Apps wishing to use + // persistent keys should directly link to PR4 or the OCDM should + // be rewritten to use PR4's CDMI API + // (modules/cdmi/real/drmcdmireal.c). + fprintf(stderr, "\n PersistentLicenseCheck: skipping persistent check"); + return CDMi_S_FALSE; +#else + DRM_RESULT dr = DRM_SUCCESS; + DRM_CONTENT_SET_PROPERTY eContentPropertyType = DRM_CSP_HEADER_NOT_SET; + + if ( !mDrmHeader.size() ) { + fprintf(stderr, "[%s:%d] mDrmHeader not set",__FUNCTION__,__LINE__); + return CDMi_FAIL; + } + if ( m_pdstrHeaderKIDs == NULL || m_cHeaderKIDs == 0 ){ + fprintf(stderr, "[%s:%d] key ids not set",__FUNCTION__,__LINE__); + return CDMi_FAIL; + } + + if ( m_eHeaderVersion == DRM_HEADER_VERSION_4_2 ) + eContentPropertyType = DRM_CSP_V4_2_HEADER; + else if ( m_eHeaderVersion == DRM_HEADER_VERSION_4_3 ) + eContentPropertyType = DRM_CSP_V4_3_HEADER; + else{ + eContentPropertyType = DRM_CSP_AUTODETECT_HEADER; + } + + for( DRM_DWORD idx = 0; idx < m_cHeaderKIDs; idx++ ){ + + KeyId keyId; + keyId.keyDecode(m_pdstrHeaderKIDs[ idx ]); + keyId.setKeyIdOrder(KeyId::KEYID_ORDER_GUID_LE); + + DECRYPT_CONTEXT decryptContext; + + if ( CDMi_SUCCESS != SetKeyIdProperty( m_pdstrHeaderKIDs[idx].pwszString, + m_pdstrHeaderKIDs[idx].cchString ) ) { + fprintf(stderr, "[%s:%d] SetKeyIdProperty failed. %s",__FUNCTION__,__LINE__,printGuid(keyId)); + ChkDR( DRM_E_FAIL ); + } + + decryptContext = NEW_DECRYPT_CONTEXT(); + + dr = ReaderBind( + g_rgpdstrRights, + NO_OF(g_rgpdstrRights), + _PolicyCallback, + &m_playreadyLevels, + &(decryptContext->oDrmDecryptContext) ); + if ( DRM_FAILED( dr ) ){ + ChkDR( dr ); + } + + decryptContext->keyId = keyId; + m_DecryptContextVector.push_back(decryptContext); + } ErrorExit: - if (DRM_FAILED(dr)) { - const DRM_CHAR* description; - DRM_ERR_GetErrorNameFromCode(dr, &description); - printf("playready error: %s\n", description); - m_eKeyState = KEY_ERROR; + if ( DRM_FAILED( dr ) ){ + CloseDecryptContexts(); + return CDMi_FAIL; + } - // The upper layer is blocked waiting for an update, let's wake it. - if (m_piCallback) { - for (uint32_t i = 0; i < oLicenseResponse.m_cAcks; ++i) { - m_piCallback->OnKeyStatusUpdate("KeyError", oLicenseResponse.m_rgoAcks[i].m_oKID.rgb, DRM_ID_SIZE); - } - m_piCallback->OnKeyStatusesUpdated(); + if ( m_piCallback ) + { + for (DECRYPT_CONTEXT &p : m_DecryptContextVector) + { + m_piCallback->OnKeyStatusUpdate(MapDrToKeyMessage( dr ), p->keyId.getmBytes(), DRM_ID_SIZE); + } + m_piCallback->OnKeyStatusesUpdated(); } - } + + m_eKeyState = KEY_READY; + + return CDMi_SUCCESS; +#endif +} + +// Allow persistent PlayReady licenses to be used in a temporary +// session. +// +// Ideally, the license server would only return temporary licenses +// and would could block all persistent license with the +// `DRM_PROCESS_LIC_RESPONSE_FLAG` value of +// `DRM_PROCESS_LIC_RESPONSE_BLOCK_PERSISTENT_LICENSES`. +// +// Instead, we allow persistent licenses to be used but attempt to +// clean them up when the session closes. +void MediaKeySession::SaveTemporaryPersistentLicenses(const DRM_LICENSE_RESPONSE* f_poLicenseResponse) { + + fprintf(stderr, "\n SaveTemporaryPersistentLicenses: response has persistent licenses: %s", + f_poLicenseResponse->m_fHasPersistentLicenses ? "true" : "false"); + + if (!f_poLicenseResponse->m_fHasPersistentLicenses) { + return; + } + + // We know there are persistent license but not which ones. Save + // them all for deletion when we close a session. + for (DRM_DWORD i = 0; i < f_poLicenseResponse->m_cAcks; ++i) { + const DRM_LICENSE_ACK *pLicenseAck = nullptr; + + pLicenseAck = f_poLicenseResponse->m_pAcks != nullptr + ? &f_poLicenseResponse->m_pAcks[ i ] : &f_poLicenseResponse->m_rgoAcks[ i ]; + + if ( DRM_SUCCEEDED( pLicenseAck->m_dwResult ) ) { + m_oPersistentLicenses.emplace_back(pLicenseAck->m_oKID, pLicenseAck->m_oLID); + } + } +} + +void MediaKeySession::DeleteTemporaryPersistentLicenses() { + DRM_RESULT dr = DRM_SUCCESS; + DRM_CONST_STRING dstrKID = DRM_EMPTY_DRM_STRING; + DRM_CONST_STRING dstrLID = DRM_EMPTY_DRM_STRING; + DRM_DWORD cbstrKID = 0; + DRM_DWORD cLicDeleted = 0; + + fprintf(stderr, "\n DeleteTemporaryPersistentLicenses: deleting %zd possibly persistent licenses", + m_oPersistentLicenses.size()); + + /* Allocate strKID buffer */ + cbstrKID = CCH_BASE64_EQUIV( sizeof( DRM_ID ) ) * sizeof( DRM_WCHAR ); + ChkMem( dstrKID.pwszString = (DRM_WCHAR *) Oem_MemAlloc( cbstrKID ) ); + dstrKID.cchString = CCH_BASE64_EQUIV( sizeof( DRM_ID ) ); + + ChkMem( dstrLID.pwszString = (DRM_WCHAR *) Oem_MemAlloc( cbstrKID ) ); + dstrLID.cchString = CCH_BASE64_EQUIV( sizeof( DRM_ID ) ); + + for (const auto& pair: m_oPersistentLicenses) { + + /* Convert KID to string */ + dr = DRM_B64_EncodeW( + (DRM_BYTE*)&pair.first, + sizeof( DRM_ID ), + (DRM_WCHAR*)dstrKID.pwszString, + &dstrKID.cchString, + DRM_BASE64_ENCODE_NO_FLAGS ); + + if (DRM_FAILED(dr)) { + fprintf(stderr, "\n DeleteTemporaryPersistentLicenses: DRM_B64_EncodeW failed for KID: 0x%08X", dr); + continue; + } + + /* Convert LID to string */ + dr = DRM_B64_EncodeW( + (DRM_BYTE*)&pair.second, + sizeof( DRM_ID ), + (DRM_WCHAR*)dstrLID.pwszString, + &dstrKID.cchString, + DRM_BASE64_ENCODE_NO_FLAGS ); + + if (DRM_FAILED(dr)) { + fprintf(stderr, "\n DeleteTemporaryPersistentLicenses: DRM_B64_EncodeW failed for LID: 0x%08X", dr); + continue; + } + + dr = Drm_StoreMgmt_DeleteLicenses( + m_poAppContext, + &dstrKID, + &dstrLID, + &cLicDeleted); + + } + +ErrorExit: + SAFE_OEM_FREE( dstrKID.pwszString ); + SAFE_OEM_FREE( dstrLID.pwszString ); + + return; +} + +/*processes the license response and creates decryptor for each valid ack available in the response*/ +void MediaKeySession::Update(const uint8_t *m_pbKeyMessageResponse, uint32_t m_cbKeyMessageResponse) { + + + DRM_RESULT dr = DRM_SUCCESS; + DRM_LICENSE_RESPONSE oLicenseResponse = { eUnknownProtocol, 0 }; + DRM_LICENSE_ACK *pLicenseAck = nullptr; + DRM_DWORD decryptionMode; + bool bIsAudioNeedNonSVPContext; + + ChkBOOL(m_eKeyState == KEY_PENDING, DRM_E_INVALIDARG); + + ChkArg(m_pbKeyMessageResponse && m_cbKeyMessageResponse > 0); + + ChkDR( ProcessLicenseResponse( + DRM_PROCESS_LIC_RESPONSE_NO_FLAGS, + const_cast(m_pbKeyMessageResponse), + m_cbKeyMessageResponse, + &oLicenseResponse ) ); + + SaveTemporaryPersistentLicenses(&oLicenseResponse); + + for (DRM_DWORD i = 0; i < oLicenseResponse.m_cAcks; ++i) { + + pLicenseAck = oLicenseResponse.m_pAcks != nullptr + ? &oLicenseResponse.m_pAcks[ i ] : &oLicenseResponse.m_rgoAcks[ i ]; + + KeyId keyId(&pLicenseAck->m_oKID.rgb[0],KeyId::KEYID_ORDER_GUID_LE); + + dr = pLicenseAck->m_dwResult; + if ( DRM_SUCCEEDED( dr ) ) { + + DECRYPT_CONTEXT decryptContext; + + if ( CDMi_SUCCESS != SetKeyIdProperty( keyId ) ) + { + dr = DRM_E_FAIL; + goto LoopEnd; + } + + decryptContext = NEW_DECRYPT_CONTEXT(); + + decryptionMode = OEM_TEE_DECRYPTION_MODE_HANDLE; + dr = Drm_Content_SetProperty(m_poAppContext, + DRM_CSP_DECRYPTION_OUTPUT_MODE, + (const DRM_BYTE*)&decryptionMode, + sizeof decryptionMode); + if (!DRM_SUCCEEDED(dr)) { + fprintf(stderr, "[%s:%d] Drm_Content_SetProperty() failed with %lx - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + goto ErrorExit; + } + + dr = ReaderBind( + g_rgpdstrRights, + NO_OF(g_rgpdstrRights), + _PolicyCallback, + &m_playreadyLevels, + &(decryptContext->oDrmDecryptContext) ); + + if ( DRM_FAILED( dr ) ){ + fprintf(stderr, "[%s:%d] ReaderBind failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + goto LoopEnd; + } + + bIsAudioNeedNonSVPContext = svpIsAudioNeedNonSVPContext(); + + if(bIsAudioNeedNonSVPContext) + { + decryptionMode = OEM_TEE_DECRYPTION_MODE_NOT_SECURE; + dr = Drm_Content_SetProperty(m_poAppContext, + DRM_CSP_DECRYPTION_OUTPUT_MODE, + (const DRM_BYTE*)&decryptionMode, + sizeof decryptionMode); + if (!DRM_SUCCEEDED(dr)) { + fprintf(stderr, "[%s:%d] Drm_Content_SetProperty() failed with %lx - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + goto ErrorExit; + } + + dr = ReaderBind( + g_rgpdstrRights, + NO_OF(g_rgpdstrRights), + _PolicyCallback, + &m_playreadyLevels, + &(decryptContext->oDrmDecryptAudioContext) ); + + if ( DRM_FAILED( dr ) ){ + fprintf(stderr, "[%s:%d] ReaderBind failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + goto LoopEnd; + } + } + + decryptContext->keyId = keyId; + + if ( oLicenseResponse.m_cAcks == 1 ){ + m_currentDecryptContext = decryptContext; + } + + m_DecryptContextVector.push_back(decryptContext); + + m_eKeyState = KEY_READY; + } + LoopEnd: + if ( m_piCallback ){ + m_piCallback->OnKeyStatusUpdate( MapDrToKeyMessage( dr ), keyId.getmBytes(), DRM_ID_SIZE); + } + } + + if ( m_eKeyState == KEY_READY ){ + dr = DRM_SUCCESS; + }else{ + fprintf(stderr, "[%s:%d] Could not bind to any licenses",__FUNCTION__,__LINE__); + dr = DRM_E_FAIL; + } + +ErrorExit: + + if (DRM_FAILED(dr)) { + fprintf(stderr, "[%s:%d] failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + m_eKeyState = KEY_ERROR; + } + if (m_piCallback){ + m_piCallback->OnKeyStatusesUpdated(); + } + SAFE_OEM_FREE( oLicenseResponse.m_pAcks ); return; } CDMi_RESULT MediaKeySession::Remove(void) { + fprintf(stderr, "[%s:%d] returning false ",__FUNCTION__,__LINE__); return CDMi_S_FALSE; } +/*Closes each DRM_DECRYPT_CONTEXT using Drm_Reader_Close()*/ +void MediaKeySession::CloseDecryptContexts(void) { + m_currentDecryptContext = nullptr; + for (DECRYPT_CONTEXT &p : m_DecryptContextVector) + { + Drm_Reader_Close(&(p->oDrmDecryptContext)); + Drm_Reader_Close(&(p->oDrmDecryptAudioContext)); + } + m_DecryptContextVector.clear(); +} + +void MediaKeySession::DeleteInMemoryLicenses() { + DRM_ID emptyId = DRM_ID_EMPTY; + + if (memcmp(&m_oBatchID, &emptyId, sizeof(DRM_ID)) == 0) { + return; + } + KeyId batchId(&m_oBatchID.rgb[0],KeyId::KEYID_ORDER_GUID_LE); + + DRM_RESULT dr = Drm_StoreMgmt_DeleteInMemoryLicenses(m_poAppContext, &m_oBatchID); + if (DRM_FAILED(dr) && dr != DRM_E_NOMORE) { + fprintf(stderr, "[%s:%d] Drm_StoreMgmt_DeleteInMemoryLicenses failed for batchId:%s. 0x%X - %s",__FUNCTION__,__LINE__,printUuid(batchId),dr,DRM_ERR_NAME(dr)); + } +} + CDMi_RESULT MediaKeySession::Close(void) { - m_eKeyState = KEY_CLOSED; - if (mInitiateChallengeGeneration == true) { - if (DRM_REVOCATION_IsRevocationSupported() && m_pbRevocationBuffer != nullptr) { + if ( m_eKeyState != KEY_CLOSED ) { +#ifdef USE_SVP + m_stSecureBuffInfo.bReleaseSecureMemRegion = true; + if(0 != svp_release_secure_buffers(m_pSVPContext, (void*)&m_stSecureBuffInfo, nullptr, nullptr, 0)) + { + fprintf(stderr, "[%s:%d] secure memory, free failed",__FUNCTION__,__LINE__); + } + else { + m_stSecureBuffInfo.bCreateSecureMemRegion = false; + m_stSecureBuffInfo.SecureMemRegionSize = 0; + } + gst_svp_ext_free_context(m_pSVPContext); + m_pSVPContext = NULL; +#endif + + SAFE_OEM_FREE(m_pbChallenge); + + SAFE_OEM_FREE(m_pchSilentURL); + + CloseDecryptContexts(); + + DeleteInMemoryLicenses(); + + DeleteTemporaryPersistentLicenses(); + + mDrmHeader.clear(); + SAFE_OEM_FREE(m_pbRevocationBuffer); - m_pbRevocationBuffer = nullptr; - } - if (m_poAppContext != nullptr) { - Drm_Uninitialize(m_poAppContext); - SAFE_OEM_FREE(m_poAppContext); - m_poAppContext = nullptr; - } + SAFE_OEM_FREE(m_pdstrHeaderKIDs); - if (m_pbOpaqueBuffer != nullptr) { - SAFE_OEM_FREE(m_pbOpaqueBuffer); - m_pbOpaqueBuffer = nullptr; - } + m_eKeyState = KEY_CLOSED; + } - if (m_oDecryptContext != nullptr) { - delete m_oDecryptContext; - m_oDecryptContext = nullptr; - } + return CDMi_SUCCESS; +} + +CDMi_RESULT MediaKeySession::PlaybackStopped(void) { + return CDMi_SUCCESS; +} + +const char* MediaKeySession::MapDrToKeyMessage( DRM_RESULT dr ) +{ + switch (dr) + { + case DRM_SUCCESS: + return "KeyUsable"; + case DRM_E_TEE_OUTPUT_PROTECTION_REQUIREMENTS_NOT_MET: + case DRM_E_TEST_OPL_MISMATCH: + return "KeyOutputRestricted"; + case DRM_E_TEE_OUTPUT_PROTECTION_INSUFFICIENT_HDCP: + return "KeyOutputRestrictedHDCP"; + case DRM_E_TEE_OUTPUT_PROTECTION_INSUFFICIENT_HDCP22: + case DRM_E_TEST_INVALID_OPL_CALLBACK: + return "KeyOutputRestrictedHDCP22"; + case DRM_E_LICENSE_NOT_FOUND: + return "LicenseNotFound"; + case DRM_E_LICENSE_EXPIRED: + return "LicenseExpired"; + default: + return "KeyInternalError"; + } +} + +CDMi_RESULT MediaKeySession::DRM_DecryptFailure(DRM_RESULT dr, const uint8_t *payloadData, uint32_t *f_pcbOpaqueClearContent, uint8_t **f_ppbOpaqueClearContent) +{ + fprintf(stderr, "[%s:%d] playready decrypt() failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); - if (m_pbChallenge != nullptr) { - SAFE_OEM_FREE(m_pbChallenge); - m_pbChallenge = nullptr; + if(f_pcbOpaqueClearContent != nullptr) + { + *f_pcbOpaqueClearContent = 0; + } + if(f_ppbOpaqueClearContent != nullptr && payloadData != nullptr) + { + *f_ppbOpaqueClearContent = (uint8_t *)payloadData; } - if (m_pchSilentURL != nullptr) { - SAFE_OEM_FREE(m_pchSilentURL); - m_pchSilentURL = nullptr; + if(m_piCallback){ + char errStr[50]; + uint64_t errCode = (0xFFFFFFFF00000000)|(dr); + sprintf(errStr,"0x%llx-DecryptError",errCode); + m_piCallback->OnError(0, CDMi_S_FALSE, errStr); + m_piCallback->OnKeyStatusUpdate(MapDrToKeyMessage( dr ), nullptr, 0); + m_piCallback->OnKeyStatusesUpdated(); } - } - m_piCallback = nullptr; - m_fCommit = FALSE; - m_decryptInited = false; + return CDMi_S_FALSE; +} - return CDMi_SUCCESS; +DECRYPT_CONTEXT MediaKeySession::GetDecryptCtx( KeyId &f_rKeyId ) +{ + for (DECRYPT_CONTEXT &ctx : m_DecryptContextVector) + { + if (ctx->keyId == f_rKeyId) + { + return ctx; + } + } + return nullptr; +} + +CDMi_RESULT MediaKeySession::SetParameter(const std::string& name, const std::string& value) +{ + CDMi_RESULT retVal = CDMi_S_FALSE; + + if(name.find("rpcId") != std::string::npos) { + // Got the RPC ID for gst-svp-ext communication + unsigned int nID = 0; + nID = (unsigned int)std::stoul(value.c_str(), nullptr, 16); + if(nID != 0) { +#ifdef USE_SVP + //fprintf(stderr, "Initializing SVP context for client side ID = %X\n", nID); + gst_svp_ext_get_context(&m_pSVPContext, Client, nID); +#endif + } + } + return retVal; } CDMi_RESULT MediaKeySession::Decrypt( - VARIABLE_IS_NOT_USED const uint8_t *f_pbSessionKey, - VARIABLE_IS_NOT_USED uint32_t f_cbSessionKey, - VARIABLE_IS_NOT_USED const EncryptionScheme encryptionScheme, - VARIABLE_IS_NOT_USED const EncryptionPattern& pattern, - const uint8_t *f_pbIV, - uint32_t f_cbIV, - uint8_t *payloadData, - uint32_t payloadDataSize, - uint32_t *f_pcbOpaqueClearContent, - uint8_t **f_ppbOpaqueClearContent, - const uint8_t, // keyIdLength - const uint8_t*, // keyId - bool ) //initWithLast15 + uint8_t* inData, + const uint32_t inDataLength, + uint8_t** outData, + uint32_t* outDataLength, + const SampleInfo* sampleInfo, + const IStreamProperties* properties) { - uint32_t *f_pdwSubSampleMapping; - uint32_t f_cdwSubSampleMapping; + CDMi_RESULT ret = CDMi_S_FALSE; + DRM_RESULT dr = DRM_SUCCESS; + DRM_RESULT err = DRM_SUCCESS; + DRM_UINT64 iv_high = 0; + DRM_UINT64 iv_low = 0; + void* pSecureToken = nullptr; + uint8_t* pEncryptedDataStart = nullptr; + uint32_t actualEncDataLength = 0; + void* header = NULL; + DRM_DWORD encryptedRegionIvCounts = 1; + DRM_DWORD encryptedRegionCounts; + std::vector encryptedRegionSkip; + std::vector encryptedRegionMapping; + bool bGstSvpStatus = false; + bool useSVP = true; // By default SVP is required + DRM_UINT64 iv_vector[2] = { 0 }; + bool bIsVideoResCheckNeed = false; + bool bIsDynamicSVPEncEnabled = false; + uint64_t mCurrentPixels; + bool bIsAudioNeedNonSVPContext; + bool bIsMultipleOpaqueSupportCTR = false; + DRM_DWORD* pDecryptedLength = 0; + DRM_BYTE* pDecryptedContent = NULL; + DRM_BYTE* pEncryptedData = NULL; + + assert(sampleInfo->ivLength > 0); + + bIsVideoResCheckNeed = svpIsVideoResCheckNeed(); + + if(bIsVideoResCheckNeed) + { + if (properties->GetMediaType() == Video) { + mCurrentPixels = properties->GetHeight() * properties->GetWidth(); + } + + /* MaxResDecode */ + if (mMaxResDecodeSet) { + if ((mCurrentPixels > mMaxResDecodePixels)) { + fprintf(stderr, "[%s:%d] video resolution:%llu exceeds maximum resolution:%lu",__FUNCTION__,__LINE__,mCurrentPixels,mMaxResDecodePixels); + return CDMi_S_FALSE; + } + } + } - SafeCriticalSection systemLock(drmAppContextMutex_); - assert(f_cbIV > 0); - if(payloadDataSize == 0){ - return CDMi_SUCCESS; + bIsDynamicSVPEncEnabled = svpIsDynamicSVPEncEnabled(); + if(bIsDynamicSVPEncEnabled) + { + if (properties->GetMediaType() != Video) { + useSVP = false; } + } - if (!m_oDecryptContext) { - fprintf(stderr, "Error: no decrypt context (yet?)\n"); - return CDMi_S_FALSE; + if ( sampleInfo->keyId != nullptr ){ + KeyId keyId(&sampleInfo->keyId[0],KeyId::KEYID_ORDER_UUID_BE); + + if (m_currentDecryptContext == nullptr + || m_currentDecryptContext->keyId != keyId) + { + m_currentDecryptContext = GetDecryptCtx( keyId ); + } + } + + if ( m_currentDecryptContext == nullptr ){ + fprintf(stderr, "[%s:%d] m_currentDecryptContext is Nullptr",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; + } + + SafeCriticalSection systemLock(drmAppContextMutex_); + + if (properties->InitLength()) { + // Netflix case + memcpy(iv_vector, sampleInfo->iv, sampleInfo->ivLength * sizeof(uint8_t)); + + } else { + // Regular case + NETWORKBYTES_TO_QWORD(iv_vector[0], sampleInfo->iv, 0); + if (sampleInfo->ivLength == 16) { + NETWORKBYTES_TO_QWORD(iv_vector[1], sampleInfo->iv, 8); } - - DRM_RESULT err = DRM_SUCCESS; -PUSH_WARNING(DISABLE_WARNING_MISSING_FIELD_INITIALIZERS) - DRM_AES_COUNTER_MODE_CONTEXT ctrContext = { 0 }; -POP_WARNING() + } + + if (gst_svp_has_header(m_pSVPContext, inData)) + { + + header = (void*)inData; + pEncryptedDataStart = reinterpret_cast(gst_svp_header_get_start_of_data(m_pSVPContext, header)); + gst_svp_header_get_field(m_pSVPContext, header, SvpHeaderFieldName::DataSize, &actualEncDataLength); + } - DRM_DWORD rgdwMappings[2]; + if (sampleInfo->subSampleCount > 0) { + for (int i = 0; i < sampleInfo->subSampleCount; i++) { + encryptedRegionMapping.push_back(sampleInfo->subSample[i].clear_bytes); + encryptedRegionMapping.push_back(sampleInfo->subSample[i].encrypted_bytes); + } + } else { + encryptedRegionMapping.push_back(0); + encryptedRegionMapping.push_back(actualEncDataLength); + } - if ( (f_pcbOpaqueClearContent == NULL) || (f_ppbOpaqueClearContent == NULL) - || (f_pbIV == NULL || f_cbIV == 0) || (m_eKeyState != KEY_READY) ) + encryptedRegionCounts = encryptedRegionMapping.size()/2; + + if(useSVP) + { + // Reallocate input memory if needed. + if(m_stSecureBuffInfo.bCreateSecureMemRegion) { - fprintf(stderr, "Error: Decrypt - Invalid argument\n"); + if (actualEncDataLength > m_stSecureBuffInfo.SecureMemRegionSize) { + m_stSecureBuffInfo.bReleaseSecureMemRegion = true; + if(0 != svp_release_secure_buffers(m_pSVPContext, (void*)&m_stSecureBuffInfo, nullptr, nullptr, 0)) + { + fprintf(stderr, "[%s:%d] Secure memory free falied",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; + } + m_stSecureBuffInfo.SecureMemRegionSize = actualEncDataLength; + m_stSecureBuffInfo.bReleaseSecureMemRegion = false; + + if(0 != svp_allocate_secure_buffers(m_pSVPContext, (void**)&m_stSecureBuffInfo, nullptr, nullptr, m_stSecureBuffInfo.SecureMemRegionSize)) + { + fprintf(stderr, "[%s:%d] Secure memory, re-allocation failed %d",__FUNCTION__,__LINE__, m_stSecureBuffInfo.SecureMemRegionSize); + return CDMi_S_FALSE; + } + } + } + + m_stSecureBuffInfo.patternClearBlocks = sampleInfo->pattern.clear_blocks; + + if(0 != svp_allocate_secure_buffers(m_pSVPContext, (void**)&m_stSecureBuffInfo, nullptr, pEncryptedDataStart, actualEncDataLength)) + { + fprintf(stderr, "[%s:%d] secure memory, allocate failed [%d]",__FUNCTION__,__LINE__, actualEncDataLength); + return CDMi_S_FALSE; + } + +/* TO DO */ +#if defined TEE_CONFIG_NEED + OEM_OPTEE_SetHandle(m_stSecureBuffInfo.pSecBufHandle); +#endif /* TEE_CONFIG_NEED */ + + bGstSvpStatus = svp_buffer_alloc_token(&pSecureToken); + if (!bGstSvpStatus) { + fprintf(stderr, "[%s:%d] memory allocation for Token is failure",__FUNCTION__,__LINE__); + m_stSecureBuffInfo.bReleaseSecureMemRegion = false; + // Free decrypted secure buffer. + svp_release_secure_buffers(m_pSVPContext, (void*)&m_stSecureBuffInfo, (void*)m_stSecureBuffInfo.pAVSecBuffer , nullptr, 0); return CDMi_S_FALSE; } - *f_pcbOpaqueClearContent = 0; - *f_ppbOpaqueClearContent = NULL; + bGstSvpStatus = svp_buffer_to_token(m_pSVPContext, (void *)&m_stSecureBuffInfo, pSecureToken); + if (!bGstSvpStatus) { + fprintf(stderr, "[%s:%d] Buffer to Token creation is failure",__FUNCTION__,__LINE__); + m_stSecureBuffInfo.bReleaseSecureMemRegion = false; + // Free decrypted secure buffer. + svp_release_secure_buffers(m_pSVPContext, (void*)&m_stSecureBuffInfo, (void*)m_stSecureBuffInfo.pAVSecBuffer , nullptr, 0); + svp_buffer_free_token(pSecureToken); + return CDMi_S_FALSE; + } + } + + if (sampleInfo->pattern.encrypted_blocks != 0){ + encryptedRegionSkip.push_back(sampleInfo->pattern.encrypted_blocks); + encryptedRegionSkip.push_back(sampleInfo->pattern.clear_blocks); + } + + if (useSVP) + { + pDecryptedLength = reinterpret_cast(actualEncDataLength); + pDecryptedContent = reinterpret_cast(m_stSecureBuffInfo.pPhysAddr); + pEncryptedData = reinterpret_cast(m_stSecureBuffInfo.pEncryptedDataBuffer); + } + else + { + pEncryptedData = pEncryptedDataStart; + } + /* For Video */ + if (useSVP == true) + { + bIsMultipleOpaqueSupportCTR = svpIsMultipleOpaqueSupportCTR(); - // TODO: can be done in another way (now abusing "initWithLast15" variable) - if (mInitWithLast15) { - // Netflix case - memcpy(&ctrContext, f_pbIV, sizeof(ctrContext)); + if(bIsMultipleOpaqueSupportCTR) + { + err = Drm_Reader_DecryptMultipleOpaque(&(m_currentDecryptContext->oDrmDecryptContext), + encryptedRegionIvCounts, + iv_vector, + sampleInfo->ivLength == 16 ? iv_vector + 1 : nullptr, + &encryptedRegionCounts, + encryptedRegionMapping.size(), + &encryptedRegionMapping[0], + encryptedRegionSkip.size(), + &encryptedRegionSkip[0], + (DRM_DWORD) actualEncDataLength, + (DRM_BYTE *) pEncryptedData, + reinterpret_cast(&pDecryptedLength), + reinterpret_cast(&pDecryptedContent)); } else { - // Regular case - std::vector iv(f_cbIV, 0); - for (uint8_t i = 0; i < f_cbIV; i++) { -#if defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - iv[(f_cbIV - 1) - i] = f_pbIV[i]; -#else - iv[i] = f_pbIV[i]; -#endif - } + err = Drm_Reader_DecryptOpaque( + &(m_currentDecryptContext->oDrmDecryptContext), + encryptedRegionMapping.size(), + reinterpret_cast(&encryptedRegionMapping[0]), + iv_vector[0], + actualEncDataLength, + (DRM_BYTE *) pEncryptedData, + reinterpret_cast(&pDecryptedLength), + reinterpret_cast(&pDecryptedContent)); + } - MEMCPY(&ctrContext.qwInitializationVector, iv.data(), iv.size()); + } + else + { + bIsAudioNeedNonSVPContext = svpIsAudioNeedNonSVPContext(); + bIsMultipleOpaqueSupportCTR = svpIsMultipleOpaqueSupportCTR(); + + if(bIsMultipleOpaqueSupportCTR) + { + /* For Audio with Non-SVP support*/ + err = Drm_Reader_DecryptMultipleOpaque(&(bIsAudioNeedNonSVPContext ? m_currentDecryptContext->oDrmDecryptAudioContext : + m_currentDecryptContext->oDrmDecryptContext), + encryptedRegionIvCounts, + iv_vector, + sampleInfo->ivLength == 16 ? iv_vector + 1 : nullptr, + &encryptedRegionCounts, + encryptedRegionMapping.size(), + &encryptedRegionMapping[0], + encryptedRegionSkip.size(), + &encryptedRegionSkip[0], + (DRM_DWORD) actualEncDataLength, + (DRM_BYTE *) pEncryptedData, + reinterpret_cast(&pDecryptedLength), + reinterpret_cast(&pDecryptedContent)); + } else { + err = Drm_Reader_DecryptOpaque( + &(bIsAudioNeedNonSVPContext ? m_currentDecryptContext->oDrmDecryptAudioContext : + m_currentDecryptContext->oDrmDecryptContext), + encryptedRegionMapping.size(), + reinterpret_cast(&encryptedRegionMapping[0]), + iv_vector[0], + actualEncDataLength, + (DRM_BYTE *) pEncryptedData, + reinterpret_cast(&pDecryptedLength), + reinterpret_cast(&pDecryptedContent)); } - rgdwMappings[0] = 0; - rgdwMappings[1] = payloadDataSize; - f_pdwSubSampleMapping = reinterpret_cast(rgdwMappings); - f_cdwSubSampleMapping = NO_OF(rgdwMappings); + } - err = Drm_Reader_DecryptOpaque( - m_oDecryptContext, - f_cdwSubSampleMapping, - reinterpret_cast(f_pdwSubSampleMapping), - ctrContext.qwInitializationVector, - payloadDataSize, - (DRM_BYTE *) payloadData, - reinterpret_cast(f_pcbOpaqueClearContent), - reinterpret_cast(f_ppbOpaqueClearContent)); + if (DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] Drm_Reader_DecryptMultipleOpaque failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + DRM_DecryptFailure(err, nullptr, nullptr, nullptr); +#ifdef USE_SVP + if (useSVP) + { + m_stSecureBuffInfo.bReleaseSecureMemRegion = false; + // Free decrypted secure buffer. + svp_release_secure_buffers(m_pSVPContext, (void*)&m_stSecureBuffInfo, (void*)m_stSecureBuffInfo.pAVSecBuffer , nullptr, 0); + svp_buffer_free_token(pSecureToken); + } +#endif + return err; + } - if (DRM_FAILED(err)) + if(useSVP) + { + // Add a header to the output buffer. + if (header) { - fprintf(stderr, "Failed to run Drm_Reader_Decrypt\n"); - return CDMi_S_FALSE; + gst_svp_header_set_field(m_pSVPContext, header, SvpHeaderFieldName::Type, TokenType::Handle); } - if ( (*f_ppbOpaqueClearContent != nullptr) && (*f_pcbOpaqueClearContent > 0) && (*f_pcbOpaqueClearContent <= payloadDataSize) ) { - ::memcpy(payloadData, *f_ppbOpaqueClearContent, *f_pcbOpaqueClearContent); - ChkVOID( DRM_Reader_FreeOpaqueDecryptedContent( m_oDecryptContext, *f_pcbOpaqueClearContent, *f_ppbOpaqueClearContent) ); - *f_ppbOpaqueClearContent = payloadData; + memcpy((void *)(uint8_t*)pEncryptedDataStart, pSecureToken, svp_token_size()); + svp_buffer_free_token(pSecureToken); + } + else + { + if (header) + { + gst_svp_header_set_field(m_pSVPContext, header, SvpHeaderFieldName::Type, TokenType::InPlace); } - - // Call commit during the decryption of the first sample. - if (!m_fCommit) { - //err = Drm_Reader_Commit(m_poAppContext, &opencdm_output_levels_callback, &levels_); - err = Drm_Reader_Commit(m_poAppContext, _PolicyCallback, nullptr); // TODO: pass along user data - if (DRM_FAILED(err)) - { - fprintf(stderr, "Failed to do Reader Commit\n"); - return CDMi_S_FALSE; - } - m_fCommit = TRUE; + + if(NULL != pDecryptedContent) + { + memcpy((void *)(uint8_t*)pEncryptedDataStart, pDecryptedContent, pDecryptedLength); + free(pDecryptedContent); + pDecryptedContent = NULL; } + } + + if (useSVP) + { + m_stSecureBuffInfo.bReleaseSecureMemRegion = false; + // Free decrypted secure buffer. + svp_release_secure_buffers(m_pSVPContext, (void*)&m_stSecureBuffInfo, nullptr , nullptr, 0); + } + + if (!m_fCommit) { + err = Drm_Reader_Commit(m_poAppContext, _PolicyCallback, &m_playreadyLevels); + m_fCommit = TRUE; + } + + // Copy and Return the Memory token in the incoming payload buffer. + *outDataLength = inDataLength; + *outData = inData; + + return CDMi_SUCCESS; - return CDMi_SUCCESS; } CDMi_RESULT MediaKeySession::ReleaseClearContent( - VARIABLE_IS_NOT_USED const uint8_t *f_pbSessionKey, - VARIABLE_IS_NOT_USED uint32_t f_cbSessionKey, + const uint8_t *f_pbSessionKey, + uint32_t f_cbSessionKey, const uint32_t f_cbClearContentOpaque, uint8_t *f_pbClearContentOpaque ) { - - CDMi_RESULT res = CDMi_S_FALSE; - if( f_pbClearContentOpaque != NULL && f_cbClearContentOpaque > 0 && m_oDecryptContext){ - ChkVOID( DRM_Reader_FreeOpaqueDecryptedContent( m_oDecryptContext, f_cbClearContentOpaque, f_pbClearContentOpaque ) ); - res = CDMi_SUCCESS; - } - else{ - fprintf(stderr,"ReleaseClearContent: Failed to free the Clear Content buffer\n"); - } - return res; -} - -void MediaKeySession::CleanLicenseStore(DRM_APP_CONTEXT *pDrmAppCtx){ - if (m_poAppContext != nullptr) { - fprintf(stderr, "Licenses cleanup"); - // Delete all the licenses added by this session - DRM_RESULT dr = Drm_StoreMgmt_DeleteInMemoryLicenses(pDrmAppCtx, &mBatchId); - // Since there are multiple licenses in a batch, we might have already cleared - // them all. Ignore DRM_E_NOMORE returned from Drm_StoreMgmt_DeleteInMemoryLicenses. - if (DRM_FAILED(dr) && (dr != DRM_E_NOMORE)) { - fprintf(stderr, "Error in Drm_StoreMgmt_DeleteInMemoryLicenses 0x%08lX", dr); - } - } + return CDMi_SUCCESS; } } // namespace CDMi diff --git a/MediaSession.h b/MediaSession.h index bc6665f..028780d 100644 --- a/MediaSession.h +++ b/MediaSession.h @@ -14,60 +14,216 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once -#define MODULE_NAME OCDM_Playready - #include #include #include #include -#ifndef SIZEOF -#define SIZEOF sizeof -#include -#undef min -#undef max -#include -#else -#include -#endif #include +#if defined DRM_ERROR_NAME_SUPPORT #include +#endif #include +#include +#include +#include + +#if defined TEE_CONFIG_NEED +#include +#endif /* TEE_CONFIG_NEED */ +#undef min +#undef max #undef __in #undef __out -#undef __reserved - #include +#include +#include #include -#include +#include +#include + +#ifdef USE_SVP +#include "gst_svp_meta.h" +#endif + +#ifndef SIZEOF +#define SIZEOF sizeof +#endif + +#ifdef DRM_ONE_CHAR +#define ONE_CHAR DRM_ONE_CHAR +#endif + +#ifdef DRM_ONE_WCHAR +#define ONE_WCHAR DRM_ONE_WCHAR +#endif + +#ifdef DRM_CREATE_DRM_STRING +#define CREATE_DRM_STRING DRM_CREATE_DRM_STRING +#endif + +#if defined DRM_ERROR_NAME_SUPPORT +#define DRM_ERR_NAME( dr ) DRM_ERR_GetErrorNameFromCode( dr, nullptr ) +#else +#define DRM_ERR_NAME( dr ) #dr +#endif + +#define DRM_E_TEE_OUTPUT_PROTECTION_INSUFFICIENT_HDCP ((DRM_RESULT)0x8004dc80) +#define DRM_E_TEE_OUTPUT_PROTECTION_INSUFFICIENT_HDCP22 ((DRM_RESULT)0x8004dc81) +typedef struct +{ + char pszGlobalDir[128]; + char pszApplicationDir[128]; +}DRM_INIT_CONTEXT; + +#define PR4ChkDR(expr) do { \ + dr = ( expr ); \ + if( DRM_FAILED( dr ) ) \ + { \ + fprintf(stderr, "errcode: 0x%X; call: %s; infunc: %s()",dr, #expr, __func__); \ + goto ErrorExit; \ + } \ + } while(0) + +/////////////////////////////////////////////////////////////////// +class KeyId +{ +public: + + enum KeyIdOrder { KEYID_ORDER_GUID_LE, KEYID_ORDER_UUID_BE, KEYID_ORDER_UNKNOWN }; + + static const KeyId EmptyKeyId; + + KeyId( const DRM_BYTE * , KeyIdOrder); + void setKeyIdOrder(KeyIdOrder); + KeyIdOrder getKeyIdOrder(); + const DRM_BYTE* getmBytes(); + DRM_RESULT keyDecode( const DRM_CONST_STRING & ); + + KeyId() + { + m_hexStr.clear(); + m_base64Str.clear(); + ZEROMEM( m_bytes, DRM_ID_SIZE ); + keyIdOrder = KEYID_ORDER_UNKNOWN; + } + ~KeyId(){ } + + const char* HexStr(); + const char* B64Str(); + KeyId& ToggleFormat(); + + bool operator==( const KeyId &keyId ); + bool operator<( const KeyId &keyId ) const; + bool operator!=( const KeyId &keyId ) + { + return !( operator==(keyId) ); + }; + +private: + DRM_BYTE m_bytes[ DRM_ID_SIZE ]; + + std::string m_base64Str; + + std::string m_hexStr; + + KeyIdOrder keyIdOrder; +}; -#include +struct __DECRYPT_CONTEXT +{ + KeyId keyId; + DRM_DECRYPT_CONTEXT oDrmDecryptContext; + DRM_DECRYPT_CONTEXT oDrmDecryptAudioContext; + + __DECRYPT_CONTEXT() + { + memset( &oDrmDecryptContext, 0, sizeof( DRM_DECRYPT_CONTEXT ) ); + memset( &oDrmDecryptAudioContext, 0, sizeof( DRM_DECRYPT_CONTEXT ) ); + } +}; + +typedef std::shared_ptr<__DECRYPT_CONTEXT> DECRYPT_CONTEXT; + +#define NEW_DECRYPT_CONTEXT() std::make_shared<__DECRYPT_CONTEXT>() + +////////////////////////////////////////////////////////////////// +class SafeCriticalSection +{ +public: + explicit SafeCriticalSection(WPEFramework::Core::CriticalSection& lock) : mLock(lock), mLocked(false) + { + relock(); + } + + ~SafeCriticalSection() + { + unlock(); + } + + void unlock() + { + if (mLocked) { + mLocked = false; + mLock.Unlock(); + } + } + + void relock() + { + if (!mLocked) { + mLocked = true; + mLock.Lock(); + } + } + + WPEFramework::Core::CriticalSection &mutex() { return mLock; } + const WPEFramework::Core::CriticalSection &mutex() const { return mLock; } +private: + WPEFramework::Core::CriticalSection& mLock; + bool mLocked; +}; namespace CDMi { +struct PlayreadyOutProtLevels +{ + uint16_t compressedDigitalVideoLevel; + uint16_t uncompressedDigitalVideoLevel; + uint16_t analogVideoLevel; +}; + + struct PlayLevels { - uint16_t compressedDigitalVideoLevel_; //!< Compressed digital video output protection level. - uint16_t uncompressedDigitalVideoLevel_; //!< Uncompressed digital video output protection level. - uint16_t analogVideoLevel_; //!< Analog video output protection level. - uint16_t compressedDigitalAudioLevel_; //!< Compressed digital audio output protection level. - uint16_t uncompressedDigitalAudioLevel_; //!< Uncompressed digital audio output protection level. + uint16_t compressedDigitalVideoLevel_; + uint16_t uncompressedDigitalVideoLevel_; + uint16_t analogVideoLevel_; + uint16_t compressedDigitalAudioLevel_; + uint16_t uncompressedDigitalAudioLevel_; }; -class LicenseResponse { +class PlayreadySession +{ public: - LicenseResponse() : dlr(new DRM_LICENSE_RESPONSE) {} - ~LicenseResponse() { delete dlr; } - DRM_LICENSE_RESPONSE * get() { return dlr; } - void clear() { memset(dlr, 0, sizeof(DRM_LICENSE_RESPONSE)); } -private: - DRM_LICENSE_RESPONSE * const dlr; + PlayreadySession(); + ~PlayreadySession(); + + DRM_APP_CONTEXT *InitializeDRM(const DRM_CONST_STRING * pDRMStoreName); + + bool IsPlayreadySessionInit() { return m_bInitCalled; } + +protected: + DRM_APP_CONTEXT *m_poAppContext; + + DRM_BYTE *m_pbPROpaqueBuf; + DRM_DWORD m_cbPROpaqueBuf; + bool m_bInitCalled; }; -class MediaKeySession : public IMediaKeySession, public IMediaKeySessionExt { +class MediaKeySession : public PlayreadySession , public IMediaKeySession , public IMediaKeySessionExt { private: enum KeyState { // Has been initialized. @@ -88,15 +244,18 @@ class MediaKeySession : public IMediaKeySession, public IMediaKeySessionExt { IndividualizationRequest = 3 }; public: - //static const std::vector m_mimeTypes; - MediaKeySession(const uint8_t *f_pbInitData, uint32_t f_cbInitData, const uint8_t *f_pbCDMData, uint32_t f_cbCDMData, DRM_APP_CONTEXT * poAppContext, bool initWithLast15, bool initiateChallengeGeneration = false); + MediaKeySession( + const uint8_t drmHeader[], + uint32_t drmHeaderLength, + DRM_APP_CONTEXT * poAppContext, bool initiateChallengeGeneration = false); + + MediaKeySession(const uint8_t *f_pbInitData, uint32_t f_cbInitData, const uint8_t *f_pbCDMData, uint32_t f_cbCDMData, DRM_APP_CONTEXT * poAppContext, bool initiateChallengeGeneration = false); ~MediaKeySession(); bool playreadyGenerateKeyRequest(); bool ready() const { return m_eKeyState == KEY_READY; } - // MediaKeySession overrides virtual void Run( const IMediaKeySessionCallback *f_piMediaKeySessionCallback); @@ -109,23 +268,19 @@ class MediaKeySession : public IMediaKeySession, public IMediaKeySessionExt { virtual CDMi_RESULT Remove(); virtual CDMi_RESULT Close(void); + virtual CDMi_RESULT PlaybackStopped(void); + virtual CDMi_RESULT SetParameter(const std::string& name, const std::string& value); virtual const char *GetSessionId(void) const; virtual const char *GetKeySystem(void) const; - virtual CDMi_RESULT Decrypt( - const uint8_t *f_pbSessionKey, - uint32_t f_cbSessionKey, - const EncryptionScheme encryptionScheme, - const EncryptionPattern& pattern, - const uint8_t *f_pbIV, - uint32_t f_cbIV, - uint8_t *f_pbData, - uint32_t f_cbData, - uint32_t *f_pcbOpaqueClearContent, - uint8_t **f_ppbOpaqueClearContent, - const uint8_t keyIdLength, - const uint8_t* keyId, - bool initWithLast15) override; + + virtual CDMi_RESULT MediaKeySession::Decrypt( + uint8_t* inData, + const uint32_t inDataLength, + uint8_t** outData, + uint32_t* outDataLength, + const SampleInfo* sampleInfo, + const IStreamProperties* properties); virtual CDMi_RESULT ReleaseClearContent( const uint8_t *f_pbSessionKey, @@ -133,26 +288,34 @@ class MediaKeySession : public IMediaKeySession, public IMediaKeySessionExt { const uint32_t f_cbClearContentOpaque, uint8_t *f_pbClearContentOpaque ); - uint32_t GetSessionIdExt(void) const override; + static DRM_BOOL m_bPrintOPLError; + + uint32_t GetSessionIdExt(void) const; - virtual CDMi_RESULT SetDrmHeader(const uint8_t drmHeader[], uint32_t drmHeaderLength) override; - virtual CDMi_RESULT GetChallengeDataExt(uint8_t * challenge, uint32_t & challengeSize, uint32_t isLDL) override; - virtual CDMi_RESULT CancelChallengeDataExt() override; - virtual CDMi_RESULT StoreLicenseData(const uint8_t licenseData[], uint32_t licenseDataSize, unsigned char * secureStopId) override; - virtual CDMi_RESULT CleanDecryptContext() override; - virtual CDMi_RESULT SelectKeyId(const uint8_t keyLength, const uint8_t keyId[]) override; + virtual CDMi_RESULT SetDrmHeader(const uint8_t drmHeader[], uint32_t drmHeaderLength); + virtual CDMi_RESULT GetChallengeDataExt(uint8_t * challenge, uint32_t & challengeSize, uint32_t isLDL); + virtual CDMi_RESULT CancelChallengeDataExt(); + virtual CDMi_RESULT StoreLicenseData(const uint8_t licenseData[], uint32_t licenseDataSize, unsigned char * secureStopId); + virtual CDMi_RESULT SelectKeyId(const uint8_t keyLength, const uint8_t keyId[]); + virtual CDMi_RESULT CleanDecryptContext(); + + private: + std::vector< DECRYPT_CONTEXT > m_DecryptContextVector; + static bool mMaxResDecodeSet; + static uint64_t mMaxResDecodePixels; - static DRM_RESULT DRM_CALL _PolicyCallback(const DRM_VOID *, DRM_POLICY_CALLBACK_TYPE f_dwCallbackType, - const DRM_KID *, - const DRM_LID *, - const DRM_VOID *); + virtual CDMi_RESULT DRM_DecryptFailure(DRM_RESULT dr, const uint8_t *payloadData, uint32_t *f_pcbOpaqueClearContent, uint8_t **f_ppbOpaqueClearContent); - DRM_BYTE *m_pbOpaqueBuffer; - DRM_DWORD m_cbOpaqueBuffer; + struct PlayreadyOutProtLevels m_playreadyLevels; + static DRM_RESULT DRM_CALL _PolicyCallback(const DRM_VOID *, + DRM_POLICY_CALLBACK_TYPE f_dwCallbackType, const DRM_KID *, + const DRM_LID *, const DRM_VOID *); + + DRM_BYTE *m_pbRevocationBuffer; KeyState m_eKeyState; DRM_CHAR m_rgchSessionID[CCH_BASE64_EQUIV(SIZEOF(DRM_ID)) + 1]; @@ -162,32 +325,68 @@ class MediaKeySession : public IMediaKeySession, public IMediaKeySessionExt { DRM_CHAR *m_pchSilentURL; std::string m_customData; IMediaKeySessionCallback *m_piCallback; - void CleanLicenseStore(DRM_APP_CONTEXT *pDrmAppCtx); - - inline void PrintBase64(const int32_t length, const uint8_t* data, const char id[]) - { - std::string base64, hex; - Thunder::Core::ToString(data, length, true, base64); - Thunder::Core::ToHexString(data, length, hex); - fprintf(stderr, "%s: %s\t[%s]", id, base64.c_str(), hex.c_str()); - } -private: std::vector mDrmHeader; - std::vector mNounce; uint32_t mSessionId; - std::unique_ptr mLicenseResponse; - std::vector mSecureStopId; PlayLevels levels_; - bool mInitWithLast15; bool mInitiateChallengeGeneration; - DRM_ID mBatchId; + DRM_DWORD m_cHeaderKIDs; + DRM_CONST_STRING *m_pdstrHeaderKIDs; + eDRM_HEADER_VERSION m_eHeaderVersion; + DRM_ID m_oBatchID; + std::vector> m_oPersistentLicenses; + DECRYPT_CONTEXT m_currentDecryptContext; + SecureBufferInfo m_stSecureBuffInfo = {0}; +#ifdef USE_SVP + void* m_pSVPContext; + unsigned int m_rpcID; +#endif + CDMi_RESULT PersistentLicenseCheck(); + DRM_RESULT ProcessLicenseResponse( + DRM_PROCESS_LIC_RESPONSE_FLAG f_eResponseFlag, + const DRM_BYTE *f_pbResponse, + DRM_DWORD f_cbResponse, + DRM_LICENSE_RESPONSE *f_pLiceneResponse ); + + const char* MapDrToKeyMessage( DRM_RESULT ); + DRM_RESULT ReaderBind( + const DRM_CONST_STRING *f_rgpdstrRights[], + DRM_DWORD f_cRights, + DRMPFNPOLICYCALLBACK f_pfnPolicyCallback, + const DRM_VOID *f_pv, + DRM_DECRYPT_CONTEXT *f_pcontextDecrypt ); + void UpdateFromLicenseResponse( DRM_LICENSE_RESPONSE & ); + DECRYPT_CONTEXT GetDecryptCtx( KeyId & ); + CDMi_RESULT SetKeyIdProperty( KeyId & f_rKeyId ); + CDMi_RESULT SetKeyIdProperty( const DRM_WCHAR *, DRM_DWORD ); + CDMi_RESULT BindKeyNow(DECRYPT_CONTEXT decryptContext); + CDMi_RESULT BindKey(KeyId keyId); + CDMi_RESULT Unbind(KeyId keyId); + void CloseDecryptContexts(); + void DeleteInMemoryLicenses(); + void SaveTemporaryPersistentLicenses(const DRM_LICENSE_RESPONSE* f_poLicenseResponse); + void DeleteTemporaryPersistentLicenses(); + const char* printGuid(KeyId &keyId); + const char* printUuid(KeyId &keyId); protected: DRM_BOOL m_fCommit; DRM_APP_CONTEXT *m_poAppContext; - DRM_DECRYPT_CONTEXT *m_oDecryptContext; bool m_decryptInited; + bool m_bDRMInitializedLocally; +}; + +class CPRDrmPlatform +{ +public: + static DRM_RESULT DrmPlatformInitialize(); + static DRM_RESULT DrmPlatformInitialize( void * ); + static DRM_RESULT DrmPlatformUninitialize(); +private: + static DRM_DWORD m_dwInitRefCount; + CPRDrmPlatform() { } }; +//extern DRM_INIT_CONTEXT g_oDrmInitContext; + } // namespace CDMi diff --git a/MediaSessionExt.cpp b/MediaSessionExt.cpp index 42ba0d2..974a853 100644 --- a/MediaSessionExt.cpp +++ b/MediaSessionExt.cpp @@ -1,118 +1,63 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 Metrological - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - #include "MediaSession.h" #include #include #include -#include -using namespace std; -#include +#ifdef USE_SVP +#include "gst_svp_meta.h" +#endif + +using namespace std; -using namespace Thunder; -using SafeCriticalSection = Core::SafeSyncType; -extern Core::CriticalSection drmAppContextMutex_; +extern WPEFramework::Core::CriticalSection drmAppContextMutex_; -// The rights we want to request. -const DRM_WCHAR PLAY[] = { DRM_ONE_WCHAR('P', '\0'), - DRM_ONE_WCHAR('l', '\0'), - DRM_ONE_WCHAR('a', '\0'), - DRM_ONE_WCHAR('y', '\0'), - DRM_ONE_WCHAR('\0', '\0') +const DRM_WCHAR PLAY[] = { ONE_WCHAR('P', '\0'), + ONE_WCHAR('l', '\0'), + ONE_WCHAR('a', '\0'), + ONE_WCHAR('y', '\0'), + ONE_WCHAR('\0', '\0') }; -const DRM_CONST_STRING PLAY_RIGHT = DRM_CREATE_DRM_STRING(PLAY); +const DRM_CONST_STRING PLAY_RIGHT = CREATE_DRM_STRING(PLAY); -static const DRM_CONST_STRING* RIGHTS[] = { &PLAY_RIGHT }; +const KeyId KeyId::EmptyKeyId; namespace CDMi { -struct CallbackInfo -{ - IMediaKeySessionCallback * _callback; - uint16_t _compressedVideo; - uint16_t _uncompressedVideo; - uint16_t _analogVideo; - uint16_t _compressedAudio; - uint16_t _uncompressedAudio; -}; +std::map mBindMap; +static const DRM_CONST_STRING* RIGHTS[] = { &PLAY_RIGHT }; -static void * PlayLevelUpdateCallback(void * data) +MediaKeySession::MediaKeySession(const uint8_t drmHeader[], uint32_t drmHeaderLength, DRM_APP_CONTEXT * poAppContext, bool initiateChallengeGeneration /* = false */) + : m_pbRevocationBuffer(nullptr) + , m_eKeyState(KEY_CLOSED) + , m_pbChallenge(nullptr) + , m_cbChallenge(0) + , m_pchSilentURL(nullptr) + , m_piCallback(nullptr) + , mSessionId(0) + , mInitiateChallengeGeneration(initiateChallengeGeneration) + , m_cHeaderKIDs(0) + , m_pdstrHeaderKIDs( nullptr ) + , m_eHeaderVersion( DRM_HEADER_VERSION_UNKNOWN ) + , m_oBatchID( DRM_ID_EMPTY ) + , m_currentDecryptContext( nullptr ) +#ifdef USE_SVP + , m_pSVPContext(nullptr) + , m_rpcID(0) +#endif + , m_fCommit(false) + , m_poAppContext(poAppContext) + , m_decryptInited(false) + , m_bDRMInitializedLocally(false) { - CallbackInfo * callbackInfo = static_cast(data); - - // When a detached thread terminates, its resources are automatically released back to the system - // (i.e. without the need for another thread to join with it). - pthread_detach(pthread_self()); - - stringstream keyMessage; - keyMessage << "{"; - keyMessage << "\"compressed-video\": " << callbackInfo->_compressedVideo << ","; - keyMessage << "\"uncompressed-video\": " << callbackInfo->_uncompressedVideo << ","; - keyMessage << "\"analog-video\": " << callbackInfo->_analogVideo << ","; - keyMessage << "\"compressed-audio\": " << callbackInfo->_compressedAudio << ","; - keyMessage << "\"uncompressed-audio\": " << callbackInfo->_uncompressedAudio; - keyMessage << "}"; - - string keyMessageStr = keyMessage.str(); - const uint8_t * messageBytes = reinterpret_cast(keyMessageStr.c_str()); - - char urlBuffer[64]; - strcpy(urlBuffer, "properties"); - callbackInfo->_callback->OnKeyMessage(messageBytes, keyMessageStr.length() + 1, urlBuffer); - - delete callbackInfo; - return nullptr; -} +#ifdef USE_SVP + gst_svp_ext_get_context(&m_pSVPContext, Client, m_rpcID); +#endif -DRM_RESULT opencdm_output_levels_callback( - const DRM_VOID *outputLevels, - DRM_POLICY_CALLBACK_TYPE callbackType, - const DRM_KID * /*f_pKID */, - const DRM_LID * /*f_pLID*/, - const DRM_VOID *data) -{ - // We only care about the play callback. - if (callbackType != DRM_PLAY_OPL_CALLBACK) - return DRM_SUCCESS; - - const IMediaKeySessionCallback * constSessionCallback = reinterpret_cast(data); - if (constSessionCallback != nullptr) { - CallbackInfo * callbackInfo = new CallbackInfo; - callbackInfo->_callback = const_cast(constSessionCallback); - - // Pull out the protection levels. - const DRM_PLAY_OPL_EX* playLevels = static_cast(outputLevels); - callbackInfo->_compressedVideo = playLevels->minOPL.wCompressedDigitalVideo; - callbackInfo->_uncompressedVideo = playLevels->minOPL.wUncompressedDigitalVideo; - callbackInfo->_analogVideo = playLevels->minOPL.wAnalogVideo; - callbackInfo->_compressedAudio = playLevels->minOPL.wCompressedDigitalAudio; - callbackInfo->_uncompressedAudio = playLevels->minOPL.wUncompressedDigitalAudio; - - // Run on a new thread, so we don't go too deep in the IPC callstack. - pthread_t threadId; - pthread_create(&threadId, nullptr, PlayLevelUpdateCallback, callbackInfo); + mDrmHeader.resize(drmHeaderLength); + memcpy(&mDrmHeader[0], drmHeader, drmHeaderLength); - } - // All done. - return DRM_SUCCESS; + m_eKeyState = KEY_INIT; } uint32_t MediaKeySession::GetSessionIdExt() const @@ -127,276 +72,327 @@ CDMi_RESULT MediaKeySession::SetDrmHeader(const uint8_t drmHeader[], uint32_t dr return CDMi_SUCCESS; } -CDMi_RESULT MediaKeySession::StoreLicenseData(const uint8_t licenseData[], uint32_t licenseDataSize, uint8_t * secureStopId) +CDMi_RESULT MediaKeySession::BindKeyNow(DECRYPT_CONTEXT decryptContext) { - // open scope for DRM_APP_CONTEXT mutex - SafeCriticalSection systemLock(drmAppContextMutex_); - - DRM_RESULT err; - + DRM_VOID * pvData = nullptr; + DRMPFNPOLICYCALLBACK pfnOPLCallback = nullptr; + DECRYPT_CONTEXT tmpDecryptContext; + DRM_RESULT dr; + + if ( CDMi_SUCCESS != SetKeyIdProperty( decryptContext->keyId ) ) + { + return CDMi_S_FALSE; + } + + dr = ReaderBind( + RIGHTS, + sizeof(RIGHTS) / sizeof(DRM_CONST_STRING*), + _PolicyCallback, + pvData, + &(decryptContext->oDrmDecryptContext) ); + + if ( DRM_FAILED( dr ) ){ + fprintf(stderr, "[%s:%d] ReaderBind failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + return CDMi_S_FALSE; + } else { + dr = Drm_Reader_Commit(m_poAppContext, _PolicyCallback, pvData); + if (DRM_FAILED(dr)) + { + fprintf(stderr, "[%s:%d] Drm_Reader_Commit failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + return CDMi_S_FALSE; + } + } + if ( nullptr == ( tmpDecryptContext = GetDecryptCtx( decryptContext->keyId ) ) ){ + m_DecryptContextVector.push_back(decryptContext); + } + return CDMi_SUCCESS; +} - mLicenseResponse->clear(); +CDMi_RESULT MediaKeySession::BindKey(KeyId keyId) +{ + DECRYPT_CONTEXT decryptContext; + decryptContext = NEW_DECRYPT_CONTEXT(); + decryptContext->keyId = keyId; + + auto it = mBindMap.find(keyId); + if (it != mBindMap.end()) + { + it->second = decryptContext; + return CDMi_SUCCESS; + } + else + { + BindKeyNow(decryptContext); + mBindMap.insert(std::make_pair(decryptContext->keyId, std::shared_ptr<__DECRYPT_CONTEXT>())); + } + return CDMi_SUCCESS; +} - err = Drm_LicenseAcq_ProcessResponse( - m_poAppContext, - DRM_PROCESS_LIC_RESPONSE_SIGNATURE_NOT_REQUIRED, - &licenseData[0], - (DRM_DWORD)licenseDataSize, - mLicenseResponse->get()); +CDMi_RESULT MediaKeySession::StoreLicenseData(const uint8_t f_rgbLicenseData[], uint32_t f_cbLicenseDataSize, uint8_t * f_pSecureStopId) +{ + DRM_RESULT err = DRM_SUCCESS; + DRM_LICENSE_RESPONSE oLicenseResponse = {eUnknownProtocol, 0}; + DRM_LICENSE_ACK *pLicenseAck = nullptr; + SafeCriticalSection systemLock(drmAppContextMutex_); - if (DRM_SUCCEEDED(err)) { - m_eKeyState = KEY_READY; + if ( f_cbLicenseDataSize == 0 ) + { + fprintf(stderr, "[%s:%d] f_cbLicenseDataSize should not be 0",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; } - if (m_piCallback && DRM_SUCCEEDED(err)) { - for (uint32_t i = 0; i < mLicenseResponse->get()->m_cAcks; ++i) { - if (DRM_SUCCEEDED(mLicenseResponse->get()->m_rgoAcks[i].m_dwResult)) { - m_piCallback->OnKeyStatusUpdate("KeyUsable", mLicenseResponse->get()->m_rgoAcks[i].m_oKID.rgb, DRM_ID_SIZE); - } - } - m_piCallback->OnKeyStatusesUpdated(); - } + memset( f_pSecureStopId, 0, DRM_ID_SIZE ); + + KeyId tmpBatchKeyId(&m_oBatchID.rgb[0],KeyId::KEYID_ORDER_GUID_LE); -// First, check the return code of Drm_LicenseAcq_ProcessResponse() - if (err == DRM_E_LICACQ_TOO_MANY_LICENSES) { - // This means the server response contained more licenses than - // DRM_MAX_LICENSE_ACK (usually 20). Should allocate space and retry. - // FIXME NRDLIB-4481: This will need to be implemented when we start - // using batch license requests. - fprintf(stderr, "Drm_LicenseAcq_ProcessResponse too many licenses in response."); + if ( tmpBatchKeyId == KeyId::EmptyKeyId ){ + fprintf(stderr, "[%s:%d] Invalid batchId/SecureStopId: %s",__FUNCTION__,__LINE__,tmpBatchKeyId.B64Str()); return CDMi_S_FALSE; } - else if (DRM_FAILED(err)) { - fprintf(stderr, "Drm_LicenseAcq_ProcessResponse failed (error: 0x%08X)", static_cast(err)); + + DRM_BYTE *pbLicenseData = ( DRM_BYTE * )&f_rgbLicenseData[ 0 ]; + + err = ProcessLicenseResponse( + DRM_PROCESS_LIC_RESPONSE_NO_FLAGS, + pbLicenseData, + f_cbLicenseDataSize, + &oLicenseResponse ); + + if (DRM_FAILED(err)) { + SAFE_OEM_FREE( oLicenseResponse.m_pAcks ); + fprintf(stderr, "[%s:%d] ProcessLicenseResponse failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); return CDMi_S_FALSE; } - // Next, examine the returned drmLicenseResponse struct for a top-level error. - if (DRM_FAILED(mLicenseResponse->get()->m_dwResult)) { - fprintf(stderr, "Error in DRM_LICENSE_RESPONSE"); + // NOTE: Netflix, for persistent licenses, the response batchId will be empty + // and this check will have to be removed, but for non-persistent, any empty batchId + // is an issue. The member m_oBatchId is generated in the GenerateChallenge + // call and that should be used for secureStopId and should match the returned + // batchId in the LICENSE_RESPONSE struct for in-memory licenses. + if ( ::memcmp( m_oBatchID.rgb, &oLicenseResponse.m_idSession.rgb[0], DRM_ID_SIZE ) != 0 ) + { + KeyId mBatch(&m_oBatchID.rgb[0],KeyId::KEYID_ORDER_GUID_LE); + fprintf(stderr, "[%s:%d] Response batchID does not equal batchID %s from challenge.",__FUNCTION__,__LINE__,mBatch.B64Str()); + SAFE_OEM_FREE( oLicenseResponse.m_pAcks ); return CDMi_S_FALSE; } - // Finally, ensure that each license in the response was processed - // successfully. - const DRM_DWORD nLicenses = mLicenseResponse->get()->m_cAcks; - for (uint32_t i=0; i < nLicenses; ++i) - { - fprintf(stderr, "Checking license %d", i); - if (DRM_FAILED(mLicenseResponse->get()->m_rgoAcks[i].m_dwResult)) { - // Special handling for DRM_E_DST_STORE_FULL. If this error is - // detected for any license, reset the DRM appcontext and return error. - if (mLicenseResponse->get()->m_rgoAcks[i].m_dwResult == DRM_E_DST_STORE_FULL) { - fprintf(stderr, "Found DRM_E_DST_STORE_FULL error in license %d, reinitializing!", i); - - err = Drm_Reinitialize(m_poAppContext); - if (DRM_FAILED(err)) + for ( DRM_DWORD i = 0; i < oLicenseResponse.m_cAcks; ++i) { + pLicenseAck = oLicenseResponse.m_pAcks != nullptr + ? &oLicenseResponse.m_pAcks[ i ] : &oLicenseResponse.m_rgoAcks[ i ]; + + KeyId keyId(pLicenseAck->m_oKID.rgb,KeyId::KEYID_ORDER_GUID_LE); + + DRM_RESULT dr = pLicenseAck->m_dwResult; + + if (DRM_SUCCEEDED( dr )) { + if ( m_piCallback != nullptr ){ + if (CDMi_SUCCESS !=BindKey(keyId)) { - fprintf(stderr, "Error: Drm_Reinitialize returned (error: 0x%08X)", static_cast(err)); - return CDMi_S_FALSE; + fprintf(stderr, "[%s:%d] BindKey() failed for keyId %s",__FUNCTION__,__LINE__,printGuid(keyId)); } - - } - else { - fprintf(stderr, "Error 0x%08lX found in license %d", (unsigned long)mLicenseResponse->get()->m_rgoAcks[i].m_dwResult, i); + if ( keyId.getKeyIdOrder() == KeyId::KEYID_ORDER_GUID_LE ) + keyId.ToggleFormat(); + m_piCallback->OnKeyStatusUpdate("KeyUsable", keyId.getmBytes(), DRM_ID_SIZE); } - return CDMi_S_FALSE; } + else + { + fprintf(stderr, "[%s:%d] Error processing license %s, 0x%X - %s",__FUNCTION__,__LINE__,printGuid(keyId),dr,DRM_ERR_NAME(dr)); + } + } + if ( m_piCallback != nullptr ) + m_piCallback->OnKeyStatusesUpdated(); + + ::memcpy( f_pSecureStopId, &m_oBatchID.rgb[ 0 ], DRM_ID_SIZE ); + + SAFE_OEM_FREE( oLicenseResponse.m_pAcks ); + + return CDMi_SUCCESS; +} + +CDMi_RESULT MediaKeySession::SelectKeyId( const uint8_t f_keyLength, const uint8_t f_keyId[] ) +{ + SafeCriticalSection systemLock(drmAppContextMutex_); + DRM_RESULT err; + DRMPFNPOLICYCALLBACK pfnOPLCallback = nullptr; + DRM_VOID * pvData = nullptr; + + pfnOPLCallback = _PolicyCallback; + + if ( f_keyId == nullptr || f_keyLength != DRM_ID_SIZE ) + { + fprintf(stderr, "[%s:%d] Bad value for keyId arg ",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; + } + + KeyId keyId(&f_keyId[0],KeyId::KEYID_ORDER_UUID_BE); + std::string keyIdHex(keyId.HexStr()); + + if ( nullptr != ( m_currentDecryptContext = GetDecryptCtx( keyId ) ) ){ + return CDMi_SUCCESS; } - // === Extract various ID's from drmLicenseResponse - // - // There are 3 ID's in the processed license response we are interested in: - // BID - License batch ID. A GUID that uniquely identifies a batch of - // licenses that were processed in one challenge/response transaction. - // The BID is a nonce unique to the transaction. If the transaction - // contains a single license, this is identical to the license nonce. - // The secure stop ID is set to the BID value. - // KID - Key ID. A GUID that uniquely identifies the media content key. This - // is the primary index for items in the license store. There can be - // multiple licenses with the same KID. - // LID - License ID. A GUID that uniquely identifies a license. This is the - // secondary index for items in the license store. - // When there are multiple licenses in the server response as in the PRK - // case, there are correspondingly multiple KID/LID entries in the processed - // response. There is always only a single BID per server response. - - // BID - mBatchId = mLicenseResponse->get()->m_oBatchID; - PrintBase64(sizeof(mBatchId.rgb), mBatchId.rgb, "BatchId/SecureStopId"); - - // Microsoft says that a batch ID of all zeros indicates some sort of error - // for in-memory licenses. Hopefully this error was already caught above. - const uint8_t zeros[sizeof(mBatchId.rgb)] = { 0 }; - if(memcmp(mBatchId.rgb, zeros, sizeof(mBatchId.rgb)) == 0){ - fprintf(stderr, "No batch ID in processed response"); + if ( CDMi_SUCCESS != SetKeyIdProperty( keyId ) ) + { + fprintf(stderr, "[%s:%d] SetKeyIdProperty failed",__FUNCTION__,__LINE__); return CDMi_S_FALSE; } - // We take the batch ID as the secure stop ID - memcpy(secureStopId, mBatchId.rgb, sizeof(mBatchId.rgb)); - // KID and LID - fprintf(stderr, "Found %d license%s in server response for :", nLicenses, (nLicenses > 1) ? "s" : ""); - for (uint32_t i=0; i < nLicenses; ++i) + CDMi_RESULT result = CDMi_SUCCESS; + + DECRYPT_CONTEXT decryptContext = NEW_DECRYPT_CONTEXT(); + err = ReaderBind( + RIGHTS, + sizeof(RIGHTS) / sizeof(DRM_CONST_STRING*), + pfnOPLCallback, + pvData, + &(decryptContext->oDrmDecryptContext ) ); + + if (DRM_FAILED(err)) { - const DRM_LICENSE_ACK * const licAck = &mLicenseResponse->get()->m_rgoAcks[i]; - fprintf(stderr, "KID/LID[%d]:", i); - PrintBase64(sizeof(licAck->m_oLID.rgb), licAck->m_oLID.rgb, "LID"); - PrintBase64(sizeof(licAck->m_oKID.rgb), licAck->m_oKID.rgb, "KID"); + fprintf(stderr, "[%s:%d] ReaderBind failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + result = CDMi_S_FALSE; + } else { + err = Drm_Reader_Commit(m_poAppContext, pfnOPLCallback, pvData); + if (DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] Drm_Reader_Commit failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + result = CDMi_S_FALSE; + } } - // All done. - return CDMi_SUCCESS; + if (result == CDMi_SUCCESS) { + m_fCommit = TRUE; + m_decryptInited = true; + decryptContext->keyId = keyId; + m_DecryptContextVector.push_back(decryptContext); + m_currentDecryptContext = decryptContext; + } + return result; } -CDMi_RESULT MediaKeySession::GetChallengeDataExt(uint8_t * challenge, uint32_t & challengeSize, uint32_t /* isLDL */) +CDMi_RESULT MediaKeySession::GetChallengeDataExt(uint8_t * f_pChallenge, uint32_t & f_ChallengeSize, uint32_t f_isLDL) { DRM_RESULT err; + DRM_CHAR *pchCustomData = nullptr; + DRM_DWORD cchCustomData = 0; + + UNREFERENCED_PARAMETER( f_isLDL ); SafeCriticalSection systemLock(drmAppContextMutex_); - // sanity check for drm header if (mDrmHeader.size() == 0) { - fprintf(stderr, "Error: No valid DRM header\n"); + fprintf(stderr, "[%s:%d] No valid DRM header",__FUNCTION__,__LINE__); return CDMi_S_FALSE; } - // Seems like we no longer have to worry about invalid app context, make sure with this ASSERT. ASSERT(m_poAppContext != nullptr); - /* - * Set the drm context's drm header property to the systemSpecificData. - */ err = Drm_Content_SetProperty(m_poAppContext, DRM_CSP_AUTODETECT_HEADER, &mDrmHeader[0], mDrmHeader.size()); if (DRM_FAILED(err)) { - fprintf(stderr, "Error: Drm_Content_SetProperty returned 0x%lX\n", (long)err); + fprintf(stderr, "[%s:%d] Drm_Content_SetProperty failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); return CDMi_S_FALSE; } - // PlayReady doesn't like valid pointer + size 0 - DRM_BYTE* passedChallenge = static_cast(challenge); - if (challengeSize == 0) { - passedChallenge = nullptr; + DRM_BYTE* pbPassedChallenge = static_cast(f_pChallenge); + if (f_ChallengeSize == 0) { + pbPassedChallenge = nullptr; } - - // Find the size of the challenge. err = Drm_LicenseAcq_GenerateChallenge(m_poAppContext, - RIGHTS, - sizeof(RIGHTS) / sizeof(DRM_CONST_STRING*), - nullptr, - nullptr, - 0, - nullptr, - nullptr, - nullptr, - nullptr, - passedChallenge, - &challengeSize, - nullptr); - - if ((err != DRM_E_BUFFERTOOSMALL) && (DRM_FAILED(err))) + RIGHTS, + sizeof(RIGHTS) / sizeof(DRM_CONST_STRING*), + nullptr, // domain id + pchCustomData, // custom data + cchCustomData, // custom data size + nullptr, // silent URL + 0, // silent URL size + nullptr, // non-silent URL + 0, // non-silent URL size + pbPassedChallenge, + &f_ChallengeSize, + &m_oBatchID ); + + + if ( DRM_FAILED( err ) ) { - fprintf(stderr, "Error: Drm_LicenseAcq_GenerateChallenge returned 0x%lX\n", (long)err); - return CDMi_S_FALSE; + if (err == DRM_E_BUFFERTOOSMALL) { + return CDMi_OUT_OF_MEMORY ; + } + else + { + fprintf(stderr, "[%s:%d] Drm_LicenseAcq_GenerateChallenge failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + return CDMi_S_FALSE; + } } - if ((passedChallenge != nullptr) && (err == DRM_E_BUFFERTOOSMALL)){ - fprintf(stderr, "Error: Drm_LicenseAcq_GenerateChallenge (error: 0x%08X)", static_cast(err)); - return CDMi_OUT_OF_MEMORY ; - } + m_eKeyState = KEY_PENDING; return CDMi_SUCCESS; } CDMi_RESULT MediaKeySession::CancelChallengeDataExt() { - return CDMi_SUCCESS; + return CDMi_S_FALSE; } -CDMi_RESULT MediaKeySession::SelectKeyId(const uint8_t /* keyLength */, const uint8_t[] /* keyId */) +CDMi_RESULT MediaKeySession::Unbind(KeyId keyId) +{ + auto it = mBindMap.find(keyId); + if (it == mBindMap.end()) + { + fprintf(stderr, "[%s:%d] failed to find binding lock with key ID",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; + } + + if (it->second.get() == nullptr) + { + mBindMap.erase(it); + return CDMi_SUCCESS; + } + + if (it->second->keyId != keyId) + { + ASSERT(it->second->keyId == keyId); + return CDMi_S_FALSE; + } + BindKeyNow(it->second); + it->second.reset(); + mBindMap.erase(it); + return CDMi_SUCCESS; +} + +CDMi_RESULT MediaKeySession::CleanDecryptContext() { - // open scope for DRM_APP_CONTEXT mutex SafeCriticalSection systemLock(drmAppContextMutex_); - DRM_RESULT err; - CDMi_RESULT result = CDMi_SUCCESS; ASSERT(m_poAppContext != nullptr); - err = Drm_Content_SetProperty(m_poAppContext, - DRM_CSP_AUTODETECT_HEADER, - &mDrmHeader[0], - mDrmHeader.size()); - if (DRM_FAILED(err)) + for (DECRYPT_CONTEXT &ctx : m_DecryptContextVector) { - fprintf(stderr, "Error: Drm_Content_SetProperty returned 0x%lX\n", (long)err); - return CDMi_S_FALSE; - } - if (m_decryptInited) { - return CDMi_SUCCESS; + Unbind(ctx->keyId); } - m_oDecryptContext = new DRM_DECRYPT_CONTEXT; - //Create a decrypt context and bind it with the drm context. - memset(m_oDecryptContext, 0, sizeof(DRM_DECRYPT_CONTEXT)); - - err = Drm_Reader_Bind( - m_poAppContext, - RIGHTS, - sizeof(RIGHTS) / sizeof(DRM_CONST_STRING*), - &opencdm_output_levels_callback, - m_piCallback, - m_oDecryptContext); + CloseDecryptContexts(); + if (m_poAppContext && !m_fCommit) + { + DRM_RESULT err = Drm_Reader_Commit(m_poAppContext, nullptr, nullptr); if (DRM_FAILED(err)) { - fprintf(stderr, "Error: Drm_Reader_Bind_Netflix returned 0x%lX\n", (long)err); - result = CDMi_S_FALSE; - } else { - - err = Drm_Reader_Commit(m_poAppContext, &opencdm_output_levels_callback, m_piCallback); - if (DRM_FAILED(err)) - { - fprintf(stderr, "Error: Drm_Reader_Commit returned 0x%lX\n", (long)err); - result = CDMi_S_FALSE; - } + fprintf(stderr, "[%s:%d] Drm_Reader_Commit failed. 0x%X - %s",__FUNCTION__,__LINE__,static_cast(err),DRM_ERR_NAME(err)); } - - if (result == CDMi_SUCCESS) { - m_fCommit = TRUE; - m_decryptInited = true; - m_eKeyState = KEY_READY; - } - else { - m_eKeyState = KEY_ERROR; } - return result; -} - -CDMi_RESULT MediaKeySession::CleanDecryptContext() -{ - // open scope for DRM_APP_CONTEXT mutex - SafeCriticalSection systemLock(drmAppContextMutex_); - fprintf(stderr, "MediaKeySession::CleanDecryptContext\n"); - CDMi_RESULT result = CDMi_SUCCESS; - - // Seems like we no longer have to worry about invalid app context, make sure with this ASSERT. - ASSERT(m_poAppContext != nullptr); - - if (m_oDecryptContext != nullptr) { - fprintf(stderr, "Closing active decrypt context"); - Drm_Reader_Close(m_oDecryptContext); - delete m_oDecryptContext; - m_oDecryptContext = nullptr; - } - - return result; + m_fCommit = FALSE; + m_decryptInited = false; + return CDMi_SUCCESS; } - } + diff --git a/MediaSystem.cpp b/MediaSystem.cpp index e2e24fd..28237e2 100644 --- a/MediaSystem.cpp +++ b/MediaSystem.cpp @@ -14,78 +14,52 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "MediaSession.h" #include #include #include #include +#include +#include +#include +#include +#include -#include "MediaSession.h" -#include -#include -#include +#include -// has its own TRACING mechanism. We do not want to use those, undefine it here to avoid a warning. -// with the TRACE macro of the PLAYREADY software. -#undef TRACE +#define CLEAN_ON_INIT 1 -using namespace std; -using namespace Thunder; -using SafeCriticalSection = Core::SafeSyncType; +#define DEVICESTORE_DIGEST_BYTES_SIZE OEM_SHA256_DIGEST_SIZE_IN_BYTES -// Each challenge saves a nonce to the PlayReady3 nonce store, and each license -// bind removes a nonce. The nonce store is also a FIFO, with the oldest nonce -// rolling off if the store is full when a new challenge is generated. This can -// be a problem if the client generates but does not process a number of licenses -// greater than the nonce fifo. So NONCE_STORE_SIZE is reported to the client -// via the getLdlSessionLimit() API. -const uint32_t NONCE_STORE_SIZE = 100; +#define ErrCheckCert() do { \ + if ( m_pbPublisherCert == NULL || m_cbPublisherCert == 0 ) { \ + fprintf(stderr, "SecureStop publisher certificate is not set."); \ + return CDMi_S_FALSE; \ + } \ + }while( 0 ) + +using namespace std; +/* TO DO: temp fix to resolve build error */ +#ifndef ENABLE_AMBIGUOUS_FIX + extern DRM_CONST_STRING g_dstrDrmPath; +#endif +DRM_CONST_STRING g_dstrCDMDrmStoreName; -Core::CriticalSection drmAppContextMutex_; +WPEFramework::Core::CriticalSection drmAppContextMutex_; static DRM_WCHAR* createDrmWchar(std::string const& s) { DRM_WCHAR* w = new DRM_WCHAR[s.length() + 1]; for (size_t i = 0; i < s.length(); ++i) - w[i] = DRM_ONE_WCHAR(s[i], '\0'); - w[s.length()] = DRM_ONE_WCHAR('\0', '\0'); + w[i] = ONE_WCHAR(s[i], '\0'); + w[s.length()] = ONE_WCHAR('\0', '\0'); return w; } -bool calcFileSha256 (const std::string& filePath, uint8_t hash[], uint32_t hashLength ) -{ - bool result(false); - - ASSERT(filePath.empty() == false); - - Core::DataElementFile dataBuffer(filePath, Core::File::USER_READ); - - if ( dataBuffer.IsValid() == false ) { - fprintf(stderr,"Failed to open %s", filePath.c_str()); - } else { - Thunder::Crypto::SHA256 calculator; - ASSERT(hashLength == calculator.Length); - - if (hashLength == calculator.Length) { - calculator.Input(dataBuffer.Buffer(), dataBuffer.Size()); - - const uint8_t* fileHash = calculator.Result(); - - ::memcpy(hash, fileHash, calculator.Length); - - result = true; - } else { - fprintf(stderr,"Output hash buffer has a incorrect size(%d), need %d bytes", hashLength, calculator.Length); - } - } - - return result; -} - -static void PackedCharsToNative(DRM_CHAR *f_pPackedString, DRM_DWORD f_cch) { +static void PackedCharsToNativeImpl(DRM_CHAR *f_pPackedString, DRM_DWORD f_cch) { DRM_DWORD ich = 0; - if( f_pPackedString == nullptr || f_cch == 0 ) { @@ -97,107 +71,203 @@ static void PackedCharsToNative(DRM_CHAR *f_pPackedString, DRM_DWORD f_cch) { } } +std::string GetDrmStorePath() +{ + const uint32_t MAXLEN = 256; + char pathStr[MAXLEN]; + if (g_dstrCDMDrmStoreName.cchString >= MAXLEN) + return ""; + DRM_UTL_DemoteUNICODEtoASCII(g_dstrCDMDrmStoreName.pwszString, + pathStr, MAXLEN); + ((DRM_BYTE*)pathStr)[g_dstrCDMDrmStoreName.cchString] = 0; + PackedCharsToNativeImpl(pathStr, g_dstrCDMDrmStoreName.cchString + 1); + + return string(pathStr); +} + namespace CDMi { class PlayReady : public IMediaKeys, public IMediaKeysExt { private: - PlayReady (const PlayReady&) = delete; - PlayReady& operator= (const PlayReady&) = delete; + class Config : public WPEFramework::Core::JSON::Container { + private: + Config& operator= (const Config&); - class Config : public Core::JSON::Container { public: - Config(const Config&) = delete; - Config& operator=(const Config&) = delete; - Config() - : Core::JSON::Container() - , MeteringCertificate() - , CertificateLabel() - { - Add(_T("metering"), &MeteringCertificate); - Add(_T("certificatelabel"), &CertificateLabel); + Config () + : ReadDir() + , StoreLocation() { + Add("read-dir", &ReadDir); + Add("store-location", &StoreLocation); + Add("home-path", &HomePath); } - ~Config() - { + Config (const Config& copy) + : ReadDir(copy.ReadDir) + , StoreLocation(copy.StoreLocation) + , HomePath(copy.HomePath) { + Add("read-dir", &ReadDir); + Add("store-location", &StoreLocation); + Add("home-path", &HomePath); + } + virtual ~Config() { } public: - Core::JSON::String MeteringCertificate; - Core::JSON::String CertificateLabel; + WPEFramework::Core::JSON::String ReadDir; + WPEFramework::Core::JSON::String StoreLocation; + WPEFramework::Core::JSON::String HomePath; + + CDMi_RESULT SetSecureStopPublisherCert( const DRM_BYTE*, DRM_DWORD ); }; +private: + PlayReady (const PlayReady&) = delete; + PlayReady& operator= (const PlayReady&) = delete; + + DRM_RESULT CleanLicenseStore() + { + return Drm_StoreMgmt_CleanupStore(m_poAppContext.get(), + DRM_STORE_CLEANUP_DELETE_EXPIRED_LICENSES | + DRM_STORE_CLEANUP_DELETE_REMOVAL_DATE_LICENSES, + nullptr, 0, nullptr); + } + public: + PlayReady() : - m_poAppContext(nullptr) - , m_meteringCertificate(nullptr) - , m_meteringCertificateSize(0) { + m_poAppContext(nullptr) { } ~PlayReady(void) { - if (m_poAppContext) - Drm_Uninitialize(m_poAppContext.get()); - - if (m_meteringCertificate != nullptr) { - delete [] m_meteringCertificate; - m_meteringCertificate = nullptr; - } + SAFE_OEM_FREE( m_pbPublisherCert ); } CDMi_RESULT CreateMediaKeySession( const std::string & keySystem, - VARIABLE_IS_NOT_USED int32_t licenseType, - VARIABLE_IS_NOT_USED const char *f_pwszInitDataType, + int32_t licenseType, + const char *f_pwszInitDataType, const uint8_t *f_pbInitData, uint32_t f_cbInitData, const uint8_t *f_pbCDMData, uint32_t f_cbCDMData, IMediaKeySession **f_ppiMediaKeySession) { - bool isNetflixPlayready = (strstr(keySystem.c_str(), "netflix") != nullptr); if (isNetflixPlayready) { - // TODO: why is the order different when dealing with netflix? - *f_ppiMediaKeySession = new CDMi::MediaKeySession(f_pbCDMData, f_cbCDMData, f_pbInitData, f_cbInitData, m_poAppContext.get(), true, !isNetflixPlayready); - } else { - *f_ppiMediaKeySession = new CDMi::MediaKeySession(f_pbInitData, f_cbInitData, f_pbCDMData, f_cbCDMData, m_poAppContext.get(), false, !isNetflixPlayready); - } - + if(!m_isAppCtxInitialized) + { + InitializeAppCtx(); + } + *f_ppiMediaKeySession = new CDMi::MediaKeySession(f_pbInitData, f_cbInitData, m_poAppContext.get(), !isNetflixPlayready); + } else { + *f_ppiMediaKeySession = new CDMi::MediaKeySession(f_pbInitData, f_cbInitData, f_pbCDMData, f_cbCDMData, m_poAppContext.get(), !isNetflixPlayready); + } return CDMi_SUCCESS; } - CDMi_RESULT SetServerCertificate( - VARIABLE_IS_NOT_USED const uint8_t *f_pbServerCertificate, - VARIABLE_IS_NOT_USED uint32_t f_cbServerCertificate) { + CDMi_RESULT SetSecureStopPublisherCert( const DRM_BYTE *f_pbPublisherCert, DRM_DWORD f_cbPublisherCert ) + { + if ( NULL == f_pbPublisherCert ) + { + fprintf(stderr, "[%s:%d] f_pbPublisherCert should not be NULL",__FUNCTION__,__LINE__); + return CDMi_FAIL; + } + if ( 0 == f_cbPublisherCert ) + { + fprintf(stderr, "[%s:%d] f_pbPublisherCert should not be 0",__FUNCTION__,__LINE__); + return CDMi_FAIL; + } + + SAFE_OEM_FREE( m_pbPublisherCert ); + m_cbPublisherCert = 0; - return CDMi_S_FALSE; + m_pbPublisherCert = (DRM_BYTE *)Oem_MemAlloc( f_cbPublisherCert ); + ZEROMEM( m_pbPublisherCert, f_cbPublisherCert ); + memcpy( m_pbPublisherCert, f_pbPublisherCert, f_cbPublisherCert ); + + m_cbPublisherCert = f_cbPublisherCert; + return CDMi_SUCCESS; + } + + CDMi_RESULT SetServerCertificate( const uint8_t *f_pbServerCertificate, uint32_t f_cbServerCertificate) + { + CDMi_RESULT cr = CDMi_SUCCESS; + if ( CDMi_FAILED( ( cr=SetSecureStopPublisherCert( f_pbServerCertificate, f_cbServerCertificate ) ) ) ) + { + fprintf(stderr, "[%s:%d] SetSecureStopPublisherCert failed",__FUNCTION__,__LINE__); + } + return cr; + } + + virtual CDMi_RESULT Metrics(uint32_t length, const uint8_t* buffer) + { + return CDMi_SUCCESS; } CDMi_RESULT DestroyMediaKeySession(IMediaKeySession *f_piMediaKeySession) { - SafeCriticalSection systemLock(drmAppContextMutex_); - VARIABLE_IS_NOT_USED MediaKeySession * mediaKeySession = dynamic_cast(f_piMediaKeySession); - ASSERT((mediaKeySession != nullptr) && "Expected a locally allocated MediaKeySession"); + MediaKeySession * mediaKeySession = dynamic_cast(f_piMediaKeySession); + if ( mediaKeySession != nullptr ) + { + delete f_piMediaKeySession; + } + else + { + fprintf(stderr, "[%s:%d] Expected a locally allocated MediaKeySession",__FUNCTION__,__LINE__); + } + return CDMi_SUCCESS; + } + + uint64_t GetDrmSystemTime() const /* override */ + { + DRM_RESULT dr = DRM_SUCCESS; + DRM_SECURETIME_CLOCK_TYPE eClockType = DRM_SECURETIME_CLOCK_TYPE_INVALID; + DRMFILETIME oftSystemTime = { 0 }; + uint64_t ui64RetTime = (uint64_t) -1; - delete f_piMediaKeySession; - return CDMi_SUCCESS; + SafeCriticalSection lock(drmAppContextMutex_); + + dr = Drm_SecureTime_GetValue( ( DRM_APP_CONTEXT* ) m_poAppContext.get(), + &oftSystemTime, &eClockType ); + + if ( dr != DRM_SUCCESS ) + { + fprintf(stderr, "[%s:%d] Drm_SecureTime_GetValue failed. 0x%X - %s",__FUNCTION__,__LINE__,dr,DRM_ERR_NAME(dr)); + } + else if ( eClockType == DRM_SECURETIME_CLOCK_TYPE_INVALID ) + { + fprintf(stderr, "[%s:%d] Drm_SecureTime_GetValue returned an invalid clock type",__FUNCTION__,__LINE__); + } + else + { + DRM_UINT64 ui64 = DRM_UI64LITERAL(0, 0); + + FILETIME_TO_UI64( oftSystemTime, ui64 ); + ui64RetTime = ( uint64_t ) DRM_UI2I64( ui64 ); + } + + return ui64RetTime; } - //////////////////// - // Ext - //////////////////// - uint64_t GetDrmSystemTime() const override + CDMi_RESULT CreateMediaKeySessionExt( + const std::string& keySystem, + const uint8_t drmHeader[], + uint32_t drmHeaderLength, + IMediaKeySessionExt** session) /* override */ { - fprintf(stderr, "%s:%d: PR is asked for system time\n", __FILE__, __LINE__); + bool isNetflixPlayready = (strstr(keySystem.c_str(), "netflix") != nullptr); + printf("\n [TEL ELXSI] isNetflixPlayready is %d",&isNetflixPlayready); + *session = new CDMi::MediaKeySession(drmHeader, drmHeaderLength, m_poAppContext.get(), !isNetflixPlayready); - // Playready version > 3 supports client time completely within the opaque blobs sent - // between the Playready client and server, so this function should really - // not have to return a real time. However, the Netflix server still needs - // a good client time for legacy reasons. - // In this reference DPI we are cheating my just returning the linux system - // time. A real implementation would be more complicated, perhaps getting - // time from some sort of secure and/or anti-rollback resource. - return static_cast(time(NULL)); + return CDMi_SUCCESS; + } + CDMi_RESULT DestroyMediaKeySessionExt(IMediaKeySession *f_piMediaKeySession) + { + SafeCriticalSection systemLock(drmAppContextMutex_); + delete f_piMediaKeySession; + return CDMi_SUCCESS; } - std::string GetVersionExt() const override + std::string GetVersionExt() const /* override */ { const uint32_t MAXLEN = 64; char versionStr[MAXLEN]; @@ -206,313 +276,544 @@ class PlayReady : public IMediaKeys, public IMediaKeysExt { DRM_UTL_DemoteUNICODEtoASCII(g_dstrReqTagPlayReadyClientVersionData.pwszString, versionStr, MAXLEN); ((DRM_BYTE*)versionStr)[g_dstrReqTagPlayReadyClientVersionData.cchString] = 0; - PackedCharsToNative(versionStr, g_dstrReqTagPlayReadyClientVersionData.cchString + 1); - + PackedCharsToNativeImpl(versionStr, g_dstrReqTagPlayReadyClientVersionData.cchString + 1); return string(versionStr); } - uint32_t GetLdlSessionLimit() const override + uint32_t GetLdlSessionLimit() const /* override */ { - return NONCE_STORE_SIZE; + return ( uint32_t )DRM_MAX_NONCE_COUNT_PER_SESSION; } - bool IsSecureStopEnabled() override + bool IsSecureStopEnabled() /* override */ { - // method not used for Playready version > 3 return true; } - CDMi_RESULT EnableSecureStop(VARIABLE_IS_NOT_USED bool enable) override + CDMi_RESULT EnableSecureStop(bool enable) /* override */ { - // method not used for Playready version > 3 return CDMi_SUCCESS; } - uint32_t ResetSecureStops() override + uint32_t ResetSecureStops() /* override */ { - // method not used for Playready version > 3 return 0; } - - CDMi_RESULT GetSecureStopIds(uint8_t ids[], uint16_t, uint32_t & count) + /*Return a list of the current SecureStop sessions.*/ + CDMi_RESULT GetSecureStopIds(uint8_t ids[], uint16_t idsLength, uint32_t & count) { SafeCriticalSection lock(drmAppContextMutex_); - CDMi_RESULT cr = CDMi_SUCCESS; - DRM_ID *ssSessionIds = nullptr; + CDMi_RESULT cr = CDMi_SUCCESS; + DRM_ID *pidSessions = NULL; + DRM_DWORD cidSessions = 0; + DRM_DWORD cBytesNeeded = 0; - DRM_RESULT dr; - dr = Drm_SecureStop_EnumerateSessions( + ErrCheckCert(); + + DRM_RESULT err = Drm_SecureStop_EnumerateSessions( m_poAppContext.get(), - m_meteringCertificateSize, //playready3MeteringCertSize, - m_meteringCertificate, //playready3MeteringCert, - &count, - &ssSessionIds); + m_cbPublisherCert, + m_pbPublisherCert, + &cidSessions, + &pidSessions ); - if (dr != DRM_SUCCESS && dr != DRM_E_NOMORE) { - fprintf(stderr, "Error in Drm_SecureStop_EnumerateSessions (error: 0x%08X)", static_cast(dr)); - cr = CDMi_S_FALSE; - } else { - for (uint32_t i = 0; i < count; ++i) + if ( err == DRM_SUCCESS ) + { + cBytesNeeded = cidSessions * DRM_ID_SIZE; + if ( idsLength < cBytesNeeded ) { - ASSERT(sizeof(ssSessionIds[i].rgb) == DRM_ID_SIZE); - memcpy(&ids[i * DRM_ID_SIZE], ssSessionIds[i].rgb, DRM_ID_SIZE); + count = cidSessions; + cr = CDMi_S_FALSE; } + else + { + count = cidSessions; - if (count) { - fprintf(stderr, "Found %d pending secure stop%s", count, (count > 1) ? "s" : ""); + for ( DRM_DWORD i = 0; i < count; ++i) + { + memcpy(&ids[i * DRM_ID_SIZE], pidSessions[i].rgb, DRM_ID_SIZE); + } } } + else + { + fprintf(stderr, "[%s:%d] Drm_GetSecureStopIds returned: 0x%lx",__FUNCTION__,__LINE__,(long)err); + cr = CDMi_S_FALSE; + } - SAFE_OEM_FREE(ssSessionIds); - + SAFE_OEM_FREE( pidSessions ); return cr; } + /*Generate a SecureStop challenge to send to the server.*/ CDMi_RESULT GetSecureStop( const uint8_t sessionID[], uint32_t sessionIDLength, - uint8_t * rawData, - uint16_t & rawSize) + uint8_t * f_pbChallenge, + uint16_t & f_cbChallenge) { SafeCriticalSection lock(drmAppContextMutex_); - CDMi_RESULT cr = CDMi_SUCCESS; - // Get the secure stop challenge - DRM_ID ssSessionDrmId; - ASSERT(sizeof(ssSessionDrmId.rgb) >= sessionIDLength); - memcpy(ssSessionDrmId.rgb, sessionID, sessionIDLength); + if ( sessionIDLength < DRM_ID_SIZE ) + { + fprintf(stderr, "[%s:%d] Invalid argument: sessionIDlength %zu expecting %zu",__FUNCTION__,__LINE__,sessionIDLength,DRM_ID_SIZE); + return CDMi_INVALID_ARG; + } + + if (f_cbChallenge == 0 || f_pbChallenge == nullptr ) + { + fprintf(stderr, "[%s:%d] Invalid argument: challenge buffer is null",__FUNCTION__,__LINE__); + return CDMi_INVALID_ARG; + } + + ErrCheckCert(); - DRM_DWORD ssChallengeSize; - DRM_BYTE *ssChallenge; + DRM_BYTE *pbChallenge = NULL; + DRM_DWORD cbChallenge = 0; + DRM_ID SID = DRM_ID_EMPTY; + CDMi_RESULT cr = CDMi_SUCCESS; - DRM_RESULT dr = Drm_SecureStop_GenerateChallenge( + ::memcpy( (void*)SID.rgb, (const void*)&sessionID[0], DRM_ID_SIZE ); + + DRM_RESULT err = Drm_SecureStop_GenerateChallenge( m_poAppContext.get(), - &ssSessionDrmId, - m_meteringCertificateSize, //playready3MeteringCertSize, - m_meteringCertificate, //playready3MeteringCert, - 0, nullptr, // no custom data - &ssChallengeSize, - &ssChallenge); - - if (dr != DRM_SUCCESS) { - fprintf(stderr, "Error in Drm_SecureStop_GenerateChallenge (error: 0x%08X)", static_cast(dr)); + &SID, + m_cbPublisherCert, + m_pbPublisherCert, + 0, + nullptr, + &cbChallenge, + &pbChallenge ); + + if ( err != DRM_SUCCESS ) + { + f_cbChallenge = 0; + fprintf(stderr, "[%s:%d] Drm_SecureStop_GenerateChallenge failed. 0x%X - %s",__FUNCTION__,__LINE__,(long)err,DRM_ERR_NAME(err)); cr = CDMi_S_FALSE; - } else { - if((rawData != nullptr) && (rawSize >= ssChallengeSize)){ - memcpy(rawData, ssChallenge, ssChallengeSize); - } - rawSize = ssChallengeSize; + } + else if ( f_cbChallenge < cbChallenge ) + { + f_cbChallenge = (uint16_t)cbChallenge; + fprintf(stderr, "[%s:%d] SecureStop challenge buffer is too small. %u, need %u",__FUNCTION__,__LINE__,f_cbChallenge,cbChallenge); + cr = CDMi_S_FALSE; + } + else + { + f_cbChallenge = (uint16_t)cbChallenge; + ::memcpy( f_pbChallenge, pbChallenge, f_cbChallenge ); } + SAFE_OEM_FREE( pbChallenge ); + return cr; } + /*Process the response from the SecureStop server.*/ CDMi_RESULT CommitSecureStop( - const uint8_t sessionID[], - uint32_t sessionIDLength, - const uint8_t serverResponse[], - uint32_t serverResponseLength) override + const uint8_t f_sessionID[], + uint32_t f_sessionIDLength, + const uint8_t f_serverResponse[], + uint32_t f_serverResponseLength) /* override */ { SafeCriticalSection lock(drmAppContextMutex_); - CDMi_RESULT cr = CDMi_SUCCESS; - if (sessionIDLength == 0) { - fprintf(stderr, "Error: empty session id"); - cr = CDMi_S_FALSE; - } - if (serverResponseLength == 0) { - cr = CDMi_S_FALSE; + ErrCheckCert(); + + if ( f_sessionIDLength < DRM_ID_SIZE ) + { + fprintf(stderr, "[%s:%d] Invalid argument: sessionIDlength %zu, expecting %zu ",__FUNCTION__,__LINE__,f_sessionIDLength,DRM_ID_SIZE); + return CDMi_INVALID_ARG; } - if (cr == CDMi_SUCCESS){ - DRM_ID sessionDrmId; - ASSERT(sizeof(sessionDrmId.rgb) >= sessionIDLength); - memcpy(sessionDrmId.rgb, sessionID, sessionIDLength); + DRM_ID SID = DRM_ID_EMPTY; + DRM_CHAR *pcchCustomData = NULL; + DRM_DWORD cchCustomData = 0; + CDMi_RESULT cr = CDMi_SUCCESS; - DRM_DWORD customDataSizeBytes = 0; - DRM_CHAR *pCustomData = NULL; + memcpy( (void*)SID.rgb, (const void*)&f_sessionID[0], DRM_ID_SIZE ); - DRM_RESULT dr; - dr = Drm_SecureStop_ProcessResponse( + DRM_RESULT err = Drm_SecureStop_ProcessResponse( m_poAppContext.get(), - &sessionDrmId, - m_meteringCertificateSize, //playready3MeteringCertSize, - m_meteringCertificate, //playready3MeteringCert, - serverResponseLength, - serverResponse, - &customDataSizeBytes, - &pCustomData); - if (dr == DRM_SUCCESS) { - fprintf(stderr, "secure stop commit successful"); - if (pCustomData && customDataSizeBytes) - { - // We currently don't use custom data from the server. Just log here. - std::string customDataStr(pCustomData, customDataSizeBytes); - fprintf(stderr, "custom data = \"%s\"", customDataStr.c_str()); - } - } - else { - fprintf(stderr, "Drm_SecureStop_ProcessResponse returned 0x%lx", static_cast(dr)); - } - - SAFE_OEM_FREE(pCustomData); + &SID, + m_cbPublisherCert, + m_pbPublisherCert, + f_serverResponseLength, + f_serverResponse, + &cchCustomData, + &pcchCustomData); + + if ( err == DRM_E_SECURESTOP_SESSION_NOT_FOUND ) + { + cr = CDMi_S_FALSE; } - + else + { + fprintf(stderr, "[%s:%d] Drm_SecureStop_ProcessResponse failed. 0x%X - %s",__FUNCTION__,__LINE__,(long)err,DRM_ERR_NAME(err)); + cr = CDMi_S_FALSE; + } + SAFE_OEM_FREE( pcchCustomData ); return cr; } - CDMi_RESULT DeleteKeyStore() override + CDMi_RESULT CreateSystemExt() /* override */ { - // There is no keyfile in PlayReady version > 3, so we cannot implement this function. + if (m_poAppContext.get() != nullptr) { + m_poAppContext.reset(); + } + + std::string rdir(m_readDir); + + drmdir_ = createDrmWchar(rdir); + + g_dstrDrmPath.pwszString = drmdir_; + g_dstrDrmPath.cchString = rdir.length(); + + // Store store location + std::string store(m_storeLocation); + + g_dstrCDMDrmStoreName.pwszString = createDrmWchar(store); + g_dstrCDMDrmStoreName.cchString = store.length(); + + // Init revocation buffer. + pbRevocationBuffer_ = new DRM_BYTE[REVOCATION_BUFFER_SIZE]; + return CDMi_SUCCESS; } - CDMi_RESULT DeleteSecureStore() override + /*Initialize the PlayReady application context*/ + CDMi_RESULT InitializeAppCtx() { - SafeCriticalSection lock(drmAppContextMutex_); + DRM_BYTE *appOpaqueBuffer = nullptr; + DRM_VOID *pDrmOemContext = NULL; + bool bIsInitSecureClockNeed = false; - if (remove(m_storeLocation.c_str()) != 0) { - fprintf(stderr, "Error removing DRM store file"); + if (m_poAppContext.get() != nullptr) { + m_poAppContext.reset(); } - return CDMi_SUCCESS; - } + m_poAppContext.reset(new DRM_APP_CONTEXT); - CDMi_RESULT GetKeyStoreHash( - VARIABLE_IS_NOT_USED uint8_t keyStoreHash[], - VARIABLE_IS_NOT_USED uint32_t keyStoreHashLength) override - { + // Init opaque buffer. + appOpaqueBuffer = new DRM_BYTE[MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE]; + + ::memset(m_poAppContext.get(), 0, sizeof(DRM_APP_CONTEXT)); + + svpGetDrmOEMContext(&pDrmOemContext); + DRM_RESULT err = Drm_Initialize(m_poAppContext.get(), pDrmOemContext, + appOpaqueBuffer, + MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE, + &g_dstrCDMDrmStoreName); + + if((err == DRM_E_SECURESTOP_STORE_CORRUPT) || \ + (err == DRM_E_SECURESTORE_CORRUPT) || \ + (err == DRM_E_DST_CORRUPTED)) { + + //if drmstore file is corrupted, remove it and init again, playready will create a new one + remove(GetDrmStorePath().c_str()); + + err = Drm_Initialize(m_poAppContext.get(), pDrmOemContext, + appOpaqueBuffer, + MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE, + &g_dstrCDMDrmStoreName ); + } + + if (DRM_FAILED(err)) { + delete [] appOpaqueBuffer; + m_poAppContext.reset(); + return CDMi_S_FALSE; + } + + ::memset(pbRevocationBuffer_, 0, REVOCATION_BUFFER_SIZE); + err = Drm_Revocation_SetBuffer(m_poAppContext.get(), pbRevocationBuffer_, REVOCATION_BUFFER_SIZE); + if(DRM_FAILED(err)) + { + delete [] appOpaqueBuffer; + m_poAppContext.reset(); + fprintf(stderr, "[%s:%d] Drm_Revocation_SetBuffer failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + return CDMi_S_FALSE; + } + + bIsInitSecureClockNeed = svpIsSecureClockInitNeed(); + + if(bIsInitSecureClockNeed) + { + DRMFILETIME ftSystemTime; /* Initialized by Drm_SecureTime_GetValue */ + DRM_SECURETIME_CLOCK_TYPE eClockType; /* Initialized by Drm_SecureTime_GetValue */ + + CDMi_RESULT cr = CDMi_SUCCESS; + DRM_RESULT dr = DRM_SUCCESS; + + dr = Drm_SecureTime_GetValue( m_poAppContext.get(), &ftSystemTime, &eClockType ); + if (dr == DRM_E_CLK_NOT_SUPPORTED) /* Secure Clock not supported, try the Anti-Rollback Clock */ + { +#if defined DRM_ANTI_ROLLBACK_CLOCK_SUPPORT + DRMSYSTEMTIME systemTime; + struct timeval tv; + struct tm *tm; - // There is no keyfile in PlayReady version > 3, so we cannot implement this function. + printf("Secure Clock not supported, trying the Anti-Rollback Clock..."); + + gettimeofday(&tv, nullptr); + tm = gmtime(&tv.tv_sec); + + systemTime.wYear = tm->tm_year+1900; + systemTime.wMonth = tm->tm_mon+1; + systemTime.wDayOfWeek = tm->tm_wday; + systemTime.wDay = tm->tm_mday; + systemTime.wHour = tm->tm_hour; + systemTime.wMinute = tm->tm_min; + systemTime.wSecond = tm->tm_sec; + systemTime.wMilliseconds = tv.tv_usec/1000; + + + if(Drm_AntiRollBackClock_Init(m_poAppContext.get(), &systemTime) != 0) + { + printf("\n antoFailed to initiize Anti-Rollback Clock, quitting...."); + return CDMi_S_FALSE; + } +#else + printf("Secure Clock and Anti-Rollback Clock is not supported..."); + return CDMi_S_FALSE; +#endif + } + else + { + if (dr != 0) { + printf("\nExpect platform to support Secure Clock or Anti-Rollback Clock. Possible certificate (error 0x%08X)", static_cast(dr)); + return CDMi_S_FALSE; + } + } + } + + if( !svpLoadRevocationList()) + { + return CDMi_S_FALSE; + } + + m_isAppCtxInitialized = true; return CDMi_SUCCESS; } - CDMi_RESULT GetSecureStoreHash( - uint8_t secureStoreHash[], - uint32_t secureStoreHashLength) override + /*Unitialize the playready context and opaque buffer*/ + CDMi_RESULT UninitializeAppCtx() { - SafeCriticalSection lock(drmAppContextMutex_); + DRM_BYTE *pbOldBuf = nullptr; + DRM_DWORD cbOldBuf = 0; + + m_isAppCtxInitialized = false; - if (calcFileSha256(m_storeLocation, secureStoreHash, secureStoreHashLength) == false) + if (m_poAppContext.get() == nullptr) { - fprintf(stderr, "Error: calcFileSha256 failed"); return CDMi_S_FALSE; } + + DRM_RESULT err = Drm_GetOpaqueBuffer( m_poAppContext.get(), &pbOldBuf, &cbOldBuf ); + if(DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] Drm_GetOpaqueBuffer failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + } + + Drm_Uninitialize(m_poAppContext.get()); + m_poAppContext.reset(); + + if ( pbOldBuf ){ + delete [] pbOldBuf; + } + return CDMi_SUCCESS; } - void Initialize(const Thunder::PluginHost::IShell * shell, const std::string& configline) + CDMi_RESULT InitSystemExt() /* override */ { - string persistentPath = shell->PersistentPath(); - string statePath = persistentPath + "/state"; // To store rollback clock state etc - m_readDir = persistentPath + "/playready"; - m_storeLocation = persistentPath + "/playready/storage/drmstore"; - - Config config; - config.FromString(configline); + SafeCriticalSection lock(drmAppContextMutex_); - if (config.MeteringCertificate.IsSet() == true) { - Core::DataElementFile dataBuffer(config.MeteringCertificate.Value(), Core::File::USER_READ | Core::File::GROUP_READ); + DRM_BYTE *appOpaqueBuffer = nullptr; - if(dataBuffer.IsValid() == false) { - TRACE_L1(_T("Failed to open %s"), config.MeteringCertificate.Value().c_str()); - } else { - m_meteringCertificateSize = dataBuffer.Size(); - m_meteringCertificate = new DRM_BYTE[m_meteringCertificateSize]; + DRM_RESULT err = CPRDrmPlatform::DrmPlatformInitialize(); - ::memcpy(m_meteringCertificate, dataBuffer.Buffer(), dataBuffer.Size()); + if(DRM_FAILED(err)) + { + if (m_poAppContext.get() != nullptr) { + m_poAppContext.reset(); } + fprintf(stderr, "[%s:%d] DrmPlatformInitialize failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + return CDMi_S_FALSE; } - if ((config.CertificateLabel.IsSet() == true) && (config.CertificateLabel.Value().empty() == false)) { - Core::SystemInfo::SetEnvironment(_T("PLAYREADY_CERTIFICATE_LABEL"), config.CertificateLabel.Value()); + if (CDMi_SUCCESS != InitializeAppCtx()) + { + fprintf(stderr, "[%s:%d] InitializeAppCtx failed.",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; } - Core::Directory stateDir(statePath.c_str()); - stateDir.Create(); +#ifdef CLEAN_ON_INIT + err = CleanLicenseStore(); + if(DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] CleanLicenseStore failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + } +#endif + return CDMi_SUCCESS; + } - Core::SystemInfo::SetEnvironment(_T("HOME"), statePath); + void Deinitialize(const WPEFramework::PluginHost::IShell * shell) + { + TeardownSystemExt(); + /* We can do SoC specific de-init requirement for Playready */ + svpPlatformUninitializePlayready(); + } - ASSERT(m_poAppContext.get() == nullptr); + CDMi_RESULT TeardownSystemExt() /* override */ + { + SafeCriticalSection lock(drmAppContextMutex_); - std::string rdir(m_readDir); + if(!m_poAppContext.get()) { + fprintf(stderr, "[%s:%d] no app context yet",__FUNCTION__,__LINE__); + return CDMi_S_FALSE; + } - // Create wchar strings from the arguments. - drmdir_ = createDrmWchar(rdir); + DRM_RESULT err = CleanLicenseStore(); + if(DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] CleanLicenseStore failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + } - // Initialize Ocdm directory. - g_dstrDrmPath.pwszString = drmdir_; - g_dstrDrmPath.cchString = rdir.length(); + if (CDMi_SUCCESS != UninitializeAppCtx() ) + { + fprintf(stderr, "[%s:%d] UninitializeAppCtx failed.",__FUNCTION__,__LINE__); + } - // Store store location - std::string store(m_storeLocation); + delete [] pbRevocationBuffer_; - drmStore_.pwszString = createDrmWchar(store); - drmStore_.cchString = store.length(); + delete [] drmdir_; + delete [] g_dstrCDMDrmStoreName.pwszString; - // Init opaque buffer. - appContextOpaqueBuffer_ = new DRM_BYTE[MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE]; + err = CPRDrmPlatform::DrmPlatformUninitialize(); + if(DRM_FAILED(err)) + { + fprintf(stderr, "[%s:%d] DrmPlatformUninitialize failed. 0x%X - %s",__FUNCTION__,__LINE__,err,DRM_ERR_NAME(err)); + return CDMi_S_FALSE; + } - // Init revocation buffer. - pbRevocationBuffer_ = new DRM_BYTE[REVOCATION_BUFFER_SIZE]; + return CDMi_SUCCESS; + } - //return CDMi_SUCCESS; + CDMi_RESULT DeleteKeyStore() /* override */ + { + return CDMi_S_FALSE; + } - // TODO: this is just a move from InitSystemExt + CDMi_RESULT DeleteSecureStore() /* override */ + { SafeCriticalSection lock(drmAppContextMutex_); + struct stat buf; + CDMi_RESULT cr = CDMi_SUCCESS; - DRM_RESULT err; + if (CDMi_SUCCESS != UninitializeAppCtx() ) + { + fprintf(stderr, "[%s:%d] UninitializeAppCtx failed.",__FUNCTION__,__LINE__); + } - // DRM Platform Initialization - err = Drm_Platform_Initialize(nullptr); - if(DRM_FAILED(err)) + if (stat(m_storeLocation.c_str(), &buf) != -1) { - fprintf(stderr, "Error in Drm_Platform_Initialize: 0x%08lX\n", err); - //return CDMi_S_FALSE; - return; + int status = remove(m_storeLocation.c_str()); + if(status == 0) + { + cr = CDMi_SUCCESS; + } + else + { + fprintf(stderr, "[%s:%d] Failed to delete key store",__FUNCTION__,__LINE__); + cr = CDMi_S_FALSE; + } } + else + cr = CDMi_SUCCESS; + + return cr; + } - std::unique_ptr appCtx; - appCtx.reset(new DRM_APP_CONTEXT); + CDMi_RESULT GetKeyStoreHash( + uint8_t keyStoreHash[], + uint32_t keyStoreHashLength) // override + { + return CDMi_S_FALSE; + } - memset(appCtx.get(), 0, sizeof(DRM_APP_CONTEXT)); - err = Drm_Initialize(appCtx.get(), nullptr, - appContextOpaqueBuffer_, - MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE, - &drmStore_); - if(DRM_FAILED(err)) { - fprintf(stderr, "Error in Drm_Initialize: 0x%08lX\n", err); - //return CDMi_S_FALSE; - return; + CDMi_RESULT GetSecureStoreHash( + uint8_t secureStoreHash[], + uint32_t secureStoreHashLength) // override + { + SafeCriticalSection lock(drmAppContextMutex_); + CDMi_RESULT ret = CDMi_SUCCESS; + + FILE* const file = fopen(m_storeLocation.c_str(), "rb"); + if (!file) + return CDMi_S_FALSE; + + SHA256_CTX sha256; + SHA256_Init(&sha256); + const int BUFSIZE = 32768; + std::vector buffer(BUFSIZE, 0); + size_t bytesRead = 0; + while ((bytesRead = fread(&buffer[0], 1, BUFSIZE, file))) { + if (!SHA256_Update(&sha256, &buffer[0], bytesRead)) { + ret = CDMi_S_FALSE; + break; + } } + fclose(file); + SHA256_Final(secureStoreHash, &sha256); - m_poAppContext.swap(appCtx); - err = Drm_Revocation_SetBuffer(m_poAppContext.get(), pbRevocationBuffer_, REVOCATION_BUFFER_SIZE); - if(DRM_FAILED(err)) - { - fprintf(stderr, "Error in Drm_Revocation_SetBuffer: 0x%08lX\n", err); - //return CDMi_S_FALSE; - return; + return ret; + } + + void OnSystemConfigurationAvailable(const std::string& configline) + { + Config config; + config.FromString(configline); + m_readDir = config.ReadDir.Value(); + m_storeLocation = config.StoreLocation.Value(); + + svpGetDrmStoragePath(m_readDir, m_storePath, m_storeLocation); + + WPEFramework::Core::Directory(m_storePath.c_str()).CreatePath(); + WPEFramework::Core::Directory(m_readDir.c_str()).CreatePath(); + + string homePath = config.HomePath.Value(); + if(!homePath.empty()) { + WPEFramework::Core::SystemInfo::SetEnvironment(_T("HOME"), homePath.c_str()); + } else { + fprintf(stderr, "[%s:%d] Warning : could not set HOME variable. SecureStop functionality may not work!",__FUNCTION__,__LINE__); } - //return CDMi_SUCCESS; + CreateSystemExt(); + + InitSystemExt(); + } + + void Initialize(const WPEFramework::PluginHost::IShell * service, const std::string& configline) + { + /* We can do SoC specific requirement for Playready */ + svpPlatformInitializePlayready(); + OnSystemConfigurationAvailable(configline); } private: DRM_WCHAR* drmdir_; - DRM_CONST_STRING drmStore_; - DRM_BYTE *appContextOpaqueBuffer_ = nullptr; DRM_BYTE *pbRevocationBuffer_ = nullptr; - std::unique_ptr m_poAppContext; + std::shared_ptr m_poAppContext; string m_readDir; string m_storeLocation; + string m_storePath; - DRM_BYTE* m_meteringCertificate; - uint32_t m_meteringCertificateSize; + DRM_BYTE *m_pbPublisherCert = nullptr; + DRM_DWORD m_cbPublisherCert = 0; + bool m_isAppCtxInitialized = false; }; static SystemFactoryType g_instance({"video/x-h264", "audio/mpeg"}); diff --git a/cmake/FindPlayReady.cmake b/cmake/FindPlayReady.cmake index b577318..11f8bf8 100644 --- a/cmake/FindPlayReady.cmake +++ b/cmake/FindPlayReady.cmake @@ -67,7 +67,7 @@ if(PLAYREADY_LIBRARY AND NOT TARGET PlayReady::PlayReady) add_library(PlayReady::PlayReady UNKNOWN IMPORTED) set_target_properties(PlayReady::PlayReady PROPERTIES IMPORTED_LOCATION "${PLAYREADY_LIBRARY}" - INTERFACE_LINK_LIBRARIES "${PLAYREADY_LIBRARY}" + INTERFACE_LINK_LIBRARIES "${PLAYREADY_LIBRARIES}" INTERFACE_LINK_DIRECTORIES "${PLAYREADY_LIBRARY_DIRS}" INTERFACE_COMPILE_OPTIONS "${PLAYREADY_FLAGS}" INTERFACE_INCLUDE_DIRECTORIES "${PLAYREADY_INCLUDE_DIRS}"