diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8770a186..02c6c8d5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -524,6 +524,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})
@@ -869,6 +885,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.*")
@@ -981,4 +1015,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/Distribution/doc/html/oscam.server.5.html b/Distribution/doc/html/oscam.server.5.html
index 3fb60c56..48fc01d2 100644
--- a/Distribution/doc/html/oscam.server.5.html
+++ b/Distribution/doc/html/oscam.server.5.html
@@ -840,6 +840,13 @@ set reader's CCcam reshare hop, default:0
+cccwantemu = 0|1
+
-
+1 = request to provide emu from CCCam server, too, default:0
+
+
+
+
ccckeepalive = 0|1
-
1 = send keepalive messages to keep connection to remote CCCam server up, default:0
diff --git a/Distribution/doc/man/oscam.server.5 b/Distribution/doc/man/oscam.server.5
index 4b5db81e..3a94758d 100644
--- a/Distribution/doc/man/oscam.server.5
+++ b/Distribution/doc/man/oscam.server.5
@@ -649,6 +649,11 @@ set reader's CCcam reshare hop, default:0
\fBx\fP = resharing for direct peer and share level x
.RE
.PP
+\fBcccwantemu\fP = \fB0\fP|\fB1\fP
+.RS 3n
+1 = request to provide emu from CCCam server, too, default:0
+.RE
+.PP
\fBccckeepalive\fP = \fB0\fP|\fB1\fP
.RS 3n
1 = send keepalive messages to keep connection to remote CCCam server up, default:0
diff --git a/Distribution/doc/txt/oscam.server.txt b/Distribution/doc/txt/oscam.server.txt
index 3d333a93..08b173ed 100644
--- a/Distribution/doc/txt/oscam.server.txt
+++ b/Distribution/doc/txt/oscam.server.txt
@@ -478,6 +478,9 @@ DESCRIPTIONS
0 = resharing for direct peer only
x = resharing for direct peer and share level x
+ cccwantemu = 0|1
+ 1 = request to provide emu from CCCam server, too, default:0
+
ccckeepalive = 0|1
1 = send keepalive messages to keep connection to remote CCCam server up, default:0
diff --git a/Makefile b/Makefile
index e9a00959..473b62ab 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
@@ -363,6 +366,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/README.config b/README.config
index 685295bf..5a61ee12 100644
--- a/README.config
+++ b/README.config
@@ -54,7 +54,7 @@ Examples:
./config.sh -D card_readers -E CARDREADER_INTERNAL
Available options:
- addons: WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF 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_COMPRESS_WEBIF 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
diff --git a/README.md b/README.md
index 8d1c6e93..c3cacec5 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,18 @@
-OSCam: Open Source Conditional Access Module
-============================================
+OSCam-Emu: Open Source CAM with Emulator
+========================================
+[](https://github.com/oscam-emu/oscam-patched/actions/workflows/build.yml)
+Wiki
+====
+https://github.com/oscam-emu/oscam-patched/wiki
License
=======
-OSCam: Open Source CAM
-Copyright (C) 2009-2024 OSCam developers
+OSCam-Emu: Open Source CAM with Emulator
+Copyright (C) 2009-2024 OSCam-Emu developers
-OSCam is based on the Streamboard mp-cardserver 0.9d by dukat and
+OSCam-Emu is based on the Streamboard mp-cardserver 0.9d by dukat and
has been extended and worked on by many more since then.
This program is free software: you can redistribute it and/or modify
@@ -24,41 +28,34 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-For the full text of the licese, please read COPYING file in OSCam
+For the full text of the licese, please read COPYING file in OSCam-Emu
top directory or visit http://www.gnu.org/licenses/gpl-3.0.html
Version history
===============
-OSCam history is accessible through Trac timeline at:
- https://git.streamboard.tv/common/oscam/-/commits/master
+OSCam-Emu history is accessible through GitHub history at:
+ https://github.com/oscam-emu/oscam-patched/commits/master/
Repositories
============
GIT repository:
- git clone https://git.streamboard.tv/common/oscam.git oscam
+ git clone https://github.com/oscam-emu/oscam-patched.git
-Building OSCam from source
+Building OSCam-Emu from source
==========================
- - Get the lastest sources from SVN
- git clone https://git.streamboard.tv/common/oscam.git oscam
-
- - Go to oscam-trunk directory.
-
- - Run `make config` to choose the features you want.
-
- - Run `make` to compile OSCam.
+https://github.com/oscam-emu/oscam-patched/wiki/Building-OSCam-Emu
For more information and examples on using the build system, please
see README.build and README.config files.
-Building OScam for different CPUs (cross-compilation)
+Building OSCam-Emu for different CPUs (cross-compilation)
=====================================================
First you need to install the target CPU toolchain. Already built toolchains
@@ -66,7 +63,7 @@ for various architectures can be downloaded from:
https://git.streamboard.tv/common/oscam/-/wikis/CrossCompiling
-In order to cross compile OSCam you need to set CROSS variable when
+In order to cross compile OSCam-Emu you need to set CROSS variable when
running make. For example to compile for SH4 architecture you need
to run: `make CROSS=sh4-linux-` or if your cross compilers are not
in your PATH - `make CROSS=/opt/STM/STLinux-2.3/devkit/sh4/bin/sh4-linux-`.
@@ -75,11 +72,11 @@ in your PATH - `make CROSS=/opt/STM/STLinux-2.3/devkit/sh4/bin/sh4-linux-`.
Dependencies
============
-OSCam by default do not depend on external libraries except when compilation
+OSCam-Emu by default do not depend on external libraries except when compilation
with SSL is requested. In that case openssl (libcrypto) library must be
installed.
-OSCam supports building with the following external dependencies:
+OSCam-Emu supports building with the following external dependencies:
- libcrypto (libssl) - 'make USE_LIBCRYPTO=1'
- libusb - 'make USE_LIBUSB=1'
- PCSC - 'make USE_PCSC=1'
@@ -104,12 +101,12 @@ Help and Support
man pages and configuration examples are in Distribution/doc directory.
-You may visit our Trac system for tracking and filling bug reports.
- https://git.streamboard.tv/common/oscam/-/issues/new
+You may visit our GitHub system for tracking and filling bug reports.
+ https://github.com/oscam-emu/oscam-patched/issues/new
If you experience any problems with OSCam, feel free to post in our support
forum under (mainly German and English language) at:
https://board.streamboard.tv/forum/
Configuration wiki:
- https://wiki.streamboard.tv/wiki/OSCam
+ https://github.com/oscam-emu/oscam-patched/wiki/OSCam-Emu-configuration
diff --git a/config.h b/config.h
index f48ca1da..1139180d 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
@@ -8,7 +10,7 @@
//#define WITH_SSL 1
#if defined(__linux__) || defined(__CYGWIN__)
#define HAVE_DVBAPI 1
-//#define WITH_EXTENDED_CW 1
+#define WITH_EXTENDED_CW 1
#endif
//#define WITH_NEUTRINO 1
#define READ_SDT_CHARSETS 1
diff --git a/config.sh b/config.sh
index 3e946317..d22b40fa 100755
--- a/config.sh
+++ b/config.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF 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_SIGNING"
+addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF 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_SIGNING 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"
@@ -12,7 +12,7 @@ CONFIG_WEBIF_JQUERY=y
CONFIG_WITH_COMPRESS_WEBIF=y
# CONFIG_WITH_SSL=n
CONFIG_HAVE_DVBAPI=y
-# CONFIG_WITH_EXTENDED_CW=n
+CONFIG_WITH_EXTENDED_CW=y
# CONFIG_WITH_NEUTRINO=n
CONFIG_READ_SDT_CHARSETS=y
# CONFIG_CS_ANTICASC=n
@@ -28,6 +28,8 @@ CONFIG_WITH_LB=y
# CONFIG_IPV6SUPPORT=n
# CONFIG_WITH_ARM_NEON=n
# CONFIG_WITH_SIGNING=n
+CONFIG_WITH_EMU=y
+CONFIG_WITH_SOFTCAM=y
# CONFIG_MODULE_CAMD33=n
CONFIG_MODULE_CAMD35=y
CONFIG_MODULE_CAMD35_TCP=y
@@ -329,14 +331,17 @@ 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_SIGNING && enable_opt WITH_SSL >/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() {
@@ -386,9 +391,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_any READER_NAGRA_MERLIN WITH_SIGNING && echo "CONFIG_LIB_SHA256=y" || echo "# CONFIG_LIB_SHA256=n"
@@ -516,6 +521,8 @@ menu_addons() {
IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \
WITH_ARM_NEON "ARM NEON (SIMD/MPE) support" $(check_test "WITH_ARM_NEON") \
WITH_SIGNING "Binary signing with X.509 certificate" $(check_test "WITH_SIGNING") \
+ WITH_EMU "Emulator support" $(check_test "WITH_EMU") \
+ WITH_SOFTCAM "Built-in SoftCam.Key" $(check_test "WITH_SOFTCAM") \
2> ${tempfile}
opt=${?}
@@ -894,12 +901,14 @@ do
;;
'-v'|'--oscam-version')
version=`grep '^#define CS_VERSION' globals.h | cut -d\" -f2`
- echo $version
+ emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'`
+ echo $version-$emuversion
break
;;
'-r'|'--oscam-revision') #//DEPRECATED, will be removed in later versions
revision=`grep '^#define CS_VERSION' globals.h | awk -F'-|"' '{print $3}'`
- echo $revision
+ emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'`
+ echo $revision-$emuversion
break
;;
'-c'|'--oscam-commit')
diff --git a/csctapi/cardreaders.h b/csctapi/cardreaders.h
index 8b6c309a..35f38c6c 100644
--- a/csctapi/cardreaders.h
+++ b/csctapi/cardreaders.h
@@ -14,5 +14,6 @@ extern const struct s_cardreader cardreader_smartreader;
extern const struct s_cardreader cardreader_stapi;
extern const struct s_cardreader cardreader_stinger;
extern const struct s_cardreader cardreader_drecas;
+extern const struct s_cardreader cardreader_emu;
#endif
diff --git a/globals.h b/globals.h
index 9a50a972..1d23c4ff 100644
--- a/globals.h
+++ b/globals.h
@@ -368,7 +368,7 @@
/* ===========================
* constants
* =========================== */
-#define SCM_URL "https://git.streamboard.tv/common/oscam"
+#define SCM_URL "https://github.com/oscam-emu/oscam-patched"
#define WIKI_URL "https://wiki.streamboard.tv/wiki"
#define BOARD_URL "https://board.streamboard.tv"
#ifndef CS_VERSION
@@ -406,14 +406,18 @@
#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
#define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header
+#ifdef WITH_EMU
+#define CS_EMMCACHESIZE 1024 // nr of EMMs that EMU reader will cache
+#else
#define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache
+#endif
#define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages
#define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine
@@ -452,6 +456,7 @@
#define R_SMART 0x7 // Smartreader+
#define R_PCSC 0x8 // PCSC
#define R_DRECAS 0x9 // Reader DRECAS
+#define R_EMU 0x17 // Reader EMU
/////// proxy readers after R_CS378X
#define R_CAMD35 0x20 // Reader cascading camd 3.5x
#define R_CAMD33 0x21 // Reader cascading camd 3.3x
@@ -889,6 +894,13 @@ typedef struct s_entitlement // contains entitlement Info
uint32_t class; // the class needed for some systems
time_t start; // startdate
time_t end; // enddate
+#ifdef WITH_EMU
+ bool isKey;
+ bool isData;
+ char name[8];
+ uint8_t *key;
+ uint32_t keyLength;
+#endif
} S_ENTITLEMENT;
struct s_client;
@@ -1752,6 +1764,7 @@ struct s_reader // contains device info, reader info and card info
char cc_build[7]; // cccam build number
int8_t cc_maxhops; // cccam max distance
int8_t cc_mindown; // cccam min downhops
+ int8_t cc_want_emu; // Schlocke: Client want to have EMUs, 0 - NO; 1 - YES
uint32_t cc_id;
int8_t cc_keepalive;
int8_t cc_hop; // For non-cccam reader: hop for virtual cards
@@ -1928,6 +1941,11 @@ struct s_reader // contains device info, reader info and card info
#endif
#ifdef MODULE_GHTTP
uint8_t ghttp_use_ssl;
+#endif
+#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 +2471,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
@@ -2638,12 +2660,18 @@ bool boxtype_is(const char *boxtype);
bool boxname_is(const char *boxname);
const char *boxtype_get(void);
const char *boxname_get(void);
+static inline bool caid_is_fake(uint16_t caid) { return caid == 0xffff; }
+static inline bool caid_is_biss(uint16_t caid) { return caid >> 8 == 0x26; }
+static inline bool caid_is_biss_fixed(uint16_t caid) { return caid == 0x2600 || caid == 0x2602; } // fixed cw, fake ecm
+static inline bool caid_is_biss_dynamic(uint16_t caid) { return caid == 0x2610; } // dynamic cw, real ecm and emm
static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; }
static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; }
static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; }
static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; }
static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; }
static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; }
+static inline bool caid_is_powervu(uint16_t caid) { return caid >> 8 == 0x0E; }
+static inline bool caid_is_director(uint16_t caid) { return caid >> 8 == 0x10; }
static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; }
static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; }
static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; }
@@ -2651,4 +2679,8 @@ static inline bool caid_is_dre(uint16_t caid) { return caid == 0x4AE0 || caid ==
static inline bool caid_is_tongfang(uint16_t caid) { return caid == 0x4A02; }
const char *get_cardsystem_desc_by_caid(uint16_t caid);
+#ifdef WITH_EMU
+FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid);
+#endif
+
#endif
diff --git a/module-cacheex.c b/module-cacheex.c
index 9287aed3..04e27699 100644
--- a/module-cacheex.c
+++ b/module-cacheex.c
@@ -921,7 +921,9 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
}
#endif
- if(chk_is_null_CW(er->cw))
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+ if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
{
cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl));
cl->cwcacheexerr++;
@@ -930,7 +932,9 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
return 0;
}
- if(get_odd_even(er) == 0)
+ // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them)
+ // Don't check for BISS2 mode CA (ECM table is always 0x80)
+ if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0)
{
cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl));
cl->cwcacheexerr++;
diff --git a/module-camd35-cacheex.c b/module-camd35-cacheex.c
index 68c24d75..f7258a02 100644
--- a/module-camd35-cacheex.c
+++ b/module-camd35-cacheex.c
@@ -670,7 +670,7 @@ void camd35_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_
if(size < 32)
size = 32;
- uint8_t token[14];
+ uint8_t token[18];
// bitfield
i2b_buf(2, feature, payload + i);
diff --git a/module-cccam-cacheex.c b/module-cccam-cacheex.c
index 6878d327..5acc7b9a 100644
--- a/module-cccam-cacheex.c
+++ b/module-cccam-cacheex.c
@@ -660,7 +660,7 @@ void cc_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_t mo
break;
// aio-version
case 32: ;
- uint8_t token[14];
+ uint8_t token[18];
// bitfield
i2b_buf(2, feature, payload + i);
diff --git a/module-cccam.c b/module-cccam.c
index c89815c8..febbbad0 100644
--- a/module-cccam.c
+++ b/module-cccam.c
@@ -1009,7 +1009,7 @@ int32_t cc_send_cli_data(struct s_client *cl)
memcpy(buf, rdr->r_usr, sizeof(rdr->r_usr));
memcpy(buf + 20, cc->node_id, 8);
- buf[28] = 0; // <-- Client wants to have EMUs, 0 - NO; 1 - YES
+ buf[28] = rdr->cc_want_emu; // <-- Client wants to have EMUs, 0 - NO; 1 - YES
memcpy(buf + 29, rdr->cc_version, sizeof(rdr->cc_version)); // cccam version (ascii)
memcpy(buf + 61, rdr->cc_build, sizeof(rdr->cc_build)); // build number (ascii)
@@ -1544,7 +1544,8 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
}
if((ncard->caid == cur_er->caid // caid matches
- || lb_match)) // or system matches if caid ends with 00
+ || (rdr->cc_want_emu && (ncard->caid == (cur_er->caid & 0xFF00))))
+ || lb_match) // or system matches if caid ends with 00 (needed for wantemu)
{
int32_t goodSidCount = ll_count(ncard->goodsids);
int32_t badSidCount = ll_count(ncard->badsids);
@@ -1583,7 +1584,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
}
}
- if(caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop))
+ if(!(rdr->cc_want_emu) && caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop))
{
xcard = ncard; // remember card (D+ / 1810 fix) if request has no provider, but card has
}
diff --git a/module-cw-cycle-check.c b/module-cw-cycle-check.c
index c8617d32..f9cc8108 100644
--- a/module-cw-cycle-check.c
+++ b/module-cw-cycle-check.c
@@ -123,7 +123,9 @@ static uint8_t checkvalidCW(ECM_REQUEST *er)
{
uint8_t ret = 1;
- if(chk_is_null_CW(er->cw))
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+ if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
{ er->rc = E_NOTFOUND; }
if(er->rc == E_NOTFOUND)
diff --git a/module-dvbapi-azbox.c b/module-dvbapi-azbox.c
index 7387f02d..321f19e8 100644
--- a/module-dvbapi-azbox.c
+++ b/module-dvbapi-azbox.c
@@ -312,7 +312,9 @@ void azbox_send_dcw(struct s_client *client, ECM_REQUEST *er)
int32_t n;
for(n = 0; n < 2; n++)
{
- if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+ if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid)))
{
memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8);
memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8);
diff --git a/module-dvbapi-mca.c b/module-dvbapi-mca.c
index 8b4d44d5..cc399e40 100644
--- a/module-dvbapi-mca.c
+++ b/module-dvbapi-mca.c
@@ -601,8 +601,10 @@ void mca_send_dcw(struct s_client *client, ECM_REQUEST *er)
int32_t n;
for(n = 0; n < 2; n++)
{
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if((memcmp(er->cw + (n * 8), demux[0].last_cw[0][0], 8) && memcmp(er->cw + (n * 8), demux[0].last_cw[0][1], 8))
- && (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
+ && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid)))
{
memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8);
memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8);
diff --git a/module-dvbapi-stapi.c b/module-dvbapi-stapi.c
index e136091d..2264bfc2 100644
--- a/module-dvbapi-stapi.c
+++ b/module-dvbapi-stapi.c
@@ -730,8 +730,10 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3
for(l = 0; l < 2; l++)
{
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0
- && (memcmp(cw + (l * 8), nullcw, 8) != 0))
+ && (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
{
for(n = 0; n < PTINUM; n++)
{
diff --git a/module-dvbapi-stapi5.c b/module-dvbapi-stapi5.c
index 2adf4518..76f0a6ff 100644
--- a/module-dvbapi-stapi5.c
+++ b/module-dvbapi-stapi5.c
@@ -731,8 +731,10 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3
for(l = 0; l < 2; l++)
{
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0
- && (memcmp(cw + (l * 8), nullcw, 8) != 0))
+ && (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
{
ErrorCode = oscam_sttkd_KeyWrite(tkd_desc_info[demux[demux_id].dev_index].key_hndl, l, cw + (l * 8));
diff --git a/module-dvbapi.c b/module-dvbapi.c
index 1ccdb46e..2f0fcea7 100644
--- a/module-dvbapi.c
+++ b/module-dvbapi.c
@@ -1883,7 +1883,14 @@ void dvbapi_start_emm_filter(int32_t demux_id)
if(match)
{
- csystem = get_cardsystem_by_caid(caid);
+ if(rdr->typ == R_EMU)
+ {
+ csystem = rdr->csystem;
+ }
+ else
+ {
+ csystem = get_cardsystem_by_caid(caid);
+ }
if(csystem)
{
if(caid != ncaid)
@@ -1904,7 +1911,15 @@ void dvbapi_start_emm_filter(int32_t demux_id)
}
else if(csystem->get_emm_filter)
{
- csystem->get_emm_filter(rdr, &dmx_filter, &filter_count);
+ if(rdr->typ == R_EMU)
+ {
+ csystem->get_emm_filter_adv(rdr, &dmx_filter, &filter_count, caid, provid, demux[demux_id].program_number,
+ demux[demux_id].tsid, demux[demux_id].onid, demux[demux_id].ens);
+ }
+ else
+ {
+ csystem->get_emm_filter(rdr, &dmx_filter, &filter_count);
+ }
}
}
else
@@ -2642,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
@@ -2682,6 +2699,63 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked,
match = 1; // so make it a match to try it!
}
+ // BISS1 and BISS2 mode 1/E or FAKE caid
+ // ecm pid is fake, so send out one fake ecm request
+ // special treatment: if we asked the cw first without starting a filter,
+ // the cw request will be killed due to no ecmfilter started
+ 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 = 7;
+ er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81
+ er->ecm[1] = 0x00;
+ er->ecm[2] = 0x04;
+ i2b_buf(2, er->srvid, er->ecm + 3);
+ i2b_buf(2, er->pmtpid, er->ecm + 5);
+
+ 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,
+ demux[demux_id].ECMpids[pid].CAID,
+ demux[demux_id].ECMpids[pid].PROVID,
+ demux[demux_id].ECMpids[pid].ECM_PID,
+ demux[demux_id].pmtpid,
+ demux[demux_id].ECMpids[pid].VPID);
+
+ demux[demux_id].curindex = pid; // set current pid to the fresh started one
+ dvbapi_start_filter(demux_id,
+ pid,
+ demux[demux_id].ECMpids[pid].ECM_PID,
+ demux[demux_id].ECMpids[pid].CAID,
+ demux[demux_id].ECMpids[pid].PROVID,
+ 0x80,
+ 0xF0,
+ 3000,
+ TYPE_ECM);
+
+ started = 1;
+ request_cw(dvbapi_client, er, demux_id, 0); // do not register ecm since this try!
+ fake_ecm = 1;
+ break; // we started an ecmfilter so stop looking for next matching reader!
+ }
+
if(match) // if matching reader found check for irdeto cas if local irdeto card check if it received emms in last 60 minutes
{
if(caid_is_irdeto(er->caid)) // irdeto cas init irdeto_curindex to wait for first index (00)
@@ -3791,7 +3865,7 @@ static void dvbapi_parse_pmt_ca_descriptor(int32_t demux_id, const uint8_t *buff
ca_system_id = b2i(2, buffer);
ca_pid = b2i(2, buffer + 2) & 0x1FFF;
- if(ca_system_id == 0x0000 || ca_pid == 0x1FFF)
+ if(ca_system_id == 0x0000 || (!caid_is_biss_fixed(ca_system_id) && !caid_is_fake(ca_system_id) && ca_pid == 0x1FFF))
{
return; // This is not a valid CAID or ECM pid
}
@@ -4379,14 +4453,33 @@ static void dvbapi_capmt_notify(struct demux_s *dmx)
static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid)
{
- bool start_emm = true;
+ bool is_powervu = false, start_emm = true;
char service_name[CS_SERVICENAME_SIZE];
+ // The CA PMT should have given us enough info to determine if descrambling
+ // is possible. Parsing the (real) PMT is not necessary, unless we have a
+ // PowerVu encrypted channel or (for some weird reason) no stream pids at all.
+ // Actually, when no streams are available, we set the PMT pid as the 1st
+ // stream pid, so we have to check against that. Finally, if the PMT pid is
+ // not included in the CA PMT, we start the PAT filter instead.
+
+#ifdef WITH_EXTENDED_CW
+ uint8_t i;
+ for(i = 0; i < demux[demux_id].ECMpidcount; i++)
+ {
+ if(caid_is_powervu(demux[demux_id].ECMpids[i].CAID))
+ {
+ is_powervu = true;
+ break;
+ }
+ }
+#endif
+
if(demux[demux_id].pmtpid == 0)
{
dvbapi_start_pat_filter(demux_id);
}
- else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid)
+ else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu)
{
dvbapi_start_pmt_filter(demux_id);
}
@@ -5586,6 +5679,7 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
if(filtertype == TYPE_ECM)
{
uint32_t chid = 0x10000;
+ int8_t pvu_skip = 0;
ECM_REQUEST *er;
if(len != 0) // len = 0 receiver encountered an internal bufferoverflow!
@@ -5615,8 +5709,25 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
return;
}
+#ifdef WITH_EMU
+ if(caid_is_powervu(curpid->CAID)) // ecm counter for powervu
+ {
+ pvu_skip = 1;
+
+ if(sctlen - 11 > buffer[9])
+ {
+ if(buffer[11 + buffer[9]] > curpid->pvu_counter
+ || (curpid->pvu_counter == 255 && buffer[11 + buffer[9]] == 0)
+ || ((curpid->pvu_counter - buffer[11 + buffer[9]]) > 5))
+ {
+ curpid->pvu_counter = buffer[11 + buffer[9]];
+ pvu_skip = 0;
+ }
+ }
+ }
+#endif
// wait for odd / even ecm change (only not for irdeto!)
- if(curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID))
+ if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip)
{
if(!(er = get_ecmtask()))
{
@@ -5960,6 +6071,35 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
return; // just skip on internal buffer overflow
}
+#ifdef WITH_EMU
+ if(caid_is_director(demux[demux_id].demux_fd[filter_num].caid))
+ {
+ uint32_t i;
+ uint32_t emmhash;
+
+ if(sctlen < 4)
+ {
+ return;
+ }
+
+ for(i = 0; i + 2 < sctlen; i++)
+ {
+ if(buffer[i] == 0xF0 && (buffer[i + 2] == 0xE1 || buffer[i + 2] == 0xE4))
+ {
+ emmhash = (buffer[3] << 8) | buffer[sctlen - 2];
+ if(demux[demux_id].demux_fd[filter_num].cadata == emmhash)
+ {
+ return;
+ }
+
+ demux[demux_id].demux_fd[filter_num].cadata = emmhash;
+ dvbapi_process_emm(demux_id, filter_num, buffer, sctlen);
+ return;
+ }
+ }
+ return;
+ }
+#endif
// fix to handle more than one irdeto emm packet
uint8_t *pbuf = buffer;
int32_t done = 0;
@@ -7178,8 +7318,10 @@ void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t *
for(n = 0; n < 2; n++)
{
// Check if cw has changed and if new cw is empty (all zeros)
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if((memcmp(cw + (n * cw_length), demux[demux_id].last_cw[stream_id][n], cw_length) != 0 || cw_empty)
- && (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0))
+ && (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0 || caid_is_biss(demux[demux_id].ECMpids[pid].CAID)))
{
// prepare ca device
uint32_t idx = dvbapi_ca_set_pid(demux_id, pid, stream_id, (algo == CA_ALGO_DES), msgid);
@@ -7490,7 +7632,9 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er)
// 0=matching ecm hash, 2=no filter, 3=table reset, 4=cache-ex response
// Check only against last_cw[0] (index 0) - No need to check the rest
- if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND)
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+ if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND && !caid_is_biss(er->caid))
{
// check for matching control word
if(memcmp(er->cw, demux[i].last_cw[0][0], 8) == 0 &&
@@ -8495,7 +8639,9 @@ int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er)
}
// Check for null cw
- if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0)
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+ if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0 && !caid_is_biss(er->caid))
{
return 5;
}
diff --git a/module-dvbapi.h b/module-dvbapi.h
index 12e9dab5..86eae695 100644
--- a/module-dvbapi.h
+++ b/module-dvbapi.h
@@ -336,6 +336,9 @@ typedef struct filter_s
uint32_t SlotHandle[10];
uint32_t BufferHandle[10];
#endif
+#ifdef WITH_EMU
+ uint32_t cadata;
+#endif
} FILTERTYPE;
#ifdef WITH_EXTENDED_CW
@@ -368,6 +371,9 @@ typedef struct s_ecmpid
uint32_t index[MAX_STREAM_INDICES]; // ca indices used for this ecm pid (index[0] holds ca index for STREAMmpids[0] and so on)
uint32_t streams; // bit mask of STREAMpids enabled for this ECMpid
uint32_t cadata;
+#ifdef WITH_EMU
+ int16_t pvu_counter;
+#endif
} ECMPIDTYPE;
typedef struct s_emmpid
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..a83dc64a
--- /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 802
+
+#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-newcamd.c b/module-newcamd.c
index 95fdae7e..5514962d 100644
--- a/module-newcamd.c
+++ b/module-newcamd.c
@@ -1118,6 +1118,13 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
// set userfilter for au enabled clients
if(aureader)
{
+#ifdef WITH_EMU
+ if(aureader->typ == R_EMU)
+ {
+ usr_filter = *get_emu_prids_for_caid(aureader, cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid);
+ }
+ else
+#endif
mk_user_au_ftab(aureader, &usr_filter);
}
@@ -1184,7 +1191,7 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
uint32_t rprid;
found = 0;
- if(pufilt->caid == aureader->caid)
+ if(pufilt->caid == aureader->caid && aureader->typ != R_EMU)
{
for(k = 0; k < aureader->nprov; k++)
{
diff --git a/module-stat.c b/module-stat.c
index ed0e85e4..ee179b79 100644
--- a/module-stat.c
+++ b/module-stat.c
@@ -897,7 +897,7 @@ void check_lb_auto_betatunnel_mode(ECM_REQUEST *er)
uint16_t get_rdr_caid(struct s_reader *rdr)
{
- if(is_network_reader(rdr))
+ if(is_network_reader(rdr) || rdr->typ == R_EMU)
{
return 0; // reader caid is not real caid
}
@@ -1288,7 +1288,7 @@ void stat_get_best_reader(ECM_REQUEST *er)
for(ea = er->matching_rdr; ea; ea = ea->next)
{
rdr = ea->reader;
- if(is_network_reader(rdr)) // reader caid is not real caid
+ if(is_network_reader(rdr) || rdr->typ == R_EMU) // reader caid is not real caid
{
prv = ea;
continue; // proxy can convert or reject
diff --git a/module-streamrelay.c b/module-streamrelay.c
index 04866ba5..27684575 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)
@@ -335,6 +393,9 @@ static void ParsePatData(stream_client_data *cdata)
{
int32_t i;
uint16_t srvid;
+#ifdef __BISS__
+ cdata->STREAMpidcount = 0;
+#endif
for (i = 8; i + 7 < SCT_LEN(cdata->pat_data); i += 4)
{
srvid = b2i(2, cdata->pat_data + i);
@@ -353,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;
@@ -528,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);
@@ -542,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;
@@ -554,20 +642,88 @@ 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);
}
break;
}
}
+#ifdef __BISS__
+ cdata->STREAMpids[cdata->STREAMpidcount] = elementary_pid;
+ 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;
@@ -616,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)
{
@@ -634,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); }
}
-#define decrypt(a) decrypt(tsbbatch, fill, a, data->connid)
+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;
+ }
+ }
+}
+
+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)
{
@@ -662,6 +1343,31 @@ static void DescrambleTsPackets(stream_client_data *data, uint8_t *stream_buf, u
continue;
}
#ifdef MODULE_RADEGAST
+#ifdef __BISS__
+ if(data->ecm_pid == 0x1FFF && caid_is_biss_fixed(data->caid))
+ {
+ uint32_t j, n;
+ uint16_t ecm_len = 7;
+ data->ecm_data[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81
+ data->ecm_data[1] = 0x00;
+ data->ecm_data[2] = 0x04;
+ i2b_buf(2, data->srvid, data->ecm_data + 3);
+ i2b_buf(2, data->pmt_pid, data->ecm_data + 5);
+ for(j = 0, n = 7; j < data->STREAMpidcount; j++, n += 2)
+ {
+ i2b_buf(2, data->STREAMpids[j], data->ecm_data + n);
+ data->ecm_data[2] += 2;
+ ecm_len += 2;
+ }
+ data->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging
+ data->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid
+ i2b_buf(2, data->tsid, data->ecm_data + ecm_len); // place tsid after the last stream pid
+ i2b_buf(2, data->onid, data->ecm_data + ecm_len + 2); // place onid right after tsid
+ i2b_buf(4, data->ens, data->ecm_data + ecm_len + 4); // place namespace at the end of the ecm
+ data->ecm_data[2] += 8;
+ ParseEcmData(data);
+ } else
+#endif // __BISS__
if (data->ecm_pid && pid == data->ecm_pid) // Process the ECM data
{
// set to null pid
@@ -750,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);
@@ -787,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);
@@ -870,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)))
{
@@ -884,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);
@@ -901,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)
@@ -922,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
{
@@ -1004,13 +1741,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
{
@@ -1047,9 +1801,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);
@@ -1100,6 +1861,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);
@@ -1245,11 +2009,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 71e97b53..6e839660 100644
--- a/module-streamrelay.h
+++ b/module-streamrelay.h
@@ -10,26 +10,47 @@
#define DVB_BUFFER_WAIT_CSA 188*(DVB_MAX_TS_PACKETS-128)
#define DVB_BUFFER_SIZE DVB_BUFFER_SIZE_CSA
-#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
+#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
-#ifndef STATIC_LIBDVBCSA
-#define STATIC_LIBDVBCSA 0
+
+//#define __BISS__
+#ifdef __BISS__
+#define MAX_STREAM_PIDS 32
#endif
+#include "cscrypt/md5.h"
+#include
+
#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;
@@ -57,7 +78,20 @@ typedef struct
uint16_t ecm_pid;
uint16_t emm_pid;
uint16_t pcr_pid;
+#ifdef __BISS__
+ uint8_t STREAMpidcount;
+ 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);
@@ -66,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-tpl.c b/module-webif-tpl.c
index 29b93646..66ce02a9 100644
--- a/module-webif-tpl.c
+++ b/module-webif-tpl.c
@@ -455,6 +455,7 @@ char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subd
check_conf(WITH_SSL, ptr2);
check_conf(WITH_STAPI, ptr2);
check_conf(WITH_STAPI5, ptr2);
+ check_conf(WITH_EMU, ptr2);
} // for
if(ok == 0)
{
diff --git a/module-webif.c b/module-webif.c
index 1c1dc568..3ccce709 100644
--- a/module-webif.c
+++ b/module-webif.c
@@ -137,8 +137,9 @@ static bool use_srvid2 = false;
#define MNU_GBX_FSTAINF 27
#define MNU_GBX_FEXPINF 28
#define MNU_GBX_INFOLOG 29
+#define MNU_CFG_FSOFTCAMKEY 30
-#define MNU_CFG_TOTAL_ITEMS 30 // sum of items above. Use it for "All inactive" in function calls too.
+#define MNU_CFG_TOTAL_ITEMS 31 // sum of items above. Use it for "All inactive" in function calls too.
static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value)
{
@@ -1318,6 +1319,12 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri
tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time);
tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count);
+#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");
@@ -2429,7 +2436,7 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
chk_reader("services", servicelabels, rdr);
chk_reader("lb_whitelist_services", servicelabelslb, rdr);
- if(is_network_reader(rdr)) //physical readers make trouble if re-started
+ if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started
{
if(rdr)
{
@@ -3206,6 +3213,8 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare);
tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect);
+ if(rdr->cc_want_emu)
+ { tpl_addVar(vars, TPLADD, "CCCWANTEMUCHECKED", "checked"); }
if(rdr->cc_keepalive)
{ tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); }
#endif
@@ -3243,6 +3252,23 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript);
#endif
+#ifdef WITH_EMU
+ //emu_auproviders
+ value = mk_t_ftab(&rdr->emu_auproviders);
+ tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value);
+ free_mk_t(value);
+
+ // Date-coded BISS keys
+ if(!apicall)
+ {
+ tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : "");
+ }
+ else
+ {
+ tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0");
+ }
+#endif
+
tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0));
// Show only parameters which needed for the reader
@@ -3264,6 +3290,9 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
case R_CAMD35 :
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT"));
break;
+ case R_EMU :
+ tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT"));
+ break;
case R_CS378X :
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT"));
break;
@@ -5292,9 +5321,34 @@ static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams
tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "
New Structure:
");
char tbuffer[83];
+#ifdef WITH_EMU
+ char keyBuffer[1024];
+#endif
int jsondelimiter = 0;
while((item = ll_iter_next(&itr)))
{
+#ifdef WITH_EMU
+ if(item->isKey)
+ {
+ tpl_addVar(vars, TPLADD, "ENTSTARTDATE", "");
+ tpl_addVar(vars, TPLADD, "ENTENDDATE", "");
+ cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer));
+ tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid");
+ tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid);
+ tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid);
+ tpl_addVar(vars, TPLADD, "ENTID", item->name);
+ tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer);
+ if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); }
+ else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); }
+ tpl_addVar(vars, TPLADD, "ENTRESNAME", "");
+
+ if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now))
+ { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); }
+
+ continue;
+ }
+#endif
+
localtime_r(&item->start, &start_t);
localtime_r(&item->end, &end_t);
@@ -5788,6 +5842,9 @@ static char *send_oscam_status(struct templatevars * vars, struct uriparams * pa
filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode));
#else
filtered = (type == cl->typ);
+#endif
+#ifdef WITH_EMU
+ if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1;
#endif
}
}
@@ -7340,6 +7397,9 @@ static char *send_oscam_files(struct templatevars * vars, struct uriparams * par
{ "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27
{ "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28
{ "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29
+#endif
+#ifdef WITH_EMU
+ { "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30
#endif
{ NULL, 0, 0 },
};
@@ -7812,7 +7872,11 @@ static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams
else if(!proxy && rdr->csystem_active) // local active reader
{
csystem = rdr->csystem;
- caid = rdr->caid;
+
+ if(rdr->typ != R_EMU)
+ {
+ caid = rdr->caid;
+ }
}
if(csystem)
@@ -8981,7 +9045,11 @@ static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePl
memcpy(*result + bufsize, buf2, n);
bufsize += n;
+#ifdef WITH_EMU
+ if(bufsize > 204800) // max request size 200kb
+#else
if(bufsize > 102400) // max request size 100kb
+#endif
{
cs_log("error: too much data received from %s", cs_inet_ntoa(in));
NULLFREE(*result);
diff --git a/oscam-chk.c b/oscam-chk.c
index b3791534..5caf94fa 100644
--- a/oscam-chk.c
+++ b/oscam-chk.c
@@ -894,7 +894,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
return 0;
}
- if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) )
+ if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF)))
{
if (!rdr->csystem)
{ return 0; }
@@ -932,7 +932,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
}
// Checking ident:
- if(!chk_rfilter(er, rdr))
+ if(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr))
{
cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label);
return (0);
@@ -993,7 +993,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
}
// Checking entitlements:
- if(ll_count(rdr->ll_entitlements) > 0)
+ if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU))
{
LL_ITER itr = ll_iter_create(rdr->ll_entitlements);
S_ENTITLEMENT *item;
@@ -1174,7 +1174,7 @@ int32_t chk_caid(uint16_t caid, CAIDTAB *ctab)
int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid)
{
- if(is_network_reader(rdr))
+ if(is_network_reader(rdr) || rdr->typ == R_EMU)
{
return 1; // reader caid is not real caid
}
diff --git a/oscam-config-global.c b/oscam-config-global.c
index 22255e9d..74e4e974 100644
--- a/oscam-config-global.c
+++ b/oscam-config-global.c
@@ -929,6 +929,10 @@ static const struct config_list streamrelay_opts[] =
DEF_OPT_INT8("stream_relay_enabled" , OFS(stream_relay_enabled), 0),
DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0),
DEF_OPT_UINT8("stream_relay_reconnect_count" , OFS(stream_relay_reconnect_count), 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-config-reader.c b/oscam-config-reader.c
index a813e98c..8f438772 100644
--- a/oscam-config-reader.c
+++ b/oscam-config-reader.c
@@ -114,6 +114,7 @@ static void protocol_fn(const char *token, char *value, void *setting, FILE *f)
{ "newcamd525", R_NEWCAMD },
{ "newcamd524", R_NEWCAMD },
{ "drecas", R_DRECAS },
+ { "emu", R_EMU },
{ NULL, 0 }
}, *p;
int i;
@@ -682,6 +683,9 @@ void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE
if(ftab_type & FTAB_FBPCAID) { rdr = container_of(setting, struct s_reader, fallback_percaid); }
if(ftab_type & FTAB_LOCALCARDS) { rdr = container_of(setting, struct s_reader, localcards); }
if(ftab_type & FTAB_IGNCHKSMCAID){ rdr = container_of(setting, struct s_reader, disablecrccws_only_for); }
+#ifdef WITH_EMU
+ if(ftab_type & FTAB_EMUAU) { rdr = container_of(setting, struct s_reader, emu_auproviders); }
+#endif
#ifdef MODULE_GBOX
if(ftab_type & FTAB_CCCGBXRESHARE){ rdr = container_of(setting, struct s_reader, ccc_gbx_reshare_ident); }
#endif
@@ -1228,6 +1232,7 @@ static const struct config_list reader_opts[] =
DEF_OPT_SSTR("cccversion" , OFS(cc_version), "", SIZEOF(cc_version)),
DEF_OPT_INT8("cccmaxhops" , OFS(cc_maxhops), DEFAULT_CC_MAXHOPS),
DEF_OPT_INT8("cccmindown" , OFS(cc_mindown), 0),
+ DEF_OPT_INT8("cccwantemu" , OFS(cc_want_emu), 0),
DEF_OPT_INT8("ccckeepalive" , OFS(cc_keepalive), DEFAULT_CC_KEEPALIVE),
DEF_OPT_INT8("cccreshare" , OFS(cc_reshare), DEFAULT_CC_RESHARE),
DEF_OPT_INT32("cccreconnect" , OFS(cc_reconnect), DEFAULT_CC_RECONNECT),
@@ -1242,6 +1247,10 @@ static const struct config_list reader_opts[] =
#endif
#ifdef READER_DRECAS
DEF_OPT_STR("stmkeys" , OFS(stmkeys), NULL),
+#endif
+#ifdef WITH_EMU
+ DEF_OPT_FUNC_X("emu_auproviders" , OFS(emu_auproviders), ftab_fn, FTAB_READER | FTAB_EMUAU),
+ DEF_OPT_INT8("emu_datecodedenabled" , OFS(emu_datecodedenabled), 0),
#endif
DEF_OPT_INT8("resetalways" , OFS(resetalways), 0),
DEF_OPT_INT8("deprecated" , OFS(deprecated), 0),
@@ -1339,7 +1348,7 @@ static bool reader_check_setting(const struct config_list *UNUSED(clist), void *
// These are written only when the reader is CCCAM
static const char *cccam_settings[] =
{
- "cccversion", "cccmaxhops", "cccmindown", "ccckeepalive",
+ "cccversion", "cccmaxhops", "cccmindown", "cccwantemu", "ccckeepalive",
"cccreconnect",
0
};
diff --git a/oscam-config.h b/oscam-config.h
index b0179dd8..e66a1aa1 100644
--- a/oscam-config.h
+++ b/oscam-config.h
@@ -64,6 +64,7 @@ enum ftab_fn
FTAB_LOCALCARDS = 0x20,
FTAB_IGNCHKSMCAID = 0x40,
FTAB_IGNCRCCEX4USERONLYFOR = 0x80,
+ FTAB_EMUAU = 0x100,
FTAB_CCCGBXRESHARE = 0x200
};
diff --git a/oscam-ecm.c b/oscam-ecm.c
index cadd2de9..9ded1fbd 100644
--- a/oscam-ecm.c
+++ b/oscam-ecm.c
@@ -1817,6 +1817,10 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
return 0;
}
+ // Special checks for rc
+ // Skip check for BISS1 - cw could be zero but still catch cw=0 by anticascading
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+
// bad/wrong chksum/ecm
if(rc == E_NOTFOUND && rcEx == E2_WRONG_CHKSUM)
{
@@ -1836,7 +1840,7 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
}
}
- if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw))
+ if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw) && !caid_is_biss(er->caid))
{
rc = E_NOTFOUND;
cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send fake cw, set rc=E_NOTFOUND!", reader ? reader->label : "-");
@@ -2023,7 +2027,9 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
if(!ea->is_pending) // not for pending ea - only once for ea
{
// cache update
- if(ea && (ea->rc < E_NOTFOUND) && !chk_is_null_CW(ea->cw))
+ // Skip check for BISS1 - cw could be indeed zero
+ // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
+ if(ea && (ea->rc < E_NOTFOUND) && (!chk_is_null_CW(ea->cw) && !caid_is_biss(er->caid)))
{
#ifdef CS_CACHEEX_AIO
int32_t ecmtime = ea->ecm_time;
@@ -2666,7 +2672,9 @@ void get_cw(struct s_client *client, ECM_REQUEST *er)
}
// Check for odd/even byte
- if(get_odd_even(er) == 0)
+ // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them)
+ // Don't check for BISS2 mode CA (ECM table is always 0x80)
+ if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0)
{
cs_log_dbg(D_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-"));
er->rc = E_INVALID;
diff --git a/oscam-emm.c b/oscam-emm.c
index 848fa341..65ba29cc 100644
--- a/oscam-emm.c
+++ b/oscam-emm.c
@@ -40,6 +40,7 @@ static int8_t cs_emmlen_is_blocked(struct s_reader *rdr, int16_t len)
static int8_t do_simple_emm_filter(struct s_reader *rdr, const struct s_cardsystem *csystem, EMM_PACKET *ep, int8_t cl_dvbapi)
{
if(is_network_reader(rdr)) { return 1; } // don't evaluate on network readers, server with local reader will check it
+ if(rdr->typ == R_EMU) { return 1; } // don't evalutate on emu reader
//copied and enhanced from module-dvbapi.c
//dvbapi_start_emm_filter()
@@ -233,6 +234,24 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid
rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X fixup to %06X (ignoring last digit)", reader->auprovid, prid);
}
+#ifdef WITH_EMU
+ if(reader->typ == R_EMU)
+ {
+ FILTER *emu_provids = get_emu_prids_for_caid(reader, caid);
+ if(emu_provids != NULL)
+ {
+ for(i = 0; i < emu_provids->nprids; i++)
+ {
+ if(provid == emu_provids->prids[i])
+ {
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+#endif
+
if(prid == provid)
{
rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X matching with emm provid = %06X -> SEND!", prid, provid);
diff --git a/oscam-simples.c b/oscam-simples.c
index 576804c9..6e1c7137 100644
--- a/oscam-simples.c
+++ b/oscam-simples.c
@@ -468,6 +468,8 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid)
if(caid_is_viaccess(caid)) { return "viaccess"; }
if(caid_is_irdeto(caid)) { return "irdeto"; }
if(caid_is_videoguard(caid)) { return "videoguard"; }
+ if(caid_is_powervu(caid)) { return "powervu"; }
+ if(caid_is_director(caid)) { return "director"; }
if(caid_is_conax(caid)) { return "conax"; }
if(caid_is_cryptoworks(caid)) { return "cryptoworks"; }
if(caid_is_betacrypt(caid)) { return "betacrypt"; }
@@ -476,6 +478,7 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid)
if(caid >= 0x5501 && caid <= 0x551A) { return "griffin"; }
if(caid_is_dre(caid)) { return "drecrypt"; }
if(caid_is_bulcrypt(caid)) { return "bulcrypt"; }
+ if(caid_is_biss(caid)) { return "biss"; }
if(caid == 0x4ABF) { return "dgcrypt"; }
return "unknown";
}
diff --git a/oscam.c b/oscam.c
index 7b6395db..175595a8 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
@@ -460,6 +464,8 @@ static void write_versionfile(bool use_stdout)
#ifdef WITH_SIGNING
write_conf(WITH_SIGNING, "Binary signing 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");
@@ -1676,6 +1682,9 @@ const struct s_cardreader *cardreaders[] =
#ifdef CARDREADER_STINGER
&cardreader_stinger,
#endif
+#ifdef WITH_EMU
+ &cardreader_emu,
+#endif
NULL
};
@@ -1856,6 +1865,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/reader-common.c b/reader-common.c
index 64aed4b1..86503bf4 100644
--- a/reader-common.c
+++ b/reader-common.c
@@ -15,6 +15,7 @@
#include "reader-common.h"
//#include "csctapi/atr.h"
#include "csctapi/icc_async.h"
+#include "readers.h" // required by the EMU reader
extern const struct s_cardsystem *cardsystems[];
extern char *RDR_CD_TXT[];
@@ -148,6 +149,18 @@ static int32_t reader_get_cardsystem(struct s_reader *reader, ATR *atr)
{
int32_t i;
+#ifdef WITH_EMU
+ if(reader->typ == R_EMU)
+ {
+ NULLFREE(reader->csystem_data);
+ rdr_log(reader, "found card system %s", reader_emu.desc);
+ reader->csystem = &reader_emu;
+ reader->csystem_active = true;
+ led_status_found_cardsystem();
+ return (reader->csystem_active);
+ }
+#endif
+
for(i = 0; cardsystems[i]; i++)
{
NULLFREE(reader->csystem_data);
diff --git a/readers.h b/readers.h
index eec350e9..316c2bea 100644
--- a/readers.h
+++ b/readers.h
@@ -17,5 +17,6 @@ extern const struct s_cardsystem reader_tongfang;
extern const struct s_cardsystem reader_bulcrypt;
extern const struct s_cardsystem reader_griffin;
extern const struct s_cardsystem reader_dgcrypt;
+extern const struct s_cardsystem reader_emu;
#endif
diff --git a/webif/config/streamrelay.html b/webif/config/streamrelay.html
index 78450028..ff7632e0 100644
--- a/webif/config/streamrelay.html
+++ b/webif/config/streamrelay.html
@@ -18,3 +18,4 @@
| Relay Port: | |
| Relay Buffer Time: | |
| Relay Reconnect Count: | |
+##TPLSTREAMEMUSETTINGS##
diff --git a/webif/config/streamrelay_emusettings.html b/webif/config/streamrelay_emusettings.html
new file mode 100644
index 00000000..dcb0618b
--- /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/files/menu.html b/webif/files/menu.html
index 4dc6f02e..2e4e6b62 100644
--- a/webif/files/menu.html
+++ b/webif/files/menu.html
@@ -10,7 +10,7 @@
##TPLFILEMENUGBOX##
-
diff --git a/webif/files/menu_softcamkey.html b/webif/files/menu_softcamkey.html
new file mode 100644
index 00000000..cf718779
--- /dev/null
+++ b/webif/files/menu_softcamkey.html
@@ -0,0 +1 @@
+
diff --git a/webif/pages_index.txt b/webif/pages_index.txt
index 954112d5..6a0368e0 100644
--- a/webif/pages_index.txt
+++ b/webif/pages_index.txt
@@ -1,10 +1,10 @@
# This file contains index of the templates.
-# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDENCYx"
+# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDANCYx"
# Lines starting with # are ignorred
# TEMPLATE_NAME - name of the template and also name of the file that
# contains the template
# FILENAME - The file that contains the template
-# DEPENDENCYx - The config variable which is responsible for this template
+# DEPENDANCYx - The config variable which is responsible for this template
JSONCACHEEX api.json/cacheex.json CS_CACHEEX
JSONCACHEEXBIT api.json/cacheexbit.json CS_CACHEEX
@@ -62,7 +62,7 @@ CONFIGCCCAM config/cccam.html
CONFIGCCCAMCTRL config/cccam_control.html MODULE_CCCSHARE
CONFIGCONTENT config/config.html
CONFIGDVBAPI config/dvbapi.html HAVE_DVBAPI
-EXTENDEDCWAPI config/dvbapi_extended_cw_api.html WITH_EXTENDED_CW
+EXTENDEDCWAPI config/dvbapi_extended_cw_api.html WITH_EXTENDED_CW,WITH_EMU
DEMUXERFIX config/dvbapi_demuxerfix.html MODULE_STREAMRELAY
CCCAMRESHAREBIT config/cccreshare.html MODULE_GBOX
CONFIGGBOX config/gbox.html MODULE_GBOX
@@ -92,6 +92,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
@@ -131,6 +132,7 @@ FILEMENUDVBAPI files/menu_dvbapi.html
FILEMENUFAKECWS files/menu_fakecws.html CS_CACHEEX
FILEMENUGBOX files/menu_gbox.html MODULE_GBOX
FILEMENUTWIN files/menu_twin.html MODULE_SERIAL
+FILEMENUSOFTCAMKEY files/menu_softcamkey.html WITH_EMU
AUTOCONF ghttp/autoconf.html MODULE_GHTTP
PREAUTOCONF ghttp/pre_autoconf.html MODULE_GHTTP
@@ -199,6 +201,7 @@ READEREDITCACHEEXBIT readerconfig/readerconfig_cacheexbit.html
READEREDITCACHEEXAIOBIT readerconfig/readerconfig_cacheexaiobit.html CS_CACHEEX_AIO
READERCONFIGCAMD35BIT readerconfig/readerconfig_camd35bit.html MODULE_CAMD35
READERCONFIGCCCAMBIT readerconfig/readerconfig_cccambit.html MODULE_CCCAM
+READERCONFIGEMUBIT readerconfig/readerconfig_emubit.html WITH_EMU
READERCONFIGCS378XBIT readerconfig/readerconfig_cs378xbit.html MODULE_CAMD35_TCP
READERCONFIGGBOXBIT readerconfig/readerconfig_gboxbit.html MODULE_GBOX
READERINFOGBOXREMM readerconfig/readerinfo_gbox_remm.html MODULE_GBOX
diff --git a/webif/readerconfig/readerconfig_cccambit.html b/webif/readerconfig/readerconfig_cccambit.html
index 8e38bc6f..82fba335 100644
--- a/webif/readerconfig/readerconfig_cccambit.html
+++ b/webif/readerconfig/readerconfig_cccambit.html
@@ -31,4 +31,5 @@
| Maxhop: | |
| Mindown: | |
| Reshare: | Global CCCam Reshare:##RESHARE## |
+ | Want Emu: | |
| Keep alive: | |
diff --git a/webif/readerconfig/readerconfig_emubit.html b/webif/readerconfig/readerconfig_emubit.html
new file mode 100644
index 00000000..8c130b7c
--- /dev/null
+++ b/webif/readerconfig/readerconfig_emubit.html
@@ -0,0 +1,2 @@
+ | AU providers: | |
+ | [BISS] Enable date-coded keys: | |