/* * (C) 2003-2006 Gabest * (C) 2006-2013 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "stdafx.h" #include #include #include "BaseClasses/streams.h" #include #include #include #include "DeCSSInputPin.h" #include "../DSUtil/DSUtil.h" #include "CSSauth.h" #include "CSSscramble.h" #include #include "moreuuids.h" // // CDeCSSInputPin // CDeCSSInputPin::CDeCSSInputPin(TCHAR* pObjectName, CTransformFilter* pFilter, HRESULT* phr, LPWSTR pName) : CTransformInputPin(pObjectName, pFilter, phr, pName) , m_varient(-1) { ZeroMemory(m_Challenge, sizeof(m_Challenge)); ZeroMemory(m_KeyCheck, sizeof(m_KeyCheck)); ZeroMemory(m_Key, sizeof(m_Key)); ZeroMemory(m_DiscKey, sizeof(m_DiscKey)); ZeroMemory(m_TitleKey, sizeof(m_TitleKey)); } STDMETHODIMP CDeCSSInputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv) { CheckPointer(ppv, E_POINTER); return QI(IKsPropertySet) __super::NonDelegatingQueryInterface(riid, ppv); } // IMemInputPin STDMETHODIMP CDeCSSInputPin::Receive(IMediaSample* pSample) { long len = pSample->GetActualDataLength(); BYTE* p = nullptr; if (SUCCEEDED(pSample->GetPointer(&p)) && len > 0) { if (m_mt.majortype == MEDIATYPE_DVD_ENCRYPTED_PACK && len == 2048 && (p[0x14] & 0x30)) { CSSdescramble(p, m_TitleKey); p[0x14] &= ~0x30; if (CComQIPtr pMS2 = pSample) { AM_SAMPLE2_PROPERTIES props; ZeroMemory(&props, sizeof(props)); if (SUCCEEDED(pMS2->GetProperties(sizeof(props), (BYTE*)&props)) && (props.dwTypeSpecificFlags & AM_UseNewCSSKey)) { props.dwTypeSpecificFlags &= ~AM_UseNewCSSKey; pMS2->SetProperties(sizeof(props), (BYTE*)&props); } } } } HRESULT hr = Transform(pSample); return hr == S_OK ? __super::Receive(pSample) : hr == S_FALSE ? S_OK : hr; } void CDeCSSInputPin::StripPacket(BYTE*& p, long& len) { GUID majortype = m_mt.majortype; if (majortype == MEDIATYPE_MPEG2_PACK || majortype == MEDIATYPE_DVD_ENCRYPTED_PACK) { if (len > 0 && *(DWORD*)p == 0xba010000) { // MEDIATYPE_*_PACK len -= 14; p += 14; if (int stuffing = (p[-1] & 7)) { len -= stuffing; p += stuffing; } majortype = MEDIATYPE_MPEG2_PES; } } if (majortype == MEDIATYPE_MPEG2_PES) { if (len > 0 && *(DWORD*)p == 0xbb010000) { len -= 4; p += 4; int hdrlen = ((p[0] << 8) | p[1]) + 2; len -= hdrlen; p += hdrlen; } if (len > 0 && ((*(DWORD*)p & 0xf0ffffff) == 0xe0010000 || (*(DWORD*)p & 0xe0ffffff) == 0xc0010000 || (*(DWORD*)p & 0xbdffffff) == 0xbd010000)) { // PES bool ps1 = (*(DWORD*)p & 0xbdffffff) == 0xbd010000; len -= 4; p += 4; long expected = ((p[0] << 8) | p[1]); len -= 2; p += 2; BYTE* p0 = p; for (int i = 0; i < 16 && *p == 0xff; i++, len--, p++) { ; } if ((*p & 0xc0) == 0x80) { // mpeg2 len -= 2; p += 2; len -= *p + 1; p += *p + 1; } else { // mpeg1 if ((*p & 0xc0) == 0x40) { len -= 2; p += 2; } if ((*p & 0x30) == 0x30 || (*p & 0x30) == 0x20) { bool pts = !!(*p & 0x20), dts = !!(*p & 0x10); if (pts) { len -= 5; } p += 5; if (dts) { ASSERT((*p & 0xf0) == 0x10); len -= 5; p += 5; } } else { len--; p++; } } if (ps1) { len--; p++; if (m_mt.subtype == MEDIASUBTYPE_DVD_LPCM_AUDIO) { len -= 6; p += 6; } else if (m_mt.subtype == MEDIASUBTYPE_DOLBY_AC3 || m_mt.subtype == MEDIASUBTYPE_WAVE_DOLBY_AC3 || m_mt.subtype == MEDIASUBTYPE_DTS || m_mt.subtype == MEDIASUBTYPE_WAVE_DTS) { len -= 3; p += 3; } } if (expected > 0) { expected -= (long)(p - p0); len = std::min(expected, len); } } } if (len < 0) { ASSERT(0); len = 0; } } // IKsPropertySet STDMETHODIMP CDeCSSInputPin::Set(REFGUID PropSet, ULONG Id, LPVOID pInstanceData, ULONG InstanceLength, LPVOID pPropertyData, ULONG DataLength) { if (PropSet != AM_KSPROPSETID_CopyProt) { return E_NOTIMPL; } switch (Id) { case AM_PROPERTY_COPY_MACROVISION: break; case AM_PROPERTY_DVDCOPY_CHLG_KEY: { // 3. auth: receive drive nonce word, also store and encrypt the buskey made up of the two nonce words AM_DVDCOPY_CHLGKEY* pChlgKey = (AM_DVDCOPY_CHLGKEY*)pPropertyData; for (int i = 0; i < 10; i++) { m_Challenge[i] = pChlgKey->ChlgKey[9 - i]; } CSSkey2(m_varient, m_Challenge, &m_Key[5]); CSSbuskey(m_varient, m_Key, m_KeyCheck); } break; case AM_PROPERTY_DVDCOPY_DISC_KEY: { // 5. receive the disckey AM_DVDCOPY_DISCKEY* pDiscKey = (AM_DVDCOPY_DISCKEY*)pPropertyData; // pDiscKey->DiscKey holds the disckey encrypted with itself and the 408 disckeys encrypted with the playerkeys bool fSuccess = false; for (int j = 0; j < g_nPlayerKeys; j++) { for (int k = 1; k < 409; k++) { BYTE DiscKey[6]; for (int i = 0; i < 5; i++) { DiscKey[i] = pDiscKey->DiscKey[k * 5 + i] ^ m_KeyCheck[4 - i]; } DiscKey[5] = 0; CSSdisckey(DiscKey, g_PlayerKeys[j]); BYTE Hash[6]; for (int i = 0; i < 5; i++) { Hash[i] = pDiscKey->DiscKey[i] ^ m_KeyCheck[4 - i]; } Hash[5] = 0; CSSdisckey(Hash, DiscKey); if (!memcmp(Hash, DiscKey, 6)) { memcpy(m_DiscKey, DiscKey, 6); j = g_nPlayerKeys; fSuccess = true; break; } } } if (!fSuccess) { return E_FAIL; } } break; case AM_PROPERTY_DVDCOPY_DVD_KEY1: { // 2. auth: receive our drive-encrypted nonce word and decrypt it for verification AM_DVDCOPY_BUSKEY* pKey1 = (AM_DVDCOPY_BUSKEY*)pPropertyData; for (int i = 0; i < 5; i++) { m_Key[i] = pKey1->BusKey[4 - i]; } m_varient = -1; for (int i = 31; i >= 0; i--) { CSSkey1(i, m_Challenge, m_KeyCheck); if (memcmp(m_KeyCheck, &m_Key[0], 5) == 0) { m_varient = i; } } } break; case AM_PROPERTY_DVDCOPY_REGION: break; case AM_PROPERTY_DVDCOPY_SET_COPY_STATE: break; case AM_PROPERTY_DVDCOPY_TITLE_KEY: { // 6. receive the title key and decrypt it with the disc key AM_DVDCOPY_TITLEKEY* pTitleKey = (AM_DVDCOPY_TITLEKEY*)pPropertyData; for (int i = 0; i < 5; i++) { m_TitleKey[i] = pTitleKey->TitleKey[i] ^ m_KeyCheck[4 - i]; } m_TitleKey[5] = 0; CSStitlekey(m_TitleKey, m_DiscKey); } break; default: return E_PROP_ID_UNSUPPORTED; } return S_OK; } STDMETHODIMP CDeCSSInputPin::Get(REFGUID PropSet, ULONG Id, LPVOID pInstanceData, ULONG InstanceLength, LPVOID pPropertyData, ULONG DataLength, ULONG* pBytesReturned) { if (PropSet != AM_KSPROPSETID_CopyProt) { return E_NOTIMPL; } switch (Id) { case AM_PROPERTY_DVDCOPY_CHLG_KEY: { // 1. auth: send our nonce word AM_DVDCOPY_CHLGKEY* pChlgKey = (AM_DVDCOPY_CHLGKEY*)pPropertyData; for (int i = 0; i < 10; i++) { pChlgKey->ChlgKey[i] = 9 - (m_Challenge[i] = i); } *pBytesReturned = sizeof(AM_DVDCOPY_CHLGKEY); } break; case AM_PROPERTY_DVDCOPY_DEC_KEY2: { // 4. auth: send back the encrypted drive nonce word to finish the authentication AM_DVDCOPY_BUSKEY* pKey2 = (AM_DVDCOPY_BUSKEY*)pPropertyData; for (int i = 0; i < 5; i++) { pKey2->BusKey[4 - i] = m_Key[5 + i]; } *pBytesReturned = sizeof(AM_DVDCOPY_BUSKEY); } break; case AM_PROPERTY_DVDCOPY_REGION: { DVD_REGION* pRegion = (DVD_REGION*)pPropertyData; pRegion->RegionData = 0; pRegion->SystemRegion = 0; *pBytesReturned = sizeof(DVD_REGION); } break; case AM_PROPERTY_DVDCOPY_SET_COPY_STATE: { AM_DVDCOPY_SET_COPY_STATE* pState = (AM_DVDCOPY_SET_COPY_STATE*)pPropertyData; pState->DVDCopyState = AM_DVDCOPYSTATE_AUTHENTICATION_REQUIRED; *pBytesReturned = sizeof(AM_DVDCOPY_SET_COPY_STATE); } break; default: return E_PROP_ID_UNSUPPORTED; } return S_OK; } STDMETHODIMP CDeCSSInputPin::QuerySupported(REFGUID PropSet, ULONG Id, ULONG* pTypeSupport) { if (PropSet != AM_KSPROPSETID_CopyProt) { return E_NOTIMPL; } switch (Id) { case AM_PROPERTY_COPY_MACROVISION: *pTypeSupport = KSPROPERTY_SUPPORT_SET; break; case AM_PROPERTY_DVDCOPY_CHLG_KEY: *pTypeSupport = KSPROPERTY_SUPPORT_GET | KSPROPERTY_SUPPORT_SET; break; case AM_PROPERTY_DVDCOPY_DEC_KEY2: *pTypeSupport = KSPROPERTY_SUPPORT_GET; break; case AM_PROPERTY_DVDCOPY_DISC_KEY: *pTypeSupport = KSPROPERTY_SUPPORT_SET; break; case AM_PROPERTY_DVDCOPY_DVD_KEY1: *pTypeSupport = KSPROPERTY_SUPPORT_SET; break; case AM_PROPERTY_DVDCOPY_REGION: *pTypeSupport = KSPROPERTY_SUPPORT_GET | KSPROPERTY_SUPPORT_SET; break; case AM_PROPERTY_DVDCOPY_SET_COPY_STATE: *pTypeSupport = KSPROPERTY_SUPPORT_GET | KSPROPERTY_SUPPORT_SET; break; case AM_PROPERTY_DVDCOPY_TITLE_KEY: *pTypeSupport = KSPROPERTY_SUPPORT_SET; break; default: return E_PROP_ID_UNSUPPORTED; } return S_OK; }