diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/CAPMT.cpp vdr-plugin-dvbapi-2.2.5+git20210627/CAPMT.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/CAPMT.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/CAPMT.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -192,6 +192,46 @@ SockHandler->Write(caPMT, toWrite); } +uint16_t CAPMT::GetCAIDFromPid(int adapter_index, int pid, int& sid) +{ + sid = 0; + if (pid <= 0) + return 0; + //DEBUGLOG("%s: adapter_index:%d pid:%d", __FUNCTION__, adapter_index, pid); + cMutexLock lock(&mutex); + + vector::iterator it; + + if (!pmt.empty()) + { + for (it = pmt.begin(); it != pmt.end(); ++it) + { + //DEBUGLOG("%s: TEST adapter_index:%d pid:%d SID:%04X CAID:%04X", __FUNCTION__, it->adapter, it->pid, it->sid, it->caid); + if (it->pid == pid && it->adapter == adapter_index) + { + sid = it->sid; + //DEBUGLOG("%s: FOUND adapter_index:%d pid:%d SID:%04X CAID:%04X", __FUNCTION__, it->adapter, it->pid, it->sid, it->caid); + return it->caid; + } + } + } + + return 0; +} + +uint16_t CAPMT::GetCAIDFromSid(int adapter_index, int sid) +{ + cMutexLock lock(&mutex); + vector::iterator it; + + if (!pmt.empty()) + for (it = pmt.begin(); it != pmt.end(); ++it) + if (it->sid == sid && it->adapter == adapter_index) + return it->caid; + + return 0; +} + void CAPMT::UpdateEcmInfo(int adapter_index, int sid, uint16_t caid, uint16_t pid, uint32_t prid, uint32_t ecmtime, char *cardsystem, char *reader, char *from, char *protocol, int8_t hops) { cMutexLock lock(&mutex); diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/CAPMT.h vdr-plugin-dvbapi-2.2.5+git20210627/CAPMT.h --- vdr-plugin-dvbapi-2.2.5+git20190727/CAPMT.h 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/CAPMT.h 2021-06-27 15:54:05.000000000 +0000 @@ -77,6 +77,8 @@ return pmt.empty(); } void SendAll(); + uint16_t GetCAIDFromSid(int adapter_index, int sid); + uint16_t GetCAIDFromPid(int adapter_index, int pid, int& sid); void UpdateEcmInfo(int adapter_index, int sid, uint16_t caid, uint16_t pid, uint32_t prid, uint32_t ecmtime, char *cardsystem, char *reader, char *from, char *protocol, int8_t hops); bool FillEcmInfo(sDVBAPIEcmInfo *ecminfo); }; diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/debian/changelog vdr-plugin-dvbapi-2.2.5+git20210627/debian/changelog --- vdr-plugin-dvbapi-2.2.5+git20190727/debian/changelog 2021-12-29 21:24:51.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/debian/changelog 2021-12-29 22:27:51.000000000 +0000 @@ -1,20 +1,7 @@ -vdr-plugin-dvbapi (2.2.5+git20190727-0fnu2~bionic) bionic; urgency=medium +vdr-plugin-dvbapi (2.2.5+git20210627-0fnu0~bionic) bionic; urgency=high - * rebuild + * new upstream snapshot + * Add Fast ECM support + * Fix for libdvbcsa - -- Frank Neumann Wed, 29 Dec 2021 22:24:51 +0100 - -vdr-plugin-dvbapi (2.2.5+git20190727-0fnu1~bionic) bionic; urgency=medium - - * rebuild - - -- Frank Neumann Sat, 25 Dec 2021 03:11:06 +0100 - -vdr-plugin-dvbapi (2.2.5+git20190727-0fnu0~bionic) bionic; urgency=high - - * new stable version - * Add extended cw mode for AES128 - * Adapt VDR's Makefile style - * Adapted to OScam svn >= 11534. Droped work with oldest OScam versions. - - -- Frank Neumann Fri, 09 Aug 2019 15:05:01 +0200 + -- Frank Neumann Wed, 29 Dec 2021 23:27:51 +0100 diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/DeCSA.cpp vdr-plugin-dvbapi-2.2.5+git20210627/DeCSA.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/DeCSA.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/DeCSA.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -16,12 +16,72 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/* ----------------- FASTECM description: + The encrypted control word is broadcast in an ECM approximately once every two seconds + The Control Word used to encrypt the transport stream packets are changed regularly, + usually every 10 seconds.If the Control Words change stops for whatever reason the STBs can use the same Control Word + to decrypt the incoming signal until the problem is fixed.This is a serious security issue. + + In each PID header there are 2 bits telling the decoder if the Odd or Even Control Word should be used.The ECM + normally contains two Control Words.This mechanism allows the ECM to carry both the Control Word currently used + and the Control Word which will be used for scrambling the next time the Control Word changes.This ensures that the + STB always has the Control Word needed to descramble the content. + + Sky Germany only has one Control Word. + We can see the CW around 620ms before it shoul be used. +*/ + #include "DeCSA.h" #include "Log.h" #include "cscrypt/des.h" DeCSA *decsa = NULL; +#define lldcast long long int +bool IsFastECMCAID(int caCaid) +{ + if (caCaid == 0x09C4 || caCaid == 0x098C || caCaid == 0x098D || caCaid == 0x09AF || //SKY DE 09AF is OBSOLETE + caCaid == 0x09CD || //Sky IT + caCaid == 0x0963) //Sky UK + { + return true; + } + + return false; +} + +class cMutexLockHelper { +private: + cMutexLock *pmutexLock; + cMutex *pmutex; +public: + cMutexLockHelper(cMutex *Mutex = NULL, bool block = true) + { + pmutexLock = NULL; + pmutex = Mutex; + if (block) + pmutexLock = new cMutexLock(pmutex); + }; + ~cMutexLockHelper() + { + if (pmutexLock) delete pmutexLock; + pmutexLock = NULL; + pmutex = NULL; + } + + void UnLock() + { + if (pmutexLock != NULL) delete pmutexLock; + pmutexLock = NULL; + } + + void ReLock() + { + if (pmutexLock == NULL) + pmutexLock = new cMutexLock(pmutex); + } +}; + bool CheckNull(const unsigned char *data, int len) { while (--len >= 0) @@ -61,111 +121,587 @@ } #endif -DeCSA::DeCSA() +DeCSAKey::DeCSAKey() { + Aes = false; + index = -1; + #ifndef LIBDVBCSA - cs = get_suggested_cluster_size(); - DEBUGLOG("clustersize=%d rangesize=%d", cs, cs * 2 + 5); - range = MALLOC(unsigned char *, (cs * 2 + 5)); - memset(keys, 0, sizeof(keys)); + key = NULL; #else - cs = dvbcsa_bs_batch_size(); - DEBUGLOG("batch_size=%d", cs); - cs_tsbbatch_even = reinterpret_cast(malloc((cs + 1) * sizeof(struct dvbcsa_bs_batch_s))); - cs_tsbbatch_odd = reinterpret_cast(malloc((cs + 1) * sizeof(struct dvbcsa_bs_batch_s))); - memset(cs_key_even, 0, sizeof(cs_key_even)); - memset(cs_key_odd, 0, sizeof(cs_key_odd)); + cs_key_even = NULL; + cs_key_odd = NULL; #endif - memset(cwSeen, 0, sizeof(cwSeen)); - memset(Aes,0,sizeof(Aes)); + #ifdef LIBSSL - memset(csa_aes_keys, 0, sizeof(csa_aes_keys)); + csa_aes_key = NULL; #endif - ResetState(); + lastcwlog = 0; + + cwSeen = 0; } -DeCSA::~DeCSA() +DeCSAKey::~DeCSAKey() { - for (int i = 0; i < MAX_CSA_IDX; i++) #ifndef LIBDVBCSA - if (keys[i]) - free_key_struct(keys[i]); - free(range); + if (key) + { + cMutexLock lock(&mutexKEY); + free_key_struct(key); + } + key = NULL; #else +{ + cMutexLock lock(&mutexKEY); + if (cs_key_even) + dvbcsa_bs_key_free(cs_key_even); + cs_key_even = NULL; + if (cs_key_odd) + dvbcsa_bs_key_free(cs_key_odd); + cs_key_odd = NULL; +} +#endif + +#ifdef LIBSSL + if (csa_aes_key) { - if (cs_key_even[i]) - dvbcsa_bs_key_free(cs_key_even[i]); - if (cs_key_odd[i]) - dvbcsa_bs_key_free(cs_key_odd[i]); + cMutexLock lock(&mutexKEY); + aes_free_key_struct(csa_aes_key); } - free(cs_tsbbatch_even); - free(cs_tsbbatch_odd); + csa_aes_key = NULL; #endif +} + +void DeCSAKey::SetAes(uint32_t usedAes) +{ + cMutexLock lock(&mutexKEY); + Aes = usedAes; +} + +uint32_t DeCSAKey::GetAes() +{ + cMutexLock lock(&mutexKEY); + return Aes; +} + +void DeCSAKey::SetAlgo(uint32_t usedAlgo) +{ + cMutexLock lock(&mutexKEY); + algo = usedAlgo; +} + +uint32_t DeCSAKey::GetAlgo() +{ + cMutexLock lock(&mutexKEY); + return algo; +} + #ifdef LIBSSL - for (int i = 0; i < MAX_CSA_IDX; i++) - if (csa_aes_keys[i]) - aes_free_key_struct(csa_aes_keys[i]); +bool DeCSAKey::GetorCreateAesKeyStruct() +{ + cMutexLock lock(&mutexKEY); + if (!csa_aes_key) + { + DEBUGLOG("GetorCreateAesKeyStruct - keyindex:%d", index); + csa_aes_key = aes_get_key_struct(); + } + return csa_aes_key != 0; +} +#endif + +bool DeCSAKey::GetorCreateKeyStruct() +{ + cMutexLock lock(&mutexKEY); +#ifndef LIBDVBCSA + if (!key) + { + DEBUGLOG("GetorCreateKeyStruct - keyindex:%d", index); + key = get_key_struct(); + } + return key != 0; +#else + if (!cs_key_even) + { + DEBUGLOG("GetorCreateKeyStruct - even, keyindex:%d", index); + cs_key_even = dvbcsa_bs_key_alloc(); + } + if (!cs_key_odd) + { + DEBUGLOG("GetorCreateKeyStruct - odd, keyindex:%d", index); + cs_key_odd = dvbcsa_bs_key_alloc(); + } + return (cs_key_even != 0) && (cs_key_odd != 0); #endif } -void DeCSA::ResetState(void) +bool DeCSAKey::CWExpired() { - DEBUGLOG("%s", __FUNCTION__); + cMutexLock lock(&mutexKEY); + if (CheckExpiredCW) + { + time_t tnow = time(NULL); + if (CheckExpiredCW && cwSeen > 0 && (tnow - cwSeen) > MAX_KEY_WAIT) + { + if ((tnow - lastcwlog) > 10) + { + lastcwlog = tnow; + DEBUGLOG("%s: CheckExpiredCW key is expired", __FUNCTION__); + } + return true; + } + else + { + lastcwlog = tnow; + } + } + return false; +} + +#ifndef LIBDVBCSA +bool DeCSAKey::SetFastECMCaidSid(int caid, int sid) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + setFastECMCaidSid(key, caid,sid); + return true; + } + return false; +} + +int DeCSAKey::Set_FastECM_CW_Parity(int pid, int parity, bool bforce, int& oldparity, bool& bfirsttimecheck, bool& bnextparityset, bool& bactivparitypatched) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + return set_FastECM_CW_Parity(key, pid, parity, bforce, oldparity, bfirsttimecheck, bnextparityset, bactivparitypatched); + } + return 1; +} + +void DeCSAKey::SetFastECMPid(int pid) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + setFastECMPid(key, pid); + } +} + +void DeCSAKey::Get_FastECM_CAID(int* caid) +{ + cMutexLock lock(&mutexKEY); + *caid = 0; + if (key) + get_FastECM_CAID(key,caid); +} + +void DeCSAKey::Get_FastECM_SID(int* caSid) +{ + cMutexLock lock(&mutexKEY); + *caSid = 0; + if (key) + { + get_FastECM_SID(key, caSid); + } +} + +void DeCSAKey::Get_FastECM_PID(int* caPid) +{ + cMutexLock lock(&mutexKEY); + *caPid = 0; + if (key) + { + get_FastECM_PID(key, caPid); + } +} + +bool DeCSAKey::Get_FastECM_struct(FAST_ECM& fecm) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + FAST_ECM* fe=get_FastECM_struct(key); + fecm = *fe; + return true; + } + return false; +} + +bool DeCSAKey::GetActiveParity(int pid, int& aparity, int& aparity2) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + getActiveParity(key,pid, aparity, aparity2); + return true; + } + return false; +} + +void DeCSAKey::InitFastEcmOnCaid(int Caid) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + struct FAST_ECM* sf = get_FastECM_struct(key); + if (sf && Caid == sf->csaCaid) + { + sf->oddparityTime = 0; + sf->evenparityTime = 0; + sf->nextparity = 0; + + sf->activparity.clear(); + sf->activparity2.clear(); + } + } +} + +void DeCSAKey::SetActiveParity2(int pid,int parity2) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + FAST_ECM* fe = get_FastECM_struct(key); + fe->activparity2[pid] = parity2; + } +} + +int DeCSAKey::Decrypt_packets(unsigned char **cluster) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + return decrypt_packets(key, cluster); + } + else + { + DEBUGLOG("%s: ind:%d Decrypt_packets key is null", __FUNCTION__, index); + } + return 0; +} + +bool DeCSAKey::Get_control_words(unsigned char *even, unsigned char *odd) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + get_control_words(key, even, odd); + return true; + } + return false; +} +#endif + +void DeCSAKey::Des(uint8_t* data, unsigned char parity) +{ + cMutexLock lock(&mutexKEY); + des(data, des_key_schedule[parity], 0); +} + +void DeCSAKey::Des_set_key(const unsigned char *cw, unsigned char parity) +{ + cMutexLock lock(&mutexKEY); + cwSeen = time(NULL); + des_set_key(cw, des_key_schedule[parity]); +} + +bool DeCSAKey::Set_even_control_word(const unsigned char *even) +{ + cMutexLock lock(&mutexKEY); #ifndef LIBDVBCSA - lastData = 0; + if (key) + { + set_even_control_word(key, even); + return true; + } +#else + if (cs_key_even) + { + dvbcsa_bs_key_set(even, cs_key_even); + return true; + } #endif + return false; } -bool DeCSA::GetKeyStruct(int idx) +bool DeCSAKey::Set_odd_control_word(const unsigned char *odd) +{ + cMutexLock lock(&mutexKEY); +#ifndef LIBDVBCSA + if (key) + { + set_odd_control_word(key, odd); + return true; + } +#else + if (cs_key_odd) + { + dvbcsa_bs_key_set(odd, cs_key_odd); + return true; + } +#endif + return false; +} + +#ifndef LIBDVBCSA +void DeCSAKey::Init_Parity2(bool binitcsa) +{ + cMutexLock lock(&mutexKEY); + if (key) + { + if (binitcsa) + DEBUGLOG("Init_Parity keyindex:%d", index); + else + DEBUGLOG("Init_Parity from Timeout keyindex:%d", index); + + Init_FastECM(key, binitcsa); + } +} +#endif + +DeCSAAdapter::DeCSAAdapter() +{ + cardindex = -1; + + bCW_Waiting = false; + bAbort = false; + +#ifndef LIBDVBCSA + csnew = get_suggested_cluster_size(); + rangenew = MALLOC(unsigned char *, (csnew * 2 + 5)); +#else + cs = dvbcsa_bs_batch_size(); + cs_tsbbatch_even = reinterpret_cast(malloc((cs + 1) * sizeof(struct dvbcsa_bs_batch_s))); + cs_tsbbatch_odd = reinterpret_cast(malloc((cs + 1) * sizeof(struct dvbcsa_bs_batch_s))); +#endif +} + +DeCSAAdapter::~DeCSAAdapter() { + cMutexLockHelper lockDecrypt(&mutexDecrypt); + #ifndef LIBDVBCSA - if (!keys[idx]) - keys[idx] = get_key_struct(); - return keys[idx] != 0; -#else - if (!cs_key_even[idx]) - cs_key_even[idx] = dvbcsa_bs_key_alloc(); - if (!cs_key_odd[idx]) - cs_key_odd[idx] = dvbcsa_bs_key_alloc(); - return (cs_key_even[idx] != 0) && (cs_key_odd[idx] != 0); + free(rangenew); +#else + free(cs_tsbbatch_even); + free(cs_tsbbatch_odd); #endif } +void DeCSAAdapter::CancelWait() +{ + if (bCW_Waiting) + { + DEBUGLOG("%s: decsa CW Waiting", __FUNCTION__); + bAbort = true; + cMutexLock lock(&mutexStopDecrypt); + bAbort = false; + DEBUGLOG("%s: decsa CW Waiting Aborted", __FUNCTION__); + } +} + +#ifndef LIBDVBCSA +void DeCSAAdapter::Init_Parity(DeCSAKey *keys, int sid, int slot,bool bdelete) +{ + cMutexLock lock(&mutexAdapter); + + if (sid < 0 && slot < 0) return; + if (sid >= 0) + DEBUGLOG("Init_Parity cardindex:%d SID %d (0x%04X)", cardindex, sid, sid); + else + DEBUGLOG("Init_Parity cardindex:%d Slot %d", cardindex, slot); + + bool bEND = false; + do + { + bEND = true; + map::iterator it; + for (it = AdapterPidMap.begin(); it != AdapterPidMap.end(); ++it) + { + int ipid = it->first; + int iidx = it->second; + + int caCaid = -1; + int caSid = -1; + int caPid = -1; + + keys[iidx].Get_FastECM_CAID(&caCaid); + keys[iidx].Get_FastECM_SID(&caSid); + keys[iidx].Get_FastECM_PID(&caPid); + + DEBUGLOG("Init_Parity cardindex:%d iidx:%d sid:%d caSid:%d pid:%d caCaid:0x%04X caPid:%d", cardindex,iidx,sid, caSid, ipid,caCaid,caPid); + + if (sid >= 0 && caSid == sid) + { + keys[iidx].Init_Parity2(); + if (bdelete) + { + DEBUGLOG("Init_Parity delete AdapterPidMap1 cardindex:%d keyindex:%d pid:%d", cardindex, iidx, ipid); + AdapterPidMap.erase(ipid); + bEND = false; + break; + } + } + else if (slot >= 0 && slot == iidx) + { + keys[iidx].Init_Parity2(); + DEBUGLOG("Init_Parity delete AdapterPidMap2 cardindex:%d keyindex:%d pid:%d", cardindex, iidx, ipid); + AdapterPidMap.erase(ipid); + bEND = false; + break; + } + } + } + while (bEND == false); + + //DebugLogPidmap(); +} +#endif + +int DeCSAAdapter::SearchPIDinMAP(int pid) +{ + cMutexLock lock(&mutexAdapter); + //we must search for pid, otherwise on tune start we use always idx 0 + //int idx = -1; + map::iterator it; + for (it = AdapterPidMap.begin(); it != AdapterPidMap.end(); ++it) + { + if (it->first == pid) + { + return it->second; + } + } + + //DEBUGLOG("%s: pid not found in m_pidmap cardindex:%d pid:%d(0x%04X) l:%d len:%d", __FUNCTION__, adapter_index,pid,pid, l,len); + return -1; +} + +void DeCSAAdapter::SetCaPid(int pid, int index) +{ + cMutexLock lock(&mutexAdapter); + DEBUGLOG("%s: SetCaPid cardindex:%d pid:%d index:%d", __FUNCTION__, cardindex, pid, index); + AdapterPidMap[pid] = index == -1 ? 0 : index; +} + +#ifndef LIBDVBCSA +void DeCSAAdapter::SetDVBAPIPid(DeCSA* parent,int slot, int dvbapiPID) +{ + if (dvbapiPID >= 0 && slot >= 0 && slot::iterator it; + for (it = AdapterPidMap.begin(); it != AdapterPidMap.end(); ++it) + { + //int ipid = it->first; + int iidx = it->second; + if (iidx == slot) + { + idxOK = iidx; + break; + } + } + + //slot not found - create it later in DeCSA::SetCaPid + if (idxOK < 0) + { + idxOK = slot; + } + + parent->SetFastECMPid(cardindex, idxOK, slot, dvbapiPID); + } + else + { + } +} +#endif + +DeCSA::DeCSA() +{ + for (int i = 0;i < MAXADAPTER;i++) + DeCSAArray[i].cardindex = i; + + for (int i = 0;i < MAX_CSA_IDX;i++) + DeCSAKeyArray[i].index = i; + + ResetState(); +} + +DeCSA::~DeCSA() +{ +} + +void DeCSA::ResetState(void) +{ + DEBUGLOG("%s", __FUNCTION__); +} + +bool DeCSA::GetKeyStruct(int idx) +{ + if (idx >= 0 && idx= 0) + { + return DeCSAKeyArray[idx].GetorCreateAesKeyStruct(); + } + return false; } #endif -bool DeCSA::SetDescr(ca_descr_t *ca_descr, bool initial) +bool DeCSA::SetDescr(ca_descr_t *ca_descr, bool initial, int adapter_index) { - DEBUGLOG("%s", __FUNCTION__); + DEBUGLOG("%s addapter:%d", __FUNCTION__, adapter_index); + cMutexLock lock(&mutex); + int idx = ca_descr->index; if (idx < MAX_CSA_IDX && GetKeyStruct(idx)) { - DEBUGLOG("%d: %4s key set", idx, ca_descr->parity ? "odd" : "even"); - cwSeen[idx] = time(NULL); - des_set_key(ca_descr->cw, des_key_schedule[idx][ca_descr->parity]); - if (ca_descr->parity == 0) - { #ifndef LIBDVBCSA - set_even_control_word(keys[idx], ca_descr->cw); -#else - dvbcsa_bs_key_set(ca_descr->cw, cs_key_even[idx]); + FAST_ECM fecm; + DeCSAKeyArray[idx].Get_FastECM_struct(fecm); + + uint64_t now = GetTick(); + uint64_t evendelta = -1; + if (fecm.evenparityTime > 0) + evendelta = now - fecm.evenparityTime; + uint64_t odddelta = -1; + if (fecm.oddparityTime > 0) + odddelta = now - fecm.oddparityTime; + + unsigned char cweven[8]; + unsigned char cwodd[8]; + DeCSAKeyArray[idx].Get_control_words(cweven, cwodd); + + DEBUGLOG("keyindex:%d adapter:%d EVENKEYOLD: CW: %02x %02x %02x %02x %02x %02x %02x %02x deltams:%lld nextparity:%d csaSid:%04x csaCaid:%04x csaPid:%04x", + idx, adapter_index, + cweven[0], cweven[1], cweven[2], cweven[3], cweven[4], cweven[5], cweven[6], cweven[7], + (lldcast)evendelta, fecm.nextparity, fecm.csaSid, fecm.csaCaid, fecm.csaPid); + DEBUGLOG("keyindex:%d adapter:%d ODDKEYOLD: CW: %02x %02x %02x %02x %02x %02x %02x %02x deltams:%lld nextparity:%d csaSid:%04x csaCaid:%04x csaPid:%04x", + idx, adapter_index, + cwodd[0], cwodd[1], cwodd[2], cwodd[3], cwodd[4], cwodd[5], cwodd[6], cwodd[7], + (lldcast)odddelta, fecm.nextparity, fecm.csaSid, fecm.csaCaid, fecm.csaPid); #endif - } + DEBUGLOG("keyindex:%d adapter:%d %4s CW key set index:%d CW: %02x %02x %02x %02x %02x %02x %02x %02x initial:%d", + idx, adapter_index, + ca_descr->parity ? "odd" : "even", ca_descr->index, + ca_descr->cw[0], ca_descr->cw[1], ca_descr->cw[2], ca_descr->cw[3], ca_descr->cw[4], ca_descr->cw[5], ca_descr->cw[6], ca_descr->cw[7], + initial); + + DeCSAKeyArray[idx].Des_set_key(ca_descr->cw, ca_descr->parity); + if (ca_descr->parity == 0) + DeCSAKeyArray[idx].Set_even_control_word(ca_descr->cw); else - { -#ifndef LIBDVBCSA - set_odd_control_word(keys[idx], ca_descr->cw); -#else - dvbcsa_bs_key_set(ca_descr->cw, cs_key_odd[idx]); -#endif - } + DeCSAKeyArray[idx].Set_odd_control_word(ca_descr->cw); } return true; } @@ -181,11 +717,13 @@ DEBUGLOG("%d: %4s aes key set", idx, ca_descr_aes->parity ? "odd" : "even"); if (ca_descr_aes->parity == 0) { - AES_set_decrypt_key(ca_descr_aes->cw, 128, &((struct aes_keys_t *) csa_aes_keys[idx])->even); + if (DeCSAKeyArray[idx].csa_aes_key) + AES_set_decrypt_key(ca_descr_aes->cw, 128, &((struct aes_keys_t *) DeCSAKeyArray[idx].csa_aes_key)->even); } else { - AES_set_decrypt_key(ca_descr_aes->cw, 128, &((struct aes_keys_t *) csa_aes_keys[idx])->odd); + if (DeCSAKeyArray[idx].csa_aes_key) + AES_set_decrypt_key(ca_descr_aes->cw, 128, &((struct aes_keys_t *) DeCSAKeyArray[idx].csa_aes_key)->odd); } } #endif @@ -212,13 +750,9 @@ { DEBUGLOG("%d: %4s aes key set", idx, ca_descr_data->parity ? "odd" : "even"); if (ca_descr_data->parity == CA_PARITY_EVEN) - { - AES_set_decrypt_key(ca_descr_data->data, 8 * ca_descr_data->length, &((struct aes_keys_t *) csa_aes_keys[idx])->even); - } + AES_set_decrypt_key(ca_descr_data->data, 8 * ca_descr_data->length, &((struct aes_keys_t*) DeCSAKeyArray[idx].csa_aes_key)->even); else - { - AES_set_decrypt_key(ca_descr_data->data, 8 * ca_descr_data->length, &((struct aes_keys_t *) csa_aes_keys[idx])->odd); - } + AES_set_decrypt_key(ca_descr_data->data, 8 * ca_descr_data->length, &((struct aes_keys_t*) DeCSAKeyArray[idx].csa_aes_key)->odd); } } #endif @@ -231,7 +765,8 @@ cMutexLock lock(&mutex); if (ca_pid->index < MAX_CSA_IDX && ca_pid->pid < MAX_CSA_PID) { - pidmap[make_pair(adapter_index, ca_pid->pid)] = ca_pid->index == -1 ? 0 : ca_pid->index; + if (ca_pid->index >= 0 && adapter_index>=0 && adapter_indexpid,ca_pid->index); DEBUGLOG("%d.%d: set pid 0x%04x", adapter_index, ca_pid->index, ca_pid->pid); } else @@ -241,14 +776,14 @@ void DeCSA::SetAlgo(uint32_t index, uint32_t usedAlgo) { - if (index < MAX_CSA_IDX) - algo[index] = usedAlgo; + if (index >= 0 && index < MAX_CSA_IDX) + DeCSAKeyArray[index].SetAlgo(usedAlgo); } void DeCSA::SetAes(uint32_t index, bool usedAes) { - if (index < MAX_CSA_IDX) - Aes[index] = usedAes; + if (index >= 0 && index < MAX_CSA_IDX) + DeCSAKeyArray[index].SetAes(usedAes); } void DeCSA::SetCipherMode(uint32_t index, uint32_t usedCipherMode) @@ -287,9 +822,164 @@ bool DeCSA::Decrypt(uint8_t adapter_index, unsigned char *data, int len, bool force) { - cMutexLock lock(&mutex); + if (adapter_index < 0 || adapter_index >= MAXADAPTER) + return false; + return DeCSAArray[adapter_index].Decrypt(this, data, len, force); +} + +#ifndef LIBDVBCSA +int DeCSA::GetCaid(uint8_t adapter_index, int pid) +{ + if (adapter_index < 0 || adapter_index >= MAXADAPTER) + return 0; + return DeCSAArray[adapter_index].GetCaid(this, pid); +} +#endif + +void DeCSA::CancelWait() +{ + for (int i = 0;i < MAXADAPTER;i++) + DeCSAArray[i].CancelWait(); +} + #ifndef LIBDVBCSA - if (!range) +void DeCSA::DebugLogPidmap() +{ + if (LogLevel < 3) return; + + for (int iadapter = 0;iadapter < MAXADAPTER;iadapter++) + { + if (DeCSAArray[iadapter].AdapterPidMap.size() > 0) + { + map::iterator it; + for (it = DeCSAArray[iadapter].AdapterPidMap.begin(); it != DeCSAArray[iadapter].AdapterPidMap.end(); ++it) + { + int ipid = it->first; + int iidx = it->second; + FAST_ECM fecm; + if (DeCSAKeyArray[iidx].Get_FastECM_struct(fecm)) + { + uint64_t now = GetTick(); + uint64_t evendelta = -1; + if (fecm.evenparityTime > 0) + evendelta = now - fecm.evenparityTime; + uint64_t odddelta = -1; + if (fecm.oddparityTime > 0) + odddelta = now - fecm.oddparityTime; + + int aparity = 0; + int aparity2 = 0; + DeCSAKeyArray[iidx].GetActiveParity(ipid, aparity, aparity2); + + DEBUGLOG("DebugLogPidmap cardindex:%d pid:%d(0x%04X) keyindex:%d SID:%d(0x%04X) caid:%d(0x%04X) DvbApiPid:%d(0x%04X) activparity:%d activparity2:%d nextparity:%d evendelta:%lld odddelta:%lld", + iadapter, ipid, ipid, iidx, fecm.csaSid, fecm.csaSid, fecm.csaCaid, fecm.csaCaid, fecm.csaPid, fecm.csaPid, + aparity, aparity2, fecm.nextparity, (lldcast)evendelta, (lldcast)odddelta); + } + } + } + } +} + +void DeCSA::Init_Parity(int cardindex, int sid, int slot,bool bdelete) +{ + if (cardindex < 0) + return; + if (sid < 0 && slot < 0) + return; + DeCSAArray[cardindex].Init_Parity(DeCSAKeyArray, sid,slot,bdelete); +} + +void DeCSA::SetDVBAPIPid(int adapter, int slot, int dvbapiPID) +{ + if (adapter>=0 && dvbapiPID >= 0 && slot >= 0 && slot=0 && GetKeyStruct(idx)) + { + DEBUGLOG("SetDVBAPIPid %d.%d (PID %d (0x%04X)) keyindex:%d",cardindex, slot, dvbapiPID, dvbapiPID, idx); + //Init_FastECM(keys[idxOK]); + DeCSAKeyArray[idx].SetFastECMPid(dvbapiPID); + } +} +#endif + +uint32_t DeCSA::GetAlgo(int idx) +{ + if (idx < 0 || idx >= MAX_CSA_IDX) + return -1; + return DeCSAKeyArray[idx].GetAlgo(); +} + +uint32_t DeCSA::GetAes(int idx) +{ + if (idx < 0 || idx >= MAX_CSA_IDX) + return -1; + return DeCSAKeyArray[idx].GetAes(); +} + +#ifndef LIBDVBCSA +int DeCSAAdapter::GetCaid(DeCSA* parent, int pid) +{ + DEBUGLOG("%s: DeCSAAdapter::GetCaid %d", __FUNCTION__, pid); + int ret = 0; + if (capmt) + { + if (LogLevel >= 3) + { + map::iterator it; + for (it = AdapterPidMap.begin(); it != AdapterPidMap.end(); ++it) + { + int idx = it->second; + if (idx >= 0) + { + int caCaid = 0; + parent->DeCSAKeyArray[idx].Get_FastECM_CAID(&caCaid); + DEBUGLOG("%s: cardindex:%d pid:%d pid:%d keyindex:%d caid:0x%04X", __FUNCTION__, cardindex,pid,it->first,idx,caCaid); + } + } + } + + int caCaid = 0; + int idx = SearchPIDinMAP(pid); + if (idx >= 0 && (pid < MAX_CSA_PID)) + parent->DeCSAKeyArray[idx].Get_FastECM_CAID(&caCaid); + DEBUGLOG("%s: DeCSAAdapter::GetCaid %d 0x%04X", __FUNCTION__, pid,caCaid); + ret = caCaid; + } + return ret; +} +#endif + +bool DeCSAAdapter::Decrypt(DeCSA* parent, unsigned char *data, int len, bool force) +{ + cTimeMs starttime(cTimeMs::Now()); + + cMutexLockHelper lockDecrypt(&mutexDecrypt); + cMutexLockHelper lockPIDMAPnew(&mutexAdapter); + +#ifndef LIBDVBCSA + bool blogfull = false; + uint8_t adapter_index = cardindex; + uint64_t sleeptime = 0; + int itimeout = 2500; //FASTECM maximum wait time for CW + int iSleep = 50; + int imaxSleep = itimeout / iSleep; + cTimeMs TimerTimeout(itimeout); + + if (!rangenew) #else if (!cs_tsbbatch_even || !cs_tsbbatch_odd) #endif @@ -298,24 +988,28 @@ return false; } - int offset; - int currIdx = -1; + int offset, currIdx = -1; #ifndef LIBDVBCSA int r = -2, ccs = 0; bool newRange = true; - range[0] = 0; + rangenew[0] = 0; + int curPid = 0; #else int ccs = 0; int payload_len; int cs_fill_even = 0; int cs_fill_odd = 0; #endif + len -= (TS_SIZE - 1); int l; + + int wantsparity = 0; + for (l = 0; l < len; l += TS_SIZE) { - if (data[l] != TS_SYNC_BYTE) - { // let higher level cope with that + if (data[l] != TS_SYNC_BYTE) // let higher level cope with that + { break; } unsigned int ev_od = data[l + 3] & 0xC0; @@ -326,83 +1020,87 @@ '10' (0x80) = Scrambled with even key '11' (0xC0) = Scrambled with odd key */ - if (ev_od & 0x80) - { // encrypted + if (ev_od & 0x80) // encrypted + { offset = ts_packet_get_payload_offset(data + l); #ifdef LIBDVBCSA payload_len = TS_SIZE - offset; #endif + int pid = ((data[l + 1] << 8) + data[l + 2]) & MAX_CSA_PID; - int idx = pidmap[make_pair(adapter_index, pid)]; - if ((pid < MAX_CSA_PID) && (currIdx < 0 || idx == currIdx)) - { // same or no index + int idx = SearchPIDinMAP(pid); + + //one idx has several pids (all pids belong to channel) + if (idx >= 0 && (pid < MAX_CSA_PID) && (currIdx < 0 || idx == currIdx)) // same or no index + { currIdx = idx; - // return if the key is expired, todo - some channels with aes not change key - if (!cipher_mode[currIdx] == CA_MODE_ECB && !Aes[currIdx] && CheckExpiredCW && time(NULL) - cwSeen[currIdx] > MAX_KEY_WAIT) +#ifndef LIBDVBCSA + curPid = pid; +#endif + if (ev_od == 0x80) //even + wantsparity = 1; + else if (ev_od == 0xC0) //odd + wantsparity = 2; + + if (currIdx<0 || (currIdx + 1) >= MAX_CSA_IDX) + ERRORLOG("%s: CheckExpiredCW currIdx is out of range %d", __FUNCTION__, currIdx); + + if (!parent->cipher_mode[currIdx] == CA_MODE_ECB && !parent->GetAes(currIdx) && parent->DeCSAKeyArray[currIdx].CWExpired()) return false; - if (algo[currIdx] == CA_ALGO_DES) + + if (parent->GetAlgo(currIdx) == CA_ALGO_DES) { if ((ev_od & 0x40) == 0) { for (int j = offset; j + 7 < 188; j += 8) - des(&data[l + j], des_key_schedule[currIdx][0], 0); - + parent->DeCSAKeyArray[currIdx].Des(&data[l + j],0); } else { for (int j = offset; j + 7 < 188; j += 8) - des(&data[l + j], des_key_schedule[currIdx][1], 0); - + parent->DeCSAKeyArray[currIdx].Des(&data[l + j], 1); } data[l + 3] &= 0x3f; // consider it decrypted now } #ifdef LIBSSL - else if (algo[currIdx] == CA_ALGO_AES128) //extended cw mode + else if (parent->GetAlgo(currIdx) == CA_ALGO_AES128) //extended cw mode { - if (cipher_mode[currIdx] == CA_MODE_ECB) - { - AES_KEY aes_key; - data[l + 3] &= 0x3f; // consider it decrypted now + if (parent->cipher_mode[currIdx] == CA_MODE_ECB) + { + AES_KEY aes_key; + data[l + 3] &= 0x3f; // consider it decrypted now - if (data[l+3] & 0x20) - { - if ((188 - offset) >> 4 == 0) - return true; - } - if (((ev_od & 0x40) >> 6) == 0) - { - aes_key = ((struct aes_keys_t *) csa_aes_keys[currIdx])->even; - } - else - { - aes_key = ((struct aes_keys_t *) csa_aes_keys[currIdx])->odd; - } - for (int j = offset; j + 16 <= 188; j += 16) - AES_ecb_encrypt(&data[l + j], &data[l + j], &aes_key, AES_DECRYPT); - } - else if (cipher_mode[currIdx] == CA_MODE_CBC) + if (data[l + 3] & 0x20) { - AES_KEY aes_key; - data[l + 3] &= 0x3f; // consider it decrypted now + if ((188 - offset) >> 4 == 0) + return true; + } + if (((ev_od & 0x40) >> 6) == 0) + aes_key = ((struct aes_keys_t*) parent->DeCSAKeyArray[currIdx].csa_aes_key)->even; + else + aes_key = ((struct aes_keys_t*) parent->DeCSAKeyArray[currIdx].csa_aes_key)->odd; + for (int j = offset; j + 16 <= 188; j += 16) + AES_ecb_encrypt(&data[l + j], &data[l + j], &aes_key, AES_DECRYPT); + } + else if (parent->cipher_mode[currIdx] == CA_MODE_CBC) + { + AES_KEY aes_key; + data[l + 3] &= 0x3f; // consider it decrypted now - if (data[l+3] & 0x20) - { - if ((188 - offset) >> 4 == 0) - return true; - } - if (((ev_od & 0x40) >> 6) == 0) - { - aes_key = ((struct aes_keys_t *) csa_aes_keys[currIdx])->even; - } - else - { - aes_key = ((struct aes_keys_t *) csa_aes_keys[currIdx])->odd; - } - for (int j = offset; j + 16 <= 188; j += 16) - AES_cbc_encrypt(&data[l + j], &data[l + j], 16, &aes_key, ivec[currIdx], AES_DECRYPT); + if (data[l + 3] & 0x20) + { + if ((188 - offset) >> 4 == 0) + return true; } + if (((ev_od & 0x40) >> 6) == 0) + aes_key = ((struct aes_keys_t*) parent->DeCSAKeyArray[currIdx].csa_aes_key)->even; + else + aes_key = ((struct aes_keys_t*) parent->DeCSAKeyArray[currIdx].csa_aes_key)->odd; + for (int j = offset; j + 16 <= 188; j += 16) + AES_cbc_encrypt(&data[l + j], &data[l + j], 16, &aes_key, parent->ivec[currIdx], AES_DECRYPT); + } } - else if (Aes[currIdx]) + else if (parent->GetAes(currIdx)) { AES_KEY aes_key; data[l + 3] &= 0x3f; // consider it decrypted now @@ -414,11 +1112,13 @@ } if (((ev_od & 0x40) >> 6) == 0) { - aes_key = ((struct aes_keys_t *) csa_aes_keys[currIdx])->even; + if (parent->DeCSAKeyArray[currIdx].csa_aes_key) + aes_key = ((struct aes_keys_t *) parent->DeCSAKeyArray[currIdx].csa_aes_key)->even; } else { - aes_key = ((struct aes_keys_t *) csa_aes_keys[currIdx])->odd; + if (parent->DeCSAKeyArray[currIdx].csa_aes_key) + aes_key = ((struct aes_keys_t *) parent->DeCSAKeyArray[currIdx].csa_aes_key)->odd; } for (int j = offset; j + 16 <= 188; j += 16) AES_ecb_encrypt(&data[l + j], &data[l + j], &aes_key, AES_DECRYPT); @@ -431,10 +1131,13 @@ { r += 2; newRange = false; - range[r] = &data[l]; - range[r + 2] = 0; + rangenew[r] = &data[l]; + rangenew[r + 2] = 0; } - range[r + 1] = &data[l + TS_SIZE]; + rangenew[r + 1] = &data[l + TS_SIZE]; + + if (++ccs >= csnew) + break; #else data[l + 3] &= 0x3f; // consider it decrypted now if (((ev_od & 0x40) >> 6) == 0) @@ -449,51 +1152,270 @@ cs_tsbbatch_odd[cs_fill_odd].len = payload_len; cs_fill_odd++; } -#endif + if (++ccs >= cs) break; +#endif } } #ifndef LIBDVBCSA - else - newRange = true; // other index, create hole + else + newRange = true; // other index, create hole #endif } - else - { // unencrypted + else // unencrypted + { // nothing, we don't create holes for unencrypted packets } } - if (algo[currIdx] == CA_ALGO_DES || Aes[currIdx] || algo[currIdx] == CA_ALGO_AES128) + + if (currIdx >= 0 && (parent->GetAlgo(currIdx) == CA_ALGO_DES || parent->DeCSAKeyArray[currIdx].Aes || parent->GetAlgo(currIdx) == CA_ALGO_AES128)) return true; + #ifndef LIBDVBCSA - if (r >= 0) - { // we have some range - if (ccs >= cs || force) + if (r >= 0) // we have some range + { + if (ccs >= csnew || force) { - if (GetKeyStruct(currIdx)) + if (currIdx >= 0 && parent->GetKeyStruct(currIdx)) { - int n = decrypt_packets(keys[currIdx], range); - if (n > 0) - return true; + if (wantsparity > 0) + { + bool bFastECM = false; + int caCaid = -1; + int caSid = -1; + int caPid = -1; + if (capmt && ENABLEFASTECM) + { + bool bdebuglogoCAID = false; + + parent->DeCSAKeyArray[currIdx].Get_FastECM_CAID(&caCaid); + parent->DeCSAKeyArray[currIdx].Get_FastECM_SID(&caSid); + parent->DeCSAKeyArray[currIdx].Get_FastECM_PID(&caPid); + + //DEBUGLOG("XXXXX CAID: %04X PID:%d currIdx:%d adapter_index:%d", caCaid, caPid, currIdx,adapter_index); + if (caCaid <= 0) + { + if (caPid >= 0) + { + uint16_t caidneu = capmt->GetCAIDFromPid(adapter_index, caPid, caSid); + if (caidneu > 0) + { + DEBUGLOG("%s: SetFastECMCaidSid adapter:%d CAID: %04X SID: %04X parity:%d pid:%d keyindex:%d", + __FUNCTION__, adapter_index, caidneu, caSid, wantsparity, curPid, currIdx); + parent->DeCSAKeyArray[currIdx].SetFastECMCaidSid(caidneu, caSid); + caCaid = caidneu; + bdebuglogoCAID = true; + } + } + } + + if (IsFastECMCAID(caCaid)) + { + if (bdebuglogoCAID) + { + DEBUGLOG("%s: using Fast ECM adapter:%d CAID: %04X SID: %04X parity:%d pid:%d keyindex:%d", + __FUNCTION__, adapter_index, caCaid, caSid, wantsparity, curPid, currIdx); + parent->DebugLogPidmap(); + } + bFastECM = true; + } + else //if (caCaid!=0x00) + { + if (bdebuglogoCAID) + { + DEBUGLOG("%s: not using Fast ECM adapter:%d CAID: %04X SID: %04X parity:%d pid:%d keyindex:%d", + __FUNCTION__, adapter_index, caCaid, caSid, wantsparity, curPid, currIdx); + parent->DebugLogPidmap(); + } + } + } + + if (bFastECM) + { + bool bdebugfull = false; + if (bdebugfull) + { + FAST_ECM fecm; + if (parent->DeCSAKeyArray[currIdx].Get_FastECM_struct(fecm)) + { + int oldparity = fecm.activparity2[curPid]; + if (oldparity != wantsparity) + { + DEBUGLOG("FULLDEBUG need new CW Parity - changed from old:%d new:%d pid:%d keyindex:%d adapter:%d", oldparity, wantsparity, curPid, currIdx, adapter_index); + } + } + } + + bool bfirsttimecheck; + bool bnextparityset; + bool bactivparitypatched; + + int oldparity = 0; + int iok = parent->DeCSAKeyArray[currIdx].Set_FastECM_CW_Parity(curPid, wantsparity, false, oldparity, bfirsttimecheck, bnextparityset, bactivparitypatched); + + if (bfirsttimecheck) DEBUGLOG("bfirsttimecheck pid:%d keyindex:%d adapter:%d", curPid, currIdx, adapter_index); + if (bnextparityset) DEBUGLOG("bnextparityset pid:%d keyindex:%d adapter:%d", curPid, currIdx, adapter_index); + if (bactivparitypatched) DEBUGLOG("bactivparitypatched pid:%d keyindex:%d adapter:%d", curPid, currIdx, adapter_index); + + //DEBUGLOG("%s: set_FastECM_CW_Parity ALL OK debugev_od:%d", __FUNCTION__, debugev_od); + if (oldparity != wantsparity) + { + DEBUGLOG("FastECM need new CW Parity - changed from old:%d new:%d pid:%d keyindex:%d adapter:%d", oldparity, wantsparity, curPid, currIdx, adapter_index); + } + if (iok == 0 && bFastECM) + { + bCW_Waiting = true; + cMutexLock lockstop(&mutexStopDecrypt); + ERRORLOG("%s: set_FastECM_CW_Parity MUST WAIT parity:%d pid:%d keyindex:%d adapter:%d len:%d", __FUNCTION__, + wantsparity, curPid, currIdx, adapter_index, len); + parent->DebugLogPidmap(); + int isleepcount = 0; + do + { + isleepcount++; + lockPIDMAPnew.UnLock(); + cCondWait::SleepMs(iSleep); + lockPIDMAPnew.ReLock(); + if (bAbort) + { + bCW_Waiting = false; + bAbort = false; + ERRORLOG("%s: bAbort parity wait adapter:%d", __FUNCTION__, adapter_index); + return false; + } + iok = parent->DeCSAKeyArray[currIdx].Set_FastECM_CW_Parity(curPid, wantsparity, false, oldparity, bfirsttimecheck, bnextparityset, bactivparitypatched); + if (bfirsttimecheck) DEBUGLOG("bfirsttimecheck pid:%d keyindex:%d adapter:%d", curPid, currIdx, adapter_index); + if (bnextparityset) DEBUGLOG("bnextparityset pid:%d keyindex:%d adapter:%d", curPid, currIdx, adapter_index); + if (bactivparitypatched) DEBUGLOG("bactivparitypatched pid:%d keyindex:%d adapter:%d", curPid, currIdx, adapter_index); + if (iok == 1) + { + sleeptime = starttime.Elapsed(); + ERRORLOG("%s: set_FastECM_CW_Parity MUST WAIT SUCCESS parity:%d pid:%d keyindex:%d adapter:%d len:%d time:%lld", __FUNCTION__, + wantsparity, curPid, currIdx, adapter_index, len, (lldcast)sleeptime); + } + else + { + if (TimerTimeout.TimedOut() || isleepcount > imaxSleep) + { + parent->DeCSAKeyArray[currIdx].Set_FastECM_CW_Parity(curPid, wantsparity, true, oldparity, bfirsttimecheck, bnextparityset, bactivparitypatched); //otherwise we sleep every time. + //InitFastEcmOnCaid(caCaid); + parent->DeCSAKeyArray[currIdx].Init_Parity2(false); + sleeptime = starttime.Elapsed(); + ERRORLOG("%s: set_FastECM_CW_Parity MUST WAIT TIMEOUT parity:%d pid:%d keyindex:%d adapter:%d len:%d time:%lld", __FUNCTION__, + wantsparity, curPid, currIdx, adapter_index, len, (lldcast)sleeptime); + iok = 1; + } + } + } while (iok == 0); + + bCW_Waiting = false; + blogfull = true; + sleeptime = starttime.Elapsed(); + } + else + { + } + } + else //nur ausgabe changed from usw... + { + FAST_ECM fecm; + if (parent->DeCSAKeyArray[currIdx].Get_FastECM_struct(fecm)) + { + int aparity2 = fecm.activparity2[curPid]; + int oldparity = aparity2; + parent->DeCSAKeyArray[currIdx].SetActiveParity2(curPid,wantsparity); + if (oldparity != wantsparity) + { + DEBUGLOG("need new CW Parity - changed from old:%d new:%d pid:%d keyindex:%d adapter:%d", oldparity, wantsparity, curPid, currIdx, adapter_index); + } + } + } + } + + if (rangenew) + { + int n = parent->DeCSAKeyArray[currIdx].Decrypt_packets(rangenew); + if (blogfull) + { + DEBUGLOG("%s: n:%d adapter:%d decrypt_packets len:%d", __FUNCTION__, n, adapter_index, len); + } + + if (n > 0) + { + return true; + } + else + { + DEBUGLOG("%s: decrypt_packets returns <= 0 n:%d adapter:%d parity:%d pid:%d keyindex:%d len:%d", __FUNCTION__, n, adapter_index, wantsparity, curPid, currIdx, len); + } + } } } } #else - if (GetKeyStruct(currIdx)) + if (currIdx >= 0 && wantsparity > 0 && parent->GetKeyStruct(currIdx) ) { if (cs_fill_even) { cs_tsbbatch_even[cs_fill_even].data = NULL; - dvbcsa_bs_decrypt(cs_key_even[currIdx], cs_tsbbatch_even, 184); + dvbcsa_bs_decrypt(parent->DeCSAKeyArray[currIdx].cs_key_even, cs_tsbbatch_even, 184); } if (cs_fill_odd) { cs_tsbbatch_odd[cs_fill_odd].data = NULL; - dvbcsa_bs_decrypt(cs_key_odd[currIdx], cs_tsbbatch_odd, 184); + dvbcsa_bs_decrypt(parent->DeCSAKeyArray[currIdx].cs_key_odd, cs_tsbbatch_odd, 184); } return true; } #endif return false; } + +void DeCSA::StopDecrypt(int adapter_index,int filter_num,int pid) +{ + if (adapter_index < 0 || adapter_index >= MAXADAPTER) + return; + + if (DeCSAArray[adapter_index].bCW_Waiting) + { + DEBUGLOG("decsa CW Waiting %s", __FUNCTION__); + DeCSAArray[adapter_index].bAbort = true; + cMutexLock lock(&DeCSAArray[adapter_index].mutexStopDecrypt); + DeCSAArray[adapter_index].bAbort = false; + DEBUGLOG("decsa CW Waiting Aborted %s", __FUNCTION__); + } + else + { + //DEBUGLOG("%s: decsa ist OK", __FUNCTION__); + } +#ifndef LIBDVBCSA + if (filter_num==1) + { + DEBUGLOG("DeCSA::StopDecrypt cardindex:%d pid:%d", adapter_index, pid); + int slot = -1; + if (DeCSAArray[adapter_index].AdapterPidMap.size() > 0) + { + map::iterator it; + for (it = DeCSAArray[adapter_index].AdapterPidMap.begin(); it != DeCSAArray[adapter_index].AdapterPidMap.end(); ++it) + { + //int ipid = it->first; + int iidx = it->second; //slotindex + int caPid = -1; + DeCSAKeyArray[iidx].Get_FastECM_PID(&caPid); + if (caPid >= 0) + { + if (caPid == pid) + { + DEBUGLOG("DeCSA::StopDecrypt cardindex:%d pid:%d slot:%d", adapter_index, pid,iidx); + slot = iidx; + break; + } + } + } + } + + Init_Parity(adapter_index, -1,slot,false); + } +#endif +} diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/DeCSA.h vdr-plugin-dvbapi-2.2.5+git20210627/DeCSA.h --- vdr-plugin-dvbapi-2.2.5+git20190727/DeCSA.h 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/DeCSA.h 2021-06-27 15:54:05.000000000 +0000 @@ -35,38 +35,127 @@ #include "openssl/aes.h" #endif +#include "DVBAPI.h" + #define MAX_CSA_PID 0x1FFF -#define MAX_CSA_IDX 16 +#define MAX_CSA_IDX 32 +#define MAXADAPTER 64 #define MAX_KEY_WAIT 25 // max seconds to consider a CW as valid -#include "DVBAPI.h" #include "CA.h" using namespace std; -class DeCSA +class DeCSA; +class DeCSAKey //Helper for FFdecsa { -private: - int cs; +public: + DeCSAKey(); + ~DeCSAKey(); + #ifndef LIBDVBCSA - unsigned char **range, *lastData; - void *keys[MAX_CSA_IDX]; + void* key; #else + struct dvbcsa_bs_key_s *cs_key_even; + struct dvbcsa_bs_key_s *cs_key_odd; + ca_descr_t *ca_descr; +#endif + + time_t cwSeen; // last time the CW for the related key was seen + time_t lastcwlog; + +#ifdef LIBSSL + void *csa_aes_key; +#endif + bool Aes; + uint32_t algo; + uint32_t des_key_schedule[2][32]; + + int index; + + bool CWExpired(); //return true if expired + bool GetorCreateKeyStruct(); +#ifdef LIBSSL + bool GetorCreateAesKeyStruct(); +#endif + void Des(uint8_t* data, unsigned char parity); + void Des_set_key(const unsigned char *cw, unsigned char parity); + bool Set_even_control_word(const unsigned char *even); + bool Set_odd_control_word(const unsigned char *odd); + +#ifndef LIBDVBCSA + bool Get_control_words(unsigned char *even, unsigned char *odd); + int Decrypt_packets(unsigned char **cluster); + void SetFastECMPid(int pid); + void Get_FastECM_CAID(int* caid); + void Get_FastECM_SID(int* caSid); + void Get_FastECM_PID(int* caPid); + bool Get_FastECM_struct(FAST_ECM& fecm); + void Init_Parity2(bool binitcsa = true); + bool SetFastECMCaidSid(int caid, int sid); + int Set_FastECM_CW_Parity(int pid, int parity, bool bforce, int& oldparity, bool& bfirsttimecheck, bool& bnextparityset, bool& bactivparitypatched); + void SetActiveParity2(int pid,int parity2); + void InitFastEcmOnCaid(int Caid); + bool GetActiveParity(int pid, int& aparity, int& aparity2); +#endif + + void SetAes(uint32_t usedAes); + uint32_t GetAes(); + + void SetAlgo(uint32_t usedAlgo); + uint32_t GetAlgo(); + + cMutex mutexKEY; +}; + +class DeCSAAdapter +{ +public: + DeCSAAdapter(); + ~DeCSAAdapter(); + + int cardindex; + + map AdapterPidMap; + + void Init_Parity(DeCSAKey *keys, int sid, int slot,bool bdelete); +#ifndef LIBDVBCSA + void SetDVBAPIPid(DeCSA* parent, int slot, int dvbapiPID); +#endif + void SetCaPid(int pid, int index); + int SearchPIDinMAP(int pid); +#ifndef LIBDVBCSA + int GetCaid(DeCSA* parent, int pid); +#endif + bool Decrypt(DeCSA* parent,unsigned char *data, int len, bool force); + void CancelWait(); + + cMutex mutexAdapter; + cMutex mutexDecrypt; + cMutex mutexStopDecrypt; + + int csnew; + +#ifndef LIBDVBCSA + unsigned char **rangenew; +#else + int cs; struct dvbcsa_bs_batch_s *cs_tsbbatch_even; struct dvbcsa_bs_batch_s *cs_tsbbatch_odd; - struct dvbcsa_bs_key_s *cs_key_even[MAX_CSA_IDX]; - struct dvbcsa_bs_key_s *cs_key_odd[MAX_CSA_IDX]; #endif -#ifdef LIBSSL - void *csa_aes_keys[MAX_CSA_IDX]; + + bool bCW_Waiting; + bool bAbort; +}; + +class DeCSA +{ +public: + DeCSAAdapter DeCSAArray[MAXADAPTER]; + DeCSAKey DeCSAKeyArray[MAX_CSA_IDX]; //maximum MAX_CSA_IDX crypted channesl over all adapters unsigned char *ivec[MAX_CSA_IDX]; -#endif - uint32_t des_key_schedule[MAX_CSA_IDX][2][32]; - uint32_t algo[MAX_CSA_IDX]; uint32_t cipher_mode[MAX_CSA_IDX]; - time_t cwSeen[MAX_CSA_IDX]; // last time the CW for the related key was seen - bool Aes[MAX_CSA_IDX]; - map, unsigned char> pidmap; + cMutex mutex; bool GetKeyStruct(int idx); bool GetKeyStructAes(int idx); @@ -79,15 +168,31 @@ DeCSA(); ~DeCSA(); bool Decrypt(uint8_t adapter_index, unsigned char *data, int len, bool force); - bool SetDescr(ca_descr_t *ca_descr, bool initial); + bool SetDescr(ca_descr_t *ca_descr, bool initial, int adapter_index); bool SetDescrAes(ca_descr_aes_t *ca_descr_aes, bool initial); bool SetCaPid(uint8_t adapter_index, ca_pid_t *ca_pid); void SetAlgo(uint32_t index, uint32_t usedAlgo); void SetAes(uint32_t index, bool usedAes); void SetCipherMode(uint32_t index, uint32_t usedCipherMode); bool SetData(ca_descr_data_t *ca_descr_data, bool initial); + + void StopDecrypt(int adapter_index,int filter_num,int pid); +#ifndef LIBDVBCSA + void Init_Parity(int cardindex, int sid, int slot,bool bdelete); + void SetDVBAPIPid(int adapter, int slot, int dvbapiPID); + void SetFastECMPid(int cardindex, int idx, int slot, int dvbapiPID); + void DebugLogPidmap(); + void InitFastEcmOnCaid(int Caid); + int GetCaid(uint8_t adapter_index, int pid); +#endif + uint32_t GetAlgo(int idx); + uint32_t GetAes(int idx); + void CancelWait(); }; +#ifndef LIBDVBCSA +extern bool IsFastECMCAID(int caCaid); +#endif extern DeCSA *decsa; #endif // ___DECSA_H diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPI.cpp vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPI.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPI.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPI.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -30,6 +30,7 @@ bool CheckExpiredCW = true; unsigned int AdapterIndexOffset = 0; +int ENABLEFASTECM=1; DVBAPI::DVBAPI(void) { @@ -151,6 +152,8 @@ strn0cpy(OSCamHost, Value, sizeof(OSCamHost)); else if (!strcasecmp(Name, CONFNAME_OSCAMPORT)) OSCamPort = atoi(Value); + else if (!strcasecmp(Name, CONFNAME_OSCAMFASTECM)) + ENABLEFASTECM = atoi(Value); else if (!strcasecmp(Name, CONFNAME_ADAPTERINDEXOFFSET)) { if (AdapterIndexOffset) diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPI.h vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPI.h --- vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPI.h 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPI.h 2021-06-27 15:54:05.000000000 +0000 @@ -19,6 +19,8 @@ #ifndef ___DVBAPI_H #define ___DVBAPI_H +extern int ENABLEFASTECM; + #include #include #include "SCCIAdapter.h" diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPISetup.cpp vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPISetup.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPISetup.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPISetup.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "DVBAPI.h" #include "DVBAPISetup.h" int LogLevel = 2; @@ -30,6 +31,8 @@ Add(new cMenuEditStrItem( *cString::sprintf("OSCam %s", tr("Host")), newOSCamHost, sizeof(newOSCamHost))); Add(new cMenuEditIntItem( *cString::sprintf("OSCam %s", tr("Port")), &newOSCamPort, 1, 0xffff)); Add(new cMenuEditIntItem( tr("Log level (0-3)"), &newLogLevel, 0, 3)); + newENABLEFASTECM = ENABLEFASTECM; + Add(new cMenuEditIntItem( tr("Enable FastECM"), &newENABLEFASTECM, 0, 1)); } void cMenuSetupDVBAPI::Store(void) @@ -39,4 +42,5 @@ SetupStore(CONFNAME_LOGLEVEL, LogLevel = newLogLevel); SetupStore(CONFNAME_OSCAMHOST, strn0cpy(OSCamHost, newOSCamHost, sizeof(OSCamHost))); SetupStore(CONFNAME_OSCAMPORT, OSCamPort = newOSCamPort); + SetupStore(CONFNAME_OSCAMFASTECM, ENABLEFASTECM = newENABLEFASTECM); } diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPISetup.h vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPISetup.h --- vdr-plugin-dvbapi-2.2.5+git20190727/DVBAPISetup.h 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/DVBAPISetup.h 2021-06-27 15:54:05.000000000 +0000 @@ -25,6 +25,7 @@ #define CONFNAME_OSCAMHOST "OSCamHost" #define CONFNAME_OSCAMPORT "OSCamPort" #define CONFNAME_ADAPTERINDEXOFFSET "AdapterIndexOffset" +#define CONFNAME_OSCAMFASTECM "OSCamFastECM" class cMenuSetupDVBAPI : public cMenuSetupPage { @@ -32,6 +33,7 @@ int newLogLevel; char newOSCamHost[HOST_NAME_MAX]; int newOSCamPort; + int newENABLEFASTECM; protected: virtual void Store(void); public: diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/FFdecsa/FFdecsa.c vdr-plugin-dvbapi-2.2.5+git20210627/FFdecsa/FFdecsa.c --- vdr-plugin-dvbapi-2.2.5+git20190727/FFdecsa/FFdecsa.c 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/FFdecsa/FFdecsa.c 2021-06-27 15:54:05.000000000 +0000 @@ -25,6 +25,45 @@ #include "FFdecsa.h" +//FASTECM START +#include +#include +uint64_t GetTick(void) +{ +#define MIN_RESOLUTION 5 // ms + static bool initialized = false; + static bool monotonic = false; + struct timespec tp; + if (!initialized) { + // check if monotonic timer is available and provides enough accurate resolution: + if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) { + //long Resolution = tp.tv_nsec; + // require a minimum resolution: + if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { + //dsyslog("cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution); + monotonic = true; + } + } + } + + initialized = true; + } + if (monotonic) + { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) + return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000; + monotonic = false; + // fall back to gettimeofday() + } + + struct timeval t; + if (gettimeofday(&t, NULL) == 0) + return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000; + return 0; +} +//FASTECM END + #ifndef NULL #define NULL 0 #endif @@ -165,6 +204,7 @@ struct csa_keys_t{ struct csa_key_t even; struct csa_key_t odd; + struct FAST_ECM fastecm; }; //-----stream cypher @@ -495,16 +535,17 @@ //-----key structure void *get_key_struct(void){ - struct csa_keys_t *keys=(struct csa_keys_t *)MALLOC(sizeof(struct csa_keys_t)); + struct csa_keys_t *keys=new struct csa_keys_t; if(keys) { static const unsigned char pk[8] = { 0,0,0,0,0,0,0,0 }; set_control_words(keys,pk,pk); + Init_FastECM(keys,true); } return keys; } void free_key_struct(void *keys){ - return FREE(keys); + delete (struct csa_keys_t*)keys; } //-----set control words @@ -543,10 +584,14 @@ } void set_even_control_word(void *keys, const unsigned char *pk){ + ((struct csa_keys_t *)keys)->fastecm.nextparity = 1; + ((struct csa_keys_t *)keys)->fastecm.evenparityTime = GetTick(); schedule_key(&((struct csa_keys_t *)keys)->even,pk); } void set_odd_control_word(void *keys, const unsigned char *pk){ + ((struct csa_keys_t *)keys)->fastecm.nextparity = 2; + ((struct csa_keys_t *)keys)->fastecm.oddparityTime = GetTick(); schedule_key(&((struct csa_keys_t *)keys)->odd,pk); } @@ -642,6 +687,13 @@ if(pkt[3]&0x20){ // incomplete packet offset=4+pkt[4]+1; len=188-offset; + if(len<=0){ //FASTECM - from: https://gitlab.com/berdyansk/astra-sm/blob/cac89dc2d24ff2095477d0c1c94e05b87766c8bd/modules/softcam/FFdecsa/FFdecsa.c + // skip broken packet + DBG(fprintf(stderr,"skip broken pkt %p (can_advance is %i)\n",pkt,can_advance)); + advanced+=can_advance; + stat_no_scramble++; + break; + } n=len>>3; residue=len-(n<<3); if(n==0){ // decrypted==encrypted! @@ -909,3 +961,188 @@ return advanced; } + +//FASTECM START +void get_FastECM_SID(void *keys, int* sid) +{ + *sid = 0; + if (keys) + { + *sid = ((struct csa_keys_t *)keys)->fastecm.csaSid; + } +} + +void get_FastECM_PID(void *keys, int* pid) +{ + *pid = 0; + if (keys) + { + *pid = ((struct csa_keys_t *)keys)->fastecm.csaPid; + } +} + +void get_FastECM_CAID(void *keys, int* caid) +{ + *caid = 0; + if (keys) + { + *caid = ((struct csa_keys_t *)keys)->fastecm.csaCaid; + } +} + +void setFastECMPid(void *keys, int pid) +{ + if (keys) + { + ((struct csa_keys_t *)keys)->fastecm.csaPid = pid; + //((struct csa_keys_t *)keys)->csaSid = 0; + //((struct csa_keys_t *)keys)->csaCaid = 0; + } +} + +void setFastECMCaidSid(void *keys, int caid, int sid) +{ + if (keys) + { + ((struct csa_keys_t *)keys)->fastecm.csaCaid = caid; + ((struct csa_keys_t *)keys)->fastecm.csaSid = sid; + } +} + +struct FAST_ECM* get_FastECM_struct(void *keys) +{ + struct csa_keys_t * sk = (struct csa_keys_t *)keys; + FAST_ECM* fecm = &sk->fastecm; + return fecm; +} + +void Init_FastECM(void *keys,bool binitcsa) +{ + if (keys) + { + ((struct csa_keys_t *)keys)->fastecm.oddparityTime = 0; + ((struct csa_keys_t *)keys)->fastecm.evenparityTime = 0; + ((struct csa_keys_t *)keys)->fastecm.nextparity = 0; + + ((struct csa_keys_t *)keys)->fastecm.activparity.clear(); + ((struct csa_keys_t *)keys)->fastecm.activparity2.clear(); + + if (binitcsa) + { + ((struct csa_keys_t *)keys)->fastecm.csaPid = 0; + ((struct csa_keys_t *)keys)->fastecm.csaSid = 0; + ((struct csa_keys_t *)keys)->fastecm.csaCaid = 0; + } + } +} + +void getActiveParity(void *keys, int pid, int& aparity, int& aparity2) +{ + aparity = 0; + aparity2 = 0; + if (!keys) return; + if (pid<=0) return; + + struct csa_keys_t * skeys = (struct csa_keys_t *)keys; + + std::map::iterator it; + it = skeys->fastecm.activparity.find(pid); + if (it != skeys->fastecm.activparity.end()) + { + it->first; //pid + aparity = it->second; //parity + } + + it = skeys->fastecm.activparity2.find(pid); + if (it != skeys->fastecm.activparity2.end()) + { + it->first; //pid + aparity2 = it->second; //parity + } +} + +int set_FastECM_CW_Parity(void *keys,int pid, int parity, bool bforce, int& oldparity, bool& bfirsttimecheck, bool& bnextparityset, bool& bactivparitypatched) //XXONURCW +{ + bfirsttimecheck = false; + bnextparityset = false; + bactivparitypatched = false; + + oldparity = parity; + if (!keys) return 1; + if (pid<=0) return 1; + + struct csa_keys_t * skeys = (struct csa_keys_t *)keys; + + oldparity = skeys->fastecm.activparity2[pid]; + skeys->fastecm.activparity2[pid] = parity; + + //wait until we got both + if (skeys->fastecm.oddparityTime == 0 || + skeys->fastecm.evenparityTime == 0) + return 1; + + if (bforce) + { + skeys->fastecm.activparity[pid] = parity; + return 1; + } + + int aparity = 0; + int aparity2 = 0; + getActiveParity(keys, pid, aparity, aparity2); + + if (aparity == 0) //firsttime check + { + bfirsttimecheck = true; + aparity = parity; + skeys->fastecm.activparity[pid] = parity; + if (skeys->fastecm.nextparity == parity) + { + //das nächste ist die größere time + if (skeys->fastecm.evenparityTime > 0 && skeys->fastecm.evenparityTime >= skeys->fastecm.oddparityTime) + { + skeys->fastecm.nextparity = 1; + bnextparityset = true; + } + else if (skeys->fastecm.oddparityTime > 0 && skeys->fastecm.oddparityTime >= skeys->fastecm.evenparityTime) + { + skeys->fastecm.nextparity = 2; + bnextparityset = true; + } + + //if (parity == 1) skeys->fastecm.nextparity = 2; + //else if (parity == 2) skeys->fastecm.nextparity = 1; + bnextparityset = true; + } + } + + if (aparity != parity) //parity changed + { + if (skeys->fastecm.nextparity == parity) //everything ok + { + skeys->fastecm.activparity[pid] = parity; + return 1; + } + else + { + //on decrypt start, even and odd comes together. sky germany only send one CW, other channels send both every time + //in that case both paritys are correct? + if (skeys->fastecm.nextparity > 0) + { + uint64_t delta = skeys->fastecm.oddparityTime - skeys->fastecm.evenparityTime; + if (skeys->fastecm.evenparityTime > skeys->fastecm.oddparityTime ) + delta = skeys->fastecm.evenparityTime - skeys->fastecm.oddparityTime; + if (delta < 500) + { + bactivparitypatched = true; + skeys->fastecm.activparity[pid] = parity; + return 1; + } + } + + return 0; //must wait + } + } + return 1; +} +//FASTECM END diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/FFdecsa/FFdecsa.h vdr-plugin-dvbapi-2.2.5+git20210627/FFdecsa/FFdecsa.h --- vdr-plugin-dvbapi-2.2.5+git20190727/FFdecsa/FFdecsa.h 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/FFdecsa/FFdecsa.h 2021-06-27 15:54:05.000000000 +0000 @@ -21,6 +21,21 @@ #ifndef FFDECSA_H #define FFDECSA_H +//FASTECM helper struct +#include +#include +struct FAST_ECM { + int nextparity; //0 undefinded, 1 even, 2 odd + uint64_t evenparityTime; + uint64_t oddparityTime; + int csaSid; + int csaCaid; + int csaPid; + + std::map activparity; //pid , activeparity, //0 undefinded, 1 even, 2 odd + std::map activparity2; //pid , activeparity, //0 undefinded, 1 even, 2 odd +}; + //----- public interface // -- how many packets can be decrypted at the same time @@ -52,11 +67,24 @@ void set_odd_control_word(void *keys, const unsigned char *odd); // -- get control words, 8 bytes each -//void get_control_words(void *keys, unsigned char *even, unsigned char *odd); +void get_control_words(void *keys, unsigned char *even, unsigned char *odd); // -- decrypt many TS packets // This interface is a bit complicated because it is designed for maximum speed. // Please read doc/how_to_use.txt. int decrypt_packets(void *keys, unsigned char **cluster); +//FASTECM start +int set_FastECM_CW_Parity(void *keys, int pid, int parity,bool bforce, int& oldparity, bool& bfirsttimecheck, bool& bnextparityset, bool& bactivparitypatched); +void Init_FastECM(void *keys, bool binitcsa); +void setFastECMCaidSid(void *keys, int caid, int sid); +void setFastECMPid(void *keys, int pid); +void get_FastECM_SID(void *keys, int* sid); +void get_FastECM_PID(void *keys, int* pid); +void get_FastECM_CAID(void *keys, int* caid); +struct FAST_ECM* get_FastECM_struct(void *keys); +void getActiveParity(void *keys, int pid, int& aparity, int& aparity2); +uint64_t GetTick(void); +//FASTECM end + #endif diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/Filter.cpp vdr-plugin-dvbapi-2.2.5+git20210627/Filter.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/Filter.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/Filter.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -32,7 +32,11 @@ if ((unsigned) pid < MAX_CSA_PID) { DEBUGLOG("%s: adapter=%d set FILTER pid=%04X start=%d, demux=%d, filter=%d", __FUNCTION__, adapter_index, pid, start, demux, num); - +#ifndef LIBDVBCSA + int inum = num; + if (decsa && inum==1) + decsa->SetDVBAPIPid(adapter_index, demux, pid); +#endif vector *flt = pidmap[make_pair(adapter_index, pid)]; vector::iterator it; if (start == 1 && filter && mask) diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/README vdr-plugin-dvbapi-2.2.5+git20210627/README --- vdr-plugin-dvbapi-2.2.5+git20190727/README 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/README 2021-06-27 15:54:05.000000000 +0000 @@ -6,6 +6,7 @@ 5. Plugin and OSCam setup 6. Commandline options 7. Note for a plugin developers +8. Fast ECM ----------------------------------------------------------------------------- 1. Introduction @@ -180,3 +181,25 @@ You can find the explanation in the following pull requests: https://github.com/manio/vdr-plugin-dvbapi/pull/104 https://github.com/manio/vdr-plugin-dvbapi/pull/105 + +----------------------------------------------------------------------------- +8. Fast ECM +----------------------------------------------------------------------------- + +Description: +CW Changes usually come 5 seconds before they are needed (enough time to +decrypt). Some Providers (Sky Germany) are sending CW changes only 600 ms +before it should be used in Data Stream. + +When decryption is delayed the new CW arrives to late and the decryption +fails. Fast ECM can hold the data stream up to 2500ms until the CW is +available (odd/even check). +While this works very good for recordings, live video/audio will stop +for a brief moment (but usually only for the first time fastecm kicks in). + +VDR compatibility: +Fast ECM needs 16 Megabyte Buffer to work perfect. +you should patch this in vdr code in file dvbdevice.c, Function cDvbDevice::OpenDvr +tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); should be +tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(16), CardIndex() + 1); +Maybe this will get default in later vdr releases (>2.4.4). diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/SCCAMSlot.cpp vdr-plugin-dvbapi-2.2.5+git20210627/SCCAMSlot.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/SCCAMSlot.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/SCCAMSlot.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -301,11 +301,18 @@ { DEBUGLOG("%d.%d stop decrypt", cardIndex, slot); capmt->ProcessSIDRequest(cardIndex, sid, ca_lm, NULL, 0); +#ifndef LIBDVBCSA + if (decsa) + decsa->Init_Parity(cardIndex, sid, -1, true); +#endif } else if (ci_cmd == 0x01 || (ci_cmd == -1 && sid != 0 && (ca_lm == 0x03 || ca_lm == 0x04 || ca_lm == 0x05))) { INFOLOG("%d.%d set CAM decrypt (SID %d (0x%04X), caLm %d, HasCaDescriptors %d)", cardIndex, slot, sid, sid, ca_lm, HasCaDescriptors); - +#ifndef LIBDVBCSA + if (decsa) + decsa->Init_Parity(cardIndex, sid, -1, false); +#endif if (!HasCaDescriptors) { vdr_caPMT = NULL; @@ -326,6 +333,14 @@ decsaFillControl.Reset(); } +void SCCAMSlot::StopDecrypting(void) +{ + if (decsa) + decsa->CancelWait(); + + cCamSlot::StopDecrypting(); +} + DeCSAFillControl::DeCSAFillControl(int MaxWaterMark, int Timeout, int DataInterval) { maxWaterMark = MaxWaterMark; diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/SCCAMSlot.h vdr-plugin-dvbapi-2.2.5+git20210627/SCCAMSlot.h --- vdr-plugin-dvbapi-2.2.5+git20190727/SCCAMSlot.h 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/SCCAMSlot.h 2021-06-27 15:54:05.000000000 +0000 @@ -97,6 +97,7 @@ virtual const char *GetCamName(void); bool ProvidesCa(const int *CaSystemIds); virtual void StartDecrypting(void); + virtual void StopDecrypting(void); }; #endif // ___SCCAMSLOT_H diff -Nru vdr-plugin-dvbapi-2.2.5+git20190727/SocketHandler.cpp vdr-plugin-dvbapi-2.2.5+git20210627/SocketHandler.cpp --- vdr-plugin-dvbapi-2.2.5+git20190727/SocketHandler.cpp 2019-07-27 08:26:48.000000000 +0000 +++ vdr-plugin-dvbapi-2.2.5+git20210627/SocketHandler.cpp 2021-06-27 15:54:05.000000000 +0000 @@ -271,7 +271,7 @@ ca_descr.index = ntohl(ca_descr.index); ca_descr.parity = ntohl(ca_descr.parity); decsa->SetAes(ca_descr_aes.index, false); - decsa->SetDescr(&ca_descr, false); + decsa->SetDescr(&ca_descr, false, adapter_index); DEBUGLOG("%s: Got CA_SET_DESCR request, adapter_index=%d, index=%x", __FUNCTION__, adapter_index, ca_descr.index); } else if (*request == CA_SET_DESCR_AES)