diff --git a/CMakeLists.txt b/CMakeLists.txt index 63c7592c..251b44c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -454,6 +454,22 @@ endif (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1) # Manage config.h based on command line parameters # Manipulate config file based on given parameters and read unset parameters +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_EMU OUTPUT_VARIABLE CONFIG_WITH_EMU OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) + add_definitions ("-DWITH_EMU") + set (WITH_EMU "1") + message (STATUS " EMU is added by config compiling with EMU") +endif (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) + +if (WITH_EMU) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_SOFTCAM OUTPUT_VARIABLE CONFIG_WITH_SOFTCAM OUTPUT_STRIP_TRAILING_WHITESPACE) + if (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1) + add_definitions ("-DWITH_SOFTCAM") + set (WITH_SOFTCAM "1") + message (STATUS " SOFTCAM is added by config linking SoftCam.Key") + endif (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1) +endif (WITH_EMU) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --show-valid OUTPUT_VARIABLE config_vars_string OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX MATCHALL "[A-Z0-9_]+" config_vars ${config_vars_string}) @@ -794,6 +810,24 @@ endif( HAVE_LIBRT OR HAVE_LIBRT_STATIC) #-------------------------------------------------------------------------------- +if (NOT OSCamOperatingSystem MATCHES "Mac OS X") +if (NOT DEFINED ENV{ANDROID_NDK}) +if (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) + if (WITH_SOFTCAM) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process (COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) + else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process (COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) + endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default") + if (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,noexecstack") + endif (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") + endif (WITH_SOFTCAM) +endif (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) +endif (NOT DEFINED ENV{ANDROID_NDK}) +endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + #----------------------- installation ----------------------------- file (GLOB config_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/oscam.*") @@ -901,4 +935,11 @@ if (HAVE_LIBDVBCSA) endif(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) endif (HAVE_LIBDVBCSA) +if (WITH_EMU) + message (STATUS " Compile with EMU support") + if (WITH_SOFTCAM) + message (STATUS " SoftCam.Key will be linked as well") + endif (WITH_SOFTCAM) +endif (WITH_EMU) + message (STATUS "") diff --git a/Makefile b/Makefile index de0270e7..60d81255 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,9 @@ ifeq "$(shell ./config.sh --enabled WITH_SSL)" "Y" override USE_SSL=1 override USE_LIBCRYPTO=1 endif +ifeq "$(shell ./config.sh --enabled WITH_EMU)" "Y" + override USE_LIBCRYPTO=1 +endif ifdef USE_SSL override USE_LIBCRYPTO=1 endif @@ -300,6 +303,32 @@ SRC-$(CONFIG_CS_CACHEEX) += module-cccam-cacheex.c SRC-$(CONFIG_MODULE_CCCAM) += module-cccam.c SRC-$(CONFIG_MODULE_CCCSHARE) += module-cccshare.c SRC-$(CONFIG_MODULE_CONSTCW) += module-constcw.c +SRC-$(CONFIG_WITH_EMU) += module-emulator.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-osemu.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-biss.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-cryptoworks.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-director.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-irdeto.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-nagravision.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-omnicrypt.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-powervu.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-viaccess.c +ifeq "$(CONFIG_WITH_EMU)" "y" +ifeq "$(CONFIG_WITH_SOFTCAM)" "y" +UNAME := $(shell uname -s) +ifneq ($(UNAME),Darwin) +ifndef ANDROID_NDK +ifndef ANDROID_STANDALONE_TOOLCHAIN +TOUCH_SK := $(shell touch SoftCam.Key) +override LDFLAGS += -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default +ifneq ($(uname_S),Cygwin) +override LDFLAGS += -Wl,-z,noexecstack +endif +endif +endif +endif +endif +endif SRC-$(CONFIG_CS_CACHEEX) += module-csp.c SRC-$(CONFIG_CW_CYCLE_CHECK) += module-cw-cycle-check.c SRC-$(CONFIG_WITH_AZBOX) += module-dvbapi-azbox.c diff --git a/config.h b/config.h index 005a3cc1..13b7ab50 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,8 @@ #ifndef CONFIG_H_ #define CONFIG_H_ +#define WITH_EMU 1 +#define WITH_SOFTCAM 1 #define WEBIF 1 #define WEBIF_LIVELOG 1 #define WEBIF_JQUERY 1 diff --git a/config.sh b/config.sh index d0f5add2..27630aa4 100755 --- a/config.sh +++ b/config.sh @@ -1,6 +1,6 @@ #!/bin/sh -addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON" +addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_EMU WITH_SOFTCAM" protocols="MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY" readers="READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT" card_readers="CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS" @@ -26,6 +26,8 @@ CONFIG_WITH_LB=y # CONFIG_CLOCKFIX=n # CONFIG_IPV6SUPPORT=n # CONFIG_WITH_ARM_NEON=n +CONFIG_WITH_EMU=y +CONFIG_WITH_SOFTCAM=y # CONFIG_MODULE_CAMD33=n CONFIG_MODULE_CAMD35=y CONFIG_MODULE_CAMD35_TCP=y @@ -295,13 +297,16 @@ get_opts() { update_deps() { # Calculate dependencies - enabled_any $(get_opts readers) $(get_opts card_readers) && enable_opt WITH_CARDREADER >/dev/null - disabled_all $(get_opts readers) $(get_opts card_readers) && disable_opt WITH_CARDREADER >/dev/null + enabled_any $(get_opts readers) $(get_opts card_readers) WITH_EMU && enable_opt WITH_CARDREADER >/dev/null + disabled_all $(get_opts readers) $(get_opts card_readers) WITH_EMU && disable_opt WITH_CARDREADER >/dev/null disabled WEBIF && disable_opt WEBIF_LIVELOG >/dev/null disabled WEBIF && disable_opt WEBIF_JQUERY >/dev/null enabled MODULE_CCCSHARE && enable_opt MODULE_CCCAM >/dev/null enabled_any CARDREADER_DB2COM CARDREADER_MP35 CARDREADER_SC8IN1 CARDREADER_STINGER && enable_opt CARDREADER_PHOENIX >/dev/null enabled CS_CACHEEX_AIO && enable_opt CS_CACHEEX >/dev/null + enabled WITH_EMU && enable_opt READER_VIACCESS >/dev/null + enabled WITH_EMU && enable_opt MODULE_NEWCAMD >/dev/null + disabled WITH_EMU && disable_opt WITH_SOFTCAM >/dev/null } list_config() { @@ -351,9 +356,9 @@ list_config() { not_have_flag USE_LIBCRYPTO && echo "CONFIG_LIB_AES=y" || echo "# CONFIG_LIB_AES=n" enabled MODULE_CCCAM && echo "CONFIG_LIB_RC6=y" || echo "# CONFIG_LIB_RC6=n" not_have_flag USE_LIBCRYPTO && enabled MODULE_CCCAM && echo "CONFIG_LIB_SHA1=y" || echo "# CONFIG_LIB_SHA1=n" - enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" - enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" - not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" + enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX WITH_EMU && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" + enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA WITH_EMU && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" + not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN WITH_EMU && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_MDC2=y" || echo "# CONFIG_LIB_MDC2=n" enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_FAST_AES=y" || echo "# CONFIG_LIB_FAST_AES=n" enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_SHA256=y" || echo "# CONFIG_LIB_SHA256=n" @@ -470,6 +475,8 @@ menu_addons() { CLOCKFIX "Clockfix (disable on old systems!)" $(check_test "CLOCKFIX") \ IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \ WITH_ARM_NEON "ARM NEON (SIMD/MPE) support" $(check_test "WITH_ARM_NEON") \ + WITH_EMU "Emulator support" $(check_test "WITH_EMU") \ + WITH_SOFTCAM "Built-in SoftCam.Key" $(check_test "WITH_SOFTCAM") \ 2> ${tempfile} opt=${?} @@ -714,7 +721,8 @@ do #get revision based on globals.h (not a git repository) revision=`grep '# define CS_SVN_VERSION' globals.h | cut -d\" -f2` fi - echo $revision + emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'` + echo $revision-$emuversion break ;; '-c'|'--oscam-commit') diff --git a/globals.h b/globals.h index 9b199a55..edc32f96 100644 --- a/globals.h +++ b/globals.h @@ -404,7 +404,7 @@ #define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values! // Support for multiple CWs per channel and other encryption algos -//#define WITH_EXTENDED_CW 1 +#define WITH_EXTENDED_CW 1 #define MAX_ECM_SIZE 1024 #define MAX_EMM_SIZE 1024 @@ -1929,6 +1929,7 @@ struct s_reader // contains device info, reader info and card info #ifdef WITH_EMU FTAB emu_auproviders; // AU providers for Emu reader int8_t emu_datecodedenabled; // date-coded keys for BISS + LLIST *ll_biss2_rsa_keys; // BISS2 RSA keys - Read from external PEM files #endif uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm LLIST *emmstat; // emm stats @@ -2453,6 +2454,10 @@ struct s_config #else #define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2 #endif +#endif +#ifdef WITH_EMU + uint32_t emu_stream_ecm_delay; + int8_t emu_stream_emm_enabled; #endif int32_t max_cache_time; // seconds ecms are stored in ecmcwcache diff --git a/module-dvbapi.c b/module-dvbapi.c index c71ff4ce..8a2d20d7 100644 --- a/module-dvbapi.c +++ b/module-dvbapi.c @@ -2657,6 +2657,8 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked, er->vpid = demux[demux_id].ECMpids[pid].VPID; er->pmtpid = demux[demux_id].pmtpid; er->onid = demux[demux_id].onid; + er->tsid = demux[demux_id].tsid; + er->ens = demux[demux_id].ens; er->msgid = msgid; #ifdef WITH_STAPI5 @@ -2704,19 +2706,30 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked, if(caid_is_fake(demux[demux_id].ECMpids[pid].CAID) || caid_is_biss_fixed(demux[demux_id].ECMpids[pid].CAID)) { int32_t j, n; - er->ecmlen = 5; + er->ecmlen = 7; er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81 er->ecm[1] = 0x00; - er->ecm[2] = 0x02; + er->ecm[2] = 0x04; i2b_buf(2, er->srvid, er->ecm + 3); + i2b_buf(2, er->pmtpid, er->ecm + 5); - for(j = 0, n = 5; j < demux[demux_id].STREAMpidcount; j++, n += 2) + for(j = 0, n = 7; j < demux[demux_id].STREAMpidcount; j++, n += 2) { i2b_buf(2, demux[demux_id].STREAMpids[j], er->ecm + n); er->ecm[2] += 2; er->ecmlen += 2; } + er->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging + er->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid + + i2b_buf(2, er->tsid, er->ecm + er->ecmlen); // place tsid after the last stream pid + i2b_buf(2, er->onid, er->ecm + er->ecmlen + 2); // place onid right after tsid + i2b_buf(4, er->ens, er->ecm + er->ecmlen + 4); // place namespace at the end of the ecm + + er->ecm[2] += 8; + er->ecmlen += 8; + cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X", demux_id, pid, diff --git a/module-emulator-biss.c b/module-emulator-biss.c new file mode 100644 index 00000000..a6b0c9e6 --- /dev/null +++ b/module-emulator-biss.c @@ -0,0 +1,883 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "module-emulator-osemu.h" +#include "module-emulator-biss.h" +#include "oscam-aes.h" +#include "oscam-string.h" +#include +#include +//#include +#include + +// DVB-CISSA v1 IV as defined in ETSI TS 103 127 +static const uint8_t dvb_cissa_iv[16] = +{ + 0x44, 0x56, 0x42, 0x54, 0x4D, 0x43, 0x50, 0x54, + 0x41, 0x45, 0x53, 0x43, 0x49, 0x53, 0x53, 0x41 +}; + +static void unify_orbitals(uint32_t *namespace) +{ + // Unify orbitals to produce same namespace among users + // Set positions according to http://satellites-xml.org + + uint16_t pos = (*namespace & 0x0FFF0000) >> 16; + + switch (pos) + { + case 29: // Rascom QAF 1R + case 31: // Eutelsat 3B + { + pos = 30; + break; + } + + case 49: + case 50: // SES 5 + { + pos = 48; // Astra 4A + break; + } + + case 215: + { + pos = 216; // Eutelsat 21B + break; + } + + case 285: // Astra 2E + { + pos = 282; // Astra 2F/2G + break; + } + + case 328: // Intelsat 28 + case 329: + case 331: // Eutelsat 33C + { + pos = 330; + break; + } + + case 359: // Eutelsat 36B + case 361: // Express AMU1 + { + pos = 360; + break; + } + + case 451: // Intelsat 904 + { + pos = 450; // Intelsat 12 + break; + } + + case 550: + case 551: // G-Sat 8/16 + { + pos = 549; // Yamal 402 + break; + } + + case 748: + case 749: // ABS 2A + { + pos = 750; + break; + } + + case 848: // Horizons 2 + case 852: // Intelsat 15 + { + pos = 850; + break; + } + + case 914: // Mesasat 3a + { + pos = 915; // Mesasat 3/3b + break; + } + + case 934: // G-Sat 17 + case 936: // Insat 4B + { + pos = 935; // G-Sat 15 + break; + } + + case 3600 - 911: // Nimiq 6 + { + pos = 3600 - 910; // Galaxy 17 + break; + } + + case 3600 - 870: // SES 2 + case 3600 - 872: // TKSat 1 + { + pos = 3600 - 871; + break; + } + + case 3600 - 432: // Sky Brasil 1 + case 3600 - 430: // Intelsat 11 + { + pos = 3600 - 431; + break; + } + + case 3600 - 376: // Telstar 11N + case 3600 - 374: // NSS 10 + { + pos = 3600 - 375; + break; + } + + case 3600 - 359: // Hispasat 36W-1 + { + pos = 3600 - 360; // Eutelsat 36 West A + break; + } + + case 3600 - 81: // Eutelsat 8 West B + { + pos = 3600 - 80; + break; + } + + case 3600 - 73: // Eutelsat 7 West A + case 3600 - 72: + case 3600 - 71: + { + pos = 3600 - 70; // Nilesat 201 + break; + } + + case 3600 - 10: // Intelsat 10-02 + case 3600 - 9: // Thor 6 + case 3600 - 7: // Thor 7 + case 3600 - 6: // Thor 7 + { + pos = 3600 - 8; // Thor 5 + break; + } + } + + *namespace = (*namespace & 0xF000FFFF) | (pos << 16); +} + +static void annotate(char *buf, uint8_t len, const uint8_t *ecm, uint16_t ecmLen, + uint32_t hash, int8_t isNamespaceHash, int8_t datecoded) +{ + // Extract useful information to append to the "Example key ..." message. + // + // For feeds, the orbital position & frequency are usually embedded in the namespace. + // See https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/frontend.cpp#L476 + // hash = (sat.orbital_position << 16); + // hash |= ((sat.frequency/1000)&0xFFFF)|((sat.polarisation&1) << 15); + // + // If the onid & tsid appear to be a unique DVB identifier, enigma2 strips the frequency + // from our namespace. See https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/scan.cpp#L55 + // In that case, our annotation contains the onid:tsid:sid triplet in lieu of frequency. + // + // For the universal case, we print the number of elementary stream pids & pmtpid. + // The sid and current time are included for all. Examples: + // + // F 1A2B3C4D 00000000 XXXXXXXXXXXXXXXX ; 110.5W 12345H sid:0001 added: 2017-10-17 @ 13:14:15 // namespace + // F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; 33.5E ABCD:9876:1234 added: 2017-10-17 @ 13:14:15 // stripped namespace + // F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; av:5 pmt:0134 sid:0001 added: 2017-10-17 @ 13:14:15 // universal + + uint8_t pidcount; + uint16_t frequency, degrees, pmtpid, srvid, tsid, onid; + uint32_t ens; + char compass, polarisation, timeStr1[9], timeStr2[19]; + + if (datecoded) + { + date_to_str(timeStr1, sizeof(timeStr1), 4, 3); + } + else + { + snprintf(timeStr1, sizeof(timeStr1), "00000000"); + } + + date_to_str(timeStr2, sizeof(timeStr2), 0, 2); + + if (isNamespaceHash) // Namespace hash + { + ens = b2i(4, ecm + ecmLen - 4); // Namespace will be the last 4 bytes of the ecm + degrees = (ens >> 16) & 0x0FFF; // Remove not-a-pid flag + + if (degrees > 1800) + { + degrees = 3600 - degrees; + compass = 'W'; + } + else + { + compass = 'E'; + } + + if (0 == (ens & 0xFFFF)) // Stripped namespace hash + { + srvid = b2i(2, ecm + 3); + tsid = b2i(2, ecm + ecmLen - 8); + onid = b2i(2, ecm + ecmLen - 6); + // Printing degree sign "\u00B0" requires c99 standard + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %04X:%04X:%04X added: %s", + hash, timeStr1, degrees / 10.0, compass, onid, tsid, srvid, timeStr2); + } + else // Full namespace hash + { + srvid = b2i(2, ecm + 3); + frequency = ens & 0x7FFF; // Remove polarity bit + polarisation = ens & 0x8000 ? 'V' : 'H'; + // Printing degree sign "\u00B0" requires c99 standard + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %5d%c sid:%04X added: %s", + hash, timeStr1, degrees / 10.0, compass, frequency, polarisation, srvid, timeStr2); + } + } + else // Universal hash + { + srvid = b2i(2, ecm + 3); + pmtpid = b2i(2, ecm + 5); + pidcount = (ecmLen - 15) / 2; // video + audio pids count + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; av:%d pmt:%04X sid:%04X added: %s", + hash, timeStr1, pidcount, pmtpid, srvid, timeStr2); + } +} + +static int8_t is_common_hash(uint32_t hash) +{ + // Check universal hash against a number of commnon universal + // hashes in order to warn users about potential key clashes + + switch (hash) + { + case 0xBAFCD9FD: // 0001 0020 0200 1010 1020 (most common hash) + return 1; + case 0xA6A4FBD4: // 0001 0800 0200 1010 1020 + return 1; + case 0xEFAB7A4D: // 0001 0800 1010 1020 0200 + return 1; + case 0x83FA15D1: // 0001 0020 0134 0100 0101 + return 1; + case 0x58934C38: // 0001 0800 1010 1020 1030 0200 + return 1; + case 0x2C3CEC17: // 0001 0020 0134 0100 + return 1; + case 0x73DF7F7E: // 0001 0020 0200 1010 1020 1030 + return 1; + case 0xAFA85BC8: // 0001 0020 0021 0022 0023 + return 1; + case 0x8C51F31D: // 0001 0800 0200 1010 1020 1030 1040 + return 1; + case 0xE2F9BD29: // 0001 0800 0200 1010 1020 1030 + return 1; + case 0xB9EBE0FF: // 0001 0100 0200 1010 1020 (less common hash) + return 1; + default: + return 0; + } +} + +static int8_t is_valid_namespace(uint32_t namespace) +{ + // Note to developers: + // If we ever have a satellite at 0.0E, edit to allow stripped namespace + // '0xA0000000' with an additional test on tsid and onid being != 0 + + uint16_t orbital, frequency; + + orbital = (namespace >> 16) & 0x0FFF; + frequency = namespace & 0x7FFF; + + if ((namespace & 0xF0000000) != 0xA0000000) return 0; // Value isn't flagged as namespace + if ((namespace & 0x0FFFFFFF) == 0x00000000) return 0; // Empty namespace + if (orbital > 3599) return 0; // Allow only DVB-S + if (frequency == 0) return 1; // Stripped namespace + if (frequency >= 3400 && frequency <= 4200) return 1; // Super extended C band + if (frequency >= 10700 && frequency <= 12750) return 1; // Ku band Europe + + return 0; +} + +static int8_t get_sw(uint32_t provider, uint8_t *sw, uint8_t sw_length, int8_t dateCoded, int8_t printMsg) +{ + // If date-coded keys are enabled in the webif, this function evaluates the expiration date + // of the found keys. Expired keys are not sent to the calling function. If date-coded keys + // are disabled, then every key is sent without any evaluation. It takes the "provider" as + // input and outputs the "sw". Returns 0 (key not found, or expired) or 1 (key found). + + // printMsg: 0 => No message + // printMsg: 1 => Print message only if key is found + // printMsg: 2 => Always print message, regardless if key is found or not + + char keyExpDate[9] = "00000000"; + + if (emu_find_key('F', provider, 0, keyExpDate, sw, sw_length, 0, 0, 0, NULL)) // Key found + { + if (dateCoded) // Date-coded keys are enabled, evaluate expiration date + { + char currentDate[9]; + date_to_str(currentDate, sizeof(currentDate), 0, 3); + + if (strncmp("00000000", keyExpDate, 9) == 0 || strncmp(currentDate, keyExpDate, 9) < 0) // Evergreen or not expired + { + if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate); + return 1; + } + else // Key expired + { + sw = NULL; // Make sure we don't send any expired key + if (printMsg == 2) cs_log("Key expired: F %08X %s", provider, keyExpDate); + return 0; + } + } + else // Date-coded keys are disabled, don't evaluate expiration date + { + if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate); + return 1; + } + } + else // Key not found + { + if (printMsg == 2) cs_log("Key not found: F %08X", provider); + return 0; + } +} + +static int8_t biss_mode1_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex) +{ + // Oscam's fake ecm consists of [sid] [pmtpid] [pid1] [pid2] ... [pidx] [tsid] [onid] [ens] + // On enigma boxes tsid, onid and namespace should be non zero, while on non-enigma + // boxes they are usually all zero. The top 4 bits of the namespace are flagged with 0xA. + + // The emulator creates a unique channel hash using srvid and enigma namespace or + // srvid, tsid, onid and namespace (in case of namespace without frequency) and + // another weaker (not unique) hash based on every pid of the channel. This universal + // hash should be available on all types of stbs (enigma and non-enigma). + + // Key searches are made from highest priority (tightest test first) to lowest priority + // (loosest test last): + // 1. Namespace hash (only on enigma boxes) + // 2. Universal hash (all box types with emu r752+) + // 3. Valid tsid, onid combination + // 4. Reverse order pid (audio, video, pmt) + // 5. Legacy srvid, ecm pid combination + // 6. Default "All Feeds" key + + // If enabled in the webif, a date based key search is performed. If the expiration + // date has passed, the key is not sent back from get_sw(). This option is used only + // in the namespace hash, universal hash and the "All Feeds" search methods. + + uint32_t i, ens = 0, hash = 0; + uint16_t srvid, tsid = 0, onid = 0, pid, ecm_len = SCT_LEN(ecm); + uint8_t *sw, sw_length, ecm_copy[ecm_len]; + char tmp_buffer1[33], tmp_buffer2[90] = "0", tmp_buffer3[90] = "0"; + + if (caid == 0x2602 && cw_ex != NULL) // BISS2 + { + cw_ex->mode = CW_MODE_ONE_CW; + cw_ex->algo = CW_ALGO_AES128; + cw_ex->algo_mode = CW_ALGO_MODE_CBC; + memcpy(cw_ex->data, dvb_cissa_iv, 16); + + sw = cw_ex->session_word; + sw_length = 16; + } + else // BISS1 + { + sw = dw; + sw_length = 8; + } + + srvid = b2i(2, ecm + 3); + + if (ecm_len >= 17) // Likely an r752+ extended ecm + { + tsid = b2i(2, ecm + ecm_len - 8); + onid = b2i(2, ecm + ecm_len - 6); + ens = b2i(4, ecm + ecm_len - 4); + } + + // 1. Namespace hash (enigma only) + if (is_valid_namespace(ens)) + { + unify_orbitals(&ens); + memcpy(ecm_copy, ecm, ecm_len); + i2b_buf(4, ens, ecm_copy + ecm_len - 4); + + for (i = 0; i < 5; i++) // Find key matching hash made with frequency modified to: f+0, then f-1, f+1, f-2, lastly f+2 + { + ecm_copy[ecm_len - 1] = (i & 1) ? ecm_copy[ecm_len - 1] - i : ecm_copy[ecm_len - 1] + i; // frequency +/- 1, 2 MHz + + if (0 != (ens & 0xFFFF)) // Full namespace - Calculate hash with srvid and namespace only + { + i2b_buf(2, srvid, ecm_copy + ecm_len - 6); // Put [srvid] right before [ens] + hash = crc32(caid, ecm_copy + ecm_len - 6, 6); + } + else // Namespace without frequency - Calculate hash with srvid, tsid, onid and namespace + { + i2b_buf(2, srvid, ecm_copy + ecm_len - 10); // Put [srvid] right before [tsid] [onid] [ens] sequence + hash = crc32(caid, ecm_copy + ecm_len - 10, 10); + } + + if (get_sw(hash, sw, sw_length, rdr->emu_datecodedenabled, i == 0 ? 2 : 1)) // Do not print "key not found" for frequency off by 1, 2 + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + + if (i == 0) // No key found matching our hash: create example SoftCam.Key BISS line for the live log + { + annotate(tmp_buffer2, sizeof(tmp_buffer2), ecm_copy, ecm_len, hash, 1, rdr->emu_datecodedenabled); + } + + if (0 == (ens & 0xFFFF)) // Namespace without frequency - Do not iterate + { + break; + } + } + } + + // 2. Universal hash (in r752+ style ecms that contain pmt pid) + if ((ens & 0xF0000000) == 0xA0000000) + { + hash = crc32(caid, ecm + 3, ecm_len - 3 - 8); // Do not include [tsid] [onid] [ens] in the hash + + if (get_sw(hash, sw, sw_length, rdr->emu_datecodedenabled, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + + // No key found matching our hash: create example SoftCam.Key BISS line for the live log + annotate(tmp_buffer3, sizeof(tmp_buffer3), ecm_copy, ecm_len, hash, 0, rdr->emu_datecodedenabled); + } + + // 3. Valid [tsid] [onid] combination (per enigma2) + if (onid != 0 && (onid != 1 || tsid >= 2) && onid < 0xFF00) + { + if (get_sw(tsid << 16 | onid, sw, sw_length, 0, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + } + + // 4. Reverse order pid search + // (better identifies channels with variable counts of audio pids) + // Strip [tsid] [onid] [ens] on r752+ ecms to be compatible with older versions) + if ((ens & 0xF0000000) == 0xA0000000) + { + ecm_len -= 8; + } + + for (i = ecm_len - 2; i >= 5; i -= 2) + { + pid = b2i(2, ecm + i); + + if (get_sw((srvid << 16) | pid, sw, sw_length, 0, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + } + + // 5. Legacy [srvid] [ecm pid] combination + if (get_sw((srvid << 16) | ecm_pid, sw, sw_length, 0, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + + // 6. Default BISS key for events with many feeds sharing the same session word + // (limited to local ecms, network ecms with ecm pid equal to zero are blocked) + if (ecm_pid != 0 && get_sw(0xA11FEED5, sw, sw_length, rdr->emu_datecodedenabled, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + cs_hexdump(0, sw, sw_length, tmp_buffer1, sizeof(tmp_buffer1)); + cs_log("No specific match found. Using 'All Feeds' key: %s", tmp_buffer1); + return EMU_OK; + } + + // Print example key lines for available hash search methods, if no key is found + if (strncmp(tmp_buffer2, "0", 2)) cs_log("Example key based on namespace hash: %s", tmp_buffer2); + if (strncmp(tmp_buffer3, "0", 2)) cs_log("Example key based on universal hash: %s", tmp_buffer3); + + // Check if universal hash is common and warn user + if (is_common_hash(hash)) cs_log("Feed has commonly used pids, universal hash clashes in SoftCam.Key are likely!"); + + return EMU_KEY_NOT_FOUND; +} + +static inline int8_t get_ecm_key(uint16_t onid, uint16_t esid, uint8_t parity, uint8_t *key) +{ + return emu_find_key('G', onid << 16 | esid, 0, parity == 0 ? "00" : "01", key, 16, 1, 0, 0, NULL); +} + +static int8_t biss2_mode_ca_ecm(const uint8_t *ecm, EXTENDED_CW *cw_ex) +{ + uint8_t ecm_cipher_type, session_key_parity; + uint8_t session_key[16], iv[16]; + uint16_t entitlement_session_id, original_network_id, descriptor_length; + uint16_t position, ecm_length = SCT_LEN(ecm); + uint32_t payload_checksum, calculated_checksum; + char tmp_buffer[64]; + struct aes_keys aes; + + // Calculate crc32 checksum and compare against the checksum bytes of the ECM + payload_checksum = b2i(4, ecm + ecm_length - 4); + calculated_checksum = ccitt32_crc((uint8_t *)ecm, ecm_length - 4); + + if (payload_checksum != calculated_checksum) + { + cs_log_dbg(D_TRACE, "ECM checksum mismatch (payload: %08X vs calculated: %08X", + payload_checksum, calculated_checksum); + return EMU_CHECKSUM_ERROR; + } + + // Unique identifiers of the session key + entitlement_session_id = b2i(2, ecm + 3); + original_network_id = b2i(2, ecm + 8); + + ecm_cipher_type = ecm[10] >> 5; + if (ecm_cipher_type != 0) // Session words shall be encrypted with AES_128_CBC + { + cs_log("ECM cipher type %d not supported", ecm_cipher_type); + return EMU_NOT_SUPPORTED; + } + + descriptor_length = b2i(2, ecm + 10) & 0x0FFF; + position = 12 + descriptor_length; + + session_key_parity = ecm[position] >> 7; // Parity can be "00" or "01" + position++; + + if (!get_ecm_key(original_network_id, entitlement_session_id, session_key_parity, session_key)) + { + return EMU_KEY_NOT_FOUND; + } + + memcpy(iv, ecm + position, 16); // "AES_128_CBC_enc_session_word_iv" + memcpy(cw_ex->session_word, ecm + position + 16, 16); // "AES_128_CBC_enc_session_word_0" + memcpy(cw_ex->session_word + 16, ecm + position + 32, 16); // "AES_128_CBC_enc_session_word_1" + + // Delete these cs_log calls when everything is confirmed to work correctly + cs_hexdump(3, iv, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "session_word_iv: %s", tmp_buffer); + + cs_hexdump(3, cw_ex->session_word, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "encrypted session_word_0: %s", tmp_buffer); + + cs_hexdump(3, cw_ex->session_word + 16, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "encrypted session_word_1: %s", tmp_buffer); + + // Decrypt session words + aes_set_key(&aes, (char *)session_key); + aes_cbc_decrypt(&aes, cw_ex->session_word, 16, iv); + memcpy(iv, ecm + position, 16); // Set iv again to the correct one + aes_cbc_decrypt(&aes, cw_ex->session_word + 16, 16, iv); + + // Delete these cs_log calls when everything is confirmed to work correctly + cs_hexdump(3, cw_ex->session_word, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "decrypted session_word_0: %s", tmp_buffer); + + cs_hexdump(3, cw_ex->session_word + 16, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "decrypted session_word_1: %s", tmp_buffer); + + cw_ex->mode = CW_MODE_ONE_CW; + cw_ex->algo = CW_ALGO_AES128; + cw_ex->algo_mode = CW_ALGO_MODE_CBC; + memcpy(cw_ex->data, dvb_cissa_iv, 16); + + return EMU_OK; +} + +int8_t biss_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex) +{ + switch (caid) + { + case 0x2600: + return biss_mode1_ecm(rdr, ecm, caid, ecm_pid, dw, NULL); + + case 0x2602: + return biss_mode1_ecm(rdr, ecm, caid, ecm_pid, NULL, cw_ex); + + case 0x2610: + return biss2_mode_ca_ecm(ecm, cw_ex); + + default: + cs_log("Unknown Biss caid %04X - Please report!", caid); + return EMU_NOT_SUPPORTED; + } +} + +static uint16_t parse_session_data_descriptor(const uint8_t *data, uint16_t esid, uint16_t onid, uint32_t *keysAdded) +{ + uint8_t descriptor_tag = data[0]; + uint8_t descriptor_length = data[1]; + + switch (descriptor_tag) + { + case 0x81: // session_key_descriptor + { + uint8_t session_key_type = data[2] >> 1; + if (session_key_type == 0) // AES-128 + { + uint8_t session_key_parity = data[2] & 0x01; + uint8_t session_key_data[16]; + memcpy(session_key_data, data + 3, 16); // This is the ECM key + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if (emu_update_key('G', onid << 16 | esid, session_key_parity ? "01" : "00", session_key_data, 16, 1, NULL)) + { + (*keysAdded)++; + char tmp[33]; + cs_hexdump(0, session_key_data, 16, tmp, sizeof(tmp)); + cs_log("Key found in EMM: G %08X %02d %s", onid << 16 | esid, session_key_parity, tmp); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + } + + case 0x82: // entitlement_flags_descriptor + break; + + default: + break; + } + + return 2 + descriptor_length; +} + +static int8_t parse_session_data(const uint8_t *data, RSA *key, uint16_t esid, uint16_t onid, uint32_t *keysAdded) +{ + // session_data is encrypted with RSA 2048 bit OAEP + // Maximum size of decrypted session_data is less than (256-41) bytes + uint8_t session_data[214]; + + if (RSA_private_decrypt(256, data, session_data, key, RSA_PKCS1_OAEP_PADDING) > 0) + { + uint16_t pos = 0; + uint16_t descriptor_length = b2i(2, session_data) & 0x0FFF; + + while (pos < descriptor_length) + { + pos += parse_session_data_descriptor(session_data + 2 + pos, esid, onid, keysAdded); + } + + return EMU_OK; + } + + return EMU_NOT_SUPPORTED; // Decryption failed for whatever reason +} + +static int8_t get_rsa_key(struct s_reader *rdr, const uint8_t *ekid, RSA **key) +{ + LL_ITER itr; + biss2_rsa_key_t *data; + + itr = ll_iter_create(rdr->ll_biss2_rsa_keys); + while ((data = ll_iter_next(&itr))) + { + if (data->ekid == ekid) + { + *key = data->key; + return 1; + } + } + + return 0; +} + +int8_t biss_emm(struct s_reader *rdr, const uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t emm_cipher_type, entitlement_priv_data_loop, entitlement_key_id[8]; + uint16_t entitlement_session_id, original_network_id, descriptor_length; + uint16_t pos, emm_length = SCT_LEN(emm); + uint32_t payload_checksum, calculated_checksum; + int8_t result = EMU_NOT_SUPPORTED; + char tmp[17]; + RSA *key; + + // Calculate crc32 checksum and compare against the checksum bytes of the EMM + payload_checksum = b2i(4, emm + emm_length - 4); + calculated_checksum = ccitt32_crc((uint8_t *)emm, emm_length - 4); + + if (payload_checksum != calculated_checksum) + { + cs_log_dbg(D_TRACE, "EMM checksum mismatch (payload: %08X vs calculated: %08X", + payload_checksum, calculated_checksum); + return EMU_CHECKSUM_ERROR; + } + + // Identifiers of the session key carried in the EMM + // We just pass them to the "parse_session_data()" function + entitlement_session_id = b2i(2, emm + 3); + original_network_id = b2i(2, emm + 8); + cs_log_dbg(D_TRACE, "onid: %04X, esid: %04X", original_network_id, entitlement_session_id); + + emm_cipher_type = emm[11] >> 5; // top 3 bits; + entitlement_priv_data_loop = (emm[11] >> 4) & 0x01; // 4th bit + + if (emm_cipher_type != 0) // EMM payload is not encrypted with RSA_2048_OAEP + { + cs_log_dbg(D_TRACE, "EMM cipher type %d not supported", emm_cipher_type); + return EMU_NOT_SUPPORTED; + } + + descriptor_length = b2i(2, emm + 12) & 0x0FFF; + pos = 14 + descriptor_length; + + while (pos < emm_length - 4) + { + // Unique identifier of the public rsa key used for "session_data" encryption + memcpy(entitlement_key_id, emm + pos, 8); + pos += 8; + + if (get_rsa_key(rdr, entitlement_key_id, &key)) // Key found + { + cs_hexdump(0, entitlement_key_id, 8, tmp, sizeof(tmp)); + cs_log_dbg(D_TRACE, "RSA key found (ekid: %s)", tmp); + + // Parse "encrypted_session_data" + result = parse_session_data(emm + pos, key, entitlement_session_id, original_network_id, keysAdded); + if (result == EMU_OK) + { + break; // No need to decrypt again with another key + } + } + else // Multiple ekid's can be present in the EMM - Do not exit just yet + { + cs_hexdump(0, entitlement_key_id, 8, tmp, sizeof(tmp)); + cs_log_dbg(D_TRACE, "RSA key not found (ekid: %s)", tmp); + + result = EMU_KEY_NOT_FOUND; + } + + pos += 256; // 2048 bits + + if (entitlement_priv_data_loop) // Skip any remaining bytes + { + pos += 2 + (b2i(2, emm + pos) & 0x0FFF); + } + } + + return result; +} + +static int8_t rsa_key_exists(struct s_reader *rdr, const biss2_rsa_key_t *item) +{ + LL_ITER itr; + biss2_rsa_key_t *data; + + itr = ll_iter_create(rdr->ll_biss2_rsa_keys); + while ((data = ll_iter_next(&itr))) + { + if (data->ekid == item->ekid) + { + return 1; + } + } + + return 0; +} + +uint16_t biss_read_pem(struct s_reader *rdr, uint8_t max_keys) +{ + FILE *fp_pri = NULL; + //FILE *fp_pub = NULL; + + char tmp[256]; + uint8_t hash[32], *der = NULL; + uint16_t i, length, count = 0;; + biss2_rsa_key_t *new_item; + + if (!rdr->ll_biss2_rsa_keys) + { + rdr->ll_biss2_rsa_keys = ll_create("ll_biss2_rsa_keys"); + } + + for (i = 0; i < max_keys; i++) + { + if (!cs_malloc(&new_item, sizeof(biss2_rsa_key_t))) + { + break; // No memory available (?) - Exit + } + + snprintf(tmp, sizeof(tmp), "%sbiss2_private_%02d.pem", emu_keyfile_path, i); + if ((fp_pri = fopen(tmp, "r")) == NULL) + { + continue; // File does not exist + } + + cs_log("Reading RSA key from: biss2_private_%02d.pem", i); + + // Read RSA private key + if ((new_item->key = PEM_read_RSAPrivateKey(fp_pri, NULL, NULL, NULL)) == NULL) + { + cs_log("Error reading RSA private key"); + continue; + } + + fclose(fp_pri); + + // Write public key in PEM formatted file + /*snprintf(tmp, sizeof(tmp), "%sbiss2_public_%02d.pem", emu_keyfile_path, i); + if ((fp_pub = fopen(tmp, "w")) != NULL) + { + PEM_write_RSA_PUBKEY(fp_pub, item->key); + fclose(fp_pub); + }*/ + + // Write public key in DER formatted file + /*snprintf(tmp, sizeof(tmp), "%sbiss2_public_%02d.der", emu_keyfile_path, i); + if ((fp_pub = fopen(tmp, "wb")) != NULL) + { + i2d_RSA_PUBKEY_fp(fp_pub, item->key); + fclose(fp_pub); + }*/ + + // Encode RSA public key into DER format + if ((length = i2d_RSA_PUBKEY(new_item->key, &der)) <= 0) + { + cs_log("Error encoding to DER format"); + NULLFREE(der); + continue; + } + + // Create SHA256 digest + EVP_MD_CTX *mdctx; + if ((mdctx = EVP_MD_CTX_create()) == NULL) + { + NULLFREE(der); + continue; + } + + EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); + EVP_DigestUpdate(mdctx, der, length); + EVP_DigestFinal_ex(mdctx, hash, NULL); + EVP_MD_CTX_destroy(mdctx); + + NULLFREE(der); + memcpy(new_item->ekid, hash, 8); + + // Add new RSA key, if not already present + if (!rsa_key_exists(rdr, new_item)) + { + ll_append(rdr->ll_biss2_rsa_keys, new_item); + count++; + } + } + + return count; +} + +#endif // WITH_EMU diff --git a/module-emulator-biss.h b/module-emulator-biss.h new file mode 100644 index 00000000..0f5900cc --- /dev/null +++ b/module-emulator-biss.h @@ -0,0 +1,22 @@ +#ifndef MODULE_EMULATOR_BISS_H +#define MODULE_EMULATOR_BISS_H + +#ifdef WITH_EMU + +#include + +#define BISS2_MAX_RSA_KEYS 16 + +typedef struct biss2_rsa_key +{ + uint8_t ekid[8]; + RSA *key; +} biss2_rsa_key_t; + +int8_t biss_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex); +int8_t biss_emm(struct s_reader *rdr, const uint8_t *emm, uint32_t *keysAdded); +uint16_t biss_read_pem(struct s_reader *rdr, uint8_t max_keys); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_BISS_H diff --git a/module-emulator-cryptoworks.c b/module-emulator-cryptoworks.c new file mode 100644 index 00000000..df6509ac --- /dev/null +++ b/module-emulator-cryptoworks.c @@ -0,0 +1,688 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" + +// Cryptoworks EMU + +static int8_t get_key(uint8_t *buf,uint32_t ident, uint8_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey) +{ + char keyName[EMU_MAX_CHAR_KEYNAME]; + uint32_t tmp; + + if ((ident >> 4) == 0xD02A) + { + keyIndex &= 0xFE; // map to even number key indexes + } + + if ((ident >> 4) == 0xD00C) + { + ident = 0x0D00C0; // map provider C? to C0 + } + else if (keyIndex == 6 && ((ident >> 8) == 0x0D05)) + { + ident = 0x0D0504; // always use provider 04 system key + } + + tmp = keyIndex; + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.2X", tmp); + + if (emu_find_key('W', ident, 0, keyName, buf, keyLength, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + return 0; +} + +static const uint8_t cw_sbox1[64] = +{ + 0xD8, 0xD7, 0x83, 0x3D, 0x1C, 0x8A, 0xF0, 0xCF, 0x72, 0x4C, 0x4D, 0xF2, 0xED, 0x33, 0x16, 0xE0, + 0x8F, 0x28, 0x7C, 0x82, 0x62, 0x37, 0xAF, 0x59, 0xB7, 0xE0, 0x00, 0x3F, 0x09, 0x4D, 0xF3, 0x94, + 0x16, 0xA5, 0x58, 0x83, 0xF2, 0x4F, 0x67, 0x30, 0x49, 0x72, 0xBF, 0xCD, 0xBE, 0x98, 0x81, 0x7F, + 0xA5, 0xDA, 0xA7, 0x7F, 0x89, 0xC8, 0x78, 0xA7, 0x8C, 0x05, 0x72, 0x84, 0x52, 0x72, 0x4D, 0x38 +}; + +static const uint8_t cw_sbox2[64] = +{ + 0xD8, 0x35, 0x06, 0xAB, 0xEC, 0x40, 0x79, 0x34, 0x17, 0xFE, 0xEA, 0x47, 0xA3, 0x8F, 0xD5, 0x48, + 0x0A, 0xBC, 0xD5, 0x40, 0x23, 0xD7, 0x9F, 0xBB, 0x7C, 0x81, 0xA1, 0x7A, 0x14, 0x69, 0x6A, 0x96, + 0x47, 0xDA, 0x7B, 0xE8, 0xA1, 0xBF, 0x98, 0x46, 0xB8, 0x41, 0x45, 0x9E, 0x5E, 0x20, 0xB2, 0x35, + 0xE4, 0x2F, 0x9A, 0xB5, 0xDE, 0x01, 0x65, 0xF8, 0x0F, 0xB2, 0xD2, 0x45, 0x21, 0x4E, 0x2D, 0xDB +}; + +static const uint8_t cw_sbox3[64] = +{ + 0xDB, 0x59, 0xF4, 0xEA, 0x95, 0x8E, 0x25, 0xD5, 0x26, 0xF2, 0xDA, 0x1A, 0x4B, 0xA8, 0x08, 0x25, + 0x46, 0x16, 0x6B, 0xBF, 0xAB, 0xE0, 0xD4, 0x1B, 0x89, 0x05, 0x34, 0xE5, 0x74, 0x7B, 0xBB, 0x44, + 0xA9, 0xC6, 0x18, 0xBD, 0xE6, 0x01, 0x69, 0x5A, 0x99, 0xE0, 0x87, 0x61, 0x56, 0x35, 0x76, 0x8E, + 0xF7, 0xE8, 0x84, 0x13, 0x04, 0x7B, 0x9B, 0xA6, 0x7A, 0x1F, 0x6B, 0x5C, 0xA9, 0x86, 0x54, 0xF9 +}; + +static const uint8_t cw_sbox4[64] = +{ + 0xBC, 0xC1, 0x41, 0xFE, 0x42, 0xFB, 0x3F, 0x10, 0xB5, 0x1C, 0xA6, 0xC9, 0xCF, 0x26, 0xD1, 0x3F, + 0x02, 0x3D, 0x19, 0x20, 0xC1, 0xA8, 0xBC, 0xCF, 0x7E, 0x92, 0x4B, 0x67, 0xBC, 0x47, 0x62, 0xD0, + 0x60, 0x9A, 0x9E, 0x45, 0x79, 0x21, 0x89, 0xA9, 0xC3, 0x64, 0x74, 0x9A, 0xBC, 0xDB, 0x43, 0x66, + 0xDF, 0xE3, 0x21, 0xBE, 0x1E, 0x16, 0x73, 0x5D, 0xA2, 0xCD, 0x8C, 0x30, 0x67, 0x34, 0x9C, 0xCB +}; + +static const uint8_t AND_bit1[8] = { 0x00, 0x40, 0x04, 0x80, 0x21, 0x10, 0x02, 0x08 }; +static const uint8_t AND_bit2[8] = { 0x80, 0x08, 0x01, 0x40, 0x04, 0x20, 0x10, 0x02 }; +static const uint8_t AND_bit3[8] = { 0x82, 0x40, 0x01, 0x10, 0x00, 0x20, 0x04, 0x08 }; +static const uint8_t AND_bit4[8] = { 0x02, 0x10, 0x04, 0x40, 0x80, 0x08, 0x01, 0x20 }; + +static void swap_key(uint8_t *key) +{ + uint8_t k[8]; + memcpy(k, key, 8); + memcpy(key, key + 8, 8); + memcpy(key + 8, k, 8); +} + +static void swap_data(uint8_t *k) +{ + uint8_t d[4]; + memcpy(d, k + 4, 4); + memcpy(k + 4, k, 4); + memcpy(k, d, 4); +} + +static void des_round(uint8_t *d, uint8_t *k) +{ + uint8_t aa[44] = + { + 1, 0, 3, 1, 2, 2, 3, 2, 1, 3, 1, 1, 3, 0, 1, 2, 3, 1, 3, 2, 2, 0, + 7, 6, 5, 4, 7, 6, 5, 7, 6, 5, 6, 7, 5, 7, 5, 7, 6, 6, 7, 5, 4, 4 + }; + + uint8_t bb[44] = + { + 0x80, 0x08, 0x10, 0x02, 0x08, 0x40, 0x01, 0x20, 0x40, 0x80, 0x04, + 0x10, 0x04, 0x01, 0x01, 0x02, 0x20, 0x20, 0x02, 0x01, 0x80, 0x04, + 0x02, 0x02, 0x08, 0x02, 0x10, 0x80, 0x01, 0x20, 0x08, 0x80, 0x01, + 0x08, 0x40, 0x01, 0x02, 0x80, 0x10, 0x40, 0x40, 0x10, 0x08, 0x01 + }; + + uint8_t ff[4] = { 0x02, 0x10, 0x04, 0x04}; + uint8_t l[24] = { 0, 2, 4, 6, 7, 5, 3, 1, 4, 5, 6, 7, 7, 6, 5, 4, 7, 4, 5, 6, 4, 7, 6, 5 }; + + uint8_t des_td[8], i, o, n, c = 1, m = 0, r = 0; + uint8_t *a = aa, *b = bb, *f = ff, *p1 = l, *p2 = l + 8, *p3 = l + 16; + + for (m = 0; m < 2; m++) + { + for (i = 0; i < 4; i++) + { + des_td[*p1++] = (m) ? ((d[*p2++] * 2) & 0x3F) | ((d[*p3++] & 0x80) ? 0x01 : 0x00) : + (d[*p2++] / 2) | ((d[*p3++] & 0x01) ? 0x80 : 0x00); + } + } + + for (i = 0; i < 8; i++) + { + c = (c) ? 0 : 1; + r = (c) ? 6 : 7; + n = (i) ? i - 1 : 1; + o = (c) ? ((k[n] & *f++) ? 1 : 0) : des_td[n]; + + for (m = 1; m < r; m++) + { + o = (c) ? (o * 2) | ((k[*a++] & *b++) ? 0x01 : 0x00) : (o / 2) | ((k[*a++] & *b++) ? 0x80 : 0x00); + } + + n = (i) ? n + 1 : 0; + des_td[n] = (c) ? des_td[n] ^ o : (o ^ des_td[n]) / 4; + } + + for (i = 0; i < 8; i++) + { + d[0] ^= (AND_bit1[i] & cw_sbox1[des_td[i]]); + d[1] ^= (AND_bit2[i] & cw_sbox2[des_td[i]]); + d[2] ^= (AND_bit3[i] & cw_sbox3[des_td[i]]); + d[3] ^= (AND_bit4[i] & cw_sbox4[des_td[i]]); + } + + swap_data(d); +} + +static void cw_48_key(uint8_t *inkey, uint8_t *outkey, uint8_t algotype) +{ + uint8_t round_counter, i = 8; + uint8_t *key128 = inkey; + uint8_t *key48 = inkey + 0x10; + + round_counter = 7 - (algotype & 7); + + memset(outkey, 0, 16); + memcpy(outkey, key48, 6); + + for ( ; i > round_counter; i--) + { + if (i > 1) + { + outkey[i - 2] = key128[i]; + } + } +} + +static void ls_des_key(uint8_t *key, uint8_t rotate_counter) +{ + uint8_t i, n; + uint8_t rnd[] = { 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1 }; + uint16_t k[8]; + + n = rnd[rotate_counter]; + + for (i = 0; i < 8; i++) + { + k[i] = key[i]; + } + + for (i = 1; i < n + 1; i++) + { + k[7] = (k[7] * 2) | ((k[4] & 0x008) ? 1 : 0); + k[6] = (k[6] * 2) | ((k[7] & 0xF00) ? 1 : 0); + k[7] &= 0xFF; + + k[5] = (k[5] * 2) | ((k[6] & 0xF00) ? 1 : 0); + k[6] &= 0xFF; + + k[4] = ((k[4] * 2) | ((k[5] & 0xF00) ? 1 : 0)) & 0xFF; + k[5] &= 0xFF; + + k[3] = (k[3] * 2) | ((k[0] & 0x008) ? 1 : 0); + k[2] = (k[2] * 2) | ((k[3] & 0xF00) ? 1 : 0); + k[3] &= 0xFF; + + k[1] = (k[1] * 2) | ((k[2] & 0xF00) ? 1 : 0); + k[2] &= 0xFF; + + k[0] = ((k[0] * 2) | ((k[1] & 0xF00) ? 1 : 0)) & 0xFF; + k[1] &= 0xFF; + } + + for (i = 0; i < 8; i++) + { + key[i] = (uint8_t) k[i]; + } +} + +static void rs_des_key(uint8_t *k, uint8_t rotate_counter) +{ + uint8_t i, c; + + for (i = 1; i < rotate_counter + 1; i++) + { + c = (k[3] & 0x10) ? 0x80 : 0; + k[3] /= 2; + + if (k[2] & 1) + { + k[3] |= 0x80; + } + + k[2] /= 2; + + if (k[1] & 1) + { + k[2] |= 0x80; + } + + k[1] /= 2; + + if (k[0] & 1) + { + k[1] |= 0x80; + } + + k[0] /= 2; + k[0] |= c ; + c = (k[7] & 0x10) ? 0x80 : 0; + k[7] /= 2; + + if (k[6] & 1) + { + k[7] |= 0x80; + } + + k[6] /= 2; + + if (k[5] & 1) + { + k[6] |= 0x80; + } + + k[5] /= 2; + + if (k[4] & 1) + { + k[5] |= 0x80; + } + + k[4] /= 2; + k[4] |= c; + } +} + +static void rs_des_subkey(uint8_t *k, uint8_t rotate_counter) +{ + uint8_t rnd[] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; + + rs_des_key(k, rnd[rotate_counter]); +} + +static void prep_key(uint8_t *key) +{ + int32_t round_counter = 6, i, a; + uint8_t DES_key[8], j; + + key[7] = 6; + memset(DES_key, 0, 8); + + do + { + a = 7; + i = key[7]; + j = key[round_counter]; + + do + { + DES_key[i] = ( (DES_key[i] * 2) | ((j & 1) ? 1 : 0) ) & 0xFF; + + j /= 2; + i--; + + if (i < 0) + { + i = 6; + } + a--; + } + while (a >= 0); + + key[7] = i; + round_counter--; + } + while (round_counter >= 0); + + a = DES_key[4]; + DES_key[4] = DES_key[6]; + DES_key[6] = a; + DES_key[7] = (DES_key[3] * 16) & 0xFF; + + memcpy(key, DES_key, 8); + rs_des_key(key, 4); +} + +static void l2_des(uint8_t *data, uint8_t *key, uint8_t algo) +{ + uint8_t i, k0[22], k1[22]; + + memcpy(k0, key, 22); + memcpy(k1, key, 22); + + cw_48_key(k0, k1, algo); + prep_key(k1); + + for (i = 0; i < 2; i++) + { + ls_des_key(k1, 15); + des_round(data, k1); + } +} + +static void r2_des(uint8_t *data, uint8_t *key, uint8_t algo) +{ + uint8_t i, k0[22], k1[22]; + + memcpy(k0, key, 22); + memcpy(k1, key, 22); + + cw_48_key(k0, k1, algo); + prep_key(k1); + + for (i = 0; i < 2; i++) + { + ls_des_key(k1, 15); + } + + for (i = 0; i < 2; i++) + { + des_round(data, k1); + rs_des_subkey(k1, 1); + } + + swap_data(data); +} + +static void cw_des(uint8_t *data, uint8_t *inkey, uint8_t m) +{ + uint8_t key[22], i; + + memcpy(key, inkey + 9, 8); + prep_key(key); + + for (i = 16; i > 0; i--) + { + if (m == 1) + { + ls_des_key(key, (uint8_t) (i - 1)); + } + + des_round( data ,key); + + if (m == 0) + { + rs_des_subkey(key, (uint8_t) (i - 1)); + } + } +} + +static void cw_dec_enc(uint8_t *d, uint8_t *k, uint8_t a, uint8_t m) +{ + uint8_t n = m & 1; + + l2_des(d, k, a); + cw_des(d, k, n); + r2_des(d, k, a); + + if (m & 2) + { + swap_key(k); + } +} + +static uint8_t process_nano80(uint8_t *data, uint32_t caid, int32_t provider, uint8_t *opKey, + uint8_t nanoLength, uint8_t nano80Algo) +{ + int32_t i, j; + uint8_t key[16], desKey[16], t[8], dat1[8], dat2[8], k0D00C000[16]; + + if (nanoLength < 11) + { + return 0; + } + + if (caid == 0x0D00 && provider != 0xA0 && !get_key(k0D00C000, 0x0D00C0, 0, 16, 1)) + { + return 0; + } + + if (nano80Algo > 1) + { + return 0; + } + + memset(t, 0, 8); + memcpy(dat1, data, 8); + + if(caid == 0x0D00 && provider != 0xA0) + { + memcpy(key, k0D00C000, 16); + } + else + { + memcpy(key, opKey, 16); + } + + des_ecb3_decrypt(data, key); + memcpy(desKey, data, 8); + memcpy(data, dat1, 8); + + if (caid == 0x0D00 && provider != 0xA0) + { + memcpy(key, &k0D00C000[8], 8); + memcpy(&key[8], k0D00C000, 8); + } + else + { + memcpy(key, &opKey[8], 8); + memcpy(&key[8], opKey, 8); + } + + des_ecb3_decrypt(data, key); + memcpy(&desKey[8], data, 8); + + for (i = 8; i + 7 < nanoLength; i += 8) + { + memcpy(dat1, &data[i], 8); + memcpy(dat2, dat1, 8); + memcpy(key, desKey, 16); + des_ecb3_decrypt(dat1, key); + + for (j = 0; j < 8; j++) + { + dat1[j] ^= t[j]; + } + + memcpy(&data[i], dat1, 8); + memcpy(t, dat2, 8); + } + + return data[10] + 5; +} + +static void cryptoworks_signature(const uint8_t *data, uint32_t length, uint8_t *key, uint8_t *signature) +{ + uint32_t i, sigPos; + int8_t algo, first; + + algo = data[0] & 7; + if (algo == 7) + { + algo = 6; + } + + memset(signature, 0, 8); + first = 1; + sigPos = 0; + + for (i = 0; i < length; i++) + { + signature[sigPos] ^= data[i]; + sigPos++; + + if (sigPos > 7) + { + if (first) + { + l2_des(signature, key, algo); + } + + cw_des(signature, key, 1); + + sigPos = 0; + first = 0; + } + } + + if (sigPos > 0) + { + cw_des(signature, key, 1); + } + + r2_des(signature, key, algo); +} + +static void decrypt_des(uint8_t *data, uint8_t algo, uint8_t *key) +{ + int32_t i; + uint8_t k[22], t[8]; + + algo &= 7; + + if (algo < 7) + { + cw_dec_enc(data, key, algo, 0); + } + else + { + memcpy(k, key, 22); + + for (i = 0; i < 3; i++) + { + cw_dec_enc(data, k, algo, i & 1); + + memcpy(t, k, 8); + memcpy(k, k + 8, 8); + memcpy(k + 8, t, 8); + } + } +} + +int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw) +{ + int32_t provider = -1; + uint8_t keyIndex = 0, nanoLength, newEcmLength, key[22], signature[8], nano80Algo = 1; + uint16_t i, j, ecmLen = SCT_LEN(ecm); + uint32_t ident; + + if (ecmLen < 8) + { + return EMU_NOT_SUPPORTED; + } + + if (ecm[7] != ecmLen - 8) + { + return EMU_NOT_SUPPORTED; + } + + memset(key, 0, 22); + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + if (ecm[i] == 0x83 && i + 2 < ecmLen) + { + provider = ecm[i + 2] & 0xFC; + keyIndex = ecm[i + 2] & 3; + keyIndex = keyIndex ? 1 : 0; + } + else if (ecm[i] == 0x84 && i + 3 < ecmLen) + { + //nano80Provider = ecm[i + 2] & 0xFC; + //nano80KeyIndex = ecm[i + 2] & 3; + //nano80KeyIndex = nano80KeyIndex ? 1 : 0; + nano80Algo = ecm[i + 3]; + } + } + + if (provider < 0) + { + switch (caid) + { + case 0x0D00: + provider = 0xC0; + break; + + case 0x0D02: + provider = 0xA0; + break; + + case 0x0D03: + provider = 0x04; + break; + + case 0x0D05: + provider = 0x04; + break; + + default: + return EMU_NOT_SUPPORTED; + } + } + + ident = (caid << 8) | provider; + + if (!get_key(key, ident, keyIndex, 16, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (!get_key(&key[16], ident, 6, 6, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + if (ecm[i] == 0x80 && i + 2 + 7 < ecmLen && i + 2 + ecm[i + 1] <= ecmLen && + (provider == 0xA0 || provider == 0xC0 || provider == 0xC4 || provider == 0xC8)) + { + nanoLength = ecm[i + 1]; + newEcmLength = process_nano80(ecm + i + 2, caid, provider, key, nanoLength, nano80Algo); + + if (newEcmLength == 0 || newEcmLength > ecmLen - (i + 2 + 3)) + { + return EMU_NOT_SUPPORTED; + } + + ecm[i + 2 + 3] = 0x81; + ecm[i + 2 + 4] = 0x70; + ecm[i + 2 + 5] = newEcmLength; + ecm[i + 2 + 6] = 0x81; + ecm[i + 2 + 7] = 0xFF; + + return cryptoworks_ecm(caid, ecm + i + 2 + 3, cw); + } + } + + if (ecmLen - 15 < 1) + { + return EMU_NOT_SUPPORTED; + } + + cryptoworks_signature(ecm + 5, ecmLen - 15, key, signature); + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + switch (ecm[i]) + { + case 0xDA: + case 0xDB: + case 0xDC: + if (i + 2 + ecm[i + 1] > ecmLen) + { + break; + } + for (j = 0; j + 7 < ecm[i + 1]; j += 8) + { + decrypt_des(&ecm[i + 2 + j], ecm[5], key); + } + break; + + case 0xDF: + if (i + 2 + 8 > ecmLen) + { + break; + } + if (memcmp(&ecm[i + 2], signature, 8)) + { + return EMU_CHECKSUM_ERROR; + } + break; + } + } + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + switch (ecm[i]) + { + case 0xDB: + if (i + 2 + ecm[i + 1] <= ecmLen && ecm[i + 1] == 16) + { + memcpy(cw, &ecm[i + 2], 16); + return EMU_OK; + } + break; + } + } + + return EMU_CW_NOT_FOUND; +} + +#endif // WITH_EMU diff --git a/module-emulator-cryptoworks.h b/module-emulator-cryptoworks.h new file mode 100644 index 00000000..c2424e24 --- /dev/null +++ b/module-emulator-cryptoworks.h @@ -0,0 +1,10 @@ +#ifndef MODULE_EMULATOR_CRYPTOWORKS_H +#define MODULE_EMULATOR_CRYPTOWORKS_H + +#ifdef WITH_EMU + +int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_CRYPTOWORKS_H diff --git a/module-emulator-director.c b/module-emulator-director.c new file mode 100644 index 00000000..c8c28b9c --- /dev/null +++ b/module-emulator-director.c @@ -0,0 +1,644 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "oscam-aes.h" +#include "oscam-string.h" + +/*************************************************************************************************/ + +// Shared functions + +static uint16_t calculate_checksum(uint8_t *data, uint8_t length) +{ + /* + * ECM and EMM checksum calculation + * 1. Combine data in 2 byte groups + * 2. Add them together + * 3. Multiply result by itself (power of 7) + * 4. XOR with fixed value 0x17E3 + */ + + uint8_t i; + uint16_t checksum = 0; + + for (i = 0; i < length; i += 2) + { + checksum += (data[i] << 8) | data[i + 1]; + } + + checksum = checksum * checksum * checksum * checksum * checksum * checksum * checksum; + checksum ^= 0x17E3; + + return checksum; +} + +static inline int8_t get_key(uint32_t keyIndex, char *keyName, uint8_t *key, uint32_t keyLength) +{ + /* + * keyIndex meaning for: + * ecm keys --> entitlementId + * emm keys --> aeskeyIndex + * aes keys --> keyIndex + * + * keyName meaning for: + * ecm keys --> "01" + * emm keys --> "MK" or "MK01" + * aes keys --> "AES" + */ + + return emu_find_key('T', keyIndex, 0, keyName, key, keyLength, 1, 0, 0, NULL); +} + +/*************************************************************************************************/ + +/* + * Director ECM emulator + * Supported versions: v4, v5, v6 (not working correctly) +*/ + +int8_t director_ecm(uint8_t *ecm, uint8_t *dw) +{ + uint8_t nanoType, nanoLength; + uint8_t *nanoData; + uint32_t pos = 3; + uint32_t entitlementId; + uint32_t ks[32]; + uint8_t ecmKey[8]; + uint16_t ecmLen = SCT_LEN(ecm); + + if (ecmLen < 5) + { + return EMU_NOT_SUPPORTED; + } + + do + { + nanoType = ecm[pos]; + nanoLength = ecm[pos + 1]; + + if (pos + 2 + nanoLength > ecmLen) + { + break; + } + + nanoData = ecm + pos + 2; + + // ECM validation + uint16_t payloadChecksum = (nanoData[nanoLength - 2] << 8) | nanoData[nanoLength - 1]; + uint16_t calculatedChecksum = calculate_checksum(nanoData, nanoLength - 2); + + if (calculatedChecksum != payloadChecksum) + { + cs_log_dbg(D_READER, "ECM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum); + return EMU_CHECKSUM_ERROR; + } + // End of ECM validation + + switch (nanoType) + { + case 0xEC: // Director v6 (September 2017) + { + if (nanoLength != 0x28) + { + cs_log_dbg(D_READER, "WARNING: nanoType EC length (%d) != %d", nanoLength, 0x28); + break; + } + + entitlementId = b2i(4, nanoData); + cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId); + + if (!get_key(entitlementId, "01", ecmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + // Step 1 - Decrypt DES CBC with ecmKey and iv = { 0 } (equal to nanoED) + uint8_t encryptedData[32] = { 0 }; + memcpy(encryptedData, nanoData + 6, 32); + + uint8_t iv[8] = { 0 }; + des_cbc_decrypt(encryptedData, iv, ecmKey, 32); + + uint8_t nanoMode = nanoData[5]; + + if ((nanoMode & 0x20) == 0) // Old algo + { + // Step 2 - Create CW (equal to nano ED) + dw[0] = encryptedData[0x05]; + dw[1] = encryptedData[0x19]; + dw[2] = encryptedData[0x1D]; + dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF; + dw[4] = encryptedData[0x0B]; + dw[5] = encryptedData[0x12]; + dw[6] = encryptedData[0x1A]; + dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF; + dw[8] = encryptedData[0x16]; + dw[9] = encryptedData[0x03]; + dw[10] = encryptedData[0x11]; + dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF; + dw[12] = encryptedData[0x18]; + dw[13] = encryptedData[0x10]; + dw[14] = encryptedData[0x0E]; + dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF; + + return EMU_OK; + } + else // New algo (overencryption with AES) + { + // Step 2 - Prepare data for AES (it is like the creation of CW in nanoED but swapped each 8 bytes) + uint8_t dataEC[16] = { 0 }; + + dataEC[0] = encryptedData[0x02]; + dataEC[1] = encryptedData[0x0E]; + dataEC[2] = encryptedData[0x10]; + dataEC[3] = encryptedData[0x18]; + dataEC[4] = encryptedData[0x09]; + dataEC[5] = encryptedData[0x11]; + dataEC[6] = encryptedData[0x03]; + dataEC[7] = encryptedData[0x16]; + + dataEC[8] = encryptedData[0x13]; + dataEC[9] = encryptedData[0x1A]; + dataEC[10] = encryptedData[0x12]; + dataEC[11] = encryptedData[0x0B]; + dataEC[12] = encryptedData[0x04]; + dataEC[13] = encryptedData[0x1D]; + dataEC[14] = encryptedData[0x19]; + dataEC[15] = encryptedData[0x05]; + + // Step 3 - Decrypt AES CBC with new aesKey and iv 2EBD816A5E749A708AE45ADDD84333DE + uint8_t aesKeyIndex = nanoMode & 0x1F; // 32 possible AES keys + uint8_t aesKey[16] = { 0 }; + + char tmpBuffer[33]; + cs_hexdump(0, aesKey, 16, tmpBuffer, sizeof(tmpBuffer)); + cs_log_dbg(D_READER, "INFO: Using AES key index: %02X, value: %s", aesKeyIndex, tmpBuffer); + + if (!get_key(aesKeyIndex, "AES", aesKey, 16)) + { + return EMU_KEY_NOT_FOUND; + } + + struct aes_keys aes; + aes_set_key(&aes, (char *)aesKey); + + uint8_t ivAes[16] = { 0x2E, 0xBD, 0x81, 0x6A, 0x5E, 0x74, 0x9A, 0x70, 0x8A, 0xE4, 0x5A, 0xDD, 0xD8, 0x43, 0x33, 0xDE }; + aes_cbc_decrypt(&aes, dataEC, 16, ivAes); + + // Step 4 - Create CW (a simple swap) + uint8_t offset; + for (offset = 0; offset < 16; offset++) + { + dw[offset] = dataEC[15 - offset]; + } + + return EMU_OK; + } + } + + case 0xED: // Director v5 (September 2016) + { + if (nanoLength != 0x26) + { + cs_log_dbg(D_READER, "WARNING: nanoType ED length (%d) != %d", nanoLength, 0x26); + break; + } + + entitlementId = b2i(4, nanoData); + cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId); + + if (!get_key(entitlementId, "01", ecmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + uint8_t encryptedData[32] = { 0 }; + memcpy(encryptedData, nanoData + 4, 32); + + uint8_t iv[8] = { 0 }; + des_cbc_decrypt(encryptedData, iv, ecmKey, 32); + + dw[0] = encryptedData[0x05]; + dw[1] = encryptedData[0x19]; + dw[2] = encryptedData[0x1D]; + dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF; + dw[4] = encryptedData[0x0B]; + dw[5] = encryptedData[0x12]; + dw[6] = encryptedData[0x1A]; + dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF; + dw[8] = encryptedData[0x16]; + dw[9] = encryptedData[0x03]; + dw[10] = encryptedData[0x11]; + dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF; + dw[12] = encryptedData[0x18]; + dw[13] = encryptedData[0x10]; + dw[14] = encryptedData[0x0E]; + dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF; + + return EMU_OK; + } + + case 0xEE: // Director v4 + { + if (nanoLength != 0x16) + { + cs_log_dbg(D_READER, "WARNING: nanoType EE length (%d) != %d", nanoLength, 0x16); + break; + } + + entitlementId = b2i(4, nanoData); + cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId); + + if (!get_key(entitlementId, "01", ecmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + memcpy(dw, nanoData + 4 + 8, 8); // even + memcpy(dw + 8, nanoData + 4, 8); // odd + + des_set_key(ecmKey, ks); + + des(dw, ks, 0); + des(dw + 8, ks, 0); + + dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF; + dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF; + dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF; + dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF; + + return EMU_OK; + } + + default: + cs_log_dbg(D_READER, "WARNING: nanoType %.2X not supported", nanoType); + return EMU_NOT_SUPPORTED; + } + + pos += 2 + nanoLength; + + } while (pos < ecmLen); + + return EMU_NOT_SUPPORTED; +} + +/*************************************************************************************************/ + +/* + * Director EMM emulator + * Supported versions: v4, v5, v6 (same as v5) +*/ + +static const uint8_t MixTable[] = +{ + 0x12, 0x78, 0x4B, 0x19, 0x13, 0x80, 0x2F, 0x84, 0x86, 0x4C, 0x09, 0x53, 0x15, 0x79, 0x6B, 0x49, + 0x10, 0x4D, 0x33, 0x43, 0x18, 0x37, 0x83, 0x38, 0x82, 0x1B, 0x6E, 0x24, 0x2A, 0x85, 0x3C, 0x3D, + 0x5A, 0x58, 0x55, 0x5D, 0x20, 0x41, 0x65, 0x51, 0x0C, 0x45, 0x63, 0x7F, 0x0F, 0x46, 0x21, 0x7C, + 0x2C, 0x61, 0x7E, 0x0A, 0x42, 0x57, 0x35, 0x16, 0x87, 0x3B, 0x4F, 0x40, 0x34, 0x22, 0x26, 0x74, + 0x32, 0x69, 0x44, 0x7A, 0x6A, 0x6D, 0x0D, 0x56, 0x23, 0x2B, 0x5C, 0x72, 0x76, 0x36, 0x28, 0x25, + 0x2E, 0x52, 0x5B, 0x6C, 0x7D, 0x30, 0x0B, 0x5E, 0x47, 0x1F, 0x7B, 0x31, 0x3E, 0x11, 0x77, 0x1E, + 0x60, 0x75, 0x54, 0x27, 0x50, 0x17, 0x70, 0x59, 0x1A, 0x2D, 0x4A, 0x67, 0x3A, 0x5F, 0x68, 0x08, + 0x4E, 0x3F, 0x29, 0x6F, 0x81, 0x71, 0x39, 0x64, 0x48, 0x66, 0x73, 0x14, 0x0E, 0x1D, 0x62, 0x1C +}; + +/* +static void rotate_bytes(uint8_t *in, int8_t n) +{ + if (n > 1) + { + uint8_t *e = in + n - 1; + do + { + uint8_t temp = *in; + *in++ = *e; + *e-- = temp; + } + while (in < e); + } +} +*/ + +static void decrypt_ecm_key(uint8_t *emmKey, uint8_t *tagData, uint8_t *ecmKey) +{ + uint8_t temp, *e, *payLoad, iv[8] = { 0 }; + + //rotate_bytes(emmKey, 8); + + e = emmKey + 8 - 1; + do + { + temp = *emmKey; + *emmKey++ = *e; + *e-- = temp; + } + while (emmKey < e); + + payLoad = tagData + 4 + 5; + des_cbc_decrypt(payLoad, iv, emmKey, 16); + + ecmKey[0] = payLoad[0x0F]; + ecmKey[1] = payLoad[0x01]; + ecmKey[2] = payLoad[0x0B]; + ecmKey[3] = payLoad[0x03]; + ecmKey[4] = payLoad[0x0E]; + ecmKey[5] = payLoad[0x04]; + ecmKey[6] = payLoad[0x0A]; + ecmKey[7] = payLoad[0x08]; +} + +static int8_t parse_emm_nano_tags(uint8_t *data, uint32_t length, uint8_t keyIndex, uint32_t *keysAdded) +{ + uint8_t tagType, tagLength, *tagData, blockIndex, emmKey[8], tagDataDecrypted[16][8]; + uint32_t pos = 0, entitlementId, ks[32]; + int32_t i, k; + char keyValue[17]; + + if (length < 2) + { + return EMU_NOT_SUPPORTED; + } + + while (pos < length) + { + tagType = data[pos]; + tagLength = data[pos+1]; + + if (pos + 2 + tagLength > length) + { + return EMU_CORRUPT_DATA; + } + + tagData = data + pos + 2; + + switch (tagType) + { + case 0xE4: // EMM_TAG_SECURITY_TABLE_DESCRIPTOR (ram emm keys) + { + uint8_t tagMode = data[pos + 2]; + + switch (tagMode) + { + case 0x01: // keySet 01 (MK01) + { + if (tagLength != 0x8A) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x8A); + return EMU_NOT_SUPPORTED; + } + + if (!get_key(keyIndex, "MK01", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + uint8_t iv[8] = { 0 }; + uint8_t *tagPayload = tagData + 2; + des_cbc_decrypt(tagPayload, iv, emmKey, 136); + + for (k = 0; k < 16; k++) // loop 16 keys + { + for (i = 0; i < 8; i++) // loop 8 bytes of key + { + tagDataDecrypted[k][i] = tagPayload[MixTable[8 * k + i]]; + } + } + + blockIndex = tagData[1] & 0x03; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + for (i = 0; i < 16; i++) + { + emu_set_key('T', (blockIndex << 4) + i, "MK01", tagDataDecrypted[i], 8, 0, NULL, NULL); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + case 0xFF: // keySet FF (MK) + { + if (tagLength != 0x82) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x82); + return EMU_NOT_SUPPORTED; + } + + if (!get_key(keyIndex, "MK", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + des_set_key(emmKey, ks); + + for (i = 0; i < 16; i++) + { + des(tagData + 2 + (i * 8), ks, 0); + } + + blockIndex = tagData[1] & 0x03; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + for (i = 0; i < 16; i++) + { + emu_set_key('T', (blockIndex << 4) + i, "MK", tagData + 2 + (i * 8), 8, 0, NULL, NULL); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + default: + cs_log_dbg(D_READER, "WARNING: nanoTag E4 mode %.2X not supported", tagMode); + return EMU_NOT_SUPPORTED; + } + break; + } + + case 0xE1: // EMM_TAG_EVENT_ENTITLEMENT_DESCRIPTOR (ecm keys) + { + uint8_t tagMode = data[pos + 2 + 4]; + + switch (tagMode) + { + case 0x00: // ecm keys from mode FF + { + if (tagLength != 0x12) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x12); + return EMU_NOT_SUPPORTED; + } + + entitlementId = b2i(4, tagData); + + if (!get_key(keyIndex, "MK", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + des_set_key(emmKey, ks); + des(tagData + 4 + 5, ks, 0); + uint8_t ecmKeyChk[1] = { 0 }; + memcpy(ecmKeyChk, tagData + 4 + 5 + 7, 1); + + if (ecmKeyChk[0] != 0x00) // check if key looks valid (last byte 0x00) + { + cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)"); + return EMU_KEY_REJECTED; + } + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if (emu_update_key('T', entitlementId, "01", tagData + 4 + 5, 8, 1, NULL)) + { + (*keysAdded)++; + cs_hexdump(0, tagData + 4 + 5, 8, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + case 0x01: // ecm keys from mode 01 + { + if (tagLength != 0x1A) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x1A); + return EMU_NOT_SUPPORTED; + } + + entitlementId = b2i(4, tagData); + + if (!get_key(keyIndex, "MK01", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + uint8_t ecmKey[8] = { 0 }; + decrypt_ecm_key(emmKey, tagData, ecmKey); + + if (ecmKey[7] != 0x00) // check if key looks valid (last byte 0x00) + { + cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)"); + return EMU_KEY_REJECTED; + } + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if (emu_update_key('T', entitlementId, "01", ecmKey, 8, 1, NULL)) + { + (*keysAdded)++; + cs_hexdump(0, ecmKey, 8, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + default: + cs_log_dbg(D_READER, "WARNING: nanoTag E1 mode %.2X not supported", tagMode); + return EMU_NOT_SUPPORTED; + } + break; + } + + default: + cs_log_dbg(D_READER, "WARNING: nanoTag %.2X not supported", tagType); + return EMU_NOT_SUPPORTED; + } + + pos += 2 + tagLength; + } + + return EMU_OK; +} + +static int8_t parse_emm_nano_data(uint8_t *data, uint32_t *nanoLength, uint32_t maxLength, + uint8_t keyIndex, uint32_t *keysAdded) +{ + uint32_t pos = 0; + uint16_t sectionLength; + int8_t ret = EMU_OK; + + if (maxLength < 2) + { + (*nanoLength) = 0; + return EMU_NOT_SUPPORTED; + } + + sectionLength = ((data[pos] << 8) | data[pos + 1]) & 0x0FFF; + + if (pos + 2 + sectionLength > maxLength) + { + (*nanoLength) = pos; + return EMU_CORRUPT_DATA; + } + + ret = parse_emm_nano_tags(data + pos + 2, sectionLength, keyIndex, keysAdded); + + pos += 2 + sectionLength; + + (*nanoLength) = pos; + return ret; +} + +int8_t director_emm(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t keyIndex, ret = EMU_OK; + uint16_t emmLen = SCT_LEN(emm); + uint32_t pos = 3; + uint32_t permissionDataType; + uint32_t nanoLength = 0; + + while (pos < emmLen && !ret) + { + permissionDataType = emm[pos]; + + switch (permissionDataType) + { + case 0x00: + break; + + case 0x01: + pos += 0x0A; + break; + + case 0x02: + pos += 0x26; + break; + + default: + cs_log_dbg(D_READER, "ERROR: unknown permissionDataType %.2X (pos: %d)", permissionDataType, pos); + return EMU_NOT_SUPPORTED; + } + + if (pos + 6 >= emmLen) + { + return EMU_CORRUPT_DATA; + } + + keyIndex = emm[pos + 1]; + + // EMM validation + // Copy payload checksum bytes and then set them to zero, + // so they do not affect the calculated checksum. + uint16_t payloadChecksum = (emm[pos + 2] << 8) | emm[pos + 3]; + memset(emm + pos + 2, 0, 2); + uint16_t calculatedChecksum = calculate_checksum(emm + 3, emmLen - 3); + + if (calculatedChecksum != payloadChecksum) + { + cs_log_dbg(D_READER, "EMM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum); + return EMU_CHECKSUM_ERROR; + } + // End of EMM validation + + pos += 0x04; + ret = parse_emm_nano_data(emm + pos, &nanoLength, emmLen - pos, keyIndex, keysAdded); + pos += nanoLength; + } + + return ret; +} + +#endif // WITH_EMU diff --git a/module-emulator-director.h b/module-emulator-director.h new file mode 100644 index 00000000..de430cf2 --- /dev/null +++ b/module-emulator-director.h @@ -0,0 +1,11 @@ +#ifndef MODULE_EMULATOR_DIRECTOR_H +#define MODULE_EMULATOR_DIRECTOR_H + +#ifdef WITH_EMU + +int8_t director_ecm(uint8_t *ecm, uint8_t *dw); +int8_t director_emm(uint8_t *emm, uint32_t *keysAdded); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_DIRECTOR_H diff --git a/module-emulator-irdeto.c b/module-emulator-irdeto.c new file mode 100644 index 00000000..c9df9708 --- /dev/null +++ b/module-emulator-irdeto.c @@ -0,0 +1,602 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "oscam-string.h" + +static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2) +{ + uint32_t i; + + switch (len) + { + case 16: + for (i = 0; i < 16; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 8: + for (i = 0; i < 8; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 4: + for (i = 0; i < 4; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + default: + while (len--) + { + *data++ = *v1++ ^ *v2++; + } + break; + } +} + +// Irdeto EMU +static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, + uint8_t isCriticalKey, uint32_t *keyRef) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + + if (*keyRef > 0xFF) + { + return 0; + } + + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + + if (emu_find_key('I', ident, 0, keyStr, buf, 16, *keyRef > 0 ? 0 : isCriticalKey, *keyRef, 0, NULL)) + { + (*keyRef)++; + return 1; + } + + return 0; +} + +static void irdeto2_encrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len) +{ + int32_t i; + const uint8_t *tmp = seed; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key + 8, ks2); + + len &= ~7; + + for (i = 0; i + 7 < len; i += 8) + { + xxor(&data[i], 8, &data[i], tmp); + tmp = &data[i]; + des(&data[i], ks1, 1); + des(&data[i], ks2, 0); + des(&data[i], ks1, 1); + } +} + +static void irdeto2_decrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len) +{ + int32_t i, n = 0; + uint8_t buf[2][8]; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key + 8, ks2); + + len &= ~7; + + memcpy(buf[n], seed, 8); + + for (i = 0; i + 7 < len; i += 8, data += 8, n ^= 1) + { + memcpy(buf[1 - n], data, 8); + des(data, ks1, 0); + des(data, ks2, 1); + des(data, ks1, 0); + xxor(data, 8, data, buf[n]); + } +} + +static int8_t calculate_hash(const uint8_t *key, const uint8_t *iv, const uint8_t *data, int32_t len) +{ + int32_t l, y; + uint8_t cbuff[32]; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key + 8, ks2); + + memset(cbuff, 0, sizeof(cbuff)); + + len -= 8; + + for (y = 0; y < len; y += 8) + { + if (y < len - 8) + { + xxor(cbuff, 8, cbuff, &data[y]); + } + else + { + l = len - y; + xxor(cbuff, l, cbuff, &data[y]); + xxor(cbuff + l, 8 - l, cbuff + l, iv + 8); + } + + des(cbuff, ks1, 1); + des(cbuff, ks2, 0); + des(cbuff, ks1, 1); + } + + return memcmp(cbuff, &data[len], 8) == 0; +} + +int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw) +{ + uint8_t keyNr = 0, length, end, key[16], okeySeed[16], keySeed[16], keyIV[16], tmp[16]; + uint8_t ecmCopy[EMU_MAX_ECM_LEN], *ecm = oecm; + uint16_t ecmLen = SCT_LEN(ecm); + uint32_t key0Ref, keySeedRef, keyIVRef, ident, i, j, l; + + if (ecmLen < 12) + { + return EMU_NOT_SUPPORTED; + } + + length = ecm[11]; + keyNr = ecm[9]; + ident = ecm[8] | caid << 8; + + if (ecmLen < length + 12) + { + return EMU_NOT_SUPPORTED; + } + + key0Ref = 0; + + while (get_key(key, ident, '0', keyNr, 1, &key0Ref)) + { + keySeedRef = 0; + + while (get_key(okeySeed, ident, 'M', 1, 1, &keySeedRef)) + { + keyIVRef = 0; + + while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef)) + { + memcpy(keySeed, okeySeed, 16); + memcpy(ecmCopy, oecm, ecmLen); + + ecm = ecmCopy; + memset(tmp, 0, 16); + irdeto2_encrypt(keySeed, tmp, key, 16); + + ecm += 12; + irdeto2_decrypt(ecm, keyIV, keySeed, length); + + i = (ecm[0] & 7) + 1; + end = length - 8 < 0 ? 0 : length - 8; + + while (i < end) + { + l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1; + + switch (ecm[i]) + { + case 0x10: + case 0x50: + if (l == 0x13 && i <= length - 8 - l) + { + irdeto2_decrypt(&ecm[i + 3], keyIV, key, 16); + } + break; + + case 0x78: + if (l == 0x14 && i <= length - 8 - l) + { + irdeto2_decrypt(&ecm[i + 4], keyIV, key, 16); + } + break; + } + i += l; + } + + i = (ecm[0] & 7) + 1; + + if (calculate_hash(keySeed, keyIV, ecm - 6, length + 6)) + { + while (i < end) + { + l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1; + + switch (ecm[i]) + { + case 0x78: + { + if (l == 0x14 && i <= length - 8 - l) + { + memcpy(dw, &ecm[i + 4], 16); + + for (j = 0; j < 16; j += 4) // fix dw checksum bytes + { + dw[j + 3] = (dw[j] + dw[j + 1] + dw[j + 2]) & 0xFF; + } + return EMU_OK; + } + } + } + i += l; + } + } + } + + if (keyIVRef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (keySeedRef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (key0Ref == 0) + { + return EMU_KEY_NOT_FOUND; + } + + return EMU_NOT_SUPPORTED; +} + +// Irdeto2 EMM EMU +static int8_t do_emm_type_op(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK, + uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded) +{ + uint8_t tmp[16]; + uint32_t end, i, l; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + + memset(tmp, 0, 16); + irdeto2_encrypt(keySeed, tmp, keyPMK, 16); + irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length); + + i = 16; + end = startOffset + (length - 8 < 0 ? 0 : length - 8); + + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x10: + case 0x50: + if (l == 0x13 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16); + } + break; + + case 0x78: + if (l == 0x14 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16); + } + break; + } + i += l; + } + + memmove(emm + 6, emm + 7, emmLen - 7); + + i = 15; + end = startOffset + (length - 9 < 0 ? 0 : length - 9); + + if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 4)) + { + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x10: + case 0x50: + { + if (l == 0x13 && i <= startOffset + length - 9 - l) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X", emm[i + 2] >> 2); + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_set_key('I', ident, keyName, &emm[i + 3], 16, 1, NULL, NULL); + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + (*keysAdded)++; + cs_hexdump(0, &emm[i + 3], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue); + } + } + } + i += l; + } + + if (*keysAdded > 0) + { + return 0; + } + } + + return 1; +} + +static int8_t do_emm_type_pmk(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK, + uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded) +{ + uint32_t end, i, j, l; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + + irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length); + + i = 13; + end = startOffset + (length - 8 < 0 ? 0 : length - 8); + + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x10: + case 0x50: + if (l == 0x13 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16); + } + break; + + case 0x78: + if (l == 0x14 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16); + } + break; + + case 0x68: + if (l == 0x26 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16 * 2); + } + break; + } + i += l; + } + + memmove(emm + 7, emm + 9, emmLen - 9); + + i = 11; + end = startOffset + (length - 10 < 0 ? 0 : length - 10); + + if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 5)) + { + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x68: + { + if (l == 0x26 && i <= startOffset + length - 10 - l) + { + for (j = 0; j < 2; j++) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "M%01X", 3 + j); + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_set_key('I', ident, keyName, &emm[i + 3 + j * 16], 16, 1, NULL, NULL); + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + (*keysAdded)++; + cs_hexdump(0, &emm[i + 3 + j * 16], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue); + } + } + } + } + i += l; + } + + if (*keysAdded > 0) + { + return 0; + } + } + + return 1; +} + +static const uint8_t fausto_xor[16] = +{ + 0x22, 0x58, 0xBD, 0x85, 0x2E, 0x8E, 0x52, 0x80, + 0xA3, 0x79, 0x98, 0x69, 0x68, 0xE2, 0xD8, 0x4D +}; + +int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded) +{ + uint8_t length, okeySeed[16], keySeed[16], keyIV[16], keyPMK[16], startOffset, emmType; + uint8_t emmCopy[EMU_MAX_EMM_LEN], *emm = oemm; + uint16_t emmLen = SCT_LEN(emm); + uint32_t ident, keySeedRef, keyIVRef, keyPMK0Ref, keyPMK1Ref, keyPMK0ERef, keyPMK1ERef; + + if (emmLen < 11) + { + return EMU_NOT_SUPPORTED; + } + + if (emm[3] == 0xC3 || emm[3] == 0xCB) + { + emmType = 2; + startOffset = 11; + } + else + { + emmType = 1; + startOffset = 10; + } + + ident = emm[startOffset - 2] | caid << 8; + length = emm[startOffset - 1]; + + if (emmLen < length + startOffset) + { + return EMU_NOT_SUPPORTED; + } + + keySeedRef = 0; + + while (get_key(okeySeed, ident, 'M', emmType == 1 ? 0 : 0xA, 1, &keySeedRef)) + { + keyIVRef = 0; + + while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef)) + { + keyPMK0Ref = 0; + keyPMK1Ref = 0; + keyPMK0ERef = 0; + keyPMK1ERef = 0; + + while (get_key(keyPMK, ident, 'M', emmType == 1 ? 3 : 0xB, 1, &keyPMK0Ref)) + { + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (emmType == 1) + { + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + else + { + if (do_emm_type_pmk(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + } + + if (emmType == 1) + { + while (get_key(keyPMK, ident, 'M', 4, 1, &keyPMK1Ref)) + { + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + + while (get_key(keyPMK, ident, 'M', 5, 1, &keyPMK0ERef)) + { + xxor(keyPMK, 16, keyPMK, fausto_xor); + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + + while (get_key(keyPMK, ident, 'M', 6, 1, &keyPMK1ERef)) + { + xxor(keyPMK, 16, keyPMK, fausto_xor); + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + } + + if (keyPMK0Ref == 0 && keyPMK1Ref == 0 && keyPMK0ERef == 0 && keyPMK1ERef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (keyIVRef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (keySeedRef == 0) + { + return 2; + } + + return EMU_NOT_SUPPORTED; +} + +int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial) +{ + uint32_t i, len; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData; + + KeyDB = emu_get_key_container('I'); + + if (KeyDB == NULL) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + if (KeyDB->EmuKeys[i].provider >> 8 != caid) + { + continue; + } + + if (strcmp(KeyDB->EmuKeys[i].keyName, "MC")) + { + continue; + } + + tmpKeyData = &KeyDB->EmuKeys[i]; + len = tmpKeyData->keyLength; + + if (len > 3) + { len = 3; } + + memcpy(hexserial + (3 - len), tmpKeyData->key, len); + return 1; + } + + return 0; +} + +#endif // WITH_EMU diff --git a/module-emulator-irdeto.h b/module-emulator-irdeto.h new file mode 100644 index 00000000..e13af0a0 --- /dev/null +++ b/module-emulator-irdeto.h @@ -0,0 +1,15 @@ +#ifndef MODULE_EMULATOR_IRDETO_H +#define MODULE_EMULATOR_IRDETO_H + +#ifdef WITH_EMU + +int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw); +int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded); + +// hexserial must be of type "uint8_t hexserial[3]" +// returns 0 on error, 1 on success +int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_IRDETO_H diff --git a/module-emulator-nagravision.c b/module-emulator-nagravision.c new file mode 100644 index 00000000..a0978713 --- /dev/null +++ b/module-emulator-nagravision.c @@ -0,0 +1,376 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/bn.h" +#include "cscrypt/des.h" +#include "cscrypt/idea.h" +#include "module-emulator-osemu.h" + +static void reverse_mem(uint8_t *in, int32_t len) +{ + uint8_t temp; + int32_t i; + + for (i = 0; i < (len / 2); i++) + { + temp = in[i]; + in[i] = in[len - i - 1]; + in[len - i - 1] = temp; + } +} + +static void reverse_mem_in_out(uint8_t *out, const uint8_t *in, int32_t n) +{ + if (n > 0) + { + out += n; + do + { + *(--out) = *(in++); + } + while (--n); + } +} + +static int8_t rsa_input(BIGNUM *d, const uint8_t *in, int32_t n, int8_t le) +{ + int8_t result = 0; + + if (le) + { + uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t) * n); + + if (tmp == NULL) + { + return 0; + } + + reverse_mem_in_out(tmp, in, n); + result = BN_bin2bn(tmp, n, d) != 0; + free(tmp); + } + else + { + result = BN_bin2bn(in, n, d) != 0; + } + + return result; +} + +static int32_t rsa_output(uint8_t *out, int32_t n, BIGNUM *r, int8_t le) +{ + int32_t s = BN_num_bytes(r); + + if (s > n) + { + uint8_t *buff = (uint8_t *)malloc(sizeof(uint8_t) * s); + + if (buff == NULL) + { + return 0; + } + + BN_bn2bin(r, buff); + memcpy(out, buff + s - n, n); + free(buff); + } + else if (s < n) + { + int32_t l = n - s; + + memset(out, 0, l); + BN_bn2bin(r, out + l); + } + else + { + BN_bn2bin(r, out); + } + + if (le) + { + reverse_mem(out, n); + } + + return s; +} + +static int32_t emu_rsa(uint8_t *out, const uint8_t *in, int32_t n, BIGNUM *exp, BIGNUM *mod, int8_t le) +{ + BN_CTX *ctx; + BIGNUM *r, *d; + int32_t result = 0; + + ctx = BN_CTX_new(); + r = BN_new(); + d = BN_new(); + + if (rsa_input(d, in, n, le) && BN_mod_exp(r, d, exp, mod, ctx)) + { + result = rsa_output(out, n, r, le); + } + + BN_free(d); + BN_free(r); + BN_CTX_free(ctx); + + return result; +} + +// Nagra EMU + +static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint8_t isCriticalKey) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + + if (emu_find_key('N', ident, 0, keyStr, buf, keyName == 'M' ? 64 : 16, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + return 0; +} + +static int8_t nagra2_signature(const uint8_t *vkey, const uint8_t *sig, const uint8_t *msg, int32_t len) +{ + uint8_t buff[16], iv[8]; + int32_t i, j; + + memcpy(buff, vkey, sizeof(buff)); + + for (i = 0; i + 7 < len; i += 8) + { + IDEA_KEY_SCHEDULE ek; + + idea_set_encrypt_key(buff, &ek); + memcpy(buff, buff + 8, 8); + memset(iv, 0, sizeof(iv)); + idea_cbc_encrypt(msg + i, buff + 8, 8, &ek, iv, IDEA_ENCRYPT); + + for (j = 7; j >= 0; j--) + { + buff[j + 8] ^= msg[i + j]; + } + } + + buff[8] &= 0x7F; + + return (memcmp(sig, buff + 8, 8) == 0); +} + +static int8_t decrypt_ecm(uint8_t *in, uint8_t *out, const uint8_t *key, int32_t len, + const uint8_t *vkey, uint8_t *keyM) +{ + BIGNUM *exp, *mod; + uint8_t iv[8]; + int32_t i = 0, sign = in[0] & 0x80; + uint8_t binExp = 3; + int8_t result = 1; + + exp = BN_new(); + mod = BN_new(); + BN_bin2bn(&binExp, 1, exp); + BN_bin2bn(keyM, 64, mod); + + if (emu_rsa(out, in + 1, 64, exp, mod, 1) <= 0) + { + BN_free(exp); + BN_free(mod); + return 0; + } + + out[63] |= sign; + + if (len > 64) + { + memcpy(out + 64, in + 65, len - 64); + } + + memset(iv, 0, sizeof(iv)); + + if (in[0] & 0x04) + { + uint8_t key1[8], key2[8]; + + reverse_mem_in_out(key1, &key[0], 8); + reverse_mem_in_out(key2, &key[8], 8); + + for (i = 7; i >= 0; i--) + { + reverse_mem(out + 8 * i, 8); + } + + des_ede2_cbc_decrypt(out, iv, key1, key2, len); + + for (i = 7; i >= 0; i--) + { + reverse_mem(out + 8 * i, 8); + } + } + else + { + IDEA_KEY_SCHEDULE ek; + + idea_set_encrypt_key(key, &ek); + idea_cbc_encrypt(out, out, len & ~7, &ek, iv, IDEA_DECRYPT); + } + + reverse_mem(out, 64); + + if (result && emu_rsa(out, out, 64, exp, mod, 0) <= 0) + { + result = 0; + } + + if (result && vkey && !nagra2_signature(vkey, out, out + 8, len - 8)) + { + result = 0; + } + + BN_free(exp); + BN_free(mod); + return result; +} + +int8_t nagra2_ecm(uint8_t *ecm, uint8_t *dw) +{ + int8_t useVerifyKey = 0; + int32_t l = 0, s; + + uint8_t cmdLen, ideaKeyNr, *dec, ideaKey[16], vKey[16], m1Key[64], mecmAlgo = 0; + uint16_t i = 0, ecmLen = SCT_LEN(ecm); + uint32_t ident, identMask, tmp1, tmp2, tmp3; + + if (ecmLen < 8) + { + return EMU_NOT_SUPPORTED; + } + + cmdLen = ecm[4] - 5; + ident = (ecm[5] << 8) + ecm[6]; + ideaKeyNr = (ecm[7] & 0x10) >> 4; + + if (ideaKeyNr) + { + ideaKeyNr = 1; + } + + if (ident == 1283 || ident == 1285 || ident == 1297) + { + ident = 1281; + } + + if (cmdLen <= 63 || ecmLen < cmdLen + 10) + { + return EMU_NOT_SUPPORTED; + } + + if (!get_key(ideaKey, ident, '0', ideaKeyNr, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (get_key(vKey, ident, 'V', 0, 0)) + { + useVerifyKey = 1; + } + + if (!get_key(m1Key, ident, 'M', 1, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + reverse_mem(m1Key, 64); + + dec = (uint8_t *)malloc(sizeof(uint8_t) * cmdLen); + if (dec == NULL) + { + return EMU_OUT_OF_MEMORY; + } + + if (!decrypt_ecm(ecm + 9, dec, ideaKey, cmdLen, useVerifyKey ? vKey : 0, m1Key)) + { + free(dec); + return EMU_NOT_SUPPORTED; + } + + for (i = (dec[14] & 0x10) ? 16 : 20; i < cmdLen && l != 3; ) + { + switch (dec[i]) + { + case 0x10: + case 0x11: + if (i + 10 < cmdLen && dec[i + 1] == 0x09) + { + s = (~dec[i]) & 1; + mecmAlgo = dec[i + 2] & 0x60; + memcpy(dw + (s << 3), &dec[i + 3], 8); + i += 11; + l |= (s + 1); + } + else + { + i++; + } + break; + + case 0x00: + i += 2; + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0xB0: + if (i + 1 < cmdLen) + { + i += dec[i + 1] + 2; + } + else + { + i++; + } + break; + + default: + i++; + continue; + } + } + + free(dec); + + if (l != 3) + { + return EMU_NOT_SUPPORTED; + } + + if (mecmAlgo > 0) + { + return EMU_NOT_SUPPORTED; + } + + identMask = ident & 0xFF00; + + if (identMask == 0x1100 || identMask == 0x500 || identMask == 0x3100) + { + memcpy(&tmp1, dw, 4); + memcpy(&tmp2, dw + 4, 4); + memcpy(&tmp3, dw + 12, 4); + memcpy(dw, dw + 8, 4); + memcpy(dw + 4, &tmp3, 4); + memcpy(dw + 8, &tmp1, 4); + memcpy(dw + 12, &tmp2, 4); + } + + return EMU_OK; +} + +#endif // WITH_EMU diff --git a/module-emulator-nagravision.h b/module-emulator-nagravision.h new file mode 100644 index 00000000..31810eb0 --- /dev/null +++ b/module-emulator-nagravision.h @@ -0,0 +1,10 @@ +#ifndef MODULE_EMULATOR_NAGRAVISION_H +#define MODULE_EMULATOR_NAGRAVISION_H + +#ifdef WITH_EMU + +int8_t nagra2_ecm(uint8_t *ecm, uint8_t *dw); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_NAGRAVISION_H diff --git a/module-emulator-omnicrypt.c b/module-emulator-omnicrypt.c new file mode 100644 index 00000000..805926db --- /dev/null +++ b/module-emulator-omnicrypt.c @@ -0,0 +1,72 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "module-emulator-osemu.h" +#include "module-emulator-omnicrypt.h" +#include "oscam-aes.h" +#include "oscam-string.h" + + +static inline int8_t get_ecm_key(uint16_t provider, uint8_t parity, uint8_t *key) +{ + return emu_find_key('O', provider, 0, parity == 0 ? "00" : "01", key, 16, 1, 0, 0, NULL); +} + +int8_t omnicrypt_ecm(uint8_t *ecm, uint8_t *dw) +{ + uint8_t section_syntax_indicator, session_key[16], session_key_parity, position; + uint16_t private_section_length, session_key_id, payload_length; + struct aes_keys aes; + + section_syntax_indicator = ecm[1] >> 7; + if (section_syntax_indicator != 0) // The private_data_bytes immediately follow the private_section_length field + { + cs_log("ECM section syntax indicator %d not supported", section_syntax_indicator); + return EMU_NOT_SUPPORTED; + } + + private_section_length = b2i(2, ecm + 1) & 0x0FFF; + if (private_section_length != 0x2D) + { + cs_log("ECM has an unsupported private section length of %d", private_section_length); + return EMU_NOT_SUPPORTED; + } + + session_key_parity = ecm[3] & 0x01; + session_key_id = b2i(2, ecm + 4); + + if (!get_ecm_key(session_key_id, session_key_parity, session_key)) + { + return EMU_KEY_NOT_FOUND; + } + aes_set_key(&aes, (char *)session_key); + + payload_length = b2i(2, ecm + 6) & 0x0FFF; + if (payload_length != 0x28) + { + cs_log("ECM has an unsupported payload length of %d", payload_length); + return EMU_NOT_SUPPORTED; + } + + for (position = 8; position + 1 < payload_length; position += 4 + 16) // Run twice for odd, even CW + { + uint8_t parity = ecm[position + 1] & 0x01; + uint8_t length = ecm[position + 3]; + + if (length != 16) + { + cs_log("CW %d has an unsupported length of %d", parity, length); + return EMU_NOT_SUPPORTED; + } + + aes_decrypt(&aes, ecm + position + 4, 16); + memcpy(dw + parity * 8, ecm + position + 4, 8); // Copy the first 8 bytes (rest are zeros) + } + + return EMU_OK; +} + +#endif // WITH_EMU diff --git a/module-emulator-omnicrypt.h b/module-emulator-omnicrypt.h new file mode 100644 index 00000000..3af8915c --- /dev/null +++ b/module-emulator-omnicrypt.h @@ -0,0 +1,10 @@ +#ifndef MODULE_EMULATOR_OMNICRYPT_H +#define MODULE_EMULATOR_OMNICRYPT_H + +#ifdef WITH_EMU + +int8_t omnicrypt_ecm(uint8_t *ecm, uint8_t *dw); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_OMNICRYPT_H diff --git a/module-emulator-osemu.c b/module-emulator-osemu.c new file mode 100644 index 00000000..a3f8db3f --- /dev/null +++ b/module-emulator-osemu.c @@ -0,0 +1,986 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "oscam-string.h" +#include "module-streamrelay.h" +#include "module-emulator-osemu.h" +#include "module-emulator-biss.h" +#include "module-emulator-cryptoworks.h" +#include "module-emulator-director.h" +#include "module-emulator-irdeto.h" +#include "module-emulator-nagravision.h" +#include "module-emulator-omnicrypt.h" +#include "module-emulator-powervu.h" +#include "module-emulator-viaccess.h" + +// Shared functions + +int8_t is_valid_dcw(uint8_t *dw) +{ + uint8_t i; + + for (i = 0; i < 8; i+= 4) + { + if (((dw[i] + dw[i + 1] + dw[i + 2]) & 0xFF) != dw[i + 3]) + { + return 0; + } + } + + return 1; +} + +int8_t char_to_bin(uint8_t *out, const char *in, uint32_t inLen) +{ + uint32_t i, tmp; + + for (i = 0; i < inLen / 2; i++) + { + if (sscanf(in + i * 2, "%02X", &tmp) != 1) + { + return 0; + } + out[i] = (uint8_t)tmp; + } + return 1; +} + +void date_to_str(char *dateStr, uint8_t len, int8_t offset, uint8_t format) +{ + // Creates a formatted date string for use in various functions. + // A positive or negative time offset (in hours) can be set as well + // as the format of the output string. + + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + rawtime += (time_t) offset * 60 * 60; // Add a positive or negative offset + localtime_r(&rawtime, &timeinfo); + + switch (format) + { + case 1: + strftime(dateStr, len, "%c", &timeinfo); + break; + + case 2: + strftime(dateStr, len, "%F @ %R", &timeinfo); + break; + + case 3: + strftime(dateStr, len, "%y%m%d%H", &timeinfo); + break; + } +} + +/* + * Key DB + * + * The Emu reader gets keys from the OSCcam-Emu binary and the "SoftCam.Key" file. + * + * The keys are stored in structures of type "KeyDataContainer", one per CAS. Each + * container points to a dynamically allocated array of type "KeyData", which holds + * the actual keys. The array initially holds up to 64 keys (64 * KeyData), and it + * is expanded by 16 every time it's filled with keys. The "KeyDataContainer" also + * includes info about the number of keys it contains ("KeyCount") and the maximum + * number of keys it can store ("KeyMax"). + * + * The "KeyData" structure, on the other hand, stores the actual key information, + * including the "identifier", "provider", "keyName", "key" and "keyLength". There + * is also a "nextKey" pointer to a similar "KeyData" structure which is only used + * for Irdeto multiple keys, in a linked list style structure. For all other CAS, + * the "nextKey" is a "NULL" pointer. + * + * For storing keys, the "SetKey" function is used. Duplicate keys are not allowed. + * When storing a key that is already present in the database, its "key" value is + * updated with the new one. For reading keys from the database, the "FindKey" + * function is used. To delete all keys in a container, the "DeleteKeysInContainer" + * function can be called. +*/ + +char *emu_keyfile_path = NULL; + +void emu_set_keyfile_path(const char *path) +{ + uint32_t pathLength; + + if (emu_keyfile_path != NULL) + { + free(emu_keyfile_path); + } + + pathLength = cs_strlen(path); + emu_keyfile_path = (char *)malloc(pathLength + 1); + if (emu_keyfile_path == NULL) + { + return; + } + cs_strncpy(emu_keyfile_path, path, pathLength + 1); +} + +KeyDataContainer CwKeys = { NULL, 0, 0 }; +KeyDataContainer ViKeys = { NULL, 0, 0 }; +KeyDataContainer NagraKeys = { NULL, 0, 0 }; +KeyDataContainer IrdetoKeys = { NULL, 0, 0 }; +KeyDataContainer BissSWs = { NULL, 0, 0 }; +KeyDataContainer Biss2Keys = { NULL, 0, 0 }; +KeyDataContainer OmnicryptKeys = { NULL, 0, 0 }; +KeyDataContainer PowervuKeys = { NULL, 0, 0 }; +KeyDataContainer TandbergKeys = { NULL, 0, 0 }; +KeyDataContainer StreamKeys = { NULL, 0, 0 }; + +KeyDataContainer *emu_get_key_container(char identifier) +{ + switch (identifier) + { + case 'W': + return &CwKeys; + case 'V': + return &ViKeys; + case 'N': + return &NagraKeys; + case 'I': + return &IrdetoKeys; + case 'F': + return &BissSWs; + case 'G': + return &Biss2Keys; + case 'O': + return &OmnicryptKeys; + case 'P': + return &PowervuKeys; + case 'T': + return &TandbergKeys; + case 'A': + return &StreamKeys; + default: + return NULL; + } +} + +static void write_key_to_file(char identifier, uint32_t provider, const char *keyName, uint8_t *key, + uint32_t keyLength, char *comment) +{ + char line[1200], dateText[100], filename[EMU_KEY_FILENAME_MAX_LEN + 1]; + char *path, *filepath, *keyValue; + uint32_t pathLength; + uint8_t fileNameLen = cs_strlen(EMU_KEY_FILENAME); + struct dirent *pDirent; + DIR *pDir; + FILE *file = NULL; + + pathLength = cs_strlen(emu_keyfile_path); + path = (char *)malloc(pathLength + 1); + if (path == NULL) + { + return; + } + cs_strncpy(path, emu_keyfile_path, pathLength + 1); + + pathLength = cs_strlen(path); + if (pathLength >= fileNameLen && strcasecmp(path + pathLength - fileNameLen, EMU_KEY_FILENAME) == 0) + { + // cut file name + path[pathLength - fileNameLen] = '\0'; + } + + pathLength = cs_strlen(path); + if (path[pathLength - 1] == '/' || path[pathLength - 1] == '\\') + { + // cut trailing / + path[pathLength - 1] = '\0'; + } + + pDir = opendir(path); + if (pDir == NULL) + { + cs_log("Cannot open key file path: %s", path); + free(path); + return; + } + + while ((pDirent = readdir(pDir)) != NULL) + { + if (strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0) + { + cs_strncpy(filename, pDirent->d_name, sizeof(filename)); + break; + } + } + closedir(pDir); + + if (pDirent == NULL) + { + cs_strncpy(filename, EMU_KEY_FILENAME, sizeof(filename)); + } + + pathLength = cs_strlen(path) + 1 + cs_strlen(filename) + 1; + filepath = (char *)malloc(pathLength); + if (filepath == NULL) + { + free(path); + return; + } + snprintf(filepath, pathLength, "%s/%s", path, filename); + free(path); + + cs_log("Writing key file: %s", filepath); + + file = fopen(filepath, "a"); + free(filepath); + if (file == NULL) + { + return; + } + + date_to_str(dateText, sizeof(dateText), 0, 1); + + keyValue = (char *)malloc((keyLength * 2) + 1); + if (keyValue == NULL) + { + fclose(file); + return; + } + cs_hexdump(0, key, keyLength, keyValue, (keyLength * 2) + 1); + + if (comment) + { + snprintf(line, sizeof(line), "\n%c %08X %s %s ; added by Emu %s %s", + identifier, provider, keyName, keyValue, dateText, comment); + } + else + { + snprintf(line, sizeof(line), "\n%c %08X %s %s ; added by Emu %s", + identifier, provider, keyName, keyValue, dateText); + } + + cs_log("Key written: %c %08X %s %s", identifier, provider, keyName, keyValue); + + free(keyValue); + + fwrite(line, cs_strlen(line), 1, file); + fclose(file); +} + +int8_t emu_set_key(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength, + uint8_t writeKey, char *comment, struct s_reader *rdr) +{ + uint32_t i, j; + uint8_t *tmpKey = NULL; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData, *newKeyData; + + identifier = (char)toupper((int)identifier); + + KeyDB = emu_get_key_container(identifier); + if (KeyDB == NULL) + { + return 0; + } + + keyName = strtoupper(keyName); + + if (identifier == 'F') // Prepare BISS keys before saving to the db + { + // Convert legacy BISS "00" & "01" keynames + if (0 == strcmp(keyName, "00") || 0 == strcmp(keyName, "01")) + { + keyName = "00000000"; + } + + // All keyNames should have a length of 8 after converting + if (cs_strlen(keyName) != 8) + { + cs_log("WARNING: Wrong key format in %s: F %08X %s", EMU_KEY_FILENAME, provider, keyName); + return 0; + } + + // Verify date-coded keyName (if enabled), ignoring old (expired) keys + if (rdr->emu_datecodedenabled) + { + char timeStr[9]; + date_to_str(timeStr, sizeof(timeStr), 0, 3); + + // Reject old date-coded keys, but allow our "00000000" evergreen label + if (strcmp("00000000", keyName) != 0 && strcmp(timeStr, keyName) >= 0) + { + return 0; + } + } + } + + // Fix checksum for BISS keys with a length of 6 + if (identifier == 'F' && keyLength == 6) + { + tmpKey = (uint8_t *)malloc(8 * sizeof(uint8_t)); + if(tmpKey == NULL) + { + return 0; + } + + tmpKey[0] = orgKey[0]; + tmpKey[1] = orgKey[1]; + tmpKey[2] = orgKey[2]; + tmpKey[3] = ((orgKey[0] + orgKey[1] + orgKey[2]) & 0xFF); + tmpKey[4] = orgKey[3]; + tmpKey[5] = orgKey[4]; + tmpKey[6] = orgKey[5]; + tmpKey[7] = ((orgKey[3] + orgKey[4] + orgKey[5]) & 0xFF); + + keyLength = 8; + } + else // All keys with a length of 8, including BISS + { + tmpKey = (uint8_t *)malloc(keyLength * sizeof(uint8_t)); + if (tmpKey == NULL) + { + return 0; + } + + memcpy(tmpKey, orgKey, keyLength); + } + + // Fix patched mgcamd format for Irdeto + if (identifier == 'I' && provider < 0xFFFF) + { + provider = provider << 8; + } + + // Key already exists on db, update its value + for (i = 0; i < KeyDB->keyCount; i++) + { + if (KeyDB->EmuKeys[i].provider != provider) + { + continue; + } + + // Don't match keyName (i.e. expiration date) for BISS1 and BISS2 mode 1/E sesssion words + if (identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName)) + { + continue; + } + + // Allow multiple keys for Irdeto + if (identifier == 'I') + { + // Reject duplicates + tmpKeyData = &KeyDB->EmuKeys[i]; + do + { + if (memcmp(tmpKeyData->key, tmpKey, tmpKeyData->keyLength < keyLength ? tmpKeyData->keyLength : keyLength) == 0) + { + free(tmpKey); + return 0; + } + tmpKeyData = tmpKeyData->nextKey; + } + while(tmpKeyData != NULL); + + // Add new key + newKeyData = (KeyData *)malloc(sizeof(KeyData)); + if (newKeyData == NULL) + { + free(tmpKey); + return 0; + } + + newKeyData->identifier = identifier; + newKeyData->provider = provider; + + if (cs_strlen(keyName) < EMU_MAX_CHAR_KEYNAME) + { + cs_strncpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + else + { + memcpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + newKeyData->keyName[EMU_MAX_CHAR_KEYNAME - 1] = 0; + newKeyData->key = tmpKey; + newKeyData->keyLength = keyLength; + newKeyData->nextKey = NULL; + + tmpKeyData = &KeyDB->EmuKeys[i]; + j = 0; + + while (tmpKeyData->nextKey != NULL) + { + if (j == 0xFE) + { + break; + } + tmpKeyData = tmpKeyData->nextKey; + j++; + } + + if (tmpKeyData->nextKey) + { + NULLFREE(tmpKeyData->nextKey->key); + NULLFREE(tmpKeyData->nextKey); + } + tmpKeyData->nextKey = newKeyData; + + if (writeKey) + { + write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment); + } + } + else // identifier != 'I' + { + free(KeyDB->EmuKeys[i].key); + KeyDB->EmuKeys[i].key = tmpKey; + KeyDB->EmuKeys[i].keyLength = keyLength; + + if (identifier == 'F') // Update keyName (i.e. expiration date) for BISS + { + cs_strncpy(KeyDB->EmuKeys[i].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + if (writeKey) + { + write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment); + } + } + return 1; + } + + // Key does not exist on db + if (KeyDB->keyCount + 1 > KeyDB->keyMax) + { + if (KeyDB->EmuKeys == NULL) // db is empty + { + KeyDB->EmuKeys = (KeyData *)malloc(sizeof(KeyData) * (KeyDB->keyMax + 64)); + if (KeyDB->EmuKeys == NULL) + { + free(tmpKey); + return 0; + } + KeyDB->keyMax += 64; + } + else // db is full, expand it + { + tmpKeyData = (KeyData *)realloc(KeyDB->EmuKeys, sizeof(KeyData) * (KeyDB->keyMax + 16)); + if (tmpKeyData == NULL) + { + free(tmpKey); + return 0; + } + KeyDB->EmuKeys = tmpKeyData; + KeyDB->keyMax += 16; + } + } + + KeyDB->EmuKeys[KeyDB->keyCount].identifier = identifier; + KeyDB->EmuKeys[KeyDB->keyCount].provider = provider; + + if (cs_strlen(keyName) < EMU_MAX_CHAR_KEYNAME) + { + cs_strncpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + else + { + memcpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + KeyDB->EmuKeys[KeyDB->keyCount].keyName[EMU_MAX_CHAR_KEYNAME - 1] = 0; + KeyDB->EmuKeys[KeyDB->keyCount].key = tmpKey; + KeyDB->EmuKeys[KeyDB->keyCount].keyLength = keyLength; + KeyDB->EmuKeys[KeyDB->keyCount].nextKey = NULL; + KeyDB->keyCount++; + + if (writeKey) + { + write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment); + } + return 1; +} + +int8_t emu_find_key(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, + uint8_t *key, uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef, + uint8_t matchLength, uint32_t *getProvider) +{ + uint32_t i; + uint16_t j; + uint8_t provider_matching_key_count = 0; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData; + + KeyDB = emu_get_key_container(identifier); + if (KeyDB == NULL) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + if ((KeyDB->EmuKeys[i].provider & ~providerIgnoreMask) != provider) + { + continue; + } + + // Don't match keyName (i.e. expiration date) for BISS + if (identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName)) + { + continue; + } + + // "matchLength" cannot be used when multiple keys are allowed + // for a single provider/keyName combination. + // Currently this is the case only for Irdeto keys. + if (matchLength && KeyDB->EmuKeys[i].keyLength != maxKeyLength) + { + continue; + } + + if (providerIgnoreMask) + { + if (provider_matching_key_count < keyRef) + { + provider_matching_key_count++; + continue; + } + else + { + keyRef = 0; + } + } + + tmpKeyData = &KeyDB->EmuKeys[i]; + + j = 0; + while (j < keyRef && tmpKeyData->nextKey != NULL) + { + j++; + tmpKeyData = tmpKeyData->nextKey; + } + + if (j == keyRef) + { + memcpy(key, tmpKeyData->key, tmpKeyData->keyLength > maxKeyLength ? maxKeyLength : tmpKeyData->keyLength); + if (tmpKeyData->keyLength < maxKeyLength) + { + memset(key + tmpKeyData->keyLength, 0, maxKeyLength - tmpKeyData->keyLength); + } + + // Report the keyName (i.e. expiration date) of the session word found + if (identifier == 'F') + { + cs_strncpy(keyName, tmpKeyData->keyName, EMU_MAX_CHAR_KEYNAME); + } + + if (getProvider != NULL) + { + (*getProvider) = tmpKeyData->provider; + } + return 1; + } + else + { + break; + } + } + + if (isCriticalKey) + { + cs_log("Key not found: %c %X %s", identifier, provider, keyName); + } + + return 0; +} + +int8_t emu_update_key(char identifier, uint32_t provider, char *keyName, uint8_t *key, + uint32_t keyLength, uint8_t writeKey, char *comment) +{ + uint32_t keyRef = 0; + uint8_t *tmpKey = (uint8_t *)malloc(sizeof(uint8_t) * keyLength); + + if (tmpKey == NULL) + { + return 0; + } + + while (emu_find_key(identifier, provider, 0, keyName, tmpKey, keyLength, 0, keyRef, 0, NULL)) + { + if (memcmp(tmpKey, key, keyLength) == 0) + { + free(tmpKey); + return 0; + } + + keyRef++; + } + + free(tmpKey); + return emu_set_key(identifier, provider, keyName, key, keyLength, writeKey, comment, NULL); +} + +static int32_t delete_keys_in_container(char identifier) +{ + // Deletes all keys stored in memory for the specified identifier, + // but keeps the container itself, re-initialized at { NULL, 0, 0 }. + // Returns the count of deleted keys. + + uint32_t oldKeyCount, i; + KeyData *tmpKeyData; + KeyDataContainer *KeyDB = emu_get_key_container(identifier); + + if (KeyDB == NULL || KeyDB->EmuKeys == NULL || KeyDB->keyCount == 0) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + // For Irdeto multiple keys only (linked list structure) + while (KeyDB->EmuKeys[i].nextKey != NULL) + { + tmpKeyData = KeyDB->EmuKeys[i].nextKey; + KeyDB->EmuKeys[i].nextKey = KeyDB->EmuKeys[i].nextKey->nextKey; + free(tmpKeyData->key); // Free key + free(tmpKeyData); // Free KeyData + } + + // For single keys (all identifiers, including Irdeto) + free(KeyDB->EmuKeys[i].key); // Free key + } + + // Free the KeyData array + NULLFREE(KeyDB->EmuKeys); + oldKeyCount = KeyDB->keyCount; + KeyDB->keyCount = 0; + KeyDB->keyMax = 0; + + return oldKeyCount; +} + +void emu_clear_keydata(void) +{ + uint32_t total = 0; + + total = CwKeys.keyCount; + total += ViKeys.keyCount; + total += NagraKeys.keyCount; + total += IrdetoKeys.keyCount; + total += BissSWs.keyCount; + total += Biss2Keys.keyCount; + total += OmnicryptKeys.keyCount; + total += PowervuKeys.keyCount; + total += TandbergKeys.keyCount; + total += StreamKeys.keyCount; + + if (total != 0) + { + cs_log("Freeing keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d", + CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount, + Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount, + StreamKeys.keyCount); + + delete_keys_in_container('W'); + delete_keys_in_container('V'); + delete_keys_in_container('N'); + delete_keys_in_container('I'); + delete_keys_in_container('F'); + delete_keys_in_container('G'); + delete_keys_in_container('O'); + delete_keys_in_container('P'); + delete_keys_in_container('T'); + delete_keys_in_container('A'); + } +} + +uint8_t emu_read_keyfile(struct s_reader *rdr, const char *opath) +{ + char line[1200], keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026], identifier; + char *path, *filepath, filename[EMU_KEY_FILENAME_MAX_LEN + 1]; + uint32_t pathLength, provider, keyLength; + uint8_t fileNameLen = cs_strlen(EMU_KEY_FILENAME); + uint8_t *key; + struct dirent *pDirent; + DIR *pDir; + FILE *file = NULL; + + pathLength = cs_strlen(opath); + path = (char *)malloc(pathLength + 1); + if (path == NULL) + { + return 0; + } + cs_strncpy(path, opath, pathLength + 1); + + pathLength = cs_strlen(path); + if (pathLength >= fileNameLen && strcasecmp(path + pathLength - fileNameLen, EMU_KEY_FILENAME) == 0) + { + // cut file name + path[pathLength - fileNameLen] = '\0'; + } + + pathLength = cs_strlen(path); + if (path[pathLength - 1] == '/' || path[pathLength - 1] == '\\') + { + // cut trailing / + path[pathLength - 1] = '\0'; + } + + pDir = opendir(path); + if (pDir == NULL) + { + cs_log("Cannot open key file path: %s", path); + free(path); + return 0; + } + + while ((pDirent = readdir(pDir)) != NULL) + { + if (strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0) + { + cs_strncpy(filename, pDirent->d_name, sizeof(filename)); + break; + } + } + closedir(pDir); + + if (pDirent == NULL) + { + cs_log("Key file not found in: %s", path); + free(path); + return 0; + } + + pathLength = cs_strlen(path) + 1 + cs_strlen(filename) + 1; + filepath = (char *)malloc(pathLength); + if (filepath == NULL) + { + free(path); + return 0; + } + snprintf(filepath, pathLength, "%s/%s", path, filename); + free(path); + + cs_log("Reading key file: %s", filepath); + + file = fopen(filepath, "r"); + free(filepath); + if (file == NULL) + { + return 0; + } + + emu_set_keyfile_path(opath); + + while (fgets(line, 1200, file)) + { + if (sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4) + { + continue; + } + + keyLength = cs_strlen(keyString) / 2; + key = (uint8_t *)malloc(keyLength); + if (key == NULL) + { + fclose(file); + return 0; + } + + if (char_to_bin(key, keyString, cs_strlen(keyString))) // Conversion OK + { + emu_set_key(identifier, provider, keyName, key, keyLength, 0, NULL, rdr); + } + else // Non-hex characters in keyString + { + if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc. + identifier != '=' && identifier != '-' && + identifier != ' ') && + !(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines + { + // Alert user regarding faulty line + cs_log("WARNING: non-hex value in %s at %c %08X %s %s", + EMU_KEY_FILENAME, identifier, provider, keyName, keyString); + } + } + free(key); + } + fclose(file); + + return 1; +} + +#if defined(WITH_SOFTCAM) && !defined(__APPLE__) && !defined(__ANDROID__) +extern uint8_t SoftCamKey_Data[] __asm__("_binary_SoftCam_Key_start"); +extern uint8_t SoftCamKey_DataEnd[] __asm__("_binary_SoftCam_Key_end"); + +void emu_read_keymemory(struct s_reader *rdr) +{ + char *keyData, *line, *saveptr, keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026], identifier; + uint32_t provider, keyLength; + uint8_t *key; + + keyData = (char *)malloc(SoftCamKey_DataEnd - SoftCamKey_Data + 1); + if (keyData == NULL) + { + return; + } + memcpy(keyData, SoftCamKey_Data, SoftCamKey_DataEnd - SoftCamKey_Data); + keyData[SoftCamKey_DataEnd-SoftCamKey_Data] = 0x00; + + line = strtok_r(keyData, "\n", &saveptr); + while (line != NULL) + { + if (sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4) + { + line = strtok_r(NULL, "\n", &saveptr); + continue; + } + + keyLength = cs_strlen(keyString) / 2; + + key = (uint8_t *)malloc(keyLength); + if (key == NULL) + { + free(keyData); + return; + } + + if (char_to_bin(key, keyString, cs_strlen(keyString))) // Conversion OK + { + emu_set_key(identifier, provider, keyName, key, keyLength, 0, NULL, rdr); + } + else // Non-hex characters in keyString + { + if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc. + identifier != '=' && identifier != '-' && + identifier != ' ') && + !(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines + { + // Alert user regarding faulty line + cs_log("WARNING: non-hex value in internal keyfile at %c %08X %s %s", + identifier, provider, keyName, keyString); + } + } + free(key); + line = strtok_r(NULL, "\n", &saveptr); + } + free(keyData); +} +#else +void emu_read_keymemory(struct s_reader *UNUSED(rdr)) { } +#endif + +static const char *get_error_reason(int8_t result) +{ + switch (result) + { + case EMU_OK: + return "No error"; + + case EMU_NOT_SUPPORTED: + return "Not supported"; + + case EMU_KEY_NOT_FOUND: + return "Key not found"; + + case EMU_KEY_REJECTED: + return "ECM key rejected"; + + case EMU_CORRUPT_DATA: + return "Corrupt data"; + + case EMU_CW_NOT_FOUND: + return "CW not found"; + + case EMU_CHECKSUM_ERROR: + return "Checksum error"; + + case EMU_OUT_OF_MEMORY: + return "Out of memory"; + + default: + return "Unknown reason"; + } +} + +int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, EXTENDED_CW *cw_ex) +{ + if (er->ecmlen < 3) + { + cs_log_dbg(D_TRACE, "Received ecm data of zero length!"); + return 4; + } + + uint16_t ecmLen = SCT_LEN(er->ecm); + uint8_t ecmCopy[ecmLen]; + int8_t result = 1; + + if (ecmLen != er->ecmlen) + { + cs_log_dbg(D_TRACE, "Actual ecm data length 0x%03X but ecm section length is 0x%03X", + er->ecmlen, ecmLen); + return 4; + } + + if (ecmLen > EMU_MAX_ECM_LEN) + { + cs_log_dbg(D_TRACE, "Actual ecm data length 0x%03X but maximum supported ecm length is 0x%03X", + er->ecmlen, EMU_MAX_ECM_LEN); + return 1; + } + + memcpy(ecmCopy, er->ecm, ecmLen); + + if (caid_is_viaccess(er->caid)) result = viaccess_ecm(ecmCopy, cw); + else if (caid_is_irdeto(er->caid)) result = irdeto2_ecm(er->caid, ecmCopy, cw); + else if (caid_is_cryptoworks(er->caid)) result = cryptoworks_ecm(er->caid, ecmCopy, cw); + else if (caid_is_powervu(er->caid)) + { +#ifdef MODULE_STREAMRELAY + result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens, NULL); +#else + result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens); +#endif + } + else if (caid_is_director(er->caid)) result = director_ecm(ecmCopy, cw); + else if (caid_is_nagra(er->caid)) result = nagra2_ecm(ecmCopy, cw); + else if (caid_is_biss(er->caid)) result = biss_ecm(rdr, er->ecm, er->caid, er->pid, cw, cw_ex); + else if (er->caid == 0x00FF) result = omnicrypt_ecm(ecmCopy, cw); // temp caid + + if (result != 0) + { + cs_log("ECM failed: %s", get_error_reason(result)); + } + + return result; +} + +int8_t emu_process_emm(struct s_reader *rdr, uint16_t caid, const uint8_t *emm, uint32_t *keysAdded) +{ + uint16_t emmLen = SCT_LEN(emm); + uint8_t emmCopy[emmLen]; + int8_t result = 1; + + if (emmLen > EMU_MAX_EMM_LEN) + { + return 1; + } + memcpy(emmCopy, emm, emmLen); + *keysAdded = 0; + + if (caid_is_viaccess(caid)) result = viaccess_emm(emmCopy, keysAdded); + else if (caid_is_irdeto(caid)) result = irdeto2_emm(caid, emmCopy, keysAdded); + else if (caid_is_powervu(caid)) result = powervu_emm(emmCopy, keysAdded); + else if (caid_is_director(caid)) result = director_emm(emmCopy, keysAdded); + else if (caid_is_biss_dynamic(caid)) result = biss_emm(rdr, emmCopy, keysAdded); + + if (result != 0) + { + cs_log_dbg(D_EMM,"EMM failed: %s", get_error_reason(result)); + } + + return result; +} + +#endif // WITH_EMU diff --git a/module-emulator-osemu.h b/module-emulator-osemu.h new file mode 100644 index 00000000..0fe2b1d5 --- /dev/null +++ b/module-emulator-osemu.h @@ -0,0 +1,96 @@ +#ifndef MODULE_EMULATOR_OSEMU_H_ +#define MODULE_EMULATOR_OSEMU_H_ + +#ifdef WITH_EMU + +// Version info +#define EMU_VERSION 801 + +#define EMU_MAX_CHAR_KEYNAME 12 +#define EMU_KEY_FILENAME "SoftCam.Key" +#define EMU_KEY_FILENAME_MAX_LEN 31 +#define EMU_MAX_ECM_LEN MAX_ECM_SIZE +#define EMU_MAX_EMM_LEN MAX_EMM_SIZE + +/* + * Error codes for ProccessECM and ProccessEMM functions + * 0 - OK + * 1 - ECM / EMM not supported + * 2 - ECM / EMM key not found + * 3 - ECM key rejected + * 4 - Corrupt data + * 5 - CW not found + * 6 - CW / ECM / EMM checksum error + * 7 - Out of memory +*/ + +#define EMU_OK 0 +#define EMU_NOT_SUPPORTED 1 +#define EMU_KEY_NOT_FOUND 2 +#define EMU_KEY_REJECTED 3 +#define EMU_CORRUPT_DATA 4 +#define EMU_CW_NOT_FOUND 5 +#define EMU_CHECKSUM_ERROR 6 +#define EMU_OUT_OF_MEMORY 7 + +typedef struct KeyData KeyData; + +struct KeyData +{ + char identifier; + uint32_t provider; + char keyName[EMU_MAX_CHAR_KEYNAME]; + uint8_t *key; + uint32_t keyLength; + KeyData *nextKey; +}; + +typedef struct +{ + KeyData *EmuKeys; + uint32_t keyCount; + uint32_t keyMax; +} KeyDataContainer; + +extern KeyDataContainer CwKeys; +extern KeyDataContainer ViKeys; +extern KeyDataContainer NagraKeys; +extern KeyDataContainer IrdetoKeys; +extern KeyDataContainer BissSWs; // 'F' identifier - BISS1 and BISS2 mode 1/E session words +extern KeyDataContainer Biss2Keys; // 'G' identifier - BISS2 mode CA session keys (ECM keys) +extern KeyDataContainer OmnicryptKeys; +extern KeyDataContainer PowervuKeys; +extern KeyDataContainer TandbergKeys; +extern KeyDataContainer StreamKeys; +extern uint8_t viasat_const[]; +extern char *emu_keyfile_path; +extern pthread_mutex_t emu_key_data_mutex; + +void emu_set_keyfile_path(const char *path); +void emu_clear_keydata(void); +uint8_t emu_read_keyfile(struct s_reader *rdr, const char *path); +void emu_read_keymemory(struct s_reader *rdr); + +int8_t is_valid_dcw(uint8_t *dw); +int8_t char_to_bin(uint8_t *out, const char *in, uint32_t inLen); +void date_to_str(char *dateStr, uint8_t len, int8_t offset, uint8_t format); + +KeyDataContainer *emu_get_key_container(char identifier); + +int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, EXTENDED_CW* cw_ex); + +int8_t emu_process_emm(struct s_reader *rdr, uint16_t caid, const uint8_t *emm, uint32_t *keysAdded); + +int8_t emu_find_key(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, + uint8_t *key, uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef, + uint8_t matchLength, uint32_t *getProvider); + +int8_t emu_set_key(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength, + uint8_t writeKey, char *comment, struct s_reader *rdr); + +int8_t emu_update_key(char identifier, uint32_t provider, char *keyName, uint8_t *key, uint32_t keyLength, + uint8_t writeKey, char *comment); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_H_ diff --git a/module-emulator-powervu.c b/module-emulator-powervu.c new file mode 100644 index 00000000..810fcecf --- /dev/null +++ b/module-emulator-powervu.c @@ -0,0 +1,2795 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-streamrelay.h" +#include "module-emulator-osemu.h" +#include "module-emulator-powervu.h" +#include "oscam-string.h" +#include "oscam-time.h" + +static inline uint8_t get_bit(uint8_t byte, uint8_t bitnb) +{ + return ((byte & (1 << bitnb)) ? 1 : 0); +} + +static inline uint8_t set_bit(uint8_t val, uint8_t bitnb, uint8_t biton) +{ + return (biton ? (val | (1 << bitnb)) : (val & ~(1 << bitnb))); +} + +static uint8_t crc8_calc(uint8_t *data, int len) +{ + int i; + uint8_t crc = 0; + uint8_t crcTable[256] = + { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 + }; + + for (i = 0; i < len; i++) + { + crc = crcTable[data[i] ^ crc]; + } + + return crc; +} + +static void pad_data(uint8_t *data, int len, uint8_t *dataPadded) +{ + int i; + uint8_t pad[] = + { + 0x01, 0x02, 0x22, 0x04, 0x20, 0x2A, 0x1F, 0x03, + 0x04, 0x06, 0x02, 0x0C, 0x2B, 0x2B, 0x01, 0x7B + }; + + for (i = 0; i < len; i++) + { + dataPadded[i] = data[i]; + } + + dataPadded[len] = 0x01; + + for (i = len + 1; i < 0x2F; i++) + { + dataPadded[i] = 0x00; + } + + dataPadded[0x2F] = len; + + for (i = 0; i < 16; i++) + { + dataPadded[0x30 + i] = pad[i]; + } +} + +static void hash_mode_01_custom_md5(uint8_t *data, uint8_t *hash) +{ + int i, j, s; + uint32_t a, b, c, d, f = 0, g; + + uint32_t T[] = + { + 0x783E16F6, 0xC267AC13, 0xA2B17F12, 0x6B8A31A4, + 0xF910654D, 0xB702DBCB, 0x266CEF60, 0x5145E47C, + 0xB92E00D6, 0xE80A4A64, 0x8A07FA77, 0xBA7D89A9, + 0xEBED8022, 0x653AAF2B, 0xF118B03B, 0x6CC16544, + 0x96EB6583, 0xF4E27E35, 0x1ABB119E, 0x068D3EF2, + 0xDAEAA8A5, 0x3C312A3D, 0x59538388, 0xA100772F, + 0xAB0165CE, 0x979959E7, 0x5DD8F53D, 0x189662BA, + 0xFD021A9C, 0x6BC2D338, 0x1EFF667E, 0x40C66888, + 0x6E9F07FF, 0x0CEF442F, 0x82D20190, 0x4E8CAEAC, + 0x0F7CB305, 0x2E73FBE7, 0x1CE884A2, 0x7A60BD52, + 0xC348B30D, 0x081CE3AA, 0xA12220E7, 0x38C7EC79, + 0xCBD8DD3A, 0x62B4FBA5, 0xAD2A63DB, 0xE4D0852E, + 0x53DE980F, 0x9C8DDA59, 0xA6B4CEDE, 0xB48A7692, + 0x0E2C46A4, 0xEB9367CB, 0x165D72EE, 0x75532B45, + 0xB9CA8E97, 0x08C8837B, 0x966F917B, 0x527515B4, + 0xF27A5E5D, 0xB71E6267, 0x7603D7E6, 0x9837DD69 + }; // CUSTOM T + + uint8_t r[] = + { + 0x06, 0x0A, 0x0F, 0x15, 0x05, 0x09, 0x0E, 0x14, + 0x04, 0x0B, 0x10, 0x17, 0x07, 0x0C, 0x11, 0x16 + }; // STANDARD REORDERED + + uint8_t tIdxInit[] = { 0, 1, 5, 0 }; // STANDARD + uint8_t tIdxIncr[] = { 1, 5, 3, 7 }; // STANDARD + + uint32_t h[] = { 0xEAD81D2E, 0xCE4DC6E9, 0xF9B5C301, 0x10325476 }; // CUSTOM h0, h1, h2, STANDARD h3 + uint32_t dataLongs[16]; + + for (i = 0; i < 16; i++) + { + dataLongs[i] = (data[4 * i + 0] << 0) + (data[4 * i + 1] << 8) + + (data[4 * i + 2] << 16) + (data[4 * i + 3] << 24); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + + for (i = 0; i < 4; i++) + { + g = tIdxInit[i]; + + for (j = 0; j < 16; j++) + { + if (i == 0) + { + f = (b & c) | (~b & d); + } + else if (i == 1) + { + f = (b & d) | (~d & c); + } + else if (i == 2) + { + f = (b ^ c ^ d); + } + else if (i == 3) + { + f = (~d | b) ^ c; + } + + f = dataLongs[g] + a + T[16 * i + j] + f; + + s = r[4 * i + (j & 3)]; + f = (f << s) | (f >> (32 - s)); + + a = d; + d = c; + c = b; + b += f; + + g = (g + tIdxIncr[i]) & 0xF; + } + } + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = h[i] >> 0; + hash[4 * i + 1] = h[i] >> 8; + hash[4 * i + 2] = h[i] >> 16; + hash[4 * i + 3] = h[i] >> 24; + } +} + +static void hash_mode_02(uint8_t *data, uint8_t *hash) +{ + int i; + uint32_t a, b, c, d, e, f = 0, tmp; + uint32_t h[] = { 0x81887F3A, 0x36CCA480, 0x99056FB1, 0x79705BAE }; + uint32_t dataLongs[80]; + + for (i = 0; i < 16; i++) + { + dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) + + (data[4 * i + 2] << 8) + (data[4 * i + 3] << 0); + } + + for (i = 0; i < 64; i++) + { + dataLongs[16 + i] = dataLongs[16 + i - 2]; + dataLongs[16 + i] ^= dataLongs[16 + i - 7]; + dataLongs[16 + i] ^= dataLongs[16 + i - 13]; + dataLongs[16 + i] ^= dataLongs[16 + i - 16]; + } + + a = dataLongs[0]; + b = dataLongs[1]; + c = dataLongs[2]; + d = dataLongs[3]; + e = dataLongs[4]; + + for (i = 0; i < 80; i++) + { + if (i < 0x15) f = (b & c) | (~b & d); + else if (i < 0x28) f = (b ^ c ^ d); + else if (i < 0x3D) f = (b & c) | (c & d) | (b & d); + else if (i < 0x50) f = (b ^ c ^ d); + + tmp = a; + a = e + f + (a << 5) + (a >> 27) + h[i / 0x14] + dataLongs[i]; + e = d; + d = c; + c = (b << 30) + (b >> 2); + b = tmp; + } + + dataLongs[0] += a; + dataLongs[1] += b; + dataLongs[2] += c; + dataLongs[3] += d; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = dataLongs[i] >> 24; + hash[4 * i + 1] = dataLongs[i] >> 16; + hash[4 * i + 2] = dataLongs[i] >> 8; + hash[4 * i + 3] = dataLongs[i] >> 0; + } +} + +static void hash_mode_03(uint8_t *data, uint8_t *hash) +{ + int i, j, k, s, s2, tmp; + uint32_t a, b, c, d, f = 0, g; + uint32_t a2, b2, c2, d2, f2 = 0, g2; + + uint32_t T[] = { 0xC88F3F2E, 0x967506BA, 0xDA877A7B, 0x0DECCDFE }; + uint32_t T2[] = { 0x01F42668, 0x39C7CDA5, 0xD490E2FE, 0x9965235D }; + + uint8_t r[] = + { + 0x0B, 0x0E, 0x0F, 0x0C, 0x05, 0x08, 0x07, 0x09, + 0x0B, 0x0D, 0x0E, 0x0F, 0x06, 0x07, 0x09, 0x08, + 0x07, 0x06, 0x08, 0x0D, 0x0B, 0x09, 0x07, 0x0F, + 0x07, 0x0C, 0x0F, 0x09, 0x0B, 0x07, 0x0D, 0x0C + }; + + uint8_t tIdxIncr[] = + { + 0x07, 0x04, 0x0D, 0x01, 0x0A, 0x06, 0x0F, 0x03, + 0x0C, 0x00, 0x09, 0x05, 0x02, 0x0E, 0x0B, 0x08, + 0x05, 0x0D, 0x02, 0x00, 0x04, 0x09, 0x03, 0x08, + 0x01, 0x0A, 0x07, 0x0B, 0x06, 0x0F, 0x0C, 0x0E + }; + + uint32_t h[] = { 0xC8616857, 0x9D3F5B8E, 0x4D7B8F76, 0x97BC8D80 }; + + uint32_t dataLongs[80]; + uint32_t result[4]; + + for (i = 0; i < 16; i++) + { + dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) + + (data[4 * i + 2] << 8) + (data[4 * i + 3] << 0); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + + a2 = h[3]; + b2 = h[2]; + c2 = h[1]; + d2 = h[0]; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 16; j++) + { + tmp = j; + + for (k = 0; k < i; k++) + { + tmp = tIdxIncr[tmp]; + } + + g = 0x0F - tmp; + g2 = tmp; + + if (i == 0) f = (b & d) | (~d & c); + else if (i == 1) f = (~c | b) ^ d; + else if (i == 2) f = (~b & d) | (b & c); + else if (i == 3) f = (b ^ c ^ d); + + if (i == 0) f2 = (b2 ^ c2 ^ d2); + else if (i == 1) f2 = (~b2 & d2) | (b2 & c2); + else if (i == 2) f2 = (~c2 | b2) ^ d2; + else if (i == 3) f2 = (b2 & d2) | (~d2 & c2); + + f = dataLongs[g] + a + T[i] + f; + s = r[0x0F + (((i & 1) ^ 1) << 4) - j]; + f = (f << s) | (f >> (32 - s)); + + f2 = dataLongs[g2] + a2 + T2[i] + f2; + s2 = r[((i & 1) << 4) + j]; + f2 = (f2 << s2) | (f2 >> (32 - s2)); + + a = d; + d = (c << 10) | (c >> 22); + c = b; + b = f; + + a2 = d2; + d2 = (c2 << 10) | (c2 >> 22); + c2 = b2; + b2 = f2; + } + } + + result[0] = h[3] + b + a2; + result[1] = h[2] + c + b2; + result[2] = h[1] + d + c2; + result[3] = h[0] + a + d2; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = result[i] >> 0; + hash[4 * i + 1] = result[i] >> 8; + hash[4 * i + 2] = result[i] >> 16; + hash[4 * i + 3] = result[i] >> 24; + } +} + +static const uint8_t table04[] = +{ + 0x02, 0x03, 0x07, 0x0B, 0x0D, 0x08, 0x00, 0x01, 0x2B, 0x2D, 0x28, 0x20, 0x21, 0x0A, 0x0C, 0x0E, + 0x22, 0x36, 0x23, 0x27, 0x29, 0x24, 0x25, 0x26, 0x2A, 0x3C, 0x3E, 0x3F, 0x0F, 0x2C, 0x2E, 0x2F, + 0x12, 0x13, 0x17, 0x1B, 0x1C, 0x18, 0x10, 0x11, 0x19, 0x14, 0x15, 0x16, 0x1A, 0x09, 0x04, 0x05, + 0x32, 0x33, 0x37, 0x3B, 0x06, 0x1C, 0x1E, 0x1F, 0x3D, 0x38, 0x30, 0x31, 0x39, 0x34, 0x35, 0x3A +}; + +static const uint8_t table05[] = +{ + 0x08, 0x09, 0x0A, 0x03, 0x04, 0x3F, 0x27, 0x28, 0x29, 0x2A, 0x05, 0x0B, 0x1B, 0x1C, 0x1C, 0x1E, + 0x20, 0x0C, 0x0D, 0x22, 0x23, 0x24, 0x00, 0x01, 0x02, 0x06, 0x07, 0x25, 0x26, 0x0E, 0x0F, 0x21, + 0x10, 0x11, 0x12, 0x2E, 0x2F, 0x13, 0x14, 0x15, 0x2B, 0x2C, 0x2D, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x30, 0x31, 0x37, 0x3B, 0x3C, 0x3D, 0x3E, 0x1F, 0x38, 0x39, 0x32, 0x33, 0x34, 0x35, 0x36, 0x3A +}; + +static const uint8_t table06[] = +{ + 0x00, 0x01, 0x02, 0x06, 0x07, 0x08, 0x03, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x04, 0x05, 0x09, 0x0D, + 0x20, 0x21, 0x22, 0x26, 0x27, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x10, 0x11, 0x12, 0x16, 0x17, 0x28, + 0x18, 0x13, 0x14, 0x15, 0x19, 0x1C, 0x1A, 0x1B, 0x1C, 0x1E, 0x1F, 0x23, 0x24, 0x25, 0x29, 0x2D, + 0x30, 0x31, 0x32, 0x36, 0x37, 0x38, 0x33, 0x34, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x35, 0x39, 0x3D +}; + +static const uint8_t table07[] = +{ + 0x10, 0x11, 0x12, 0x17, 0x1C, 0x1E, 0x0E, 0x38, 0x39, 0x3A, 0x13, 0x14, 0x29, 0x2A, 0x16, 0x1F, + 0x00, 0x01, 0x02, 0x3C, 0x3D, 0x3E, 0x3F, 0x07, 0x08, 0x09, 0x03, 0x04, 0x05, 0x06, 0x3B, 0x0A, + 0x20, 0x21, 0x22, 0x19, 0x1A, 0x1B, 0x1C, 0x0B, 0x0C, 0x15, 0x23, 0x24, 0x25, 0x26, 0x18, 0x0F, + 0x30, 0x31, 0x2B, 0x33, 0x34, 0x35, 0x36, 0x37, 0x27, 0x28, 0x2C, 0x2D, 0x2E, 0x2F, 0x32, 0x0D +}; + +static const uint8_t table08[] = +{ + 0x10, 0x11, 0x1E, 0x17, 0x18, 0x19, 0x12, 0x13, 0x14, 0x1C, 0x1C, 0x15, 0x0D, 0x05, 0x06, 0x0A, + 0x00, 0x01, 0x0E, 0x07, 0x08, 0x09, 0x02, 0x2D, 0x25, 0x26, 0x2A, 0x2B, 0x2F, 0x03, 0x04, 0x0C, + 0x20, 0x21, 0x2E, 0x27, 0x28, 0x29, 0x30, 0x31, 0x3E, 0x37, 0x38, 0x39, 0x22, 0x23, 0x24, 0x2C, + 0x32, 0x33, 0x34, 0x3C, 0x3D, 0x35, 0x36, 0x3A, 0x3B, 0x0B, 0x0F, 0x16, 0x1A, 0x1B, 0x1F, 0x3F +}; + +static const uint8_t table09[] = +{ + 0x20, 0x21, 0x24, 0x22, 0x23, 0x2A, 0x2B, 0x33, 0x35, 0x38, 0x39, 0x36, 0x2D, 0x2C, 0x2E, 0x2F, + 0x00, 0x01, 0x04, 0x02, 0x25, 0x28, 0x08, 0x09, 0x06, 0x07, 0x0A, 0x0B, 0x0D, 0x0C, 0x0E, 0x0F, + 0x10, 0x11, 0x14, 0x12, 0x13, 0x15, 0x19, 0x16, 0x29, 0x26, 0x03, 0x17, 0x1A, 0x1C, 0x1C, 0x1E, + 0x30, 0x31, 0x34, 0x32, 0x37, 0x3A, 0x3B, 0x3D, 0x3C, 0x3E, 0x3F, 0x1B, 0x05, 0x18, 0x27, 0x1F +}; + +static const uint8_t table0A[] = +{ + 0x00, 0x04, 0x05, 0x0B, 0x0C, 0x06, 0x09, 0x0A, 0x0E, 0x0D, 0x0F, 0x25, 0x15, 0x1B, 0x1C, 0x16, + 0x10, 0x11, 0x01, 0x02, 0x03, 0x07, 0x08, 0x12, 0x13, 0x17, 0x18, 0x14, 0x23, 0x27, 0x28, 0x24, + 0x30, 0x31, 0x32, 0x33, 0x37, 0x38, 0x34, 0x35, 0x3B, 0x3C, 0x20, 0x21, 0x22, 0x2B, 0x2C, 0x26, + 0x36, 0x39, 0x3A, 0x3E, 0x3D, 0x19, 0x1A, 0x1E, 0x1C, 0x1F, 0x3F, 0x29, 0x2A, 0x2E, 0x2D, 0x2F +}; + +static void hash_modes_04_to_0A_tables(uint8_t *data, uint8_t *hash, const uint8_t *table) +{ + int i; + + for (i = 0; i < 16; i++) + { + hash[i] = table[i]; + hash[i] ^= data[table[i]]; + hash[i] ^= table[16 + i]; + hash[i] ^= data[table[16 + i]]; + hash[i] ^= table[32 + i]; + hash[i] ^= data[table[32 + i]]; + hash[i] ^= table[48 + i]; + hash[i] ^= data[table[48 + i]]; + } +} + +static const uint8_t table0F[] = { 0xC7, 0x45, 0x15, 0x71, 0x61, 0x07, 0x05, 0x47 }; +static const uint8_t table10[] = { 0x0F, 0x47, 0x2B, 0x6C, 0xAD, 0x0F, 0xB3, 0xEA }; +static const uint8_t table11[] = { 0xB1, 0x46, 0xD1, 0x66, 0x5D, 0x28, 0x59, 0xD2 }; +static const uint8_t table12[] = { 0x0B, 0x4B, 0xD7, 0x68, 0x5F, 0xAD, 0x4B, 0xBB }; +static const uint8_t table13[] = { 0x4F, 0x4E, 0xE1, 0x6A, 0x21, 0xD3, 0xF7, 0xA6 }; +static const uint8_t table14[] = { 0xDD, 0x39, 0xB9, 0x65, 0x03, 0x91, 0xF1, 0xAC }; +static const uint8_t table15[] = { 0x3F, 0x50, 0xB5, 0x6F, 0x37, 0xC9, 0x13, 0x5D }; +static const uint8_t table16[] = { 0xF9, 0x5C, 0xFD, 0x72, 0x19, 0x42, 0x23, 0x6B }; +static const uint8_t table17[] = { 0xDF, 0x60, 0x93, 0x64, 0x33, 0x16, 0xB3, 0x8A }; +static const uint8_t table18[] = { 0x09, 0x64, 0x5F, 0x6B, 0xFB, 0x21, 0x19, 0xE4 }; + +static void hash_modes_0F_to_18_tables(uint8_t *data, uint8_t *hash, const uint8_t *table) +{ + int i; + uint32_t t[4], tmp; + + memset(hash, 0x00, 16); + + t[0] = (table[1] << 8) + table[0]; + t[1] = (table[3] << 8) + table[2]; + t[2] = (table[5] << 8) + table[4]; + t[3] = (table[7] << 8) + table[6]; + + for (i = 0; i < 60; i += 4) + { + t[0] = ((t[0] & 0xFFFF) * t[2]) + (t[0] >> 16); + t[1] = ((t[1] & 0xFFFF) * t[3]) + (t[1] >> 16); + tmp = t[0] + t[1]; + + hash[(i + 0) & 0x0F] = hash[(i + 0) & 0x0F] ^ data[i + 0] ^ (tmp >> 24); + hash[(i + 1) & 0x0F] = hash[(i + 1) & 0x0F] ^ data[i + 1] ^ (tmp >> 16); + hash[(i + 2) & 0x0F] = hash[(i + 2) & 0x0F] ^ data[i + 2] ^ (tmp >> 8); + hash[(i + 3) & 0x0F] = hash[(i + 3) & 0x0F] ^ data[i + 3] ^ (tmp >> 0); + } +} + +static const uint8_t table19[] = { 0x02, 0x03, 0x05, 0x10 }; +static const uint8_t table1A[] = { 0x01, 0x05, 0x08, 0x10 }; +static const uint8_t table1B[] = { 0x03, 0x07, 0x08, 0x10 }; +static const uint8_t table1C[] = { 0x03, 0x05, 0x0A, 0x10 }; +static const uint8_t table1D[] = { 0x03, 0x07, 0x0A, 0x10 }; +static const uint8_t table1E[] = { 0x01, 0x05, 0x0B, 0x10 }; +static const uint8_t table1F[] = { 0x06, 0x07, 0x0B, 0x10 }; +static const uint8_t table20[] = { 0x01, 0x08, 0x0B, 0x10 }; +static const uint8_t table21[] = { 0x01, 0x07, 0x0C, 0x10 }; +static const uint8_t table22[] = { 0x05, 0x0B, 0x0C, 0x10 }; +static const uint8_t table23[] = { 0x0B, 0x0C, 0x0D, 0x10 }; +static const uint8_t table24[] = { 0x07, 0x09, 0x0E, 0x10 }; +static const uint8_t table25[] = { 0x01, 0x04, 0x0F, 0x10 }; +static const uint8_t table26[] = { 0x07, 0x08, 0x0F, 0x10 }; +static const uint8_t table27[] = { 0x02, 0x0B, 0x0F, 0x10 }; + +static void hash_modes_19_to_27_tables_3(uint8_t *data, uint8_t *hash, const uint8_t *table) +{ + int i; + uint8_t val, it[4]; + uint16_t seed = 0xFFFF, tmp; + + memset(hash, 0x00, 16); + + for (i = 0; i < 4; i++) + { + it[i] = 0x10 - table[i]; + } + + for (i = 0; i < 16; i++) + { + val = ((seed >> it[0]) ^ (seed >> it[1]) ^ (seed >> it[2]) ^ (seed >> it[3])) & 0x01; + + if (val == 0x00) + { + seed = seed >> 1; + } + else + { + seed = (seed >> 1) | 0x8000; + } + tmp = seed + (data[i] << 8) + data[i + 32]; + + val = ((seed >> it[0]) ^ (seed >> it[1]) ^ (seed >> it[2]) ^ (seed >> it[3])) & 0x01; + + if (val == 0x00) + { + seed = seed >> 1; + } + else + { + seed = (seed >> 1) | 0x8000; + } + tmp = tmp + seed + (data[i + 16] << 8) + data[i + 48]; + + hash[i & 0x0F] ^= tmp >> 8; + hash[(i + 1) & 0x0F] ^= tmp; + } +} + +static void create_hash(uint8_t *data, int len, uint8_t *hash, int mode) +{ + if ((mode > 0x27) || (mode == 0x0B) || (mode == 0x0C) || + (mode == 0x0D) || (mode == 0x0E) || (mode == 0)) + { + memset(hash, 0, 16); + return; + } + + uint8_t dataPadded[64]; + + pad_data(data, len, dataPadded); + + switch (mode) + { + case 1: + hash_mode_01_custom_md5(dataPadded, hash); + break; + + case 2: + hash_mode_02(dataPadded, hash); + break; + + case 3: + hash_mode_03(dataPadded, hash); + break; + + case 4: + hash_modes_04_to_0A_tables(dataPadded, hash, table04); + break; + + case 5: + hash_modes_04_to_0A_tables(dataPadded, hash, table05); + break; + + case 6: + hash_modes_04_to_0A_tables(dataPadded, hash, table06); + break; + + case 7: + hash_modes_04_to_0A_tables(dataPadded, hash, table07); + break; + + case 8: + hash_modes_04_to_0A_tables(dataPadded, hash, table08); + break; + + case 9: + hash_modes_04_to_0A_tables(dataPadded, hash, table09); + break; + + case 10: + hash_modes_04_to_0A_tables(dataPadded, hash, table0A); + break; + + case 15: + hash_modes_0F_to_18_tables(dataPadded, hash, table0F); + break; + + case 16: + hash_modes_0F_to_18_tables(dataPadded, hash, table10); + break; + + case 17: + hash_modes_0F_to_18_tables(dataPadded, hash, table11); + break; + + case 18: + hash_modes_0F_to_18_tables(dataPadded, hash, table12); + break; + + case 19: + hash_modes_0F_to_18_tables(dataPadded, hash, table13); + break; + + case 20: + hash_modes_0F_to_18_tables(dataPadded, hash, table14); + break; + + case 21: + hash_modes_0F_to_18_tables(dataPadded, hash, table15); + break; + + case 22: + hash_modes_0F_to_18_tables(dataPadded, hash, table16); + break; + + case 23: + hash_modes_0F_to_18_tables(dataPadded, hash, table17); + break; + + case 24: + hash_modes_0F_to_18_tables(dataPadded, hash, table18); + break; + + case 25: + hash_modes_19_to_27_tables_3(dataPadded, hash, table19); + break; + + case 26: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1A); + break; + + case 27: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1B); + break; + + case 28: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1C); + break; + + case 29: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1D); + break; + + case 30: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1E); + break; + + case 31: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1F); + break; + + case 32: + hash_modes_19_to_27_tables_3(dataPadded, hash, table20); + break; + + case 33: + hash_modes_19_to_27_tables_3(dataPadded, hash, table21); + break; + + case 34: + hash_modes_19_to_27_tables_3(dataPadded, hash, table22); + break; + + case 35: + hash_modes_19_to_27_tables_3(dataPadded, hash, table23); + break; + + case 36: + hash_modes_19_to_27_tables_3(dataPadded, hash, table24); + break; + + case 37: + hash_modes_19_to_27_tables_3(dataPadded, hash, table25); + break; + + case 38: + hash_modes_19_to_27_tables_3(dataPadded, hash, table26); + break; + + case 39: + hash_modes_19_to_27_tables_3(dataPadded, hash, table27); + break; + + default: + cs_log("A new hash mode [%d] is in use.", mode); + break; + } +} + +static void create_hash_mode_03(uint8_t *data, uint8_t *hash) +{ + int i, j, c; + uint8_t buffer0[16], buffer1[8], buffer2[8], tmpBuff1[4], tmpBuff2[4]; + + uint8_t table[] = + { + 0x68, 0xCE, 0xE7, 0x71, 0xCC, 0x3A, 0x0B, 0x6E, 0x2A, 0x43, 0x17, 0x07, 0x5A, 0xD9, 0x14, 0x5B, + 0xB0, 0x8E, 0xA8, 0x7F, 0xD8, 0xA2, 0xCF, 0x73, 0xC2, 0xB9, 0x5D, 0x46, 0xDD, 0x2C, 0xE2, 0x2D, + 0xFD, 0x50, 0xE9, 0x7C, 0x28, 0x72, 0x9B, 0xAA, 0xEC, 0x24, 0x74, 0xAB, 0x00, 0x1C, 0x8B, 0x65, + 0x38, 0x13, 0x22, 0x82, 0xAC, 0x9A, 0x4D, 0x2B, 0xEA, 0x04, 0x31, 0x84, 0x32, 0x3D, 0x36, 0x53, + 0x5F, 0x42, 0x96, 0xDE, 0x47, 0x08, 0x51, 0x4B, 0x3E, 0xD1, 0x1E, 0x12, 0xD2, 0x1F, 0x7D, 0x26, + 0xCD, 0x57, 0x8C, 0xB6, 0xD3, 0xF8, 0x11, 0xAD, 0x6A, 0x88, 0x95, 0x21, 0xE8, 0xBF, 0x6B, 0x27, + 0xBE, 0xA3, 0x33, 0xB8, 0x9E, 0xB3, 0x6C, 0xC3, 0x06, 0xC7, 0x6F, 0x99, 0x97, 0xDA, 0x09, 0xAF, + 0xAE, 0xCB, 0x79, 0x37, 0x55, 0x85, 0x8D, 0x2F, 0x8A, 0x70, 0xA1, 0x7A, 0x66, 0x29, 0x67, 0x0F, + 0xEB, 0x9C, 0xC8, 0xC4, 0xD6, 0x4C, 0xDF, 0x1A, 0xC0, 0x01, 0x64, 0xBC, 0x4E, 0xE1, 0x54, 0xD7, + 0x4F, 0xB7, 0x5E, 0xCA, 0xF0, 0x91, 0xE4, 0x59, 0x4A, 0xC6, 0x83, 0x8F, 0xBD, 0x61, 0xFF, 0x56, + 0x92, 0xF1, 0x5C, 0x77, 0xC9, 0x20, 0xF4, 0xE5, 0x10, 0x69, 0x03, 0x1D, 0xD5, 0x45, 0xF6, 0x0E, + 0xEF, 0xA0, 0xE3, 0x58, 0xFC, 0xED, 0x80, 0x16, 0xEE, 0xFA, 0x02, 0xF5, 0xB4, 0x0A, 0xE0, 0x0C, + 0xF7, 0xF9, 0xBA, 0x7E, 0x18, 0x78, 0x19, 0xB5, 0x0D, 0x44, 0x34, 0xD4, 0xDC, 0x30, 0x6D, 0x3B, + 0x63, 0x41, 0x48, 0x40, 0xA7, 0xA5, 0xC5, 0x98, 0x76, 0x3F, 0xC1, 0x25, 0x93, 0x49, 0xD0, 0x62, + 0x2E, 0x75, 0xDB, 0x94, 0xF3, 0x52, 0x05, 0x81, 0xFB, 0xBB, 0xA6, 0x89, 0x39, 0xA4, 0xF2, 0xA9, + 0xFE, 0x60, 0x3C, 0x15, 0xB1, 0x35, 0x86, 0x9D, 0x9F, 0x90, 0x1B, 0xE6, 0x7B, 0x23, 0x87, 0xB2 + }; + + for (i = 0; i < 4; i++) + { + buffer0[0 + i] = data[12 + i]; + buffer0[4 + i] = data[8 + i]; + buffer0[8 + i] = data[4 + i]; + buffer0[12 + i] = data[0 + i]; + } + + for (c = 0; c < 12; c++) + { + for (i = 0; i < 4; i++) + { + buffer1[0 + i] = buffer0[8 + i] ^ buffer0[12 + i]; + buffer1[4 + i] = buffer0[0 + i] ^ buffer0[4 + i]; + } + + for (i = 0; i < 8; i++) + { + buffer1[i] = table[buffer1[i] ^ data[16 + 16 * (c % 3) + i]]; + } + + for (j = 0; j < 8; j++) + { + buffer2[j] = 0; + for (i = 0; i < 8; i++) + { + buffer2[j] ^= buffer1[i] * (j * i + 1); + } + } + + for (i = 0; i < 8; i++) + { + buffer2[i] = table[buffer2[i] ^ data[24 + 16 * (c % 3) + i]] ^ data[16 + 16 * (c % 3) + i]; + } + + for (i = 0; i < 4; i++) + { + buffer0[12 + i] ^= buffer2[0 + i]; + buffer0[8 + i] ^= buffer2[0 + i]; + buffer0[4 + i] ^= buffer2[4 + i]; + buffer0[0 + i] ^= buffer2[4 + i]; + } + + tmpBuff1[0] = buffer0[14]; + tmpBuff1[1] = buffer0[15]; + tmpBuff1[2] = buffer0[12] ^ buffer0[14]; + tmpBuff1[3] = buffer0[13] ^ buffer0[15]; + + tmpBuff2[0] = buffer0[6]; + tmpBuff2[1] = buffer0[7]; + tmpBuff2[2] = buffer0[4] ^ buffer0[6]; + tmpBuff2[3] = buffer0[5] ^ buffer0[7]; + + for (i = 0; i < 4; i++) + { + buffer0[12 + i] = tmpBuff1[i]; + buffer0[4 + i] = tmpBuff2[i]; + } + } + + for (i = 0; i < 4; i++) + { + hash[0 + i] = buffer0[12 + i] ^ data[0 + i]; + hash[4 + i] = buffer0[8 + i] ^ data[4 + i]; + hash[8 + i] = buffer0[4 + i] ^ data[8 + i]; + hash[12 + i] = buffer0[0 + i] ^ data[12 + i]; + } +} + +static void create_data_cw_mode_03(uint8_t *seed, int lenSeed, uint8_t *basecw, + uint8_t val, uint8_t *ecmBody, uint8_t *data) +{ + int idxData = 8, idxSeed = 0, idxBase = 0; + uint8_t padding[] = + { + 0x4A, 0x56, 0x7F, 0x16, 0xFC, 0x1F, 0x5B, 0x95, + 0x19, 0xEF, 0x75, 0x14, 0x0E, 0x9E, 0x17, 0x3C, + 0xF5, 0xB7, 0xA0, 0x93, 0xA3, 0x0F, 0xFA, 0x38, + 0x7A, 0x34, 0x6C, 0xDC, 0xFB, 0xB0, 0x24, 0x42, + 0x74, 0x72, 0x1C, 0xDC, 0x1E, 0xA1, 0x6D, 0xAB, + 0xC8, 0x44, 0x53, 0xEF, 0x56, 0x00, 0xE9, 0x97, + 0x48, 0x77, 0xF8, 0x00, 0x8E, 0x0B, 0x78, 0xA2 + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x0F]; + data[1] = ecmBody[0x09]; + data[2] = ecmBody[0x10]; + data[3] = ecmBody[0x11]; + data[4] = ecmBody[0x05]; + data[5] = ecmBody[0x07]; + data[6] = ecmBody[0x08]; + data[7] = ecmBody[0x0A]; + + while (idxBase < 7) + { + if ((idxBase == 0) || (idxBase == 2) || (idxBase == 5)) + { + data[idxData++] = val; + } + + if (idxSeed < lenSeed) + { + data[idxData++] = seed[idxSeed++]; + } + + data[idxData++] = basecw[idxBase++]; + } +} + +static void create_data_unmask_mode_03(uint8_t *ecmBody, uint8_t *data) +{ + uint8_t padding[] = + { + 0xB1, 0x7C, 0xD2, 0xA7, 0x5E, 0x45, 0x6C, 0x36, + 0xF0, 0xB6, 0x81, 0xF3, 0x25, 0x06, 0x65, 0x06, + 0x6B, 0xBF, 0x4C, 0xE7, 0xED, 0x6E, 0x85, 0x00, + 0xCC, 0xF2, 0x61, 0x48, 0x62, 0x24, 0x0E, 0x3C, + 0x05, 0x89, 0xA5, 0x39, 0x5A, 0x4E, 0x9B, 0xC8, + 0x14, 0x78, 0xEA, 0xB6, 0xFB, 0xF8, 0x10, 0xE6, + 0x61, 0xF5, 0x3A, 0xBC, 0x5B, 0x79, 0x09, 0x97 + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x17]; + data[1] = ecmBody[0x26]; + data[2] = ecmBody[0x19]; + data[3] = ecmBody[0x21]; + data[4] = ecmBody[0x26]; + data[5] = ecmBody[0x31]; + data[6] = ecmBody[0x21]; + data[7] = ecmBody[0x27]; +} + +static void hash_04_add(uint32_t *buffer, int a, int b, int c, int d, int e, int f) +{ + uint32_t tmp1 = (buffer[a] & 1) + (buffer[b] & 1); + uint32_t tmp2 = (buffer[a] >> 1) + (buffer[b] >> 1) + (tmp1 >> 1); + + buffer[e] = buffer[c] + buffer[d] + (tmp2 >> 31); + buffer[f] = tmp2 + tmp2 + (tmp1 & 1); +} + +static void hash_04_shift(uint32_t *buffer, int a, int b, uint8_t shift) +{ + uint32_t tmp1 = (buffer[a] >> (32 - shift)) + (buffer[b] << shift); + uint32_t tmp2 = (buffer[b] >> (32 - shift)) + (buffer[a] << shift); + + buffer[b] = tmp1; + buffer[a] = tmp2; +} + +static void hash_04_xor(uint32_t *buffer, int a, int b, int c, int d) +{ + buffer[a] ^= buffer[b]; + buffer[c] ^= buffer[d]; +} + +static void hash_04_swap(uint32_t *buffer, int a, int b) +{ + uint32_t tmp = buffer[a]; + + buffer[a] = buffer[b]; + buffer[b] = tmp; +} + +static void hash_04_core(uint32_t *buffer) +{ + hash_04_add(buffer, 0, 6, 7, 1, 7, 6); + hash_04_shift(buffer, 5, 4, 0x0D); + hash_04_xor(buffer, 4, 2, 5, 3); + hash_04_swap(buffer, 7, 6); + hash_04_add(buffer, 6, 2, 3, 7, 3, 2); + hash_04_shift(buffer, 1, 0, 0x10); + hash_04_xor(buffer, 0, 4, 1, 5); + hash_04_add(buffer, 6, 2, 3, 7, 7, 6); + hash_04_shift(buffer, 1, 0, 0x15); + hash_04_add(buffer, 6, 0, 1, 7, 1, 0); + hash_04_xor(buffer, 2, 4, 3, 5); + hash_04_shift(buffer, 5, 4, 0x11); + hash_04_xor(buffer, 4, 2, 5, 3); + hash_04_swap(buffer, 3, 2); +} + +static void create_hash_mode_04(uint8_t *data, uint8_t *hash) +{ + int i, j; + uint32_t d0, d1, h0, h1, h2, h3; + uint32_t buffer[] = + { + 0x1F253724, 0x3E8136B3, 0x9677CEDF, 0x25B5E75A, + 0x9494BC16, 0xCFD3FB34, 0xF37C75BB, 0x97D4632E + }; + + for (j = 0; j < 64; j += 8) + { + d0 = (data[j + 3] << 24) + (data[j + 2] << 16) + (data[j + 1] << 8) + data[j + 0]; + d1 = (data[j + 7] << 24) + (data[j + 6] << 16) + (data[j + 5] << 8) + data[j + 4]; + + buffer[0] ^= d0; + buffer[1] ^= d1; + + for (i = 0; i < 2; i++) + { + hash_04_core(buffer); + } + + buffer[6] ^= d0; + buffer[7] ^= d1; + } + + buffer[1] ^= 0x40000000; + buffer[0] ^= 0x00000000; + + for (i = 0; i < 2; i++) + { + hash_04_core(buffer); + } + + buffer[7] ^= 0x40000000; + buffer[6] ^= 0x00000000; + buffer[2] ^= 0xEE; + + for (i = 0; i < 4; i++) + { + hash_04_core(buffer); + } + + h0 = buffer[0] ^ buffer[2] ^ buffer[4] ^ buffer[6]; + h1 = buffer[1] ^ buffer[3] ^ buffer[5] ^ buffer[7]; + + hash[0] = (uint8_t) h0; + hash[1] = (uint8_t) (h0 >> 8); + hash[2] = (uint8_t) (h0 >> 16); + hash[3] = (uint8_t) (h0 >> 24); + hash[4] = (uint8_t) h1; + hash[5] = (uint8_t) (h1 >> 8); + hash[6] = (uint8_t) (h1 >> 16); + hash[7] = (uint8_t) (h1 >> 24); + + buffer[4] ^= 0xDD; + + for (i = 0; i < 4; i++) + { + hash_04_core(buffer); + } + + h2 = buffer[0] ^ buffer[2] ^ buffer[4] ^ buffer[6]; + h3 = buffer[1] ^ buffer[3] ^ buffer[5] ^ buffer[7]; + + hash[8] = (uint8_t) h2; + hash[9] = (uint8_t) (h2 >> 8); + hash[10] = (uint8_t) (h2 >> 16); + hash[11] = (uint8_t) (h2 >> 24); + hash[12] = (uint8_t) h3; + hash[13] = (uint8_t) (h3 >> 8); + hash[14] = (uint8_t) (h3 >> 16); + hash[15] = (uint8_t) (h3 >> 24); +} + +static void create_data_cw_mode_04(uint8_t *seed, int lenSeed, uint8_t *basecw, + uint8_t val, uint8_t *ecmBody, uint8_t *data) +{ + uint8_t padding[] = + { + 0x18, 0xD6, 0x24, 0xA8, 0xDE, 0x14, 0xD8, 0x30, + 0x3C, 0xB2, 0x24, 0x54, 0x17, 0x5A, 0x28, 0x61, + 0xBC, 0xB9, 0x29, 0xAD, 0xA5, 0x13, 0xD4, 0x24, + 0x6D, 0x61, 0x40, 0xC8, 0xFD, 0x27, 0xD7, 0xFF, + 0x3E, 0x84, 0x50, 0xC2, 0x47, 0x4C, 0xD5, 0xC5, + 0xF2, 0x79, 0xAD, 0x02, 0xC5, 0x05, 0x7B, 0xFD, + 0x60, 0x4A, 0x16, 0xE5, 0xAA, 0x0E, 0x97, 0x1C + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x0E]; + data[1] = ecmBody[0x0A]; + data[2] = ecmBody[0x0C]; + data[3] = ecmBody[0x04]; + data[4] = ecmBody[0x10]; + data[5] = ecmBody[0x08]; + data[6] = ecmBody[0x05]; + data[7] = ecmBody[0x0F]; + + int idxData = 8, idxSeed = 0, idxBase = 0; + + while (idxBase < 7) + { + if ((idxBase == 0) || (idxBase == 1) || (idxBase == 2)) + { + data[idxData++] = val; + } + + if (idxSeed < lenSeed) + { + data[idxData++] = seed[idxSeed++]; + } + + data[idxData++] = basecw[idxBase++]; + } +} + +static void create_data_unmask_mode_04(uint8_t *ecmBody, uint8_t *data) +{ + uint8_t padding[] = + { + 0x0E, 0x4A, 0x85, 0x85, 0xF9, 0xC0, 0xCC, 0x00, + 0xBA, 0x9B, 0x98, 0x35, 0x4C, 0xD2, 0xC1, 0x6C, + 0x87, 0x32, 0x9B, 0x82, 0x31, 0x5B, 0x1D, 0xB4, + 0xB8, 0x98, 0x74, 0xFF, 0x31, 0x66, 0x08, 0x79, + 0x47, 0xCE, 0x96, 0x4D, 0xE9, 0x52, 0xCF, 0x8F, + 0xEC, 0x5C, 0x07, 0xBC, 0x09, 0xA2, 0x82, 0x78, + 0x3D, 0xB9, 0xFF, 0x3F, 0x76, 0x72, 0x6F, 0x9C + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x17]; + data[1] = ecmBody[0x2B]; + data[2] = ecmBody[0x1D]; + data[3] = ecmBody[0x2D]; + data[4] = ecmBody[0x0B]; + data[5] = ecmBody[0x06]; + data[6] = ecmBody[0x2F]; + data[7] = ecmBody[0x1E]; +} + +static uint8_t get_mode_cw(uint8_t *extraData) +{ + uint64_t data = ((uint32_t)extraData[0] << 24) + (extraData[1] << 16) + (extraData[2] << 8) + extraData[3]; + uint64_t t1 = (data * 0x76E9DEA7) >> 50; + uint64_t t2 = (t1 * 0x51EB851F) >> 36; + uint64_t t3 = t2 * 0x32; + uint8_t r = t1 - t3; + return r; +} + +static uint8_t get_mode_unmask(uint8_t *extraData) +{ + uint64_t data = ((uint32_t)extraData[0] << 24) + (extraData[1] << 16) + (extraData[2] << 8) + extraData[3]; + uint64_t t1 = (data * 0xB9CD6BE5) >> 45; + uint64_t t2 = (t1 * 0x51EB851F) >> 36; + uint64_t t3 = t2 * 0x32; + uint8_t r = t1 - t3; + return r; +} + +static void create_data_ecm_emm(uint8_t *emmEcm, uint8_t *pos, int lenHeader, int len, uint8_t *data) +{ + int i; + + for (i = 0; i < len; i++) + { + data[i] = emmEcm[lenHeader + pos[i]]; + } +} + +static uint8_t create_data_cw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, + uint8_t val, uint8_t *seedEcmCw, uint8_t *data) +{ + int i; + + for (i = 0; i < lenSeed; i++) + { + data[i] = seed[i]; + } + + for (i = 0; i < 7; i++) + { + data[lenSeed + i] = baseCw[i]; + } + + data[lenSeed + 7] = val; + + for (i = 0; i < 16; i++) + { + data[lenSeed + 7 + 1 + i] = seedEcmCw[i]; + } + + return lenSeed + 7 + 1 + 0x10; +} + +static uint8_t unmask_ecm(uint8_t *ecm, uint8_t *seedEcmCw, uint8_t *modeCW) +{ + int i, l; + uint8_t data[64], mask[16]; + uint8_t hashModeEcm, hashModeCw, modeUnmask = 0; + uint32_t crc; + + uint8_t sourcePos[] = + { + 0x04, 0x05, 0x06, 0x07, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x17, 0x1C, 0x1D, 0x1F, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x29, 0x2C, 0x2D, 0x2E + }; + + uint8_t destPos[] = + { + 0x08, 0x09, 0x11, 0x18, 0x19, 0x1A, 0x1B, 0x1E, + 0x20, 0x21, 0x22, 0x28, 0x2A, 0x2B, 0x2F, 0x30 + }; + + uint8_t seedCwPos[] = { 0x07, 0x0A, 0x04, 0x0D, 0x05, 0x0E, 0x06, 0x0B, 0x10, 0x0C, 0x0F }; + + // Create seed for CW decryption + memset(seedEcmCw, 0, 16); + + int extraBytesLen = ecm[9]; + int startOffset = extraBytesLen + 10; + + for (i = 0; i < 11; i++) + { + seedEcmCw[i] = ecm[startOffset + seedCwPos[i]]; + } + + *modeCW = 0; + if (extraBytesLen > 0) + { + *modeCW = get_mode_cw(ecm + 10); + } + + // Read hash mode CW + hashModeCw = ecm[28 + extraBytesLen] ^ crc8_calc(seedEcmCw, 16); + + // Create mask for ECM decryption + create_data_ecm_emm(ecm, sourcePos, startOffset, 24, data); + + hashModeEcm = ecm[8] ^ crc8_calc(data, 24); + + if (extraBytesLen > 0) + { + modeUnmask = get_mode_unmask(ecm + 10); + } + + if (modeUnmask == 0x03) + { + ecm[startOffset + 0x21] -= ecm[startOffset + 0x07]; + ecm[startOffset + 0x26] -= ecm[startOffset + 0x05]; + ecm[startOffset + 0x26] -= ecm[startOffset + 0x08]; + ecm[startOffset + 0x19] -= ecm[startOffset + 0x06]; + ecm[startOffset + 0x31] -= ecm[startOffset + 0x09]; + ecm[startOffset + 0x27] -= ecm[startOffset + 0x0C]; + ecm[startOffset + 0x21] -= ecm[startOffset + 0x0B]; + ecm[startOffset + 0x17] -= ecm[startOffset + 0x04]; + + create_data_unmask_mode_03(ecm + startOffset, data); + create_hash_mode_03(data, mask); + + // Unmask body + ecm[startOffset + 0x06] ^= mask[0x02]; + ecm[startOffset + 0x0B] ^= mask[0x06]; + ecm[startOffset + 0x0C] ^= mask[0x07]; + ecm[startOffset + 0x0D] ^= mask[0x08]; + ecm[startOffset + 0x0E] ^= mask[0x09]; + ecm[startOffset + 0x0F] ^= mask[0x0A]; + ecm[startOffset + 0x11] ^= mask[0x0B]; + ecm[startOffset + 0x18] ^= mask[0x0C]; + ecm[startOffset + 0x2D] ^= mask[0x0A]; + ecm[startOffset + 0x07] ^= mask[0x03]; + ecm[startOffset + 0x1B] ^= mask[0x0D]; + ecm[startOffset + 0x30] ^= mask[0x0C]; + ecm[startOffset + 0x1C] ^= mask[0x0E]; + ecm[startOffset + 0x1E] ^= mask[0x00]; + ecm[startOffset + 0x04] ^= mask[0x00]; + ecm[startOffset + 0x05] ^= mask[0x01]; + ecm[startOffset + 0x1F] ^= mask[0x01]; + ecm[startOffset + 0x2C] ^= mask[0x09]; + ecm[startOffset + 0x20] ^= mask[0x02]; + ecm[startOffset + 0x1D] ^= mask[0x0F]; + ecm[startOffset + 0x23] ^= mask[0x04]; + ecm[startOffset + 0x09] ^= mask[0x05]; + ecm[startOffset + 0x22] ^= mask[0x03]; + ecm[startOffset + 0x24] ^= mask[0x05]; + ecm[startOffset + 0x08] ^= mask[0x04]; + ecm[startOffset + 0x28] ^= mask[0x06]; + ecm[startOffset + 0x29] ^= mask[0x07]; + ecm[startOffset + 0x2A] ^= mask[0x08]; + ecm[startOffset + 0x2E] ^= mask[0x0B]; + + for (i = 0; i < ecm[9]; i++) + { + ecm[10 + i] = 0x00; + } + } + else if (modeUnmask == 0x04) + { + ecm[startOffset + 0x1E] -= ecm[startOffset + 0x0D]; + ecm[startOffset + 0x1D] -= ecm[startOffset + 0x07]; + ecm[startOffset + 0x2B] -= ecm[startOffset + 0x05]; + ecm[startOffset + 0x2D] -= ecm[startOffset + 0x08]; + ecm[startOffset + 0x17] -= ecm[startOffset + 0x04]; + ecm[startOffset + 0x2F] -= ecm[startOffset + 0x0C]; + ecm[startOffset + 0x06] -= ecm[startOffset + 0x0A]; + ecm[startOffset + 0x0B] -= ecm[startOffset + 0x09]; + + create_data_unmask_mode_04(ecm + startOffset, data); + create_hash_mode_04(data, mask); + + // Unmask body + ecm[startOffset + 0x04] ^= mask[0x00]; + ecm[startOffset + 0x05] ^= mask[0x01]; + ecm[startOffset + 0x07] ^= mask[0x02]; + ecm[startOffset + 0x08] ^= mask[0x03]; + ecm[startOffset + 0x09] ^= mask[0x04]; + ecm[startOffset + 0x0A] ^= mask[0x05]; + ecm[startOffset + 0x0C] ^= mask[0x06]; + ecm[startOffset + 0x0D] ^= mask[0x07]; + ecm[startOffset + 0x0E] ^= mask[0x08]; + ecm[startOffset + 0x10] ^= mask[0x09]; + ecm[startOffset + 0x11] ^= mask[0x0A]; + ecm[startOffset + 0x18] ^= mask[0x0B]; + ecm[startOffset + 0x1A] ^= mask[0x0C]; + ecm[startOffset + 0x1B] ^= mask[0x0D]; + ecm[startOffset + 0x1C] ^= mask[0x0E]; + ecm[startOffset + 0x1F] ^= mask[0x0F]; + ecm[startOffset + 0x22] ^= mask[0x00]; + ecm[startOffset + 0x24] ^= mask[0x01]; + ecm[startOffset + 0x25] ^= mask[0x02]; + ecm[startOffset + 0x26] ^= mask[0x03]; + ecm[startOffset + 0x27] ^= mask[0x04]; + ecm[startOffset + 0x28] ^= mask[0x05]; + ecm[startOffset + 0x29] ^= mask[0x06]; + ecm[startOffset + 0x2A] ^= mask[0x07]; + ecm[startOffset + 0x2C] ^= mask[0x08]; + ecm[startOffset + 0x2E] ^= mask[0x09]; + ecm[startOffset + 0x31] ^= mask[0x0A]; + + for (i = 0; i < ecm[9]; i++) + { + ecm[10 + i] = 0x00; + } + } + else + { + create_hash(data, 24, mask, hashModeEcm); + + // Unmask body + for (i = 0; i < 16; i++) + { + ecm[startOffset + destPos[i]] ^= mask[i & 0x0F]; + } + } + + // Fix header + ecm[3] &= 0x0F; + ecm[3] |= 0x30; + ecm[8] = 0x00; + ecm[28 + extraBytesLen] = 0x00; + + // Fix CRC (optional) + l = (((ecm[1] << 8) + ecm[2]) & 0xFFF) + 3 - 4; + + crc = ccitt32_crc(ecm, l); + + ecm[l + 0] = crc >> 24; + ecm[l + 1] = crc >> 16; + ecm[l + 2] = crc >> 8; + ecm[l + 3] = crc >> 0; + + for (i = 0; i < 11; i++) + { + seedEcmCw[i] = ecm[startOffset + seedCwPos[i]]; + } + + return hashModeCw; +} + +static void create_cw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, uint8_t val, uint8_t *seedEcmCw, + uint8_t *cw, int modeDesCsa, int hashMode, int modeCW, uint8_t *ecmBody) +{ + int i; + uint8_t data[64], hash[16], lenData; + uint8_t tableFixParity[] = + { + 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07, + 0x08, 0x08, 0x0B, 0x0B, 0x0D, 0x0D, 0x0E, 0x0E, + 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16, + 0x19, 0x19, 0x1A, 0x1A, 0x1C, 0x1C, 0x1F, 0x1F, + 0x20, 0x20, 0x23, 0x23, 0x25, 0x25, 0x26, 0x26, + 0x29, 0x29, 0x2A, 0x2A, 0x2C, 0x2C, 0x2F, 0x2F, + 0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37, + 0x38, 0x38, 0x3B, 0x3B, 0x3D, 0x3D, 0x3E, 0x3E, + 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46, + 0x49, 0x49, 0x4A, 0x4A, 0x4C, 0x4C, 0x4F, 0x4F, + 0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57, + 0x58, 0x58, 0x5B, 0x5B, 0x5D, 0x5D, 0x5E, 0x5E, + 0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67, + 0x68, 0x68, 0x6B, 0x6B, 0x6D, 0x6D, 0x6E, 0x6E, + 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76, + 0x79, 0x79, 0x7A, 0x7A, 0x7C, 0x7C, 0x7F, 0x7F, + 0x80, 0x80, 0x83, 0x83, 0x85, 0x85, 0x86, 0x86, + 0x89, 0x89, 0x8A, 0x8A, 0x8C, 0x8C, 0x8F, 0x8F, + 0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97, + 0x98, 0x98, 0x9B, 0x9B, 0x9D, 0x9D, 0x9E, 0x9E, + 0xA1, 0xA1, 0xA2, 0xA2, 0xA4, 0xA4, 0xA7, 0xA7, + 0xA8, 0xA8, 0xAB, 0xAB, 0xAD, 0xAD, 0xAE, 0xAE, + 0xB0, 0xB0, 0xB3, 0xB3, 0xB5, 0xB5, 0xB6, 0xB6, + 0xB9, 0xB9, 0xBA, 0xBA, 0xBC, 0xBC, 0xBF, 0xBF, + 0xC1, 0xC1, 0xC2, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7, + 0xC8, 0xC8, 0xCB, 0xCB, 0xCD, 0xCD, 0xCE, 0xCE, + 0xD0, 0xD0, 0xD3, 0xD3, 0xD5, 0xD5, 0xD6, 0xD6, + 0xD9, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDF, 0xDF, + 0xE0, 0xE0, 0xE3, 0xE3, 0xE5, 0xE5, 0xE6, 0xE6, + 0xE9, 0xE9, 0xEA, 0xEA, 0xEC, 0xEC, 0xEF, 0xEF, + 0xF1, 0xF1, 0xF2, 0xF2, 0xF4, 0xF4, 0xF7, 0xF7, + 0xF8, 0xF8, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE + }; + + if (modeCW == 0x03) + { + create_data_cw_mode_03(seed, lenSeed, baseCw, val, ecmBody, data); + create_hash_mode_03(data, hash); + + cw[0] = hash[0x09]; + cw[1] = hash[0x01]; + cw[2] = hash[0x0F]; + cw[3] = hash[0x0E]; + cw[4] = hash[0x04]; + cw[5] = hash[0x02]; + cw[6] = hash[0x05]; + cw[7] = hash[0x0D]; + } + else if (modeCW == 0x04) + { + create_data_cw_mode_04(seed, lenSeed, baseCw, val, ecmBody, data); + create_hash_mode_04(data, hash); + + cw[0] = hash[0x08]; + cw[1] = hash[0x0F]; + cw[2] = hash[0x02]; + cw[3] = hash[0x0A]; + cw[4] = hash[0x06]; + cw[5] = hash[0x03]; + cw[6] = hash[0x09]; + cw[7] = hash[0x0D]; + } + else + { + lenData = create_data_cw(seed, lenSeed, baseCw, val, seedEcmCw, data); + create_hash(data, lenData, hash, hashMode); + + for (i = 0; i < 8; i++) + { + cw[i] = hash[i]; + } + } + + if (modeDesCsa == 0) // DES - Fix Parity Bits + { + for (i = 0; i < 8; i++) + { + cw[i] = tableFixParity[cw[i]]; + } + } + else if (modeDesCsa == 1) // CSA - Fix Checksums + { + cw[3] = cw[0] + cw[1] + cw[2]; + cw[7] = cw[4] + cw[5] + cw[6]; + } +} + +static uint32_t create_channel_hash(uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens) +{ + uint8_t buffer[8]; + uint32_t channel_hash = 0; + + if (ens) + { + i2b_buf(2, tsid, buffer); + i2b_buf(2, onid, buffer + 2); + i2b_buf(4, ens, buffer + 4); + + channel_hash = crc32(caid, buffer, sizeof(buffer)); + } + + return channel_hash; +} + +static uint16_t get_channel_group(uint32_t channel_hash) +{ + uint8_t tmp[2]; + uint16_t group = 0; + + if (channel_hash && emu_find_key('P', channel_hash, 0x00000000, "GROUP", tmp, 2, 0, 0, 0, NULL)) + { + group = b2i(2, tmp); + } + + return group; +} + +static inline int8_t get_ecm_key(uint8_t *key, uint32_t provider, uint32_t ignore_mask, uint8_t keyIndex, uint32_t keyRef) +{ + return emu_find_key('P', provider, ignore_mask, keyIndex == 1 ? "01" : "00", key, 7, 0, keyRef, 0, NULL); +} + +static inline int8_t get_emm_key(uint8_t *key, char *uniqueAddress, uint32_t keyRef, uint32_t *groupId) +{ + return emu_find_key('P', 0, 0xFFFFFFFF, uniqueAddress, key, 7, 0, keyRef, 0, groupId); +} + +static const uint8_t PowerVu_A0_S_1[16] = +{ + 0x33, 0xA4, 0x44, 0x3C, 0xCA, 0x2E, 0x75, 0x7B, + 0xBC, 0xE6, 0xE5, 0x35, 0xA0, 0x55, 0xC9, 0xA2 +}; + +static const uint8_t PowerVu_A0_S_2[16] = +{ + 0x5A, 0xB0, 0x2C, 0xBC, 0xDA, 0x32, 0xE6, 0x92, + 0x40, 0x53, 0x6E, 0xF9, 0x69, 0x11, 0x1E, 0xFB +}; + +static const uint8_t PowerVu_A0_S_3[16] = +{ + 0x4E, 0x18, 0x9B, 0x19, 0x79, 0xFB, 0x01, 0xFA, + 0xE3, 0xE1, 0x28, 0x3D, 0x32, 0xE4, 0x92, 0xEA +}; + +static const uint8_t PowerVu_A0_S_4[16] = +{ + 0x05, 0x6F, 0x37, 0x66, 0x35, 0xE1, 0x58, 0xD0, + 0xB4, 0x6A, 0x97, 0xAE, 0xD8, 0x91, 0x27, 0x56 +}; + +static const uint8_t PowerVu_A0_S_5[16] = +{ + 0x7B, 0x26, 0xAD, 0x34, 0x3D, 0x77, 0x39, 0x51, + 0xE0, 0xE0, 0x48, 0x8C, 0x39, 0xF5, 0xE8, 0x47 +}; + +static const uint8_t PowerVu_A0_S_6[16] = +{ + 0x74, 0xFA, 0x4D, 0x79, 0x42, 0x39, 0xD1, 0xA4, + 0x99, 0xA3, 0x97, 0x07, 0xDF, 0x14, 0x3A, 0xC4 +}; + +static const uint8_t PowerVu_A0_S_7[16] = +{ + 0xC6, 0x1E, 0x3C, 0x24, 0x11, 0x08, 0x5D, 0x6A, + 0xEB, 0x97, 0xB9, 0x25, 0xA7, 0xFA, 0xE9, 0x1A +}; + +static const uint8_t PowerVu_A0_S_8[16] = +{ + 0x9A, 0xAD, 0x72, 0xD7, 0x7C, 0x68, 0x3B, 0x55, + 0x1D, 0x4A, 0xA2, 0xB0, 0x38, 0xB9, 0x56, 0xD0 +}; + +static const uint8_t PowerVu_A0_S_9[32] = +{ + 0x61, 0xDA, 0x5F, 0xB7, 0xEB, 0xC6, 0x3F, 0x6C, + 0x09, 0xF3, 0x64, 0x38, 0x33, 0x08, 0xAA, 0x15, + 0xCC, 0xEF, 0x22, 0x64, 0x01, 0x2C, 0x12, 0xDE, + 0xF4, 0x6E, 0x3C, 0xCD, 0x1A, 0x64, 0x63, 0x7C +}; + +static const uint8_t PowerVu_00_S_1[16] = +{ + 0x97, 0x13, 0xEB, 0x6B, 0x04, 0x5E, 0x60, 0x3A, + 0xD9, 0xCC, 0x91, 0xC2, 0x5A, 0xFD, 0xBA, 0x0C +}; + +static const uint8_t PowerVu_00_S_2[16] = +{ + 0x61, 0x3C, 0x03, 0xB0, 0xB5, 0x6F, 0xF8, 0x01, + 0xED, 0xE0, 0xE5, 0xF3, 0x78, 0x0F, 0x0A, 0x73 +}; + +static const uint8_t PowerVu_00_S_3[16] = +{ + 0xFD, 0xDF, 0xD2, 0x97, 0x06, 0x14, 0x91, 0xB5, + 0x36, 0xAD, 0xBC, 0xE1, 0xB3, 0x00, 0x66, 0x41 +}; + +static const uint8_t PowerVu_00_S_4[16] = +{ + 0x8B, 0xD9, 0x18, 0x0A, 0xED, 0xEE, 0x61, 0x34, + 0x1A, 0x79, 0x80, 0x8C, 0x1E, 0x7F, 0xC5, 0x9F +}; + +static const uint8_t PowerVu_00_S_5[16] = +{ + 0xB0, 0xA1, 0xF2, 0xB8, 0xEA, 0x72, 0xDD, 0xD3, + 0x30, 0x65, 0x2B, 0x1E, 0xE9, 0xE1, 0x45, 0x29 +}; + +static const uint8_t PowerVu_00_S_6[16] = +{ + 0x5D, 0xCA, 0x53, 0x75, 0xB2, 0x24, 0xCE, 0xAF, + 0x21, 0x54, 0x9E, 0xBE, 0x02, 0xA9, 0x4C, 0x5D +}; + +static const uint8_t PowerVu_00_S_7[16] = +{ + 0x42, 0x66, 0x72, 0x83, 0x1B, 0x2D, 0x22, 0xC9, + 0xF8, 0x4D, 0xBA, 0xCD, 0xBB, 0x20, 0xBD, 0x6B +}; + +static const uint8_t PowerVu_00_S_8[16] = +{ + 0xC4, 0x0C, 0x6B, 0xD3, 0x6D, 0x94, 0x7E, 0x53, + 0xCE, 0x96, 0xAC, 0x40, 0x2C, 0x7A, 0xD3, 0xA9 +}; + +static const uint8_t PowerVu_00_S_9[32] = +{ + 0x31, 0x82, 0x4F, 0x9B, 0xCB, 0x6F, 0x9D, 0xB7, + 0xAE, 0x68, 0x0B, 0xA0, 0x93, 0x15, 0x32, 0xE2, + 0xED, 0xE9, 0x47, 0x29, 0xC2, 0xA8, 0x92, 0xEF, + 0xBA, 0x27, 0x22, 0x57, 0x76, 0x54, 0xC0, 0x59 +}; + +static uint8_t powervu_sbox(uint8_t *input, uint8_t mode) +{ + uint8_t s_index, bit, last_index, last_bit; + uint8_t const *Sbox1, *Sbox2, *Sbox3, *Sbox4, *Sbox5, *Sbox6, *Sbox7, *Sbox8, *Sbox9; + + if (mode) + { + Sbox1 = PowerVu_A0_S_1; + Sbox2 = PowerVu_A0_S_2; + Sbox3 = PowerVu_A0_S_3; + Sbox4 = PowerVu_A0_S_4; + Sbox5 = PowerVu_A0_S_5; + Sbox6 = PowerVu_A0_S_6; + Sbox7 = PowerVu_A0_S_7; + Sbox8 = PowerVu_A0_S_8; + Sbox9 = PowerVu_A0_S_9; + } + else + { + Sbox1 = PowerVu_00_S_1; + Sbox2 = PowerVu_00_S_2; + Sbox3 = PowerVu_00_S_3; + Sbox4 = PowerVu_00_S_4; + Sbox5 = PowerVu_00_S_5; + Sbox6 = PowerVu_00_S_6; + Sbox7 = PowerVu_00_S_7; + Sbox8 = PowerVu_00_S_8; + Sbox9 = PowerVu_00_S_9; + } + + bit = (get_bit(input[2], 0) << 2) | (get_bit(input[3], 4) << 1) | (get_bit(input[5], 3)); + s_index = (get_bit(input[0], 0) << 3) | (get_bit(input[2], 6) << 2) | (get_bit(input[2], 4) << 1) | (get_bit(input[5], 7)); + last_bit = get_bit(Sbox1[s_index], 7 - bit); + + bit = (get_bit(input[5], 0) << 2) | (get_bit(input[4], 0) << 1) | (get_bit(input[6], 2)); + s_index = (get_bit(input[2], 1) << 3) | (get_bit(input[2], 2) << 2) | (get_bit(input[5], 5) << 1) | (get_bit(input[5], 1)); + last_bit = last_bit | (get_bit(Sbox2[s_index], 7 - bit) << 1); + + bit = (get_bit(input[6], 0) << 2) | (get_bit(input[1], 7) << 1) | (get_bit(input[6], 7)); + s_index = (get_bit(input[1], 3) << 3) | (get_bit(input[3], 7) << 2) | (get_bit(input[1], 5) << 1) | (get_bit(input[5], 2)); + last_bit = last_bit | (get_bit(Sbox3[s_index], 7 - bit) << 2); + + bit = (get_bit(input[1], 0) << 2) | (get_bit(input[2], 7) << 1) | (get_bit(input[2], 5)); + s_index = (get_bit(input[6], 3) << 3) | (get_bit(input[6], 4) << 2) | (get_bit(input[6], 6) << 1) | (get_bit(input[3], 5)); + last_index = get_bit(Sbox4[s_index], 7 - bit); + + bit = (get_bit(input[3], 3) << 2) | (get_bit(input[4], 6) << 1) | (get_bit(input[3], 2)); + s_index = (get_bit(input[3], 1) << 3) | (get_bit(input[4], 5) << 2) | (get_bit(input[3], 0) << 1) | (get_bit(input[4], 7)); + last_index = last_index | (get_bit(Sbox5[s_index], 7 - bit) << 1); + + bit = (get_bit(input[5], 4) << 2) | (get_bit(input[4], 4) << 1) | (get_bit(input[1], 2)); + s_index = (get_bit(input[2], 3) << 3) | (get_bit(input[6], 5) << 2) | (get_bit(input[1], 4) << 1) | (get_bit(input[4], 1)); + last_index = last_index | (get_bit(Sbox6[s_index], 7 - bit) << 2); + + bit = (get_bit(input[0], 6) << 2) | (get_bit(input[0], 7) << 1) | (get_bit(input[0], 4)); + s_index = (get_bit(input[0], 5) << 3) | (get_bit(input[0], 3) << 2) | (get_bit(input[0], 1) << 1) | (get_bit(input[0], 2)); + last_index = last_index | (get_bit(Sbox7[s_index], 7 - bit) << 3); + + bit = (get_bit(input[4], 2) << 2) | (get_bit(input[4], 3) << 1) | (get_bit(input[1], 1)); + s_index = (get_bit(input[1], 6) << 3) | (get_bit(input[6], 1) << 2) | (get_bit(input[5], 6) << 1) | (get_bit(input[3], 6)); + last_index = last_index | (get_bit(Sbox8[s_index], 7 - bit) << 4); + + return (get_bit(Sbox9[last_index & 0x1F], 7 - last_bit) & 1) ? 1 : 0; +} + +static void powervu_decrypt(uint8_t *data, uint32_t length, uint8_t *key, uint8_t sbox) +{ + uint32_t i; + int32_t j, k; + uint8_t curByte, tmpBit; + + for (i = 0; i < length; i++) + { + curByte = data[i]; + + for (j = 7; j >= 0; j--) + { + data[i] = set_bit(data[i], j, (get_bit(curByte, j) ^ powervu_sbox(key, sbox)) ^ get_bit(key[0], 7)); + tmpBit = get_bit(data[i], j) ^ (get_bit(key[6], 0)); + + if (tmpBit) + { + key[3] ^= 0x10; + } + + for (k = 6; k > 0; k--) + { + key[k] = (key[k] >> 1) | (key[k - 1] << 7); + } + + key[0] = (key[0] >> 1); + key[0] = set_bit(key[0], 7, tmpBit); + } + } +} + +static void expand_des_key(unsigned char *key) +{ + uint8_t i, j, parity; + uint8_t tmpKey[7]; + + memcpy(tmpKey, key, 7); + + key[0] = (tmpKey[0] & 0xFE); + key[1] = ((tmpKey[0] << 7) | ((tmpKey[1] >> 1) & 0xFE)); + key[2] = ((tmpKey[1] << 6) | ((tmpKey[2] >> 2) & 0xFE)); + key[3] = ((tmpKey[2] << 5) | ((tmpKey[3] >> 3) & 0xFE)); + key[4] = ((tmpKey[3] << 4) | ((tmpKey[4] >> 4) & 0xFE)); + key[5] = ((tmpKey[4] << 3) | ((tmpKey[5] >> 5) & 0xFE)); + key[6] = ((tmpKey[5] << 2) | ((tmpKey[6] >> 6) & 0xFE)); + key[7] = (tmpKey[6] << 1); + + for (i = 0; i < 8; i++) + { + parity = 1; + + for (j = 1; j < 8; j++) + { + if ((key[i] >> j) & 0x1) + { + parity = ~parity & 0x01; + } + } + + key[i] |= parity; + } +} + +static uint8_t get_conv_cw_index(uint8_t ecmTag) +{ + switch (ecmTag) + { + case PVU_CONVCW_VID_ECM: + return PVU_CW_VID; + + case PVU_CONVCW_HSD_ECM: + return PVU_CW_HSD; + + case PVU_CONVCW_A1_ECM: + return PVU_CW_A1; + + case PVU_CONVCW_A2_ECM: + return PVU_CW_A2; + + case PVU_CONVCW_A3_ECM: + return PVU_CW_A3; + + case PVU_CONVCW_A4_ECM: + return PVU_CW_A4; + + case PVU_CONVCW_UTL_ECM: + return PVU_CW_UTL; + + case PVU_CONVCW_VBI_ECM: + return PVU_CW_VBI; + + default: + return PVU_CW_VBI; + } +} + +static uint16_t get_seed_iv(uint8_t seedType, uint8_t *ecm) +{ + switch (seedType) + { + case PVU_CW_VID: + return ((ecm[0x10] & 0x1F) << 3) | 0; + + case PVU_CW_HSD: + return ((ecm[0x12] & 0x1F) << 3) | 2; + + case PVU_CW_A1: + return ((ecm[0x11] & 0x3F) << 3) | 1; + + case PVU_CW_A2: + return ((ecm[0x13] & 0x3F) << 3) | 1; + + case PVU_CW_A3: + return ((ecm[0x19] & 0x3F) << 3) | 1; + + case PVU_CW_A4: + return ((ecm[0x1A] & 0x3F) << 3) | 1; + + case PVU_CW_UTL: + return ((ecm[0x14] & 0x0F) << 3) | 4; + + case PVU_CW_VBI: + return (((ecm[0x15] & 0xF8) >> 3) << 3) | 5; + + default: + return 0; + } +} + +static uint8_t expand_seed(uint8_t seedType, uint8_t *seed) +{ + uint8_t seedLength = 0, i; + + switch (seedType) + { + case PVU_CW_VID: + case PVU_CW_HSD: + seedLength = 4; + break; + + case PVU_CW_A1: + case PVU_CW_A2: + case PVU_CW_A3: + case PVU_CW_A4: + seedLength = 3; + break; + + case PVU_CW_UTL: + case PVU_CW_VBI: + seedLength = 2; + break; + + default: + return seedLength; + } + + for (i = seedLength; i < 7; i++) + { + seed[i] = seed[i % seedLength]; + } + + return seedLength; +} + +static void calculate_seed(uint8_t seedType, uint8_t *ecm, uint8_t *seedBase, + uint8_t *key, uint8_t *seed, uint8_t sbox) +{ + uint16_t tmpSeed; + + tmpSeed = get_seed_iv(seedType, ecm + 23); + + seed[0] = (tmpSeed >> 2) & 0xFF; + seed[1] = ((tmpSeed & 0x3) << 6) | (seedBase[0] >> 2); + seed[2] = ( seedBase[0] << 6) | (seedBase[1] >> 2); + seed[3] = ( seedBase[1] << 6) | (seedBase[2] >> 2); + seed[4] = ( seedBase[2] << 6) | (seedBase[3] >> 2); + seed[5] = ( seedBase[3] << 6); + + powervu_decrypt(seed, 6, key, sbox); + + seed[0] = (seed[1] << 2) | (seed[2] >> 6); + seed[1] = (seed[2] << 2) | (seed[3] >> 6); + seed[2] = (seed[3] << 2) | (seed[4] >> 6); + seed[3] = (seed[4] << 2) | (seed[5] >> 6); +} + +static void calculate_cw(uint8_t seedType, uint8_t *seed, uint8_t csaUsed, uint8_t *convolvedCw, + uint8_t *cw, uint8_t *baseCw, uint8_t *seedEcmCw, uint8_t hashModeCw, + uint8_t needsUnmasking, uint8_t xorMode, int modeCW, uint8_t* ecmBody) +{ + int32_t k; + uint8_t seedLength, val = 0; + + seedLength = expand_seed(seedType, seed); + + if (needsUnmasking && (((modeCW >= 0x00) && (hashModeCw > 0) && (hashModeCw <= 0x27) && + (hashModeCw != 0x0B) && (hashModeCw != 0x0C) && (hashModeCw != 0x0D) && (hashModeCw != 0x0E)) || + (modeCW == 0x03) || (modeCW == 0x04))) + { + switch (seedType) + { + case PVU_CW_VID: + val = 0; + break; + + case PVU_CW_A1: + case PVU_CW_A2: + case PVU_CW_A3: + case PVU_CW_A4: + val = 1; + break; + + case PVU_CW_HSD: + val = 2; + break; + + case PVU_CW_UTL: + val = 4; + break; + + case PVU_CW_VBI: + val = 5; + break; + } + + create_cw(seed, seedLength, baseCw, val, seedEcmCw, cw, csaUsed, hashModeCw, modeCW, ecmBody); + + if (csaUsed) + { + cw[0] = cw[0] ^ convolvedCw[0]; + cw[1] = cw[1] ^ convolvedCw[1]; + cw[2] = cw[2] ^ convolvedCw[2]; + cw[3] = cw[3] ^ convolvedCw[3]; + cw[4] = cw[4] ^ convolvedCw[4]; + cw[5] = cw[5] ^ convolvedCw[5]; + cw[6] = cw[6] ^ convolvedCw[6]; + cw[7] = cw[7] ^ convolvedCw[7]; + + cw[3] = cw[0] + cw[1] + cw[2]; + cw[7] = cw[4] + cw[5] + cw[6]; + } + } + else + { + if (csaUsed) + { + for (k = 0; k < 7; k++) + { + seed[k] ^= baseCw[k]; + } + + cw[0] = seed[0] ^ convolvedCw[0]; + cw[1] = seed[1] ^ convolvedCw[1]; + cw[2] = seed[2] ^ convolvedCw[2]; + cw[3] = seed[3] ^ convolvedCw[3]; + cw[4] = seed[3] ^ convolvedCw[4]; + cw[5] = seed[4] ^ convolvedCw[5]; + cw[6] = seed[5] ^ convolvedCw[6]; + cw[7] = seed[6] ^ convolvedCw[7]; + } + else + { + if (xorMode == 0) + { + for (k = 0; k < 7; k++) + { + cw[k] = seed[k] ^ baseCw[k]; + } + } + + if (xorMode == 1) + { + for (k = 0; k < 3; k++) + { + cw[k] = seed[k] ^ baseCw[k]; + } + + for (k = 3; k < 7; k++) + { + cw[k] = baseCw[k]; + } + } + + expand_des_key(cw); + } + } +} + +#ifdef MODULE_STREAMRELAY +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens, emu_stream_client_key_data *cdata) +#else +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens) +#endif +{ + uint32_t i, j, k; + uint32_t ecmCrc32, keyRef0, keyRef1, keyRef2, channel_hash, group_id = 0; + + uint16_t ecmLen = SCT_LEN(ecm); + uint16_t nanoLen, channelId, ecmSrvid; + + uint8_t keyIndex, sbox, decrypt_ok, calculateAll, hashModeCw = 0, needsUnmasking, xorMode; + uint8_t nanoCmd, nanoChecksum, keyType, fixedKey, oddKey, bid, csaUsed, modeCW = 0, offsetBody; + + uint8_t ecmKey[7], tmpEcmKey[7], seedBase[4], baseCw[7], seed[8][8], cw[8][8], convolvedCw[8][8]; + uint8_t ecmPart1[14], ecmPart2[27], unmaskedEcm[ecmLen], seedEcmCw[16]; + + //char tmpBuffer1[512]; + char tmpBuffer2[17]; + +#ifdef MODULE_STREAMRELAY + emu_stream_cw_item *cw_item; + int8_t update_global_key = 0; + int8_t update_global_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; + + memset(update_global_keys, 0, sizeof(update_global_keys)); +#else +#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4 +#endif + + if (ecmLen < 7) + { + return EMU_NOT_SUPPORTED; + } + + needsUnmasking = (ecm[3] & 0xF0) == 0x50; + + //cs_log_dbg(D_ATR, "ecm1: %s", cs_hexdump(0, ecm, ecmLen, tmpBuffer1, sizeof(tmpBuffer1))); + + if (needsUnmasking) + { + hashModeCw = unmask_ecm(ecm, seedEcmCw, &modeCW); + } + + //cs_log_dbg(D_ATR, "needsUnmasking=%d", needsUnmasking); + //cs_log_dbg(D_ATR, "ecm2: %s", cs_hexdump(0, ecm, ecmLen, tmpBuffer1, sizeof(tmpBuffer1))); + + memcpy(unmaskedEcm, ecm, ecmLen); + + ecmCrc32 = b2i(4, ecm + ecmLen - 4); + + if (ccitt32_crc(ecm, ecmLen - 4) != ecmCrc32) + { + return EMU_CHECKSUM_ERROR; + } + ecmLen -= 4; + + for (i = 0; i < 8; i++) + { + memset(convolvedCw[i], 0, 8); + } + + for (i = 3; i + 3 < ecmLen; ) + { + nanoLen = (((ecm[i] & 0x0F) << 8) | ecm[i + 1]); + i += 2; + + if (nanoLen > 0) + { + nanoLen--; + } + nanoCmd = ecm[i++]; + + if (i + nanoLen > ecmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x27: + if (nanoLen < 15) + { + break; + } + + nanoChecksum = 0; + for (j = 4; j < 15; j++) + { + nanoChecksum += ecm[i + j]; + } + + if (nanoChecksum != 0) + { + break; + } + + keyType = get_conv_cw_index(ecm[i + 4]); + memcpy(convolvedCw[keyType], &ecm[i + 6], 8); + break; + + default: + break; + } + + i += nanoLen; + } + + for (i = 3; i + 3 < ecmLen; ) + { + nanoLen = (((ecm[i] & 0x0F) << 8) | ecm[i + 1]); + i += 2; + + if (nanoLen > 0) + { + nanoLen--; + } + nanoCmd = ecm[i++]; + + if (i + nanoLen > ecmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x20: + { + if (nanoLen < 54) + { + break; + } + + offsetBody = i + 4 + ecm[i + 3]; + i += ecm[i + 3]; // Extra Data Length + + csaUsed = get_bit(ecm[i + 7], 7); + fixedKey = !get_bit(ecm[i + 6], 5); + oddKey = get_bit(ecm[i + 6], 4); + xorMode = get_bit(ecm[i + 6], 0); + bid = (get_bit(ecm[i + 7], 1) << 1) | get_bit(ecm[i + 7], 0); + sbox = get_bit(ecm[i + 6], 3); + + keyIndex = (fixedKey << 3) | (bid << 2) | oddKey; + channelId = b2i(2, ecm + i + 23); + ecmSrvid = (channelId >> 4) | ((channelId & 0xF) << 12); + + cs_log_dbg(D_ATR, "csaUsed: %d, xorMode: %d, ecmSrvid: %04X (%d), hashModeCw: %d, modeCW: %d", + csaUsed, xorMode, ecmSrvid, srvid, hashModeCw, modeCW); + + channel_hash = create_channel_hash(caid, tsid, onid, ens); + group_id = get_channel_group(channel_hash); + + cs_log_dbg(D_ATR, "channel hash: %08X, group id: %04X", channel_hash, group_id); + + decrypt_ok = 0; + + memcpy(ecmPart1, ecm + i + 8, 14); + memcpy(ecmPart2, ecm + i + 27, 27); + + keyRef0 = 0; + keyRef1 = 0; + keyRef2 = 0; + + do + { + if (!group_id || !get_ecm_key(ecmKey, group_id << 16, 0x0000FFFF, keyIndex, keyRef0++)) + { + if (!get_ecm_key(ecmKey, ecmSrvid, 0xFFFF0000, keyIndex, keyRef1++)) + { + if (!get_ecm_key(ecmKey, channelId, 0xFFFF0000, keyIndex, keyRef2++)) + { + cs_log("Key not found or invalid: P ****%04X %02X", ecmSrvid, keyIndex); + + if (group_id) // Print only if there is a matching "GROUP" entry + { + cs_log("Key not found or invalid: P %04XFFFF %02X", group_id, keyIndex); + } + + return EMU_KEY_NOT_FOUND; + } + } + } + + powervu_decrypt(ecm + i + 8, 14, ecmKey, sbox); + + if ((ecm[i + 6] != ecm[i + 6 + 7]) || (ecm[i + 6 + 8] != ecm[i + 6 + 15])) + { + memcpy(ecm + i + 8, ecmPart1, 14); + continue; + } + + memcpy(tmpEcmKey, ecmKey, 7); + + powervu_decrypt(ecm + i + 27, 27, ecmKey, sbox); + + if ((ecm[i + 23] != ecm[i + 23 + 29]) || (ecm[i + 23 + 1] != ecm[i + 23 + 30])) + { + memcpy(ecm + i + 8, ecmPart1, 14); + memcpy(ecm + i + 27, ecmPart2, 27); + continue; + } + + decrypt_ok = 1; + } + while (!decrypt_ok); + + memcpy(seedBase, ecm + i + 6 + 2, 4); + +#ifdef MODULE_STREAMRELAY + if (cdata == NULL) + { + SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); + for (j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++) + { + if (!stream_server_has_ecm[j] && emu_stream_cur_srvid[j] == srvid) + { + update_global_key = 1; + update_global_keys[j] = 1; + } + } + SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex); + } + + calculateAll = cdata != NULL || update_global_key || cw_ex != NULL; +#else + calculateAll = cw_ex != NULL; +#endif + + if (calculateAll) // Calculate all seeds + { + for (j = 0; j < 8; j++) + { + memcpy(ecmKey, tmpEcmKey, 7); + calculate_seed(j, ecm + i, seedBase, ecmKey, seed[j], sbox); + } + } + else // Calculate only video seed + { + memcpy(ecmKey, tmpEcmKey, 7); + calculate_seed(PVU_CW_VID, ecm + i, seedBase, ecmKey, seed[PVU_CW_VID], sbox); + } + + memcpy(baseCw, ecm + i + 6 + 8, 7); + + if (calculateAll) // Calculate all CWs + { + for (j = 0; j < 8; j++) + { + calculate_cw(j, seed[j], csaUsed, convolvedCw[j], cw[j], baseCw, seedEcmCw, + hashModeCw, needsUnmasking, xorMode, modeCW, unmaskedEcm + offsetBody); + + if (csaUsed) + { + for (k = 0; k < 8; k += 4) + { + cw[j][k + 3] = ((cw[j][k] + cw[j][k + 1] + cw[j][k + 2]) & 0xFF); + } + } + + cs_log_dbg(D_ATR, "calculated cw %d: %s", j, + cs_hexdump(0, cw[j], 8, tmpBuffer2, sizeof(tmpBuffer2))); + } + + //cs_log_dbg(D_ATR, "csaUsed=%d, cw: %s cdata=%x, cw_ex=%x", + // csaUsed, cs_hexdump(3, cw[0], 8, tmpBuffer1, sizeof(tmpBuffer1)), + // (unsigned int)cdata, (unsigned int)cw_ex); + +#ifdef MODULE_STREAMRELAY + if (update_global_key) + { + for (j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++) + { + if (update_global_keys[j]) + { + cw_item = (emu_stream_cw_item *)malloc(sizeof(emu_stream_cw_item)); + if (cw_item != NULL) + { + cw_item->csa_used = csaUsed; + cw_item->is_even = ecm[0] == 0x80 ? 1 : 0; + cs_ftime(&cw_item->write_time); + add_ms_to_timeb(&cw_item->write_time, cfg.emu_stream_ecm_delay); + memcpy(cw_item->cw, cw, sizeof(cw)); + ll_append(ll_emu_stream_delayed_keys[j], cw_item); + } + } + } + } + + if (cdata != NULL) + { + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++) + { + if (csaUsed) + { + if (ecm[0] == 0x80) + { + if (has_dvbcsa_ecm) + { + dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][EVEN]); + } + } + else + { + if (has_dvbcsa_ecm) + { + dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][ODD]); + } + } + + cdata->csa_used = 1; + } + else + { + if (ecm[0] == 0x80) + { + des_set_key(cw[j], cdata->pvu_des_ks[j][0]); + } + else + { + des_set_key(cw[j], cdata->pvu_des_ks[j][1]); + } + + cdata->csa_used = 0; + } + } + } +#endif + + if (cw_ex != NULL) + { + cw_ex->mode = CW_MODE_MULTIPLE_CW; + + if (csaUsed) + { + cw_ex->algo = CW_ALGO_CSA; + cw_ex->algo_mode = CW_ALGO_MODE_CBC; + } + else + { + cw_ex->algo = CW_ALGO_DES; + cw_ex->algo_mode = CW_ALGO_MODE_ECB; + } + + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS; j++) + { + memset(cw_ex->audio[j], 0, 16); + + if (ecm[0] == 0x80) + { + memcpy(cw_ex->audio[j], cw[PVU_CW_A1 + j], 8); + } + else + { + memcpy(&cw_ex->audio[j][8], cw[PVU_CW_A1 + j], 8); + } + } + + memset(cw_ex->data, 0, 16); + + if (ecm[0] == 0x80) + { + memcpy(cw_ex->data, cw[PVU_CW_HSD], 8); + } + else + { + memcpy(&cw_ex->data[8], cw[PVU_CW_HSD], 8); + } + } + } + else // Calculate only video CW + { + calculate_cw(PVU_CW_VID, seed[PVU_CW_VID], csaUsed, convolvedCw[PVU_CW_VID], + cw[PVU_CW_VID], baseCw, seedEcmCw, hashModeCw, needsUnmasking, + xorMode, modeCW, unmaskedEcm + offsetBody); + + if (csaUsed) + { + for (k = 0; k < 8; k += 4) + { + cw[PVU_CW_VID][k + 3] = ((cw[PVU_CW_VID][k] + cw[PVU_CW_VID][k + 1] + cw[PVU_CW_VID][k + 2]) & 0xFF); + } + } + + cs_log_dbg(D_ATR, "calculated video only cw: %s", + cs_hexdump(0, cw[PVU_CW_VID], 8, tmpBuffer2, sizeof(tmpBuffer2))); + } + + memset(dw, 0, 16); + + if (ecm[0] == 0x80) + { + memcpy(dw, cw[PVU_CW_VID], 8); + } + else + { + memcpy(&dw[8], cw[PVU_CW_VID], 8); + } + + return EMU_OK; + } + + default: + break; + } + + i += nanoLen; + } + + return EMU_NOT_SUPPORTED; +} + +// PowerVu EMM EMU +static void create_data_unmask_emm_mode_03(uint8_t *emmBody, uint8_t *data) +{ + int i; + uint8_t padding[] = + { + 0xB3, 0x60, 0x35, 0xC8, 0x5C, 0x26, 0xC1, 0xD0, + 0x88, 0x86, 0x57, 0xB6, 0x45, 0xA7, 0xDF, 0x7E, + 0xF0, 0xA8, 0x49, 0xFB, 0x79, 0x6C, 0xAF, 0xB0 + }; + + memcpy(data + 40, padding, 24); + + for (i = 0; i < 5; i++) + { + data[0 + i * 8] = emmBody[0x18 + i * 0x1B]; + data[1 + i * 8] = emmBody[0x16 + i * 0x1B]; + data[2 + i * 8] = emmBody[0x07 + i * 0x1B]; + data[3 + i * 8] = emmBody[0x0B + i * 0x1B]; + data[4 + i * 8] = emmBody[0x06 + i * 0x1B]; + data[5 + i * 8] = emmBody[0x19 + i * 0x1B]; + data[6 + i * 8] = emmBody[0x15 + i * 0x1B]; + data[7 + i * 8] = emmBody[0x03 + i * 0x1B]; + } +} + +static void create_data_unmask_emm_mode_04(uint8_t *emmBody, uint8_t *data) +{ + int i; + uint8_t padding[] = + { + 0x56, 0xC7, 0x05, 0x66, 0xC7, 0x4E, 0xC1, 0xA0, + 0x9E, 0xD1, 0xFE, 0x92, 0xE8, 0xCD, 0x5F, 0xAF, + 0xCF, 0xE5, 0xE9, 0x9E, 0x7A, 0x38, 0xAC, 0x68 + }; + + memcpy(data + 0x28, padding, 0x18); + + for (i = 0; i < 5; i++) + { + data[0 + i * 8] = emmBody[0x06 + i * 0x1B]; + data[1 + i * 8] = emmBody[0x19 + i * 0x1B]; + data[2 + i * 8] = emmBody[0x16 + i * 0x1B]; + data[3 + i * 8] = emmBody[0x0A + i * 0x1B]; + data[4 + i * 8] = emmBody[0x13 + i * 0x1B]; + data[5 + i * 8] = emmBody[0x05 + i * 0x1B]; + data[6 + i * 8] = emmBody[0x14 + i * 0x1B]; + data[7 + i * 8] = emmBody[0x18 + i * 0x1B]; + } +} + +static uint8_t get_mode_unmask_emm(uint8_t *extraData) +{ + uint16_t data = ((uint16_t)extraData[0] << 8) + extraData[1]; + + if (data == 0) + { + return 0x00; + } + + switch (data & 0x0881) + { + case 0x0080: + case 0x0881: + return 0x01; + + case 0x0001: + case 0x0880: + return 0x02; + + case 0x0800: + case 0x0081: + return 0x03; + + case 0x0000: + case 0x0801: + switch (data & 0x9020) + { + case 0x8000: + case 0x9000: + return 0x04; + + case 0x0020: + case 0x9020: + return 0x05; + + case 0x0000: + case 0x1000: + return 0x06; + + case 0x1020: + case 0x8020: + switch (data & 0x2014) + { + case 0x2004: + case 0x2010: + return 0x07; + + case 0x0000: + case 0x0004: + return 0x08; + + case 0x0014: + case 0x2014: + return 0x09; + + case 0x0010: + case 0x2000: + return 0x00; + } + break; + } + break; + } + return 0x00; +} + +static void unmask_emm(uint8_t *emm) +{ + uint32_t crc, i, l; + uint8_t hashModeEmm, modeUnmask, data[64], mask[16]; + + uint8_t sourcePos[] = + { + 0x03, 0x0C, 0x0D, 0x11, 0x15, 0x18, 0x1D, 0x1F, 0x25, 0x2A, + 0x32, 0x35, 0x3A, 0x3B, 0x3E, 0x42, 0x47, 0x48, 0x53, 0x58, + 0x5C, 0x61, 0x66, 0x69, 0x71, 0x72, 0x78, 0x7B, 0x81, 0x84 + }; + + uint8_t destPos[] = + { + 0x02, 0x08, 0x0B, 0x0E, 0x13, 0x16, 0x1E, 0x23, 0x28, 0x2B, + 0x2F, 0x33, 0x38, 0x3C, 0x40, 0x44, 0x4A, 0x4D, 0x54, 0x57, + 0x5A, 0x63, 0x68, 0x6A, 0x70, 0x75, 0x76, 0x7D, 0x82, 0x85 + }; + + // Create Mask for ECM decryption + create_data_ecm_emm(emm, sourcePos, 19, 30, data); + + hashModeEmm = emm[8] ^ crc8_calc(data, 30); + modeUnmask = get_mode_unmask_emm(emm + 16); + + if ((modeUnmask == 0x00) || (modeUnmask > 4)) + { + create_hash(data, 30, mask, hashModeEmm); + + // Unmask Body + for (i = 0; i < 30; i++) + { + emm[19 + destPos[i]] ^= mask[i & 0x0F]; + } + } + else if (modeUnmask == 0x03) + { + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x03 + i * 0x1B] -= emm[0x13 + 0x0D + i * 0x1B]; + emm[0x13 + 0x06 + i * 0x1B] -= emm[0x13 + 0x1A + i * 0x1B]; + emm[0x13 + 0x07 + i * 0x1B] -= emm[0x13 + 0x10 + i * 0x1B]; + emm[0x13 + 0x0B + i * 0x1B] -= emm[0x13 + 0x17 + i * 0x1B]; + emm[0x13 + 0x15 + i * 0x1B] -= emm[0x13 + 0x05 + i * 0x1B]; + emm[0x13 + 0x16 + i * 0x1B] -= emm[0x13 + 0x0F + i * 0x1B]; + emm[0x13 + 0x18 + i * 0x1B] -= emm[0x13 + 0x14 + i * 0x1B]; + emm[0x13 + 0x19 + i * 0x1B] -= emm[0x13 + 0x04 + i * 0x1B]; + } + + create_data_unmask_emm_mode_03(emm + 0x13, data); + create_hash_mode_03(data, mask); + + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x14 + i * 0x1B] ^= mask[0x00]; + emm[0x13 + 0x0F + i * 0x1B] ^= mask[0x01]; + emm[0x13 + 0x10 + i * 0x1B] ^= mask[0x02]; + emm[0x13 + 0x17 + i * 0x1B] ^= mask[0x03]; + emm[0x13 + 0x1A + i * 0x1B] ^= mask[0x04]; + emm[0x13 + 0x04 + i * 0x1B] ^= mask[0x05]; + emm[0x13 + 0x05 + i * 0x1B] ^= mask[0x06]; + emm[0x13 + 0x0D + i * 0x1B] ^= mask[0x07]; + emm[0x13 + 0x09 + i * 0x1B] ^= mask[0x08]; + emm[0x13 + 0x0A + i * 0x1B] ^= mask[0x09]; + emm[0x13 + 0x0E + i * 0x1B] ^= mask[0x0A]; + emm[0x13 + 0x11 + i * 0x1B] ^= mask[0x0B]; + emm[0x13 + 0x12 + i * 0x1B] ^= mask[0x0C]; + emm[0x13 + 0x13 + i * 0x1B] ^= mask[0x0D]; + emm[0x13 + 0x08 + i * 0x1B] ^= mask[0x0E]; + emm[0x13 + 0x0C + i * 0x1B] ^= mask[0x0F]; + } + } + else if (modeUnmask == 0x04) + { + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x05 + i * 0x1B] -= emm[0x13 + 0x04 + i * 0x1B]; + emm[0x13 + 0x06 + i * 0x1B] -= emm[0x13 + 0x0B + i * 0x1B]; + emm[0x13 + 0x0A + i * 0x1B] -= emm[0x13 + 0x17 + i * 0x1B]; + emm[0x13 + 0x13 + i * 0x1B] -= emm[0x13 + 0x1A + i * 0x1B]; + emm[0x13 + 0x14 + i * 0x1B] -= emm[0x13 + 0x0E + i * 0x1B]; + emm[0x13 + 0x16 + i * 0x1B] -= emm[0x13 + 0x15 + i * 0x1B]; + emm[0x13 + 0x18 + i * 0x1B] -= emm[0x13 + 0x08 + i * 0x1B]; + emm[0x13 + 0x19 + i * 0x1B] -= emm[0x13 + 0x12 + i * 0x1B]; + } + + create_data_unmask_emm_mode_04(emm + 0x13, data); + create_hash_mode_04(data, mask); + + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x0B + i * 0x1B] ^= mask[0x00]; + emm[0x13 + 0x12 + i * 0x1B] ^= mask[0x01]; + emm[0x13 + 0x15 + i * 0x1B] ^= mask[0x02]; + emm[0x13 + 0x17 + i * 0x1B] ^= mask[0x03]; + emm[0x13 + 0x1A + i * 0x1B] ^= mask[0x04]; + emm[0x13 + 0x04 + i * 0x1B] ^= mask[0x05]; + emm[0x13 + 0x0E + i * 0x1B] ^= mask[0x06]; + emm[0x13 + 0x08 + i * 0x1B] ^= mask[0x07]; + emm[0x13 + 0x09 + i * 0x1B] ^= mask[0x08]; + emm[0x13 + 0x0C + i * 0x1B] ^= mask[0x09]; + emm[0x13 + 0x03 + i * 0x1B] ^= mask[0x0A]; + emm[0x13 + 0x0F + i * 0x1B] ^= mask[0x0B]; + emm[0x13 + 0x10 + i * 0x1B] ^= mask[0x0C]; + emm[0x13 + 0x07 + i * 0x1B] ^= mask[0x0D]; + emm[0x13 + 0x0D + i * 0x1B] ^= mask[0x0E]; + emm[0x13 + 0x11 + i * 0x1B] ^= mask[0x0F]; + } + } + else + { + cs_log("A new unknown emm mode [%d] is in use.", modeUnmask); + } + + // Fix Header + emm[3] &= 0x0F; + emm[3] |= 0x10; + emm[8] = 0x00; + + // Fix CRC (optional) + l = (((emm[1] << 8) + emm[2]) & 0xFFF) + 3 - 4; + crc = ccitt32_crc(emm, l); + + emm[l + 0] = crc >> 24; + emm[l + 1] = crc >> 16; + emm[l + 2] = crc >> 8; + emm[l + 3] = crc >> 0; +} + +static int8_t update_ecm_keys_by_group(uint32_t groupId, uint8_t keyIndex, uint8_t *Key, uint32_t uniqueAddress) +{ + int8_t ret = 0; + uint8_t oldKey[7]; + uint32_t foundProvider = 0, keyRef = 0; + char indexStr[3], uaInfo[13]; + + snprintf(indexStr, 3, "%02X", keyIndex); + snprintf(uaInfo, 13, "UA: %08X", uniqueAddress); + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + while (emu_find_key('P', groupId << 16 & 0xFFFF0000, 0x0000FFFF, indexStr, oldKey, 7, 0, keyRef, 0, &foundProvider)) + { + keyRef++; + + if (memcmp(oldKey, Key, 7) == 0) // New ECM key already in the db + { + continue; + } + + if (emu_set_key('P', foundProvider, indexStr, Key, 7, 1, uaInfo, NULL)) + { + ret = 1; + } + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + return ret; +} + +int8_t powervu_emm(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t emmInfo, emmType, decryptOk = 0; + uint8_t emmKey[7], tmpEmmKey[7], tmp[26]; + uint16_t emmLen = SCT_LEN(emm); + uint32_t i, uniqueAddress, groupId, keyRef = 0; + //uint32_t emmCrc32; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[16]; + + if (emmLen < 50) + { + return EMU_NOT_SUPPORTED; + } + + // Check if unmasking is needed + if ((emm[3] & 0xF0) == 0x50) + { + unmask_emm(emm); + } + + // looks like checksum does not work for all EMMs + //emmCrc32 = b2i(4, emm+emmLen-4); + // + //if(ccitt32_crc(emm, emmLen-4) != emmCrc32) + //{ + // return EMU_CHECKSUM_ERROR; + //} + emmLen -= 4; + + uniqueAddress = b2i(4, emm + 12); + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.8X", uniqueAddress); + + do + { + if (!get_emm_key(emmKey, keyName, keyRef++, &groupId)) + { + //cs_log_dbg(D_ATR, "EMM key for UA %s is missing", keyName); + return EMU_KEY_NOT_FOUND; + } + + for (i = 19; i + 27 <= emmLen; i += 27) + { + emmInfo = emm[i]; + + if (!get_bit(emmInfo, 7)) + { + continue; + } + + //keyNb = emm[i] & 0x0F; + + memcpy(tmp, emm + i + 1, 26); + memcpy(tmpEmmKey, emmKey, 7); + powervu_decrypt(emm + i + 1, 26, tmpEmmKey, 0); + + if ((emm[13] != emm[i + 24]) || (emm[14] != emm[i + 25]) || (emm[15] != emm[i + 26])) + { + memcpy(emm + i + 1, tmp, 26); + memcpy(tmpEmmKey, emmKey, 7); + powervu_decrypt(emm + i + 1, 26, tmpEmmKey, 1); + + if ((emm[13] != emm[i + 24]) || (emm[14] != emm[i + 25]) || (emm[15] != emm[i + 26])) + { + memcpy(emm + i + 1, tmp, 26); + memcpy(tmpEmmKey, emmKey, 7); + continue; + } + } + + decryptOk = 1; + + emmType = emm[i + 2] & 0x7F; + + if (emmType > 1) + { + continue; + } + + if (emm[i + 3] == 0 && emm[i + 4] == 0) + { + cs_hexdump(0, &emm[i + 3], 7, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: P %04X**** %02X %s -> REJECTED (looks invalid) UA: %08X", + groupId, emmType, keyValue, uniqueAddress); + continue; + } + + update_ecm_keys_by_group(groupId, emmType, &emm[i + 3], uniqueAddress); + + (*keysAdded)++; + cs_hexdump(0, &emm[i + 3], 7, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: P %04X**** %02X %s ; UA: %08X", groupId, emmType, keyValue, uniqueAddress); + } + + } while (!decryptOk); + + return EMU_OK; +} + +int8_t powervu_get_hexserials(uint8_t hexserials[][4], uint32_t maxCount, uint16_t srvid) +{ + //srvid == 0xFFFF -> get all + + int8_t alreadyAdded; + uint8_t tmp[4]; + uint32_t i, j, k, groupid, length, count = 0; + KeyDataContainer *KeyDB; + + KeyDB = emu_get_key_container('P'); + if (KeyDB == NULL) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount && count < maxCount; i++) + { + if (KeyDB->EmuKeys[i].provider <= 0x0000FFFF) // skip EMM keys + { + continue; + } + + if (srvid != 0xFFFF && (KeyDB->EmuKeys[i].provider & 0x0000FFFF) != srvid) + { + continue; + } + + // This "groupid" has an ECM key with our "srvid" + // (in ECM keys "groupid" is top 16 bits) + groupid = KeyDB->EmuKeys[i].provider >> 16; + + for (j = 0; j < KeyDB->keyCount && count < maxCount; j++) + { + // Skip EMM keys belonging to other groups + // (in EMM keys "groupid" is bottom 16 bits) + if (KeyDB->EmuKeys[j].provider != groupid) + { + continue; + } + + length = cs_strlen(KeyDB->EmuKeys[j].keyName); + + if (length < 3) + { + continue; + } + + if (length > 8) + { + length = 8; + } + + memset(tmp, 0, 4); + char_to_bin(tmp + (4 - (length / 2)), KeyDB->EmuKeys[j].keyName, length); + + for (k = 0, alreadyAdded = 0; k < count; k++) + { + if (!memcmp(hexserials[k], tmp, 4)) + { + alreadyAdded = 1; + break; + } + } + + if (!alreadyAdded) + { + memcpy(hexserials[count], tmp, 4); + count++; + } + } + } + + return count; +} + +int8_t powervu_get_hexserials_new(uint8_t hexserials[][4], uint32_t maxCount, uint16_t caid, + uint16_t tsid, uint16_t onid, uint32_t ens) +{ + int8_t alreadyAdded; + uint8_t tmp[4]; + uint32_t i, j, channel_hash, group_id, length, count = 0; + KeyDataContainer *KeyDB; + + KeyDB = emu_get_key_container('P'); + if (KeyDB == NULL) + { + return 0; + } + + channel_hash = create_channel_hash(caid, tsid, onid, ens); + group_id = get_channel_group(channel_hash); + + if (group_id == 0) // No group found for this hash + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount && count < maxCount; i++) + { + // Skip EMM keys belonging to other groups + // (in EMM keys "groupid" is bottom 16 bits) + if (KeyDB->EmuKeys[i].provider != group_id) + { + continue; + } + + length = cs_strlen(KeyDB->EmuKeys[i].keyName); + + if (length < 3) + { + continue; + } + + if (length > 8) + { + length = 8; + } + + memset(tmp, 0, 4); + char_to_bin(tmp + (4 - (length / 2)), KeyDB->EmuKeys[i].keyName, length); + + for (j = 0, alreadyAdded = 0; j < count; j++) + { + if (!memcmp(hexserials[j], tmp, 4)) + { + alreadyAdded = 1; + break; + } + } + + if (!alreadyAdded) + { + memcpy(hexserials[count], tmp, 4); + count++; + } + } + + return count; +} + +#endif // WITH_EMU diff --git a/module-emulator-powervu.h b/module-emulator-powervu.h new file mode 100644 index 00000000..b50d2088 --- /dev/null +++ b/module-emulator-powervu.h @@ -0,0 +1,63 @@ +#ifndef MODULE_EMULATOR_POWERVU_H +#define MODULE_EMULATOR_POWERVU_H + +#ifdef WITH_EMU + +#define PVU_CW_VID 0 // VIDeo +#define PVU_CW_HSD 1 // High Speed Data +#define PVU_CW_A1 2 // Audio 1 +#define PVU_CW_A2 3 // Audio 2 +#define PVU_CW_A3 4 // Audio 3 +#define PVU_CW_A4 5 // Audio 4 +#define PVU_CW_UTL 6 // UTiLity +#define PVU_CW_VBI 7 // Vertical Blanking Interval + +#define PVU_CONVCW_VID_ECM 0x80 // VIDeo +#define PVU_CONVCW_HSD_ECM 0x40 // High Speed Data +#define PVU_CONVCW_A1_ECM 0x20 // Audio 1 +#define PVU_CONVCW_A2_ECM 0x10 // Audio 2 +#define PVU_CONVCW_A3_ECM 0x08 // Audio 3 +#define PVU_CONVCW_A4_ECM 0x04 // Audio 4 +#define PVU_CONVCW_UTL_ECM 0x02 // UTiLity +#define PVU_CONVCW_VBI_ECM 0x01 // Vertical Blanking Interval + +#ifdef MODULE_STREAMRELAY +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens, emu_stream_client_key_data *cdata); +#else +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens); +#endif +int8_t powervu_emm(uint8_t *emm, uint32_t *keysAdded); + +/* + * This function searches for EMM keys and adds their Unique Addresses (UA) as EMM filters. + * The EMM keys are picked from all group id's that have ECM keys for the srvid specified + * as input. If there is a large ammount of EMM keys matching these criteria, only the first + * "maxCount" UA's are added as EMM filters. The rest are not used at all. + * + * In the rare case where two or more EMM keys with the same UA belong to different groups, + * and these groups also have ECM keys for the srvid in request, there is a chance the ECM + * keys in the "wrong" group to be updated. This is because the EMM algorithm has no way of + * knowing in which group the service id belongs to. A workaround for this designing flaw + * is to make sure there are no EMM keys with the same UA between different groups. + * + * Hexserials must be of type "uint8_t hexserials[maxCount][4]". If srvid is equal to 0xFFFF + * all serials are added (no service id filtering is done). Returns the count of hexserials + * added as filters. +*/ +int8_t powervu_get_hexserials(uint8_t hexserials[][4], uint32_t maxCount, uint16_t srvid); + +/* + * Like the previous function, it adds UAs as EMM filters. It is used in conjunction with the + * new method of entering ECM keys, where one key can serve every channel in the group. Since + * there is no srvid to search for, we need to know the group id prior to searching for EMM + * keys. To do so, this function calulates a hash using the tsid, onid and enigma namespace of + * the transponder, which is only available in enigma2. + * + * Hexserials must be of type "uint8_t hexserials[maxCount][4]" like before. It returns the + * count of hexserials added as filters. +*/ +int8_t powervu_get_hexserials_new(uint8_t hexserials[][4], uint32_t maxCount, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_POWERVU_H diff --git a/module-emulator-viaccess.c b/module-emulator-viaccess.c new file mode 100644 index 00000000..b5fea651 --- /dev/null +++ b/module-emulator-viaccess.c @@ -0,0 +1,1183 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "module-newcamd-des.h" +#include "oscam-aes.h" +#include "oscam-string.h" + +// from reader-viaccess.c: +void hdSurEncPhase1_D2_0F_11(uint8_t *CWs); +void hdSurEncPhase2_D2_0F_11(uint8_t *CWs); +void hdSurEncPhase1_D2_13_15(uint8_t *cws); +void hdSurEncPhase2_D2_13_15(uint8_t *cws); + +// Viaccess EMU + +static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, + uint32_t keyLength, uint8_t isCriticalKey) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + + if (emu_find_key('V', ident, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + if (ident == 0xD00040 && emu_find_key('V', 0x030B00, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + return 0; +} + +static void via1_mod(const uint8_t *key2, uint8_t *data) +{ + int32_t kb, db; + + for (db = 7; db >= 0; db--) + { + for (kb = 7; kb > 3; kb--) + { + int32_t a0 = kb ^ db; + int32_t pos = 7; + + if (a0 & 4) + { + a0 ^= 7; + pos ^= 7; + } + + a0 = (a0 ^ (kb & 3)) + (kb & 3); + + if (!(a0 & 4)) + { + data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF)); + } + } + } + + for (db = 0; db < 8; db++) + { + for (kb = 0; kb < 4; kb++) + { + int32_t a0 = kb ^ db; + int32_t pos = 7; + + if (a0 & 4) + { + a0 ^= 7; + pos ^= 7; + } + + a0 = (a0 ^ (kb & 3)) + (kb & 3); + + if (!(a0 & 4)) + { + data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF)); + } + } + } +} + +static void via1_decode(uint8_t *data, uint8_t *key) +{ + via1_mod(key + 8, data); + nc_des(key, DES_ECM_CRYPT, data); + via1_mod(key + 8, data); +} + +static void via1_hash(uint8_t *data, uint8_t *key) +{ + via1_mod(key + 8, data); + nc_des(key, DES_ECM_HASH, data); + via1_mod(key + 8, data); +} + +static inline void via1_do_hash(uint8_t *hashbuffer, uint8_t *pH, uint8_t data, uint8_t *hashkey) +{ + hashbuffer[*pH] ^= data; + (*pH)++; + + if (*pH == 8) + { + via1_hash(hashbuffer, hashkey); + *pH = 0; + } +} + +static int8_t via1_decrypt(uint8_t *ecm, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex) +{ + int32_t msg_pos, encStart = 0, hash_start, i; + + uint8_t tmp, k, pH, foundData = 0; + uint8_t work_key[16], signature[8], hashbuffer[8], prepared_key[16], hashkey[16]; + uint8_t *data, *des_data1, *des_data2; + + uint16_t ecmLen = SCT_LEN(ecm); + + if (ident == 0) + { + return EMU_CORRUPT_DATA; + } + + memset(work_key, 0, 16); + + if (!get_key(work_key, ident, '0', desKeyIndex, 8, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (ecmLen < 11) + { + return EMU_NOT_SUPPORTED; + } + + data = ecm + 9; + des_data1 = dw; + des_data2 = dw + 8; + + msg_pos = 0; + pH = 0; + memset(hashbuffer, 0, sizeof(hashbuffer)); + memcpy(hashkey, work_key, sizeof(hashkey)); + memset(signature, 0, 8); + + while (9 + msg_pos + 2 < ecmLen) + { + switch (data[msg_pos]) + { + case 0xEA: + if (9 + msg_pos + 2 + 15 < ecmLen) + { + encStart = msg_pos + 2; + memcpy(des_data1, &data[msg_pos + 2], 8); + memcpy(des_data2, &data[msg_pos + 2 + 8], 8); + foundData |= 1; + } + break; + + case 0xF0: + if (9 + msg_pos + 2 + 7 < ecmLen) + { + memcpy(signature, &data[msg_pos + 2], 8); + foundData |= 2; + } + break; + } + msg_pos += data[msg_pos + 1] + 2; + } + + if (foundData != 3) + { + return EMU_NOT_SUPPORTED; + } + + pH = i = 0; + + if (data[0] == 0x9F && 10 + data[1] <= ecmLen) + { + via1_do_hash(hashbuffer, &pH, data[i++], hashkey); + via1_do_hash(hashbuffer, &pH, data[i++], hashkey); + + for (hash_start = 0; hash_start < data[1]; hash_start++) + { + via1_do_hash(hashbuffer, &pH, data[i++], hashkey); + } + + while (pH != 0) + { + via1_do_hash(hashbuffer, &pH, 0, hashkey); + } + } + + if (work_key[7] == 0) + { + for (; i < encStart + 16; i++) + { + via1_do_hash(hashbuffer, &pH, data[i], hashkey); + } + memcpy(prepared_key, work_key, 8); + } + else + { + prepared_key[0] = work_key[2]; + prepared_key[1] = work_key[3]; + prepared_key[2] = work_key[4]; + prepared_key[3] = work_key[5]; + prepared_key[4] = work_key[6]; + prepared_key[5] = work_key[0]; + prepared_key[6] = work_key[1]; + prepared_key[7] = work_key[7]; + + memcpy(prepared_key + 8, work_key + 8, 8); + + if (work_key[7] & 1) + { + for (; i < encStart; i++) + { + via1_do_hash(hashbuffer, &pH, data[i], hashkey); + } + + k = ((work_key[7] & 0xF0) == 0) ? 0x5A : 0xA5; + + for (i = 0; i < 8; i++) + { + tmp = des_data1[i]; + des_data1[i] = (k & hashbuffer[pH] ) ^ tmp; + via1_do_hash(hashbuffer, &pH, tmp, hashkey); + } + + for (i = 0; i < 8; i++) + { + tmp = des_data2[i]; + des_data2[i] = (k & hashbuffer[pH] ) ^ tmp; + via1_do_hash(hashbuffer, &pH, tmp, hashkey); + } + } + else + { + for ( ; i < encStart + 16; i++) + { + via1_do_hash(hashbuffer, &pH, data[i], hashkey); + } + } + } + + via1_decode(des_data1, prepared_key); + via1_decode(des_data2, prepared_key); + via1_hash(hashbuffer, hashkey); + + if (memcmp(signature, hashbuffer, 8)) + { + return EMU_CHECKSUM_ERROR; + } + + return EMU_OK; +} + +static int8_t via26_process_dw(uint8_t *indata, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t pv1,pv2, i; + uint8_t Tmp[8], T1Key[300], P1Key[8], KeyDes1[16], KeyDes2[16], XorKey[8]; + uint32_t ks1[32], ks2[32]; + + if (!get_key(T1Key, ident, 'T', 1, 300, 1)) + { + return 2; + } + + if (!get_key(P1Key, ident, 'P', 1, 8, 1)) + { + return 2; + } + + if (!get_key(KeyDes1, ident, 'D', 1, 16, 1)) + { + return 2; + } + + if (!get_key(KeyDes2, ident, '0', desKeyIndex, 16, 1)) + { + return 2; + } + + if (!get_key(XorKey, ident, 'X', 1, 8, 1)) + { + return 2; + } + + for (i = 0; i < 8; i++) + { + pv1 = indata[i]; + Tmp[i] = T1Key[pv1]; + } + + for (i = 0; i < 8; i++) + { + pv1 = P1Key[i]; + pv2 = Tmp[pv1]; + indata[i] = pv2; + } + + des_set_key(KeyDes1, ks1); + des(indata, ks1, 1); + + for (i = 0; i < 8; i++) + { + indata[i] ^= XorKey[i]; + } + + des_set_key(KeyDes2, ks1); + des_set_key(KeyDes2 + 8, ks2); + des(indata, ks1, 0); + des(indata, ks2, 1); + des(indata, ks1, 0); + + for (i = 0; i < 8; i++) + { + indata[i] ^= XorKey[i]; + } + + des_set_key(KeyDes1, ks1); + des(indata, ks1, 0); + + for (i = 0; i < 8; i++) + { + pv1 = indata[i]; + pv2 = P1Key[i]; + Tmp[pv2] = pv1; + } + + for (i = 0; i < 8; i++) + { + pv1 = Tmp[i]; + pv2 = T1Key[pv1]; + indata[i] = pv2; + } + + return 0; +} + +static int8_t via26_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t tmpData[8], C1[8]; + uint8_t *pXorVector; + int32_t i, j; + + if (ident == 0) + { + return EMU_CORRUPT_DATA; + } + + if (!get_key(C1, ident, 'C', 1, 8, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + for (i = 0; i < 2; i++) + { + memcpy(tmpData, source + i * 8, 8); + via26_process_dw(tmpData, ident, desKeyIndex); + + if (i != 0) + { + pXorVector = source; + } + else + { + pXorVector = &C1[0]; + } + + for (j = 0; j < 8; j++) + { + dw[i * 8 + j] = tmpData[j] ^ pXorVector[j]; + } + + // Fix CW checksum bytes + for (j = 3; j < 8; j += 4) + { + dw[i * 8 + j] = (dw[i * 8 + j - 3] + dw[i * 8 + j - 2] + dw[i * 8 + j - 1]) & 0xFF; + } + } + + return EMU_OK; +} + +static void via3_core(uint8_t *data, uint8_t Off, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key) +{ + uint8_t i; + uint32_t lR2, lR3, lR4, lR6, lR7; + + switch (ident) + { + case 0x032820: + { + for (i = 0; i < 4; i++) + { + data[i] ^= XorKey[(Off + i) & 0x07]; + } + + lR2 = (data[0] ^ 0xBD) + data[0]; + lR3 = (data[3] ^ 0xEB) + data[3]; + lR2 = (lR2 - lR3) ^ data[2]; + lR3 = ((0x39 * data[1]) << 2); + data[4] = (lR2 | lR3) + data[2]; + + lR3 = ((((data[0] + 6) ^ data[0]) | (data[2] << 1)) ^ 0x65) + data[0]; + lR2 = (data[1] ^ 0xED) + data[1]; + lR7 = ((data[3] + 0x29) ^ data[3]) * lR2; + data[5] = lR7 + lR3; + + lR2 = ((data[2] ^ 0x33) + data[2]) & 0x0A; + lR3 = (data[0] + 0xAD) ^ data[0]; + lR3 = lR3 + lR2; + lR2 = data[3] * data[3]; + lR7 = (lR2 | 1) + data[1]; + data[6] = (lR3 | lR7) + data[1]; + + lR3 = data[1] & 0x07; + lR2 = (lR3 - data[2]) & (data[0] | lR2 | 0x01); + data[7] = lR2 + data[3]; + + for (i = 0; i < 4; i++) + { + data[i + 4] = T1Key[data[i + 4]]; + } + } + break; + + case 0x030B00: + { + for (i = 0; i < 4; i++) + { + data[i] ^= XorKey[(Off + i) & 0x07]; + } + + lR6 = (data[3] + 0x6E) ^ data[3]; + lR6 = (lR6 * (data[2] << 1)) + 0x17; + lR3 = (data[1] + 0x77) ^ data[1]; + lR4 = (data[0] + 0xD7) ^ data[0]; + data[4] = ((lR4 & lR3) | lR6) + data[0]; + + lR4 = ((data[3] + 0x71) ^ data[3]) ^ 0x90; + lR6 = (data[1] + 0x1B) ^ data[1]; + lR4 = (lR4 * lR6) ^ data[0]; + data[5] = (lR4 ^ (data[2] << 1)) + data[1]; + + lR3 = (data[3] * data[3]) | 0x01; + lR4 = (((data[2] ^ 0x35) + data[2]) | lR3) + data[2]; + lR6 = data[1] ^ (data[0] + 0x4A); + data[6] = lR6 + lR4; + + lR3 = (data[0] * (data[2] << 1)) | data[1]; + lR4 = 0xFE - data[3]; + lR3 = lR4 ^ lR3; + data[7] = lR3 + data[3]; + + for (i = 0; i < 4; i++) + { + data[4 + i] = T1Key[data[4 + i]]; + } + } + break; + + default: + break; + } +} + +static void via3_fct1(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key) +{ + uint8_t t; + + via3_core(data, 0, ident, XorKey, T1Key); + + switch (ident) + { + case 0x032820: + { + t = data[4]; + data[4] = data[7]; + data[7] = t; + } + break; + + case 0x030B00: + { + t = data[5]; + data[5] = data[7]; + data[7] = t; + } + break; + + default: + break; + } +} + +static void via3_fct2(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key) +{ + uint8_t t; + + via3_core(data, 4, ident, XorKey, T1Key); + + switch (ident) + { + case 0x032820: + { + t = data[4]; + data[4] = data[7]; + data[7] = data[5]; + data[5] = data[6]; + data[6] = t; + } + break; + + case 0x030B00: + { + t = data[6]; + data[6] = data[7]; + data[7] = t; + } + break; + + default: + break; + } +} + +static int8_t via3_process_dw(uint8_t *data, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t i; + uint8_t tmp[8], T1Key[300], P1Key[8], KeyDes[16], XorKey[8]; + uint32_t ks1[32], ks2[32]; + + if (!get_key(T1Key, ident, 'T', 1, 300, 1)) + { + return 2; + } + + if (!get_key(P1Key, ident, 'P', 1, 8, 1)) + { + return 2; + } + + if (!get_key(KeyDes, ident, '0', desKeyIndex, 16, 1)) + { + return 2; + } + + if (!get_key(XorKey, ident, 'X', 1, 8, 1)) + { + return 2; + } + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i + 4]; + } + + via3_fct1(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i] ^ tmp[i + 4]; + } + + via3_fct2(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] ^= XorKey[i + 4]; + } + + for (i = 0; i < 4; i++) + { + data[i] = data[i + 4] ^ tmp[i + 4]; + data[i + 4] = tmp[i]; + } + + des_set_key(KeyDes, ks1); + des_set_key(KeyDes + 8, ks2); + + des(data, ks1, 0); + des(data, ks2, 1); + des(data, ks1, 0); + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i + 4]; + } + + via3_fct2(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i] ^ tmp[i + 4]; + } + + via3_fct1(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] ^= XorKey[i]; + } + + for (i = 0; i < 4; i++) + { + data[i] = data[i + 4] ^ tmp[i + 4]; + data[i + 4] = tmp[i]; + } + + return 0; +} + +static void via3_final_mix(uint8_t *dw) +{ + uint8_t tmp[4]; + + memcpy(tmp, dw, 4); + memcpy(dw, dw + 4, 4); + memcpy(dw + 4, tmp, 4); + + memcpy(tmp, dw + 8, 4); + memcpy(dw + 8, dw + 12, 4); + memcpy(dw + 12, tmp, 4); +} + +static int8_t via3_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex, + uint8_t aesKeyIndex, uint8_t aesMode, int8_t doFinalMix) +{ + int8_t aesAfterCore = 0, needsAES = (aesKeyIndex != 0xFF); + int32_t i, j; + + uint8_t tmpData[8], C1[8]; + uint8_t *pXorVector; + + char aesKey[16]; + + if (ident == 0) + { + return EMU_CORRUPT_DATA; + } + + if (!get_key(C1, ident, 'C', 1, 8, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (needsAES && !get_key((uint8_t *)aesKey, ident, 'E', aesKeyIndex, 16, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (aesMode == 0x0D || aesMode == 0x11 || aesMode == 0x15) + { + aesAfterCore = 1; + } + + if (needsAES && !aesAfterCore) + { + if (aesMode == 0x0F) + { + hdSurEncPhase1_D2_0F_11(source); + hdSurEncPhase2_D2_0F_11(source); + } + else if (aesMode == 0x13) + { + hdSurEncPhase1_D2_13_15(source); + } + + struct aes_keys aes; + aes_set_key(&aes, aesKey); + aes_decrypt(&aes, source, 16); + + if (aesMode == 0x0F) + { + hdSurEncPhase1_D2_0F_11(source); + } + else if (aesMode == 0x13) + { + hdSurEncPhase2_D2_13_15(source); + } + } + + for (i = 0; i < 2; i++) + { + memcpy(tmpData, source + i * 8, 8); + via3_process_dw(tmpData, ident, desKeyIndex); + + if (i != 0) + { + pXorVector = source; + } + else + { + pXorVector = &C1[0]; + } + + for (j = 0; j < 8; j++) + { + dw[i * 8 + j] = tmpData[j] ^ pXorVector[j]; + } + } + + if (needsAES && aesAfterCore) + { + if (aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(dw); + hdSurEncPhase2_D2_0F_11(dw); + } + else if (aesMode == 0x15) + { + hdSurEncPhase1_D2_13_15(dw); + } + + struct aes_keys aes; + aes_set_key(&aes, aesKey); + aes_decrypt(&aes, dw, 16); + + if (aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(dw); + } + + if (aesMode == 0x15) + { + hdSurEncPhase2_D2_13_15(dw); + } + } + + if (ident == 0x030B00) + { + if (doFinalMix) + { + via3_final_mix(dw); + } + + if (!is_valid_dcw(dw) || !is_valid_dcw(dw + 8)) + { + return EMU_CHECKSUM_ERROR; + } + } + + return EMU_OK; +} + +int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw) +{ + int8_t doFinalMix = 0; + + uint8_t nanoCmd = 0, nanoLen = 0, version = 0, providerKeyLen = 0; + uint8_t desKeyIndex = 0, aesMode = 0, aesKeyIndex = 0xFF; + uint16_t i = 0, keySelectPos = 0, ecmLen = SCT_LEN(ecm); + uint32_t currentIdent = 0; + + for (i = 4; i + 2 < ecmLen; ) + { + nanoCmd = ecm[i++]; + nanoLen = ecm[i++]; + + if (i + nanoLen > ecmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x40: + if (nanoLen < 0x03) + { + break; + } + version = ecm[i]; + if (nanoLen == 3) + { + currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0); + desKeyIndex = ecm[i + 2] & 0x0F; + keySelectPos = i + 3; + } + else + { + currentIdent = (ecm[i] << 16) | (ecm[i + 1] << 8) | ((ecm[i + 2] >> 4) & 0x0F); + desKeyIndex = ecm[i + 3]; + keySelectPos = i + 4; + } + providerKeyLen = nanoLen; + break; + + case 0x90: + if (nanoLen < 0x03) + { + break; + } + version = ecm[i]; + currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0); + desKeyIndex = ecm[i + 2] & 0x0F; + keySelectPos = i + 4; + if ((version == 3) && (nanoLen > 3)) + { + desKeyIndex = ecm[i + (nanoLen - 4)] & 0x0F; + } + providerKeyLen = nanoLen; + break; + + case 0x80: + nanoLen = 0; + break; + + case 0xD2: + if (nanoLen < 0x02) + { + break; + } + aesMode = ecm[i]; + aesKeyIndex = ecm[i + 1]; + break; + + case 0xDD: + nanoLen = 0; + break; + + case 0xEA: + if (nanoLen < 0x10) + { + break; + } + if (version < 2) + { + return via1_decrypt(ecm, dw, currentIdent, desKeyIndex); + } + else if (version == 2) + { + return via26_decrypt(ecm + i, dw, currentIdent, desKeyIndex); + } + else if (version == 3) + { + doFinalMix = 0; + if (currentIdent == 0x030B00 && providerKeyLen > 3) + { + if (keySelectPos + 2 >= ecmLen) + { + break; + } + if (ecm[keySelectPos] == 0x05 && ecm[keySelectPos + 1] == 0x67 && + (ecm[keySelectPos + 2] == 0x00 || ecm[keySelectPos + 2] == 0x01)) + { + if (ecm[keySelectPos + 2] == 0x01) + { + doFinalMix = 1; + } + } + else + { + break; + } + } + + return via3_decrypt(ecm + i, dw, currentIdent, desKeyIndex, aesKeyIndex, aesMode, doFinalMix); + } + break; + + default: + break; + } + + i += nanoLen; + } + + return EMU_NOT_SUPPORTED; +} + +// Viaccess EMM EMU + +int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t nanoCmd = 0, subNanoCmd = 0, *tmp; + uint8_t ecmKeyCount = 0, emmKeyIndex = 0, aesMode = 0x0D; + uint8_t nanoLen = 0, subNanoLen = 0, haveEmmXorKey = 0, haveNewD0 = 0; + uint8_t ecmKeys[6][16], keyD0[2], emmKey[16], emmXorKey[16], provName[17]; + + uint16_t i = 0, j = 0, k = 0, emmLen = SCT_LEN(emm); + uint32_t ui1, ui2, ui3, ecmKeyIndex[6], provider = 0, ecmProvider = 0; + + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + struct aes_keys aes; + + memset(keyD0, 0, 2); + memset(ecmKeyIndex, 0, sizeof(uint32_t) * 6); + + for (i = 3; i + 2 < emmLen; ) + { + nanoCmd = emm[i++]; + nanoLen = emm[i++]; + + if (i + nanoLen > emmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x90: + { + if (nanoLen < 3) + { + break; + } + + ui1 = emm[i + 2]; + ui2 = emm[i + 1]; + ui3 = emm[i]; + provider = (ui1 | (ui2 << 8) | (ui3 << 16)); + + if (provider == 0x00D00040) + { + ecmProvider = 0x030B00; + } + else + { + return EMU_NOT_SUPPORTED; + } + break; + } + + case 0xD2: + { + if (nanoLen < 2) + { + break; + } + + emmKeyIndex = emm[i + 1]; + break; + } + + case 0x41: + { + if (nanoLen < 1) + { + break; + } + + if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + memset(provName, 0, 17); + memset(emmXorKey, 0, 16); + + k = nanoLen < 16 ? nanoLen : 16; + + memcpy(provName, &emm[i], k); + aes_set_key(&aes, (char *)emmKey); + aes_decrypt(&aes, emmXorKey, 16); + + for (j = 0; j < 16; j++) + { + provName[j] ^= emmXorKey[j]; + } + provName[k] = 0; + + if (strcmp((char *)provName, "TNTSAT") != 0 && + strcmp((char *)provName, "TNTSATPRO") != 0 && + strcmp((char *)provName, "CSAT V") != 0) + { + return EMU_NOT_SUPPORTED; + } + + break; + } + + case 0xBA: + { + if (nanoLen < 2) + { + break; + } + + get_key(keyD0, ecmProvider, 'D', 0, 2, 0); + + ui1 = (emm[i] << 8) | emm[i + 1]; + + if( (uint32_t)((keyD0[0] << 8) | keyD0[1]) < ui1 || (keyD0[0] == 0x00 && keyD0[1] == 0x00)) + { + keyD0[0] = emm[i]; + keyD0[1] = emm[i + 1]; + haveNewD0 = 1; + break; + } + + return EMU_OK; + } + + case 0xBC: + { + break; + } + + case 0x43: + { + if (nanoLen < 16) + { + break; + } + + memcpy(emmXorKey, &emm[i], 16); + haveEmmXorKey = 1; + + break; + } + + case 0x44: + { + if (nanoLen < 3) + { + break; + } + + if (!haveEmmXorKey) + { + memset(emmXorKey, 0, 16); + } + + tmp = (uint8_t *)malloc(((nanoLen / 16) + 1) * 16 * sizeof(uint8_t)); + if (tmp == NULL) + { + return EMU_OUT_OF_MEMORY; + } + + memcpy(tmp, &emm[i], nanoLen); + aes_set_key(&aes, (char *)emmKey); + + for (j = 0; j < nanoLen; j += 16) + { + aes_decrypt(&aes, emmXorKey, 16); + + for (k = 0; k < 16; k++) + { + tmp[j + k] ^= emmXorKey[k]; + } + } + + memcpy(&emm[i - 2], tmp, nanoLen); + free(tmp); + nanoLen = 0; + i -= 2; + break; + } + + case 0x68: + { + if (ecmKeyCount > 5) + { + break; + } + + for (j = i; j + 2 < i + nanoLen; ) + { + subNanoCmd = emm[j++]; + subNanoLen = emm[j++]; + + if (j + subNanoLen > i + nanoLen) + { + break; + } + + switch (subNanoCmd) + { + case 0xD2: + { + if (nanoLen < 2) + { + break; + } + + aesMode = emm[j]; + emmKeyIndex = emm[j + 1]; + break; + } + + case 0x01: + { + if(nanoLen < 17) + { + break; + } + + ecmKeyIndex[ecmKeyCount] = emm[j]; + memcpy(&ecmKeys[ecmKeyCount], &emm[j + 1], 16); + + if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1)) + { + break; + } + + if (aesMode == 0x0F || aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]); + hdSurEncPhase2_D2_0F_11(ecmKeys[ecmKeyCount]); + } + else if (aesMode == 0x13 || aesMode == 0x15) + { + hdSurEncPhase1_D2_13_15(ecmKeys[ecmKeyCount]); + } + + aes_set_key(&aes, (char *)emmKey); + aes_decrypt(&aes, ecmKeys[ecmKeyCount], 16); + + if (aesMode == 0x0F || aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]); + } + else if (aesMode == 0x13 || aesMode == 0x15) + { + hdSurEncPhase2_D2_13_15(ecmKeys[ecmKeyCount]); + } + + ecmKeyCount++; + break; + } + + default: + break; + } + + j += subNanoLen; + } + break; + } + + case 0xF0: + { + if (nanoLen != 4) + { + break; + } + + ui1 = ((emm[i + 2] << 8) | (emm[i + 1] << 16) | (emm[i] << 24) | emm[i + 3]); + + if (ccitt32_crc(emm + 3, emmLen - 11) != ui1) + { + return EMU_CHECKSUM_ERROR; + } + + if (haveNewD0) + { + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_set_key('V', ecmProvider, "D0", keyD0, 2, 1, NULL, NULL); + + for (j = 0; j < ecmKeyCount; j++) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "E%X", ecmKeyIndex[j]); + emu_set_key('V', ecmProvider, keyName, ecmKeys[j], 16, 1, NULL, NULL); + + (*keysAdded)++; + cs_hexdump(0, ecmKeys[j], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: V %06X %s %s", ecmProvider, keyName, keyValue); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + } + + default: + break; + } + + i += nanoLen; + } + + return EMU_OK; +} + +#endif // WITH_EMU diff --git a/module-emulator-viaccess.h b/module-emulator-viaccess.h new file mode 100644 index 00000000..6f4afcab --- /dev/null +++ b/module-emulator-viaccess.h @@ -0,0 +1,11 @@ +#ifndef MODULE_EMULATOR_VIACCESS_H +#define MODULE_EMULATOR_VIACCESS_H + +#ifdef WITH_EMU + +int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw); +int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_VIACCESS_H diff --git a/module-emulator.c b/module-emulator.c new file mode 100644 index 00000000..865e6687 --- /dev/null +++ b/module-emulator.c @@ -0,0 +1,894 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "module-streamrelay.h" +#include "module-emulator-osemu.h" +#include "module-emulator-biss.h" +#include "module-emulator-irdeto.h" +#include "module-emulator-powervu.h" +#include "oscam-conf-chk.h" +#include "oscam-config.h" +#include "oscam-reader.h" +#include "oscam-string.h" + +/* + * Readers in OSCam consist of 2 basic parts. + * The hardware or the device part. This is where physical smart cards are inserted + * and made available to OSCam. + * The software or the emulation part. This is where the actual card reading is done, + * including ecm and emm processing (i.e emulation of the various cryptosystems). + * In the Emu reader, the device part has no meaning, but we have to create it in + * order to be compatible with OSCam's reader structure. +*/ + +/* + * Create the Emu "emulation" part. This is of type s_cardsystem. + * Similar structures are found in the main sources folder (files reader-xxxxxx.c) + * for every cryptosystem supported by OSCam. + * Here we read keys from our virtual card (aka the SoftCam.Key file) and we inform + * OSCam about them. This is done with the emu_card_info() function. Keep in mind + * that Emu holds all its keys to separate structures for faster access. + * In addition, ECM and EMM requests are processed here, with the emu_do_ecm() and + * emu_do_emm() functions. +*/ + +#define CS_OK 1 +#define CS_ERROR 0 + +extern char cs_confdir[128]; +#ifdef MODULE_STREAMRELAY +static int8_t emu_key_data_mutex_init = 0; +#endif +pthread_mutex_t emu_key_data_mutex; + +static void set_hexserial_to_version(struct s_reader *rdr) +{ + char cVersion[32]; + uint32_t version = EMU_VERSION; + uint8_t hversion[2]; + memset(hversion, 0, 2); + snprintf(cVersion, sizeof(cVersion), "%04d", version); + char_to_bin(hversion, cVersion, 4); + rdr->hexserial[3] = hversion[0]; + rdr->hexserial[4] = hversion[1]; +} + +static void set_prids(struct s_reader *rdr) +{ + int32_t i, j; + + rdr->nprov = 0; + + for (i = 0; (i < rdr->emu_auproviders.nfilts) && (rdr->nprov < CS_MAXPROV); i++) + { + for (j = 0; (j < rdr->emu_auproviders.filts[i].nprids) && (rdr->nprov < CS_MAXPROV); j++) + { + i2b_buf(4, rdr->emu_auproviders.filts[i].prids[j], rdr->prid[i]); + rdr->nprov++; + } + } +} + +static void emu_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint8_t *key, char *keyName, uint32_t keyLength, uint8_t isData) +{ + if (!rdr->ll_entitlements) + { + rdr->ll_entitlements = ll_create("ll_entitlements"); + } + + S_ENTITLEMENT *item; + if (cs_malloc(&item, sizeof(S_ENTITLEMENT))) + { + // fill item + item->caid = caid; + item->provid = provid; + item->id = 0; + item->class = 0; + item->start = 0; + item->end = 2147472000; + item->type = 0; + item->isKey = 1; + memcpy(item->name, keyName, 8); + item->key = key; + item->keyLength = keyLength; + item->isData = isData; + + // add item + ll_append(rdr->ll_entitlements, item); + } +} + +static void refresh_entitlements(struct s_reader *rdr) +{ + uint32_t i; + uint16_t caid; + KeyData *tmpKeyData; + LL_ITER itr; + biss2_rsa_key_t *item; + + cs_clear_entitlement(rdr); + + for (i = 0; i < StreamKeys.keyCount; i++) + { + emu_add_entitlement(rdr, b2i(2, StreamKeys.EmuKeys[i].key), StreamKeys.EmuKeys[i].provider, StreamKeys.EmuKeys[i].key, + StreamKeys.EmuKeys[i].keyName, StreamKeys.EmuKeys[i].keyLength, 1); + } + + for (i = 0; i < ViKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x0500, ViKeys.EmuKeys[i].provider, ViKeys.EmuKeys[i].key, + ViKeys.EmuKeys[i].keyName, ViKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < IrdetoKeys.keyCount; i++) + { + tmpKeyData = &IrdetoKeys.EmuKeys[i]; + do + { + emu_add_entitlement(rdr, tmpKeyData->provider >> 8, tmpKeyData->provider & 0xFF, + tmpKeyData->key, tmpKeyData->keyName, tmpKeyData->keyLength, 0); + + tmpKeyData = tmpKeyData->nextKey; + } + while (tmpKeyData != NULL); + } + + for (i = 0; i < CwKeys.keyCount; i++) + { + emu_add_entitlement(rdr, CwKeys.EmuKeys[i].provider >> 8, CwKeys.EmuKeys[i].provider & 0xFF, + CwKeys.EmuKeys[i].key, CwKeys.EmuKeys[i].keyName, CwKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < PowervuKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x0E00, PowervuKeys.EmuKeys[i].provider, PowervuKeys.EmuKeys[i].key, + PowervuKeys.EmuKeys[i].keyName, PowervuKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < TandbergKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x1010, TandbergKeys.EmuKeys[i].provider, TandbergKeys.EmuKeys[i].key, + TandbergKeys.EmuKeys[i].keyName, TandbergKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < NagraKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x1801, NagraKeys.EmuKeys[i].provider, NagraKeys.EmuKeys[i].key, + NagraKeys.EmuKeys[i].keyName, NagraKeys.EmuKeys[i].keyLength, 0); + } + + // Session words for BISS1 mode 1/E (caid 2600) and BISS2 mode 1/E (caid 2602) + for (i = 0; i < BissSWs.keyCount; i++) + { + caid = (BissSWs.EmuKeys[i].keyLength == 8) ? 0x2600 : 0x2602; + emu_add_entitlement(rdr, caid, BissSWs.EmuKeys[i].provider, BissSWs.EmuKeys[i].key, + BissSWs.EmuKeys[i].keyName, BissSWs.EmuKeys[i].keyLength, 0); + } + + // Session keys (ECM keys) for BISS2 mode CA + for (i = 0; i < Biss2Keys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x2610, Biss2Keys.EmuKeys[i].provider, Biss2Keys.EmuKeys[i].key, + Biss2Keys.EmuKeys[i].keyName, Biss2Keys.EmuKeys[i].keyLength, 0); + } + + // RSA keys (EMM keys) for BISS2 mode CA + itr = ll_iter_create(rdr->ll_biss2_rsa_keys); + while ((item = ll_iter_next(&itr))) + { + emu_add_entitlement(rdr, 0x2610, 0, item->ekid, "RSAPRI", 8, 0); + } + + for (i = 0; i < OmnicryptKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x00FF, OmnicryptKeys.EmuKeys[i].provider, OmnicryptKeys.EmuKeys[i].key, + OmnicryptKeys.EmuKeys[i].keyName, OmnicryptKeys.EmuKeys[i].keyLength, 0); + } +} + +static int32_t emu_do_ecm(struct s_reader *rdr, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + if (!emu_process_ecm(rdr, er, ea->cw, &ea->cw_ex)) + { + return CS_OK; + } + + return CS_ERROR; +} + +static int32_t emu_do_emm(struct s_reader *rdr, EMM_PACKET *emm) +{ + uint32_t keysAdded = 0; + + if (emm->emmlen < 3) + { + return CS_ERROR; + } + + if (SCT_LEN(emm->emm) > emm->emmlen) + { + return CS_ERROR; + } + + if (!emu_process_emm(rdr, b2i(2, emm->caid), emm->emm, &keysAdded)) + { + if (keysAdded > 0) + { + refresh_entitlements(rdr); + } + + return CS_OK; + } + + return CS_ERROR; +} + +static int32_t emu_card_info(struct s_reader *rdr) +{ + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + + // Delete keys from Emu's memory + emu_clear_keydata(); + + // Delete BISS2 mode CA RSA keys + ll_destroy_data(&rdr->ll_biss2_rsa_keys); + + // Read keys built in the OSCam-Emu binary + emu_read_keymemory(rdr); + + // Read keys from SoftCam.Key file + emu_set_keyfile_path(cs_confdir); + + if (!emu_read_keyfile(rdr, cs_confdir)) + { + if (emu_read_keyfile(rdr, "/var/keys/")) + { + emu_set_keyfile_path("/var/keys/"); + } + } + + // Read BISS2 mode CA RSA keys from PEM files + biss_read_pem(rdr, BISS2_MAX_RSA_KEYS); + + cs_log("Total keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d", + CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount, + Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount, + StreamKeys.keyCount); + + // Inform OSCam about all available keys. + // This is used for listing the "entitlements" in the webif's reader page. + refresh_entitlements(rdr); + + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + set_prids(rdr); + + set_hexserial_to_version(rdr); + + return CS_OK; +} + +/* +static int32_t emu_card_init(struct s_reader *UNUSED(rdr), struct s_ATR *UNUSED(atr)) +{ + return CS_ERROR; +} +*/ + +int32_t emu_get_via3_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + uint32_t provid = 0; + + if(ep->emm[3] == 0x90 && ep->emm[4] == 0x03) + { + provid = b2i(3, ep->emm + 5); + provid &= 0xFFFFF0; + i2b_buf(4, provid, ep->provid); + } + + switch (ep->emm[0]) + { + case 0x88: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, 4); + rdr_log_dbg(rdr, D_EMM, "UNIQUE"); + return 1; + + case 0x8A: + case 0x8B: + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + return 1; + + case 0x8C: + case 0x8D: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED (part)"); + // We need those packets to pass otherwise we would never + // be able to complete EMM reassembly + return 1; + + case 0x8E: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED"); + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 3, 3); + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +int32_t emu_get_ird2_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + int32_t l = (ep->emm[3] & 0x07); + int32_t base = (ep->emm[3] >> 3); + char dumprdrserial[l * 3], dumpemmserial[l * 3]; + + switch (l) + { + case 0: + // global emm, 0 bytes addressed + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL base = %02x", base); + return 1; + + case 2: + // shared emm, 2 bytes addressed + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED l = %d ep = {%s} rdr = {%s} base = %02x", + l, dumpemmserial, dumprdrserial, base); + return 1; + + case 3: + // unique emm, 3 bytes addressed + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE l = %d ep = {%s} rdr = {%s} base = %02x", + l, dumpemmserial, dumprdrserial, base); + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +int32_t emu_get_pvu_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + if (ep->emm[0] == 0x82) + { + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 12, 4); + } + else + { + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + } + return 1; +} + +int32_t emu_get_tan_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + if (ep->emm[0] == 0x82 || ep->emm[0] == 0x83) + { + ep->type = GLOBAL; + } + else + { + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + } + return 1; +} + +int32_t emu_get_biss_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + switch (ep->emm[0]) + { + case 0x81: // Spec say this is for EMM, but oscam (and all other crypto systems) use it for ECM + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + ep->type = GLOBAL; + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +static int32_t emu_get_emm_type(struct emm_packet_t *ep, struct s_reader *rdr) +{ + uint16_t caid = b2i(2, ep->caid); + + if (caid_is_viaccess(caid)) return emu_get_via3_emm_type(ep, rdr); + if (caid_is_irdeto(caid)) return emu_get_ird2_emm_type(ep, rdr); + if (caid_is_powervu(caid)) return emu_get_pvu_emm_type(ep, rdr); + if (caid_is_director(caid)) return emu_get_tan_emm_type(ep, rdr); + if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_type(ep, rdr); + + return CS_ERROR; +} + +FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid) +{ + int32_t i; + + for (i = 0; i < rdr->emu_auproviders.nfilts; i++) + { + if (caid == rdr->emu_auproviders.filts[i].caid) + { + return &rdr->emu_auproviders.filts[i]; + } + } + + return NULL; +} + +static int32_t emu_get_via3_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = 1; + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8A; + filters[idx].mask[0] = 0xFE; + filters[idx].filter[3] = 0x80; + filters[idx].mask[3] = 0x80; + idx++; + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_ird2_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t caid, uint32_t UNUSED(provid)) +{ + uint8_t hexserial[3], prid[4]; + FILTER *emu_provids; + int8_t have_provid = 0, have_serial = 0; + int32_t i; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if(irdeto2_get_hexserial(caid, hexserial)) + { + have_serial = 1; + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + emu_provids = get_emu_prids_for_caid(rdr, caid); + if (emu_provids != NULL && emu_provids->nprids > 0) + { + have_provid = 1; + } + + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = have_serial + (2 * (have_provid ? emu_provids->nprids : 0)); + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + unsigned int idx = 0; + + if (have_serial) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], hexserial, 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + } + + for (i = 0; have_provid && i < emu_provids->nprids; i++) + { + i2b_buf(4, emu_provids->prids[i], prid); + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &prid[1], 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFA; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &prid[1], 2); + memset(&filters[idx].mask[2], 0xFF, 2); + idx++; + } + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_pvu_emm_filter(struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, + uint16_t caid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens) +{ + uint8_t hexserials[32][4]; + uint32_t i, count = 0; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + count = powervu_get_hexserials_new(hexserials, 32, caid, tsid, onid, ens); + if (count == 0) + { + count = powervu_get_hexserials(hexserials, 32, srvid); + if (count == 0) + { + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + return CS_ERROR; + } + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = count; + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + for (i = 0; i < count; i++) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].filter[10] = hexserials[i][0]; + filters[idx].filter[11] = hexserials[i][1]; + filters[idx].filter[12] = hexserials[i][2]; + filters[idx].filter[13] = hexserials[i][3]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[10] = 0xFF; + filters[idx].mask[11] = 0xFF; + filters[idx].mask[12] = 0xFF; + filters[idx].mask[13] = 0xFF; + idx++; + } + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_tan_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = 2; + uint8_t buf[8]; + + if (!emu_find_key('T', 0x40, 0, "MK", buf, 8, 0, 0, 0, NULL) && + !emu_find_key('T', 0x40, 0, "MK01", buf, 8, 0, 0, 0, NULL)) + { + return CS_ERROR; + } + + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + idx++; + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_biss_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = 15; + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + uint8_t i; + + for (i = 0; i < max_filter_count; i++) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x81 + i; // What about table 0x81? + filters[idx].mask[0] = 0xFF; + idx++; + + *filter_count = idx; + } + } + return CS_OK; +} + +static int32_t emu_get_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **UNUSED(emm_filters), unsigned int *UNUSED(filter_count)) +{ + return CS_ERROR; +} + +static int32_t emu_get_emm_filter_adv(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, + uint16_t caid, uint32_t provid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens) +{ + if (caid_is_viaccess(caid)) return emu_get_via3_emm_filter(rdr, emm_filters, filter_count, caid, provid); + if (caid_is_irdeto(caid)) return emu_get_ird2_emm_filter(rdr, emm_filters, filter_count, caid, provid); + if (caid_is_powervu(caid)) return emu_get_pvu_emm_filter(emm_filters, filter_count, caid, srvid, tsid, onid, ens); + if (caid_is_director(caid)) return emu_get_tan_emm_filter(rdr, emm_filters, filter_count, caid, provid); + if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_filter(rdr, emm_filters, filter_count, caid, provid); + + return CS_ERROR; +} + +const struct s_cardsystem reader_emu = +{ + .desc = "emu", + .caids = (uint16_t[]){ 0x05, 0x06, 0x0D, 0x0E, 0x10, 0x18, 0x26, 0 }, + .do_ecm = emu_do_ecm, + .do_emm = emu_do_emm, + .card_info = emu_card_info, + //.card_init = emu_card_init, // apparently this is not needed at all + .get_emm_type = emu_get_emm_type, + .get_emm_filter = emu_get_emm_filter, // needed to pass checks + .get_emm_filter_adv = emu_get_emm_filter_adv, +}; + +/* + * Create the Emu virtual "device" part. This is of type s_cardreader. + * Similar structures are found in the csctapi (Card System Card Terminal API) + * folder for every IFD (InterFace Device), aka smart card reader. + * Since we have no hardware to initialize, we start our Stream Relay server + * with the emu_reader_init() function. + * At Emu shutdown, we remove keys from memory with the emu_close() function. +*/ + +#define CR_OK 0 +#define CR_ERROR 1 + +static int32_t emu_reader_init(struct s_reader *UNUSED(reader)) +{ +#ifdef MODULE_STREAMRELAY + if (cfg.stream_relay_enabled && (stream_server_thread_init == 0)) + { + int32_t i; + stream_server_thread_init = 1; + SAFE_MUTEX_INIT(&emu_fixed_key_srvid_mutex, NULL); + + for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) + { + SAFE_MUTEX_INIT(&emu_fixed_key_data_mutex[i], NULL); + ll_emu_stream_delayed_keys[i] = ll_create("ll_emu_stream_delayed_keys"); + memset(&emu_fixed_key_data[i], 0, sizeof(emu_stream_client_key_data)); + } + + start_thread("stream_key_delayer", stream_key_delayer, NULL, NULL, 1, 1); + cs_log("Stream key delayer initialized"); + } + + // Initialize mutex for exclusive access to key database and key file + if (!emu_key_data_mutex_init) + { + SAFE_MUTEX_INIT(&emu_key_data_mutex, NULL); + emu_key_data_mutex_init = 1; + } +#endif + return CR_OK; +} + +static int32_t emu_close(struct s_reader *UNUSED(reader)) +{ + cs_log("Reader is shutting down"); + + // Delete keys from Emu's memory + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_clear_keydata(); + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + return CR_OK; +} + +static int32_t emu_get_status(struct s_reader *UNUSED(reader), int32_t *in) { *in = 1; return CR_OK; } +static int32_t emu_activate(struct s_reader *UNUSED(reader), struct s_ATR *UNUSED(atr)) { return CR_OK; } +static int32_t emu_transmit(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(expectedlen), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; } +static int32_t emu_receive(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; } +static int32_t emu_write_settings(struct s_reader *UNUSED(reader), struct s_cardreader_settings *UNUSED(s)) { return CR_OK; } +static int32_t emu_card_write(struct s_reader *UNUSED(pcsc_reader), const uint8_t *UNUSED(buf), uint8_t *UNUSED(cta_res), uint16_t *UNUSED(cta_lr), int32_t UNUSED(l)) { return CR_OK; } +static int32_t emu_set_protocol(struct s_reader *UNUSED(rdr), uint8_t *UNUSED(params), uint32_t *UNUSED(length), uint32_t UNUSED(len_request)) { return CR_OK; } + +const struct s_cardreader cardreader_emu = +{ + .desc = "emu", + .typ = R_EMU, + .skip_extra_atr_parsing = 1, + .reader_init = emu_reader_init, + .get_status = emu_get_status, + .activate = emu_activate, + .transmit = emu_transmit, + .receive = emu_receive, + .close = emu_close, + .write_settings = emu_write_settings, + .card_write = emu_card_write, + .set_protocol = emu_set_protocol, +}; + +void add_emu_reader(void) +{ + // This function is called inside oscam.c and creates an emu [reader] with default + // settings in oscam.server file. If an emu [reader] already exists, it uses that. + + LL_ITER itr; + struct s_reader *rdr; + int8_t haveEmuReader = 0; + char emuName[] = "emulator"; + char *ctab, *ftab, *emu_auproviders, *disablecrccws_only_for; + + // Check if emu [reader] entry already exists in oscam.server file and get it + itr = ll_iter_create(configured_readers); + while ((rdr = ll_iter_next(&itr))) + { + if (rdr->typ == R_EMU) + { + haveEmuReader = 1; + break; + } + } + + rdr = NULL; + + // If there's no emu [reader] in oscam.server, create one with default settings + if (!haveEmuReader) + { + if (!cs_malloc(&rdr, sizeof(struct s_reader))) + { + return; + } + + reader_set_defaults(rdr); + + rdr->enable = 1; + rdr->typ = R_EMU; + cs_strncpy(rdr->label, emuName, sizeof(emuName)); + cs_strncpy(rdr->device, emuName, sizeof(emuName)); + + // CAIDs + ctab = strdup("0500,0604,0D00,0E00,1010,1801,2600,2602,2610"); + chk_caidtab(ctab, &rdr->ctab); + NULLFREE(ctab); + + // Idents + ftab = strdup("0500:020A00,021110;" + "0604:000000;" + "0D00:0000C0;" + "0E00:000000;" + "1010:000000;" + "1801:000000,001101,002111,007301;" + "2600:000000;" + "2602:000000;" + "2610:000000;" + ); + chk_ftab(ftab, &rdr->ftab); + NULLFREE(ftab); + + // AU providers + emu_auproviders = strdup("0604:010200;0E00:000000;1010:000000;2610:000000;"); + chk_ftab(emu_auproviders, &rdr->emu_auproviders); + NULLFREE(emu_auproviders); + + // EMM cache + rdr->cachemm = 2; + rdr->rewritemm = 1; + rdr->logemm = 2; + rdr->deviceemm = 1; + + // User group + rdr->grp = 0x1ULL; + + // Add the "device" part to our emu reader + rdr->crdr = &cardreader_emu; + + // Disable CW checksum test for PowerVu + disablecrccws_only_for = strdup("0E00:000000"); + chk_ftab(disablecrccws_only_for, &rdr->disablecrccws_only_for); + NULLFREE(disablecrccws_only_for); + + reader_fixups_fn(rdr); + ll_append(configured_readers, rdr); + } + + // Set DVB Api delayer option +#ifdef HAVE_DVBAPI + if (cfg.dvbapi_enabled && cfg.dvbapi_delayer < 60) + { + cfg.dvbapi_delayer = 60; + } +#endif + + cs_log("OSCam-Emu version %d", EMU_VERSION); +} + +#endif // WITH_EMU diff --git a/module-streamrelay.c b/module-streamrelay.c index 771e4c4a..e388446a 100644 --- a/module-streamrelay.c +++ b/module-streamrelay.c @@ -14,6 +14,12 @@ #include "oscam-time.h" #include "oscam-chk.h" +#ifdef WITH_EMU +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "module-emulator-powervu.h" +#endif + #define STREAM_UNDEFINED 0x00 #define STREAM_VIDEO 0x01 #define STREAM_AUDIO 0x02 @@ -28,18 +34,32 @@ typedef struct int32_t connid; } stream_client_conn_data; -char stream_source_host[256]; -char *stream_source_auth = NULL; -uint32_t cluster_size = 50; +static char stream_source_host[256]; +static char *stream_source_auth = NULL; +static uint32_t cluster_size = 50; bool has_dvbcsa_ecm = 0, is_dvbcsa_static = 1; static uint8_t stream_server_mutex_init = 0; static pthread_mutex_t stream_server_mutex; static int32_t glistenfd, gconncount = 0, gconnfd[STREAM_SERVER_MAX_CONNECTIONS]; - -static pthread_mutex_t fixed_key_srvid_mutex; -static uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; -static stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; +#ifdef WITH_EMU +#define STATIC /* none */ +#else +#define STATIC static +#endif +STATIC pthread_mutex_t fixed_key_srvid_mutex; +STATIC uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; +STATIC stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; + +#ifdef WITH_EMU +int8_t stream_server_thread_init = 0; +int8_t emu_stream_emm_enabled = 0; +uint8_t emu_stream_server_mutex_init = 0; +int8_t stream_server_has_ecm[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +#endif #ifdef MODULE_RADEGAST static int32_t gRadegastFd = 0; @@ -131,10 +151,13 @@ static void radegast_client_ecm(stream_client_data *cdata) } } } +#endif // MODULE_RADEGAST +#ifdef WITH_EMU void ParseEcmData(stream_client_data *cdata) { uint8_t *data = cdata->ecm_data; + uint8_t dcw[16]; uint16_t section_length = SCT_LEN(data); if (section_length < 11) @@ -142,9 +165,35 @@ void ParseEcmData(stream_client_data *cdata) return; } - radegast_client_ecm(cdata); + if (caid_is_powervu(cdata->caid)) + { + if (data[11] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[11] == 0) || ((cdata->ecm_nb - data[11]) > 5)) + { + cdata->ecm_nb = data[11]; + cdata->key.connid = cdata->connid; + powervu_ecm(data, dcw, NULL, cdata->srvid, cdata->caid, cdata->tsid, cdata->onid, cdata->ens, &cdata->key); + } + } +#ifdef MODULE_RADEGAST + else + { + radegast_client_ecm(cdata); + } +#endif } -#endif // MODULE_RADEGAST +#else +#define ParseEcmData radegast_client_ecm +#endif + +#if DVBCSA_KEY_ECM +#define DVBCSA_HEADER_ECM 1 +#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b) +#else +#define DVBCSA_HEADER_ECM 0 +#endif +#ifndef STATIC_LIBDVBCSA +#define STATIC_LIBDVBCSA 0 +#endif static void write_cw(ECM_REQUEST *er, int32_t connid) { @@ -155,7 +204,11 @@ static void write_cw(ECM_REQUEST *er, int32_t connid) { if (has_dvbcsa_ecm) { +#ifdef WITH_EMU + dvbcsa_bs_key_set(er->cw, key_data[connid].key[0][EVEN]); +#else dvbcsa_bs_key_set(er->cw, key_data[connid].key[EVEN]); +#endif } } @@ -163,31 +216,36 @@ static void write_cw(ECM_REQUEST *er, int32_t connid) { if (has_dvbcsa_ecm) { +#ifdef WITH_EMU + dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[0][ODD]); +#else dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[ODD]); +#endif } } +#ifdef WITH_EMU + SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[connid]); + emu_fixed_key_data[connid].csa_used = 1; + SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[connid]); +#endif } bool stream_write_cw(ECM_REQUEST *er) { int32_t i; - if (er->rc == E_FOUND) + bool cw_written = false; + //SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) { - bool cw_written = false; - //SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); - for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + if (stream_cur_srvid[i] == er->srvid) { - if (stream_cur_srvid[i] == er->srvid) - { - write_cw(er, i); - cw_written = true; - // don't return as there might be more connections for the same channel (e.g. recordings) - } + write_cw(er, i); + cw_written = true; + // don't return as there might be more connections for the same channel (e.g. recordings) } - //SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); - return cw_written; } - return true; + //SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + return cw_written; } static void SearchTsPackets(const uint8_t *buf, const uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset) @@ -356,6 +414,21 @@ static void ParsePatData(stream_client_data *cdata) } } +#ifdef WITH_EMU +static int8_t stream_client_get_caid(stream_client_data *cdata) +{ + uint32_t tmp1 = (cdata->srvid << 16) | cdata->pmt_pid; + uint8_t tmp2[2]; + + if (emu_find_key('A', tmp1, 0, "FAKE", tmp2, 2, 0, 0, 0, NULL)) + { + cdata->caid = b2i(2, tmp2); + return 1; + } + return 0; +} +#endif + static void ParseDescriptors(const uint8_t *buffer, const uint16_t info_length, uint8_t *type) { uint32_t i; @@ -531,6 +604,9 @@ static void ParsePmtData(stream_client_data *cdata) case 0xD1: case 0xEA: { +#ifdef WITH_EMU + cdata->video_pid = elementary_pid; +#endif cs_log_dbg(D_READER, "Stream client %i found video pid: 0x%04X (%i)", cdata->connid, elementary_pid, elementary_pid); stream_parse_pmt_ca_descriptor(cdata->pmt_data, i, 5, es_info_length, cdata); @@ -545,6 +621,15 @@ static void ParsePmtData(stream_client_data *cdata) case 0x2E: case 0x81: { +#ifdef WITH_EMU + if (cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) + { + continue; + } + + cdata->audio_pids[cdata->audio_pid_count] = elementary_pid; + cdata->audio_pid_count++; +#endif cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", cdata->connid, elementary_pid, elementary_pid); break; @@ -557,11 +642,23 @@ static void ParsePmtData(stream_client_data *cdata) ParseDescriptors(cdata->pmt_data + i + 5, es_info_length, &type); if (type == STREAM_AUDIO) { +#ifdef WITH_EMU + if (cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) + { + continue; + } + + cdata->audio_pids[cdata->audio_pid_count] = elementary_pid; + cdata->audio_pid_count++; +#endif cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", cdata->connid, elementary_pid, elementary_pid); } else if (type == STREAM_TELETEXT) { +#ifdef WITH_EMU + cdata->teletext_pid = elementary_pid; +#endif cs_log_dbg(D_READER, "Stream client %i found teletext pid: 0x%04X (%i)", cdata->connid, elementary_pid, elementary_pid); } @@ -573,8 +670,60 @@ static void ParsePmtData(stream_client_data *cdata) cdata->STREAMpidcount++; #endif } +#ifdef WITH_EMU + // If we haven't found a CAID for this service, + // search the keyDB for a fake one + if (cdata->caid == NO_CAID_VALUE && stream_client_get_caid(cdata) == 1) + { + cs_log_dbg(D_READER, "Stream client %i found fake caid: 0x%04X (%i)", + cdata->connid, cdata->caid, cdata->caid); + } +#endif +} + +#ifdef WITH_EMU +static void ParseCatData(stream_client_data *cdata) +{ + uint8_t *data = cdata->cat_data; + uint32_t i; + + for (i = 8; i < (b2i(2, data + 1) & 0xFFF) - 1; i += data[i + 1] + 2) + { + if (data[i] != 0x09) + { + continue; + } + + uint16_t caid = b2i(2, data + i + 2); + + if (caid_is_powervu(caid)) // add all supported caids here + { + if (cdata->caid == NO_CAID_VALUE) + { + cdata->caid = caid; + } + cdata->emm_pid = b2i(2, data + i + 4) & 0x1FFF;; + cs_log_dbg(D_READER, "Stream client %i found emm pid: 0x%04X (%i)", + cdata->connid, cdata->emm_pid, cdata->emm_pid); + break; + } + } } +static void ParseEmmData(stream_client_data *cdata) +{ + uint32_t keysAdded = 0; + + emu_process_emm(NULL, cdata->caid, cdata->emm_data, &keysAdded); + + if (keysAdded) + { + //refresh_entitlements(rdr); + cs_log("Stream client %i found %i keys", cdata->connid, keysAdded); + } +} +#endif + static void ParseTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) { uint8_t payloadStart; @@ -623,7 +772,11 @@ static void ParseTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32 } } -static void decrypt(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid) +#ifdef WITH_EMU +static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid, uint8_t cw_type) +#else +static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid) +#endif { if (fill[oddeven] > 0) { @@ -641,10 +794,531 @@ static void decrypt(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const fill[oddeven] = 0; +#ifdef WITH_EMU + dvbcsa_bs_decrypt(key_data[connid].key[cw_type][oddeven], tsbbatch, 184); +#else dvbcsa_bs_decrypt(key_data[connid].key[oddeven], tsbbatch, 184); +#endif + } +} +#ifndef WITH_EMU +#define decrypt(a) decrypt_csa(tsbbatch, fill, a, data->connid) +#else // multiple cw +#define decrypt(a) decrypt_csa(tsbbatch, fill, a, data->connid, 0) +#define decrypt_pvu(a, b) decrypt_csa(tsbbatch, fill, a, data->connid, b) +#endif + +#ifdef WITH_EMU +static void DescrambleTsPacketsPowervu(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) +{ + uint32_t i, j, tsHeader, *deskey; + uint16_t pid, offset, fill[2] = {0,0}; + uint8_t *pdata, payloadStart, oddeven = 0, cw_type = 0; + int8_t oddKeyUsed; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + payloadStart = (tsHeader & 0x400000) >> 22; + offset = (tsHeader & 0x20) ? 4 + stream_buf[i + 4] + 1 : 4; + + if (packetSize - offset < 1) + { + continue; + } + + if (emu_stream_emm_enabled && pid == 0x0001 && data->have_cat_data != 1) // Search the CAT for EMM pids + { + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + + ParseTsData(0x01, 0xFF, 8, &data->have_cat_data, data->cat_data, sizeof(data->cat_data), + &data->cat_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseCatData, data); + continue; + } + + if (emu_stream_emm_enabled && data->emm_pid && pid == data->emm_pid) // Process the EMM data + { + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + + ParseTsData(0x80, 0xF0, 3, &data->have_emm_data, data->emm_data, sizeof(data->emm_data), + &data->emm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEmmData, data); + continue; + } + + if (data->ecm_pid && pid == data->ecm_pid) // Process the ECM data + { + stream_server_has_ecm[data->connid] = 1; + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + ParseTsData(0x80, 0xFE, 3, &data->have_ecm_data, data->ecm_data, sizeof(data->ecm_data), + &data->ecm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEcmData, data); + continue; + } + + if ((tsHeader & 0xC0) == 0) + { + continue; + } + + if (data->key.csa_used) + { + stream_buf[i + 3] &= 0x3f; // consider it decrypted now + oddeven = (tsHeader & 0xC0) == 0xC0 ? ODD: EVEN; + decrypt_pvu(oddeven == ODD ? EVEN : ODD, cw_type); + + if (pid == data->video_pid) // start with video pid, since it is most dominant + { + cw_type = PVU_CW_VID; + tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; + tsbbatch[fill[oddeven]].len = packetSize - offset; + fill[oddeven]++; + if (fill[oddeven] > cluster_size - 1) + { + decrypt_pvu(oddeven, cw_type); + } + } + else + { + if (cw_type != PVU_CW_VID) + { + decrypt_pvu(oddeven, PVU_CW_VID); + } + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + if (cw_type != PVU_CW_A1 + j) + { + decrypt_pvu(oddeven, PVU_CW_A1 + j); + } + cw_type = PVU_CW_A1 + j; + tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; + tsbbatch[fill[oddeven]].len = packetSize - offset; + fill[oddeven]++; + if (fill[oddeven] > cluster_size - 1) + { + decrypt_pvu(oddeven, cw_type); + } + } + } + } + } + else + { + oddKeyUsed = (tsHeader & 0xC0) == 0xC0 ? 1 : 0; + deskey = NULL; + + if (pid == data->video_pid) + { + deskey = data->key.pvu_des_ks[PVU_CW_VID][oddKeyUsed]; + } + else + { + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + deskey = data->key.pvu_des_ks[PVU_CW_A1 + j][oddKeyUsed]; + } + } + } + + if (deskey == NULL) + { + deskey = data->key.pvu_des_ks[PVU_CW_HSD][oddKeyUsed]; + } + + for (j = offset; j + 7 < 188; j += 8) + { + pdata = stream_buf + i + j; + des(pdata, deskey, 0); + } + + stream_buf[i + 3] &= 0x3F; + } + } + if (data->key.csa_used) { decrypt_pvu(oddeven, cw_type); } +} +static void DescrambleTsPacketsRosscrypt1(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + int8_t is_av_pid; + int32_t j; + + uint8_t scramblingControl; + uint16_t pid, offset; + uint32_t i, tsHeader; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + scramblingControl = tsHeader & 0xC0; + + if (tsHeader & 0x20) + { + offset = 4 + stream_buf[i + 4] + 1; + } + else + { + offset = 4; + } + + if (packetSize - offset < 1) + { + continue; + } + + if (scramblingControl == 0) + { + continue; + } + + if (!(stream_buf[i + 3] & 0x10)) + { + stream_buf[i + 3] &= 0x3F; + continue; + } + + is_av_pid = 0; + + if (pid == data->video_pid) + { + is_av_pid = 1; + } + else + { + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + is_av_pid = 1; + break; + } + } + } + + if (is_av_pid) + { + static uint8_t dyn_key[184]; + static uint8_t last_packet[184]; + + // Reset key on channel change + if (data->reset_key_data == 1) + { + memset(dyn_key, 0x00, 184); + memset(last_packet, 0x00, 184); + data->reset_key_data = 0; + } + + if (memcmp(last_packet, stream_buf + i + 4, 184) == 0) + { + if (memcmp(dyn_key, stream_buf + i + 4, 184) != 0) + { + memcpy(dyn_key, stream_buf + i + 4, 184); + } + } + else + { + memcpy(last_packet, stream_buf + i + 4, 184); + } + + for (j = 0; j < 184; j++) + { + stream_buf[i + 4 + j] ^= dyn_key[j]; + } + + stream_buf[i + 3] &= 0x3F; + } } } -#define decrypt(a) decrypt(tsbbatch, fill, a, data->connid) + +static void DescrambleTsPacketsCompel(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + int8_t is_pes_pid; // any PES pid + int32_t j; + const int8_t limit = 4; + + uint8_t scramblingControl; + uint16_t pid, offset; + uint32_t i, tsHeader; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + scramblingControl = tsHeader & 0xC0; + + if (tsHeader & 0x20) + { + offset = 4 + stream_buf[i + 4] + 1; + } + else + { + offset = 4; + } + + if (packetSize - offset < 1) + { + continue; + } + + if (scramblingControl == 0) + { + continue; + } + + if (!(stream_buf[i + 3] & 0x10)) + { + stream_buf[i + 3] &= 0x3F; + continue; + } + + is_pes_pid = 0; + + if (pid == data->video_pid) + { + is_pes_pid = 1; + } + else if (pid == data->teletext_pid) + { + is_pes_pid = 1; + } + else + { + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + is_pes_pid = 1; + break; + } + } + } + + if (is_pes_pid) + { + static uint8_t dyn_key[184]; + static uint8_t found_key_bytes[184]; + static uint8_t found_key_bytes_count = 8; + static uint8_t lastScramblingControl = 0xFF; + + int8_t matches00 = 0; + int8_t matchesFF = 0; + int8_t last00_was_good = 0; + int8_t lastFF_was_good = 0; + + // Reset key when scrambling control changes from odd to even + // and vice versa (every ~53 seconds) or when we change channel + if (lastScramblingControl != scramblingControl) + { + memset(dyn_key, 0x00, 184); + memset(found_key_bytes, 0, 184); + found_key_bytes_count = 8; + lastScramblingControl = scramblingControl; + + //cs_log_dbg(D_READER, "resetting key data (scrambling control: %02X)", scramblingControl); + } + + for (j = 8; j < 184; j++) + { + if (found_key_bytes_count == 184) + { + break; + } + + if (stream_buf[i + 4 + j] == 0x00) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0x3F) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (last00_was_good == 1) + { + last00_was_good = 0; + matches00--; + } + else + { + matches00 -= 2; + } + + if (matches00 < 0) + { + matches00 = 0; + } + } + + if (stream_buf[i + 4 + j] == 0xC0) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0xFF) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (lastFF_was_good == 1) + { + lastFF_was_good = 0; + matchesFF--; + } + else + { + matchesFF -= 2; + } + + if (matchesFF < 0) + { + matchesFF = 0; + } + } + } + + for (j = 183; j >= 8; j--) + { + if (found_key_bytes_count == 184) + { + break; + } + + if (stream_buf[i + 4 + j] == 0x00) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0x3F) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (last00_was_good == 1) + { + last00_was_good = 0; + matches00--; + } + else + { + matches00 -= 2; + } + + if (matches00 < 0) + { + matches00 = 0; + } + } + + if (stream_buf[i + 4 + j] == 0xC0) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0xFF) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (lastFF_was_good == 1) + { + lastFF_was_good = 0; + matchesFF--; + } + else + { + matchesFF -= 2; + } + + if (matchesFF < 0) + { + matchesFF = 0; + } + } + } + + for (j = 8; j < 184; j++) + { + stream_buf[i + 4 + j] ^= dyn_key[j]; + } + } + + stream_buf[i + 3] &= 0x3F; // Clear scrambling bits + } +} +#endif // WITH_EMU static void DescrambleTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) { @@ -782,6 +1456,9 @@ static void stream_client_disconnect(stream_client_conn_data *conndata) SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); stream_cur_srvid[conndata->connid] = NO_SRVID_VALUE; +#ifdef WITH_EMU + stream_server_has_ecm[conndata->connid] = 0; +#endif SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); SAFE_MUTEX_LOCK(&stream_server_mutex); @@ -819,10 +1496,12 @@ static void *stream_client_handler(void *arg) uint16_t packetCount = 0, packetSize = 0, startOffset = 0; uint32_t remainingDataPos, remainingDataLength, tmp_pids[4]; uint8_t descrambling = 0; - +#ifdef WITH_EMU + int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait; +#else const int32_t cur_dvb_buffer_size = DVB_BUFFER_SIZE_CSA; const int32_t cur_dvb_buffer_wait = DVB_BUFFER_WAIT_CSA; - +#endif struct dvbcsa_bs_batch_s *tsbbatch; cs_log("Stream client %i connected", conndata->connid); @@ -902,8 +1581,16 @@ static void *stream_client_handler(void *arg) return NULL; } +#ifndef WITH_EMU key_data[conndata->connid].key[ODD] = dvbcsa_bs_key_alloc(); key_data[conndata->connid].key[EVEN] = dvbcsa_bs_key_alloc(); +#else + for (i = 0; i < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; i++) + { + key_data[conndata->connid].key[i][ODD] = dvbcsa_bs_key_alloc(); + key_data[conndata->connid].key[i][EVEN] = dvbcsa_bs_key_alloc(); + } +#endif if (!cs_malloc(&tsbbatch, (cluster_size + 1) * sizeof(struct dvbcsa_bs_batch_s))) { @@ -916,6 +1603,9 @@ static void *stream_client_handler(void *arg) SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); stream_cur_srvid[conndata->connid] = data->srvid; +#ifdef WITH_EMU + stream_server_has_ecm[conndata->connid] = 0; +#endif SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); cs_log("Stream client %i request %s", conndata->connid, stream_path); @@ -933,6 +1623,9 @@ static void *stream_client_handler(void *arg) data->have_cat_data = 0; data->have_ecm_data = 0; data->have_emm_data = 0; +#ifdef WITH_EMU + data->reset_key_data = 1; +#endif while (!exit_oscam && clientStatus != -1 && streamConnectErrorCount < 3 && streamDataErrorCount < 15) @@ -954,6 +1647,18 @@ static void *stream_client_handler(void *arg) && (streamConnectErrorCount < 3 || streamDataErrorCount < 15)) #endif { +#ifdef WITH_EMU + if (data->key.csa_used) + { + cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_CSA; + cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_CSA; + } + else + { + cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_DES; + cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_DES; + } +#endif streamStatus = recv(streamfd, stream_buf + bytesRead, cur_dvb_buffer_size - bytesRead, MSG_WAITALL); if (streamStatus == 0) // socket closed { @@ -1023,13 +1728,30 @@ static void *stream_client_handler(void *arg) // We have both PAT and PMT data - We can start descrambling if (data->have_pat_data == 1 && data->have_pmt_data == 1) { - if (chk_ctab_ex(data->caid, &cfg.stream_relay_ctab) && (data->caid != 0xA101 || data->caid == NO_CAID_VALUE)) + if (chk_ctab_ex(data->caid, &cfg.stream_relay_ctab)) { +#ifdef WITH_EMU + if (caid_is_powervu(data->caid)) + { + DescrambleTsPacketsPowervu(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); + } + else if (data->caid == 0xA101) // Rosscrypt1 + { + DescrambleTsPacketsRosscrypt1(data, stream_buf + startOffset, packetCount * packetSize, packetSize); + } + else if (data->caid == NO_CAID_VALUE) // Compel + { + DescrambleTsPacketsCompel(data, stream_buf + startOffset, packetCount * packetSize, packetSize); + } + else +#endif // WITH_EMU + { DescrambleTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); if (!descrambling && cfg.stream_relay_buffer_time) { cs_sleepms(cfg.stream_relay_buffer_time); descrambling = 1; } + } } else { @@ -1066,9 +1788,16 @@ static void *stream_client_handler(void *arg) NULLFREE(http_buf); NULLFREE(stream_buf); - +#ifndef WITH_EMU dvbcsa_bs_key_free(key_data[conndata->connid].key[ODD]); dvbcsa_bs_key_free(key_data[conndata->connid].key[EVEN]); +#else + for (i = 0; i < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; i++) + { + dvbcsa_bs_key_free(key_data[conndata->connid].key[i][ODD]); + dvbcsa_bs_key_free(key_data[conndata->connid].key[i][EVEN]); + } +#endif NULLFREE(tsbbatch); NULLFREE(data); @@ -1119,6 +1848,9 @@ void *stream_server(void *UNUSED(a)) for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) { stream_cur_srvid[i] = NO_SRVID_VALUE; +#ifdef WITH_EMU + stream_server_has_ecm[i] = 0; +#endif } SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); @@ -1264,11 +1996,85 @@ void init_stream_server(void) b64encode(authtmp, cs_strlen(authtmp), &stream_source_auth); } +#ifdef WITH_EMU + emu_stream_emm_enabled = cfg.emu_stream_emm_enabled; +#endif start_thread("stream_server", stream_server, NULL, NULL, 1, 1); cs_log("Stream Relay server initialized"); } } +#if WITH_EMU +void *stream_key_delayer(void *UNUSED(arg)) +{ + int32_t i, j; + const uint8_t ecm = 0; + emu_stream_client_key_data *cdata; + LL_ITER it; + emu_stream_cw_item *item; + struct timeb t_now; + + while (!exit_oscam) + { + cs_ftime(&t_now); + + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + it = ll_iter_create(ll_emu_stream_delayed_keys[i]); + + while ((item = ll_iter_next(&it))) + { + if (comp_timeb(&t_now, &item->write_time) < 0) + { + break; + } + + SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[i]); + + cdata = &emu_fixed_key_data[i]; + + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++) + { + if (item->csa_used) + { + if (item->is_even) + { + dvbcsa_bs_key_set(item->cw[j], key_data[cdata->connid].key[j][EVEN]); + } + else + { + dvbcsa_bs_key_set(item->cw[j], key_data[cdata->connid].key[j][ODD]); + } + + cdata->csa_used = 1; + } + else + { + if (item->is_even) + { + des_set_key(item->cw[j], cdata->pvu_des_ks[j][0]); + } + else + { + des_set_key(item->cw[j], cdata->pvu_des_ks[j][1]); + } + + cdata->csa_used = 0; + } + } + SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[i]); + + ll_iter_remove_data(&it); + } + } + + cs_sleepms(25); + } + + return NULL; +} +#endif + void stop_stream_server(void) { int32_t i; diff --git a/module-streamrelay.h b/module-streamrelay.h index 27d4e0c3..6e839660 100644 --- a/module-streamrelay.h +++ b/module-streamrelay.h @@ -10,6 +10,18 @@ #define DVB_BUFFER_WAIT_CSA 188*(DVB_MAX_TS_PACKETS-128) #define DVB_BUFFER_SIZE DVB_BUFFER_SIZE_CSA +#ifdef WITH_EMU +#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4 +#define EMU_DVB_BUFFER_SIZE_CSA DVB_BUFFER_SIZE_CSA +#define EMU_DVB_BUFFER_WAIT_CSA DVB_BUFFER_WAIT_CSA +#define EMU_DVB_BUFFER_SIZE_DES 188*32 +#define EMU_DVB_BUFFER_WAIT_DES 188*29 +#define EMU_STREAM_SERVER_MAX_CONNECTIONS STREAM_SERVER_MAX_CONNECTIONS +#define emu_fixed_key_srvid_mutex fixed_key_srvid_mutex +#define emu_stream_cur_srvid stream_cur_srvid +#define emu_stream_client_data stream_client_data +#endif + //#define __BISS__ #ifdef __BISS__ #define MAX_STREAM_PIDS 32 @@ -17,24 +29,28 @@ #include "cscrypt/md5.h" #include -#if DVBCSA_KEY_ECM -#define DVBCSA_HEADER_ECM 1 -#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b) -#else -#define DVBCSA_HEADER_ECM 0 -#endif -#ifndef STATIC_LIBDVBCSA -#define STATIC_LIBDVBCSA 0 -#endif #define EVEN 0 #define ODD 1 typedef struct { +#ifdef WITH_EMU + struct dvbcsa_bs_key_s *key[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2]; +#else struct dvbcsa_bs_key_s *key[2]; +#endif } stream_client_key_data; +#ifdef WITH_EMU +typedef struct +{ + uint32_t pvu_des_ks[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2][32]; + int8_t csa_used; + int32_t connid; +} emu_stream_client_key_data; +#endif + typedef struct { int32_t connid; @@ -67,6 +83,15 @@ typedef struct uint16_t STREAMpids[MAX_STREAM_PIDS]; #endif uint8_t ecm_md5[MD5_DIGEST_LENGTH]; +#ifdef WITH_EMU + int16_t ecm_nb; + int8_t reset_key_data; + uint16_t video_pid; + uint16_t teletext_pid; + uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS]; + uint8_t audio_pid_count; + emu_stream_client_key_data key; +#endif } stream_client_data; void *stream_server(void *a); @@ -75,6 +100,30 @@ void stop_stream_server(void); bool stream_write_cw(ECM_REQUEST *er); +#ifdef WITH_EMU +extern int8_t stream_server_thread_init; +extern pthread_mutex_t fixed_key_srvid_mutex; +extern uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; +extern int8_t stream_server_has_ecm[STREAM_SERVER_MAX_CONNECTIONS]; +extern uint8_t emu_stream_server_mutex_init; +extern bool has_dvbcsa_ecm; + +typedef struct +{ + struct timeb write_time; + int8_t csa_used; + int8_t is_even; + uint8_t cw[8][8]; +} emu_stream_cw_item; + +extern pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +extern stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; +extern emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS]; + +extern LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +void *stream_key_delayer(void *arg); +#endif // WITH_EMU + #endif // MODULE_STREAMRELAY #endif // MODULE_STREAMRELAY_H_ diff --git a/module-webif.c b/module-webif.c index 1e9b5659..c26daf13 100644 --- a/module-webif.c +++ b/module-webif.c @@ -1318,6 +1318,12 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri tpl_printf(vars, TPLADD, "STREAM_RELAY_PORT", "%d", cfg.stream_relay_port); tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time); +#ifdef WITH_EMU + tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay); +#endif + tpl_printf(vars, TPLADD, "TMP", "STREAMRELAYENABLEDSELECTED%d", cfg.stream_relay_enabled); tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); diff --git a/oscam-config-global.c b/oscam-config-global.c index 2721602f..47cf2cd9 100644 --- a/oscam-config-global.c +++ b/oscam-config-global.c @@ -928,6 +928,10 @@ static const struct config_list streamrelay_opts[] = DEF_OPT_INT32("stream_relay_port" , OFS(stream_relay_port), 17999), DEF_OPT_INT8("stream_relay_enabled" , OFS(stream_relay_enabled), 0), DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0), +#ifdef WITH_EMU + DEF_OPT_INT8("stream_emm_enabled" , OFS(emu_stream_emm_enabled), 0), + DEF_OPT_UINT32("stream_ecm_delay" , OFS(emu_stream_ecm_delay), 600), +#endif DEF_OPT_FUNC("stream_relay_ctab" , OFS(stream_relay_ctab), check_caidtab_fn), DEF_LAST_OPT }; diff --git a/oscam.c b/oscam.c index 6997bc2d..dae5fe4b 100644 --- a/oscam.c +++ b/oscam.c @@ -41,6 +41,10 @@ #include "reader-common.h" #include "module-gbox.h" +#ifdef WITH_EMU + void add_emu_reader(void); +#endif + #ifdef WITH_SSL #include #include @@ -426,6 +430,8 @@ static void write_versionfile(bool use_stdout) #if defined(__arm__) || defined(__aarch64__) write_conf(WITH_ARM_NEON, "ARM NEON (SIMD/MPE) support"); #endif + write_conf(WITH_EMU, "Emulator support"); + write_conf(WITH_SOFTCAM, "Built-in SoftCam.Key"); fprintf(fp, "\n"); write_conf(MODULE_CAMD33, "camd 3.3x"); @@ -1826,6 +1832,9 @@ int32_t main(int32_t argc, char *argv[]) init_readerdb(); #ifdef MODULE_STREAMRELAY init_stream_server(); +#endif +#ifdef WITH_EMU + add_emu_reader(); #endif cfg.account = init_userdb(); init_signal(); diff --git a/webif/config/streamrelay.html b/webif/config/streamrelay.html index af860a9b..6e55efd0 100644 --- a/webif/config/streamrelay.html +++ b/webif/config/streamrelay.html @@ -17,3 +17,4 @@ ##TPLSTREAMCLIENTSOURCEHOST## Relay Port: Relay Buffer Time: +##TPLSTREAMEMUSETTINGS## diff --git a/webif/config/streamrelay_emusettings.html b/webif/config/streamrelay_emusettings.html new file mode 100644 index 00000000..08b4647b --- /dev/null +++ b/webif/config/streamrelay_emusettings.html @@ -0,0 +1,9 @@ + Process EMM from stream (emu): + + + + + ECM fix delay (emu): diff --git a/webif/pages_index.txt b/webif/pages_index.txt index e0dabd5a..3588e2b2 100644 --- a/webif/pages_index.txt +++ b/webif/pages_index.txt @@ -91,6 +91,7 @@ CONFIGMENURADEGAST config/menu_radegast.html CONFIGMENUSCAM config/menu_scam.html MODULE_SCAM CONFIGMENUSTREAMRELAY config/menu_streamrelay.html MODULE_STREAMRELAY STREAMCLIENTSOURCEHOST config/streamrelay_streamclientsourcehost.html MODULE_RADEGAST +STREAMEMUSETTINGS config/streamrelay_emusettings.html WITH_EMU CONFIGMENUSERIAL config/menu_serial.html MODULE_SERIAL CONFIGMONITOR config/monitor.html MODULE_MONITOR CONFIGNEWCAMD config/newcamd.html MODULE_NEWCAMD diff --git a/webif/status/status.html b/webif/status/status.html index d5f3846a..80034318 100644 --- a/webif/status/status.html +++ b/webif/status/status.html @@ -20,8 +20,8 @@