diff --git a/CMakeLists.txt b/CMakeLists.txt index dc19535f8..4603c4dd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,6 @@ if (OSCamOperatingSystem MATCHES "Windows/Cygwin") ${CMAKE_CURRENT_SOURCE_DIR}/csctapi ${CMAKE_CURRENT_SOURCE_DIR}/cscrypt ${CMAKE_CURRENT_SOURCE_DIR}/minilzo - ${CMAKE_CURRENT_SOURCE_DIR}/ffdecsa ${CMAKE_CURRENT_SOURCE_DIR}/extapi/cygwin /usr/include/w32api ${OPTIONAL_INCLUDE_DIR} @@ -111,7 +110,6 @@ else (OSCamOperatingSystem MATCHES "Windows/Cygwin") ${CMAKE_CURRENT_SOURCE_DIR}/csctapi ${CMAKE_CURRENT_SOURCE_DIR}/cscrypt ${CMAKE_CURRENT_SOURCE_DIR}/minilzo - ${CMAKE_CURRENT_SOURCE_DIR}/ffdecsa ${OPTIONAL_INCLUDE_DIR} ) endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") @@ -419,6 +417,39 @@ else (HAVE_PCSC) endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") endif (HAVE_PCSC) +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled MODULE_STREAMRELAY OUTPUT_VARIABLE CONFIG_STREAMRELAY OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1) + if (LIBDVBCSADIR) + check_include_file ("${LIBDVBCSADIR}/include/dvbcsa/dvbcsa.h" FOUND_LIBDVBCSA) + set (LIBADVBCSA_LIBRARY "${LIBDVBCSADIR}/libdvbcsa.a") + set (LIBDVBCSA_LIBRARY "${LIBDVBCSADIR}/libdvbcsa.so") + else (LIBDVBCSADIR) + check_include_file ("dvbcsa/dvbcsa.h" FOUND_LIBDVBCSA) + find_library (LIBADVBCSA_LIBRARY NAMES libdvbcsa.a) + find_library (LIBDVBCSA_LIBRARY NAMES dvbcsa) + endif (LIBDVBCSADIR) + + if (HAVE_LIBDVBCSA) + if (STATIC_LIBDVBCSA AND FOUND_LIBDVBCSA AND EXISTS ${LIBADVBCSA_LIBRARY}) + message(STATUS " static libdvbcsa found (libdvbcsa.a).") + add_library(imp_libdvbcsa STATIC IMPORTED) + set_property(TARGET imp_libdvbcsa PROPERTY IMPORTED_LOCATION ${LIBADVBCSA_LIBRARY}) + set (dvbcsa_link "imp_libdvbcsa") + set (STATICLIBDVBCSA True) + elseif ((NOT STATIC_LIBDVBCSA OR STATIC_LIBDVBCSA EQUAL 0) AND FOUND_LIBDVBCSA AND EXISTS ${LIBDVBCSA_LIBRARY}) + message(STATUS " libdvbcsa found (libdvbcsa.so).") + add_library(imp_libdvbcsa SHARED IMPORTED) + set_property(TARGET imp_libdvbcsa PROPERTY IMPORTED_LOCATION ${LIBDVBCSA_LIBRARY} ) + set(dvbcsa_link dvbcsa) + set (STATICLIBDVBCSA False) + else (STATIC_LIBDVBCSA AND FOUND_LIBDVBCSA AND EXISTS ${LIBADVBCSA_LIBRARY}) + message(FATAL_ERROR " no libdvbcsa found!") + endif (STATIC_LIBDVBCSA AND FOUND_LIBDVBCSA AND EXISTS ${LIBADVBCSA_LIBRARY}) + else (HAVE_LIBDVBCSA) + message(FATAL_ERROR " HAVE_LIBDVBCSA disabled!") + endif (HAVE_LIBDVBCSA) +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 @@ -467,7 +498,6 @@ execute_process (COMMAND make --no-print-directory --quiet -C ${CMAKE_CURRENT_SO add_subdirectory (csctapi) add_subdirectory (minilzo) add_subdirectory (cscrypt) -add_subdirectory (ffdecsa) #----------------------- file groups ------------------------------ execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled MODULE_CAMD33 OUTPUT_VARIABLE CAMD33 OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -517,7 +547,7 @@ add_library (${csoscam} STATIC ${csoscam_srcs} ${csoscam_hdrs}) set (exe_name "oscam") add_executable (${exe_name} ${exe_srcs} ${exe_hdrs}) -target_link_libraries (${exe_name} ${csoscam} ${csmodules} ${csreaders} csctapi cscrypt minilzo ffdecsa) +target_link_libraries (${exe_name} ${csoscam} ${csmodules} ${csreaders} csctapi cscrypt minilzo) if(HAVE_LIBRT AND HAVE_LIBUSB) if (LIBUSBDIR) set (libusb_link "imp_libusb") @@ -645,7 +675,7 @@ elseif (OSCamOperatingSystem MATCHES "SU980") target_link_libraries (${exe_name} rt entropic) endif (OSCamOperatingSystem MATCHES "Linux") -target_link_libraries (${exe_name} ${libusb_link} ${rt_link} ${setupapi_link} ${ole32_link} ${shell32_link} ${pthread_link} ${dl_link} ${xcas_link}) +target_link_libraries (${exe_name} ${libusb_link} ${rt_link} ${setupapi_link} ${ole32_link} ${shell32_link} ${pthread_link} ${dl_link} ${xcas_link} ${dvbcsa_link}) #----------------------- put svnversion in the build ------------------------------ # at every target rebuild, we re-build the oscam.c compilation... @@ -671,10 +701,26 @@ add_definitions ("-D'CS_SVN_VERSION=\"${CS_SVN_VERSION}\"'") execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine COMMAND tr -d '\n' OUTPUT_VARIABLE CS_TARGET) add_definitions ("-D'CS_TARGET=\"${CS_TARGET}\"'") + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_ARM_NEON OUTPUT_VARIABLE CONFIG_WITH_ARM_NEON OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_ARM_NEON MATCHES "Y" AND NOT WITH_ARM_NEON EQUAL 0) + add_definitions ("-DWITH_ARM_NEON") + set (WITH_ARM_NEON "1") +elseif (CONFIG_WITH_ARM_NEON MATCHES "Y" AND NOT WITH_ARM_NEON EQUAL 0) + message (status " The config file has WITH_ARM_NEON enabled, but You disabled it by you cmake command COMPILING WHITOUT arm neon optimization") +endif (CONFIG_WITH_ARM_NEON MATCHES "Y" AND NOT WITH_ARM_NEON EQUAL 0) #----------------------- global compile and link options ------------------------------ -#enable sse2 on x86 +#enable sse2 on x86, neon on arm if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3") +elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "(arm)|(ARM)") + if (WITH_ARM_NEON EQUAL 1) + message(status " ARM NEON is enabled, compiling with neon optimization") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon") + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mfpu=neon") + else (WITH_ARM_NEON EQUAL 1) + message(status " ARM NEON is disabled, compiling without neon optimization") + endif (WITH_ARM_NEON EQUAL 1) endif (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") # disable warning about unused but set variables in gcc 4.6+ @@ -684,7 +730,7 @@ if (CMAKE_COMPILER_IS_GNUCC) list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) list(GET GCC_VERSION_COMPONENTS 0 GCC_MINOR) add_definitions ("-W -Wall ") - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2 -pipe -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-schedule-insns") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2") set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ggdb") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb") @@ -846,9 +892,9 @@ if (HAVE_PTHREAD) message (STATUS " use system pthread functions") endif (HAVE_PTHREAD) if (HAVE_LIBUSB) -if(STATIC_LIBUSB EQUAL 1) - message (STATUS " You selected to enable static libusb") -endif(STATIC_LIBUSB EQUAL 1) + if(STATIC_LIBUSB EQUAL 1) + message (STATUS " You selected to enable static libusb") + endif(STATIC_LIBUSB EQUAL 1) if(STATICLIBUSB AND NOT LIBUSBDIR) message (STATUS " use static libusb functions") else(STATICLIBUSB AND NOT LIBUSBDIR) @@ -863,6 +909,24 @@ endif(STATIC_LIBUSB EQUAL 1) endif(LIBUSBDIR AND STATIC_LIBUSB EQUAL 0) endif(STATICLIBUSB AND NOT LIBUSBDIR) endif (HAVE_LIBUSB) +if (HAVE_LIBDVBCSA) + if(STATIC_LIBDVBCSA EQUAL 1) + message (STATUS " You selected to enable static libdvbcsa") + endif(STATIC_LIBDVBCSA EQUAL 1) + if(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) + message (STATUS " use static libdvbcsa functions") + else(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) + if (LIBDVBCSADIR AND STATIC_LIBDVBCSA EQUAL 0) + message(STATUS " use system libdvbcsa from selected LIBDVBCSADIR functions") + elseif (LIBDVBCSADIR AND STATIC_LIBDVBCSA EQUAL 1) + message(STATUS " use static libdvbcsa from selected LIBDVBCSADIR functions") + elseif(LIBDVBCSADIR) + message(STATUS " use system libdvbcsa from selected LIBDVBCSADIR functions") + elseif(NOT LIBDVBCSADIR AND NOT STATIC_LIBDVBCSA) + message (STATUS " use system libdvbcsa functions") + endif(LIBDVBCSADIR AND STATIC_LIBDVBCSA EQUAL 0) + endif(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) +endif (HAVE_LIBDVBCSA) if (WITH_EMU) message (STATUS " Compile with EMU support") diff --git a/Distribution/doc/html/oscam.conf.5.html b/Distribution/doc/html/oscam.conf.5.html index 898183c09..6a8c40d21 100644 --- a/Distribution/doc/html/oscam.conf.5.html +++ b/Distribution/doc/html/oscam.conf.5.html @@ -1634,6 +1634,16 @@

The [dvbapi] section

+demuxer_fix = 0|1 +

+try fixing audio/video sync errors: +

+
 0 = disabled (default) +
 1 = enabled +

+ +

+ cw_delay = milli-seconds

delay of CW writing, default:none diff --git a/Distribution/doc/man/oscam.conf.5 b/Distribution/doc/man/oscam.conf.5 index 4eef81544..ec5794425 100644 --- a/Distribution/doc/man/oscam.conf.5 +++ b/Distribution/doc/man/oscam.conf.5 @@ -1234,6 +1234,14 @@ mode writing provider name into \fBoscam.srvid2\fR file: \fB1\fP = enabled .RE .PP +\fBdemuxer_fix\fP = \fB0\fP|\fB1\fP +.RS 3n +try fixing audio/video sync errors: + + \fB0\fP = disabled (default) + \fB1\fP = enabled +.RE +.PP \fBcw_delay\fP = \fBmilli-seconds\fP .RS 3n delay of CW writing, default:none diff --git a/Distribution/doc/txt/oscam.conf.txt b/Distribution/doc/txt/oscam.conf.txt index 0c7e2a756..4d9e1651e 100644 --- a/Distribution/doc/txt/oscam.conf.txt +++ b/Distribution/doc/txt/oscam.conf.txt @@ -864,6 +864,12 @@ DESCRIPTIONS 0 = disabled (default) 1 = enabled + demuxer_fix = 0|1 + try fixing audio/video sync errors: + + 0 = disabled (default) + 1 = enabled + cw_delay = milli-seconds delay of CW writing, default:none diff --git a/Makefile b/Makefile index 4782cc6bf..f01a0c167 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,10 @@ ifeq ($(uname_S),FreeBSD) LIB_DL := endif +ifeq "$(shell ./config.sh --enabled MODULE_STREAMRELAY)" "Y" + override USE_LIBDVBCSA=1 +endif + override STD_LIBS := -lm $(LIB_PTHREAD) $(LIB_DL) $(LIB_RT) override STD_DEFS := -D'CS_SVN_VERSION="$(SVN_REV)"' override STD_DEFS += -D'CS_CONFDIR="$(CONF_DIR)"' @@ -58,18 +62,25 @@ override STD_DEFS += -D'CS_CONFDIR="$(CONF_DIR)"' CC_WARN = -W -Wall -Wshadow -Wredundant-decls -Wstrict-prototypes -Wold-style-definition # Compiler optimizations -CC_OPTS = -O2 -ggdb -pipe -ffunction-sections -fdata-sections +ifeq ($(HOSTCC),clang) + CC_OPTS = -O2 -ggdb -pipe -ffunction-sections -fdata-sections -fomit-frame-pointer +else + CC_OPTS = -O2 -ggdb -pipe -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-schedule-insns +endif CC = $(CROSS_DIR)$(CROSS)gcc STRIP = $(CROSS_DIR)$(CROSS)strip LDFLAGS = -Wl,--gc-sections +#enable sse2 on x86, neon on arm TARGETHELP := $(shell $(CC) --target-help 2>&1) ifneq (,$(findstring sse2,$(TARGETHELP))) -override CFLAGS += -fexpensive-optimizations -mmmx -msse -msse2 -msse3 -else -override CFLAGS += -fexpensive-optimizations +override CFLAGS += -mmmx -msse -msse2 -msse3 +else ifneq (,$(findstring neon,$(TARGETHELP))) + ifeq "$(shell ./config.sh --enabled WITH_ARM_NEON)" "Y" + override CFLAGS += -mfpu=neon + endif endif # The linker for powerpc have bug that prevents --gc-sections from working @@ -106,6 +117,7 @@ DEFAULT_SU980_LIB = -lentropic -lrt DEFAULT_AZBOX_LIB = -Lextapi/openxcas -lOpenXCASAPI DEFAULT_LIBCRYPTO_LIB = -lcrypto DEFAULT_SSL_LIB = -lssl +DEFAULT_LIBDVBCSA_LIB = -ldvbcsa ifeq ($(uname_S),Linux) DEFAULT_LIBUSB_LIB = -lusb-1.0 -lrt else @@ -117,15 +129,13 @@ ifeq ($(uname_S),FreeBSD) DEFAULT_LIBUSB_LIB = -lusb endif ifeq ($(uname_S),Darwin) - FIX_OPENSSL_FLAGS_DIR := $(shell ln -sf /usr/local/opt/openssl@1.1/include/openssl /usr/local/include) - FIX_OPENSSL_LIB_DIR := $(shell ln -sf /usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib /usr/local/lib) - FIX_OPENSSL_LIBCRYPTO_DIR := $(shell ln -sf /usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib /usr/local/lib) - DEFAULT_LIBCRYPTO_LIB = -L/usr/local/opt/openssl@1.1/lib -lcrypto - DEFAULT_SSL_LIB = -L/usr/local/opt/openssl@1.1/lib -lssl + DEFAULT_SSL_LIB = -L/usr/local/opt/openssl/lib -lssl + DEFAULT_LIBCRYPTO_LIB = -L/usr/local/opt/openssl/lib -lcrypto + DEFAULT_LIBDVBCSA_LIB = -L/usr/local/opt/libdvbcsa/lib -ldvbcsa DEFAULT_LIBUSB_FLAGS = -I/usr/local/opt/libusb/include - DEFAULT_LIBUSB_LIB = -L/usr/local/opt/libusb/lib -lusb-1.0 -framework IOKit -framework CoreFoundation -framework Security + DEFAULT_LIBUSB_LIB = -L/usr/local/opt/libusb/lib -lusb-1.0 -lobjc -framework IOKit -framework CoreFoundation -framework Security DEFAULT_PCSC_FLAGS = -I/usr/local/opt/pcsc-lite/include/PCSC - DEFAULT_PCSC_LIB = -L/usr/local/opt/pcsc-lite/lib -framework IOKit -framework CoreFoundation -framework PCSC + DEFAULT_PCSC_LIB = -L/usr/local/opt/pcsc-lite/lib -lpcsclite -lobjc -framework IOKit -framework CoreFoundation -framework PCSC else # Get the compiler's last include PATHs. Basicaly it is /usr/include # but in case of cross compilation it might be something else. @@ -180,6 +190,7 @@ $(eval $(call prepare_use_flags,SSL,ssl)) $(eval $(call prepare_use_flags,LIBCRYPTO,)) $(eval $(call prepare_use_flags,LIBUSB,libusb)) $(eval $(call prepare_use_flags,PCSC,pcsc)) +$(eval $(call prepare_use_flags,LIBDVBCSA,libdvbcsa)) $(eval $(call prepare_use_flags,UTF8)) # Add PLUS_TARGET and EXTRA_TARGET to TARGET @@ -285,7 +296,6 @@ 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-streamserver.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 @@ -293,7 +303,6 @@ SRC-$(CONFIG_WITH_EMU) += module-emulator-irdeto.c SRC-$(CONFIG_WITH_EMU) += module-emulator-nagravision.c SRC-$(CONFIG_WITH_EMU) += module-emulator-powervu.c SRC-$(CONFIG_WITH_EMU) += module-emulator-viaccess.c -SRC-$(CONFIG_WITH_EMU) += ffdecsa/ffdecsa.c ifeq "$(CONFIG_WITH_EMU)" "y" ifeq "$(CONFIG_WITH_SOFTCAM)" "y" UNAME := $(shell uname -s) @@ -325,7 +334,6 @@ SRC-$(CONFIG_MODULE_GBOX) += module-gbox-sms.c SRC-$(CONFIG_MODULE_GBOX) += module-gbox-remm.c SRC-$(CONFIG_MODULE_GBOX) += module-gbox-cards.c SRC-$(CONFIG_MODULE_GBOX) += module-gbox.c -SRC-$(CONFIG_IRDETO_GUESSING) += module-ird-guess.c SRC-$(CONFIG_LCDSUPPORT) += module-lcd.c SRC-$(CONFIG_LEDSUPPORT) += module-led.c SRC-$(CONFIG_MODULE_MONITOR) += module-monitor.c @@ -336,6 +344,7 @@ SRC-$(CONFIG_MODULE_GHTTP) += module-ghttp.c SRC-$(CONFIG_MODULE_RADEGAST) += module-radegast.c SRC-$(CONFIG_MODULE_SCAM) += module-scam.c SRC-$(CONFIG_MODULE_SERIAL) += module-serial.c +SRC-$(CONFIG_MODULE_STREAMRELAY) += module-streamrelay.c SRC-$(CONFIG_WITH_LB) += module-stat.c SRC-$(CONFIG_WEBIF) += module-webif-lib.c SRC-$(CONFIG_WEBIF) += module-webif-tpl.c @@ -407,7 +416,7 @@ SRC := $(subst config.c,$(OBJDIR)/config.c,$(SRC)) # starts the compilation. all: @./config.sh --use-flags "$(USE_FLAGS)" --objdir "$(OBJDIR)" --make-config.mak - @-mkdir -p $(OBJDIR)/cscrypt $(OBJDIR)/csctapi $(OBJDIR)/minilzo $(OBJDIR)/ffdecsa $(OBJDIR)/webif + @-mkdir -p $(OBJDIR)/cscrypt $(OBJDIR)/csctapi $(OBJDIR)/minilzo $(OBJDIR)/webif @-printf "\ +-------------------------------------------------------------------------------\n\ | OSCam ver: $(VER) rev: $(SVN_REV) target: $(TARGET)\n\ @@ -454,7 +463,7 @@ $(OBJDIR)/config.o: $(OBJDIR)/config.c $(Q)$(CC) $(STD_DEFS) $(CC_OPTS) $(CC_WARN) $(CFLAGS) -c $< -o $@ $(OBJDIR)/%.o: %.c Makefile - @$(CC) -MP -MM -MT $@ -o $(subst .o,.d,$@) $< + @$(CC) $(CFLAGS) -MP -MM -MT $@ -o $(subst .o,.d,$@) $< $(SAY) "CC $<" $(Q)$(CC) $(STD_DEFS) $(CC_OPTS) $(CC_WARN) $(CFLAGS) -c $< -o $@ @@ -697,6 +706,13 @@ OSCam build system documentation\n\ SSL_LDFLAGS='$(DEFAULT_SSL_FLAGS)'\n\ SSL_LIB='$(DEFAULT_SSL_LIB)'\n\ Using USE_SSL=1 adds to '-ssl' to PLUS_TARGET.\n\ +\n\ + USE_LIBDVBCSA=1 - Request linking with libdvbcsa. USE_LIBDVBCSA is automatically\n\ + The variables that control USE_LIBDVBCSA=1 build are:\n\ + LIBDVBCSA_FLAGS='$(DEFAULT_LIBDVBCSA_FLAGS)'\n\ + LIBDVBCSA_CFLAGS='$(DEFAULT_LIBDVBCSA_FLAGS)'\n\ + LIBDVBCSA_LDFLAGS='$(DEFAULT_LIBDVBCSA_FLAGS)'\n\ + LIBDVBCSA_LIB='$(DEFAULT_LIBDVBCSA_LIB)'\n\ \n\ USE_UTF8=1 - Request UTF-8 enabled webif by default.\n\ \n\ @@ -815,6 +831,8 @@ OSCam build system documentation\n\ make USE_LIBCRYPTO=1 LIBCRYPTO_LIB=\"/usr/lib/libcrypto.a\"\n\n\ Build OSCam with static libssl and libcrypto:\n\ make USE_SSL=1 SSL_LIB=\"/usr/lib/libssl.a\" LIBCRYPTO_LIB=\"/usr/lib/libcrypto.a\"\n\n\ + Build OSCam with static libdvbcsa:\n\ + make USE_LIBDVBCSA=1 LIBDVBCSA_LIB=\"/usr/lib/libdvbcsa.a\"\n\n\ Build with verbose messages and size optimizations:\n\ make V=1 CC_OPTS=-Os\n\n\ Build and set oscam file name:\n\ diff --git a/README b/README index ba2dea4fd..a2af8b975 100644 --- a/README +++ b/README @@ -6,7 +6,7 @@ License ======= OSCam: Open Source CAM -Copyright (C) 2009-2020 OSCam developers +Copyright (C) 2009-2024 OSCam developers OSCam is based on the Streamboard mp-cardserver 0.9d by dukat and has been extended and worked on by many more since then. diff --git a/config.h b/config.h index 8995af7a3..833378383 100644 --- a/config.h +++ b/config.h @@ -10,11 +10,11 @@ //#define WITH_SSL 1 #if defined(__linux__) || defined(__CYGWIN__) #define HAVE_DVBAPI 1 +//#define WITH_EXTENDED_CW 1 #endif //#define WITH_NEUTRINO 1 #define READ_SDT_CHARSETS 1 //#define CLOCKFIX 1 -#define IRDETO_GUESSING 1 //#define CS_ANTICASC 1 #define WITH_DEBUG 1 #define WITH_LB 1 @@ -24,6 +24,7 @@ //#define LCDSUPPORT 1 //#define LEDSUPPORT 1 //#define IPV6SUPPORT 1 +//#define WITH_ARM_NEON 1 #define MODULE_MONITOR 1 //#define MODULE_CAMD33 1 @@ -38,7 +39,8 @@ //#define MODULE_CONSTCW 1 //#define MODULE_PANDORA 1 //#define MODULE_GHTTP 1 -#define MODULE_SCAM 1 +//#define MODULE_SCAM 1 +//#define MODULE_STREAMRELAY 1 #define WITH_CARDREADER 1 @@ -64,7 +66,7 @@ //#define CARDREADER_SC8IN1 1 //#define CARDREADER_SMARGO 1 //#define CARDREADER_DB2COM 1 -#define CARDREADER_STINGER 1 +//#define CARDREADER_STINGER 1 //#define CARDREADER_DRECAS 1 #ifdef WITH_PCSC diff --git a/config.sh b/config.sh index fd5bb0f35..582c94032 100755 --- a/config.sh +++ b/config.sh @@ -1,7 +1,7 @@ #!/bin/sh -addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY TOUCH WITH_SSL HAVE_DVBAPI WITH_NEUTRINO READ_SDT_CHARSETS IRDETO_GUESSING CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT 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" +addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY TOUCH 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" @@ -12,9 +12,9 @@ CONFIG_WEBIF_JQUERY=y # CONFIG_TOUCH=n # CONFIG_WITH_SSL=n CONFIG_HAVE_DVBAPI=y +# CONFIG_WITH_EXTENDED_CW=n # CONFIG_WITH_NEUTRINO=n CONFIG_READ_SDT_CHARSETS=y -CONFIG_IRDETO_GUESSING=y # CONFIG_CS_ANTICASC=n CONFIG_WITH_DEBUG=y CONFIG_MODULE_MONITOR=y @@ -26,6 +26,7 @@ CONFIG_WITH_LB=y # CONFIG_LEDSUPPORT=n # CONFIG_CLOCKFIX=n # CONFIG_IPV6SUPPORT=n +# CONFIG_WITH_ARM_NEON=n CONFIG_WITH_EMU=y CONFIG_WITH_SOFTCAM=y # CONFIG_MODULE_CAMD33=n @@ -39,8 +40,9 @@ CONFIG_MODULE_GBOX=y # CONFIG_MODULE_SERIAL=n # CONFIG_MODULE_CONSTCW=n # CONFIG_MODULE_PANDORA=n -CONFIG_MODULE_SCAM=y +# CONFIG_MODULE_SCAM=n # CONFIG_MODULE_GHTTP=n +# CONFIG_MODULE_STREAMRELAY=n CONFIG_WITH_CARDREADER=y CONFIG_READER_NAGRA_COMMON=y CONFIG_READER_NAGRA=y @@ -65,7 +67,7 @@ CARDREADER_INTERNAL=y # CARDREADER_DB2COM=n # CARDREADER_STAPI=n # CARDREADER_STAPI5=n -CARDREADER_STINGER=y +# CARDREADER_STINGER=n " usage() { @@ -459,20 +461,21 @@ menu_addons() { TOUCH "Touch Web Interface" $(check_test "TOUCH") \ WITH_SSL "OpenSSL support" $(check_test "WITH_SSL") \ HAVE_DVBAPI "DVB API" $(check_test "HAVE_DVBAPI") \ + WITH_EXTENDED_CW "DVB API EXTENDED CW API" $(check_test "WITH_EXTENDED_CW") \ WITH_NEUTRINO "Neutrino support" $(check_test "WITH_NEUTRINO") \ READ_SDT_CHARSETS "DVB API read-sdt charsets" $(check_test "READ_SDT_CHARSETS") \ - IRDETO_GUESSING "Irdeto guessing" $(check_test "IRDETO_GUESSING") \ CS_ANTICASC "Anti cascading" $(check_test "CS_ANTICASC") \ WITH_DEBUG "Debug messages" $(check_test "WITH_DEBUG") \ MODULE_MONITOR "Monitor" $(check_test "MODULE_MONITOR") \ WITH_LB "Loadbalancing" $(check_test "WITH_LB") \ CS_CACHEEX "Cache exchange" $(check_test "CS_CACHEEX") \ - CS_CACHEEX_AIO "Cache exchange aio (depend on Cache exchange)" $(check_test "CS_CACHEEX_AIO") \ + CS_CACHEEX_AIO "Cache exchange aio (depend on Cache exchange)" $(check_test "CS_CACHEEX_AIO") \ CW_CYCLE_CHECK "CW Cycle Check" $(check_test "CW_CYCLE_CHECK") \ LCDSUPPORT "LCD support" $(check_test "LCDSUPPORT") \ LEDSUPPORT "LED support" $(check_test "LEDSUPPORT") \ CLOCKFIX "Clockfix (disable on old systems!)" $(check_test "CLOCKFIX") \ IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \ + WITH_ARM_NEON "ARM NEON (SIMD/MPE) support" $(check_test "WITH_ARM_NEON") \ WITH_EMU "Emulator support" $(check_test "WITH_EMU") \ WITH_SOFTCAM "Built-in SoftCam.Key" $(check_test "WITH_SOFTCAM") \ 2> ${tempfile} @@ -499,6 +502,7 @@ menu_protocols() { MODULE_PANDORA "Pandora" $(check_test "MODULE_PANDORA") \ MODULE_GHTTP "Ghttp" $(check_test "MODULE_GHTTP") \ MODULE_SCAM "scam" $(check_test "MODULE_SCAM") \ + MODULE_STREAMRELAY "Stream Relay" $(check_test "MODULE_STREAMRELAY") \ 2> ${tempfile} opt=${?} diff --git a/cscrypt/bn_exp.c b/cscrypt/bn_exp.c index ec1d43f98..6c5d58518 100644 --- a/cscrypt/bn_exp.c +++ b/cscrypt/bn_exp.c @@ -268,4 +268,262 @@ int BN_mod_exp_simple(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, return (ret); } +int +BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) +{ + /* like BN_mod, but returns non-negative remainder + * (i.e., 0 <= r < |d| always holds) + */ + + if (!(BN_mod(r, m, d, ctx))) + return 0; + if (!r->neg) + return 1; + /* now -|d| < r < 0, so we have to set r : = r + |d| */ + return (d->neg ? BN_sub : BN_add)(r, r, d); +} + +/* solves ax == 1 (mod n) */ +BIGNUM * +BN_mod_inverse(BIGNUM *ret, BIGNUM *a, const BIGNUM *n, BN_CTX *ctx) +{ + BIGNUM *A, *B, *X, *Y, *M, *D, *T = NULL; + int sign; + + bn_check_top(a); + bn_check_top(n); + + BN_CTX_start(ctx); + A = BN_CTX_get(ctx); + B = BN_CTX_get(ctx); + X = BN_CTX_get(ctx); + D = BN_CTX_get(ctx); + M = BN_CTX_get(ctx); + Y = BN_CTX_get(ctx); + T = BN_CTX_get(ctx); + if (T == NULL) goto err; + + if (ret == NULL) goto err; + + BN_one(X); + BN_zero(Y); + if (BN_copy(B, a) == NULL) goto err; + if (BN_copy(A, n) == NULL) goto err; + A->neg = 0; + if (B->neg || (BN_ucmp(B, A) >= 0)) { + if (!BN_nnmod(B, B, A, ctx)) goto err; + } + sign = -1; + /* From B = a mod |n|, A = |n| it follows that + * + * 0 <= B < A, + * -sign*X*a == B (mod |n|), + * sign*Y*a == A (mod |n|). + */ + + if (BN_is_odd(n) && (BN_num_bits(n) <= (BN_BITS <= 32 ? 450 : 2048))) { + /* Binary inversion algorithm; requires odd modulus. + * This is faster than the general algorithm if the modulus + * is sufficiently small (about 400 .. 500 bits on 32-bit + * sytems, but much more on 64-bit systems) + */ + int shift; + + while (!BN_is_zero(B)) { + /* + * 0 < B < |n|, + * 0 < A <= |n|, + * (1) -sign*X*a == B (mod |n|), + * (2) sign*Y*a == A (mod |n|) + */ + + /* Now divide B by the maximum possible power of two in the integers, + * and divide X by the same value mod |n|. + * When we're done, (1) still holds. + */ + shift = 0; + while (!BN_is_bit_set(B, shift)) /* note that 0 < B */ { + shift++; + + if (BN_is_odd(X)) { + if (!BN_uadd(X, X, n)) goto err; + } + /* now X is even, so we can easily divide it by two */ + if (!BN_rshift1(X, X)) goto err; + } + if (shift > 0) { + if (!BN_rshift(B, B, shift)) goto err; + } + + + /* Same for A and Y. Afterwards, (2) still holds. */ + shift = 0; + while (!BN_is_bit_set(A, shift)) /* note that 0 < A */ { + shift++; + + if (BN_is_odd(Y)) { + if (!BN_uadd(Y, Y, n)) goto err; + } + /* now Y is even */ + if (!BN_rshift1(Y, Y)) goto err; + } + if (shift > 0) { + if (!BN_rshift(A, A, shift)) goto err; + } + + /* We still have (1) and (2). + * Both A and B are odd. + * The following computations ensure that + * + * 0 <= B < |n|, + * 0 < A < |n|, + * (1) -sign*X*a == B (mod |n|), + * (2) sign*Y*a == A (mod |n|), + * + * and that either A or B is even in the next iteration. + */ + if (BN_ucmp(B, A) >= 0) { + /* -sign*(X + Y)*a == B - A (mod |n|) */ + if (!BN_uadd(X, X, Y)) goto err; + /* NB: we could use BN_mod_add_quick(X, X, Y, n), but that + * actually makes the algorithm slower + */ + if (!BN_usub(B, B, A)) goto err; + } else { + /* sign*(X + Y)*a == A - B (mod |n|) */ + if (!BN_uadd(Y, Y, X)) goto err; + /* as above, BN_mod_add_quick(Y, Y, X, n) would slow things down */ + if (!BN_usub(A, A, B)) goto err; + } + } + } else { + /* general inversion algorithm */ + + while (!BN_is_zero(B)) { + BIGNUM *tmp; + + /* + * 0 < B < A, + * (*) -sign*X*a == B (mod |n|), + * sign*Y*a == A (mod |n|) + */ + + /* (D, M) : = (A/B, A%B) ... */ + if (BN_num_bits(A) == BN_num_bits(B)) { + if (!BN_one(D)) goto err; + if (!BN_sub(M, A, B)) goto err; + } else if (BN_num_bits(A) == BN_num_bits(B) + 1) { + /* A/B is 1, 2, or 3 */ + if (!BN_lshift1(T, B)) goto err; + if (BN_ucmp(A, T) < 0) { + /* A < 2*B, so D = 1 */ + if (!BN_one(D)) goto err; + if (!BN_sub(M, A, B)) goto err; + } else { + /* A >= 2*B, so D = 2 or D = 3 */ + if (!BN_sub(M, A, T)) goto err; + if (!BN_add(D, T, B)) goto err; + /* use D ( := 3 * B) as temp */ + if (BN_ucmp(A, D) < 0) { + /* A < 3*B, so D = 2 */ + if (!BN_set_word(D, 2)) goto err; + /* M ( = A - 2*B) already has the correct value */ + } else { + /* only D = 3 remains */ + if (!BN_set_word(D, 3)) goto err; + /* currently M = A - 2 * B, + * but we need M = A - 3 * B + */ + if (!BN_sub(M, M, B)) goto err; + } + } + } else { + if (!BN_div(D, M, A, B, ctx)) goto err; + } + + /* Now + * A = D*B + M; + * thus we have + * (**) sign*Y*a == D*B + M (mod |n|). + */ + + tmp = A; /* keep the BIGNUM object, the value does not matter */ + + /* (A, B) : = (B, A mod B) ... */ + A = B; + B = M; + /* ... so we have 0 <= B < A again */ + + /* Since the former M is now B and the former B is now A, + * (**) translates into + * sign*Y*a == D*A + B (mod |n|), + * i.e. + * sign*Y*a - D*A == B (mod |n|). + * Similarly, (*) translates into + * -sign*X*a == A (mod |n|). + * + * Thus, + * sign*Y*a + D*sign*X*a == B (mod |n|), + * i.e. + * sign*(Y + D*X)*a == B (mod |n|). + * + * So if we set (X, Y, sign) : = (Y + D*X, X, -sign), we arrive back at + * -sign*X*a == B (mod |n|), + * sign*Y*a == A (mod |n|). + * Note that X and Y stay non-negative all the time. + */ + + /* most of the time D is very small, so we can optimize tmp : = D*X+Y */ + if (BN_is_one(D)) { + if (!BN_add(tmp, X, Y)) goto err; + } else { + if (BN_is_word(D, 2)) { + if (!BN_lshift1(tmp, X)) goto err; + } else if (BN_is_word(D, 4)) { + if (!BN_lshift(tmp, X, 2)) goto err; + } else if (D->top == 1) { + if (!BN_copy(tmp, X)) goto err; + if (!BN_mul_word(tmp, D->d[0])) goto err; + } else { + if (!BN_mul(tmp, D, X, ctx)) goto err; + } + if (!BN_add(tmp, tmp, Y)) goto err; + } + + M = Y; /* keep the BIGNUM object, the value does not matter */ + Y = X; + X = tmp; + sign = -sign; + } + } + + /* + * The while loop (Euclid's algorithm) ends when + * A == gcd(a, n); + * we have + * sign*Y*a == A (mod |n|), + * where Y is non-negative. + */ + + if (sign < 0) { + if (!BN_sub(Y, n, Y)) goto err; + } + /* Now Y*a == A (mod |n|). */ + + + if (BN_is_one(A)) { + /* Y*a == 1 (mod |n|) */ + if (!Y->neg && BN_ucmp(Y, n) < 0) { + if (!BN_copy(ret, Y)) goto err; + } else { + if (!BN_nnmod(ret, Y, n, ctx)) goto err; + } + } else { + goto err; + } +err: + BN_CTX_end(ctx); + return (ret); +} + #endif diff --git a/cscrypt/mdc2.c b/cscrypt/mdc2.c index 269a8fbc2..d889e8ab6 100644 --- a/cscrypt/mdc2.c +++ b/cscrypt/mdc2.c @@ -1,35 +1,31 @@ #include "../globals.h" #include "mdc2.h" -/* To everybody who want to fix this external openssl dependency bug - * read this starting at page 5 https://board.streamboard.tv/forum/thread/47678-oscam-bug-report/?pageNo=5 - * Don't touch this if you have no real fix. Right now resolution is can't fix - */ #undef c2l #define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ - l|=((DES_LONG)(*((c)++)))<< 8L, \ - l|=((DES_LONG)(*((c)++)))<<16L, \ - l|=((DES_LONG)(*((c)++)))<<24L) + l|=((DES_LONG)(*((c)++)))<< 8L, \ + l|=((DES_LONG)(*((c)++)))<<16L, \ + l|=((DES_LONG)(*((c)++)))<<24L) #undef l2c #define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ - *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ - *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ - *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) # define FP(l,r) \ - { \ - register DES_LONG tt; \ - PERM_OP(l,r,tt, 1,0x55555555L); \ - PERM_OP(r,l,tt, 8,0x00ff00ffL); \ - PERM_OP(l,r,tt, 2,0x33333333L); \ - PERM_OP(r,l,tt,16,0x0000ffffL); \ - PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ - } - -//OPENSSL_GLOBAL const DES_LONG DES_SPtrans[8][64] = -const DES_LONG DES_SPtrans_Oscam[8][64] = + { \ + register DES_LONG tt; \ + PERM_OP(l,r,tt, 1,0x55555555L); \ + PERM_OP(r,l,tt, 8,0x00ff00ffL); \ + PERM_OP(l,r,tt, 2,0x33333333L); \ + PERM_OP(r,l,tt,16,0x0000ffffL); \ + PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ + } + +#if !defined(WITH_SSL) || !defined(WITH_LIBCRYPTO) +const DES_LONG DES_SPtrans[8][64] = { { /* nibble 0 */ @@ -184,42 +180,42 @@ const DES_LONG DES_SPtrans_Oscam[8][64] = 0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L, } }; +#endif - -# define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g) -# define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ - u=R^s[S ]; \ - t=R^s[S+1] - -# define D_ENCRYPT(LL,R,S) { \ - LOAD_DATA_tmp(R,S,u,t,E0,E1); \ - t=ROTATE(t,4); \ - LL^= \ - DES_SPtrans_Oscam[0][(u>> 2L)&0x3f]^ \ - DES_SPtrans_Oscam[2][(u>>10L)&0x3f]^ \ - DES_SPtrans_Oscam[4][(u>>18L)&0x3f]^ \ - DES_SPtrans_Oscam[6][(u>>26L)&0x3f]^ \ - DES_SPtrans_Oscam[1][(t>> 2L)&0x3f]^ \ - DES_SPtrans_Oscam[3][(t>>10L)&0x3f]^ \ - DES_SPtrans_Oscam[5][(t>>18L)&0x3f]^ \ - DES_SPtrans_Oscam[7][(t>>26L)&0x3f]; } +#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g) +#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ + u=R^s[S ]; \ + t=R^s[S+1] + +#define D_ENCRYPT(LL,R,S) { \ + LOAD_DATA_tmp(R,S,u,t,E0,E1); \ + t=ROTATE(t,4); \ + LL^= \ + DES_SPtrans[0][(u>> 2L)&0x3f]^ \ + DES_SPtrans[2][(u>>10L)&0x3f]^ \ + DES_SPtrans[4][(u>>18L)&0x3f]^ \ + DES_SPtrans[6][(u>>26L)&0x3f]^ \ + DES_SPtrans[1][(t>> 2L)&0x3f]^ \ + DES_SPtrans[3][(t>>10L)&0x3f]^ \ + DES_SPtrans[5][(t>>18L)&0x3f]^ \ + DES_SPtrans[7][(t>>26L)&0x3f]; } #define IP(l,r) \ - { \ - register DES_LONG tt; \ - PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ - PERM_OP(l,r,tt,16,0x0000ffffL); \ - PERM_OP(r,l,tt, 2,0x33333333L); \ - PERM_OP(l,r,tt, 8,0x00ff00ffL); \ - PERM_OP(r,l,tt, 1,0x55555555L); \ - } + { \ + register DES_LONG tt; \ + PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ + PERM_OP(l,r,tt,16,0x0000ffffL); \ + PERM_OP(r,l,tt, 2,0x33333333L); \ + PERM_OP(l,r,tt, 8,0x00ff00ffL); \ + PERM_OP(r,l,tt, 1,0x55555555L); \ + } -# define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ - (b)^=(t),\ - (a)^=((t)<<(n))) +#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) -# define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) +#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) @@ -254,7 +250,7 @@ static const unsigned char odd_parity[256] = }; #define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ - (a)=(a)^(t)^(t>>(16-(n)))) + (a)=(a)^(t)^(t>>(16-(n)))) # define ITERATIONS 16 # define HALF_ITERATIONS 8 @@ -494,7 +490,8 @@ void DES_set_odd_parity(DES_cblock *key) } -void DES_encrypt1_Oscam(DES_LONG *data, DES_key_schedule *ks, int enc) +#if !defined(WITH_SSL) || !defined(WITH_LIBCRYPTO) +void DES_encrypt1(DES_LONG *data, DES_key_schedule *ks, int enc) { register DES_LONG l=0, r=0, t=0, u=0; //l = r = t = u = 0; @@ -569,7 +566,7 @@ void DES_encrypt1_Oscam(DES_LONG *data, DES_key_schedule *ks, int enc) data[0] = l; data[1] = r; } - +#endif static void mdc2_body(MDC2_CTX *c, const unsigned char *in, size_t len); @@ -639,11 +636,11 @@ static void mdc2_body(MDC2_CTX *c, const unsigned char *in, size_t len) DES_set_odd_parity(&c->h); DES_set_key_unchecked(&c->h, &k); - DES_encrypt1_Oscam(d, &k, 1); + DES_encrypt1(d, &k, 1); DES_set_odd_parity(&c->hh); DES_set_key_unchecked(&c->hh, &k); - DES_encrypt1_Oscam(dd, &k, 1); + DES_encrypt1(dd, &k, 1); ttin0 = tin0 ^ dd[0]; ttin1 = tin1 ^ dd[1]; diff --git a/cscrypt/mdc2.h b/cscrypt/mdc2.h index 8476b376d..e57c3ed35 100644 --- a/cscrypt/mdc2.h +++ b/cscrypt/mdc2.h @@ -59,12 +59,20 @@ #ifndef HEADER_MDC2_H #define HEADER_MDC2_H -/* To everybody who want to fix this external openssl dependency bug - * read this starting at page 5 https://board.streamboard.tv/forum/thread/47678-oscam-bug-report/?pageNo=5 - * Don't touch this if you have no real fix. Right now resolution is can't fix - */ - +#if defined(WITH_SSL) || defined(WITH_LIBCRYPTO) #include +#else +#define DES_KEY_SZ (sizeof(DES_cblock)) +typedef unsigned int DES_LONG; +typedef unsigned char DES_cblock[8]; +typedef unsigned char const_DES_cblock[8]; +typedef struct DES_ks { + union { + DES_cblock cblock; + DES_LONG deslong[2]; + } ks[16]; +} DES_key_schedule; +#endif #ifdef __cplusplus extern "C" { diff --git a/csctapi/icc_async.c b/csctapi/icc_async.c index 34d6c83b3..c4a7e9e9a 100644 --- a/csctapi/icc_async.c +++ b/csctapi/icc_async.c @@ -54,6 +54,8 @@ static void calculate_cak7_vars(struct s_reader *reader, const ATR *atr) mbedtls_sha256_free(&ctx_sha256); memcpy(reader->cak7_aes_key,aes_key,32); memcpy(reader->cak7_aes_iv,aes_iv,16); + char tmp[128]; + rdr_log(reader, "Initial AES: %s", cs_hexdump(1, reader->cak7_aes_key + 16, 16, tmp, sizeof(tmp))); } void calculate_cak7_cmd(struct s_reader *reader, uint8_t *cmdin,uint8_t cmdlen,uint8_t *cmdout) @@ -74,18 +76,101 @@ void do_cak7_cmd(struct s_reader *reader,unsigned char *cta_res, uint16_t *p_cta // head req[0]=0x80; req[1]=0xCA; - // len - req[4]=inlen; + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + req[4]=inlen + 1; + } + else + { + req[4]=inlen; + } req[sizeof(req)-1]=resplen; data[4]=(reader->cak7_seq>>16)&0xFF; data[5]=(reader->cak7_seq>>8)&0xFF; data[6]=(reader->cak7_seq)&0xFF; + rdr_log_dump_dbg(reader, D_READER, data, inlen, "preparing data for writing to cardreader"); calculate_cak7_cmd(reader,data,inlen,&req[5]); + rdr_log_dump_dbg(reader, D_READER, req, sizeof(req), "write to cardreader"); if(!ICC_Async_CardWrite(reader, req, sizeof(req), cta_res, p_cta_lr)) { - AesCtx ctx; - AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); - AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + if(cta_res[*p_cta_lr - 2] == 0x61) + { + uint8_t resp[] = {0x00,0xC0,0x00,0x00,0x00}; + memcpy(resp + 4,&cta_res[*p_cta_lr - 1],1); + if(!ICC_Async_CardWrite(reader, resp, sizeof(resp), cta_res, p_cta_lr)) + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + else + { + *p_cta_lr=0; + } + } + else if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card answered 6F01 - trying one more time"); + if(!ICC_Async_CardWrite(reader, req, sizeof(req), cta_res, p_cta_lr)) + { + if(cta_res[*p_cta_lr - 2] == 0x61) + { + uint8_t resp[] = {0x00,0xC0,0x00,0x00,0x00}; + memcpy(resp + 4,&cta_res[*p_cta_lr - 1],1); + if(!ICC_Async_CardWrite(reader, resp, sizeof(resp), cta_res, p_cta_lr)) + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + else + { + *p_cta_lr=0; + } + } + else if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card needs reinit"); + } + } + else + { + *p_cta_lr=0; + } + } + } + else + { + if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card answered 6F01 - trying one more time"); + if(!ICC_Async_CardWrite(reader, req, sizeof(req), cta_res, p_cta_lr)) + { + if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card needs reinit"); + } + else + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + } + else + { + *p_cta_lr=0; + } + } + else + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + } } else { @@ -245,29 +330,46 @@ int32_t ICC_Async_Activate(struct s_reader *reader, ATR *atr, uint16_t deprecate return ERROR; } + reader->cak7type = 0; #ifdef READER_NAGRA_MERLIN - bool need_nagra_layer_switch = false; - bool is_cak7 = false; - - static const uint8_t hd03atr [] = {0x3F,0xFF,0x95,0x00,0xFF,0x91,0x81,0x71,0xA0,0x47,0x00,0x44,0x4E,0x41,0x53,0x50,0x31,0x39,0x30,0x20,0x4D,0x65,0x72,0x51,0x32,0x35,0x4F}; //HD03, HD03A (CAK6.3 Mode) - static const uint8_t hd03atr2[] = {0x3F,0xFF,0x95,0x00,0xFF,0x91,0x81,0x71,0xFE,0x57,0x00,0x44,0x4E,0x41,0x53,0x50,0x34,0x31,0x30,0x20,0x52,0x65,0x76,0x51,0x32,0x35,0x17}; //HD03, HD03A (CAK7 Mode) - static const uint8_t hd04atr [] = {0x3F,0xFF,0x95,0x00,0xFF,0x91,0x81,0x71,0xFE,0x57,0x00,0x44,0x4E,0x41,0x53,0x50,0x34,0x32,0x30,0x20,0x52,0x65,0x76,0x53,0x36,0x30,0x17}; //HD04, HD04A, HD04B (CAK7 only) - static const uint8_t hd04hatr[] = {0x3F,0xFF,0x95,0x00,0xFF,0x91,0x81,0x71,0xFE,0x57,0x00,0x44,0x4E,0x41,0x53,0x50,0x34,0x32,0x30,0x20,0x52,0x65,0x76,0x53,0x36,0x34,0x13}; //HD04H (CAK7 only) - static const uint8_t hd05atr [] = {0x3F,0xFF,0x95,0x00,0xFF,0x91,0x81,0x71,0xFE,0x57,0x00,0x44,0x4E,0x41,0x53,0x50,0x34,0x35,0x30,0x20,0x52,0x65,0x76,0x57,0x36,0x30,0x14}; //HD05, HD05A (CAK7 only) ATR_GetRaw(atr, atrarr, &atr_size); - if(!memcmp(hd03atr, atrarr, atr_size)) need_nagra_layer_switch = true; - if(!memcmp(hd03atr2, atrarr, atr_size) || !memcmp(hd04atr, atrarr, atr_size) || !memcmp(hd04hatr, atrarr, atr_size) || !memcmp(hd05atr, atrarr, atr_size)) is_cak7 = true; - if(is_cak7) + if((memcmp(atrarr + 8, "DNASP40", 7) == 0) || (memcmp(atrarr + 11, "DNASP41", 7) == 0) || (memcmp(atrarr + 11, "DNASP48", 7) == 0)) + { + rdr_log(reader, "card needs reset before init"); + memset(atr, 0, 1); + call(crdr_ops->activate(reader, atr)); //try to read the atr of this layer + ATR_GetRaw(atr, atrarr, &atr_size); + rdr_log(reader,"ATR: %s", cs_hexdump(1, atrarr, atr_size, tmp, sizeof(tmp))); + // Parse_ATR and InitCard need to be included in lock because they change parity of serial port + if(crdr_ops->lock) + { + crdr_ops->lock(reader); + } + int32_t ret1 = Parse_ATR(reader, atr, deprecated); + if(crdr_ops->unlock) + { + crdr_ops->unlock(reader); + } + if(ret1) + { + rdr_log(reader, "ERROR: Parse_ATR returned error"); + return ERROR; + } + } + + if((memcmp(atrarr + 8, "DNASP4", 6) == 0) || (memcmp(atrarr + 11, "DNASP4", 6) == 0)) { - rdr_log(reader, "detected nagra merlin card in CAK7 mode"); + rdr_log(reader, "detected card in CAK7 mode"); calculate_cak7_vars(reader, atr); + reader->cak7type = 1; } - else if(need_nagra_layer_switch) + else if(((memcmp(atrarr + 7, "pp", 2) == 0 && ((atrarr[9]&0x0F) >= 10)) || (memcmp(atrarr + 11, "DNASP18", 7) == 0) || (memcmp(atrarr + 11, "DNASP19", 7) == 0) || (memcmp(atrarr + 11, "DNASP1A", 7) == 0)) && reader->cak7_mode) { - rdr_log(reader, "detected nagra merlin card in legacy mode -> try switch nagra layer to CAK7"); - uint8_t changerom_handshake[] = { 0x80, 0xCA, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }; + rdr_log(reader, "detected card in CAK6/Seca mode -> try switch to Nagra CAK7"); + uint8_t changerom_handshake[22]; + memset(changerom_handshake, 0x00, 22); calculate_changerom_cmd(reader, atr, &changerom_handshake[5]); memset(reader->rom, 0, 15); @@ -275,6 +377,8 @@ int32_t ICC_Async_Activate(struct s_reader *reader, ATR *atr, uint16_t deprecate memset(cta_res, 0, CTA_RES_LEN); uint16_t cta_lr; + changerom_handshake[0] = 0x80; + changerom_handshake[1] = 0xCA; changerom_handshake[4] = 0x11; // 0x11: length of data we will send uint8_t cta_res1_ok = 0x61; uint8_t cta_res2_ok = 0x10; @@ -287,6 +391,9 @@ int32_t ICC_Async_Activate(struct s_reader *reader, ATR *atr, uint16_t deprecate cta_res2_ok = 0x00; } + changerom_handshake[21] = 0x10; + + reader->cak7type = 1; if(!ICC_Async_CardWrite(reader, changerom_handshake, sizeof(changerom_handshake), cta_res, &cta_lr)) { if(cta_res[cta_lr-2] == cta_res1_ok && cta_res[cta_lr-1] == cta_res2_ok) @@ -295,17 +402,31 @@ int32_t ICC_Async_Activate(struct s_reader *reader, ATR *atr, uint16_t deprecate memset(atr, 0, 1); call(crdr_ops->activate(reader, atr)); //try to read the atr of this layer ATR_GetRaw(atr, atrarr, &atr_size); - rdr_log(reader,"Nagra layer ATR: %s", cs_hexdump(1, atrarr, atr_size, tmp, sizeof(tmp))); + rdr_log(reader,"ATR: %s", cs_hexdump(1, atrarr, atr_size, tmp, sizeof(tmp))); calculate_cak7_vars(reader, atr); + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + reader->cak7type = 3; + } + else + { + reader->cak7type = 1; + } + if(crdr_ops->lock) { crdr_ops->lock(reader); } - Parse_ATR(reader, atr, deprecated); + int32_t ret2 = Parse_ATR(reader, atr, deprecated); if(crdr_ops->unlock) { crdr_ops->unlock(reader); } + if(ret2) + { + rdr_log(reader, "ERROR: Parse_ATR returned error"); + return ERROR; + } } else { @@ -362,7 +483,7 @@ int32_t ICC_Async_CardWrite(struct s_reader *reader, unsigned char *command, uin case ATR_PROTOCOL_TYPE_T1: ret = Protocol_T1_Command(reader, command, command_len, rsp, lr); type = 1; - if(ret != OK && !crdr_ops->skip_t1_command_retries) + if(ret != OK && !crdr_ops->skip_t1_command_retries && reader->cak7type == 0) { //try to resync rdr_log(reader, "Resync error: readtimeouts %d/%d (max/min) us, writetimeouts %d/%d (max/min) us", reader->maxreadtimeout, reader->minreadtimeout, reader->maxwritetimeout, reader->minwritetimeout); @@ -398,7 +519,7 @@ int32_t ICC_Async_CardWrite(struct s_reader *reader, unsigned char *command, uin } try++; } - while((try < 3) && (ret != OK)); // always do one retry when failing + while((try < 3) && (ret != OK) && (((type == 0 || type == 1) && reader->cak7type == 0) || type == 14)); // always do one retry when failing if(crdr_ops->unlock) { crdr_ops->unlock(reader); diff --git a/ffdecsa/CMakeLists.txt b/ffdecsa/CMakeLists.txt deleted file mode 100644 index d5be555f8..000000000 --- a/ffdecsa/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -project (ffdecsa) - -file (GLOB ffdecsa_srcs "ffdecsa.c") -file (GLOB ffdecsa_hdrs "*.h") - -set (lib_name "ffdecsa") - -add_library (${lib_name} STATIC ${ffdecsa_srcs} ${ffdecsa_hdrs}) diff --git a/ffdecsa/COPYING b/ffdecsa/COPYING deleted file mode 100644 index a43ea2126..000000000 --- a/ffdecsa/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/ffdecsa/Makefile b/ffdecsa/Makefile deleted file mode 100644 index b03684a08..000000000 --- a/ffdecsa/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -parent: - @$(MAKE) --no-print-directory -C .. diff --git a/ffdecsa/ffdecsa.c b/ffdecsa/ffdecsa.c deleted file mode 100644 index 2c7169a79..000000000 --- a/ffdecsa/ffdecsa.c +++ /dev/null @@ -1,926 +0,0 @@ -/* FFdecsa -- fast decsa algorithm - * - * Copyright (C) 2003-2004 fatih89r - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include - -#include "ffdecsa.h" - -#ifndef NULL -#define NULL 0 -#endif - -//#define DEBUG -#ifdef DEBUG -#define DBG(a) a -#else -#define DBG(a) -#endif - -//// parallelization stuff, large speed differences are possible -// possible choices -#define PARALLEL_32_4CHAR 320 -#define PARALLEL_32_4CHARA 321 -#define PARALLEL_32_INT 322 -#define PARALLEL_64_8CHAR 640 -#define PARALLEL_64_8CHARA 641 -#define PARALLEL_64_2INT 642 -#define PARALLEL_64_LONG 643 -#define PARALLEL_64_MMX 644 -#define PARALLEL_128_16CHAR 1280 -#define PARALLEL_128_16CHARA 1281 -#define PARALLEL_128_4INT 1282 -#define PARALLEL_128_2LONG 1283 -#define PARALLEL_128_2MMX 1284 -#define PARALLEL_128_SSE 1285 -#define PARALLEL_128_SSE2 1286 - -//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice //////// -#ifndef PARALLEL_MODE - -#if defined(__x86_64__) || defined(_M_X64) -#define PARALLEL_MODE PARALLEL_128_SSE2 - -#elif defined(__mips__) || defined(__mips) || defined(__MIPS__) -#define PARALLEL_MODE PARALLEL_64_LONG - -#elif defined(__sh__) || defined(__SH4__) -#define PARALLEL_MODE PARALLEL_32_INT -#define COPY_UNALIGNED_PKT -#define MEMALIGN_VAL 4 - -#else -#define PARALLEL_MODE PARALLEL_32_INT -#endif - -#endif -//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice //////// - -#include "parallel_generic.h" -//// conditionals -#if PARALLEL_MODE==PARALLEL_32_4CHAR -#include "parallel_032_4char.h" -#elif PARALLEL_MODE==PARALLEL_32_4CHARA -#include "parallel_032_4charA.h" -#elif PARALLEL_MODE==PARALLEL_32_INT -#include "parallel_032_int.h" -#elif PARALLEL_MODE==PARALLEL_64_8CHAR -#include "parallel_064_8char.h" -#elif PARALLEL_MODE==PARALLEL_64_8CHARA -#include "parallel_064_8charA.h" -#elif PARALLEL_MODE==PARALLEL_64_2INT -#include "parallel_064_2int.h" -#elif PARALLEL_MODE==PARALLEL_64_LONG -#include "parallel_064_long.h" -#elif PARALLEL_MODE==PARALLEL_64_MMX -#include "parallel_064_mmx.h" -#elif PARALLEL_MODE==PARALLEL_128_16CHAR -#include "parallel_128_16char.h" -#elif PARALLEL_MODE==PARALLEL_128_16CHARA -#include "parallel_128_16charA.h" -#elif PARALLEL_MODE==PARALLEL_128_4INT -#include "parallel_128_4int.h" -#elif PARALLEL_MODE==PARALLEL_128_2LONG -#include "parallel_128_2long.h" -#elif PARALLEL_MODE==PARALLEL_128_2MMX -#include "parallel_128_2mmx.h" -#elif PARALLEL_MODE==PARALLEL_128_SSE -#include "parallel_128_sse.h" -#elif PARALLEL_MODE==PARALLEL_128_SSE2 -#include "parallel_128_sse2.h" -#else -#error "unknown/undefined parallel mode" -#endif - -// stuff depending on conditionals - -#define BYTES_PER_GROUP (GROUP_PARALLELISM/8) -#define BYPG BYTES_PER_GROUP -#define BITS_PER_GROUP GROUP_PARALLELISM -#define BIPG BITS_PER_GROUP - -// platform specific - -#ifdef __arm__ -#if !defined(MEMALIGN_VAL) || MEMALIGN_VAL<4 -#undef MEMALIGN_VAL -#define MEMALIGN_VAL 4 -#endif -#define COPY_UNALIGNED_PKT -#endif - -// - -#ifndef MALLOC -#define MALLOC(X) malloc(X) -#endif -#ifndef FREE -#define FREE(X) free(X) -#endif -#ifdef MEMALIGN_VAL -#define MEMALIGN __attribute__((aligned(MEMALIGN_VAL))) -#else -#define MEMALIGN -#endif - -//// debug tool - -#ifdef DEBUG -static void dump_mem(const char *string, const unsigned char *p, int len, int linelen){ - int i; - for(i=0;i>4)&0xf; - iA[1]=(ck[0] )&0xf; - iA[2]=(ck[1]>>4)&0xf; - iA[3]=(ck[1] )&0xf; - iA[4]=(ck[2]>>4)&0xf; - iA[5]=(ck[2] )&0xf; - iA[6]=(ck[3]>>4)&0xf; - iA[7]=(ck[3] )&0xf; - iB[0]=(ck[4]>>4)&0xf; - iB[1]=(ck[4] )&0xf; - iB[2]=(ck[5]>>4)&0xf; - iB[3]=(ck[5] )&0xf; - iB[4]=(ck[6]>>4)&0xf; - iB[5]=(ck[6] )&0xf; - iB[6]=(ck[7]>>4)&0xf; - iB[7]=(ck[7] )&0xf; -} - -//----- stream main function - -#define STREAM_INIT -#include "stream.c" -#undef STREAM_INIT - -#define STREAM_NORMAL -#include "stream.c" -#undef STREAM_NORMAL - - -//-----block decypher - -//-----key schedule for block decypher - -static void key_schedule_block( - unsigned char *ck, // [In] ck[0]-ck[7] 8 bytes | Key. - unsigned char *kk) // [Out] kk[0]-kk[55] 56 bytes | Key schedule. -{ - static const unsigned char key_perm[0x40] = { - 0x12,0x24,0x09,0x07,0x2A,0x31,0x1D,0x15, 0x1C,0x36,0x3E,0x32,0x13,0x21,0x3B,0x40, - 0x18,0x14,0x25,0x27,0x02,0x35,0x1B,0x01, 0x22,0x04,0x0D,0x0E,0x39,0x28,0x1A,0x29, - 0x33,0x23,0x34,0x0C,0x16,0x30,0x1E,0x3A, 0x2D,0x1F,0x08,0x19,0x17,0x2F,0x3D,0x11, - 0x3C,0x05,0x38,0x2B,0x0B,0x06,0x0A,0x2C, 0x20,0x3F,0x2E,0x0F,0x03,0x26,0x10,0x37, - }; - - int i,j,k; - int bit[64]; - int newbit[64]; - int kb[7][8]; - - // 56 steps - // 56 key bytes kk(55)..kk(0) by key schedule from ck - - // kb(6,0) .. kb(6,7) = ck(0) .. ck(7) - kb[6][0] = ck[0]; - kb[6][1] = ck[1]; - kb[6][2] = ck[2]; - kb[6][3] = ck[3]; - kb[6][4] = ck[4]; - kb[6][5] = ck[5]; - kb[6][6] = ck[6]; - kb[6][7] = ck[7]; - - // calculate kb[5] .. kb[0] - for(i=5; i>=0; i--){ - // 64 bit perm on kb - for(j=0; j<8; j++){ - for(k=0; k<8; k++){ - bit[j*8+k] = (kb[i+1][j] >> (7-k)) & 1; - newbit[key_perm[j*8+k]-1] = bit[j*8+k]; - } - } - for(j=0; j<8; j++){ - kb[i][j] = 0; - for(k=0; k<8; k++){ - kb[i][j] |= newbit[j*8+k] << (7-k); - } - } - } - - // xor to give kk - for(i=0; i<7; i++){ - for(j=0; j<8; j++){ - kk[i*8+j] = kb[i][j] ^ i; - } - } - -} - -//-----block utils - -static inline __attribute__((always_inline)) void trasp_N_8 (unsigned char *in,unsigned char* out,int count){ - int *ri=(int *)in; - int *ibi=(int *)out; - int j,i,k,g; - // copy and first step - for(g=0;g>16) | (b&0xffff0000) ; - } - } - } -//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM); -// now 01010101 - for(j=0;j<8;j+=2){ - for(i=0;i<1;i++){ - for(k=0;k>8) | (b&0xff00ff00); - } - } - } -//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM); -// now 00000000 -} - -static inline __attribute__((always_inline)) void trasp_8_N (unsigned char *in,unsigned char* out,int count){ - int *ri=(int *)in; - int *bdi=(int *)out; - int j,i,k,g; -#define INTS_PER_ROW (GROUP_PARALLELISM/8*2) -//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM); -// now 00000000 - for(j=0;j<8;j+=2){ - for(i=0;i<1;i++){ - for(k=0;k>8) | (b&0xff00ff00); - } - } - } -//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM); -// now 01010101 - for(j=0;j<8;j+=4){ - for(i=0;i<2;i++){ - for(k=0;k>16) | (b&0xffff0000) ; - } - } - } -//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM); -// now 01230123 - for(g=0;g=0;i--){ - { - MEMALIGN batch tkkmulti=kkmulti[i]; - batch *si=(batch *)sbox_in; - batch *r6_N=(batch *)(r+roff+GROUP_PARALLELISM*6); - for(g=0;gck,pk,8); -// precalculations for stream - key_schedule_stream(key->ck,key->iA,key->iB); - for(by=0;by<8;by++){ - for(bi=0;bi<8;bi++){ - key->ck_g[by][bi]=(key->ck[by]&(1<iA_g[by][bi]=(key->iA[by]&(1<iB_g[by][bi]=(key->iB[by]&(1<ck,key->kk); - for(i=0;i<56;i++){ - for(j=0;jkkmulti[i])+j)=key->kk[i]; - } - } -} - -void set_control_words(void *keys, const unsigned char *ev, const unsigned char *od){ - schedule_key(&((struct csa_keys_t *)keys)->even,ev); - schedule_key(&((struct csa_keys_t *)keys)->odd,od); -} - -void set_even_control_word(void *keys, const unsigned char *pk){ - schedule_key(&((struct csa_keys_t *)keys)->even,pk); -} - -void set_odd_control_word(void *keys, const unsigned char *pk){ - schedule_key(&((struct csa_keys_t *)keys)->odd,pk); -} - -//-----get control words - -void get_control_words(void *keys, unsigned char *even, unsigned char *odd){ - memcpy(even,&((struct csa_keys_t *)keys)->even.ck,8); - memcpy(odd,&((struct csa_keys_t *)keys)->odd.ck,8); -} - -//----- decrypt - -int decrypt_packets(void *keys, unsigned char **cluster){ - // statistics, currently unused - int stat_no_scramble=0; - int stat_reserved=0; - int stat_decrypted[2]={0,0}; - int stat_decrypted_mini=0; - unsigned char **clst; - unsigned char **clst2; - int grouped; - int group_ev_od; - int advanced; - int can_advance; - unsigned char *g_pkt[GROUP_PARALLELISM]; - int g_len[GROUP_PARALLELISM]; - int g_offset[GROUP_PARALLELISM]; - int g_n[GROUP_PARALLELISM]; - int g_residue[GROUP_PARALLELISM]; - unsigned char *pkt; - int xc0,ev_od,len,offset,n,residue; - struct csa_key_t* k; - int i,j,iter,g; - int t23,tsmall; - int alive[24]; -//icc craziness int pad1=0; //////////align! FIXME - unsigned char *encp[GROUP_PARALLELISM]; - MEMALIGN unsigned char stream_in[GROUP_PARALLELISM*8]; - MEMALIGN unsigned char stream_out[GROUP_PARALLELISM*8]; - MEMALIGN unsigned char ib[GROUP_PARALLELISM*8]; - MEMALIGN unsigned char block_out[GROUP_PARALLELISM*8]; -#ifdef COPY_UNALIGNED_PKT - unsigned char *unaligned[GROUP_PARALLELISM]; - MEMALIGN unsigned char alignedBuff[GROUP_PARALLELISM][188]; -#endif - struct stream_regs regs; - -//icc craziness i=(int)&pad1;//////////align!!! FIXME - - // build a list of packets to be processed - clst=cluster; - grouped=0; - advanced=0; - can_advance=1; - group_ev_od=-1; // silence incorrect compiler warning - pkt=*clst; - do{ // find a new packet - if(grouped==GROUP_PARALLELISM){ - // full - break; - } - if(pkt==NULL){ - // no more ranges - break; - } - if(pkt>=*(clst+1)){ - // out of this range, try next - clst++;clst++; - pkt=*clst; - continue; - } - - do{ // handle this packet - xc0=pkt[3]&0xc0; - DBG(fprintf(stderr," exam pkt=%p, xc0=%02x, can_adv=%i\n",pkt,xc0,can_advance)); - if(xc0==0x00){ - DBG(fprintf(stderr,"skip clear pkt %p (can_advance is %i)\n",pkt,can_advance)); - advanced+=can_advance; - stat_no_scramble++; - break; - } - if(xc0==0x40){ - DBG(fprintf(stderr,"skip reserved pkt %p (can_advance is %i)\n",pkt,can_advance)); - advanced+=can_advance; - stat_reserved++; - break; - } - if(xc0==0x80||xc0==0xc0){ // encrypted - ev_od=(xc0&0x40)>>6; // 0 even, 1 odd - if(grouped==0) group_ev_od=ev_od; // this group will be all even (or odd) - if(group_ev_od==ev_od){ // could be added to group - pkt[3]&=0x3f; // consider it decrypted now - if(pkt[3]&0x20){ // incomplete packet - offset=4+pkt[4]+1; - len=188-offset; - n=len>>3; - residue=len-(n<<3); - if(n==0){ // decrypted==encrypted! - DBG(fprintf(stderr,"DECRYPTED MINI! (can_advance is %i)\n",can_advance)); - advanced+=can_advance; - stat_decrypted_mini++; - break; // this doesn't need more processing - } - }else{ - len=184; - offset=4; - n=23; - residue=0; - } - g_pkt[grouped]=pkt; - g_len[grouped]=len; - g_offset[grouped]=offset; - g_n[grouped]=n; - g_residue[grouped]=residue; - DBG(fprintf(stderr,"%2i: eo=%i pkt=%p len=%03i n=%2i residue=%i\n",grouped,ev_od,pkt,len,n,residue)); - grouped++; - advanced+=can_advance; - stat_decrypted[ev_od]++; - } - else{ - can_advance=0; - DBG(fprintf(stderr,"skip pkt %p and can_advance set to 0\n",pkt)); - break; // skip and go on - } - } - } while(0); - - if(can_advance){ - // move range start forward - *clst+=188; - } - // next packet, if there is one - pkt+=188; - } while(1); - DBG(fprintf(stderr,"-- result: grouped %i pkts, advanced %i pkts\n",grouped,advanced)); - - // delete empty ranges and compact list - clst2=cluster; - for(clst=cluster;*clst!=NULL;clst+=2){ - // if not empty - if(*clst<*(clst+1)){ - // it will remain - *clst2=*clst; - *(clst2+1)=*(clst+1); - clst2+=2; - } - } - *clst2=NULL; - - if(grouped==0){ - // no processing needed - return advanced; - } - - // sort them, longest payload first - // we expect many n=23 packets and a few n<23 - DBG(fprintf(stderr,"PRESORTING\n")); - for(i=0;i=0;tsmall--){ - if(g_n[tsmall]==23) break; - } -DBG(fprintf(stderr,"tsmall after for =%i\n",tsmall)); - - if(tsmall-t23<1) break; - -DBG(fprintf(stderr,"swap t23=%i,tsmall=%i\n",t23,tsmall)); - - g_swap(t23,tsmall); - - t23++; - tsmall--; -DBG(fprintf(stderr,"new t23=%i,tsmall=%i\n\n",t23,tsmall)); - } - DBG(fprintf(stderr,"packets with n=23, t23=%i grouped=%i\n",t23,grouped)); - DBG(fprintf(stderr,"MIDSORTING\n")); - for(i=0;ig_n[i]){ - g_swap(i,j); - } - } - } - DBG(fprintf(stderr,"POSTSORTING\n")); - for(i=0;i=0;i--){ - alive[i]+=alive[i+1]; - } - DBG(fprintf(stderr,"ALIVE\n")); - for(i=0;i<=23;i++){ - DBG(fprintf(stderr,"alive%2i=%i\n",i,alive[i])); - } - - // choose key - if(group_ev_od==0){ - k=&((struct csa_keys_t *)keys)->even; - } - else{ - k=&((struct csa_keys_t *)keys)->odd; - } - - //INIT -//#define INITIALIZE_UNUSED_INPUT -#ifdef INITIALIZE_UNUSED_INPUT -// unnecessary zeroing. -// without this, we operate on uninitialized memory -// when grouped>>>>ITER 0\n")); - iter=0; - stream_cypher_group_init(®s,k->iA_g,k->iB_g,stream_in); - // fill first ib - for(g=0;g0;iter++){ -DBG(fprintf(stderr,">>>>>ITER %i\n",iter)); - // alive and just dead packets: calc block - block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]); -DBG(dump_mem("BLO_ib ",block_out,8*alive[iter-1],8)); - // all packets (dead too): calc stream - stream_cypher_group_normal(®s,stream_out); -//dump_mem("stream_out",stream_out,GROUP_PARALLELISM*8,BYPG); - - // alive packets: calc ib - for(g=0;g>>>>ITER 23\n")); - iter=23; - // calc block - block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]); -DBG(dump_mem("23BLO_ib ",block_out,8*alive[iter-1],8)); - // just dead packets: write decrypted data - for(g=alive[iter];g=4?32-1:0))+j); - } -} - -typedef unsigned int batch; -#define BYTES_PER_BATCH 4 -#define B_FFN_ALL_29() 0x29292929 -#define B_FFN_ALL_02() 0x02020202 -#define B_FFN_ALL_04() 0x04040404 -#define B_FFN_ALL_10() 0x10101010 -#define B_FFN_ALL_40() 0x40404040 -#define B_FFN_ALL_80() 0x80808080 - -#define M_EMPTY() diff --git a/ffdecsa/parallel_064_long.h b/ffdecsa/parallel_064_long.h deleted file mode 100644 index 09f7b95dd..000000000 --- a/ffdecsa/parallel_064_long.h +++ /dev/null @@ -1,39 +0,0 @@ -/* FFdecsa -- fast decsa algorithm - * - * Copyright (C) 2007 Dark Avenger - * 2003-2004 fatih89r - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "parallel_std_def.h" - -typedef unsigned long long group; -#define GROUP_PARALLELISM 64 -#define FF0() 0x0ULL -#define FF1() 0xffffffffffffffffULL - -typedef unsigned long long batch; -#define BYTES_PER_BATCH 8 -#define B_FFN_ALL_29() 0x2929292929292929ULL -#define B_FFN_ALL_02() 0x0202020202020202ULL -#define B_FFN_ALL_04() 0x0404040404040404ULL -#define B_FFN_ALL_10() 0x1010101010101010ULL -#define B_FFN_ALL_40() 0x4040404040404040ULL -#define B_FFN_ALL_80() 0x8080808080808080ULL - -#define M_EMPTY() - -#include "fftable.h" diff --git a/ffdecsa/parallel_128_sse2.h b/ffdecsa/parallel_128_sse2.h deleted file mode 100644 index c7bd72517..000000000 --- a/ffdecsa/parallel_128_sse2.h +++ /dev/null @@ -1,82 +0,0 @@ -/* FFdecsa -- fast decsa algorithm - * - * Copyright (C) 2007 Dark Avenger - * 2003-2004 fatih89r - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include - -#define MEMALIGN_VAL 16 - -union __u128i { - unsigned int u[4]; - __m128i v; -}; - -static const union __u128i ff0 = {{0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}}; -static const union __u128i ff1 = {{0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU}}; - -typedef __m128i group; -#define GROUP_PARALLELISM 128 -#define FF0() ff0.v -#define FF1() ff1.v -#define FFAND(a,b) _mm_and_si128((a),(b)) -#define FFOR(a,b) _mm_or_si128((a),(b)) -#define FFXOR(a,b) _mm_xor_si128((a),(b)) -#define FFNOT(a) _mm_xor_si128((a),FF1()) -#define MALLOC(X) _mm_malloc(X,16) -#define FREE(X) _mm_free(X) - -/* BATCH */ - -static const union __u128i ff29 = {{0x29292929U, 0x29292929U, 0x29292929U, 0x29292929U}}; -static const union __u128i ff02 = {{0x02020202U, 0x02020202U, 0x02020202U, 0x02020202U}}; -static const union __u128i ff04 = {{0x04040404U, 0x04040404U, 0x04040404U, 0x04040404U}}; -static const union __u128i ff10 = {{0x10101010U, 0x10101010U, 0x10101010U, 0x10101010U}}; -static const union __u128i ff40 = {{0x40404040U, 0x40404040U, 0x40404040U, 0x40404040U}}; -static const union __u128i ff80 = {{0x80808080U, 0x80808080U, 0x80808080U, 0x80808080U}}; - -typedef __m128i batch; -#define BYTES_PER_BATCH 16 -#define B_FFN_ALL_29() ff29.v -#define B_FFN_ALL_02() ff02.v -#define B_FFN_ALL_04() ff04.v -#define B_FFN_ALL_10() ff10.v -#define B_FFN_ALL_40() ff40.v -#define B_FFN_ALL_80() ff80.v - -#define B_FFAND(a,b) FFAND(a,b) -#define B_FFOR(a,b) FFOR(a,b) -#define B_FFXOR(a,b) FFXOR(a,b) -#define B_FFSH8L(a,n) _mm_slli_epi64((a),(n)) -#define B_FFSH8R(a,n) _mm_srli_epi64((a),(n)) - -#define M_EMPTY() - -#undef BEST_SPAN -#define BEST_SPAN 16 - -#undef XOR_BEST_BY -inline static void XOR_BEST_BY(unsigned char *d, unsigned char *s1, unsigned char *s2) -{ - __m128i vs1 = _mm_load_si128((__m128i*)s1); - __m128i vs2 = _mm_load_si128((__m128i*)s2); - vs1 = _mm_xor_si128(vs1, vs2); - _mm_store_si128((__m128i*)d, vs1); -} - -#include "fftable.h" diff --git a/ffdecsa/parallel_generic.h b/ffdecsa/parallel_generic.h deleted file mode 100644 index 2af4c1c32..000000000 --- a/ffdecsa/parallel_generic.h +++ /dev/null @@ -1,102 +0,0 @@ -/* FFdecsa -- fast decsa algorithm - * - * Copyright (C) 2003-2004 fatih89r - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - - -#if 0 -//// generics -#define COPY4BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \ - *pd = *ps; }while(0) -#define COPY8BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd = *ps; }while(0) -#define COPY16BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd = *ps; \ - *(pd+1) = *(ps+1); }while(0) -#define COPY32BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd = *ps; \ - *(pd+1) = *(ps+1) \ - *(pd+2) = *(ps+2) \ - *(pd+3) = *(ps+3); }while(0) -#define XOR4BY(d,s1,s2) do{ int *pd=(int *)(d), *ps1=(int *)(s1), *ps2=(int *)(s2); \ - *pd = *ps1 ^ *ps2; }while(0) -#define XOR8BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \ - *pd = *ps1 ^ *ps2; }while(0) -#define XOR16BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \ - *pd = *ps1 ^ *ps2; \ - *(pd+8) = *(ps1+8) ^ *(ps2+8); }while(0) -#define XOR32BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \ - *pd = *ps1 ^ *ps2; \ - *(pd+1) = *(ps1+1) ^ *(ps2+1); \ - *(pd+2) = *(ps1+2) ^ *(ps2+2); \ - *(pd+3) = *(ps1+3) ^ *(ps2+3); }while(0) -#define XOR32BV(d,s1,s2) do{ int *const pd=(int *const)(d), *ps1=(const int *const)(s1), *ps2=(const int *const)(s2); \ - int z; \ - for(z=0;z<8;z++){ \ - pd[z]=ps1[z]^ps2[z]; \ - } \ - }while(0) -#define XOREQ4BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \ - *pd ^= *ps; }while(0) -#define XOREQ8BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd ^= *ps; }while(0) -#define XOREQ16BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd ^= *ps; \ - *(pd+1) ^=*(ps+1); }while(0) -#define XOREQ32BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd ^= *ps; \ - *(pd+1) ^=*(ps+1); \ - *(pd+2) ^=*(ps+2); \ - *(pd+3) ^=*(ps+3); }while(0) -#define XOREQ32BY4(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \ - *pd ^= *ps; \ - *(pd+1) ^=*(ps+1); \ - *(pd+2) ^=*(ps+2); \ - *(pd+3) ^=*(ps+3); \ - *(pd+4) ^=*(ps+4); \ - *(pd+5) ^=*(ps+5); \ - *(pd+6) ^=*(ps+6); \ - *(pd+7) ^=*(ps+7); }while(0) -#define XOREQ32BV(d,s) do{ unsigned char *pd=(unsigned char *)(d), *ps=(unsigned char *)(s); \ - int z; \ - for(z=0;z<32;z++){ \ - pd[z]^=ps[z]; \ - } \ - }while(0) - -#else -#define XOR_4_BY(d,s1,s2) do{ int *pd=(int *)(d), *ps1=(int *)(s1), *ps2=(int *)(s2); \ - *pd = *ps1 ^ *ps2; }while(0) -#define XOR_8_BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \ - *pd = *ps1 ^ *ps2; }while(0) -#define XOREQ_4_BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \ - *pd ^= *ps; }while(0) -#define XOREQ_8_BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd ^= *ps; }while(0) -#define COPY_4_BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \ - *pd = *ps; }while(0) -#define COPY_8_BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \ - *pd = *ps; }while(0) - -#define BEST_SPAN 8 -#define XOR_BEST_BY(d,s1,s2) do{ XOR_8_BY(d,s1,s2); }while(0); -#define XOREQ_BEST_BY(d,s) do{ XOREQ_8_BY(d,s); }while(0); -#define COPY_BEST_BY(d,s) do{ COPY_8_BY(d,s); }while(0); - -#define END_MM do{ }while(0); -#endif diff --git a/ffdecsa/parallel_std_def.h b/ffdecsa/parallel_std_def.h deleted file mode 100644 index 10517d483..000000000 --- a/ffdecsa/parallel_std_def.h +++ /dev/null @@ -1,29 +0,0 @@ -/* FFdecsa -- fast decsa algorithm - * - * Copyright (C) 2003-2004 fatih89r - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#define FFXOR(a,b) ((a)^(b)) -#define FFAND(a,b) ((a)&(b)) -#define FFOR(a,b) ((a)|(b)) -#define FFNOT(a) (~(a)) - -#define B_FFAND(a,b) ((a)&(b)) -#define B_FFOR(a,b) ((a)|(b)) -#define B_FFXOR(a,b) ((a)^(b)) -#define B_FFSH8L(a,n) ((a)<<(n)) -#define B_FFSH8R(a,n) ((a)>>(n)) diff --git a/ffdecsa/stream.c b/ffdecsa/stream.c deleted file mode 100644 index 1bda8521c..000000000 --- a/ffdecsa/stream.c +++ /dev/null @@ -1,906 +0,0 @@ -/* FFdecsa -- fast decsa algorithm - * - * Copyright (C) 2003-2004 fatih89r - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - - -// define statics only once, when STREAM_INIT -#ifdef STREAM_INIT -struct stream_regs { - group A[32+10][4]; // 32 because we will move back (virtual shift register) - group B[32+10][4]; // 32 because we will move back (virtual shift register) - group X[4]; - group Y[4]; - group Z[4]; - group D[4]; - group E[4]; - group F[4]; - group p; - group q; - group r; - }; - -static inline void trasp64_32_88ccw(unsigned char *data){ -/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/ -#define row ((unsigned int *)data) - int i,j; - for(j=0;j<64;j+=32){ - unsigned int t,b; - for(i=0;i<16;i++){ - t=row[j+i]; - b=row[j+16+i]; - row[j+i] = (t&0x0000ffff) | ((b )<<16); - row[j+16+i]=((t )>>16) | (b&0xffff0000) ; - } - } - for(j=0;j<64;j+=16){ - unsigned int t,b; - for(i=0;i<8;i++){ - t=row[j+i]; - b=row[j+8+i]; - row[j+i] = (t&0x00ff00ff) | ((b&0x00ff00ff)<<8); - row[j+8+i] =((t&0xff00ff00)>>8) | (b&0xff00ff00); - } - } - for(j=0;j<64;j+=8){ - unsigned int t,b; - for(i=0;i<4;i++){ - t=row[j+i]; - b=row[j+4+i]; - row[j+i] =((t&0x0f0f0f0f)<<4) | (b&0x0f0f0f0f); - row[j+4+i] = (t&0xf0f0f0f0) | ((b&0xf0f0f0f0)>>4); - } - } - for(j=0;j<64;j+=4){ - unsigned int t,b; - for(i=0;i<2;i++){ - t=row[j+i]; - b=row[j+2+i]; - row[j+i] =((t&0x33333333)<<2) | (b&0x33333333); - row[j+2+i] = (t&0xcccccccc) | ((b&0xcccccccc)>>2); - } - } - for(j=0;j<64;j+=2){ - unsigned int t,b; - for(i=0;i<1;i++){ - t=row[j+i]; - b=row[j+1+i]; - row[j+i] =((t&0x55555555)<<1) | (b&0x55555555); - row[j+1+i] = (t&0xaaaaaaaa) | ((b&0xaaaaaaaa)>>1); - } - } -#undef row -} - -static inline void trasp64_32_88cw(unsigned char *data){ -/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate clockwise)*/ -#define row ((unsigned int *)data) - int i,j; - for(j=0;j<64;j+=32){ - unsigned int t,b; - for(i=0;i<16;i++){ - t=row[j+i]; - b=row[j+16+i]; - row[j+i] = (t&0x0000ffff) | ((b )<<16); - row[j+16+i]=((t )>>16) | (b&0xffff0000) ; - } - } - for(j=0;j<64;j+=16){ - unsigned int t,b; - for(i=0;i<8;i++){ - t=row[j+i]; - b=row[j+8+i]; - row[j+i] = (t&0x00ff00ff) | ((b&0x00ff00ff)<<8); - row[j+8+i] =((t&0xff00ff00)>>8) | (b&0xff00ff00); - } - } - for(j=0;j<64;j+=8){ - unsigned int t,b; - for(i=0;i<4;i++){ - t=row[j+i]; - b=row[j+4+i]; - row[j+i] =((t&0xf0f0f0f0)>>4) | (b&0xf0f0f0f0); - row[j+4+i]= (t&0x0f0f0f0f) | ((b&0x0f0f0f0f)<<4); - } - } - for(j=0;j<64;j+=4){ - unsigned int t,b; - for(i=0;i<2;i++){ - t=row[j+i]; - b=row[j+2+i]; - row[j+i] =((t&0xcccccccc)>>2) | (b&0xcccccccc); - row[j+2+i]= (t&0x33333333) | ((b&0x33333333)<<2); - } - } - for(j=0;j<64;j+=2){ - unsigned int t,b; - for(i=0;i<1;i++){ - t=row[j+i]; - b=row[j+1+i]; - row[j+i] =((t&0xaaaaaaaa)>>1) | (b&0xaaaaaaaa); - row[j+1+i]= (t&0x55555555) | ((b&0x55555555)<<1); - } - } -#undef row -} - -//64-64---------------------------------------------------------- -static inline void trasp64_64_88ccw(unsigned char *data){ -/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/ -#define row ((unsigned long long int *)data) - int i,j; - for(j=0;j<64;j+=64){ - unsigned long long int t,b; - for(i=0;i<32;i++){ - t=row[j+i]; - b=row[j+32+i]; - row[j+i] = (t&0x00000000ffffffffULL) | ((b )<<32); - row[j+32+i]=((t )>>32) | (b&0xffffffff00000000ULL) ; - } - } - for(j=0;j<64;j+=32){ - unsigned long long int t,b; - for(i=0;i<16;i++){ - t=row[j+i]; - b=row[j+16+i]; - row[j+i] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16); - row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ; - } - } - for(j=0;j<64;j+=16){ - unsigned long long int t,b; - for(i=0;i<8;i++){ - t=row[j+i]; - b=row[j+8+i]; - row[j+i] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8); - row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL); - } - } - for(j=0;j<64;j+=8){ - unsigned long long int t,b; - for(i=0;i<4;i++){ - t=row[j+i]; - b=row[j+4+i]; - row[j+i] =((t&0x0f0f0f0f0f0f0f0fULL)<<4) | (b&0x0f0f0f0f0f0f0f0fULL); - row[j+4+i] = (t&0xf0f0f0f0f0f0f0f0ULL) | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4); - } - } - for(j=0;j<64;j+=4){ - unsigned long long int t,b; - for(i=0;i<2;i++){ - t=row[j+i]; - b=row[j+2+i]; - row[j+i] =((t&0x3333333333333333ULL)<<2) | (b&0x3333333333333333ULL); - row[j+2+i] = (t&0xccccccccccccccccULL) | ((b&0xccccccccccccccccULL)>>2); - } - } - for(j=0;j<64;j+=2){ - unsigned long long int t,b; - for(i=0;i<1;i++){ - t=row[j+i]; - b=row[j+1+i]; - row[j+i] =((t&0x5555555555555555ULL)<<1) | (b&0x5555555555555555ULL); - row[j+1+i] = (t&0xaaaaaaaaaaaaaaaaULL) | ((b&0xaaaaaaaaaaaaaaaaULL)>>1); - } - } -#undef row -} - -static inline void trasp64_64_88cw(unsigned char *data){ -/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate clockwise)*/ -#define row ((unsigned long long int *)data) - int i,j; - for(j=0;j<64;j+=64){ - unsigned long long int t,b; - for(i=0;i<32;i++){ - t=row[j+i]; - b=row[j+32+i]; - row[j+i] = (t&0x00000000ffffffffULL) | ((b )<<32); - row[j+32+i]=((t )>>32) | (b&0xffffffff00000000ULL) ; - } - } - for(j=0;j<64;j+=32){ - unsigned long long int t,b; - for(i=0;i<16;i++){ - t=row[j+i]; - b=row[j+16+i]; - row[j+i] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16); - row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ; - } - } - for(j=0;j<64;j+=16){ - unsigned long long int t,b; - for(i=0;i<8;i++){ - t=row[j+i]; - b=row[j+8+i]; - row[j+i] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8); - row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL); - } - } - for(j=0;j<64;j+=8){ - unsigned long long int t,b; - for(i=0;i<4;i++){ - t=row[j+i]; - b=row[j+4+i]; - row[j+i] =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) | (b&0xf0f0f0f0f0f0f0f0ULL); - row[j+4+i] = (t&0x0f0f0f0f0f0f0f0fULL) | ((b&0x0f0f0f0f0f0f0f0fULL)<<4); - } - } - for(j=0;j<64;j+=4){ - unsigned long long int t,b; - for(i=0;i<2;i++){ - t=row[j+i]; - b=row[j+2+i]; - row[j+i] =((t&0xccccccccccccccccULL)>>2) | (b&0xccccccccccccccccULL); - row[j+2+i] = (t&0x3333333333333333ULL) | ((b&0x3333333333333333ULL)<<2); - } - } - for(j=0;j<64;j+=2){ - unsigned long long int t,b; - for(i=0;i<1;i++){ - t=row[j+i]; - b=row[j+1+i]; - row[j+i] =((t&0xaaaaaaaaaaaaaaaaULL)>>1) | (b&0xaaaaaaaaaaaaaaaaULL); - row[j+1+i] = (t&0x5555555555555555ULL) | ((b&0x5555555555555555ULL)<<1); - } - } -#undef row -} - -//64-128---------------------------------------------------------- -static inline void trasp64_128_88ccw(unsigned char *data){ -/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/ -#define halfrow ((unsigned long long int *)data) - int i,j; - for(j=0;j<64;j+=64){ - unsigned long long int t,b; - for(i=0;i<32;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+32+i)]; - halfrow[2*(j+i)] = (t&0x00000000ffffffffULL) | ((b )<<32); - halfrow[2*(j+32+i)]=((t )>>32) | (b&0xffffffff00000000ULL) ; - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+32+i)+1]; - halfrow[2*(j+i)+1] = (t&0x00000000ffffffffULL) | ((b )<<32); - halfrow[2*(j+32+i)+1]=((t )>>32) | (b&0xffffffff00000000ULL) ; - } - } - for(j=0;j<64;j+=32){ - unsigned long long int t,b; - for(i=0;i<16;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+16+i)]; - halfrow[2*(j+i)] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16); - halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ; - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+16+i)+1]; - halfrow[2*(j+i)+1] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16); - halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ; - } - } - for(j=0;j<64;j+=16){ - unsigned long long int t,b; - for(i=0;i<8;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+8+i)]; - halfrow[2*(j+i)] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8); - halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+8+i)+1]; - halfrow[2*(j+i)+1] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8); - halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL); - } - } - for(j=0;j<64;j+=8){ - unsigned long long int t,b; - for(i=0;i<4;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+4+i)]; - halfrow[2*(j+i)] =((t&0x0f0f0f0f0f0f0f0fULL)<<4) | (b&0x0f0f0f0f0f0f0f0fULL); - halfrow[2*(j+4+i)] = (t&0xf0f0f0f0f0f0f0f0ULL) | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+4+i)+1]; - halfrow[2*(j+i)+1] =((t&0x0f0f0f0f0f0f0f0fULL)<<4) | (b&0x0f0f0f0f0f0f0f0fULL); - halfrow[2*(j+4+i)+1] = (t&0xf0f0f0f0f0f0f0f0ULL) | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4); - } - } - for(j=0;j<64;j+=4){ - unsigned long long int t,b; - for(i=0;i<2;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+2+i)]; - halfrow[2*(j+i)] =((t&0x3333333333333333ULL)<<2) | (b&0x3333333333333333ULL); - halfrow[2*(j+2+i)] = (t&0xccccccccccccccccULL) | ((b&0xccccccccccccccccULL)>>2); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+2+i)+1]; - halfrow[2*(j+i)+1] =((t&0x3333333333333333ULL)<<2) | (b&0x3333333333333333ULL); - halfrow[2*(j+2+i)+1] = (t&0xccccccccccccccccULL) | ((b&0xccccccccccccccccULL)>>2); - } - } - for(j=0;j<64;j+=2){ - unsigned long long int t,b; - for(i=0;i<1;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+1+i)]; - halfrow[2*(j+i)] =((t&0x5555555555555555ULL)<<1) | (b&0x5555555555555555ULL); - halfrow[2*(j+1+i)] = (t&0xaaaaaaaaaaaaaaaaULL) | ((b&0xaaaaaaaaaaaaaaaaULL)>>1); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+1+i)+1]; - halfrow[2*(j+i)+1] =((t&0x5555555555555555ULL)<<1) | (b&0x5555555555555555ULL); - halfrow[2*(j+1+i)+1] = (t&0xaaaaaaaaaaaaaaaaULL) | ((b&0xaaaaaaaaaaaaaaaaULL)>>1); - } - } -#undef halfrow -} - -static inline void trasp64_128_88cw(unsigned char *data){ -/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate clockwise)*/ -#define halfrow ((unsigned long long int *)data) - int i,j; - for(j=0;j<64;j+=64){ - unsigned long long int t,b; - for(i=0;i<32;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+32+i)]; - halfrow[2*(j+i)] = (t&0x00000000ffffffffULL) | ((b )<<32); - halfrow[2*(j+32+i)]=((t )>>32) | (b&0xffffffff00000000ULL) ; - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+32+i)+1]; - halfrow[2*(j+i)+1] = (t&0x00000000ffffffffULL) | ((b )<<32); - halfrow[2*(j+32+i)+1]=((t )>>32) | (b&0xffffffff00000000ULL) ; - } - } - for(j=0;j<64;j+=32){ - unsigned long long int t,b; - for(i=0;i<16;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+16+i)]; - halfrow[2*(j+i)] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16); - halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ; - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+16+i)+1]; - halfrow[2*(j+i)+1] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16); - halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ; - } - } - for(j=0;j<64;j+=16){ - unsigned long long int t,b; - for(i=0;i<8;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+8+i)]; - halfrow[2*(j+i)] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8); - halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+8+i)+1]; - halfrow[2*(j+i)+1] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8); - halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL); - } - } - for(j=0;j<64;j+=8){ - unsigned long long int t,b; - for(i=0;i<4;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+4+i)]; - halfrow[2*(j+i)] =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) | (b&0xf0f0f0f0f0f0f0f0ULL); - halfrow[2*(j+4+i)] = (t&0x0f0f0f0f0f0f0f0fULL) | ((b&0x0f0f0f0f0f0f0f0fULL)<<4); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+4+i)+1]; - halfrow[2*(j+i)+1] =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) | (b&0xf0f0f0f0f0f0f0f0ULL); - halfrow[2*(j+4+i)+1] = (t&0x0f0f0f0f0f0f0f0fULL) | ((b&0x0f0f0f0f0f0f0f0fULL)<<4); - } - } - for(j=0;j<64;j+=4){ - unsigned long long int t,b; - for(i=0;i<2;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+2+i)]; - halfrow[2*(j+i)] =((t&0xccccccccccccccccULL)>>2) | (b&0xccccccccccccccccULL); - halfrow[2*(j+2+i)] = (t&0x3333333333333333ULL) | ((b&0x3333333333333333ULL)<<2); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+2+i)+1]; - halfrow[2*(j+i)+1] =((t&0xccccccccccccccccULL)>>2) | (b&0xccccccccccccccccULL); - halfrow[2*(j+2+i)+1] = (t&0x3333333333333333ULL) | ((b&0x3333333333333333ULL)<<2); - } - } - for(j=0;j<64;j+=2){ - unsigned long long int t,b; - for(i=0;i<1;i++){ - t=halfrow[2*(j+i)]; - b=halfrow[2*(j+1+i)]; - halfrow[2*(j+i)] =((t&0xaaaaaaaaaaaaaaaaULL)>>1) | (b&0xaaaaaaaaaaaaaaaaULL); - halfrow[2*(j+1+i)] = (t&0x5555555555555555ULL) | ((b&0x5555555555555555ULL)<<1); - t=halfrow[2*(j+i)+1]; - b=halfrow[2*(j+1+i)+1]; - halfrow[2*(j+i)+1] =((t&0xaaaaaaaaaaaaaaaaULL)>>1) | (b&0xaaaaaaaaaaaaaaaaULL); - halfrow[2*(j+1+i)+1] = (t&0x5555555555555555ULL) | ((b&0x5555555555555555ULL)<<1); - } - } -#undef halfrow -} -#endif - - -#ifdef STREAM_INIT -void stream_cypher_group_init( - struct stream_regs *regs, - group iA[8][4], // [In] iA00,iA01,...iA73 32 groups | Derived from key. - group iB[8][4], // [In] iB00,iB01,...iB73 32 groups | Derived from key. - unsigned char *sb) // [In] (SB0,SB1,...SB7)...x32 32*8 bytes | Extra input. -#endif -#ifdef STREAM_NORMAL -void stream_cypher_group_normal( - struct stream_regs *regs, - unsigned char *cb) // [Out] (CB0,CB1,...CB7)...x32 32*8 bytes | Output. -#endif -{ -#ifdef STREAM_INIT - group in1[4]; - group in2[4]; -#endif - group extra_B[4]; - group fa,fb,fc,fd,fe; - group s1a,s1b,s2a,s2b,s3a,s3b,s4a,s4b,s5a,s5b,s6a,s6b,s7a,s7b; - group next_E[4]; - group tmp0,tmp1,tmp2,tmp3,tmp4; -#ifdef STREAM_INIT - group *sb_g=(group *)sb; -#endif -#ifdef STREAM_NORMAL - group *cb_g=(group *)cb; -#endif - int aboff; - int i,j,k,b; - int dbg; - -#ifdef STREAM_INIT - DBG(fprintf(stderr,":::::::::: BEGIN STREAM INIT\n")); -#endif -#ifdef STREAM_NORMAL - DBG(fprintf(stderr,":::::::::: BEGIN STREAM NORMAL\n")); -#endif -#ifdef STREAM_INIT -for(j=0;j<64;j++){ - DBG(fprintf(stderr,"precall prerot stream_in[%2i]=",j)); - DBG(dump_mem("",sb+BYPG*j,BYPG,BYPG)); -} - -DBG(dump_mem("stream_prerot ",sb,GROUP_PARALLELISM*8,BYPG)); -#if GROUP_PARALLELISM==32 -trasp64_32_88ccw(sb); -#endif -#if GROUP_PARALLELISM==64 -trasp64_64_88ccw(sb); -#endif -#if GROUP_PARALLELISM==128 -trasp64_128_88ccw(sb); -#endif -DBG(dump_mem("stream_postrot",sb,GROUP_PARALLELISM*8,BYPG)); - -for(j=0;j<64;j++){ - DBG(fprintf(stderr,"precall stream_in[%2i]=",j)); - DBG(dump_mem("",sb+BYPG*j,BYPG,BYPG)); -} -#endif - - aboff=32; - -#ifdef STREAM_INIT - // load first 32 bits of ck into A[aboff+0]..A[aboff+7] - // load last 32 bits of ck into B[aboff+0]..B[aboff+7] - // all other regs = 0 - for(i=0;i<8;i++){ - for(b=0;b<4;b++){ -DBG(fprintf(stderr,"dbg from iA A[%i][%i]=",i,b)); -DBG(dump_mem("",(unsigned char *)&iA[i][b],BYPG,BYPG)); -DBG(fprintf(stderr," dbg from iB B[%i][%i]=",i,b)); -DBG(dump_mem("",(unsigned char *)&iB[i][b],BYPG,BYPG)); - regs->A[aboff+i][b]=iA[i][b]; - regs->B[aboff+i][b]=iB[i][b]; - } - } - for(b=0;b<4;b++){ - regs->A[aboff+8][b]=FF0(); - regs->A[aboff+9][b]=FF0(); - regs->B[aboff+8][b]=FF0(); - regs->B[aboff+9][b]=FF0(); - } - for(b=0;b<4;b++){ - regs->X[b]=FF0(); - regs->Y[b]=FF0(); - regs->Z[b]=FF0(); - regs->D[b]=FF0(); - regs->E[b]=FF0(); - regs->F[b]=FF0(); - } - regs->p=FF0(); - regs->q=FF0(); - regs->r=FF0(); -#endif - -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"dbg A0[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->A[aboff+0][dbg],BYPG,BYPG)); - DBG(fprintf(stderr,"dbg B0[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->B[aboff+0][dbg],BYPG,BYPG)); -} - -//////////////////////////////////////////////////////////////////////////////// - - // EXTERNAL LOOP - 8 bytes per operation - for(i=0;i<8;i++){ - - DBG(fprintf(stderr,"--BEGIN EXTERNAL LOOP %i\n",i)); - -#ifdef STREAM_INIT - for(b=0;b<4;b++){ - in1[b]=sb_g[8*i+4+b]; - in2[b]=sb_g[8*i+b]; - } -#endif - - // INTERNAL LOOP - 2 bits per iteration - for(j=0; j<4; j++){ - - DBG(fprintf(stderr,"---BEGIN INTERNAL LOOP %i (EXT %i, INT %i)\n",j,i,j)); - - // from A0..A9, 35 bits are selected as inputs to 7 s-boxes - // 5 bits input per s-box, 2 bits output per s-box - - // we can select bits with zero masking and shifting operations - // and synthetize s-boxes with optimized boolean functions. - // this is the actual reason we do all the crazy transposition - // stuff to switch between normal and bit slice representations. - // this code really flies. - - fe=regs->A[aboff+3][0];fa=regs->A[aboff+0][2];fb=regs->A[aboff+5][1];fc=regs->A[aboff+6][3];fd=regs->A[aboff+8][0]; -/* 1000 1110 1110 0001 : lev 7: */ //tmp0=( fa^( fb^( ( ( ( fa|fb )^fc )|( fc^fd ) )^ALL_ONES ) ) ); -/* 1110 0010 0011 0011 : lev 6: */ //tmp1=( ( fa|fb )^( ( fc&( fa|( fb^fd ) ) )^ALL_ONES ) ); -/* 0011 0110 1000 1101 : lev 5: */ //tmp2=( fa^( ( fb&fd )^( ( fa&fd )|fc ) ) ); -/* 0101 0101 1001 0011 : lev 5: */ //tmp3=( ( fa&fc )^( fa^( ( fa&fb )|fd ) ) ); -/* 1000 1110 1110 0001 : lev 7: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFOR(FFXOR(FFOR(fa,fb),fc),FFXOR(fc,fd)),FF1()))); -/* 1110 0010 0011 0011 : lev 6: */ tmp1=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fa,FFXOR(fb,fd))),FF1())); -/* 0011 0110 1000 1101 : lev 5: */ tmp2=FFXOR(fa,FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),fc))); -/* 0101 0101 1001 0011 : lev 5: */ tmp3=FFXOR(FFAND(fa,fc),FFXOR(fa,FFOR(FFAND(fa,fb),fd))); - s1a=FFXOR(tmp0,FFAND(fe,tmp1)); - s1b=FFXOR(tmp2,FFAND(fe,tmp3)); -//dump_mem("s1as1b-fe",&fe,BYPG,BYPG); -//dump_mem("s1as1b-fa",&fa,BYPG,BYPG); -//dump_mem("s1as1b-fb",&fb,BYPG,BYPG); -//dump_mem("s1as1b-fc",&fc,BYPG,BYPG); -//dump_mem("s1as1b-fd",&fd,BYPG,BYPG); - - fe=regs->A[aboff+1][1];fa=regs->A[aboff+2][2];fb=regs->A[aboff+5][3];fc=regs->A[aboff+6][0];fd=regs->A[aboff+8][1]; -/* 1001 1110 0110 0001 : lev 6: */ //tmp0=( fa^( ( fb&( fc|fd ) )^( fc^( fd^ALL_ONES ) ) ) ); -/* 0000 0011 0111 1011 : lev 5: */ //tmp1=( ( fa&( fb^fd ) )|( ( fa|fb )&fc ) ); -/* 1100 0110 1101 0010 : lev 6: */ //tmp2=( ( fb&fd )^( ( fa&fd )|( fb^( fc^ALL_ONES ) ) ) ); -/* 0001 1110 1111 0101 : lev 5: */ //tmp3=( ( fa&fd )|( fa^( fb^( fc&fd ) ) ) ); -/* 1001 1110 0110 0001 : lev 6: */ tmp0=FFXOR(fa,FFXOR(FFAND(fb,FFOR(fc,fd)),FFXOR(fc,FFXOR(fd,FF1())))); -/* 0000 0011 0111 1011 : lev 5: */ tmp1=FFOR(FFAND(fa,FFXOR(fb,fd)),FFAND(FFOR(fa,fb),fc)); -/* 1100 0110 1101 0010 : lev 6: */ tmp2=FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),FFXOR(fb,FFXOR(fc,FF1())))); -/* 0001 1110 1111 0101 : lev 5: */ tmp3=FFOR(FFAND(fa,fd),FFXOR(fa,FFXOR(fb,FFAND(fc,fd)))); - s2a=FFXOR(tmp0,FFAND(fe,tmp1)); - s2b=FFXOR(tmp2,FFAND(fe,tmp3)); - - fe=regs->A[aboff+0][3];fa=regs->A[aboff+1][0];fb=regs->A[aboff+4][1];fc=regs->A[aboff+4][3];fd=regs->A[aboff+5][2]; -/* 0100 1011 1001 0110 : lev 5: */ //tmp0=( fa^( fb^( ( fc&( fa|fd ) )^fd ) ) ); -/* 1101 0101 1000 1100 : lev 7: */ //tmp1=( ( fa&fc )^( ( fa^fd )|( ( fb|fc )^( fd^ALL_ONES ) ) ) ); -/* 0010 0111 1101 1000 : lev 4: */ //tmp2=( fa^( ( ( fb^fc )&fd )^fc ) ); -/* 1111 1111 1111 1111 : lev 0: */ //tmp3=ALL_ONES; -/* 0100 1011 1001 0110 : lev 5: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFAND(fc,FFOR(fa,fd)),fd))); -/* 1101 0101 1000 1100 : lev 7: */ tmp1=FFXOR(FFAND(fa,fc),FFOR(FFXOR(fa,fd),FFXOR(FFOR(fb,fc),FFXOR(fd,FF1())))); -/* 0010 0111 1101 1000 : lev 4: */ tmp2=FFXOR(fa,FFXOR(FFAND(FFXOR(fb,fc),fd),fc)); -/* 1111 1111 1111 1111 : lev 0: */ tmp3=FF1(); - s3a=FFXOR(tmp0,FFAND(FFNOT(fe),tmp1)); - s3b=FFXOR(tmp2,FFAND(fe,tmp3)); - - fe=regs->A[aboff+2][3];fa=regs->A[aboff+0][1];fb=regs->A[aboff+1][3];fc=regs->A[aboff+3][2];fd=regs->A[aboff+7][0]; -/* 1011 0101 0100 1001 : lev 7: */ //tmp0=( fa^( ( fc&( fa^fd ) )|( fb^( fc|( fd^ALL_ONES ) ) ) ) ); -/* 0010 1101 0110 0110 : lev 6: */ //tmp1=( ( fa&fb )^( fb^( ( ( fa|fc )&fd )^fc ) ) ); -/* 0110 0111 1101 0000 : lev 7: */ //tmp2=( fa^( ( fb&fc )|( ( ( fa&( fb^fd ) )|fc )^fd ) ) ); -/* 1111 1111 1111 1111 : lev 0: */ //tmp3=ALL_ONES; -/* 1011 0101 0100 1001 : lev 7: */ tmp0=FFXOR(fa,FFOR(FFAND(fc,FFXOR(fa,fd)),FFXOR(fb,FFOR(fc,FFXOR(fd,FF1()))))); -/* 0010 1101 0110 0110 : lev 6: */ tmp1=FFXOR(FFAND(fa,fb),FFXOR(fb,FFXOR(FFAND(FFOR(fa,fc),fd),fc))); -/* 0110 0111 1101 0000 : lev 7: */ tmp2=FFXOR(fa,FFOR(FFAND(fb,fc),FFXOR(FFOR(FFAND(fa,FFXOR(fb,fd)),fc),fd))); -/* 1111 1111 1111 1111 : lev 0: */ tmp3=FF1(); - s4a=FFXOR(tmp0,FFAND(fe,FFXOR(tmp1,tmp0))); - s4b=FFXOR(FFXOR(s4a,tmp2),FFAND(fe,tmp3)); - - fe=regs->A[aboff+4][2];fa=regs->A[aboff+3][3];fb=regs->A[aboff+5][0];fc=regs->A[aboff+7][1];fd=regs->A[aboff+8][2]; -/* 1000 1111 0011 0010 : lev 7: */ //tmp0=( ( ( fa&( fb|fc ) )^fb )|( ( ( fa^fc )|fd )^ALL_ONES ) ); -/* 0110 1011 0000 1011 : lev 6: */ //tmp1=( fb^( ( fc^fd )&( fc^( fb|( fa^fd ) ) ) ) ); -/* 0001 1010 0111 1001 : lev 6: */ //tmp2=( ( fa&fc )^( fb^( ( fb|( fa^fc ) )&fd ) ) ); -/* 0101 1101 1101 0101 : lev 4: */ //tmp3=( ( ( fa^fb )&( fc^ALL_ONES ) )|fd ); -/* 1000 1111 0011 0010 : lev 7: */ tmp0=FFOR(FFXOR(FFAND(fa,FFOR(fb,fc)),fb),FFXOR(FFOR(FFXOR(fa,fc),fd),FF1())); -/* 0110 1011 0000 1011 : lev 6: */ tmp1=FFXOR(fb,FFAND(FFXOR(fc,fd),FFXOR(fc,FFOR(fb,FFXOR(fa,fd))))); -/* 0001 1010 0111 1001 : lev 6: */ tmp2=FFXOR(FFAND(fa,fc),FFXOR(fb,FFAND(FFOR(fb,FFXOR(fa,fc)),fd))); -/* 0101 1101 1101 0101 : lev 4: */ tmp3=FFOR(FFAND(FFXOR(fa,fb),FFXOR(fc,FF1())),fd); - s5a=FFXOR(tmp0,FFAND(fe,tmp1)); - s5b=FFXOR(tmp2,FFAND(fe,tmp3)); - - fe=regs->A[aboff+2][1];fa=regs->A[aboff+3][1];fb=regs->A[aboff+4][0];fc=regs->A[aboff+6][2];fd=regs->A[aboff+8][3]; -/* 0011 0110 0010 1101 : lev 6: */ //tmp0=( ( ( fa&fc )&fd )^( ( fb&( fa|fd ) )^fc ) ); -/* 1110 1110 1011 1011 : lev 3: */ //tmp1=( ( ( fa^fc )&fd )^ALL_ONES ); -/* 0101 1000 0110 0111 : lev 6: */ //tmp2=( ( fa&( fb|fc ) )^( fb^( ( fb&fc )|fd ) ) ); -/* 0001 0011 0000 0001 : lev 5: */ //tmp3=( fc&( ( fa&( fb^fd ) )^( fb|fd ) ) ); -/* 0011 0110 0010 1101 : lev 6: */ tmp0=FFXOR(FFAND(FFAND(fa,fc),fd),FFXOR(FFAND(fb,FFOR(fa,fd)),fc)); -/* 1110 1110 1011 1011 : lev 3: */ tmp1=FFXOR(FFAND(FFXOR(fa,fc),fd),FF1()); -/* 0101 1000 0110 0111 : lev 6: */ tmp2=FFXOR(FFAND(fa,FFOR(fb,fc)),FFXOR(fb,FFOR(FFAND(fb,fc),fd))); -/* 0001 0011 0000 0001 : lev 5: */ tmp3=FFAND(fc,FFXOR(FFAND(fa,FFXOR(fb,fd)),FFOR(fb,fd))); - s6a=FFXOR(tmp0,FFAND(fe,tmp1)); - s6b=FFXOR(tmp2,FFAND(fe,tmp3)); - - fe=regs->A[aboff+1][2];fa=regs->A[aboff+2][0];fb=regs->A[aboff+6][1];fc=regs->A[aboff+7][2];fd=regs->A[aboff+7][3]; -/* 0111 1000 1001 0110 : lev 5: */ //tmp0=( fb^( ( fc&fd )|( fa^( fc^fd ) ) ) ); -/* 0100 1001 0101 1011 : lev 6: */ //tmp1=( ( fb|fd )&( ( fa&fc )|( fb^( fc^fd ) ) ) ); -/* 0100 1001 1011 1001 : lev 5: */ //tmp2=( ( fa|fb )^( ( fc&( fb|fd ) )^fd ) ); -/* 1111 1111 1101 1101 : lev 3: */ //tmp3=( fd|( ( fa&fc )^ALL_ONES ) ); -/* 0111 1000 1001 0110 : lev 5: */ tmp0=FFXOR(fb,FFOR(FFAND(fc,fd),FFXOR(fa,FFXOR(fc,fd)))); -/* 0100 1001 0101 1011 : lev 6: */ tmp1=FFAND(FFOR(fb,fd),FFOR(FFAND(fa,fc),FFXOR(fb,FFXOR(fc,fd)))); -/* 0100 1001 1011 1001 : lev 5: */ tmp2=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fb,fd)),fd)); -/* 1111 1111 1101 1101 : lev 3: */ tmp3=FFOR(fd,FFXOR(FFAND(fa,fc),FF1())); - s7a=FFXOR(tmp0,FFAND(fe,tmp1)); - s7b=FFXOR(tmp2,FFAND(fe,tmp3)); - - -/* - we have just done this: - - int sbox1[0x20] = {2,0,1,1,2,3,3,0, 3,2,2,0,1,1,0,3, 0,3,3,0,2,2,1,1, 2,2,0,3,1,1,3,0}; - int sbox2[0x20] = {3,1,0,2,2,3,3,0, 1,3,2,1,0,0,1,2, 3,1,0,3,3,2,0,2, 0,0,1,2,2,1,3,1}; - int sbox3[0x20] = {2,0,1,2,2,3,3,1, 1,1,0,3,3,0,2,0, 1,3,0,1,3,0,2,2, 2,0,1,2,0,3,3,1}; - int sbox4[0x20] = {3,1,2,3,0,2,1,2, 1,2,0,1,3,0,0,3, 1,0,3,1,2,3,0,3, 0,3,2,0,1,2,2,1}; - int sbox5[0x20] = {2,0,0,1,3,2,3,2, 0,1,3,3,1,0,2,1, 2,3,2,0,0,3,1,1, 1,0,3,2,3,1,0,2}; - int sbox6[0x20] = {0,1,2,3,1,2,2,0, 0,1,3,0,2,3,1,3, 2,3,0,2,3,0,1,1, 2,1,1,2,0,3,3,0}; - int sbox7[0x20] = {0,3,2,2,3,0,0,1, 3,0,1,3,1,2,2,1, 1,0,3,3,0,1,1,2, 2,3,1,0,2,3,0,2}; - - s12 = sbox1[ (((A3>>0)&1)<<4) | (((A0>>2)&1)<<3) | (((A5>>1)&1)<<2) | (((A6>>3)&1)<<1) | (((A8>>0)&1)<<0) ] - |sbox2[ (((A1>>1)&1)<<4) | (((A2>>2)&1)<<3) | (((A5>>3)&1)<<2) | (((A6>>0)&1)<<1) | (((A8>>1)&1)<<0) ]; - s34 = sbox3[ (((A0>>3)&1)<<4) | (((A1>>0)&1)<<3) | (((A4>>1)&1)<<2) | (((A4>>3)&1)<<1) | (((A5>>2)&1)<<0) ] - |sbox4[ (((A2>>3)&1)<<4) | (((A0>>1)&1)<<3) | (((A1>>3)&1)<<2) | (((A3>>2)&1)<<1) | (((A7>>0)&1)<<0) ]; - s56 = sbox5[ (((A4>>2)&1)<<4) | (((A3>>3)&1)<<3) | (((A5>>0)&1)<<2) | (((A7>>1)&1)<<1) | (((A8>>2)&1)<<0) ] - |sbox6[ (((A2>>1)&1)<<4) | (((A3>>1)&1)<<3) | (((A4>>0)&1)<<2) | (((A6>>2)&1)<<1) | (((A8>>3)&1)<<0) ]; - s7 = sbox7[ (((A1>>2)&1)<<4) | (((A2>>0)&1)<<3) | (((A6>>1)&1)<<2) | (((A7>>2)&1)<<1) | (((A7>>3)&1)<<0) ]; -*/ - - // use 4x4 xor to produce extra nibble for T3 - - extra_B[3]=FFXOR(FFXOR(FFXOR(regs->B[aboff+2][0],regs->B[aboff+5][1]),regs->B[aboff+6][2]),regs->B[aboff+8][3]); - extra_B[2]=FFXOR(FFXOR(FFXOR(regs->B[aboff+5][0],regs->B[aboff+7][1]),regs->B[aboff+2][3]),regs->B[aboff+3][2]); - extra_B[1]=FFXOR(FFXOR(FFXOR(regs->B[aboff+4][3],regs->B[aboff+7][2]),regs->B[aboff+3][0]),regs->B[aboff+4][1]); - extra_B[0]=FFXOR(FFXOR(FFXOR(regs->B[aboff+8][2],regs->B[aboff+5][3]),regs->B[aboff+2][1]),regs->B[aboff+7][0]); -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"extra_B[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)&extra_B[dbg],BYPG,BYPG)); -} - - // T1 = xor all inputs - // in1, in2, D are only used in T1 during initialisation, not generation - for(b=0;b<4;b++){ - regs->A[aboff-1][b]=FFXOR(regs->A[aboff+9][b],regs->X[b]); - } - -#ifdef STREAM_INIT - for(b=0;b<4;b++){ - regs->A[aboff-1][b]=FFXOR(FFXOR(regs->A[aboff-1][b],regs->D[b]),((j % 2) ? in2[b] : in1[b])); - } -#endif - -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"next_A0[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->A[aboff-1][dbg],BYPG,BYPG)); -} - - // T2 = xor all inputs - // in1, in2 are only used in T1 during initialisation, not generation - // if p=0, use this, if p=1, rotate the result left - for(b=0;b<4;b++){ - regs->B[aboff-1][b]=FFXOR(FFXOR(regs->B[aboff+6][b],regs->B[aboff+9][b]),regs->Y[b]); - } - -#ifdef STREAM_INIT - for(b=0;b<4;b++){ - regs->B[aboff-1][b]=FFXOR(regs->B[aboff-1][b],((j % 2) ? in1[b] : in2[b])); - } -#endif - -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"next_B0[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->B[aboff-1][dbg],BYPG,BYPG)); -} - - // if p=1, rotate left (yes, this is what we're doing) - tmp3=regs->B[aboff-1][3]; - regs->B[aboff-1][3]=FFXOR(regs->B[aboff-1][3],FFAND(FFXOR(regs->B[aboff-1][3],regs->B[aboff-1][2]),regs->p)); - regs->B[aboff-1][2]=FFXOR(regs->B[aboff-1][2],FFAND(FFXOR(regs->B[aboff-1][2],regs->B[aboff-1][1]),regs->p)); - regs->B[aboff-1][1]=FFXOR(regs->B[aboff-1][1],FFAND(FFXOR(regs->B[aboff-1][1],regs->B[aboff-1][0]),regs->p)); - regs->B[aboff-1][0]=FFXOR(regs->B[aboff-1][0],FFAND(FFXOR(regs->B[aboff-1][0],tmp3),regs->p)); - -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"next_B0[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->B[aboff-1][dbg],BYPG,BYPG)); -} - - // T3 = xor all inputs - for(b=0;b<4;b++){ - regs->D[b]=FFXOR(FFXOR(regs->E[b],regs->Z[b]),extra_B[b]); - } - -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"D[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->D[dbg],BYPG,BYPG)); -} - - // T4 = sum, carry of Z + E + r - for(b=0;b<4;b++){ - next_E[b]=regs->F[b]; - } - - tmp0=FFXOR(regs->Z[0],regs->E[0]); - tmp1=FFAND(regs->Z[0],regs->E[0]); - regs->F[0]=FFXOR(regs->E[0],FFAND(regs->q,FFXOR(regs->Z[0],regs->r))); - tmp3=FFAND(tmp0,regs->r); - tmp4=FFOR(tmp1,tmp3); - - tmp0=FFXOR(regs->Z[1],regs->E[1]); - tmp1=FFAND(regs->Z[1],regs->E[1]); - regs->F[1]=FFXOR(regs->E[1],FFAND(regs->q,FFXOR(regs->Z[1],tmp4))); - tmp3=FFAND(tmp0,tmp4); - tmp4=FFOR(tmp1,tmp3); - - tmp0=FFXOR(regs->Z[2],regs->E[2]); - tmp1=FFAND(regs->Z[2],regs->E[2]); - regs->F[2]=FFXOR(regs->E[2],FFAND(regs->q,FFXOR(regs->Z[2],tmp4))); - tmp3=FFAND(tmp0,tmp4); - tmp4=FFOR(tmp1,tmp3); - - tmp0=FFXOR(regs->Z[3],regs->E[3]); - tmp1=FFAND(regs->Z[3],regs->E[3]); - regs->F[3]=FFXOR(regs->E[3],FFAND(regs->q,FFXOR(regs->Z[3],tmp4))); - tmp3=FFAND(tmp0,tmp4); - regs->r=FFXOR(regs->r,FFAND(regs->q,FFXOR(FFOR(tmp1,tmp3),regs->r))); // ultimate carry - -/* - we have just done this: (believe it or not) - - if (q) { - F = Z + E + r; - r = (F >> 4) & 1; - F = F & 0x0f; - } - else { - F = E; - } -*/ - for(b=0;b<4;b++){ - regs->E[b]=next_E[b]; - } -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"F[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->F[dbg],BYPG,BYPG)); -} -DBG(fprintf(stderr,"r=")); -DBG(dump_mem("",(unsigned char *)®s->r,BYPG,BYPG)); -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"E[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->E[dbg],BYPG,BYPG)); -} - - // this simple instruction is virtually shifting all the shift registers - aboff--; - -/* - we've just done this: - - A9=A8;A8=A7;A7=A6;A6=A5;A5=A4;A4=A3;A3=A2;A2=A1;A1=A0;A0=next_A0; - B9=B8;B8=B7;B7=B6;B6=B5;B5=B4;B4=B3;B3=B2;B2=B1;B1=B0;B0=next_B0; -*/ - - regs->X[0]=s1a; - regs->X[1]=s2a; - regs->X[2]=s3b; - regs->X[3]=s4b; - regs->Y[0]=s3a; - regs->Y[1]=s4a; - regs->Y[2]=s5b; - regs->Y[3]=s6b; - regs->Z[0]=s5a; - regs->Z[1]=s6a; - regs->Z[2]=s1b; - regs->Z[3]=s2b; - regs->p=s7a; - regs->q=s7b; -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"X[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->X[dbg],BYPG,BYPG)); -} -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"Y[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->Y[dbg],BYPG,BYPG)); -} -for(dbg=0;dbg<4;dbg++){ - DBG(fprintf(stderr,"Z[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)®s->Z[dbg],BYPG,BYPG)); -} -DBG(fprintf(stderr,"p=")); -DBG(dump_mem("",(unsigned char *)®s->p,BYPG,BYPG)); -DBG(fprintf(stderr,"q=")); -DBG(dump_mem("",(unsigned char *)®s->q,BYPG,BYPG)); - -#ifdef STREAM_NORMAL - // require 4 loops per output byte - // 2 output bits are a function of the 4 bits of D - // xor 2 by 2 - cb_g[8*i+7-2*j]=FFXOR(regs->D[2],regs->D[3]); - cb_g[8*i+6-2*j]=FFXOR(regs->D[0],regs->D[1]); -for(dbg=0;dbg<8;dbg++){ - DBG(fprintf(stderr,"op[%i]=",dbg)); - DBG(dump_mem("",(unsigned char *)&cb_g[8*i+dbg],BYPG,BYPG)); -} -#endif - -DBG(fprintf(stderr,"---END INTERNAL LOOP\n")); - - } // INTERNAL LOOP - -DBG(fprintf(stderr,"--END EXTERNAL LOOP\n")); - - } // EXTERNAL LOOP - - // move 32 steps forward, ready for next call - for(k=0;k<10;k++){ - for(b=0;b<4;b++){ -DBG(fprintf(stderr,"moving forward AB k=%i b=%i\n",k,b)); - regs->A[32+k][b]=regs->A[k][b]; - regs->B[32+k][b]=regs->B[k][b]; - } - } - - -//////////////////////////////////////////////////////////////////////////////// - -#ifdef STREAM_NORMAL -for(j=0;j<64;j++){ - DBG(fprintf(stderr,"postcall prerot cb[%2i]=",j)); - DBG(dump_mem("",(unsigned char *)(cb+BYPG*j),BYPG,BYPG)); -} - -#if GROUP_PARALLELISM==32 -trasp64_32_88cw(cb); -#endif -#if GROUP_PARALLELISM==64 -trasp64_64_88cw(cb); -#endif -#if GROUP_PARALLELISM==128 -trasp64_128_88cw(cb); -#endif - -for(j=0;j<64;j++){ - DBG(fprintf(stderr,"postcall postrot cb[%2i]=",j)); - DBG(dump_mem("",(unsigned char *)(cb+BYPG*j),BYPG,BYPG)); -} -#endif - -#ifdef STREAM_INIT - DBG(fprintf(stderr,":::::::::: END STREAM INIT\n")); -#endif -#ifdef STREAM_NORMAL - DBG(fprintf(stderr,":::::::::: END STREAM NORMAL\n")); -#endif - -} - diff --git a/globals.h b/globals.h index cc5690348..4d130e794 100644 --- a/globals.h +++ b/globals.h @@ -368,7 +368,7 @@ /* =========================== * constants * =========================== */ -#define CS_VERSION "1.20_svn" +#define CS_VERSION "1.30_svn" #ifndef CS_SVN_VERSION # define CS_SVN_VERSION "test" #endif @@ -628,6 +628,7 @@ enum {E2_GLOBAL = 0, E2_GROUP, E2_CAID, E2_IDENT, E2_CLASS, E2_CHID, E2_QUEUE, E #define CW_ALGO_CSA 0 #define CW_ALGO_DES 1 #define CW_ALGO_AES128 2 +#define CW_ALGO_CSA_ALT 3 #define CW_ALGO_MODE_ECB 0 #define CW_ALGO_MODE_CBC 1 @@ -1556,6 +1557,7 @@ struct s_reader // contains device info, reader info and card info int32_t resetcounter; // actual count uint32_t auprovid; // AU only for this provid int8_t audisabled; // exclude reader from auto AU + int8_t autype; int8_t needsemmfirst; // 0: reader descrambles without emm first, 1: reader needs emms before it can descramble struct timeb emm_last; // time of last successfully written emm int8_t smargopatch; @@ -1574,27 +1576,56 @@ struct s_reader // contains device info, reader info and card info #ifdef READER_CRYPTOWORKS int8_t needsglobalfirst; // 0:Write one Global EMM for SHARED EMM disabled 1:Write one Global EMM for SHARED EMM enabled #endif -#if defined(READER_NAGRA_MERLIN) || defined(READER_NAGRA) - uint8_t nuid[4]; - uint8_t nuid_length; - uint8_t cwekey[16]; - uint8_t cwekey_length; +#if defined(READER_NAGRA) + uint8_t cak63nuid[4]; + uint8_t cak63nuid_length; + uint8_t cak63cwekey[16]; + uint8_t cak63cwekey_length; #endif #ifdef READER_NAGRA_MERLIN - uint8_t irdid[4]; - uint8_t irdid_length; - uint8_t public_exponent[3]; - uint8_t public_exponent_length; uint8_t mod1[112]; uint8_t mod1_length; + uint8_t cmd0eprov[2]; + uint8_t cmd0eprov_length; + uint8_t mod2[112]; + uint8_t mod2_length; + uint8_t tmprsa[112]; uint8_t data50[80]; uint8_t data50_length; uint8_t mod50[80]; uint8_t mod50_length; + uint8_t key3588[136]; + uint8_t key3588_length; uint8_t key60[96]; - uint8_t key60_length; uint8_t exp60[96]; - uint8_t exp60_length; + uint8_t key68[104]; + uint8_t exp68[104]; + uint8_t key3des[16]; + uint8_t klucz68[24]; + uint8_t pairtype; + uint8_t hasunique; + uint8_t key3460[96]; + uint8_t key3460_length; + uint8_t key3310[16]; + uint8_t key3310_length; + uint8_t cwekey0[16]; + uint8_t cwekey0_length; + uint8_t cwekey1[16]; + uint8_t cwekey1_length; + uint8_t cwekey2[16]; + uint8_t cwekey2_length; + uint8_t cwekey3[16]; + uint8_t cwekey3_length; + uint8_t cwekey4[16]; + uint8_t cwekey4_length; + uint8_t cwekey5[16]; + uint8_t cwekey5_length; + uint8_t cwekey6[16]; + uint8_t cwekey6_length; + uint8_t cwekey7[16]; + uint8_t cwekey7_length; + uint8_t idird[4]; + uint8_t idird_length; uint8_t kdt05_00[216]; uint8_t kdt05_10[208]; uint8_t cardid[8]; @@ -1605,7 +1636,12 @@ struct s_reader // contains device info, reader info and card info uint8_t block3[8]; uint8_t v[8]; uint8_t iout[8]; + uint32_t dword_83DBC; uint8_t data2[4]; + uint8_t ecmheader[4]; + uint8_t timestmp1[4]; + uint8_t timestmp2[4]; + uint8_t cak7expo[0x11]; uint8_t data[0x80]; uint8_t step1[0x60]; uint8_t step2[0x68]; @@ -1614,12 +1650,22 @@ struct s_reader // contains device info, reader info and card info uint8_t result[104]; uint8_t stillencrypted[0x50]; uint8_t resultrsa[0x50]; - uint32_t cak7_restart; uint32_t cak7_seq; + uint32_t needrestart; + uint8_t otpcsc[2]; + uint8_t otpcsc_length; + uint8_t otacsc[2]; + uint8_t otacsc_length; + uint8_t forcepair[1]; + uint8_t forcepair_length; uint8_t cak7_camstate; uint8_t cak7_aes_key[32]; uint8_t cak7_aes_iv[16]; - struct timeb last_refresh; + int8_t forcecwswap; + int8_t evensa; + int8_t forceemmg; + int8_t cwpkota; + #endif #ifdef CS_CACHEEX CECSP cacheex; // CacheEx Settings @@ -1644,6 +1690,12 @@ struct s_reader // contains device info, reader info and card info int32_t l_port; CAIDTAB ctab; uint32_t boxid; + int8_t cak7_mode; + uint8_t cak7type; + uint8_t cwpkcaid[2]; + uint8_t cwpkcaid_length; + uint8_t nuid[4]; + uint8_t nuid_length; int8_t nagra_read; // read nagra ncmed records: 0 Disabled (default), 1 read all records, 2 read valid records only int8_t detect_seca_nagra_tunneled_card; int8_t force_irdeto; @@ -1651,6 +1703,8 @@ struct s_reader // contains device info, reader info and card info uint8_t boxkey_length; uint8_t rsa_mod[120]; // rsa modulus for nagra cards. uint8_t rsa_mod_length; + uint8_t cwpk_mod[16]; // cwpk modulus for conax cards. + uint8_t cwpk_mod_length; uint8_t des_key[128]; // 3des key for Viaccess 16 bytes, des key for Dre 128 bytes uint8_t des_key_length; uint8_t atr[64]; @@ -1662,8 +1716,18 @@ struct s_reader // contains device info, reader info and card info SIDTABS lb_sidtabs; uint8_t hexserial[8]; int32_t nprov; + int32_t nsa; + int32_t nemm84; + int32_t nemm83u; + int32_t nemm83s; + int32_t nemm87; uint8_t prid[CS_MAXPROV][8]; uint8_t sa[CS_MAXPROV][4]; // viaccess & seca + uint8_t emm84[CS_MAXPROV][3]; + uint8_t emm83u[CS_MAXPROV][6]; + uint8_t emm83s[CS_MAXPROV][6]; + uint8_t emm87[CS_MAXPROV][6]; + uint8_t emm82; uint8_t read_old_classes; // viaccess uint8_t maturity; // viaccess & seca maturity level uint16_t caid; @@ -2338,6 +2402,9 @@ struct s_config int8_t dvbapi_read_sdt; int8_t dvbapi_write_sdt_prov; int8_t dvbapi_extended_cw_api; +#ifdef MODULE_STREAMRELAY + int8_t dvbapi_demuxer_fix; +#endif #endif #ifdef CS_ANTICASC @@ -2386,16 +2453,23 @@ struct s_config struct s_ip *scam_allowed; #endif -#ifdef WITH_EMU - char *emu_stream_source_host; - int32_t emu_stream_source_port; - char *emu_stream_source_auth_user; - char *emu_stream_source_auth_password; - int32_t emu_stream_relay_port; - uint32_t emu_stream_ecm_delay; - int8_t emu_stream_relay_enabled; - int8_t emu_stream_emm_enabled; - CAIDTAB emu_stream_relay_ctab; // use the stream server for these caids +#ifdef MODULE_STREAMRELAY + char *stream_source_host; + int32_t stream_source_port; + char *stream_source_auth_user; + char *stream_source_auth_password; + int32_t stream_relay_port; +#ifdef MODULE_RADEGAST + int8_t stream_client_source_host; +#endif + int8_t stream_relay_enabled; + uint32_t stream_relay_buffer_time; + CAIDTAB stream_relay_ctab; // use the stream server for these caids +#ifdef WITH_NEUTRINO +#define DEFAULT_STREAM_SOURCE_PORT 31339 //Neutrino +#else +#define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2 +#endif #endif int32_t max_cache_time; // seconds ecms are stored in ecmcwcache diff --git a/module-dvbapi.c b/module-dvbapi.c index 82a4f490b..ac48833e7 100644 --- a/module-dvbapi.c +++ b/module-dvbapi.c @@ -12,6 +12,7 @@ #include "module-dvbapi-stapi.h" #include "module-dvbapi-chancache.h" #include "module-stat.h" +#include "module-streamrelay.h" #include "oscam-chk.h" #include "oscam-client.h" #include "oscam-config.h" @@ -1248,6 +1249,14 @@ static int32_t dvbapi_detect_api(void) maxfilter = filtercount; cs_log("Detected %s Api: %d, userconfig boxtype: %d maximum number of filters is %d (oscam limit is %d)", device_path, selected_api, cfg.dvbapi_boxtype, filtercount, MAX_FILTER); + +#ifdef MODULE_STREAMRELAY + // Log enabled demuxer fix + if(cfg.dvbapi_demuxer_fix) + { + cs_log("Demuxer fix enabled, try fixing stream relay audio/video sync..."); + } +#endif } // try at least 8 adapters @@ -6830,10 +6839,24 @@ static void *dvbapi_main_local(void *cli) } // count ecm filters to see if demuxing is possible anyway - if(demux[i].demux_fd[g].type == TYPE_ECM) +#ifdef MODULE_STREAMRELAY + if(cfg.dvbapi_demuxer_fix) { - ecmcounter++; + if(demux[i].demux_fd[g].type == TYPE_ECM || demux[i].demux_fd[g].type == 3 || demux[i].demux_fd[g].type == 6) + { + ecmcounter++; + } } + else + { +#endif + if(demux[i].demux_fd[g].type == TYPE_ECM) + { + ecmcounter++; + } +#ifdef MODULE_STREAMRELAY + } +#endif // count emm filters also if(demux[i].demux_fd[g].type == TYPE_EMM) @@ -7502,6 +7525,25 @@ void delayer(ECM_REQUEST *er, uint32_t delay) } } +#ifdef WITH_EXTENDED_CW +bool caid_is_csa_alt(uint16_t caid) +{ + return caid == 0x09c4 || caid == 0x098c || caid==0x098d; +} + +bool select_csa_alt(ECM_REQUEST *er) +{ + if(caid_is_csa_alt(er->caid)) + { + if((er->ecm[2] - er->ecm[4]) == 4) + { + return true; + } + } + return false; +} +#endif + void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er) { int32_t i, j, k, handled = 0; @@ -7849,8 +7891,14 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er) delayer(er, delay); -#ifdef WITH_EMU - if(!chk_ctab_ex(er->caid, &cfg.emu_stream_relay_ctab) || !cfg.emu_stream_relay_enabled) +#ifdef MODULE_STREAMRELAY + bool set_dvbapi_cw = true; + if(chk_ctab_ex(er->caid, &cfg.stream_relay_ctab) && cfg.stream_relay_enabled) + { + // streamserver set cw + set_dvbapi_cw = !stream_write_cw(er); + } + if (set_dvbapi_cw) #endif switch(selected_api) { @@ -7920,6 +7968,14 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er) { dvbapi_write_cw(i, j, 0, er->cw_ex.session_word, 16, er->cw_ex.data, 16, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); } + else if(er->cw_ex.algo == CW_ALGO_CSA) + { + if(select_csa_alt(er)) + { + er->cw_ex.algo = CW_ALGO_CSA_ALT; + } + dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + } else { dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); diff --git a/module-emulator-osemu.c b/module-emulator-osemu.c index e063be06a..786bee270 100644 --- a/module-emulator-osemu.c +++ b/module-emulator-osemu.c @@ -5,8 +5,8 @@ #ifdef WITH_EMU #include "oscam-string.h" +#include "module-streamrelay.h" #include "module-emulator-osemu.h" -#include "module-emulator-streamserver.h" #include "module-emulator-biss.h" #include "module-emulator-cryptoworks.h" #include "module-emulator-director.h" @@ -929,7 +929,14 @@ int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, 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)) result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens, NULL); + 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); diff --git a/module-emulator-powervu.c b/module-emulator-powervu.c index 53da749f7..478fdf4e0 100644 --- a/module-emulator-powervu.c +++ b/module-emulator-powervu.c @@ -5,9 +5,8 @@ #ifdef WITH_EMU #include "cscrypt/des.h" -#include "ffdecsa/ffdecsa.h" +#include "module-streamrelay.h" #include "module-emulator-osemu.h" -#include "module-emulator-streamserver.h" #include "module-emulator-powervu.h" #include "oscam-string.h" #include "oscam-time.h" @@ -649,7 +648,7 @@ static void create_hash(uint8_t *data, int len, uint8_t *hash, int mode) 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; @@ -1866,8 +1865,11 @@ static void calculate_cw(uint8_t seedType, uint8_t *seed, uint8_t csaUsed, uint8 } } -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) +#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; @@ -1884,12 +1886,15 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid //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; @@ -2068,7 +2073,7 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid while (!decrypt_ok); memcpy(seedBase, ecm + i + 6 + 2, 4); - +#ifdef MODULE_STREAMRELAY if (cdata == NULL) { SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); @@ -2084,7 +2089,9 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid } 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++) @@ -2123,7 +2130,7 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid //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++) @@ -2146,25 +2153,19 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid if (cdata != NULL) { - for (j = 0; j < 8; j++) + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++) { if (csaUsed) { - if (cdata->pvu_csa_ks[j] == NULL) - { - cdata->pvu_csa_ks[j] = get_key_struct(); - } - if (ecm[0] == 0x80) { - set_even_control_word(cdata->pvu_csa_ks[j], cw[j]); - } + dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][EVEN]); } else { - set_odd_control_word(cdata->pvu_csa_ks[j], cw[j]); + dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][ODD]); } - cdata->pvu_csa_used = 1; + cdata->csa_used = 1; } else { @@ -2177,11 +2178,11 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid des_set_key(cw[j], cdata->pvu_des_ks[j][1]); } - cdata->pvu_csa_used = 0; + cdata->csa_used = 0; } } } - +#endif if (cw_ex != NULL) { cw_ex->mode = CW_MODE_MULTIPLE_CW; @@ -2197,7 +2198,7 @@ int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid cw_ex->algo_mode = CW_ALGO_MODE_ECB; } - for (j = 0; j < 4; j++) + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS; j++) { memset(cw_ex->audio[j], 0, 16); diff --git a/module-emulator-powervu.h b/module-emulator-powervu.h index 369be5e1b..b50d20887 100644 --- a/module-emulator-powervu.h +++ b/module-emulator-powervu.h @@ -21,7 +21,11 @@ #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); /* diff --git a/module-emulator-streamserver.c b/module-emulator-streamserver.c deleted file mode 100644 index a828a4f49..000000000 --- a/module-emulator-streamserver.c +++ /dev/null @@ -1,1812 +0,0 @@ -#define MODULE_LOG_PREFIX "emu" - -#include "globals.h" - -#ifdef WITH_EMU - -#include "cscrypt/des.h" -#include "ffdecsa/ffdecsa.h" -#include "module-emulator-osemu.h" -#include "module-emulator-streamserver.h" -#include "module-emulator-powervu.h" -#include "oscam-config.h" -#include "oscam-net.h" -#include "oscam-string.h" -#include "oscam-time.h" -#include "oscam-chk.h" - -#define STREAM_UNDEFINED 0x00 -#define STREAM_VIDEO 0x01 -#define STREAM_AUDIO 0x02 -#define STREAM_SUBTITLE 0x03 -#define STREAM_TELETEXT 0x04 - -extern int32_t exit_oscam; - -typedef struct -{ - int32_t connfd; - int32_t connid; -} emu_stream_client_conn_data; - -int8_t stream_server_thread_init = 0; -char emu_stream_source_host[256] = {"127.0.0.1"}; -int32_t emu_stream_source_port = 8001; -char *emu_stream_source_auth = NULL; -int32_t emu_stream_relay_port = 17999; -int8_t emu_stream_emm_enabled = 0; -uint32_t cluster_size = 50; - -static uint8_t emu_stream_server_mutex_init = 0; -static pthread_mutex_t emu_stream_server_mutex; -static int32_t glistenfd, gconncount = 0, gconnfd[EMU_STREAM_SERVER_MAX_CONNECTIONS]; - -pthread_mutex_t emu_fixed_key_srvid_mutex; -uint16_t emu_stream_cur_srvid[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -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]; - -static void SearchTsPackets(uint8_t *buf, uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset) -{ - uint32_t i; - - (*packetSize) = 0; - (*startOffset) = 0; - - for (i = 0; i < bufLength; i++) - { - if (buf[i] == 0x47) - { - // if three packets align, probably safe to assume correct size - if ((buf[i + 188] == 0x47) & (buf[i + 376] == 0x47)) - { - (*packetSize) = 188; - (*startOffset) = i; - return; - } - else if ((buf[i + 204] == 0x47) & (buf[i + 408] == 0x47)) - { - (*packetSize) = 204; - (*startOffset) = i; - return; - } - else if ((buf[i + 208] == 0x47) & (buf[i + 416] == 0x47)) - { - (*packetSize) = 208; - (*startOffset) = i; - return; - } - } - } -} - -typedef void (*ts_data_callback)(emu_stream_client_data *cdata); - -static void ParseTsData(uint8_t table_id, uint8_t table_mask, uint8_t min_table_length, int8_t *flag, - uint8_t *data, uint16_t data_length, uint16_t *data_pos, int8_t payloadStart, - uint8_t *buf, int32_t len, ts_data_callback func, emu_stream_client_data *cdata) -{ - int8_t found_start = 0; - int32_t free_data_length, copySize, i; - uint16_t section_length, offset = 0; - - if (len < 1) - { - return; - } - - if (*flag == 0 && !payloadStart) - { - return; - } - - if (*flag == 0) - { - *data_pos = 0; - offset = 1 + buf[0]; - } - else if (payloadStart) - { - offset = 1; - } - - if (len - offset < 1) - { - return; - } - - free_data_length = data_length - *data_pos; - copySize = (len - offset) > free_data_length ? free_data_length : (len - offset); - - memcpy(data + (*data_pos), buf + offset, copySize); - (*data_pos) += copySize; - - found_start = 0; - for (i = 0; i < *data_pos; i++) - { - if ((data[i] & table_mask) == table_id) - { - if (i != 0) - { - if ((*data_pos) - i > i) - { - memmove(data, &data[i], (*data_pos) - i); - } - else - { - memcpy(data, &data[i], (*data_pos) - i); - } - - *data_pos -= i; - } - found_start = 1; - break; - } - } - - if (!found_start) - { - *flag = 0; - return; - } - - *flag = 2; - - if (*data_pos < 3) - { - return; - } - - section_length = SCT_LEN(data); - - if (section_length > data_length || section_length < min_table_length) - { - *flag = 0; - return; - } - - if ((*data_pos) < section_length) - { - return; - } - - func(cdata); - - found_start = 0; - for (i = section_length; i < *data_pos; i++) - { - if ((data[i] & table_mask) == table_id) - { - if ((*data_pos) - i > i) - { - memmove(data, &data[i], (*data_pos) - i); - } - else - { - memcpy(data, &data[i], (*data_pos) - i); - } - - *data_pos -= i; - found_start = 1; - break; - } - } - - if (!found_start) - { - *data_pos = 0; - } - - *flag = 1; -} - -static void ParsePatData(emu_stream_client_data *cdata) -{ - int32_t i; - uint8_t *data = cdata->pat_data; - uint16_t srvid, section_length = SCT_LEN(data); - - for (i = 8; i + 7 < section_length; i += 4) - { - srvid = b2i(2, data + i); - if (srvid == 0) - { - continue; - } - - if (cdata->srvid == srvid) - { - cdata->pmt_pid = b2i(2, data + i + 2) & 0x1FFF; - cs_log_dbg(D_READER, "Stream client %i found pmt pid: 0x%04X (%i)", - cdata->connid, cdata->pmt_pid, cdata->pmt_pid); - break; - } - } -} - -static int8_t stream_client_get_caid(emu_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; -} - -static void ParseDescriptors(uint8_t *buffer, uint16_t info_length, uint8_t *type) -{ - uint8_t descriptor_tag = buffer[0], descriptor_length = 0; - uint32_t j, k; - - if (info_length < 1) - { - return; - } - - for (j = 0; j + 1 < info_length; j += descriptor_length + 2) - { - descriptor_tag = buffer[j]; - descriptor_length = buffer[j + 1]; - - switch (descriptor_tag) - { - case 0x05: // Registration descriptor - { - // "HDMV" format identifier is removed - // OSCam does not need to know about Blu-ray - const char format_identifiers_audio[10][5] = - { - "AC-3", "BSSD", "dmat", "DRA1", "DTS1", - "DTS2", "DTS3", "EAC3", "mlpa", "Opus", - }; - - for (k = 0; k < 10; k++) - { - if (memcmp(buffer + j + 2, format_identifiers_audio[k], 4) == 0) - { - *type = STREAM_AUDIO; - break; - } - } - break; - } - - //case 0x09: // CA descriptor - //{ - // break; - //} - - case 0x46: // VBI teletext descriptor (DVB) - case 0x56: // teletext descriptor (DVB) - { - *type = STREAM_TELETEXT; - break; - } - - case 0x59: // subtitling descriptor (DVB) - { - *type = STREAM_SUBTITLE; - break; - } - - case 0x6A: // AC-3 descriptor (DVB) - case 0x7A: // enhanced AC-3 descriptor (DVB) - case 0x7B: // DTS descriptor (DVB) - case 0x7C: // AAC descriptor (DVB) - case 0x81: // AC-3 descriptor (ATSC) - case 0xCC: // Enhanced AC-3 descriptor (ATSC) - { - *type = STREAM_AUDIO; - break; - } - - case 0x7F: // extension descriptor (DVB) - { - uint8_t extension_descriptor_tag = buffer[j + 2]; - - switch (extension_descriptor_tag) - { - case 0x0E: // DTS-HD descriptor (DVB) - case 0x0F: // DTS Neural descriptor (DVB) - case 0x15: // AC-4 descriptor (DVB) - *type = STREAM_AUDIO; - break; - - case 0x20: // TTML subtitling descriptor (DVB) - *type = STREAM_SUBTITLE; - break; - - default: - *type = STREAM_UNDEFINED; - break; - } - break; - } - - default: - break; - } - } -} - -static void ParsePmtData(emu_stream_client_data *cdata) -{ - int32_t i; - uint8_t *data = cdata->pmt_data; - uint8_t descriptor_tag = 0, descriptor_length = 0, stream_type; - uint16_t program_info_length = 0, es_info_length = 0, elementary_pid, caid; - uint16_t section_length = SCT_LEN(data); - - cdata->pcr_pid = b2i(2, data + 8) & 0x1FFF; - if (cdata->pcr_pid != 0x1FFF) - { - cs_log_dbg(D_READER, "Stream client %i found pcr pid: 0x%04X (%i)", - cdata->connid, cdata->pcr_pid, cdata->pcr_pid); - } - - program_info_length = b2i(2, data + 10) & 0xFFF; - - if (12 + program_info_length >= section_length) - { - return; - } - - // parse program descriptors (we are looking only for CA descriptor here) - for (i = 12; i + 1 < 12 + program_info_length; i += descriptor_length + 2) - { - descriptor_tag = data[i]; - descriptor_length = data[i + 1]; - - if (descriptor_length < 1) - { - break; - } - - if (i + 1 + descriptor_length >= 12 + program_info_length) - { - break; - } - - if (descriptor_tag == 0x09 && descriptor_length >= 4) - { - caid = b2i(2, data + i + 2); - - if (caid_is_powervu(caid) || caid == 0xA101) // add all supported caids here - { - if (cdata->caid == NO_CAID_VALUE) - { - cdata->caid = caid; - } - cdata->ecm_pid = b2i(2, data + i + 4) & 0x1FFF; - cs_log_dbg(D_READER, "Stream client %i found ecm pid: 0x%04X (%i)", - cdata->connid, cdata->ecm_pid, cdata->ecm_pid); - break; - } - } - } - - for (i = 12 + program_info_length; i + 4 < section_length; i += 5 + es_info_length) - { - stream_type = data[i]; - elementary_pid = b2i(2, data + i + 1) & 0x1FFF; - es_info_length = b2i(2, data + i + 3) & 0xFFF; - - switch (stream_type) - { - case 0x01: - case 0x02: - case 0x10: - case 0x1B: - case 0x20: - case 0x24: - case 0x25: - case 0x42: - case 0xD1: - case 0xEA: - { - cdata->video_pid = elementary_pid; - cs_log_dbg(D_READER, "Stream client %i found video pid: 0x%04X (%i)", - cdata->connid, elementary_pid, elementary_pid); - break; - } - - case 0x03: - case 0x04: - case 0x0F: - case 0x11: - case 0x1C: - case 0x2D: - case 0x2E: - case 0x81: - { - 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++; - cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", - cdata->connid, elementary_pid, elementary_pid); - break; - } - - case 0x06: - //case 0x81: // some ATSC AC-3 streams do not contain the AC-3 descriptor! - case 0x87: - { - uint8_t type = STREAM_UNDEFINED; - ParseDescriptors(data + i + 5, es_info_length, &type); - - if (type == STREAM_AUDIO) - { - 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++; - 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) - { - cdata->teletext_pid = elementary_pid; - cs_log_dbg(D_READER, "Stream client %i found teletext pid: 0x%04X (%i)", - cdata->connid, elementary_pid, elementary_pid); - } - break; - } - } - } - - // 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); - } -} - -static void ParseCatData(emu_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(emu_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); - } -} - -static void ParseEcmData(emu_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) - { - return; - } - - 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]; - powervu_ecm(data, dcw, NULL, cdata->srvid, cdata->caid, cdata->tsid, cdata->onid, cdata->ens, &cdata->key); - } - } - //else if () // All other caids - //{ - //emu_process_ecm(); - //} -} - -static void ParseTsPackets(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) -{ - uint8_t payloadStart; - 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; - payloadStart = (tsHeader & 0x400000) >> 22; - - if (tsHeader & 0x20) - { - offset = 4 + stream_buf[i + 4] + 1; - } - else - { - offset = 4; - } - - if (packetSize - offset < 1) - { - continue; - } - - if (pid == 0x0000 && data->have_pat_data != 1) // Search the PAT for the PMT pid - { - ParseTsData(0x00, 0xFF, 16, &data->have_pat_data, data->pat_data, sizeof(data->pat_data), - &data->pat_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParsePatData, data); - continue; - } - - if (pid == data->pmt_pid && data->have_pmt_data != 1) // Search the PMT for PCR, ECM, Video and Audio pids - { - ParseTsData(0x02, 0xFF, 21, &data->have_pmt_data, data->pmt_data, sizeof(data->pmt_data), - &data->pmt_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParsePmtData, data); - continue; - } - - // We have bot PAT and PMT data - No need to search the rest of the packets - if (data->have_pat_data == 1 && data->have_pmt_data == 1) - { - break; - } - } -} - -static void DescrambleTsPacketsPowervu(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) -{ - int8_t oddKeyUsed; - - uint8_t scramblingControl, payloadStart, oddeven; - uint8_t *pdata, *packetClusterV[256]; - uint8_t *packetClusterA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS][64]; // separate cluster arrays for video and each audio track - uint16_t pid, offset; - uint32_t i, j, k, tsHeader; - uint32_t scrambled_packets = 0, scrambled_packetsA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = { 0 }; - uint32_t *deskey; - uint32_t cs = 0; // video cluster start - uint32_t ce = 1; // video cluster end - uint32_t csa[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = { 0 }; // cluster index for audio tracks - - void *csakeyA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = { 0 }; - void *csakeyV = 0; - emu_stream_client_key_data *keydata; - - packetClusterV[0] = NULL; - - for (i = 0; i < bufLength; i += packetSize) - { - tsHeader = b2i(4, stream_buf + i); - pid = (tsHeader & 0x1FFF00) >> 8; - scramblingControl = tsHeader & 0xC0; - payloadStart = (tsHeader & 0x400000) >> 22; - - if (tsHeader & 0x20) - { - offset = 4 + stream_buf[i + 4] + 1; - } - else - { - offset = 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 (scramblingControl == 0) - { - continue; - } - - if (!(stream_buf[i + 3] & 0x10)) - { - stream_buf[i + 3] &= 0x3F; - continue; - } - - oddKeyUsed = scramblingControl == 0xC0 ? 1 : 0; - - if (!stream_server_has_ecm[data->connid]) - { - keydata = &emu_fixed_key_data[data->connid]; - SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[data->connid]); - data->key.pvu_csa_used = keydata->pvu_csa_used; - } - else - { - keydata = &data->key; - } - - if (keydata->pvu_csa_used) - { - oddeven = scramblingControl; // for detecting odd/even switch - - if (pid == data->video_pid) // start with video pid, since it is most dominant - { - csakeyV = keydata->pvu_csa_ks[PVU_CW_VID]; - - if (csakeyV != NULL) - { - cs = 0; - ce = 1; - packetClusterV[cs] = stream_buf + i; // set first cluster start - packetClusterV[ce] = stream_buf + i + packetSize - 1; - scrambled_packets = 1; - - // Now iterate through the rest of the packets and create clusters for batch decryption - for (j = i + packetSize; j < bufLength; j += packetSize) - { - tsHeader = b2i(4, stream_buf + j); - pid = (tsHeader & 0x1FFF00) >> 8; - - if (pid == data->video_pid) - { - if (oddeven != (tsHeader & 0xC0)) // changed key so stop adding clusters - { - break; - } - - if (cs > ce) // First video packet for each cluster - { - packetClusterV[cs] = stream_buf + j; - ce = cs + 1; - } - - scrambled_packets++; - } - else - { - if (cs < ce) // First non-video packet - need to set end of video cluster - { - packetClusterV[ce] = stream_buf + j - 1; - cs = ce + 1; - } - - if ((tsHeader & 0xC0) == 0) - { - continue; - } - - if (oddeven != (tsHeader & 0xC0)) // changed key so stop adding clusters - { - j = bufLength; // to break out of outer loop also - break; - } - - // Check for audio tracks and create single packet clusters - for (k = 0; k < data->audio_pid_count; k++) - { - if (pid == data->audio_pids[k]) - { - packetClusterA[k][csa[k]] = stream_buf + j; - csa[k]++; - packetClusterA[k][csa[k]] = stream_buf + j + packetSize - 1; - csa[k]++; - scrambled_packetsA[k]++; - } - } - } - } - - if (cs > ce) // last packet was not a video packet, so set null for end of all clusters - { - packetClusterV[cs] = NULL; - } - else - { - // last packet was a video packet, so set end of cluster to end of last packet - if (scrambled_packets > 1) - { - packetClusterV[ce] = stream_buf + j - 1; - } - - packetClusterV[ce + 1] = NULL; // add null to end of cluster list - } - - while (j >= cluster_size) - { - j = decrypt_packets(csakeyV, packetClusterV); - } - - for (k = 0; k < data->audio_pid_count; k++) - { - // if audio track has scrambled packets, set null to mark end and decrypt - if (scrambled_packetsA[k]) - { - csakeyA[k] = keydata->pvu_csa_ks[PVU_CW_A1 + k]; - packetClusterA[k][csa[k]] = NULL; - decrypt_packets(csakeyA[k], packetClusterA[k]); - csa[k] = 0; - scrambled_packetsA[k] = 0; - } - } - } - } - else - { - for (j = 0; j < data->audio_pid_count; j++) - { - if (pid == data->audio_pids[j]) - { - csakeyA[0] = keydata->pvu_csa_ks[PVU_CW_A1 + j]; - } - } - - if (csakeyA[0] != NULL) - { - packetClusterA[0][0] = stream_buf + i; - packetClusterA[0][1] = stream_buf + i + packetSize - 1; - packetClusterA[0][2] = NULL; - decrypt_packets(csakeyA[0], packetClusterA[0]); - } - } - } - else - { - deskey = NULL; - - if (pid == data->video_pid) - { - deskey = keydata->pvu_des_ks[PVU_CW_VID][oddKeyUsed]; - } - else - { - for (j = 0; j < data->audio_pid_count; j++) - { - if (pid == data->audio_pids[j]) - { - deskey = keydata->pvu_des_ks[PVU_CW_A1 + j][oddKeyUsed]; - } - } - } - - if (deskey == NULL) - { - deskey = keydata->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 (!stream_server_has_ecm[data->connid]) - { - SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[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 - } -} - -static int32_t connect_to_stream(char *http_buf, int32_t http_buf_len, char *stream_path) -{ - struct sockaddr_in cservaddr; - IN_ADDR_T in_addr; - - int32_t streamfd = socket(AF_INET, SOCK_STREAM, 0); - if (streamfd == -1) - { - return -1; - } - - struct timeval tv; - tv.tv_sec = 2; - tv.tv_usec = 0; - if (setsockopt(streamfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) - { - cs_log("ERROR: setsockopt() failed for SO_RCVTIMEO"); - return -1; - } - - bzero(&cservaddr, sizeof(cservaddr)); - cservaddr.sin_family = AF_INET; - cs_resolve(emu_stream_source_host, &in_addr, NULL, NULL); - SIN_GET_ADDR(cservaddr) = in_addr; - cservaddr.sin_port = htons(emu_stream_source_port); - - if (connect(streamfd, (struct sockaddr *)&cservaddr, sizeof(cservaddr)) == -1) - { - return -1; - } - - if (emu_stream_source_auth) - { - snprintf(http_buf, http_buf_len, "GET %s HTTP/1.1\nHost: %s:%u\n" - "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0\n" - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n" - "Accept-Language: en-US\n" - "Authorization: Basic %s\n" - "Connection: keep-alive\n\n", stream_path, emu_stream_source_host, emu_stream_source_port, emu_stream_source_auth); - } - else - { - snprintf(http_buf, http_buf_len, "GET %s HTTP/1.1\nHost: %s:%u\n" - "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0\n" - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n" - "Accept-Language: en-US\n" - "Connection: keep-alive\n\n", stream_path, emu_stream_source_host, emu_stream_source_port); - } - - if (send(streamfd, http_buf, cs_strlen(http_buf), 0) == -1) - { - return -1; - } - - return streamfd; -} - -static void stream_client_disconnect(emu_stream_client_conn_data *conndata) -{ - int32_t i; - - SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); - emu_stream_cur_srvid[conndata->connid] = NO_SRVID_VALUE; - stream_server_has_ecm[conndata->connid] = 0; - SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex); - - SAFE_MUTEX_LOCK(&emu_stream_server_mutex); - for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) - { - if (gconnfd[i] == conndata->connfd) - { - gconnfd[i] = -1; - gconncount--; - } - } - SAFE_MUTEX_UNLOCK(&emu_stream_server_mutex); - - shutdown(conndata->connfd, 2); - close(conndata->connfd); - - cs_log("Stream client %i disconnected",conndata->connid); - - NULLFREE(conndata); -} - -static void *stream_client_handler(void *arg) -{ - emu_stream_client_conn_data *conndata = (emu_stream_client_conn_data *)arg; - emu_stream_client_data *data; - - char *http_buf, stream_path[255], stream_path_copy[255]; - char *saveptr, *token, http_version[4]; - - int8_t streamConnectErrorCount = 0, streamDataErrorCount = 0; - int32_t bytesRead = 0, http_status_code = 0; - int32_t clientStatus, streamStatus, streamfd; - int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait, i; - - uint8_t *stream_buf; - uint16_t packetCount = 0, packetSize = 0, startOffset = 0; - uint32_t remainingDataPos, remainingDataLength, tmp_pids[4]; - - cs_log("Stream client %i connected", conndata->connid); - - if (!cs_malloc(&http_buf, 1024)) - { - stream_client_disconnect(conndata); - return NULL; - } - - if (!cs_malloc(&stream_buf, EMU_DVB_BUFFER_SIZE)) - { - NULLFREE(http_buf); - stream_client_disconnect(conndata); - return NULL; - } - - if (!cs_malloc(&data, sizeof(emu_stream_client_data))) - { - NULLFREE(http_buf); - NULLFREE(stream_buf); - stream_client_disconnect(conndata); - return NULL; - } - - clientStatus = recv(conndata->connfd, http_buf, 1024, 0); - if (clientStatus < 1) - { - NULLFREE(http_buf); - NULLFREE(stream_buf); - NULLFREE(data); - stream_client_disconnect(conndata); - return NULL; - } - - http_buf[1023] = '\0'; - if (sscanf(http_buf, "GET %254s ", stream_path) < 1) - { - NULLFREE(http_buf); - NULLFREE(stream_buf); - NULLFREE(data); - stream_client_disconnect(conndata); - return NULL; - } - - cs_strncpy(stream_path_copy, stream_path, sizeof(stream_path)); - - token = strtok_r(stream_path_copy, ":", &saveptr); // token 0 - for (i = 1; token != NULL && i < 7; i++) // tokens 1 to 6 - { - token = strtok_r(NULL, ":", &saveptr); - if (token == NULL) - { - break; - } - - if (i >= 3) // We olny need token 3 (srvid), 4 (tsid), 5 (onid) and 6 (ens) - { - if (sscanf(token, "%x", &tmp_pids[i - 3]) != 1) - { - tmp_pids[i - 3] = 0; - } - } - } - - data->srvid = tmp_pids[0] & 0xFFFF; - data->tsid = tmp_pids[1] & 0xFFFF; - data->onid = tmp_pids[2] & 0xFFFF; - data->ens = tmp_pids[3]; - - if (data->srvid == 0) // We didn't get a srvid - Exit - { - NULLFREE(http_buf); - NULLFREE(stream_buf); - NULLFREE(data); - stream_client_disconnect(conndata); - return NULL; - } - - SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); - emu_stream_cur_srvid[conndata->connid] = data->srvid; - stream_server_has_ecm[conndata->connid] = 0; - SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex); - - cs_log("Stream client %i request %s", conndata->connid, stream_path); - - cs_log_dbg(D_READER, "Stream client %i received srvid: %04X tsid: %04X onid: %04X ens: %08X", - conndata->connid, data->srvid, data->tsid, data->onid, data->ens); - - snprintf(http_buf, 1024, "HTTP/1.0 200 OK\nConnection: Close\nContent-Type: video/mpeg\nServer: stream_enigma2\n\n"); - clientStatus = send(conndata->connfd, http_buf, cs_strlen(http_buf), 0); - - data->connid = conndata->connid; - data->caid = NO_CAID_VALUE; - data->have_pat_data = 0; - data->have_pmt_data = 0; - data->have_cat_data = 0; - data->have_ecm_data = 0; - data->have_emm_data = 0; - data->reset_key_data = 1; - - while (!exit_oscam && clientStatus != -1 && streamConnectErrorCount < 3 - && streamDataErrorCount < 15) - { - streamfd = connect_to_stream(http_buf, 1024, stream_path); - if (streamfd == -1) - { - cs_log("WARNING: stream client %i cannot connect to stream source", conndata->connid); - streamConnectErrorCount++; - cs_sleepms(500); - continue; - } - - streamStatus = 0; - bytesRead = 0; - - while (!exit_oscam && clientStatus != -1 && streamStatus != -1 - && streamConnectErrorCount < 3 && streamDataErrorCount < 15) - { - if (data->key.pvu_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; - } - - streamStatus = recv(streamfd, stream_buf + bytesRead, cur_dvb_buffer_size - bytesRead, MSG_WAITALL); - if (streamStatus == 0) // socket closed - { - cs_log("WARNING: stream client %i - stream source closed connection", conndata->connid); - streamConnectErrorCount++; - cs_sleepms(100); - break; - } - - if (streamStatus < 0) // error - { - if ((errno == EWOULDBLOCK) | (errno == EAGAIN)) - { - cs_log("WARNING: stream client %i no data from stream source", conndata->connid); - streamDataErrorCount++; // 2 sec timeout * 15 = 30 seconds no data -> close - cs_sleepms(100); - continue; - } - - cs_log("WARNING: stream client %i error receiving data from stream source", conndata->connid); - streamConnectErrorCount++; - cs_sleepms(100); - break; - } - - if (streamStatus < cur_dvb_buffer_size - bytesRead) // probably just received header but no stream - { - if (!bytesRead && streamStatus > 13 && - sscanf((const char*)stream_buf, "HTTP/%3s %d ", http_version , &http_status_code) == 2 && - http_status_code != 200) - { - cs_log("ERROR: stream client %i got %d response from stream source", conndata->connid, http_status_code); - streamConnectErrorCount++; - cs_sleepms(100); - break; - } - else - { - cs_log_dbg(0, "WARNING: stream client %i non-full buffer from stream source", conndata->connid); - streamDataErrorCount++; - cs_sleepms(100); - } - } - else - { - streamDataErrorCount = 0; - } - - streamConnectErrorCount = 0; - bytesRead += streamStatus; - - if (bytesRead >= cur_dvb_buffer_wait) - { - startOffset = 0; - - // only search if not starting on ts packet or unknown packet size - if (stream_buf[0] != 0x47 || packetSize == 0) - { - SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset); - } - - if (packetSize == 0) - { - bytesRead = 0; - } - else - { - packetCount = ((bytesRead - startOffset) / packetSize); - - // 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.emu_stream_relay_ctab)) - { - if (caid_is_powervu(data->caid)) - { - DescrambleTsPacketsPowervu(data, stream_buf + startOffset, packetCount * packetSize, packetSize); - } - 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 - { - cs_log_dbg(D_READER, "Stream client %i caid %04X not enabled in stream relay config", - conndata->connid, data->caid); - } - } - else // Search PAT and PMT packets for service information - { - ParseTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize); - } - - clientStatus = send(conndata->connfd, stream_buf + startOffset, packetCount * packetSize, 0); - - remainingDataPos = startOffset + (packetCount * packetSize); - remainingDataLength = bytesRead - remainingDataPos; - - if (remainingDataPos < remainingDataLength) - { - memmove(stream_buf, stream_buf + remainingDataPos, remainingDataLength); - } - else - { - memcpy(stream_buf, stream_buf + remainingDataPos, remainingDataLength); - } - - bytesRead = remainingDataLength; - } - } - } - - close(streamfd); - } - - NULLFREE(http_buf); - NULLFREE(stream_buf); - - for (i = 0; i < 8; i++) - { - if (data->key.pvu_csa_ks[i]) - { - free_key_struct(data->key.pvu_csa_ks[i]); - } - } - NULLFREE(data); - - stream_client_disconnect(conndata); - return NULL; -} - -void *stream_server(void *UNUSED(a)) -{ - struct sockaddr_in servaddr, cliaddr; - socklen_t clilen; - int32_t connfd, reuse = 1, i; - int8_t connaccepted; - emu_stream_client_conn_data *conndata; - - cluster_size = get_internal_parallelism(); - cs_log("INFO: FFDecsa parallel mode = %d", cluster_size); - - if (!emu_stream_server_mutex_init) - { - SAFE_MUTEX_INIT(&emu_stream_server_mutex, NULL); - emu_stream_server_mutex_init = 1; - } - - SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); - for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) - { - emu_stream_cur_srvid[i] = NO_SRVID_VALUE; - stream_server_has_ecm[i] = 0; - } - SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex); - - for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) - { - gconnfd[i] = -1; - } - - glistenfd = socket(AF_INET, SOCK_STREAM, 0); - if (glistenfd == -1) - { - cs_log("ERROR: cannot create stream server socket"); - return NULL; - } - - bzero(&servaddr,sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = htonl(INADDR_ANY); - servaddr.sin_port = htons(emu_stream_relay_port); - setsockopt(glistenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); - - if (bind(glistenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) - { - cs_log("ERROR: cannot bind to stream server socket"); - close(glistenfd); - return NULL; - } - - if (listen(glistenfd, 3) == -1) - { - cs_log("ERROR: cannot listen to stream server socket"); - close(glistenfd); - return NULL; - } - - while (!exit_oscam) - { - clilen = sizeof(cliaddr); - connfd = accept(glistenfd,(struct sockaddr *)&cliaddr, &clilen); - - if (connfd == -1) - { - cs_log("ERROR: accept() failed"); - break; - } - - connaccepted = 0; - - if (cs_malloc(&conndata, sizeof(emu_stream_client_conn_data))) - { - SAFE_MUTEX_LOCK(&emu_stream_server_mutex); - if (gconncount < EMU_STREAM_SERVER_MAX_CONNECTIONS) - { - for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) - { - if (gconnfd[i] == -1) - { - gconnfd[i] = connfd; - gconncount++; - connaccepted = 1; - - conndata->connfd = connfd; - conndata->connid = i; - - break; - } - } - } - SAFE_MUTEX_UNLOCK(&emu_stream_server_mutex); - } - - if (connaccepted) - { - int on = 1; - if (setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) - { - cs_log("ERROR: stream client %i setsockopt() failed for TCP_NODELAY", conndata->connid); - } - - start_thread("emu stream client", stream_client_handler, (void*)conndata, NULL, 1, 0); - } - else - { - shutdown(connfd, 2); - close(connfd); - cs_log("ERROR: stream server client dropped because of too many connections (%i)", EMU_STREAM_SERVER_MAX_CONNECTIONS); - } - - cs_sleepms(20); - } - - close(glistenfd); - - return NULL; -} - -void *stream_key_delayer(void *UNUSED(arg)) -{ - int32_t i, j; - 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 < EMU_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 < 8; j++) - { - if (item->csa_used) - { - if (cdata->pvu_csa_ks[j] == NULL) - { - cdata->pvu_csa_ks[j] = get_key_struct(); - } - - if (item->is_even) - { - set_even_control_word(cdata->pvu_csa_ks[j], item->cw[j]); - } - else - { - set_odd_control_word(cdata->pvu_csa_ks[j], item->cw[j]); - } - - cdata->pvu_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->pvu_csa_used = 0; - } - } - SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[i]); - - ll_iter_remove_data(&it); - } - } - - cs_sleepms(25); - } - - return NULL; -} - -void stop_stream_server(void) -{ - int32_t i; - - SAFE_MUTEX_LOCK(&emu_stream_server_mutex); - for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) - { - if (gconnfd[i] != -1) - { - shutdown(gconnfd[i], 2); - close(gconnfd[i]); - gconnfd[i] = -1; - } - } - - gconncount = 0; - SAFE_MUTEX_UNLOCK(&emu_stream_server_mutex); - - shutdown(glistenfd, 2); - close(glistenfd); -} - -#endif // WITH_EMU diff --git a/module-emulator-streamserver.h b/module-emulator-streamserver.h deleted file mode 100644 index 2b1c2ac73..000000000 --- a/module-emulator-streamserver.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef MODULE_EMULATOR_STREAMSERVER_H_ -#define MODULE_EMULATOR_STREAMSERVER_H_ - -#ifdef WITH_EMU - -#define EMU_STREAM_SERVER_MAX_CONNECTIONS 8 -#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 16 - -#define EMU_DVB_MAX_TS_PACKETS 278 -#define EMU_DVB_BUFFER_SIZE_CSA 188*EMU_DVB_MAX_TS_PACKETS -#define EMU_DVB_BUFFER_WAIT_CSA 188*(EMU_DVB_MAX_TS_PACKETS-128) -#define EMU_DVB_BUFFER_SIZE_DES 188*32 -#define EMU_DVB_BUFFER_WAIT_DES 188*29 -#define EMU_DVB_BUFFER_SIZE EMU_DVB_BUFFER_SIZE_CSA - -typedef struct -{ - uint32_t pvu_des_ks[8][2][32]; - int8_t pvu_csa_used; - void* pvu_csa_ks[8]; -} emu_stream_client_key_data; - -typedef struct -{ - int32_t connid; - int8_t have_cat_data; - int8_t have_pat_data; - int8_t have_pmt_data; - int8_t have_ecm_data; - int8_t have_emm_data; - int8_t reset_key_data; - uint8_t cat_data[1024+208]; - uint8_t pat_data[1024+208]; - uint8_t pmt_data[1024+208]; - uint8_t ecm_data[1024+208]; - uint8_t emm_data[1024+208]; - uint16_t cat_data_pos; - uint16_t pat_data_pos; - uint16_t pmt_data_pos; - uint16_t ecm_data_pos; - uint16_t emm_data_pos; - uint16_t srvid; - uint16_t caid; - uint16_t tsid; - uint16_t onid; - uint32_t ens; - uint16_t pmt_pid; - uint16_t ecm_pid; - uint16_t emm_pid; - uint16_t video_pid; - uint16_t teletext_pid; - uint16_t pcr_pid; - uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS]; - uint8_t audio_pid_count; - int16_t ecm_nb; - emu_stream_client_key_data key; -} emu_stream_client_data; - -extern char emu_stream_source_host[256]; -extern int32_t emu_stream_source_port; -extern char *emu_stream_source_auth; -extern int32_t emu_stream_relay_port; -extern int8_t emu_stream_emm_enabled; - -extern int8_t stream_server_thread_init; - -void *stream_server(void *a); -void stop_stream_server(void); - -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_srvid_mutex; -extern uint16_t emu_stream_cur_srvid[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -extern int8_t stream_server_has_ecm[EMU_STREAM_SERVER_MAX_CONNECTIONS]; - -extern pthread_mutex_t emu_fixed_key_data_mutex[EMU_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_EMULATOR_STREAMSERVER_H_ diff --git a/module-emulator.c b/module-emulator.c index bb510747b..6417a9626 100644 --- a/module-emulator.c +++ b/module-emulator.c @@ -4,8 +4,8 @@ #ifdef WITH_EMU +#include "module-streamrelay.h" #include "module-emulator-osemu.h" -#include "module-emulator-streamserver.h" #include "module-emulator-biss.h" #include "module-emulator-irdeto.h" #include "module-emulator-powervu.h" @@ -39,7 +39,9 @@ #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) @@ -724,11 +726,10 @@ const struct s_cardsystem reader_emu = static int32_t emu_reader_init(struct s_reader *UNUSED(reader)) { - int32_t i; - char authtmp[128]; - - if (cfg.emu_stream_relay_enabled && (stream_server_thread_init == 0)) +#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); @@ -741,24 +742,6 @@ static int32_t emu_reader_init(struct s_reader *UNUSED(reader)) start_thread("stream_key_delayer", stream_key_delayer, NULL, NULL, 1, 1); cs_log("Stream key delayer initialized"); - - cs_strncpy(emu_stream_source_host, cfg.emu_stream_source_host, sizeof(emu_stream_source_host)); - emu_stream_source_port = cfg.emu_stream_source_port; - emu_stream_relay_port = cfg.emu_stream_relay_port; - emu_stream_emm_enabled = cfg.emu_stream_emm_enabled; - - if (cfg.emu_stream_source_auth_user && cfg.emu_stream_source_auth_password) - { - snprintf(authtmp, sizeof(authtmp), "%s:%s", cfg.emu_stream_source_auth_user, cfg.emu_stream_source_auth_password); - b64encode(authtmp, cs_strlen(authtmp), &emu_stream_source_auth); - } - else - { - NULLFREE(emu_stream_source_auth); - } - - start_thread("stream_server", stream_server, NULL, NULL, 1, 1); - cs_log("Stream relay server initialized"); } // Initialize mutex for exclusive access to key database and key file @@ -767,7 +750,7 @@ static int32_t emu_reader_init(struct s_reader *UNUSED(reader)) SAFE_MUTEX_INIT(&emu_key_data_mutex, NULL); emu_key_data_mutex_init = 1; } - +#endif return CR_OK; } diff --git a/module-gbox.c b/module-gbox.c index bb28f3b5a..1b0697d00 100644 --- a/module-gbox.c +++ b/module-gbox.c @@ -1810,7 +1810,6 @@ static int8_t gbox_check_header_recvd(struct s_client *cli, struct s_client *pro cs_log_dump_dbg(D_READER, data, n, "-> decrypted data (%d bytes) from %s:", n, cs_inet_ntoa(cli->ip)); peer_received_pw = b2i(4, data + 6); - peer_recvd_id = gbox_convert_password_to_id(peer_received_pw); my_received_pw = b2i(4, data + 2); rcvd_header_cmd = b2i(2, data); diff --git a/module-ird-guess.c b/module-ird-guess.c deleted file mode 100644 index 5c998b9d0..000000000 --- a/module-ird-guess.c +++ /dev/null @@ -1,178 +0,0 @@ -#define MODULE_LOG_PREFIX "irdguess" - -#include "globals.h" - -#ifdef IRDETO_GUESSING -#include "module-ird-guess.h" -#include "oscam-string.h" -#include "oscam-conf.h" - -struct s_irdeto_quess -{ - int32_t b47; - uint16_t caid; - uint16_t sid; - struct s_irdeto_quess *next; -}; - -static struct s_irdeto_quess **itab; - -int32_t init_irdeto_guess_tab(void) -{ - FILE *fp = open_config_file("oscam.ird"); - if(!fp) - { return 1; } - - int32_t i, j, skip; - int32_t b47; - char token[128], *ptr, *saveptr1 = NULL; - char zSid[5]; - uint8_t b3; - uint16_t caid, sid; - struct s_irdeto_quess *ird_row, *head; - - if(!cs_malloc(&itab, sizeof(struct s_irdeto_quess *) * 0xff)) - { - fclose(fp); - return 0; - } - - while(fgets(token, sizeof(token), fp)) - { - if(cs_strlen(token) < 20) { continue; } - for(i = b3 = b47 = caid = sid = skip = 0, ptr = strtok_r(token, ":", &saveptr1); (i < 4) && (ptr); ptr = strtok_r(NULL, ":", &saveptr1), i++) - { - trim(ptr); - if(*ptr == ';' || *ptr == '#' || *ptr == '-') - { - skip = 1; - break; - } - - switch(i) - { - case 0: - b3 = a2i(ptr, 2); - break; - - case 1: - b47 = a2i(ptr, 8); - break; - - case 2: - caid = a2i(ptr, 4); - break; - - case 3: - for(j = 0; j < 4; j++) - { - zSid[j] = ptr[j]; - } - zSid[4] = 0; - sid = a2i(zSid, 4); - break; - } - } - - if(!skip) - { - if(!cs_malloc(&ird_row, sizeof(struct s_irdeto_quess))) - { - fclose(fp); - return (1); - } - - ird_row->b47 = b47; - ird_row->caid = caid; - ird_row->sid = sid; - - head = itab[b3]; - if(head) - { - while(head->next) - { head = head->next; } - head->next = ird_row; - } - else - { itab[b3] = ird_row; } - //cs_log_dbg(D_CLIENT, "%02X:%08X:%04X:%04X", b3, b47, caid, sid); - } - } - fclose(fp); - - for(i = 0; i < 0xff; i++) - { - head = itab[i]; - while(head) - { - cs_log_dbg(D_CLIENT, "itab[%02X]: b47=%08X, caid=%04X, sid=%04X", - i, head->b47, head->caid, head->sid); - head = head->next; - } - } - return (0); -} - -void free_irdeto_guess_tab(void) -{ - uint8_t i; - if(!itab) - { return; } - - for(i = 0; i < 0xff; i++) - { - struct s_irdeto_quess *head = itab[i]; - while(head) - { - void *next = head->next; - NULLFREE(head); - head = next; - } - } - NULLFREE(itab); -} - -void guess_irdeto(ECM_REQUEST *er) -{ - uint8_t b3; - int32_t b47; - //uint16_t chid; - struct s_irdeto_quess *ptr; - - if(!itab) - { return; } - - b3 = er->ecm[3]; - ptr = itab[b3]; - if(!ptr) - { - cs_log_dbg(D_TRACE, "unknown irdeto byte 3: %02X", b3); - return; - } - - b47 = b2i(4, er->ecm + 4); - //chid = b2i(2, er->ecm+6); - //cs_log_dbg(D_TRACE, "ecm: b47=%08X, ptr->b47=%08X, ptr->caid=%04X", b47, ptr->b47, ptr->caid); - - while(ptr) - { - if(b47 == ptr->b47) - { - if(er->srvid && (er->srvid != ptr->sid)) - { - cs_log_dbg(D_TRACE, "sid mismatched (ecm: %04X, guess: %04X), wrong oscam.ird file?", - er->srvid, ptr->sid); - return; - } - er->caid = ptr->caid; - er->srvid = ptr->sid; - er->chid = (uint16_t)ptr->b47; - //cs_log_dbg(D_TRACE, "quess_irdeto() found caid=%04X, sid=%04X, chid=%04X", - //er->caid, er->srvid, er->chid); - return; - } - ptr = ptr->next; - } -} - -#endif diff --git a/module-ird-guess.h b/module-ird-guess.h deleted file mode 100644 index 75c1c246e..000000000 --- a/module-ird-guess.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef MODULE_IRD_GUESS_H -#define MODULE_IRD_GUESS_H - -#ifdef IRDETO_GUESSING -int32_t init_irdeto_guess_tab(void); -void guess_irdeto(ECM_REQUEST *er); -void free_irdeto_guess_tab(void); -#else -static inline int32_t init_irdeto_guess_tab(void) -{ - return 0; -} -static inline void guess_irdeto(ECM_REQUEST *UNUSED(er)) { } -static inline void free_irdeto_guess_tab(void) { } -#endif - -#endif diff --git a/module-radegast.c b/module-radegast.c index b514203ef..da3265aef 100644 --- a/module-radegast.c +++ b/module-radegast.c @@ -7,6 +7,10 @@ #include "oscam-net.h" #include "oscam-string.h" #include "oscam-reader.h" +#ifdef MODULE_STREAMRELAY +#include "module-streamrelay.h" +#include "oscam-chk.h" +#endif static int32_t radegast_connect(void); @@ -86,6 +90,12 @@ static void radegast_send_dcw(struct s_client *client, ECM_REQUEST *er) mbuf[0] = 0x02; // DCW if(er->rc < E_NOTFOUND) { +#ifdef MODULE_STREAMRELAY + if(chk_ctab_ex(er->caid, &cfg.stream_relay_ctab) && cfg.stream_relay_enabled) + { + stream_write_cw(er); + } +#endif mbuf[1] = 0x12; // len (overall) mbuf[2] = 0x05; // ACCESS mbuf[3] = 0x10; // len diff --git a/module-streamrelay.c b/module-streamrelay.c new file mode 100644 index 000000000..dc19e2846 --- /dev/null +++ b/module-streamrelay.c @@ -0,0 +1,1247 @@ +#define MODULE_LOG_PREFIX "relay" + +#include "globals.h" + +#ifdef MODULE_STREAMRELAY + +#include "module-streamrelay.h" +#include "oscam-config.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-chk.h" + +#define STREAM_UNDEFINED 0x00 +#define STREAM_VIDEO 0x01 +#define STREAM_AUDIO 0x02 +#define STREAM_SUBTITLE 0x03 +#define STREAM_TELETEXT 0x04 + +extern int32_t exit_oscam; + +typedef struct +{ + int32_t connfd; + int32_t connid; +} stream_client_conn_data; + +char stream_source_host[256]; +char *stream_source_auth = NULL; +uint32_t cluster_size = 50; + +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 MODULE_RADEGAST +static int32_t gRadegastFd = 0; + +static bool connect_to_radegast(void) +{ + struct SOCKADDR cservaddr; + + if (gRadegastFd == 0) + gRadegastFd = socket(DEFAULT_AF, SOCK_STREAM, 0); + + if (gRadegastFd < 0) + { + gRadegastFd = 0; + return false; + } + + int32_t flags = fcntl(gRadegastFd, F_GETFL); + fcntl(gRadegastFd, F_SETFL, flags | O_NONBLOCK); + + bzero(&cservaddr, sizeof(cservaddr)); + SIN_GET_FAMILY(cservaddr) = DEFAULT_AF; + SIN_GET_PORT(cservaddr) = htons(cfg.rad_port); + SIN_GET_ADDR(cservaddr) = cfg.rad_srvip; + + if (connect(gRadegastFd, (struct sockaddr *)&cservaddr, sizeof(cservaddr)) == -1) + { + return false; + } + + return true; +} + +static void close_radegast_connection(void) +{ + close(gRadegastFd); + gRadegastFd = 0; +} + +static bool send_to_radegast(uint8_t* data, int len) +{ + if (send(gRadegastFd, data, len, 0) < 0) + { + cs_log("send_to_radegast: Send failure"); + return false; + } + return true; +} + +static void radegast_client_ecm(stream_client_data *cdata) +{ + uint16_t section_length = SCT_LEN(cdata->ecm_data); + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + MD5(cdata->ecm_data, section_length, md5tmp); + + if (!memcmp(cdata->ecm_md5, md5tmp, MD5_DIGEST_LENGTH)) { return; } + memcpy(cdata->ecm_md5, md5tmp, MD5_DIGEST_LENGTH); + + uint16_t packet_len; + static uint8_t header_len = 2; + static uint8_t payload_static_len = 12; + + if (gRadegastFd <= 0) + { connect_to_radegast(); } + + packet_len = header_len + payload_static_len + section_length; + uint8_t outgoing_data[packet_len]; + outgoing_data[0] = 1; + outgoing_data[1] = payload_static_len + section_length; + outgoing_data[2] = 10; // caid + outgoing_data[3] = 2; + outgoing_data[4] = cdata->caid >> 8; + outgoing_data[5] = cdata->caid & 0xFF; + outgoing_data[6] = 9; // srvid + outgoing_data[7] = 4; + outgoing_data[8] = cdata->srvid & 0xFF; + outgoing_data[10] = cdata->srvid >> 8; + outgoing_data[12] = 3; + outgoing_data[13] = section_length; + + memcpy(outgoing_data + header_len + payload_static_len, cdata->ecm_data, section_length); + + if (!send_to_radegast(outgoing_data, packet_len)) + { + close_radegast_connection(); + if (connect_to_radegast()) + { + send_to_radegast(outgoing_data, packet_len); + } + } +} + +void ParseEcmData(stream_client_data *cdata) +{ + uint8_t *data = cdata->ecm_data; + uint16_t section_length = SCT_LEN(data); + + if (section_length < 11) + { + return; + } + + radegast_client_ecm(cdata); +} +#endif // MODULE_RADEGAST + +static void write_cw(ECM_REQUEST *er, int32_t connid) +{ + const uint8_t ecm = (caid_is_videoguard(er->caid) && (er->ecm[4] != 0 && (er->ecm[2] - er->ecm[4]) == 4)) ? 4 : 0; + if (memcmp(er->cw, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0) + { + dvbcsa_bs_key_set(er->cw, key_data[connid].key[EVEN]); + } + + if (memcmp(er->cw + 8, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0) + { + dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[ODD]); + } +} + +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++) + { + 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) + } + } + //SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + return cw_written; + } + return true; +} + +static void SearchTsPackets(const uint8_t *buf, const uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset) +{ + uint32_t i; + + for (i = 0; i < bufLength; i++) + { + if (buf[i] == 0x47) + { + // if three packets align, probably safe to assume correct size + if ((buf[i + 188] == 0x47) & (buf[i + 376] == 0x47)) + { + (*packetSize) = 188; + (*startOffset) = i; + return; + } + else if ((buf[i + 204] == 0x47) & (buf[i + 408] == 0x47)) + { + (*packetSize) = 204; + (*startOffset) = i; + return; + } + else if ((buf[i + 208] == 0x47) & (buf[i + 416] == 0x47)) + { + (*packetSize) = 208; + (*startOffset) = i; + return; + } + } + } + + (*packetSize) = 0; + (*startOffset) = 0; +} + +typedef void (*ts_data_callback)(stream_client_data *cdata); + +static void ParseTsData(const uint8_t table_id, const uint8_t table_mask, const uint8_t min_table_length, int8_t *flag, + uint8_t *data, const uint16_t data_length, uint16_t *data_pos, const int8_t payloadStart, + const uint8_t *buf, const int32_t len, ts_data_callback func, stream_client_data *cdata) +{ + int32_t i; + uint16_t offset = 0; + bool found_start = 0; + + if (len < 1) + { + return; + } + + if (*flag == 0 && !payloadStart) + { + return; + } + + if (*flag == 0) + { + *data_pos = 0; + offset = 1 + buf[0]; + } + else if (payloadStart) + { + offset = 1; + } + + if ((len - offset) < 1) + { + return; + } + + const int32_t free_data_length = (data_length - *data_pos); + const int32_t copySize = (len - offset) > free_data_length ? free_data_length : (len - offset); + + memcpy(data + *data_pos, buf + offset, copySize); + *data_pos += copySize; + + for (i = 0; i < *data_pos; i++) + { + if ((data[i] & table_mask) == table_id) + { + if (i != 0) + { + if (*data_pos - i > i) + { + memmove(data, &data[i], *data_pos - i); + } + else + { + memcpy(data, &data[i], *data_pos - i); + } + + *data_pos -= i; + } + found_start = 1; + break; + } + } + + const uint16_t section_length = SCT_LEN(data); + + if (!found_start || (section_length > data_length) || (section_length < min_table_length)) + { + *flag = 0; + return; + } + + if ((*data_pos < section_length) || (*data_pos < 3)) + { + *flag = 2; + return; + } + + func(cdata); + + found_start = 0; + for (i = section_length; i < *data_pos; i++) + { + if ((data[i] & table_mask) == table_id) + { + if (*data_pos - i > i) + { + memmove(data, &data[i], *data_pos - i); + } + else + { + memcpy(data, &data[i], *data_pos - i); + } + + *data_pos -= i; + found_start = 1; + break; + } + } + + if (!found_start || (data_length < *data_pos + copySize + 1)) + { + *data_pos = 0; + } + + *flag = 1; +} + +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); + if (srvid == 0) + { + continue; + } + + if (cdata->srvid == srvid) + { + cdata->pmt_pid = b2i(2, cdata->pat_data + i + 2) & 0x1FFF; + cs_log_dbg(D_READER, "Stream client %i found pmt pid: 0x%04X (%i)", + cdata->connid, cdata->pmt_pid, cdata->pmt_pid); + break; + } + } +} + +static void ParseDescriptors(const uint8_t *buffer, const uint16_t info_length, uint8_t *type) +{ + uint32_t i; + uint8_t j, descriptor_length = 0; + + if (info_length < 1) + { + return; + } + + for (i = 0; i + 1 < info_length; i += descriptor_length + 2) + { + descriptor_length = buffer[i + 1]; + switch (buffer[i]) // descriptor tag + { + case 0x05: // Registration descriptor + { + // "HDMV" format identifier is removed + // Cam does not need to know about Blu-ray + const char format_identifiers_audio[10][5] = + { + "AC-3", "BSSD", "dmat", "DRA1", "DTS1", + "DTS2", "DTS3", "EAC3", "mlpa", "Opus", + }; + for (j = 0; j < 10; j++) + { + if (memcmp(buffer + i + 2, format_identifiers_audio[j], 4) == 0) + { + *type = STREAM_AUDIO; + break; + } + } + break; + } + //case 0x09: // CA descriptor + //{ + // break; + //} + case 0x46: // VBI teletext descriptor (DVB) + case 0x56: // teletext descriptor (DVB) + { + *type = STREAM_TELETEXT; + break; + } + case 0x59: // subtitling descriptor (DVB) + { + *type = STREAM_SUBTITLE; + break; + } + case 0x6A: // AC-3 descriptor (DVB) + case 0x7A: // enhanced AC-3 descriptor (DVB) + case 0x7B: // DTS descriptor (DVB) + case 0x7C: // AAC descriptor (DVB) + case 0x81: // AC-3 descriptor (ATSC) + case 0xCC: // Enhanced AC-3 descriptor (ATSC) + { + *type = STREAM_AUDIO; + break; + } + case 0x7F: // extension descriptor (DVB) + { + switch(buffer[i + 2]) // extension descriptor tag + { + case 0x0E: // DTS-HD descriptor (DVB) + case 0x0F: // DTS Neural descriptor (DVB) + case 0x15: // AC-4 descriptor (DVB) + *type = STREAM_AUDIO; + break; + + case 0x20: // TTML subtitling descriptor (DVB) + *type = STREAM_SUBTITLE; + break; + + default: + *type = STREAM_UNDEFINED; + break; + } + break; + } + default: + break; + } + } +} + +static void stream_parse_pmt_ca_descriptor(const uint8_t *data, const int32_t data_pos, const int32_t offset, const uint16_t info_length, stream_client_data *cdata) +{ + if (cdata->ecm_pid) + { + return; + } + + // parse program descriptors (we are looking only for CA descriptor here) + int32_t i; + uint16_t caid; + uint8_t descriptor_tag, descriptor_length = 0; + + for (i = offset; i + 1 < offset + info_length; i += descriptor_length + 2) + { + descriptor_tag = data[i + data_pos]; + descriptor_length = data[i + 1 + data_pos]; + if (descriptor_length < 1) + { + break; + } + + if (i + 1 + descriptor_length >= offset + info_length) + { + break; + } + + if (descriptor_tag == 0x09 && descriptor_length >= 4) + { + caid = b2i(2, data + i + 2 + data_pos); + if (chk_ctab_ex(caid, &cfg.stream_relay_ctab)) + { + if (cdata->caid == NO_CAID_VALUE) + { + cdata->caid = caid; + } + + if (cdata->caid != caid) + { + continue; + } + cdata->ecm_pid = b2i(2, data + i + 4 + data_pos) & 0x1FFF; + cs_log_dbg(D_READER, "Stream client %i found ecm pid: 0x%04X (%i)", + cdata->connid, cdata->ecm_pid, cdata->ecm_pid); + } + } + } +} + +static void ParsePmtData(stream_client_data *cdata) +{ + int32_t i; + uint16_t program_info_length = 0, es_info_length = 0, elementary_pid; + const uint16_t section_length = SCT_LEN(cdata->pmt_data); + uint8_t offset = 0; + + cdata->ecm_pid = 0; + cdata->pcr_pid = b2i(2, cdata->pmt_data + 8) & 0x1FFF; + + if (cdata->pcr_pid != 0x1FFF) + { + cs_log_dbg(D_READER, "Stream client %i found pcr pid: 0x%04X (%i)", + cdata->connid, cdata->pcr_pid, cdata->pcr_pid); + } + program_info_length = b2i(2, cdata->pmt_data + 10) & 0xFFF; + if (!program_info_length) + { + offset = 5; + program_info_length = (b2i(2, cdata->pmt_data + 10 + offset) & 0xFFF); + } + if (12 + offset + program_info_length >= section_length) { return; } + stream_parse_pmt_ca_descriptor(cdata->pmt_data, 0, 12 + offset, program_info_length, cdata); + + offset = offset == 5 ? 0 : program_info_length; + for (i = 12 + offset; i + 4 < section_length; i += 5 + es_info_length) + { + elementary_pid = b2i(2, cdata->pmt_data + i + 1) & 0x1FFF; + es_info_length = b2i(2, cdata->pmt_data + i + 3) & 0xFFF; + switch (cdata->pmt_data[i]) // stream type + { + case 0x01: + case 0x02: + case 0x10: + case 0x1B: + case 0x20: + case 0x24: + case 0x25: + case 0x42: + case 0xD1: + case 0xEA: + { + 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); + break; + } + case 0x03: + case 0x04: + case 0x0F: + case 0x11: + case 0x1C: + case 0x2D: + case 0x2E: + case 0x81: + { + cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); + break; + } + case 0x06: + //case 0x81: // some ATSC AC-3 streams do not contain the AC-3 descriptor! + case 0x87: + { + uint8_t type = STREAM_UNDEFINED; + ParseDescriptors(cdata->pmt_data + i + 5, es_info_length, &type); + if (type == STREAM_AUDIO) + { + 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) + { + 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 + } +} + +static void ParseTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + uint8_t payloadStart; + 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; + payloadStart = (tsHeader & 0x400000) >> 22; + + if (tsHeader & 0x20) + { + offset = 4 + stream_buf[i + 4] + 1; + } + else + { + offset = 4; + } + + if (packetSize - offset < 1) + { + continue; + } + + if (pid == 0x0000 && data->have_pat_data != 1) // Search the PAT for the PMT pid + { + ParseTsData(0x00, 0xFF, 16, &data->have_pat_data, data->pat_data, sizeof(data->pat_data), + &data->pat_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParsePatData, data); + continue; + } + + if (pid == data->pmt_pid && data->have_pmt_data != 1) // Search the PMT for PCR, ECM, Video and Audio pids + { + ParseTsData(0x02, 0xFF, 21, &data->have_pmt_data, data->pmt_data, sizeof(data->pmt_data), + &data->pmt_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParsePmtData, data); + continue; + } + + // We have bot PAT and PMT data - No need to search the rest of the packets + if (data->have_pat_data == 1 && data->have_pmt_data == 1) + { + break; + } + } +} + +static void decrypt(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid) +{ + if (fill[oddeven] > 0) + { +#if 0 + uint16_t i; + for(i = fill[oddeven]; i <= cluster_size; i++) + { + tsbbatch[i].data = NULL; + tsbbatch[i].len = 0; + } +#else + tsbbatch[fill[oddeven]].data = NULL; +#endif + //cs_log_dbg(D_READER, "dvbcsa (%s), batch=%d", oddeven == ODD ? "odd" : "even", fill[oddeven]); + + fill[oddeven] = 0; + + dvbcsa_bs_decrypt(key_data[connid].key[oddeven], tsbbatch, 184); + } +} +#define decrypt(a) decrypt(tsbbatch, fill, a, data->connid) + +static void DescrambleTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) +{ + uint32_t i, tsHeader; + uint16_t offset, fill[2] = {0,0}; + uint8_t oddeven = 0; +#ifdef MODULE_RADEGAST + uint16_t pid; + uint8_t payloadStart; +#endif + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); +#ifdef MODULE_RADEGAST + pid = (tsHeader & 0x1FFF00) >> 8; + payloadStart = (tsHeader & 0x400000) >> 22; +#endif + offset = (tsHeader & 0x20) ? 4 + stream_buf[i + 4] + 1 : 4; + if (packetSize - offset < 1) + { + 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 + 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; + } +#endif // MODULE_RADEGAST + if ((tsHeader & 0xC0) == 0) + { + continue; + } + + stream_buf[i + 3] &= 0x3f; // consider it decrypted now + oddeven = (tsHeader & 0xC0) == 0xC0 ? ODD: EVEN; + decrypt(oddeven == ODD ? EVEN : ODD); + tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; + tsbbatch[fill[oddeven]].len = packetSize - offset; + fill[oddeven]++; + + if (fill[oddeven] > cluster_size - 1) + { + decrypt(oddeven); + } + } + + decrypt(oddeven); +} + +static int32_t connect_to_stream(char *http_buf, int32_t http_buf_len, char *stream_path) +{ + struct SOCKADDR cservaddr; + IN_ADDR_T in_addr; + + int32_t streamfd = socket(DEFAULT_AF, SOCK_STREAM, 0); + if (streamfd == -1) { return -1; } + + struct timeval tv; + tv.tv_sec = 2; + tv.tv_usec = 0; + if (setsockopt(streamfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) + { + cs_log("ERROR: setsockopt() failed for SO_RCVTIMEO"); + return -1; + } + + bzero(&cservaddr, sizeof(cservaddr)); + SIN_GET_FAMILY(cservaddr) = DEFAULT_AF; + SIN_GET_PORT(cservaddr) = htons(cfg.stream_source_port); + cs_resolve(stream_source_host, &in_addr, NULL, NULL); + SIN_GET_ADDR(cservaddr) = in_addr; + + if (connect(streamfd, (struct sockaddr *)&cservaddr, sizeof(cservaddr)) == -1) + { + cs_log("WARNING: Connect to stream source port %d failed", cfg.stream_source_port); + return -1; + } + + if (stream_source_auth) + { + snprintf(http_buf, http_buf_len, "GET %s HTTP/1.1\nHost: %s:%u\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n" + "Accept-Language: en-US\n" + "Authorization: Basic %s\n" + "Connection: keep-alive\n\n", stream_path, stream_source_host, cfg.stream_source_port, stream_source_auth); + } + else + { + snprintf(http_buf, http_buf_len, "GET %s HTTP/1.1\nHost: %s:%u\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n" + "Accept-Language: en-US\n" + "Connection: keep-alive\n\n", stream_path, stream_source_host, cfg.stream_source_port); + } + + if (send(streamfd, http_buf, cs_strlen(http_buf), 0) == -1) { return -1; } + return streamfd; +} + +static void stream_client_disconnect(stream_client_conn_data *conndata) +{ + int32_t i; + + SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + stream_cur_srvid[conndata->connid] = NO_SRVID_VALUE; + SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + + SAFE_MUTEX_LOCK(&stream_server_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (gconnfd[i] == conndata->connfd) + { + gconnfd[i] = -1; + gconncount--; + } + } + SAFE_MUTEX_UNLOCK(&stream_server_mutex); + + shutdown(conndata->connfd, 2); + close(conndata->connfd); + + cs_log("Stream client %i disconnected",conndata->connid); + + NULLFREE(conndata); +} + +static void *stream_client_handler(void *arg) +{ + stream_client_conn_data *conndata = (stream_client_conn_data *)arg; + stream_client_data *data; + + char *http_buf, stream_path[255], stream_path_copy[255]; + char *saveptr, *token, http_version[4]; + + int8_t streamConnectErrorCount = 0, streamDataErrorCount = 0; + int32_t bytesRead = 0, http_status_code = 0; + int32_t i, clientStatus, streamStatus, streamfd; + + uint8_t *stream_buf; + uint16_t packetCount = 0, packetSize = 0, startOffset = 0; + uint32_t remainingDataPos, remainingDataLength, tmp_pids[4]; + uint8_t descrambling = 0; + + const int32_t cur_dvb_buffer_size = DVB_BUFFER_SIZE_CSA; + const int32_t cur_dvb_buffer_wait = DVB_BUFFER_WAIT_CSA; + + struct dvbcsa_bs_batch_s *tsbbatch; + + cs_log("Stream client %i connected", conndata->connid); + + if (!cs_malloc(&http_buf, 1024)) + { + stream_client_disconnect(conndata); + return NULL; + } + + if (!cs_malloc(&stream_buf, DVB_BUFFER_SIZE)) + { + NULLFREE(http_buf); + stream_client_disconnect(conndata); + return NULL; + } + + if (!cs_malloc(&data, sizeof(stream_client_data))) + { + NULLFREE(http_buf); + NULLFREE(stream_buf); + stream_client_disconnect(conndata); + return NULL; + } + + clientStatus = recv(conndata->connfd, http_buf, 1024, 0); + if (clientStatus < 1) + { + NULLFREE(http_buf); + NULLFREE(stream_buf); + NULLFREE(data); + stream_client_disconnect(conndata); + return NULL; + } + + http_buf[1023] = '\0'; + if (sscanf(http_buf, "GET %254s ", stream_path) < 1) + { + NULLFREE(http_buf); + NULLFREE(stream_buf); + NULLFREE(data); + stream_client_disconnect(conndata); + return NULL; + } + + cs_strncpy(stream_path_copy, stream_path, sizeof(stream_path)); + + token = strtok_r(stream_path_copy, ":", &saveptr); // token 0 + for (i = 1; token != NULL && i < 7; i++) // tokens 1 to 6 + { + token = strtok_r(NULL, ":", &saveptr); + if (token == NULL) + { + break; + } + + if (i >= 3) // We olny need token 3 (srvid), 4 (tsid), 5 (onid) and 6 (ens) + { + if (sscanf(token, "%x", &tmp_pids[i - 3]) != 1) + { + tmp_pids[i - 3] = 0; + } + } + } + + data->srvid = tmp_pids[0] & 0xFFFF; + data->tsid = tmp_pids[1] & 0xFFFF; + data->onid = tmp_pids[2] & 0xFFFF; + data->ens = tmp_pids[3]; + + if (data->srvid == 0) // We didn't get a srvid - Exit + { + NULLFREE(http_buf); + NULLFREE(stream_buf); + NULLFREE(data); + stream_client_disconnect(conndata); + return NULL; + } + + key_data[conndata->connid].key[ODD] = dvbcsa_bs_key_alloc(); + key_data[conndata->connid].key[EVEN] = dvbcsa_bs_key_alloc(); + + if (!cs_malloc(&tsbbatch, (cluster_size + 1) * sizeof(struct dvbcsa_bs_batch_s))) + { + NULLFREE(http_buf); + NULLFREE(stream_buf); + NULLFREE(data); + stream_client_disconnect(conndata); + return NULL; + } + + SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + stream_cur_srvid[conndata->connid] = data->srvid; + SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + + cs_log("Stream client %i request %s", conndata->connid, stream_path); + + cs_log_dbg(D_READER, "Stream client %i received srvid: %04X tsid: %04X onid: %04X ens: %08X", + conndata->connid, data->srvid, data->tsid, data->onid, data->ens); + + snprintf(http_buf, 1024, "HTTP/1.0 200 OK\nConnection: Close\nContent-Type: video/mpeg\nServer: stream_enigma2\n\n"); + clientStatus = send(conndata->connfd, http_buf, cs_strlen(http_buf), 0); + + data->connid = conndata->connid; + data->caid = NO_CAID_VALUE; + data->have_pat_data = 0; + data->have_pmt_data = 0; + data->have_cat_data = 0; + data->have_ecm_data = 0; + data->have_emm_data = 0; + + while (!exit_oscam && clientStatus != -1 && streamConnectErrorCount < 3 + && streamDataErrorCount < 15) + { + streamfd = connect_to_stream(http_buf, 1024, stream_path); + if (streamfd == -1) + { + cs_log("WARNING: stream client %i cannot connect to stream source", conndata->connid); + streamConnectErrorCount++; + cs_sleepms(500); + continue; + } + streamStatus = 0; + bytesRead = 0; + while (!exit_oscam && clientStatus != -1 && streamStatus != -1 +#if 0 + && streamConnectErrorCount < 3 && streamDataErrorCount < 15) +#else + && (streamConnectErrorCount < 3 || streamDataErrorCount < 15)) +#endif + { + streamStatus = recv(streamfd, stream_buf + bytesRead, cur_dvb_buffer_size - bytesRead, MSG_WAITALL); + if (streamStatus == 0) // socket closed + { + cs_log("WARNING: stream client %i - stream source closed connection", conndata->connid); + streamConnectErrorCount++; + cs_sleepms(100); + break; + } + if (streamStatus < 0) // error + { + if ((errno == EWOULDBLOCK) | (errno == EAGAIN)) + { + cs_log("WARNING: stream client %i no data from stream source", conndata->connid); + streamDataErrorCount++; // 2 sec timeout * 15 = 30 seconds no data -> close + cs_sleepms(100); + continue; + } + cs_log("WARNING: stream client %i error receiving data from stream source", conndata->connid); + streamConnectErrorCount++; + cs_sleepms(100); + break; + } + if (streamStatus < cur_dvb_buffer_size - bytesRead) // probably just received header but no stream + { + if (!bytesRead && streamStatus > 13 && + sscanf((const char*)stream_buf, "HTTP/%3s %d ", http_version , &http_status_code) == 2 && + http_status_code != 200) + { + cs_log("ERROR: stream client %i got %d response from stream source", conndata->connid, http_status_code); + streamConnectErrorCount++; + cs_sleepms(100); + break; + } + else + { + cs_log_dbg(0, "WARNING: stream client %i non-full buffer from stream source", conndata->connid); + streamDataErrorCount++; + cs_sleepms(100); + } + } + else + { + streamDataErrorCount = 0; + } + + streamConnectErrorCount = 0; + bytesRead += streamStatus; + + if (bytesRead >= cur_dvb_buffer_wait) + { + startOffset = 0; + + // only search if not starting on ts packet or unknown packet size + if (stream_buf[0] != 0x47 || packetSize == 0) + { + SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset); + } + + if (packetSize == 0) + { + bytesRead = 0; + } + else + { + packetCount = ((bytesRead - startOffset) / packetSize); + + // 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)) + { + 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 + { + cs_log_dbg(D_READER, "Stream client %i caid %04X not enabled in stream relay config", + conndata->connid, data->caid); + } + } + else // Search PAT and PMT packets for service information + { + ParseTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize); + } + + clientStatus = send(conndata->connfd, stream_buf + startOffset, packetCount * packetSize, 0); + + remainingDataPos = startOffset + (packetCount * packetSize); + remainingDataLength = bytesRead - remainingDataPos; + + if (remainingDataPos < remainingDataLength) + { + memmove(stream_buf, stream_buf + remainingDataPos, remainingDataLength); + } + else + { + memcpy(stream_buf, stream_buf + remainingDataPos, remainingDataLength); + } + + bytesRead = remainingDataLength; + } + } + } + + close(streamfd); + } + + NULLFREE(http_buf); + NULLFREE(stream_buf); + + dvbcsa_bs_key_free(key_data[conndata->connid].key[ODD]); + dvbcsa_bs_key_free(key_data[conndata->connid].key[EVEN]); + NULLFREE(tsbbatch); + + NULLFREE(data); + + stream_client_disconnect(conndata); + return NULL; +} + +void *stream_server(void *UNUSED(a)) +{ + struct sockaddr_in servaddr, cliaddr; + socklen_t clilen; + int32_t connfd, reuse = 1, i; + int8_t connaccepted; + stream_client_conn_data *conndata; + + cluster_size = dvbcsa_bs_batch_size(); + cs_log("INFO: " +#if DVBCSA_KEY_ECM > 0 + "(ecm) " +#endif + "dvbcsa parallel mode = %d (relay buffer time: %d ms)", cluster_size, cfg.stream_relay_buffer_time); + + if (!stream_server_mutex_init) + { + SAFE_MUTEX_INIT(&stream_server_mutex, NULL); + stream_server_mutex_init = 1; + } + + SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + stream_cur_srvid[i] = NO_SRVID_VALUE; + } + SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + gconnfd[i] = -1; + } + + glistenfd = socket(AF_INET, SOCK_STREAM, 0); + if (glistenfd == -1) + { + cs_log("ERROR: cannot create stream server socket"); + return NULL; + } + + bzero(&servaddr,sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + servaddr.sin_port = htons(cfg.stream_relay_port); + setsockopt(glistenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + if (bind(glistenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) + { + cs_log("ERROR: cannot bind to stream server socket"); + close(glistenfd); + return NULL; + } + + if (listen(glistenfd, 3) == -1) + { + cs_log("ERROR: cannot listen to stream server socket"); + close(glistenfd); + return NULL; + } + + while (!exit_oscam) + { + clilen = sizeof(cliaddr); + connfd = accept(glistenfd,(struct sockaddr *)&cliaddr, &clilen); + + if (connfd == -1) + { + cs_log("ERROR: accept() failed"); + break; + } + + connaccepted = 0; + +#ifdef MODULE_RADEGAST + if(cfg.stream_client_source_host) + { + // Read ip of client who wants to play the stream + unsigned char *ip = (unsigned char *)&cliaddr.sin_addr.s_addr; + cs_log("Stream Client ip is: %d.%d.%d.%d, will fetch stream there\n", ip[0], ip[1], ip[2], ip[3]); + + // Store ip of client in stream_source_host variable + snprintf(stream_source_host, sizeof(stream_source_host), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + } +#endif + + if (cs_malloc(&conndata, sizeof(stream_client_conn_data))) + { + SAFE_MUTEX_LOCK(&stream_server_mutex); + if (gconncount < STREAM_SERVER_MAX_CONNECTIONS) + { + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (gconnfd[i] == -1) + { + gconnfd[i] = connfd; + gconncount++; + connaccepted = 1; + + conndata->connfd = connfd; + conndata->connid = i; + + break; + } + } + } + SAFE_MUTEX_UNLOCK(&stream_server_mutex); + } + + if (connaccepted) + { + int on = 1; + if (setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + { + cs_log("ERROR: stream client %i setsockopt() failed for TCP_NODELAY", conndata->connid); + } + + start_thread("stream client", stream_client_handler, (void*)conndata, NULL, 1, 0); + } + else + { + shutdown(connfd, 2); + close(connfd); + cs_log("ERROR: stream server client dropped because of too many connections (%i)", STREAM_SERVER_MAX_CONNECTIONS); + } + + cs_sleepms(20); + } + + close(glistenfd); + + return NULL; +} + +void init_stream_server(void) +{ + char authtmp[128]; + + if (cfg.stream_relay_enabled) + { + + cs_strncpy(stream_source_host, cfg.stream_source_host, sizeof(stream_source_host)); + + if (cfg.stream_source_auth_user && cfg.stream_source_auth_password) + { + snprintf(authtmp, sizeof(authtmp), "%s:%s", cfg.stream_source_auth_user, cfg.stream_source_auth_password); + b64encode(authtmp, cs_strlen(authtmp), &stream_source_auth); + } + + start_thread("stream_server", stream_server, NULL, NULL, 1, 1); + cs_log("Stream Relay server initialized"); + } +} + +void stop_stream_server(void) +{ + int32_t i; + + SAFE_MUTEX_LOCK(&stream_server_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (gconnfd[i] != -1) + { + shutdown(gconnfd[i], 2); + close(gconnfd[i]); + gconnfd[i] = -1; + } + } + + gconncount = 0; + SAFE_MUTEX_UNLOCK(&stream_server_mutex); + +#ifdef MODULE_RADEGAST + close_radegast_connection(); +#endif + + shutdown(glistenfd, 2); + close(glistenfd); +} + +#endif // MODULE_STREAMRELAY diff --git a/module-streamrelay.h b/module-streamrelay.h new file mode 100644 index 000000000..5b6785707 --- /dev/null +++ b/module-streamrelay.h @@ -0,0 +1,74 @@ +#ifndef MODULE_STREAMRELAY_H_ +#define MODULE_STREAMRELAY_H_ + +#ifdef MODULE_STREAMRELAY + +#define STREAM_SERVER_MAX_CONNECTIONS 16 + +#define DVB_MAX_TS_PACKETS 278 +#define DVB_BUFFER_SIZE_CSA 188*DVB_MAX_TS_PACKETS +#define DVB_BUFFER_WAIT_CSA 188*(DVB_MAX_TS_PACKETS-128) +#define DVB_BUFFER_SIZE DVB_BUFFER_SIZE_CSA + +//#define __BISS__ +#ifdef __BISS__ +#define MAX_STREAM_PIDS 32 +#endif + +#include "cscrypt/md5.h" +#include +#if DVBCSA_KEY_ECM > 0 +#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b) +#endif + +#define EVEN 0 +#define ODD 1 + +typedef struct +{ + struct dvbcsa_bs_key_s *key[2]; +} stream_client_key_data; + +typedef struct +{ + int32_t connid; + int8_t have_cat_data; + int8_t have_pat_data; + int8_t have_pmt_data; + int8_t have_ecm_data; + int8_t have_emm_data; + uint8_t cat_data[1024+208]; + uint8_t pat_data[1024+208]; + uint8_t pmt_data[1024+208]; + uint8_t ecm_data[1024+208]; + uint8_t emm_data[1024+208]; + uint16_t cat_data_pos; + uint16_t pat_data_pos; + uint16_t pmt_data_pos; + uint16_t ecm_data_pos; + uint16_t emm_data_pos; + uint16_t srvid; + uint16_t caid; + uint16_t tsid; + uint16_t onid; + uint32_t ens; + uint16_t pmt_pid; + 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]; +} stream_client_data; + +void *stream_server(void *a); +void init_stream_server(void); +void stop_stream_server(void); + +bool stream_write_cw(ECM_REQUEST *er); + +#endif // MODULE_STREAMRELAY + +#endif // MODULE_STREAMRELAY_H_ diff --git a/module-webif-tpl.c b/module-webif-tpl.c index e94d693b1..32e793f78 100644 --- a/module-webif-tpl.c +++ b/module-webif-tpl.c @@ -423,7 +423,6 @@ char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subd check_conf(READ_SDT_CHARSETS, ptr2); check_conf(CLOCKFIX, ptr2); check_conf(IPV6SUPPORT, ptr2); - check_conf(IRDETO_GUESSING, ptr2); check_conf(LCDSUPPORT, ptr2); check_conf(LEDSUPPORT, ptr2); check_conf(MODULE_CAMD33, ptr2); diff --git a/module-webif.c b/module-webif.c index 29c96083b..66d237197 100644 --- a/module-webif.c +++ b/module-webif.c @@ -1298,7 +1298,7 @@ static char *send_oscam_config_scam(struct templatevars *vars, struct uriparams } #endif -#ifdef WITH_EMU +#ifdef MODULE_STREAMRELAY static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uriparams *params) { char *value; @@ -1307,22 +1307,22 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri webif_save_config("streamrelay", vars, params); - tpl_printf(vars, TPLADD, "STREAM_SOURCE_HOST", "%s", cfg.emu_stream_source_host); - tpl_printf(vars, TPLADD, "STREAM_SOURCE_PORT", "%d", cfg.emu_stream_source_port); - if(cfg.emu_stream_source_auth_user) - { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_USER", "%s", cfg.emu_stream_source_auth_user); } - if(cfg.emu_stream_source_auth_password) - { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_PASSWORD", "%s", cfg.emu_stream_source_auth_password); } - tpl_printf(vars, TPLADD, "STREAM_RELAY_PORT", "%d", cfg.emu_stream_relay_port); - tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay); - - tpl_printf(vars, TPLADD, "TMP", "STREAMRELAYENABLEDSELECTED%d", cfg.emu_stream_relay_enabled); - tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_SOURCE_HOST", "%s", cfg.stream_source_host); + tpl_printf(vars, TPLADD, "STREAM_SOURCE_PORT", "%d", cfg.stream_source_port); + if(cfg.stream_source_auth_user) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_USER", "%s", cfg.stream_source_auth_user); } + if(cfg.stream_source_auth_password) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_PASSWORD", "%s", cfg.stream_source_auth_password); } +#ifdef MODULE_RADEGAST + tpl_addVar(vars, TPLADD, "STREAM_CLIENT_SOURCE_HOST", (cfg.stream_client_source_host == 1) ? "checked" : ""); +#endif + tpl_printf(vars, TPLADD, "STREAM_RELAY_PORT", "%d", cfg.stream_relay_port); + tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time); - tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled); + tpl_printf(vars, TPLADD, "TMP", "STREAMRELAYENABLEDSELECTED%d", cfg.stream_relay_enabled); tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); - value = mk_t_caidtab(&cfg.emu_stream_relay_ctab); + value = mk_t_caidtab(&cfg.stream_relay_ctab); tpl_addVar(vars, TPLADD, "STREAM_RELAY_CTAB", value); free_mk_t(value); @@ -1684,6 +1684,12 @@ static char *send_oscam_config_dvbapi(struct templatevars *vars, struct uriparam if(cfg.dvbapi_write_sdt_prov > 0) { tpl_addVar(vars, TPLADD, "WRITESDTPROVCHECKED", "checked"); } +#ifdef MODULE_STREAMRELAY + //demuxer_fix + if(cfg.dvbapi_demuxer_fix > 0) + { tpl_addVar(vars, TPLADD, "DEMUXERFIXCHECKED", "checked"); } +#endif + //TCP listen port if(cfg.dvbapi_listenport > 0) { tpl_printf(vars, TPLADD, "LISTENPORT", "%d", cfg.dvbapi_listenport); } @@ -1758,7 +1764,7 @@ static char *send_oscam_config(struct templatevars *vars, struct uriparams *para #ifdef MODULE_SCAM else if(!strcmp(part, "scam")) { return send_oscam_config_scam(vars, params); } #endif -#ifdef WITH_EMU +#ifdef MODULE_STREAMRELAY else if(!strcmp(part, "streamrelay")) { return send_oscam_config_streamrelay(vars, params); } #endif #ifdef MODULE_CCCAM @@ -2025,6 +2031,10 @@ static char *send_oscam_reader(struct templatevars *vars, struct uriparams *para tpl_addVar(vars, TPLADD, "LASTGSMS", rdr->last_gsms); } #endif + if(apicall) + { + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + } tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, rdr->label)); if(!existing_insert) { @@ -2048,6 +2058,15 @@ static char *send_oscam_reader(struct templatevars *vars, struct uriparams *para { tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", (const char*)new_proto); tpl_addVar(vars, TPLADD, "CLIENTPROTO", (const char*)new_proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", new_proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char*)new_proto); + } + } if(rdr->cacheex.feature_bitfield & 32) tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", rdr->cacheex.aio_version); @@ -2058,10 +2077,28 @@ static char *send_oscam_reader(struct templatevars *vars, struct uriparams *para { tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", proto); tpl_addVar(vars, TPLADD, "CLIENTPROTO", proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } } #else tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } #endif switch(rdr->card_status) { @@ -2119,6 +2156,15 @@ static char *send_oscam_reader(struct templatevars *vars, struct uriparams *para tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } } } @@ -2620,6 +2666,9 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam tpl_addVar(vars, TPLADD, "AUDISABLEDVALUE", (rdr->audisabled == 1) ? "1" : "0"); } + tpl_printf(vars, TPLADD, "TMP", "AUTYPE%d", rdr->autype); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + // AUprovid if(rdr->auprovid) { tpl_printf(vars, TPLADD, "AUPROVID", "%06X", rdr->auprovid); } @@ -2682,7 +2731,50 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam { tpl_printf(vars, TPLAPPEND, "BOXKEY", "%02X", rdr->boxkey[i]); } } +#ifdef READER_CONAX + // CWPK Key + len = rdr->cwpk_mod_length; + if(len > 0) + { + for(i = 0; i < len; i++) { tpl_printf(vars, TPLAPPEND, "CWPKKEY", "%02X", rdr->cwpk_mod[i]); } + } +#endif + +#ifdef READER_NAGRA + // nuid (CAK6.3) + len = rdr->cak63nuid_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63NUID", "%02X", rdr->cak63nuid[i]); } + } + + // cwekey (CAK6.3) + len = rdr->cak63cwekey_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63CWEKEY", "%02X", rdr->cak63cwekey[i]); } + } +#endif + #ifdef READER_NAGRA_MERLIN + // idird (CAK7) + len = rdr->idird_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "IDIRD", "%02X", rdr->idird[i]); } + } + + // cmd0e_provider (CAK7) + len = rdr->cmd0eprov_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CMD0EPROV", "%02X", rdr->cmd0eprov[i]); } + } + // mod1 (CAK7) len = rdr->mod1_length; if(len > 0) @@ -2691,6 +2783,38 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam { tpl_printf(vars, TPLAPPEND, "MOD1", "%02X", rdr->mod1[i]); } } + // mod2 (CAK7) + len = rdr->mod2_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD2", "%02X", rdr->mod2[i]); } + } + + // key3588 (CAK7) + len = rdr->key3588_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3588", "%02X", rdr->key3588[i]); } + } + + // key3310 (CAK7) + len = rdr->key3310_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3310", "%02X", rdr->key3310[i]); } + } + + // key3460 (CAK7) + len = rdr->key3460_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3460", "%02X", rdr->key3460[i]); } + } + // data50 (CAK7) len = rdr->data50_length; if(len > 0) @@ -2707,39 +2831,131 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam { tpl_printf(vars, TPLAPPEND, "MOD50", "%02X", rdr->mod50[i]); } } - // key60 (CAK7) - len = rdr->key60_length; + // nuid (CAK7) + len = rdr->nuid_length; if(len > 0) { for(i = 0; i < len ; i++) - { tpl_printf(vars, TPLAPPEND, "KEY60", "%02X", rdr->key60[i]); } + { tpl_printf(vars, TPLAPPEND, "NUID", "%02X", rdr->nuid[i]); } } - // exp60 (CAK7) - len = rdr->exp60_length; + // OTP CSC (CAK7) + len = rdr->otpcsc_length; if(len > 0) { for(i = 0; i < len ; i++) - { tpl_printf(vars, TPLAPPEND, "EXP60", "%02X", rdr->exp60[i]); } + { tpl_printf(vars, TPLAPPEND, "OTPCSC", "%02X", rdr->otpcsc[i]); } } - // nuid (CAK7) - len = rdr->nuid_length; + // OTA CSC (CAK7) + len = rdr->otacsc_length; if(len > 0) { for(i = 0; i < len ; i++) - { tpl_printf(vars, TPLAPPEND, "NUID", "%02X", rdr->nuid[i]); } + { tpl_printf(vars, TPLAPPEND, "OTACSC", "%02X", rdr->otacsc[i]); } } - // cwekey (CAK7) - len = rdr->cwekey_length; + // Force Pairing Type (CAK7) + len = rdr->forcepair_length; if(len > 0) { for(i = 0; i < len ; i++) - { tpl_printf(vars, TPLAPPEND, "CWEKEY", "%02X", rdr->cwekey[i]); } + { tpl_printf(vars, TPLAPPEND, "FORCEPAIR", "%02X", rdr->forcepair[i]); } } + + // cwekey0 (CAK7) + len = rdr->cwekey0_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY0", "%02X", rdr->cwekey0[i]); } + } + + // cwekey1 (CAK7) + len = rdr->cwekey1_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY1", "%02X", rdr->cwekey1[i]); } + } + + // cwekey2 (CAK7) + len = rdr->cwekey2_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY2", "%02X", rdr->cwekey2[i]); } + } + + // cwekey3 (CAK7) + len = rdr->cwekey3_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY3", "%02X", rdr->cwekey3[i]); } + } + + // cwekey4 (CAK7) + len = rdr->cwekey4_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY4", "%02X", rdr->cwekey4[i]); } + } + + // cwekey5 (CAK7) + len = rdr->cwekey5_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY5", "%02X", rdr->cwekey5[i]); } + } + + // cwekey6 (CAK7) + len = rdr->cwekey6_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY6", "%02X", rdr->cwekey6[i]); } + } + + // cwekey7 (CAK7) + len = rdr->cwekey7_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWEKEY7", "%02X", rdr->cwekey7[i]); } + } + + // force_cw_swap + if(rdr->forcecwswap) + { tpl_addVar(vars, TPLADD, "FORCECWSWAPCHECKED", "checked"); } + + // only_even_SA + if(rdr->evensa) + { tpl_addVar(vars, TPLADD, "EVENSACHECKED", "checked"); } + + // force_EMM_82 + if(rdr->forceemmg) + { tpl_addVar(vars, TPLADD, "FORCEEMMGCHECKED", "checked"); } + + // OTA_CWPKs + if(rdr->cwpkota) + { tpl_addVar(vars, TPLADD, "CWPKOTACHECKED", "checked"); } #endif + // CWPK CaID (CAK7) + len = rdr->cwpkcaid_length; + if(len > 0) + { + for(i = 0; i < len ; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKCAID", "%02X", rdr->cwpkcaid[i]); } + } + + // cak7_mode + if(rdr->cak7_mode) + { tpl_addVar(vars, TPLADD, "NAGRACAK7MODECHECKED", "checked"); } + // ins7E if(rdr->ins7E[0x1A]) { @@ -8434,7 +8650,7 @@ static char *send_oscam_api(struct templatevars * vars, FILE * f, struct uripara cmd_pack->client = webif_client; cmd_pack->cmdlen = strlen(getParam(params, "cmd")) / 2; - if(cmd_pack->cmdlen > 0 && abs(cmd_pack->cmdlen) <= sizeof(cmd_pack->cmd)) + if(cmd_pack->cmdlen > 0 && (unsigned long)abs(cmd_pack->cmdlen) <= sizeof(cmd_pack->cmd)) { if(key_atob_l(getParam(params, "cmd"), cmd_pack->cmd, cmd_pack->cmdlen*2)) { diff --git a/oscam-config-global.c b/oscam-config-global.c index 164601c3a..2721602f2 100644 --- a/oscam-config-global.c +++ b/oscam-config-global.c @@ -591,7 +591,7 @@ static const struct config_list webif_opts[] = DEF_OPT_INT8("http_status_log" , OFS(http_status_log) , 1), #endif #ifndef WEBIF_JQUERY - DEF_OPT_STR("http_extern_jquery" , OFS(http_extern_jquery) , "//code.jquery.com/jquery-1.12.4.min.js"), + DEF_OPT_STR("http_extern_jquery" , OFS(http_extern_jquery) , "//code.jquery.com/jquery-3.7.1.min.js"), #endif DEF_LAST_OPT }; @@ -910,23 +910,25 @@ static const struct config_list scam_opts[] = static const struct config_list scam_opts[] = { DEF_LAST_OPT }; #endif -#ifdef WITH_EMU +#ifdef MODULE_STREAMRELAY static bool streamrelay_should_save_fn(void *UNUSED(var)) { - return 1; + return cfg.stream_relay_enabled; } static const struct config_list streamrelay_opts[] = { DEF_OPT_SAVE_FUNC(streamrelay_should_save_fn), - DEF_OPT_STR("stream_source_host" , OFS(emu_stream_source_host), "127.0.0.1"), - DEF_OPT_INT32("stream_source_port" , OFS(emu_stream_source_port), 8001), - DEF_OPT_STR("stream_source_auth_user" , OFS(emu_stream_source_auth_user), NULL), - DEF_OPT_STR("stream_source_auth_password" , OFS(emu_stream_source_auth_password), NULL), - DEF_OPT_INT32("stream_relay_port" , OFS(emu_stream_relay_port), 17999), - DEF_OPT_UINT32("stream_ecm_delay" , OFS(emu_stream_ecm_delay), 600), - DEF_OPT_INT8("stream_relay_enabled" , OFS(emu_stream_relay_enabled), 1), - DEF_OPT_INT8("stream_emm_enabled" , OFS(emu_stream_emm_enabled), 1), - DEF_OPT_FUNC("stream_relay_ctab" , OFS(emu_stream_relay_ctab), check_caidtab_fn), + DEF_OPT_STR("stream_source_host" , OFS(stream_source_host), "127.0.0.1"), + DEF_OPT_INT32("stream_source_port" , OFS(stream_source_port), DEFAULT_STREAM_SOURCE_PORT), + DEF_OPT_STR("stream_source_auth_user" , OFS(stream_source_auth_user), NULL), + DEF_OPT_STR("stream_source_auth_password" , OFS(stream_source_auth_password), NULL), +#ifdef MODULE_RADEGAST + DEF_OPT_INT8("stream_client_source_host" , OFS(stream_client_source_host), 0), +#endif + DEF_OPT_INT32("stream_relay_port" , OFS(stream_relay_port), 17999), + DEF_OPT_INT8("stream_relay_enabled" , OFS(stream_relay_enabled), 0), + DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0), + DEF_OPT_FUNC("stream_relay_ctab" , OFS(stream_relay_ctab), check_caidtab_fn), DEF_LAST_OPT }; #else @@ -1355,6 +1357,9 @@ static const struct config_list dvbapi_opts[] = DEF_OPT_INT8("read_sdt" , OFS(dvbapi_read_sdt) , 0), DEF_OPT_INT8("write_sdt_prov" , OFS(dvbapi_write_sdt_prov) , 0), DEF_OPT_INT8("extended_cw_api", OFS(dvbapi_extended_cw_api), 0), +#ifdef MODULE_STREAMRELAY + DEF_OPT_INT8("demuxer_fix" , OFS(dvbapi_demuxer_fix) , 0), +#endif DEF_OPT_FUNC("boxtype" , OFS(dvbapi_boxtype) , dvbapi_boxtype_fn), DEF_OPT_FUNC("services" , OFS(dvbapi_sidtabs.ok) , dvbapi_services_fn), // OBSOLETE OPTIONS diff --git a/oscam-config-reader.c b/oscam-config-reader.c index c91ae93fa..61a15324f 100644 --- a/oscam-config-reader.c +++ b/oscam-config-reader.c @@ -230,6 +230,43 @@ static void boxid_fn(const char *token, char *value, void *setting, FILE *f) { fprintf_conf(f, token, "\n"); } } +static void cwpkkey_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + // rdr_log(rdr, "CWPK config key length: %16X", len); + if(len == 0 || len > 32) + { + rdr->cwpk_mod_length = 0; + memset(rdr->cwpk_mod, 0, sizeof(rdr->cwpk_mod)); + } + else + { + if(key_atob_l(value, rdr->cwpk_mod, len)) + { + fprintf(stderr, "reader cwpkkey parse error, %s=%s\n", token, value); + rdr->cwpk_mod_length = 0; + memset(rdr->cwpk_mod, 0, sizeof(rdr->cwpk_mod)); + } + else + { + rdr->cwpk_mod_length = len/2; + } + } + return; + } + int32_t len = rdr->cwpk_mod_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwpkkey", "%s\n", cs_hexdump(0, rdr->cwpk_mod, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwpkkey", "\n"); } +} + static void rsakey_fn(const char *token, char *value, void *setting, FILE *f) { struct s_reader *rdr = setting; @@ -375,6 +412,150 @@ static void mod1_fn(const char *token, char *value, void *setting, FILE *f) { fprintf_conf(f, "mod1", "\n"); } } +static void mod2_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 224) + { + rdr->mod2_length = 0; + memset(rdr->mod2, 0, 112); + } + else + { + if(key_atob_l(value, rdr->mod2, len)) + { + fprintf(stderr, "reader mod2 parse error, %s=%s\n", token, value); + rdr->mod2_length = 0; + memset(rdr->mod2, 0, sizeof(rdr->mod2)); + } + else + { + rdr->mod2_length = len/2; + } + } + return; + } + int32_t len = rdr->mod2_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "mod2", "%s\n", cs_hexdump(0, rdr->mod2, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "mod2", "\n"); } +} + +static void idird_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 8) + { + rdr->idird_length = 0; + memset(rdr->idird, 0, 4); + } + else + { + if(key_atob_l(value, rdr->idird, len)) + { + fprintf(stderr, "reader idird parse error, %s=%s\n", token, value); + rdr->idird_length = 0; + memset(rdr->idird, 0, sizeof(rdr->idird)); + } + else + { + rdr->idird_length = len/2; + } + } + return; + } + int32_t len = rdr->idird_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "idird", "%s\n", cs_hexdump(0, rdr->idird, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "idird", "\n"); } +} + +static void cmd0eprov_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 4) + { + rdr->cmd0eprov_length = 0; + memset(rdr->cmd0eprov, 0, 2); + } + else + { + if(key_atob_l(value, rdr->cmd0eprov, len)) + { + fprintf(stderr, "reader cmd0eprov parse error, %s=%s\n", token, value); + rdr->cmd0eprov_length = 0; + memset(rdr->cmd0eprov, 0, sizeof(rdr->cmd0eprov)); + } + else + { + rdr->cmd0eprov_length = len/2; + } + } + return; + } + int32_t len = rdr->cmd0eprov_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cmd0eprov", "%s\n", cs_hexdump(0, rdr->cmd0eprov, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cmd0eprov", "\n"); } +} + +static void key3588_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 272) + { + rdr->key3588_length = 0; + memset(rdr->key3588, 0, 136); + } + else + { + if(key_atob_l(value, rdr->key3588, len)) + { + fprintf(stderr, "reader key3588 parse error, %s=%s\n", token, value); + rdr->key3588_length = 0; + memset(rdr->key3588, 0, sizeof(rdr->key3588)); + } + else + { + rdr->key3588_length = len/2; + } + } + return; + } + int32_t len = rdr->key3588_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "key3588", "%s\n", cs_hexdump(0, rdr->key3588, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "key3588", "\n"); } +} + static void data50_fn(const char *token, char *value, void *setting, FILE *f) { struct s_reader *rdr = setting; @@ -447,7 +628,7 @@ static void mod50_fn(const char *token, char *value, void *setting, FILE *f) { fprintf_conf(f, "mod50", "\n"); } } -static void key60_fn(const char *token, char *value, void *setting, FILE *f) +static void key3460_fn(const char *token, char *value, void *setting, FILE *f) { struct s_reader *rdr = setting; if(value) @@ -455,72 +636,70 @@ static void key60_fn(const char *token, char *value, void *setting, FILE *f) int32_t len = cs_strlen(value); if(len != 192) { - rdr->key60_length = 0; - memset(rdr->key60, 0, 96); + rdr->key3460_length = 0; + memset(rdr->key3460, 0, 96); } else { - if(key_atob_l(value, rdr->key60, len)) + if(key_atob_l(value, rdr->key3460, len)) { - fprintf(stderr, "reader key60 parse error, %s=%s\n", token, value); - rdr->key60_length = 0; - memset(rdr->key60, 0, sizeof(rdr->key60)); + fprintf(stderr, "reader key3460 parse error, %s=%s\n", token, value); + rdr->key3460_length = 0; + memset(rdr->key3460, 0, sizeof(rdr->key3460)); } else { - rdr->key60_length = len/2; + rdr->key3460_length = len/2; } } return; } - int32_t len = rdr->key60_length; + int32_t len = rdr->key3460_length; if(len > 0) { char tmp[len * 2 + 1]; - fprintf_conf(f, "key60", "%s\n", cs_hexdump(0, rdr->key60, len, tmp, sizeof(tmp))); + fprintf_conf(f, "key3460", "%s\n", cs_hexdump(0, rdr->key3460, len, tmp, sizeof(tmp))); } else if(cfg.http_full_cfg) - { fprintf_conf(f, "key60", "\n"); } + { fprintf_conf(f, "key3460", "\n"); } } -static void exp60_fn(const char *token, char *value, void *setting, FILE *f) +static void key3310_fn(const char *token, char *value, void *setting, FILE *f) { struct s_reader *rdr = setting; if(value) { int32_t len = cs_strlen(value); - if(len != 192) + if(len != 32) { - rdr->exp60_length = 0; - memset(rdr->exp60, 0, 96); + rdr->key3310_length = 0; + memset(rdr->key3310, 0, 16); } else { - if(key_atob_l(value, rdr->exp60, len)) + if(key_atob_l(value, rdr->key3310, len)) { - fprintf(stderr, "reader exp60 parse error, %s=%s\n", token, value); - rdr->exp60_length = 0; - memset(rdr->exp60, 0, sizeof(rdr->exp60)); + fprintf(stderr, "reader key3310 parse error, %s=%s\n", token, value); + rdr->key3310_length = 0; + memset(rdr->key3310, 0, sizeof(rdr->key3310)); } else { - rdr->exp60_length = len/2; + rdr->key3310_length = len/2; } } return; } - int32_t len = rdr->exp60_length; + int32_t len = rdr->key3310_length; if(len > 0) { char tmp[len * 2 + 1]; - fprintf_conf(f, "exp60", "%s\n", cs_hexdump(0, rdr->exp60, len, tmp, sizeof(tmp))); + fprintf_conf(f, "key3310", "%s\n", cs_hexdump(0, rdr->key3310, len, tmp, sizeof(tmp))); } else if(cfg.http_full_cfg) - { fprintf_conf(f, "exp60", "\n"); } + { fprintf_conf(f, "key3310", "\n"); } } -#endif -#if defined(READER_NAGRA_MERLIN) || defined(READER_NAGRA) static void nuid_fn(const char *token, char *value, void *setting, FILE *f) { struct s_reader *rdr = setting; @@ -557,7 +736,477 @@ static void nuid_fn(const char *token, char *value, void *setting, FILE *f) { fprintf_conf(f, "nuid", "\n"); } } -static void cwekey_fn(const char *token, char *value, void *setting, FILE *f) +static void forcepair_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(len != 2) + { + rdr->forcepair_length = 0; + memset(rdr->forcepair, 0, 1); + } + else + { + if(key_atob_l(value, rdr->forcepair, len)) + { + fprintf(stderr, "reader forcepair parse error, %s=%s\n", token, value); + rdr->forcepair_length = 0; + memset(rdr->forcepair, 0, sizeof(rdr->forcepair)); + } + else + { + rdr->forcepair_length = len/2; + } + } + return; + } + int32_t len = rdr->forcepair_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "forcepair", "%s\n", cs_hexdump(0, rdr->forcepair, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "forcepair", "\n"); } +} + +static void otpcsc_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 4) + { + rdr->otpcsc_length = 0; + memset(rdr->otpcsc, 0, 2); + } + else + { + if(key_atob_l(value, rdr->otpcsc, len)) + { + fprintf(stderr, "reader otpcsc parse error, %s=%s\n", token, value); + rdr->otpcsc_length = 0; + memset(rdr->otpcsc, 0, sizeof(rdr->otpcsc)); + } + else + { + rdr->otpcsc_length = len/2; + } + } + return; + } + int32_t len = rdr->otpcsc_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "otpcsc", "%s\n", cs_hexdump(0, rdr->otpcsc, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "otpcsc", "\n"); } +} + +static void otacsc_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 4) + { + rdr->otacsc_length = 0; + memset(rdr->otacsc, 0, 2); + } + else + { + if(key_atob_l(value, rdr->otacsc, len)) + { + fprintf(stderr, "reader otacsc parse error, %s=%s\n", token, value); + rdr->otacsc_length = 0; + memset(rdr->otacsc, 0, sizeof(rdr->otacsc)); + } + else + { + rdr->otacsc_length = len/2; + } + } + return; + } + int32_t len = rdr->otacsc_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "otacsc", "%s\n", cs_hexdump(0, rdr->otacsc, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "otacsc", "\n"); } +} + +static void cwpkcaid_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 4) + { + rdr->cwpkcaid_length = 0; + memset(rdr->cwpkcaid, 0, 2); + } + else + { + if(key_atob_l(value, rdr->cwpkcaid, len)) + { + fprintf(stderr, "reader cwpkcaid parse error, %s=%s\n", token, value); + rdr->cwpkcaid_length = 0; + memset(rdr->cwpkcaid, 0, sizeof(rdr->cwpkcaid)); + } + else + { + rdr->cwpkcaid_length = len/2; + } + } + return; + } + int32_t len = rdr->cwpkcaid_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwpkcaid", "%s\n", cs_hexdump(0, rdr->cwpkcaid, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwpkcaid", "\n"); } +} + +static void cwekey0_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey0_length = 0; + memset(rdr->cwekey0, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey0, len)) + { + fprintf(stderr, "reader cwekey0 parse error, %s=%s\n", token, value); + rdr->cwekey0_length = 0; + memset(rdr->cwekey0, 0, sizeof(rdr->cwekey0)); + } + else + { + rdr->cwekey0_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey0_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey0", "%s\n", cs_hexdump(0, rdr->cwekey0, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey0", "\n"); } +} + +static void cwekey1_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey1_length = 0; + memset(rdr->cwekey1, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey1, len)) + { + fprintf(stderr, "reader cwekey1 parse error, %s=%s\n", token, value); + rdr->cwekey1_length = 0; + memset(rdr->cwekey1, 0, sizeof(rdr->cwekey1)); + } + else + { + rdr->cwekey1_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey1_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey1", "%s\n", cs_hexdump(0, rdr->cwekey1, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey1", "\n"); } +} + +static void cwekey2_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey2_length = 0; + memset(rdr->cwekey2, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey2, len)) + { + fprintf(stderr, "reader cwekey2 parse error, %s=%s\n", token, value); + rdr->cwekey2_length = 0; + memset(rdr->cwekey2, 0, sizeof(rdr->cwekey2)); + } + else + { + rdr->cwekey2_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey2_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey2", "%s\n", cs_hexdump(0, rdr->cwekey2, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey2", "\n"); } +} + +static void cwekey3_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey3_length = 0; + memset(rdr->cwekey3, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey3, len)) + { + fprintf(stderr, "reader cwekey3 parse error, %s=%s\n", token, value); + rdr->cwekey3_length = 0; + memset(rdr->cwekey3, 0, sizeof(rdr->cwekey3)); + } + else + { + rdr->cwekey3_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey3_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey3", "%s\n", cs_hexdump(0, rdr->cwekey3, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey3", "\n"); } +} + +static void cwekey4_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey4_length = 0; + memset(rdr->cwekey4, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey4, len)) + { + fprintf(stderr, "reader cwekey4 parse error, %s=%s\n", token, value); + rdr->cwekey4_length = 0; + memset(rdr->cwekey4, 0, sizeof(rdr->cwekey4)); + } + else + { + rdr->cwekey4_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey4_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey4", "%s\n", cs_hexdump(0, rdr->cwekey4, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey4", "\n"); } +} + +static void cwekey5_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey5_length = 0; + memset(rdr->cwekey5, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey5, len)) + { + fprintf(stderr, "reader cwekey5 parse error, %s=%s\n", token, value); + rdr->cwekey5_length = 0; + memset(rdr->cwekey5, 0, sizeof(rdr->cwekey5)); + } + else + { + rdr->cwekey5_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey5_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey5", "%s\n", cs_hexdump(0, rdr->cwekey5, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey5", "\n"); } +} + +static void cwekey6_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey6_length = 0; + memset(rdr->cwekey6, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey6, len)) + { + fprintf(stderr, "reader cwekey6 parse error, %s=%s\n", token, value); + rdr->cwekey6_length = 0; + memset(rdr->cwekey6, 0, sizeof(rdr->cwekey6)); + } + else + { + rdr->cwekey6_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey6_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey6", "%s\n", cs_hexdump(0, rdr->cwekey6, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey6", "\n"); } +} + +static void cwekey7_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + if(len != 32) + { + rdr->cwekey7_length = 0; + memset(rdr->cwekey7, 0, 16); + } + else + { + if(key_atob_l(value, rdr->cwekey7, len)) + { + fprintf(stderr, "reader cwekey7 parse error, %s=%s\n", token, value); + rdr->cwekey7_length = 0; + memset(rdr->cwekey7, 0, sizeof(rdr->cwekey7)); + } + else + { + rdr->cwekey7_length = len/2; + } + } + return; + } + int32_t len = rdr->cwekey7_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwekey7", "%s\n", cs_hexdump(0, rdr->cwekey7, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwekey7", "\n"); } +} +#endif + +#if defined(READER_NAGRA) +static void cak63nuid_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(len != 8) + { + rdr->cak63nuid_length = 0; + memset(rdr->cak63nuid, 0, 4); + } + else + { + if(key_atob_l(value, rdr->cak63nuid, len)) + { + fprintf(stderr, "reader cak63nuid parse error, %s=%s\n", token, value); + rdr->cak63nuid_length = 0; + memset(rdr->cak63nuid, 0, sizeof(rdr->cak63nuid)); + } + else + { + rdr->cak63nuid_length = len/2; + } + } + return; + } + int32_t len = rdr->cak63nuid_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cak63nuid", "%s\n", cs_hexdump(0, rdr->cak63nuid, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cak63nuid", "\n"); } +} + +static void cak63cwekey_fn(const char *token, char *value, void *setting, FILE *f) { struct s_reader *rdr = setting; if(value) @@ -565,32 +1214,32 @@ static void cwekey_fn(const char *token, char *value, void *setting, FILE *f) int32_t len = cs_strlen(value); if(len != 32) { - rdr->cwekey_length = 0; - memset(rdr->cwekey, 0, 16); + rdr->cak63cwekey_length = 0; + memset(rdr->cak63cwekey, 0, 16); } else { - if(key_atob_l(value, rdr->cwekey, len)) + if(key_atob_l(value, rdr->cak63cwekey, len)) { - fprintf(stderr, "reader cwekey parse error, %s=%s\n", token, value); - rdr->cwekey_length = 0; - memset(rdr->cwekey, 0, sizeof(rdr->cwekey)); + fprintf(stderr, "reader cak63cwekey parse error, %s=%s\n", token, value); + rdr->cak63cwekey_length = 0; + memset(rdr->cak63cwekey, 0, sizeof(rdr->cak63cwekey)); } else { - rdr->cwekey_length = len/2; + rdr->cak63cwekey_length = len/2; } } return; } - int32_t len = rdr->cwekey_length; + int32_t len = rdr->cak63cwekey_length; if(len > 0) { char tmp[len * 2 + 1]; - fprintf_conf(f, "cwekey", "%s\n", cs_hexdump(0, rdr->cwekey, len, tmp, sizeof(tmp))); + fprintf_conf(f, "cak63cwekey", "%s\n", cs_hexdump(0, rdr->cak63cwekey, len, tmp, sizeof(tmp))); } else if(cfg.http_full_cfg) - { fprintf_conf(f, "cwekey", "\n"); } + { fprintf_conf(f, "cak63cwekey", "\n"); } } #endif @@ -1205,18 +1854,42 @@ static const struct config_list reader_opts[] = DEF_OPT_FUNC("boxid" , 0, boxid_fn), DEF_OPT_FUNC("boxkey" , 0, boxkey_fn), DEF_OPT_FUNC("rsakey" , 0, rsakey_fn), + DEF_OPT_FUNC("cwpkkey" , 0, cwpkkey_fn), DEF_OPT_FUNC("deskey" , 0, deskey_fn), #ifdef READER_NAGRA_MERLIN DEF_OPT_FUNC("mod1" , 0, mod1_fn), + DEF_OPT_FUNC("idird" , 0, idird_fn), + DEF_OPT_FUNC("cmd0eprov" , 0, cmd0eprov_fn), + DEF_OPT_FUNC("mod2" , 0, mod2_fn), + DEF_OPT_FUNC("key3588" , 0, key3588_fn), + DEF_OPT_FUNC("key3460" , 0, key3460_fn), + DEF_OPT_FUNC("key3310" , 0, key3310_fn), DEF_OPT_FUNC("data50" , 0, data50_fn), DEF_OPT_FUNC("mod50" , 0, mod50_fn), - DEF_OPT_FUNC("key60" , 0, key60_fn), - DEF_OPT_FUNC("exp60" , 0, exp60_fn), -#endif -#if defined(READER_NAGRA_MERLIN) || defined(READER_NAGRA) DEF_OPT_FUNC("nuid" , 0, nuid_fn), - DEF_OPT_FUNC("cwekey" , 0, cwekey_fn), + DEF_OPT_FUNC("forcepair" , 0, forcepair_fn), + DEF_OPT_FUNC("otpcsc" , 0, otpcsc_fn), + DEF_OPT_FUNC("otacsc" , 0, otacsc_fn), + DEF_OPT_FUNC("cwpkcaid" , 0, cwpkcaid_fn), + DEF_OPT_FUNC("cwekey0" , 0, cwekey0_fn), + DEF_OPT_FUNC("cwekey1" , 0, cwekey1_fn), + DEF_OPT_FUNC("cwekey2" , 0, cwekey2_fn), + DEF_OPT_FUNC("cwekey3" , 0, cwekey3_fn), + DEF_OPT_FUNC("cwekey4" , 0, cwekey4_fn), + DEF_OPT_FUNC("cwekey5" , 0, cwekey5_fn), + DEF_OPT_FUNC("cwekey6" , 0, cwekey6_fn), + DEF_OPT_FUNC("cwekey7" , 0, cwekey7_fn), + DEF_OPT_INT8("forcecwswap" , OFS(forcecwswap), 0), + DEF_OPT_INT8("evensa" , OFS(evensa), 0), + DEF_OPT_INT8("forceemmg" , OFS(forceemmg), 0), + DEF_OPT_INT8("cwpkota" , OFS(cwpkota), 0), #endif +#if defined(READER_NAGRA) + DEF_OPT_FUNC("cak63nuid" , 0, cak63nuid_fn), + DEF_OPT_FUNC("cak63cwekey" , 0, cak63cwekey_fn), +#endif + + DEF_OPT_INT8("cak7_mode" , OFS(cak7_mode), 0), DEF_OPT_FUNC_X("ins7e" , OFS(ins7E), ins7E_fn, SIZEOF(ins7E)), DEF_OPT_FUNC_X("ins42" , OFS(ins42), ins42_fn, SIZEOF(ins42)), DEF_OPT_FUNC_X("ins7e11" , OFS(ins7E11), ins7E_fn, SIZEOF(ins7E11)), @@ -1295,6 +1968,7 @@ static const struct config_list reader_opts[] = #endif DEF_OPT_INT8("deprecated" , OFS(deprecated), 0), DEF_OPT_INT8("audisabled" , OFS(audisabled), 0), + DEF_OPT_INT8("autype" , OFS(autype), 0), DEF_OPT_FUNC("auprovid" , 0, auprovid_fn), DEF_OPT_INT8("ndsversion" , OFS(ndsversion), 0), DEF_OPT_FUNC("ratelimitecm" , 0, ratelimitecm_fn), @@ -1329,14 +2003,17 @@ static bool reader_check_setting(const struct config_list *UNUSED(clist), void * "fix9993", "rsakey", "deskey", "ins7e", "ins42", "ins7e11", "ins2e06", "k1_generic", "k1_unique", "force_irdeto", "needsemmfirst", "boxkey", "atr", "detect", "nagra_read", "mhz", "cardmhz", "readtiers", "read_old_classes", "use_gpio", "needsglobalfirst", #ifdef READER_NAGRA_MERLIN - "mod1", "data50", "mod50", "key60", "exp60", + "mod1", "idird", "cmd0eprov", "mod2", "key3588", "key3460", "key3310", "data50", "mod50", "nuid", "forcepair", "otpcsc", "otacsc", "cwpkcaid", "cwekey0", "cwekey1", "cwekey2", "cwekey3", "cwekey4", "cwekey5", "cwekey6", "cwekey7", #endif -#if defined(READER_NAGRA_MERLIN) || defined(READER_NAGRA) - "nuid", "cwekey", +#if defined(READER_NAGRA) + "cak63nuid", "cak63cwekey", #endif #if defined(READER_DRE) || defined(READER_DRECAS) "exec_cmd_file", #endif +#ifdef READER_CONAX + "cwpkkey", +#endif #ifdef WITH_AZBOX "mode", #endif @@ -1346,7 +2023,7 @@ static bool reader_check_setting(const struct config_list *UNUSED(clist), void * // These are written only when the reader is network reader static const char *network_only_settings[] = { - "user", "inactivitytimeout", "reconnecttimeout", + "user", "inactivitytimeout", "reconnecttimeout", "autype", 0 }; if(is_network_reader(reader)) diff --git a/oscam-config.c b/oscam-config.c index 530a7e629..f4bdfeb89 100644 --- a/oscam-config.c +++ b/oscam-config.c @@ -13,12 +13,9 @@ #include "oscam-string.h" #include "oscam-time.h" -extern uint16_t len4caid[256]; - #define cs_srid "oscam.srvid" #define cs_ratelimit "oscam.ratelimit" #define cs_trid "oscam.tiers" -#define cs_l4ca "oscam.guess" #define cs_sidt "oscam.services" #define cs_whitelist "oscam.whitelist" #define cs_provid "oscam.provid" @@ -1391,46 +1388,6 @@ void global_whitelist_read(void) } } -void init_len4caid(void) -{ - FILE *fp = open_config_file(cs_l4ca); - if(!fp) - { return; } - - int32_t nr; - char *value, *token; - - if(!cs_malloc(&token, MAXLINESIZE)) - { return; } - - memset(len4caid, 0, sizeof(uint16_t) << 8); - for(nr = 0; fgets(token, MAXLINESIZE, fp);) - { - int32_t i, c; - char *ptr; - if(!(value = strchr(token, ':'))) - { continue; } - *value++ = '\0'; - if((ptr = strchr(value, '#'))) - { * ptr = '\0'; } - if(cs_strlen(trim(token)) != 2) - { continue; } - if(cs_strlen(trim(value)) != 4) - { continue; } - if((i = byte_atob(token)) < 0) - { continue; } - if((c = word_atob(value)) < 0) - { continue; } - len4caid[i] = c; - nr++; - } - NULLFREE(token); - fclose(fp); - if(nr) - { cs_log("%d lengths for caid guessing loaded", nr); } - return; -} - #ifdef MODULE_SERIAL static struct s_twin *twin_read_int(void) { diff --git a/oscam-config.h b/oscam-config.h index a430ce360..e66a1aa1c 100644 --- a/oscam-config.h +++ b/oscam-config.h @@ -38,7 +38,6 @@ int32_t init_provid(void); int32_t init_srvid(void); int32_t init_tierid(void); int32_t init_fakecws(void); -void init_len4caid(void); #ifdef MODULE_SERIAL struct ecmtw get_twin(ECM_REQUEST *er); // get twin channel diff --git a/oscam-ecm.c b/oscam-ecm.c index 489fb380c..4bccafcff 100644 --- a/oscam-ecm.c +++ b/oscam-ecm.c @@ -7,7 +7,6 @@ #include "module-led.h" #include "module-stat.h" #include "module-webif.h" -#include "module-ird-guess.h" #include "module-cw-cycle-check.h" #include "module-gbox.h" #include "oscam-cache.h" @@ -30,7 +29,6 @@ extern CS_MUTEX_LOCK ecmcache_lock; extern struct ecm_request_t *ecmcwcache; -extern uint16_t len4caid[256]; extern uint32_t ecmcwcache_size; extern int32_t exit_oscam; @@ -2142,41 +2140,6 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui return res; } -static void guess_cardsystem(ECM_REQUEST *er) -{ - uint16_t last_hope = 0; - - // viaccess - check by provid-search - if((er->prid = chk_provid(er->ecm, 0x500))) - { er->caid = 0x500; } - - // nagra - // is ecm[1] always 0x30 ? - // is ecm[3] always 0x07 ? - if((er->ecm[6] == 1) && (er->ecm[4] == er->ecm[2] - 2)) - { er->caid = 0x1801; } - - // seca2 - very poor - if((er->ecm[8] == 0x10) && ((er->ecm[9] & 0xF1) == 1)) - { last_hope = 0x100; } - - // is cryptoworks, but which caid ? - if((er->ecm[3] == 0x81) && (er->ecm[4] == 0xFF) && - (!er->ecm[5]) && (!er->ecm[6]) && (er->ecm[7] == er->ecm[2] - 5)) - { - last_hope = 0xd00; - } - - if(!er->caid && er->ecm[2] == 0x31 && er->ecm[0x0b] == 0x28) - { guess_irdeto(er); } - - if(!er->caid) // guess by len... - { er->caid = len4caid[er->ecm[2] + 3]; } - - if(!er->caid) - { er->caid = last_hope; } -} - // chid calculation from module stat to here // to improve the quickfix concerning ecm chid info and extend it // to all client requests wereby the chid is known in module stat @@ -2474,27 +2437,9 @@ void get_cw(struct s_client *client, ECM_REQUEST *er) snprintf(er->msglog, sizeof(er->msglog), "invalid user group %s", username(client)); } - if(!er->caid) - { guess_cardsystem(er); } - - /* Quickfix Area */ - // add chid for all client requests as in module stat update_chid(er); - // quickfix for 0100:000065 - if(er->caid == 0x100 && er->prid == 0x65 && er->srvid == 0) - { er->srvid = 0x0642; } - - // Quickfixes for Opticum/Globo HD9500 - // Quickfix for 0500:030300 - if(er->caid == 0x500 && er->prid == 0x030300) - { er->prid = 0x030600; } - - // Quickfix for 0500:D20200 - if(er->caid == 0x500 && er->prid == 0xD20200) - { er->prid = 0x030600; } - // betacrypt ecm with nagra header if(chk_is_betatunnel_caid(er->caid) == 1 && (er->ecmlen == 0x89 || er->ecmlen == 0x4A) && er->ecm[3] == 0x07 && (er->ecm[4] == 0x84 || er->ecm[4] == 0x45)) { diff --git a/oscam-emm.c b/oscam-emm.c index fc75799c8..d0df4bab5 100644 --- a/oscam-emm.c +++ b/oscam-emm.c @@ -165,7 +165,31 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid if(reader->audisabled) { return 0; } - if(reader->caid != caid) + if(reader->cwpkcaid_length && reader->nuid_length) + { + uint8_t check[1]; + check[0] = caid & 0xFF; + if(check[0] == reader->cwpkcaid[1]) + { + return 1; + } + } + + uint16_t emmcaid; + if(reader->caid == 0x186D) + { + emmcaid = reader->caid - 0x03; + } + else if (reader->caid == 0x1856) + { + emmcaid = reader->caid + 0x28; + } + else + { + emmcaid = reader->caid; + } + + if(emmcaid != caid) { int caid_found = 0; if (!reader->csystem) @@ -173,13 +197,13 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid for(i = 0; reader->csystem->caids[i]; i++) { uint16_t cs_caid = reader->csystem->caids[i]; - if (reader->caid && cs_caid == caid) + if (emmcaid && cs_caid == caid) { caid_found = 1; break; } - if ((reader->caid == 0) && chk_ctab_ex(caid, &reader->ctab)) + if ((emmcaid == 0) && chk_ctab_ex(caid, &reader->ctab)) { caid_found = 1; break; @@ -188,7 +212,7 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid } if(!caid_found) { - rdr_log_dbg(reader, D_EMM, "reader_caid %04X != emmpid caid %04X -> SKIP!", reader->caid, caid); + rdr_log_dbg(reader, D_EMM, "reader_caid %04X != emmpid caid %04X -> SKIP!", emmcaid, caid); return 0; } } diff --git a/oscam-work.c b/oscam-work.c index 148dc7dc4..bee981dc1 100644 --- a/oscam-work.c +++ b/oscam-work.c @@ -11,9 +11,6 @@ #include "oscam-string.h" #include "oscam-work.h" #include "reader-common.h" -#ifdef READER_NAGRA_MERLIN -#include "reader-nagracak7.h" -#endif #include "module-cccam.h" #include "module-cccam-data.h" #include "module-cccshare.h" @@ -130,6 +127,7 @@ void *work_thread(void *ptr) cl->work_mbuf = mbuf; // Track locally allocated data, because some callback may call cs_exit/cs_disconect_client/pthread_exit and then mbuf would be leaked int32_t n = 0, rc = 0, i, idx, s, dblvl; + (void)dblvl; uint8_t dcw[16]; int8_t restart_reader = 0; @@ -305,6 +303,7 @@ void *work_thread(void *ptr) break; case ACTION_READER_SENDCMD: +#ifdef WITH_CARDREADER dblvl = cs_dblevel; cs_dblevel = dblvl | D_READER; rc = cardreader_do_rawcmd(reader, data->ptr); @@ -323,6 +322,7 @@ void *work_thread(void *ptr) } } cs_dblevel = dblvl; +#endif break; case ACTION_READER_CARDINFO: @@ -330,14 +330,10 @@ void *work_thread(void *ptr) break; case ACTION_READER_POLL_STATUS: +#ifdef READER_VIDEOGUARD cardreader_poll_status(reader); - break; - -#ifdef READER_NAGRA_MERLIN - case ACTION_READER_RENEW_SK: - CAK7_getCamKey(reader); - break; #endif + break; case ACTION_READER_INIT: if(!cl->init_done) diff --git a/oscam-work.h b/oscam-work.h index 5fb9e75ea..ac8368957 100644 --- a/oscam-work.h +++ b/oscam-work.h @@ -16,10 +16,7 @@ enum actions ACTION_READER_CHECK_HEALTH = 11, // wr11 ACTION_READER_CAPMT_NOTIFY = 12, // wr12 ACTION_READER_POLL_STATUS = 13, // wr13 -#ifdef READER_NAGRA_MERLIN - ACTION_READER_RENEW_SK = 14, // wr14 -#endif - ACTION_READER_SENDCMD = 15, // wr15 + ACTION_READER_SENDCMD = 14, // wr14 // Client actions ACTION_CLIENT_UDP = 22, // wc22 ACTION_CLIENT_TCP = 23, // wc23 diff --git a/oscam.c b/oscam.c index 137a7ded3..93809669f 100644 --- a/oscam.c +++ b/oscam.c @@ -16,13 +16,13 @@ #include "module-dvbapi-mca.h" #include "module-dvbapi-chancache.h" #include "module-gbox-sms.h" -#include "module-ird-guess.h" #include "module-lcd.h" #include "module-led.h" #include "module-stat.h" #include "module-webif.h" #include "module-webif-tpl.h" #include "module-cw-cycle-check.h" +#include "module-streamrelay.h" #include "oscam-chk.h" #include "oscam-cache.h" #include "oscam-client.h" @@ -43,7 +43,6 @@ #ifdef WITH_EMU void add_emu_reader(void); - void stop_stream_server(void); #endif #ifdef WITH_SSL @@ -90,7 +89,6 @@ struct s_client *first_client = NULL; // Pointer to clients list, first client i struct s_reader *first_active_reader = NULL; // list of active readers (enable=1 deleted = 0) LLIST *configured_readers = NULL; // list of all (configured) readers -uint16_t len4caid[256]; // table for guessing caid (by len) char cs_confdir[128]; uint16_t cs_dblevel = 0; // Debug Level int32_t thread_pipe[2] = {0, 0}; @@ -149,7 +147,7 @@ static void show_usage(void) "| |_| |___) | |_| (_| | | | | | |\n" " \\___/|____/ \\___\\__,_|_| |_| |_|\n\n"); printf("OSCam Cardserver v%s, build r%s (%s)\n", CS_VERSION, CS_SVN_VERSION, CS_TARGET); - printf("Copyright (C) 2009-2020 OSCam developers.\n"); + printf("Copyright (C) 2009-2024 OSCam developers.\n"); printf("This program is distributed under GPLv3.\n"); printf("OSCam is based on Streamboard mp-cardserver v0.9d written by dukat\n"); printf("Visit https://board.streamboard.tv/ for more details.\n\n"); @@ -416,6 +414,10 @@ static void write_versionfile(bool use_stdout) write_conf(HAVE_DVBAPI, "DVB API support"); if(config_enabled(HAVE_DVBAPI)) { + if(config_enabled(MODULE_STREAMRELAY)) + { + write_conf(true, "DVB API with Stream Relay support"); + } write_conf(WITH_AZBOX, "DVB API with AZBOX support"); write_conf(WITH_MCA, "DVB API with MCA support"); write_conf(WITH_COOLAPI, "DVB API with COOLAPI support"); @@ -425,7 +427,6 @@ static void write_versionfile(bool use_stdout) write_conf(WITH_NEUTRINO, "DVB API with NEUTRINO support"); write_conf(READ_SDT_CHARSETS, "DVB API read-sdt charsets"); } - write_conf(IRDETO_GUESSING, "Irdeto guessing"); write_conf(CS_ANTICASC, "Anti-cascading support"); write_conf(WITH_DEBUG, "Debug mode"); write_conf(MODULE_MONITOR, "Monitor"); @@ -444,6 +445,9 @@ static void write_versionfile(bool use_stdout) case CLOCK_TYPE_MONOTONIC: write_conf(CLOCKFIX, "Clockfix with monotonic clock"); break; } write_conf(IPV6SUPPORT, "IPv6 support"); +#if defined(__arm__) || defined(__aarch64__) + write_conf(WITH_ARM_NEON, "ARM NEON (SIMD/MPE) support"); +#endif write_conf(WITH_EMU, "Emulator support"); write_conf(WITH_SOFTCAM, "Built-in SoftCam.Key"); @@ -461,6 +465,7 @@ static void write_versionfile(bool use_stdout) write_conf(MODULE_CONSTCW, "constant CW"); write_conf(MODULE_PANDORA, "Pandora"); write_conf(MODULE_GHTTP, "ghttp"); + write_conf(MODULE_STREAMRELAY, "Streamrelay"); fprintf(fp, "\n"); write_conf(WITH_CARDREADER, "Reader support"); @@ -1843,6 +1848,9 @@ int32_t main(int32_t argc, char *argv[]) init_sidtab(); init_readerdb(); +#ifdef MODULE_STREAMRELAY + init_stream_server(); +#endif #ifdef WITH_EMU add_emu_reader(); #endif @@ -1857,9 +1865,6 @@ int32_t main(int32_t argc, char *argv[]) cacheex_init(); - init_len4caid(); - init_irdeto_guess_tab(); - write_versionfile(false); led_init(); @@ -1934,7 +1939,7 @@ int32_t main(int32_t argc, char *argv[]) #ifdef MODULE_GBOX stop_gbx_ticker(); #endif -#ifdef WITH_EMU +#ifdef MODULE_STREAMRELAY stop_stream_server(); #endif webif_close(); @@ -1993,7 +1998,6 @@ int32_t main(int32_t argc, char *argv[]) cfg.account = NULL; init_free_sidtab(); free_readerdb(); - free_irdeto_guess_tab(); config_free(); ssl_done(); diff --git a/reader-common.c b/reader-common.c index 5df23a694..cb6753f7b 100644 --- a/reader-common.c +++ b/reader-common.c @@ -37,6 +37,11 @@ static void reader_nullcard(struct s_reader *reader) reader->csystem = NULL; memset(reader->hexserial, 0, sizeof(reader->hexserial)); memset(reader->prid, 0xFF, sizeof(reader->prid)); + memset(reader->sa, 0, sizeof(reader->sa)); + memset(reader->emm84, 0, sizeof(reader->emm84)); + memset(reader->emm83s, 0, sizeof(reader->emm83s)); + memset(reader->emm83u, 0, sizeof(reader->emm83u)); + memset(reader->emm87, 0, sizeof(reader->emm87)); reader->caid = 0; reader->nprov = 0; cs_clear_entitlement(reader); diff --git a/reader-conax.c b/reader-conax.c index 00317be77..3b08ade26 100644 --- a/reader-conax.c +++ b/reader-conax.c @@ -2,6 +2,91 @@ #ifdef READER_CONAX #include "cscrypt/bn.h" #include "reader-common.h" +#include "cscrypt/des.h" + +static int32_t CWPK_CNX(struct s_reader *reader,uint8_t *msg) +{ +int32_t ret = 0; + +uint8_t CWp1[8]; +uint8_t CWp2[8]; +uint8_t CWs1[8]; +uint8_t CWs2[8]; + +CWp1[0] = msg[7]; +CWp1[1] = msg[8]; +CWp1[2] = msg[9]; +CWp1[3] = msg[10]; +CWp1[4] = msg[11]; +CWp1[5] = msg[12]; +CWp1[6] = msg[13]; +CWp1[7] = msg[14]; + +CWp2[0] = msg[22]; +CWp2[1] = msg[23]; +CWp2[2] = msg[24]; +CWp2[3] = msg[25]; +CWp2[4] = msg[26]; +CWp2[5] = msg[27]; +CWp2[6] = msg[28]; +CWp2[7] = msg[29]; + +des_ecb3_decrypt(CWp1,reader->cwpk_mod); +des_ecb3_decrypt(CWp2,reader->cwpk_mod); +CWs1[0] = CWp1[4]; +CWs1[1] = CWp1[5]; +CWs1[2] = CWp1[6]; +CWs1[3] = CWp1[7]; +CWs1[4] = CWp1[0]; +CWs1[5] = CWp1[1]; +CWs1[6] = CWp1[2]; +CWs1[7] = CWp1[3]; + +CWs2[0] = CWp2[4]; +CWs2[1] = CWp2[5]; +CWs2[2] = CWp2[6]; +CWs2[3] = CWp2[7]; +CWs2[4] = CWp2[0]; +CWs2[5] = CWp2[1]; +CWs2[6] = CWp2[2]; +CWs2[7] = CWp2[3]; + +int chkok = 1; +if(((CWs1[0] + CWs1[1] + CWs1[2]) & 0xFF) != CWs1[3]) +{ + chkok = 0; + rdr_log(reader, "CW0 checksum error [0]"); +} +if(((CWs1[4] + CWs1[5] + CWs1[6]) & 0xFF) != CWs1[7]) +{ + chkok = 0; + rdr_log(reader, "CW0 checksum error [1]"); +} +if(((CWs2[0] + CWs2[1] + CWs2[2]) & 0xFF) != CWs2[3]) +{ + chkok = 0; + rdr_log(reader, "CW1 checksum error [0]"); +} +if(((CWs2[4] + CWs2[5] + CWs2[6]) & 0xFF) != CWs2[7]) +{ + chkok = 0; + rdr_log(reader, "CW1 checksum error [1]"); +} + +if(chkok == 1) +{ + memcpy(&msg[7],CWs1,0x08); + memcpy(&msg[22],CWs2,0x08); + + ret = 0; +} +if(chkok != 1) +{ + ret = -8; +} + +return ret; +} static int32_t RSA_CNX(struct s_reader *reader, uint8_t *msg, uint8_t *mod, uint8_t *exp, uint32_t cta_lr, uint32_t modbytes, uint32_t expbytes) { @@ -114,6 +199,26 @@ static int32_t read_record(struct s_reader *reader, const uint8_t *cmd, const ui return (cta_lr - 2); } +static int32_t check_pairing(struct s_reader *reader, const uint8_t *cmd, const uint8_t *data, uint8_t *cta_res) +{ + uint16_t cta_lr; + + if(reader->cwpk_mod_length) + { + write_cmd(cmd, data); + rdr_log(reader, "CWPK Pairing is active"); + } + else if(reader->rsa_mod_length) + { + rdr_log(reader, "RSA Pairing is active"); + } + else + { + rdr_log(reader, "Pairing is not active"); + } + return OK; +} + static uint8_t PairingECMRotation(struct s_reader *reader, const ECM_REQUEST *er, int32_t n) { uint8_t cta_res[CTA_RES_LEN] = { 0x00 }; @@ -147,6 +252,7 @@ static int32_t conax_card_init(struct s_reader *reader, ATR *newatr) uint8_t cta_res[CTA_RES_LEN]; int32_t i, j, n; static const uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x10, 0x01, 0x40 }; + static const uint8_t inscp[] = { 0xDD, 0x26, 0x00, 0x00, 0x04, 0x6C, 0x02, 0x10,0x00 }; uint8_t ins82[] = { 0xDD, 0x82, 0x00, 0x00, 0x11, 0x11, 0x0f, 0x01, 0xb0, 0x0f, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x09, 0x04, 0x0b, 0x00, 0xe0, 0x30, 0x2b }; @@ -213,6 +319,7 @@ static int32_t conax_card_init(struct s_reader *reader, ATR *newatr) rdr_log(reader, "Provider: %d Provider-Id: %06X", j + 1, b2i(4, reader->prid[j])); rdr_log_sensitive(reader, "Provider: %d SharedAddress: {%08X}", j + 1, b2i(4, reader->sa[j])); } + check_pairing(reader, inscp, inscp + 5, cta_res); return OK; } @@ -239,16 +346,37 @@ static int32_t conax_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, stru uint8_t exp[] = { 0x01, 0x00, 0x01 }; uint8_t buf[256]; + char ppp = 0x00; + if((n = check_sct_len(er->ecm, 3)) < 0) { return ERROR; } buf[0] = 0x14; buf[1] = n + 1; - if(0x0 != PairingECMRotation(reader, er, n)) - { buf[2] = 2; } // card will answer with encrypted dw + if(reader->cwpk_mod_length) + { + buf[2] = 4; + ppp = 0x01; + } + else if(0x0 != reader->rsa_mod[0]) + { + if(0x0 != PairingECMRotation(reader, er, n)) + { + buf[2] = 2; + ppp = 0x03; + } + else + { + buf[2] = 0; + ppp = 0x02; + } + } else - { buf[2] = 0; } + { + buf[2] = 0; + ppp = 0x02; + } memcpy(buf + 3, er->ecm, n); insA2[4] = n + 3; @@ -263,13 +391,25 @@ static int32_t conax_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, stru if((cta_res[cta_lr - 2] == 0x98) || ((cta_res[cta_lr - 2] == 0x90))) { /*checks if answer is encrypted with RSA algo and decrypts it if needed*/ - if(0x81 == cta_res[0] && 2 == cta_res[2] >> 5) /*81 XX 5X*/ + if(0x81 == cta_res[0] && 2 == cta_res[2] >> 5 && 0x03 == ppp) /*81 XX 5X*/ { if(0x00 == cta_res[cta_lr - 1]) { rc = RSA_CNX(reader, cta_res, reader->rsa_mod, exp, cta_lr, 64u, 3u); } else { rc = -4; } /*card has no right to decode this channel*/ } + else if(0x01 == ppp) + { + if(0x00 == cta_res[cta_lr - 1]) + { + /*trying to decode using CWPK*/ + rc = CWPK_CNX(reader, cta_res); /*enabled when no loging needed*/ + } + else + { + rc = -4; + } + } if(0 == rc) { @@ -341,6 +481,10 @@ static int32_t conax_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, stru case -4: rdr_log(reader, "card has no right to decode this channel"); break; + + case -8: + rdr_log(reader, "CWPK is faulty"); + break; } /* answer 9011 - conax smart card need reset */ diff --git a/reader-dre-cas.c b/reader-dre-cas.c index d3bc3815d..642a27e85 100644 --- a/reader-dre-cas.c +++ b/reader-dre-cas.c @@ -236,14 +236,20 @@ static int32_t drecas_MSP_command(struct s_reader *reader, const uint8_t *cmd, i return OK; } +#define drecas_MSP_script_nb(cmd, len) \ + drecas_MSP_command(reader, cmd, len, cta_res, &cta_lr); \ + #define drecas_MSP_script(cmd, len) \ { \ - drecas_MSP_command(reader, cmd, len, cta_res, &cta_lr); \ + drecas_MSP_script_nb(cmd, len) \ } +#define drecas_MSP_cmd_nb(cmd) \ + drecas_MSP_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr); \ + #define drecas_MSP_cmd(cmd) \ { \ - drecas_MSP_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr); \ + drecas_MSP_cmd_nb(cmd) \ } static int32_t drecas_STM_command(struct s_reader *reader, const uint8_t *cmd, int32_t cmdlen, uint8_t *cta_res, uint16_t *p_cta_lr) @@ -342,14 +348,20 @@ static int32_t drecas_STM_command(struct s_reader *reader, const uint8_t *cmd, i return OK; } +#define drecas_STM_script_nb(cmd, len) \ + drecas_STM_command(reader, cmd, len, cta_res, &cta_lr); \ + #define drecas_STM_script(cmd, len) \ { \ - drecas_STM_command(reader, cmd, len, cta_res, &cta_lr); \ + drecas_STM_script_nb(cmd, len) \ } +#define drecas_STM_cmd_nb(cmd) \ + drecas_STM_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr); \ + #define drecas_STM_cmd(cmd) \ { \ - drecas_STM_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr); \ + drecas_STM_cmd_nb(cmd) \ } static int32_t drecas_set_provider_info(struct s_reader *reader) @@ -363,7 +375,7 @@ static int32_t drecas_set_provider_info(struct s_reader *reader) cs_clear_entitlement(reader); - if((drecas_MSP_cmd(subscr))) // ask subscription packages, returns error on 0x11 card + if(({drecas_MSP_cmd_nb(subscr)})) // ask subscription packages, returns error on 0x11 card { uint8_t pbm[32]; char tmp_dbg[65]; @@ -451,12 +463,12 @@ static int32_t drecas_card_init(struct s_reader *reader, ATR *newatr) cmd54[1] = csystem_data->provider; uint8_t geocode = 0; - if((drecas_MSP_cmd(cmd54))) // error would not be fatal, like on 0x11 cards + if(({drecas_MSP_cmd_nb(cmd54)})) // error would not be fatal, like on 0x11 cards { geocode = cta_res[7]; } providers[1] = csystem_data->provider; - if(!(drecas_MSP_cmd(providers))) + if(!({drecas_MSP_cmd_nb(providers)})) { return ERROR; } // fatal error if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) @@ -557,11 +569,11 @@ static int32_t drecas_card_init(struct s_reader *reader, ATR *newatr) if(tempbuf[0] != '7' && tempbuf[1] != '4') { - rdr_log(reader, "Script %s", (drecas_MSP_script(usercmd, cmd_len)) ? "done" : "error"); + rdr_log(reader, "Script %s", ({drecas_MSP_script_nb(usercmd, cmd_len)}) ? "done" : "error"); } else { - rdr_log(reader, "Script %s", (drecas_STM_script(usercmd, cmd_len)) ? "done" : "error"); + rdr_log(reader, "Script %s", ({drecas_STM_script_nb(usercmd, cmd_len)}) ? "done" : "error"); } } while(!feof(pFile)); @@ -637,7 +649,7 @@ static int32_t drecas_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, str ecmcmd51[33] = csystem_data->provider; // no part of sig - if((drecas_MSP_cmd(ecmcmd51))) // ecm request + if(({drecas_MSP_cmd_nb(ecmcmd51)})) // ecm request { if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) { return ERROR; } // exit if response is not 90 00 @@ -665,7 +677,7 @@ static int32_t drecas_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, str return ERROR; } - if(!(drecas_STM_cmd(stm_keys_t.stmcmd34[er->ecm[5] + (er->ecm[6] == 0x3B ? 0 : 32)]))) + if(!({drecas_STM_cmd_nb(stm_keys_t.stmcmd34[er->ecm[5] + (er->ecm[6] == 0x3B ? 0 : 32)])})) { rdr_log_dbg(reader, D_READER, "Error STM set key: %s",cs_hexdump(0, cta_res, cta_lr, tmp_dbg, sizeof(tmp_dbg))); return ERROR; @@ -681,7 +693,7 @@ static int32_t drecas_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, str stm_curkey[0] = er->ecm[5]; stm_curkey[1] = er->ecm[6]; - if(!(drecas_STM_cmd(ecmcmd33))) + if(!({drecas_STM_cmd_nb(ecmcmd33)})) { return ERROR; } if(cta_res[1] != 0x17 || cta_res[6] != 0xD2) @@ -745,7 +757,7 @@ static int32_t drecas_do_emm(struct s_reader *reader, EMM_PACKET *ep) memcpy(&emmcmd58[1], &ep->emm[40], 24); emmcmd58[25] = csystem_data->provider; - if((drecas_MSP_cmd(emmcmd58))) + if(({drecas_MSP_cmd_nb(emmcmd58)})) if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) { return ERROR; } } @@ -765,7 +777,7 @@ static int32_t drecas_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmcmd52[0x39] = csystem_data->provider; - if((drecas_MSP_cmd(emmcmd52))) + if(({drecas_MSP_cmd_nb(emmcmd52)})) if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) { return ERROR; } // exit if response is not 90 00 } @@ -802,7 +814,7 @@ static int32_t drecas_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmcmd52[0x39] = csystem_data->provider; - if((drecas_MSP_cmd(emmcmd52))) + if(({drecas_MSP_cmd_nb(emmcmd52)})) if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) { return ERROR; } // exit if response is not 90 00 } diff --git a/reader-dre.c b/reader-dre.c index f9d922234..0cafe07e4 100644 --- a/reader-dre.c +++ b/reader-dre.c @@ -177,19 +177,28 @@ static int32_t dre_command(struct s_reader *reader, const uint8_t *cmd, int32_t return OK; } +#define dre_script_nb(cmd, len, cmd_type, crypted, keynum) \ + dre_command(reader, cmd, len, cta_res, &cta_lr, crypted, keynum, crypted, cmd_type); \ + #define dre_script(cmd, len, cmd_type, crypted, keynum) \ { \ - dre_command(reader, cmd, len, cta_res, &cta_lr, crypted, keynum, crypted, cmd_type); \ + dre_script_nb(cmd, len, cmd_type, crypted, keynum) \ } +#define dre_cmd_nb(cmd) \ + dre_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr, 0, 0, 0, 0); \ + #define dre_cmd(cmd) \ { \ - dre_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr, 0, 0, 0, 0); \ + dre_cmd_nb(cmd) \ } +#define dre_cmd_c_nb(cmd,crypted,keynum) \ + dre_command(reader, cmd, sizeof(cmd),cta_res,&cta_lr, crypted, keynum, 1, 0); \ + #define dre_cmd_c(cmd,crypted,keynum) \ { \ - dre_command(reader, cmd, sizeof(cmd),cta_res,&cta_lr, crypted, keynum, 1, 0); \ + dre_cmd_c_nb(cmd,crypted,keynum) \ } static int32_t dre_set_provider_info(struct s_reader *reader) @@ -236,7 +245,7 @@ static int32_t dre_set_provider_info(struct s_reader *reader) chk_subscr: - if((dre_script(subscr, subscr_cmd_len, 0, 0, 0))) // ask subscription packages, returns error on 0x11 card + if(({dre_script_nb(subscr, subscr_cmd_len, 0, 0, 0)})) // ask subscription packages, returns error on 0x11 card { uint8_t pbm[subscr_len]; char tmp_dbg[subscr_len*2+1]; @@ -411,7 +420,7 @@ static int32_t dre_card_init(struct s_reader *reader, ATR *newatr) switch(atr[6]) { case 0: - if(!(dre_cmd(cmd56))) { return ERROR; } + if(!({dre_cmd_nb(cmd56)})) { return ERROR; } if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } switch(cta_res[4]) @@ -483,11 +492,11 @@ static int32_t dre_card_init(struct s_reader *reader, ATR *newatr) cmd54[1] = csystem_data->provider; uint8_t geocode = 0; - if((dre_cmd(cmd54))) // error would not be fatal, like on 0x11 cards + if(({dre_cmd_nb(cmd54)})) // error would not be fatal, like on 0x11 cards { geocode = cta_res[3]; } providers[1] = csystem_data->provider; - if(!(dre_cmd(providers))) + if(!({dre_cmd_nb(providers)})) { return ERROR; } // fatal error if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) @@ -639,7 +648,7 @@ static int32_t dre_card_init(struct s_reader *reader, ATR *newatr) /*ret =*/ - rdr_log(reader, "Script %s", (dre_script(usercmd, cmd_len, ignoreProvid, crypted, cryptkey)) ? "done" : "error"); + rdr_log(reader, "Script %s", ({dre_script_nb(usercmd, cmd_len, ignoreProvid, crypted, cryptkey)}) ? "done" : "error"); } while(!feof(pFile)); } @@ -708,7 +717,7 @@ static int32_t dre_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct rdr_log_dbg(reader, D_READER, "unused ECM info front:%s", cs_hexdump(0, er->ecm, 8, tmp_dbg, sizeof(tmp_dbg))); rdr_log_dbg(reader, D_READER, "unused ECM info back:%s", cs_hexdump(0, er->ecm + 24, er->ecm[2] + 2 - 24, tmp_dbg, sizeof(tmp_dbg))); - if((dre_cmd(ecmcmd41))) // ecm request + if(({dre_cmd_nb(ecmcmd41)})) // ecm request { if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 @@ -734,7 +743,7 @@ static int32_t dre_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct rdr_log_dbg(reader, D_READER, "unused ECM info back:%s", cs_hexdump(0, er->ecm + 37, 4, tmp_dbg, sizeof(tmp_dbg))); ecmcmd51[33] = csystem_data->provider; // no part of sig - if((dre_cmd(ecmcmd51))) // ecm request + if(({dre_cmd_nb(ecmcmd51)})) // ecm request { if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 @@ -863,7 +872,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) memcpy(&emmcmd58[1], &ep->emm[40], 24); emmcmd58[25] = csystem_data->provider; - if((dre_cmd(emmcmd58))) + if(({dre_cmd_nb(emmcmd58)})) if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } } @@ -882,7 +891,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmcmd52[0x39] = csystem_data->provider; - if((dre_cmd(emmcmd52))) + if(({dre_cmd_nb(emmcmd52)})) if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 } @@ -918,7 +927,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmcmd52[0x39] = csystem_data->provider; - if((dre_cmd(emmcmd52))) + if(({dre_cmd_nb(emmcmd52)})) if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 } @@ -1031,7 +1040,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) memcpy(&emmcmd91[9], &ep->emm[keypos], 8); - if((dre_cmd(emmcmd91))) + if(({dre_cmd_nb(emmcmd91)})) if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00) || (cta_res[2] != 0xA2)) return ERROR; // exit if response is not 90 00 @@ -1188,7 +1197,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmcmd42[49] = ep->emm[i * 49 + 41]; // keynr emmcmd42[50] = 0x58 + ep->emm[40]; // package nr emmcmd42[51] = csystem_data->provider; - if((dre_cmd(emmcmd42))) + if(({dre_cmd_nb(emmcmd42)})) { if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 @@ -1208,7 +1217,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) 59 05 A2 02 05 01 5B 90 00 */ - if((dre_cmd(emmcmd42))) // first emm request + if(({dre_cmd_nb(emmcmd42)})) // first emm request { if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 @@ -1221,7 +1230,7 @@ static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmcmd42[50] = 0x58; emmcmd42[49] = ep->emm[54]; // keynr - if((dre_cmd(emmcmd42))) // second emm request + if(({dre_cmd_nb(emmcmd42)})) // second emm request { if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } // exit if response is not 90 00 diff --git a/reader-nagra-common.c b/reader-nagra-common.c index eaf40c88c..0b044398a 100644 --- a/reader-nagra-common.c +++ b/reader-nagra-common.c @@ -2,83 +2,661 @@ #include "reader-common.h" #include "reader-nagra-common.h" -// returns 1 if shared emm matches SA, unique emm matches serial, or global or unknown +int32_t get_prov_idx(struct s_reader *rdr, const uint8_t *provid) +{ + int prov; + for(prov = 0; prov < rdr->nprov; prov++) // search for provider index + { + if(!memcmp(provid, &rdr->prid[prov][2], 2)) + { + return (prov); + } + } + return (-1); +} + int32_t nagra_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) { - switch(ep->emm[0]) + if(rdr->cak7type == 3 || rdr->autype == 1) { - case 0x83: - memset(ep->hexserial, 0x00, 0x08); - ep->hexserial[0] = ep->emm[5]; - ep->hexserial[1] = ep->emm[4]; - ep->hexserial[2] = ep->emm[3]; - if(ep->emm[7] == 0x10) - { + int i; + + switch(ep->emm[0]) + { + case 0x82: + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 3, 6); + if(!memcmp(rdr->hexserial, ep->hexserial, 6)) + { + ep->type = UNIQUE; + return 1; + } + else if ((ep->emm[3] == 0x00) && (ep->emm[4] == 0x00) && (ep->emm[5] == 0x00) && (ep->emm[6] == 0x00) && (ep->emm[7] == 0x00) && (ep->emm[8] == 0xD3) && (ep->emm[9] == 0x87)) + { + ep->type = GLOBAL; + return 1; + } + return 0; + + case 0x84: ep->type = SHARED; - return (!memcmp(rdr->sa[0], ep->hexserial, 0x03)); - } - else - { - ep->hexserial[3] = ep->emm[6]; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 5, 3); + i = get_prov_idx(rdr, ep->emm + 3); + + if(i == -1) + { + return 0; + } + + return (!memcmp(rdr->sa[i], ep->hexserial, 3)); + + case 0x83: + ep->type = GLOBAL; + uint8_t filtr[] = {0x83, 0x00, 0x74}; + return (!memcmp(ep->emm, filtr, 3)); + + case 0x90: ep->type = UNIQUE; - return (!memcmp(rdr->hexserial + 2, ep->hexserial, 0x04)); - } + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + return (!memcmp(rdr->nuid, ep->hexserial, 4)); + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } + } + else if(rdr->cak7type == 1) + { + int i; + switch(ep->emm[0]) + { + case 0x82: + ep->type = GLOBAL; + if(rdr->emm82 == 1 && ep->emm[3] == 0x00 && ep->emm[4] == 0x00 && ep->emm[5] == 0x00) + { + return 1; + } + return 0; + + case 0x83: + if(ep->emm[7] == 0x10) + { + ep->type = SHARED; + + for(i = 0; i < rdr->nemm83s; i++) + { + if(!memcmp(rdr->emm83s[i] + 1, ep->emm + 3, 0x03)) + { + return 1; + } + } + } + else + { + ep->type = UNIQUE; + + for(i = 0; i < rdr->nemm83u; i++) + { + if(!memcmp(rdr->emm83u[i] + 1, ep->emm + 3, 0x04)) + { + return 1; + } + } + } + return 0; - case 0x82: - ep->type = GLOBAL; - return 1; + case 0x84: + ep->type = GLOBAL; - default: - ep->type = UNKNOWN; - return 1; + for(i = 0; i < rdr->nemm84; i++) + { + if(!memcmp(rdr->emm84[i] + 1, ep->emm + 3, 0x02)) + { + return 1; + } + } + return 0; + + case 0x87: + ep->type = SHARED; + + for(i = 0; i < rdr->nemm87; i++) + { + if(!memcmp(rdr->emm87[i] + 1, ep->emm + 3, 0x04)) + { + return 1; + } + } + return 0; + + case 0x90: + ep->type = UNIQUE; + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + return (!memcmp(rdr->nuid, ep->hexserial, 4)); + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } + } + else if(rdr->autype == 2) + { + int i; + switch(ep->emm[0]) + { + case 0x82: + ep->type = GLOBAL; + if(ep->emm[3] == 0x00 && ep->emm[4] == 0x00 && ep->emm[5] == 0x00) + { + return 1; + } + return 0; + + case 0x83: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + if(ep->emm[7] == 0x10) + { + ep->type = SHARED; + + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + + if(!memcmp(rdr->sa[i], ep->hexserial, 0x03)) + { + return 1; + } + } + } + else + { + ep->hexserial[3] = ep->emm[6]; + ep->type = UNIQUE; + + return (!memcmp(rdr->hexserial + 2, ep->hexserial, 0x04)); + } + return 0; + + case 0x84: + ep->type = GLOBAL; + return 1; + + case 0x87: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + ep->type = SHARED; + + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + if(!memcmp(rdr->sa[i], ep->hexserial, 0x04)) + { + return 1; + } + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } + } + else + { + int i; + switch(ep->emm[0]) + { + case 0x82: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[6]; + ep->hexserial[2] = ep->emm[7]; + ep->hexserial[3] = ep->emm[8]; + if (!memcmp(rdr->hexserial + 2, ep->hexserial, 0x04)) + { + ep->type = UNIQUE; + return 1; + } + else if ((ep->emm[3] == 0x00) && (ep->emm[4] == 0x00) && (ep->emm[5] == 0x00) && (ep->emm[6] == 0x00) && (ep->emm[7] == 0x00) && ((ep->emm[8] == 0x04) || (ep->emm[8] == 0xD3)) && ((ep->emm[9] == 0x84) || (ep->emm[9] == 0x8F) || (ep->emm[9] == 0x87))) + { + ep->type = GLOBAL; + return 1; + } + return 0; + + case 0x84: + memset(ep->hexserial, 0x00, 0x08); + memcpy(ep->hexserial, ep->emm + 5, 3); + if ((ep->emm[2] == 0x77) && (ep->emm[3] == 0x00)) + { + ep->type = SHARED; + i = get_prov_idx(rdr, ep->emm + 3); + + if(i == -1) + { + return 0; + } + + return (!memcmp(rdr->sa[i], ep->hexserial, 3)); + } + else if ((ep->emm[3] == 0x00) && ((ep->emm[4] == 0x71) || (ep->emm[4] == 0x32) || (ep->emm[4] == 0xEC)) && (ep->emm[5] == 0x00) && (ep->emm[6] == 0x00) && (ep->emm[7] == 0x00) && (ep->emm[8] == 0x04) && (ep->emm[9] == 0x84)) + { + ep->type = GLOBAL; + return 1; + } + return 0; + + case 0x83: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + if(ep->emm[7] == 0x10) + { + ep->type = SHARED; + + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + + if(!memcmp(rdr->sa[i], ep->hexserial, 0x03)) + { + return 1; + } + } + } + else if (!memcmp(rdr->hexserial + 2, ep->hexserial, 0x04)) + { + ep->type = UNIQUE; + return 1; + } + else if ((ep->emm[5] == 0x04) && (ep->emm[6] == 0x70)) + { + ep->type = GLOBAL; + return 1; + } + return 0; + + case 0x87: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + ep->type = SHARED; + + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + if(!memcmp(rdr->sa[i], ep->hexserial, 0x03)) + { + return 1; + } + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } } } int32_t nagra_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) { - if(*emm_filters == NULL) + if(rdr->cak7type == 3 || rdr->autype == 1) + { + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 2 + (2 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], rdr->hexserial, 6); + memset(&filters[idx].mask[1], 0xFF, 6); + idx++; + + int32_t prov; + for(prov = 0; prov < rdr->nprov; prov++) + { + if(!memcmp(rdr->sa[prov], "\x00\x00\x00", 3)) + { + continue; + } + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + memcpy(&filters[idx].filter[3], &rdr->sa[prov], 3); + memset(&filters[idx].mask[3], 0xFF, 3); + idx++; + } + + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x90; + filters[idx].filter[1] = rdr->nuid[2]; + filters[idx].filter[2] = rdr->nuid[1]; + filters[idx].filter[3] = rdr->nuid[0]; + filters[idx].filter[4] = rdr->nuid[3]; + memset(&filters[idx].mask[0], 0xFF, 5); + idx++; + } + + *filter_count = idx; + } + + return OK; + } + else if(rdr->cak7type == 1) { - const unsigned int max_filter_count = 3; - if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + if(*emm_filters == NULL) { - return ERROR; + const unsigned int max_filter_count = 2 + (4 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + if(rdr->emm82 == 1) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + idx++; + } + + int32_t i; + for(i = 0; i < rdr->nemm83u; i++) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm83u[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + for(i = 0; i < rdr->nemm83s; i++) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm83s[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + for(i = 0; i < rdr->nemm84; i++) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm84[i], 3); + memset(&filters[idx].mask[0], 0xFF, 3); + idx++; + } + + for(i = 0; i < rdr->nemm87; i++) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm87[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x90; + filters[idx].filter[1] = rdr->nuid[2]; + filters[idx].filter[2] = rdr->nuid[1]; + filters[idx].filter[3] = rdr->nuid[0]; + filters[idx].filter[4] = rdr->nuid[3]; + memset(&filters[idx].mask[0], 0xFF, 5); + idx++; + } + + *filter_count = idx; } - struct s_csystem_emm_filter *filters = *emm_filters; - *filter_count = 0; - - int32_t idx = 0; - - filters[idx].type = EMM_UNIQUE; - filters[idx].enabled = 1; - filters[idx].filter[0] = 0x83; - filters[idx].filter[1] = rdr->hexserial[4]; - filters[idx].filter[2] = rdr->hexserial[3]; - filters[idx].filter[3] = rdr->hexserial[2]; - filters[idx].filter[4] = rdr->hexserial[5]; - filters[idx].filter[5] = 0x00; - memset(&filters[idx].mask[0], 0xFF, 6); - idx++; - - filters[idx].type = EMM_SHARED; - filters[idx].enabled = 1; - filters[idx].filter[0] = 0x83; - filters[idx].filter[1] = rdr->sa[0][2]; - filters[idx].filter[2] = rdr->sa[0][1]; - filters[idx].filter[3] = rdr->sa[0][0]; - filters[idx].filter[4] = 0x00; - filters[idx].filter[5] = 0x10; - memset(&filters[idx].mask[0], 0xFF, 6); - idx++; - - filters[idx].type = EMM_GLOBAL; - filters[idx].enabled = 1; - filters[idx].filter[0] = 0x82; - filters[idx].mask[0] = 0xFF; - idx++; - - *filter_count = idx; + return OK; } + else if(rdr->autype == 2) + { + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 3 + (2 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return 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] = 0x84; + filters[idx].mask[0] = 0xFF; + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->hexserial[4]; + filters[idx].filter[2] = rdr->hexserial[3]; + filters[idx].filter[3] = rdr->hexserial[2]; + filters[idx].filter[4] = rdr->hexserial[5]; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + int i; + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->sa[i][2]; + filters[idx].filter[2] = rdr->sa[i][1]; + filters[idx].filter[3] = rdr->sa[i][0]; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x10; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; - return OK; + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x87; + filters[idx].filter[1] = rdr->sa[i][2]; + filters[idx].filter[2] = rdr->sa[i][1]; + filters[idx].filter[3] = rdr->sa[i][0]; + filters[idx].filter[4] = rdr->sa[i][3]; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + *filter_count = idx; + } + + return OK; + } + else + { + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 5 + (3 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], rdr->hexserial, 6); + memset(&filters[idx].mask[1], 0xFF, 6); + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->hexserial[4]; + filters[idx].filter[2] = rdr->hexserial[3]; + filters[idx].filter[3] = rdr->hexserial[2]; + filters[idx].filter[4] = rdr->hexserial[5]; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + 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] = 0x84; + 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++; + + int32_t prov; + for(prov = 0; prov < rdr->nprov; prov++) + { + if(!memcmp(rdr->sa[prov], "\x00\x00\x00", 3)) + { + continue; + } + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + memcpy(&filters[idx].filter[3], &rdr->sa[prov], 3); + memset(&filters[idx].mask[3], 0xFF, 3); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->sa[prov][2]; + filters[idx].filter[2] = rdr->sa[prov][1]; + filters[idx].filter[3] = rdr->sa[prov][0]; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x10; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x87; + filters[idx].filter[1] = rdr->sa[prov][2]; + filters[idx].filter[2] = rdr->sa[prov][1]; + filters[idx].filter[3] = rdr->sa[prov][0]; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + *filter_count = idx; + } + + return OK; + } } diff --git a/reader-nagra.c b/reader-nagra.c index 141206d34..3e67b77d3 100644 --- a/reader-nagra.c +++ b/reader-nagra.c @@ -425,13 +425,13 @@ static int32_t NegotiateSessionKey(struct s_reader *reader) if(!csystem_data->is_n3_na) { - if (reader->nuid_length == 4) //nuid is set + if (reader->cak63nuid_length == 4) //nuid is set { // inject provid cmd2a[26] = reader->prid[0][2]; cmd2a[27] = reader->prid[0][3]; - memcpy(&cmd2a[1], reader->nuid, 4); // inject NUID + memcpy(&cmd2a[1], reader->cak63nuid, 4); // inject NUID if (!do_cmd(reader, 0x2a,0x1E,0xAA,0x42, cmd2a, cta_res, &cta_lr)) { @@ -659,7 +659,7 @@ static void addProvider(struct s_reader *reader, uint8_t *cta_res) static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_res, uint16_t cta_lr) { struct nagra_data *csystem_data = reader->csystem_data; - char ds[36], de[36]; + char ds[20], de[16]; uint16_t chid; switch(dt) @@ -828,7 +828,7 @@ static int32_t nagra2_card_init(struct s_reader *reader, ATR *newatr) } memcpy(reader->rom, cta_res + 2, 15); } - else if(reader->detect_seca_nagra_tunneled_card && memcmp(atr + 7, "pp", 2) == 0 && ((atr[9]&0x0F) >= 10)) + else if(!reader->cak7_mode && reader->detect_seca_nagra_tunneled_card && memcmp(atr + 7, "pp", 2) == 0 && ((atr[9]&0x0F) >= 10)) { rdr_log(reader, "detect seca/nagra tunneled card"); @@ -1394,14 +1394,14 @@ static int32_t nagra2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, str { rdr_log_dbg(reader, D_READER, "3DES encryption of CWs detected. Using CWPK index:%02X", (csystem_data->ird_info & 7)); - if(reader->cwekey_length != 16) + if(reader->cak63cwekey_length != 16) { rdr_log_dbg(reader, D_READER, "ERROR: Invalid CWPK, can not decrypt CW"); return ERROR; } - des_ecb3_decrypt(_cwe0, reader->cwekey); - des_ecb3_decrypt(_cwe1, reader->cwekey); + des_ecb3_decrypt(_cwe0, reader->cak63cwekey); + des_ecb3_decrypt(_cwe1, reader->cak63cwekey); rdr_log_dbg(reader, D_READER, "CW0 after 3DES decrypt: %s", cs_hexdump(1, _cwe0, 8, tmp_dbg, sizeof(tmp_dbg))); rdr_log_dbg(reader, D_READER, "CW1 after 3DES decrypt: %s", cs_hexdump(1, _cwe1, 8, tmp_dbg, sizeof(tmp_dbg))); diff --git a/reader-nagracak7.c b/reader-nagracak7.c index 6fc06ad00..e0368051d 100644 --- a/reader-nagracak7.c +++ b/reader-nagracak7.c @@ -1,25 +1,21 @@ #include "globals.h" #ifdef READER_NAGRA_MERLIN +#include "math.h" #include "cscrypt/bn.h" #include "cscrypt/idea.h" #include "csctapi/icc_async.h" #include "oscam-time.h" #include "reader-common.h" #include "reader-nagra-common.h" -#include "reader-nagracak7.h" #include "oscam-work.h" #include "cscrypt/des.h" #include "cscrypt/mdc2.h" - +static const uint8_t public_exponent[] = { 0x01, 0x00, 0x01 }; static const uint8_t d00ff[] = { 0x00, 0xFF, 0xFF, 0xFF }; -static uint8_t data1[] = { 0x00, 0x00, 0x00, 0x01 }; - // Datatypes -#define SYSID_CAID 0x02 -#define IRDINFO 0x03 -#define DT05 0x05 -#define TIERS 0x0C - +#define IRDINFO 0x03 +#define TIERS 0x0C +#define SYSID 0x05 static time_t tier_date(uint64_t date, char *buf, int32_t l) { time_t ut = +694224000L + (date >> 1); @@ -32,8 +28,7 @@ static time_t tier_date(uint64_t date, char *buf, int32_t l) } return ut; } - -void rsa_decrypt(uint8_t *edata50, int len, uint8_t *out, uint8_t *key, int keylen, uint8_t *expo, uint8_t expolen) +static void rsa_decrypt(uint8_t *edata50, int len, uint8_t *out, uint8_t *key, int keylen) { BN_CTX *ctx0 = BN_CTX_new(); #ifdef WITH_LIBCRYPTO @@ -44,7 +39,7 @@ void rsa_decrypt(uint8_t *edata50, int len, uint8_t *out, uint8_t *key, int keyl BIGNUM *bnCT0 = BN_CTX_get(ctx0); BIGNUM *bnPT0 = BN_CTX_get(ctx0); BN_bin2bn(&key[0], keylen, bnN0); - BN_bin2bn(&expo[0], expolen, bnE0); + BN_bin2bn(public_exponent, 0x03, bnE0); BN_bin2bn(&edata50[0], len, bnCT0); BN_mod_exp(bnPT0, bnCT0, bnE0, bnN0, ctx0); memset(out,0x00,len); @@ -52,88 +47,381 @@ void rsa_decrypt(uint8_t *edata50, int len, uint8_t *out, uint8_t *key, int keyl BN_CTX_end(ctx0); BN_CTX_free(ctx0); } - static void addProvider(struct s_reader *reader, uint8_t *cta_res) { - uint8_t i; + int i; bool toadd = true; - for(i = 0; i < reader->nprov; i++) { - if((cta_res[19] == reader->prid[i][2]) && (cta_res[20] == reader->prid[i][3])) + if((cta_res[0] == reader->prid[i][2]) && (cta_res[1] == reader->prid[i][3])) { toadd = false; } } - if(toadd) { reader->prid[reader->nprov][0] = 0; reader->prid[reader->nprov][1] = 0; - reader->prid[reader->nprov][2] = cta_res[19]; - reader->prid[reader->nprov][3] = cta_res[20]; - memcpy(reader->sa[reader->nprov], reader->sa[0], 0x04); + reader->prid[reader->nprov][2] = cta_res[0]; + reader->prid[reader->nprov][3] = cta_res[1]; reader->nprov += 1; } } - +static int32_t get_prov_index(struct s_reader *reader, const uint8_t *provid) +{ + int prov; + for(prov = 0; prov < reader->nprov; prov++) + { + if(!memcmp(provid, &reader->prid[prov][2], 2)) + { + return (prov); + } + } + return (-1); +} +static void addSA(struct s_reader *reader, uint8_t *cta_res) +{ + if((cta_res[0] == 0x83 && cta_res[5] == 0x10) || cta_res[0] == 0x87) + { + int i; + bool toadd = true; + if(reader->evensa) + { + unsigned long sax = (cta_res[3] << 16) + (cta_res[2] << 8) + (cta_res[1]); + if(sax % 2 != 0) + { + sax--; + cta_res[3]=(sax>>16)&0xFF; + cta_res[2]=(sax>>8)&0xFF; + cta_res[1]=(sax)&0xFF; + } + } + for(i = 0; i < reader->nsa; i++) + { + if((cta_res[1] == reader->sa[i][2]) && (cta_res[2] == reader->sa[i][1]) && (cta_res[3] == reader->sa[i][0]) && (cta_res[4] == reader->sa[i][3])) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00", 3))) + { + reader->sa[reader->nsa][0] = cta_res[3]; + reader->sa[reader->nsa][1] = cta_res[2]; + reader->sa[reader->nsa][2] = cta_res[1]; + reader->sa[reader->nsa][3] = cta_res[4]; + reader->nsa += 1; + } + } +} +static void addSAseca(struct s_reader *reader, uint8_t *cta_res) +{ + if(cta_res[0] == 0x84) + { + addProvider(reader, cta_res + 1); + if(memcmp(cta_res + 3, "\x00\x00\x00", 3)) + { + int i; + i = get_prov_index(reader, cta_res + 1); + memcpy(reader->sa[i], cta_res + 3, 3); + } + } +} +static void addemmfilter(struct s_reader *reader, uint8_t *cta_res) +{ + if(cta_res[0] == 0x82) + { + reader->emm82 = 1; + } + else if(cta_res[0] == 0x84) + { + int i; + bool toadd = true; + for(i = 0; i < reader->nemm84; i++) + { + if(!memcmp(cta_res, reader->emm84[i], 3)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00", 2))) + { + reader->emm84[reader->nemm84][0] = cta_res[0]; + reader->emm84[reader->nemm84][1] = cta_res[1]; + reader->emm84[reader->nemm84][2] = cta_res[2]; + reader->nemm84 += 1; + } + } + else if(cta_res[0] == 0x83 && cta_res[5] == 0x00) + { + int i; + bool toadd = true; + for(i = 0; i < reader->nemm83u; i++) + { + if(!memcmp(cta_res, reader->emm83u[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00\x00", 4))) + { + memcpy(reader->emm83u[reader->nemm83u], cta_res, 6); + reader->nemm83u += 1; + } + } + else if(cta_res[0] == 0x83 && cta_res[5] == 0x10) + { + int i; + bool toadd = true; + if(reader->evensa) + { + unsigned long sax = (cta_res[3] << 16) + (cta_res[2] << 8) + (cta_res[1]); + if(sax % 2 != 0) + { + sax--; + cta_res[3]=(sax>>16)&0xFF; + cta_res[2]=(sax>>8)&0xFF; + cta_res[1]=(sax)&0xFF; + } + } + for(i = 0; i < reader->nemm83s; i++) + { + if(!memcmp(cta_res, reader->emm83s[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00", 3))) + { + memcpy(reader->emm83s[reader->nemm83s], cta_res, 6); + reader->nemm83s += 1; + } + } + else if(cta_res[0] == 0x87) + { + int i; + bool toadd = true; + if(reader->evensa) + { + unsigned long sax = (cta_res[3] << 16) + (cta_res[2] << 8) + (cta_res[1]); + if(sax % 2 != 0) + { + sax--; + cta_res[3]=(sax>>16)&0xFF; + cta_res[2]=(sax>>8)&0xFF; + cta_res[1]=(sax)&0xFF; + } + } + for(i = 0; i < reader->nemm87; i++) + { + if(!memcmp(cta_res, reader->emm87[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00", 3))) + { + memcpy(reader->emm87[reader->nemm87], cta_res, 6); + reader->nemm87 += 1; + } + } +} static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_res, uint16_t cta_lr) { - char ds[36], de[36]; - + char ds[27], de[27]; switch(dt) { - case SYSID_CAID: + case 0x02: { reader->prid[0][0] = 0x00; reader->prid[0][1] = 0x00; reader->prid[0][2] = cta_res[19]; reader->prid[0][3] = cta_res[20]; - reader->prid[1][0] = 0x00; reader->prid[1][1] = 0x00; reader->prid[1][2] = 0x00; reader->prid[1][3] = 0x00; - memcpy(reader->sa[1], reader->sa[0], 0x04); reader->nprov += 1; reader->caid = (SYSTEM_NAGRA | cta_res[25]); rdr_log_dbg(reader, D_READER, "CAID : %04X", reader->caid); return OK; } - case IRDINFO: // case 0x03 { if(cta_res[21] == 0x9C) { uint32_t timestamp = b2i(0x04, cta_res + 22); - reader->card_valid_to = tier_date(timestamp, de, 11); - rdr_log(reader, "Provider Sys ID: %02X %02X is active to: %s", cta_res[19], cta_res[20], de); + uint8_t timestamp186D[4] = {0xA6, 0x9E, 0xFB, 0x7F}; + uint32_t timestamp186Db2i = b2i(0x04, timestamp186D); + if(reader->caid == 0x186D) + { + reader->card_valid_to = tier_date(timestamp186Db2i, de, 11); + } + else + { + reader->card_valid_to = tier_date(timestamp, de, 11); + } + uint16_t chid = 0; + uint32_t id = b2i(0x02, cta_res + 19); + uint32_t start_date; + uint32_t expire_date; + start_date = 1; + expire_date = b2i(0x04, cta_res + 22); + cs_add_entitlement(reader, + reader->caid, + id, + chid, + 0, + tier_date(start_date, ds, 11), + tier_date(expire_date, de, 11), + 4, + 1); + rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); + addProvider(reader, cta_res + 19); + } + if((reader->caid == 0x1856) && (cta_res[21] == 0x01)) + { + uint16_t chid = 0; + uint32_t id = b2i(0x02, cta_res + 19); + uint32_t start_date; + uint32_t expire_date; + start_date = 1; + expire_date = b2i(0x04, cta_res + 22); + cs_add_entitlement(reader, + reader->caid, + id, + chid, + 0, + tier_date(start_date, ds, 11), + tier_date(expire_date, de, 11), + 4, + 1); + rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); + addProvider(reader, cta_res + 19); + } + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + uint16_t chid = 0; + uint32_t id = b2i(0x02, cta_res + 19); + uint32_t start_date; + uint32_t expire_date; + start_date = 1; + expire_date = b2i(0x04, cta_res + 22); + cs_add_entitlement(reader, + reader->caid, + id, + chid, + 0, + tier_date(start_date, ds, 11), + tier_date(expire_date, de, 11), + 4, + 1); + rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); + addProvider(reader, cta_res + 19); + } + return OK; + } + case 0x04: + { + if(cta_res[18] != 0x80) + { + addProvider(reader, cta_res + 19); + uint8_t check[] = {0x00, 0x01}; + uint8_t checkecmcaid[] = {0xFF, 0x07}; + if (reader->caid == 0x186D) + { + check[0] = (reader->caid - 0x03) & 0xFF; + } + else if (reader->caid == 0x1856) + { + check[0] = (reader->caid + 0x28) & 0xFF; + } + else + { + check[0] = reader->caid & 0xFF; + } + int p; + for(p=23; p < (cta_lr - 6); p++) + { + if(!memcmp(cta_res + p, check, 2)) + { + addProvider(reader, cta_res + p + 2); + if(reader->cak7type == 3) + { + addSAseca(reader, cta_res + p + 5); + } + else + { + if ((reader->caid == 0x1884) && (((cta_res + p + 5)[0] == 0x83) || ((cta_res + p + 5)[0] == 0x87)) && ((cta_res + p + 5)[2] == reader->cardid[1]) && ((cta_res + p + 5)[3] == reader->cardid[0]) && ((cta_res + p + 5)[4] == 0x00)) + { + (cta_res + p + 5)[1] -= 0x01; + } + if ((reader->caid == 0x1856) && ((cta_res + p + 5)[0] == 0x87) && ((cta_res + p + 5)[1] != reader->cardid[2]) && ((cta_res + p + 5)[2] != reader->cardid[1]) && ((cta_res + p + 5)[3] != reader->cardid[0]) && ((cta_res + p + 5)[4] != reader->cardid[3])) + { + (cta_res + p + 5)[4] = 0x00; + } + addSA(reader, cta_res + p + 5); + addemmfilter(reader, cta_res + p + 5); + } + } + if(!memcmp(cta_res + p, checkecmcaid, 2)) + { + reader->caid = (SYSTEM_NAGRA | (cta_res + p + 2)[0]); + } + } + } + return OK; + } + case 0x09: + { + if((cta_res[19] == cta_res[23]) && (cta_res[20] == cta_res[24])) + { + addProvider(reader, cta_res + 19); } return OK; } - - case DT05: // case 0x05 + case SYSID: // case 0x05 { - IDEA_KEY_SCHEDULE ks; memcpy(reader->edata,cta_res + 26, 0x70); reader->dt5num = cta_res[20]; - rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod1, reader->mod1_length, reader->public_exponent, reader->public_exponent_length); - + char tmp[8]; + rdr_log(reader, "Card has DT05_%s", cs_hexdump(1, &reader->dt5num, 1, tmp, sizeof(tmp))); if(reader->dt5num == 0x00) { + IDEA_KEY_SCHEDULE ks; + rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod1, reader->mod1_length); memcpy(reader->kdt05_00,&reader->out[18], 0x5C + 2); memcpy(&reader->kdt05_00[0x5C + 2], cta_res + 26 + 0x70, 6); memcpy(reader->ideakey1, reader->out, 16); + rdr_log_dump_dbg(reader, D_READER, reader->ideakey1, 16, "IDEAKEY1: "); memcpy(reader->block3, cta_res + 26 + 0x70 + 6, 8); idea_set_encrypt_key(reader->ideakey1, &ks); memset(reader->v, 0, sizeof(reader->v)); idea_cbc_encrypt(reader->block3, reader->iout, 8, &ks, reader->v, IDEA_DECRYPT); memcpy(&reader->kdt05_00[0x5C + 2 + 6],reader->iout, 8); + uint8_t mdc_hash1[MDC2_DIGEST_LENGTH]; + memset(mdc_hash1,0x00,MDC2_DIGEST_LENGTH); + uint8_t check1[0x7E]; + memset(check1, 0x00, 0x7E); + memcpy(check1 + 18, reader->kdt05_00, 0x6C); + MDC2_CTX c1; + MDC2_Init(&c1); + MDC2_Update(&c1, check1, 0x7E); + MDC2_Final(&(mdc_hash1[0]), &c1); + rdr_log_dump_dbg(reader, D_READER, mdc_hash1, 16, "MDC_HASH: "); + if(memcmp(mdc_hash1 + 1, reader->ideakey1 + 1, 14) == 0) + { + rdr_log(reader, "DT05_00 is correct"); + } + else + { + rdr_log(reader, "DT05_00 error - check MOD1"); + } rdr_log_dump_dbg(reader, D_READER, reader->kdt05_00, sizeof(reader->kdt05_00), "DT05_00: "); } - if(reader->dt5num == 0x10) { + IDEA_KEY_SCHEDULE ks; + rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod1, reader->mod1_length); memcpy(reader->kdt05_10, &reader->out[16], 6 * 16); memcpy(reader->ideakey1, reader->out, 16); memcpy(reader->block3, cta_res + 26 + 0x70, 8); @@ -143,9 +431,14 @@ static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_r memcpy(&reader->kdt05_10[6 * 16],reader->iout,8); rdr_log_dump_dbg(reader, D_READER, reader->kdt05_10, sizeof(reader->kdt05_10), "DT05_10: "); } + if(reader->dt5num == 0x20) + { + rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod2, reader->mod2_length); + memcpy(reader->tmprsa, reader->out, 0x70); + reader->hasunique = 1; + } return OK; } - case TIERS: // case 0x0C { uint16_t chid; @@ -156,28 +449,40 @@ static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_r uint32_t expire_date1; uint32_t expire_date2; uint32_t expire_date; - switch(reader->caid) { + case 0x1843: // HD02 + start_date = b2i(0x04, cta_res + 42); + expire_date1 = b2i(0x04, cta_res + 28); + expire_date2 = b2i(0x04, cta_res + 46); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + break; case 0x1860: // HD03 start_date = b2i(0x04, cta_res + 42); expire_date1 = b2i(0x04, cta_res + 28); expire_date2 = b2i(0x04, cta_res + 46); expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; break; - case 0x186A: // HD04, HD05 start_date = b2i(0x04, cta_res + 53); expire_date1 = b2i(0x04, cta_res + 39); expire_date2 = b2i(0x04, cta_res + 57); expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; break; - + case 0x1861: // Polsat, Vodafone D08 + start_date = b2i(0x04, cta_res + 42); + expire_date = b2i(0x04, cta_res + 28); + break; + case 0x1830: // Max TV + start_date = b2i(0x04, cta_res + 42); + expire_date1 = b2i(0x04, cta_res + 28); + expire_date2 = b2i(0x04, cta_res + 46); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + break; default: // unknown card start_date = 1; - expire_date = 0x569EFB7F; + expire_date = 0xA69EFB7F; } - cs_add_entitlement(reader, reader->caid, id, @@ -188,70 +493,61 @@ static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_r 4, 1); rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); - addProvider(reader, cta_res); + addProvider(reader, cta_res + 19); } return OK; } - default: return OK; } return ERROR; } - static int32_t CAK7do_cmd(struct s_reader *reader, uint8_t dt, uint8_t len, uint8_t *res, uint16_t *rlen, int32_t sub, uint8_t retlen) { uint8_t dtdata[0x10]; memset(dtdata, 0xCC, len); - dtdata[7] = 0x04; dtdata[8] = 0x04; - dtdata[9] = (sub >> 16) & 0xFF; dtdata[10] = (sub >> 8) & 0xFF; dtdata[11] = (sub) & 0xFF; - dtdata[12] = dt; - do_cak7_cmd(reader, res, rlen, dtdata, sizeof(dtdata), retlen); - return OK; } - static int32_t CAK7GetDataType(struct s_reader *reader, uint8_t dt) { def_resp; - int32_t sub = 0x00; uint8_t retlen = 0x10; while(1) { CAK7do_cmd(reader, dt, 0x10, cta_res, &cta_lr, sub, retlen); + rdr_log_dump_dbg(reader, D_READER, cta_res, cta_lr, "Decrypted Answer:"); // hier eigentlich check auf 90 am ende usw... obs halt klarging ... - - if((cta_lr == 0) || (cta_res[cta_lr-2] == 0x6F && cta_res[cta_lr-1] == 0x01)) + if(cta_lr == 0) + { + break; + } + if(cta_res[cta_lr-2] == 0x6F && cta_res[cta_lr-1] == 0x01) { reader->card_status = CARD_NEED_INIT; add_job(reader->client, ACTION_READER_RESTART, NULL, 0); break; } - uint32_t newsub = (cta_res[9] << 16) + (cta_res[10] << 8) + (cta_res[11]); if(newsub == 0xFFFFFF) { break; } - if(cta_res[12] == dt) { uint8_t oretlen = retlen; retlen = cta_res[13] + 0x10 + 0x2; - while(retlen % 0x10 != 0x00) { retlen++; } - if(retlen == oretlen) { sub = newsub + 1; @@ -264,82 +560,315 @@ static int32_t CAK7GetDataType(struct s_reader *reader, uint8_t dt) break; } } - return OK; } - -void CAK7_getCamKey(struct s_reader *reader) +static void sub_6AD78(uint32_t *dinit) // gbox function +{ + uint32_t v0 = (uint32_t) * dinit; + double f0; + f0 = v0; + double f12 = 16807; + double f15 = 2147483647; + f12 = f0 * f12; + double v12; + v12 = fmod(f12, f15); + *dinit = v12; +} +static void calc_cak7_exponent(uint32_t *dinit, uint8_t *out, uint8_t len) +{ + memset(out, 0x00, len); + sub_6AD78(dinit); + int nR4 = 0; + int nR5 = 0; + while(true) + { + uint32_t nR0 = (uint32_t)* dinit; + int nR3 = nR4 + 3; + nR5 += 4; + if(nR3 > len) + { + break; + } + out[nR5 - 1] = ((nR0 ) & 0xFF); + out[nR5 - 2] = ((nR0 >> 8) & 0xFF); + out[nR5 - 3] = ((nR0 >> 16) & 0xFF); + out[nR5 - 4] = ((nR0 >> 24) & 0xFF); + nR4 += 4; + sub_6AD78(dinit); + } + uint32_t nR0 = (uint32_t)* dinit; + while(nR4 < len) + { + out[nR4] = nR0 & 0xFF; + nR4++; + nR0 >>= 8; + } + out[0] &= 0x03; + out[0x10] |= 0x01; +} +static void IdeaDecrypt(unsigned char *data, int len, const unsigned char *key, unsigned char *iv) +{ +unsigned char v[8]; +if(!iv) { memset(v,0,sizeof(v)); iv=v; } +IDEA_KEY_SCHEDULE ks; +idea_set_encrypt_key(key,&ks); +idea_cbc_encrypt(data,data,len&~7,&ks,iv,IDEA_DECRYPT); +} +static inline void xxxor(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; + } +} +static void CreateRSAPair60(struct s_reader *reader, const unsigned char *key) +{ +unsigned char idata[96]; +int i; +for(i=11; i>=0; i--) { +unsigned char *d=&idata[i*8]; +memcpy(d,&key[13],8); +*d^=i; +IdeaDecrypt(d,8,key,0); +xxxor(d,8,d,&key[13]); +*d^=i; +} +BN_CTX *ctx5 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO +BN_CTX_start(ctx5); +#endif +BIGNUM *p = BN_CTX_get(ctx5); +BIGNUM *q = BN_CTX_get(ctx5); +BIGNUM *m = BN_CTX_get(ctx5); +BIGNUM *e = BN_CTX_get(ctx5); +BIGNUM *a = BN_CTX_get(ctx5); +BIGNUM *r = BN_CTX_get(ctx5); +// Calculate P +idata[0] |= 0x80; +idata[47] |= 1; +BN_bin2bn(idata,48,p); +BN_add_word(p,(key[21] << 5 ) | ((key[22] & 0xf0) >> 3)); +// Calculate Q +idata[48] |= 0x80; +idata[95] |= 1; +BN_bin2bn(idata+48,48,q); +BN_add_word(q,((key[22]&0xf)<<9) | (key[23]<<1)); +// Calculate M=P*Q +BN_mul(m,p,q,ctx5); +memset(reader->key60,0x00,0x60); +BN_bn2bin(m, reader->key60 + (0x60 - BN_num_bytes(m))); +rdr_log_dump_dbg(reader, D_READER, reader->key60, sizeof(reader->key60), "key60: "); +// Calculate D +BN_sub_word(p,1); +BN_sub_word(q,1); +BN_mul(e,p,q,ctx5); +BN_bin2bn(public_exponent,3,a); +BN_mod_inverse(r, a, e, ctx5); +memset(reader->exp60,0x00,0x60); +BN_bn2bin(r, reader->exp60 + (0x60 - BN_num_bytes(r))); +rdr_log_dump_dbg(reader, D_READER, reader->exp60, sizeof(reader->exp60), "exp60: "); +BN_CTX_end(ctx5); +BN_CTX_free(ctx5); +} +static void CreateRSAPair68(struct s_reader *reader, const unsigned char *key) +{ +unsigned char idata[104]; +int i; +for(i=12; i>=0; i--) { +unsigned char *d=&idata[i*8]; +memcpy(d,&key[13],8); +*d^=i; +IdeaDecrypt(d,8,key,0); +xxxor(d,8,d,&key[13]); +*d^=i; +} +BN_CTX *ctx6 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO +BN_CTX_start(ctx6); +#endif +BIGNUM *p = BN_CTX_get(ctx6); +BIGNUM *q = BN_CTX_get(ctx6); +BIGNUM *m = BN_CTX_get(ctx6); +BIGNUM *e = BN_CTX_get(ctx6); +BIGNUM *a = BN_CTX_get(ctx6); +BIGNUM *r = BN_CTX_get(ctx6); +// Calculate P +idata[0] |= 0x80; +idata[51] |= 1; +BN_bin2bn(idata,52,p); +BN_add_word(p,(key[21] << 5 ) | ((key[22] & 0xf0) >> 3)); +// Calculate Q +idata[52] |= 0x80; +idata[103] |= 1; +BN_bin2bn(idata+52,52,q); +BN_add_word(q,((key[22]&0xf)<<9) | (key[23]<<1)); +// Calculate M=P*Q +BN_mul(m,p,q,ctx6); +memset(reader->key68,0x00,0x68); +BN_bn2bin(m, reader->key68 + (0x68 - BN_num_bytes(m))); +rdr_log_dump_dbg(reader, D_READER, reader->key68, sizeof(reader->key68), "key68: "); +// Calculate D +BN_sub_word(p,1); +BN_sub_word(q,1); +BN_mul(e,p,q,ctx6); +BN_bin2bn(public_exponent,3,a); +BN_mod_inverse(r, a, e, ctx6); +memset(reader->exp68,0x00,0x68); +BN_bn2bin(r, reader->exp68 + (0x68 - BN_num_bytes(r))); +rdr_log_dump_dbg(reader, D_READER, reader->exp68, sizeof(reader->exp68), "exp68: "); +BN_CTX_end(ctx6); +BN_CTX_free(ctx6); +} +static void dt05_20(struct s_reader *reader) +{ + uint8_t data_20_00[72]; + uint8_t sig_20_00[16]; + uint8_t data_20_id[72]; + uint8_t data_20_x[64]; + uint8_t data_20_fin[72]; + uint8_t data_20_flag58[16]; + rdr_log_dump_dbg(reader, D_READER, reader->tmprsa, sizeof(reader->tmprsa), "DT05_20 after RSA: "); + // copy signature + memcpy(sig_20_00, reader->tmprsa+24, 16); + // copy data + memcpy(data_20_00, reader->tmprsa+40, 72); + // IDEA encrypt 0x48 data + int i; + int offs = 0; + for(i=0; i<9; i++) + { + IDEA_KEY_SCHEDULE ks; + idea_set_encrypt_key(reader->key3310, &ks); + idea_ecb_encrypt(data_20_00+offs, data_20_id+offs, &ks); + offs+=8; + } + // xor + for (i=0; i<64; i++) + { + data_20_x[i] = data_20_00[i] ^ data_20_id[i+8]; + } + rdr_log_dump_dbg(reader, D_READER, data_20_x, sizeof(data_20_x), "data_20_x: "); + // create final data block + memcpy(data_20_fin,data_20_id,8); + memcpy(data_20_fin+8,data_20_x,64); + rdr_log_dump_dbg(reader, D_READER, data_20_fin, sizeof(data_20_fin), "data_20_fin: "); + uint8_t mdc_hash4[MDC2_DIGEST_LENGTH]; + memset(mdc_hash4,0x00,MDC2_DIGEST_LENGTH); + uint8_t check4[112]; + memset(check4, 0x00, 112); + memcpy(check4, reader->cardid, 4); + memcpy(check4 + 4, reader->idird, 4); + memcpy(check4 + 23, reader->tmprsa + 23, 1); + memcpy(check4 + 40, data_20_fin, 72); + MDC2_CTX c4; + MDC2_Init(&c4); + MDC2_Update(&c4, check4, 112); + MDC2_Final(&(mdc_hash4[0]), &c4); + if(memcmp(mdc_hash4, sig_20_00, 16) == 0) + { + rdr_log(reader, "DT05_20 is correct"); + } + else + { + rdr_log(reader, "DT05_20 error - check MOD2"); + } + // Store 3des software key Flag58 CW overencrypt + memcpy(data_20_flag58, data_20_x+16, 16); + memcpy(reader->key3des, data_20_flag58, 16); + rdr_log_dump_dbg(reader, D_READER, reader->key3des, sizeof(reader->key3des), "Flag58 3DES Key: "); + // create rsa pair from final data + memcpy(reader->klucz68, data_20_fin, 0x18); + rdr_log_dump_dbg(reader, D_READER, reader->klucz68, sizeof(reader->klucz68), "klucz68: "); +} +static int32_t CAK7_cmd03_global(struct s_reader *reader) { def_resp; - uint8_t cmd0e[] = {0xCC,0xCC,0xCC,0xCC,0x00,0x00,0x09,0x0E,0x83,0x00,0x00,0x00,0x00,0x00,0x64,0x65,0x6D,0x6F,0x34,0x11,0x9D, - 0x7E,0xEE,0xCE,0x53,0x09,0x80,0xAE,0x6B,0x5A,0xEE,0x3A,0x41,0xCE,0x09,0x75,0xEF,0xA6,0xBF,0x1E,0x98,0x4F, - 0xA4,0x11,0x6F,0x43,0xCA,0xCD,0xD0,0x6E,0x69,0xFA,0x25,0xC1,0xF9,0x11,0x8E,0x7A,0xD0,0x19,0xC0,0xEB,0x00, - 0xC0,0x57,0x2A,0x40,0xB7,0xFF,0x8A,0xBB,0x25,0x21,0xD7,0x50,0xE7,0x35,0xA1,0x85,0xCD,0xA6,0xD3,0xDE,0xB3, - 0x3D,0x16,0xD4,0x94,0x76,0x8A,0x82,0x8C,0x70,0x25,0xD4,0x00,0xD0,0x64,0x8C,0x26,0xB9,0x5F,0x44,0xFF,0x73, - 0x70,0xAB,0x43,0xF5,0x68,0xA2,0xB1,0xB5,0x8A,0x8E,0x02,0x5F,0x96,0x06,0xA8,0xC3,0x4F,0x15,0xCD,0x99,0xC2, - 0x69,0xB8,0x35,0x68,0x11,0x4C,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0xCC,0xCC,0xCC,0xCC}; - - get_random_bytes(data1, 0x04); - if (data1[3] == 0xFF) - { - data1[3]--; - } - memcpy(cmd0e + 9, data1, 0x04); - data1[3]++; - - if (reader->irdid_length == 4) - { - memcpy(&cmd0e[14], reader->irdid, reader->irdid_length); // inject irdid - } - - // inject provid - cmd0e[18] = reader->prid[0][2]; - cmd0e[19] = reader->prid[0][3]; - - if (reader->nuid_length == 4) - { - memcpy(&cmd0e[132], reader->nuid, reader->nuid_length); // inject NUID - } - - do_cak7_cmd(reader,cta_res, &cta_lr, cmd0e, sizeof(cmd0e), 0x20); - - reader->cak7_restart = (cta_res[22] << 16); - reader->cak7_restart += (cta_res[23] << 8); - reader->cak7_restart += (cta_res[24] ); - reader->cak7_restart--; - - memcpy(reader->cardid,cta_res + 14, 4); - rdr_log_dump_dbg(reader, D_READER, reader->cardid, 0x04, "CardSerial: "); - - memcpy(reader->hexserial + 2, reader->cardid, 4); - memcpy(reader->sa[0], reader->cardid, 3); - memcpy(reader->sa[1], reader->sa[0], 4); - - unsigned long datal = (cta_res[9] << 24) + (cta_res[10] << 16) + (cta_res[11] << 8) + (cta_res[12]); - datal++; - reader->data2[0] = (datal >> 24) & 0xFF; - reader->data2[1] = (datal >> 16) & 0xFF; - reader->data2[2] = (datal >> 8) & 0xFF; - reader->data2[3] = (datal ) & 0xFF; - - rsa_decrypt(reader->data50, reader->data50_length, reader->data, reader->mod50, reader->mod50_length, reader->public_exponent, reader->public_exponent_length); - - memcpy(&reader->step1[0], d00ff, 4); - memcpy(&reader->step1[4], reader->data, 0x50); - memcpy(&reader->step1[4 + 0x50], reader->irdid, reader->irdid_length); - memcpy(&reader->step1[4 + 4 + 0x50], data1, 0x04); - memcpy(&reader->step1[4 + 4 + 4 + 0x50], reader->data2, 0x04); - rsa_decrypt(reader->step1, 0x60, reader->data, reader->key60, reader->key60_length, reader->exp60, reader->exp60_length); - + if(reader->cak7_seq <= 15) + { + unsigned char klucz[24]; + memset(klucz, 0x00, 24); + memcpy(klucz, reader->key3588, 24); + CreateRSAPair60(reader, klucz); + } + BN_CTX *ctx1 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx1); +#endif + BIGNUM *bnN1 = BN_CTX_get(ctx1); + BIGNUM *bnE1 = BN_CTX_get(ctx1); + BIGNUM *bnCT1 = BN_CTX_get(ctx1); + BIGNUM *bnPT1 = BN_CTX_get(ctx1); + BN_bin2bn(&reader->key60[0], 0x60, bnN1); + BN_bin2bn(&reader->exp60[0], 0x60, bnE1); + BN_bin2bn(&reader->step1[0], 0x60, bnCT1); + BN_mod_exp(bnPT1, bnCT1, bnE1, bnN1, ctx1); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT1, reader->data + (0x60 - BN_num_bytes(bnPT1))); + BN_CTX_end(ctx1); + BN_CTX_free(ctx1); memcpy(&reader->step2[0], d00ff, 4); memcpy(&reader->step2[4], reader->cardid, 4); memcpy(&reader->step2[8], reader->data, 0x60); - rsa_decrypt(reader->step2, 0x68, reader->data, reader->kdt05_10, 0x68, reader->public_exponent, reader->public_exponent_length); - + rdr_log_dump_dbg(reader, D_READER, reader->step2, sizeof(reader->step2), "STEP 2:"); + BN_CTX *ctx2 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx2); +#endif + BIGNUM *bnN2 = BN_CTX_get(ctx2); + BIGNUM *bnE2 = BN_CTX_get(ctx2); + BIGNUM *bnCT2 = BN_CTX_get(ctx2); + BIGNUM *bnPT2 = BN_CTX_get(ctx2); + BN_bin2bn(&reader->kdt05_10[0], 0x68, bnN2); + BN_bin2bn(public_exponent, 3, bnE2); + BN_bin2bn(&reader->step2[0], 0x68, bnCT2); + BN_mod_exp(bnPT2, bnCT2, bnE2, bnN2, ctx2); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT2, reader->data + (0x68 - BN_num_bytes(bnPT2))); + BN_CTX_end(ctx2); + BN_CTX_free(ctx2); memcpy(&reader->step3[0], d00ff, 4); memcpy(&reader->step3[4], reader->data, 0x68); - rsa_decrypt(reader->step3, 0x6c, reader->data, reader->kdt05_00, 0x6c, reader->public_exponent, reader->public_exponent_length); - + rdr_log_dump_dbg(reader, D_READER, reader->step3, sizeof(reader->step3), "STEP 3:"); + BN_CTX *ctx3 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx3); +#endif + BIGNUM *bnN3 = BN_CTX_get(ctx3); + BIGNUM *bnE3 = BN_CTX_get(ctx3); + BIGNUM *bnCT3 = BN_CTX_get(ctx3); + BIGNUM *bnPT3 = BN_CTX_get(ctx3); + BN_bin2bn(&reader->kdt05_00[0], 0x6c, bnN3); + BN_bin2bn(public_exponent, 3, bnE3); + BN_bin2bn(&reader->step3[0], 0x6c, bnCT3); + BN_mod_exp(bnPT3, bnCT3, bnE3, bnN3, ctx3); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT3, reader->data + (0x6c - BN_num_bytes(bnPT3))); + BN_CTX_end(ctx3); + BN_CTX_free(ctx3); uint8_t cmd03[] = {0xCC,0xCC,0xCC,0xCC,0x00,0x00,0x0A,0x03,0x6C, 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, @@ -349,64 +878,450 @@ void CAK7_getCamKey(struct s_reader *reader) 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC}; - memcpy(&cmd03[9],reader->data,0x6c); do_cak7_cmd(reader,cta_res,&cta_lr,cmd03,sizeof(cmd03),0x90); - + if(cta_lr == 0) + { + rdr_log(reader, "card is not responding to CMD03 - check your data"); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, cta_res, 0x90, "CMD03 ANSWER:"); memcpy(reader->encrypted,&cta_res[10],0x68); - rsa_decrypt(reader->encrypted, 0x68, reader->result, reader->kdt05_10, 0x68, reader->public_exponent, reader->public_exponent_length); - + BN_CTX *ctx = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx); +#endif + BIGNUM *bnN = BN_CTX_get(ctx); + BIGNUM *bnE = BN_CTX_get(ctx); + BIGNUM *bnCT = BN_CTX_get(ctx); + BIGNUM *bnPT = BN_CTX_get(ctx); + BN_bin2bn(&reader->kdt05_10[0], 104, bnN); + BN_bin2bn(public_exponent, 3, bnE); + BN_bin2bn(&reader->encrypted[0], 104, bnCT); + BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx); + memset(reader->result, 0, 104); + BN_bn2bin(bnPT, reader->result + (104 - BN_num_bytes(bnPT))); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + //uint8_t stillencrypted[0x50]; memcpy(reader->stillencrypted,&reader->result[12],0x50); - rsa_decrypt(reader->stillencrypted, 0x50, reader->resultrsa, reader->mod50, reader->mod50_length, reader->public_exponent, reader->public_exponent_length); - - uint8_t mdc_hash[MDC2_DIGEST_LENGTH]; - memset(mdc_hash,0x00,MDC2_DIGEST_LENGTH); - MDC2_CTX c; - MDC2_Init(&c); - MDC2_Update(&c, reader->resultrsa, sizeof(reader->resultrsa)); - MDC2_Final(&(mdc_hash[0]), &c); - - memcpy(&reader->cak7_aes_key[16],mdc_hash,16); - memcpy(reader->cak7_aes_key,mdc_hash,16); + //uint8_t resultrsa[0x50]; + BN_CTX *ctxs = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctxs); +#endif + BIGNUM *bnNs = BN_CTX_get(ctxs); + BIGNUM *bnEs = BN_CTX_get(ctxs); + BIGNUM *bnCTs = BN_CTX_get(ctxs); + BIGNUM *bnPTs = BN_CTX_get(ctxs); + BN_bin2bn(&reader->mod50[0], reader->mod50_length, bnNs); + BN_bin2bn(&reader->cak7expo[0], 0x11, bnEs); + BN_bin2bn(&reader->stillencrypted[0], 0x50, bnCTs); + BN_mod_exp(bnPTs, bnCTs, bnEs, bnNs, ctxs); + memset(reader->resultrsa, 0x00, 0x50); + BN_bn2bin(bnPTs, reader->resultrsa + (0x50 - BN_num_bytes(bnPTs))); + BN_CTX_end(ctxs); + BN_CTX_free(ctxs); + uint8_t mdc_hash3[MDC2_DIGEST_LENGTH]; + memset(mdc_hash3,0x00,MDC2_DIGEST_LENGTH); + MDC2_CTX c3; + MDC2_Init(&c3); + MDC2_Update(&c3, reader->resultrsa, sizeof(reader->resultrsa)); + MDC2_Final(&(mdc_hash3[0]), &c3); + memcpy(&reader->cak7_aes_key[16],mdc_hash3,16); + memcpy(reader->cak7_aes_key,mdc_hash3,16); + char tmp7[128]; + rdr_log(reader, "New AES: %s", cs_hexdump(1, reader->cak7_aes_key, 16, tmp7, sizeof(tmp7))); + return OK; } - -void CAK7_reinit(struct s_reader *reader) +static int32_t CAK7_cmd03_unique(struct s_reader *reader) { - ATR newatr[ATR_MAX_SIZE]; - memset(newatr, 0, 1); - if(ICC_Async_Activate(reader, newatr, 0)) + def_resp; + BN_CTX *ctx1 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx1); +#endif + BIGNUM *bnN1 = BN_CTX_get(ctx1); + BIGNUM *bnE1 = BN_CTX_get(ctx1); + BIGNUM *bnCT1 = BN_CTX_get(ctx1); + BIGNUM *bnPT1 = BN_CTX_get(ctx1); + BN_bin2bn(&reader->key3460[0], 0x60, bnN1); + BN_bin2bn(public_exponent, 3, bnE1); + BN_bin2bn(&reader->step1[0], 0x60, bnCT1); + BN_mod_exp(bnPT1, bnCT1, bnE1, bnN1, ctx1); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT1, reader->data + (0x60 - BN_num_bytes(bnPT1))); + BN_CTX_end(ctx1); + BN_CTX_free(ctx1); + memcpy(&reader->step2[0], d00ff, 4); + memcpy(&reader->step2[4], reader->cardid, 4); + memcpy(&reader->step2[8], reader->data, 0x60); + rdr_log_dump_dbg(reader, D_READER, reader->step2, sizeof(reader->step2), "STEP 2:"); + if(reader->cak7_seq <= 15) { - reader->card_status = CARD_NEED_INIT; - add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + dt05_20(reader); + CreateRSAPair68(reader, reader->klucz68); + } + BN_CTX *ctx2 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx2); +#endif + BIGNUM *bnN2 = BN_CTX_get(ctx2); + BIGNUM *bnE2 = BN_CTX_get(ctx2); + BIGNUM *bnCT2 = BN_CTX_get(ctx2); + BIGNUM *bnPT2 = BN_CTX_get(ctx2); + BN_bin2bn(&reader->key68[0], 0x68, bnN2); + BN_bin2bn(&reader->exp68[0], 0x68, bnE2); + BN_bin2bn(&reader->step2[0], 0x68, bnCT2); + BN_mod_exp(bnPT2, bnCT2, bnE2, bnN2, ctx2); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT2, reader->data + (0x68 - BN_num_bytes(bnPT2))); + BN_CTX_end(ctx2); + BN_CTX_free(ctx2); + memcpy(&reader->step3[0], d00ff, 4); + memcpy(&reader->step3[4], reader->data, 0x68); + rdr_log_dump_dbg(reader, D_READER, reader->step3, sizeof(reader->step3), "STEP 3:"); + BN_CTX *ctx3 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx3); +#endif + BIGNUM *bnN3 = BN_CTX_get(ctx3); + BIGNUM *bnE3 = BN_CTX_get(ctx3); + BIGNUM *bnCT3 = BN_CTX_get(ctx3); + BIGNUM *bnPT3 = BN_CTX_get(ctx3); + BN_bin2bn(&reader->kdt05_00[0], 0x6c, bnN3); + BN_bin2bn(public_exponent, 3, bnE3); + BN_bin2bn(&reader->step3[0], 0x6c, bnCT3); + BN_mod_exp(bnPT3, bnCT3, bnE3, bnN3, ctx3); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT3, reader->data + (0x6c - BN_num_bytes(bnPT3))); + BN_CTX_end(ctx3); + BN_CTX_free(ctx3); + uint8_t cmd03[] = {0xCC,0xCC,0xCC,0xCC,0x00,0x00,0x0A,0x03,0x6C, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC}; + memcpy(&cmd03[9],reader->data,0x6c); + do_cak7_cmd(reader,cta_res,&cta_lr,cmd03,sizeof(cmd03),0x90); + if(cta_lr == 0) + { + rdr_log(reader, "card is not responding to CMD03 - check your data"); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, cta_res, 0x90, "CMD03 ANSWER:"); + memcpy(reader->encrypted,&cta_res[18],0x60); + BN_CTX *ctx = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx); +#endif + BIGNUM *bnN = BN_CTX_get(ctx); + BIGNUM *bnE = BN_CTX_get(ctx); + BIGNUM *bnCT = BN_CTX_get(ctx); + BIGNUM *bnPT = BN_CTX_get(ctx); + BN_bin2bn(&reader->key3460[0], 96, bnN); + BN_bin2bn(public_exponent, 3, bnE); + BN_bin2bn(&reader->encrypted[0], 96, bnCT); + BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx); + memset(reader->result, 0, 96); + BN_bn2bin(bnPT, reader->result + (96 - BN_num_bytes(bnPT))); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + rdr_log_dump_dbg(reader, D_READER, reader->result, 96, "after RSA_3460: "); + //uint8_t stillencrypted[0x50]; + memcpy(reader->stillencrypted,&reader->result[4],0x50); + //uint8_t resultrsa[0x50]; + BN_CTX *ctxs = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctxs); +#endif + BIGNUM *bnNs = BN_CTX_get(ctxs); + BIGNUM *bnEs = BN_CTX_get(ctxs); + BIGNUM *bnCTs = BN_CTX_get(ctxs); + BIGNUM *bnPTs = BN_CTX_get(ctxs); + BN_bin2bn(&reader->mod50[0], reader->mod50_length, bnNs); + BN_bin2bn(&reader->cak7expo[0], 0x11, bnEs); + BN_bin2bn(&reader->stillencrypted[0], 0x50, bnCTs); + BN_mod_exp(bnPTs, bnCTs, bnEs, bnNs, ctxs); + memset(reader->resultrsa, 0x00, 0x50); + BN_bn2bin(bnPTs, reader->resultrsa + (0x50 - BN_num_bytes(bnPTs))); + BN_CTX_end(ctxs); + BN_CTX_free(ctxs); + uint8_t mdc_hash5[MDC2_DIGEST_LENGTH]; + memset(mdc_hash5,0x00,MDC2_DIGEST_LENGTH); + MDC2_CTX c5; + MDC2_Init(&c5); + MDC2_Update(&c5, reader->resultrsa, sizeof(reader->resultrsa)); + MDC2_Final(&(mdc_hash5[0]), &c5); + memcpy(&reader->cak7_aes_key[16],mdc_hash5,16); + memcpy(reader->cak7_aes_key,mdc_hash5,16); + char tmp7[128]; + rdr_log(reader, "New AES: %s", cs_hexdump(1, reader->cak7_aes_key, 16, tmp7, sizeof(tmp7))); + return OK; +} +static int32_t CAK7_GetCamKey(struct s_reader *reader) +{ + def_resp; + uint8_t cmd0e[] = {0xCC,0xCC,0xCC,0xCC,0x00,0x00,0x00,0x0E,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, + 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC}; + if(!reader->nuid_length) + { + uint8_t cmd02[] = {0x02,0x7B}; + memcpy(cmd0e + 7, cmd02, 2); + rdr_log(reader, "using CMD02"); + } + else + { + memcpy(cmd0e + 132, reader->nuid, reader->nuid_length); // inject NUID + uint8_t cwekeycount = 0; + if(reader->cwekey0_length) + { cwekeycount++; } + if(reader->cwekey1_length) + { cwekeycount++; } + if(reader->cwekey2_length) + { cwekeycount++; } + if(reader->cwekey3_length) + { cwekeycount++; } + if(reader->cwekey4_length) + { cwekeycount++; } + if(reader->cwekey5_length) + { cwekeycount++; } + if(reader->cwekey6_length) + { cwekeycount++; } + if(reader->cwekey7_length) + { cwekeycount++; } + if(cwekeycount == 0) + { + rdr_log(reader, "only NUID defined - enter at least CWPK0"); + return ERROR; + } + else + { + if(reader->otpcsc_length) + { + memcpy(cmd0e + 136, reader->otpcsc, reader->otpcsc_length); + } + else + { + if(!reader->cwpkota) + { + cmd0e[136] = 0x00; + cmd0e[137] = cwekeycount; + } + else + { + cmd0e[136] = 0x00; + cmd0e[137] = 0x00; + } + } + if(reader->otacsc_length) + { + memcpy(cmd0e + 138, reader->otacsc, reader->otacsc_length); + } + else + { + if(reader->cwpkota) + { + cmd0e[138] = 0x00; + cmd0e[139] = cwekeycount; + } + else + { + cmd0e[138] = 0x00; + cmd0e[139] = 0x00; + } + } + } + char tmp[16]; + rdr_log(reader, "OTP CSC No. of keys: %s", cs_hexdump(1, cmd0e + 136, 2, tmp, sizeof(tmp))); + rdr_log(reader, "OTA CSC No. of keys: %s", cs_hexdump(1, cmd0e + 138, 2, tmp, sizeof(tmp))); + } + if(reader->forcepair_length) + { + rdr_log(reader, "Forcing Pairing Type"); + memcpy(cmd0e + 13, reader->forcepair, 1); + } + else + { + if(reader->hasunique == 1) + { + cmd0e[13] = 0x40; + } + } + memcpy(cmd0e + 14, reader->idird, 4); + if(reader->cmd0eprov_length) + { + memcpy(cmd0e + 18, reader->cmd0eprov, 2); + } + else + { + memcpy(cmd0e + 18, reader->prid[0] + 2, 2); + } + memcpy(cmd0e + 20, reader->key3588 + 24, 0x70); + if(reader->cak7_seq <= 15) + { + srand(time(NULL)); + } + uint32_t data1r = rand() % 4294967294u; + reader->timestmp1[0]=(data1r>>24)&0xFF; + reader->timestmp1[1]=(data1r>>16)&0xFF; + reader->timestmp1[2]=(data1r>>8)&0xFF; + reader->timestmp1[3]=(data1r)&0xFF; + memcpy(cmd0e + 9, reader->timestmp1, 0x04); + rdr_log_dump_dbg(reader, D_READER, reader->timestmp1, 4, "DATA1 CMD0E:"); + rdr_log_dump_dbg(reader, D_READER, reader->prid[0], 4, "SysID:"); + do_cak7_cmd(reader,cta_res, &cta_lr, cmd0e, sizeof(cmd0e), 0x20); + if(cta_lr == 0) + { + rdr_log(reader, "card is not responding to CMD02/E - check your data"); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, cta_res, 0x20, "Decrypted answer to CMD02/0E:"); + reader->needrestart = (cta_res[22] << 16); + reader->needrestart += (cta_res[23] << 8); + reader->needrestart += (cta_res[24] ); + reader->needrestart--; + if(reader->cak7_seq <= 15) + { + rdr_log(reader, "card needs FASTreinit after %d CMDs", reader->needrestart); } else { - reader->cak7_seq = 0; - CAK7_getCamKey(reader); + uint32_t cmdleft = reader->needrestart - reader->cak7_seq; + rdr_log(reader, "%d CMDs left to FASTreinit", cmdleft); } + reader->dword_83DBC = (cta_res[18] << 24); + reader->dword_83DBC += (cta_res[19] << 16); + reader->dword_83DBC += (cta_res[20] << 8); + reader->dword_83DBC += (cta_res[21] ); + calc_cak7_exponent(&reader->dword_83DBC, reader->cak7expo, 0x11); + rdr_log_dump_dbg(reader, D_READER, reader->cak7expo, 0x11, "CAK7 Exponent:"); + memcpy(reader->cardid,cta_res + 14, 4); + rdr_log_dump_dbg(reader, D_READER, reader->cardid, 0x04, "CardSerial: "); + memcpy(reader->hexserial + 2, reader->cardid, 4); + unsigned long datal = (cta_res[9] << 24) + (cta_res[10] << 16) + (cta_res[11] << 8) + (cta_res[12]); + datal++; + reader->data2[0] = (datal >> 24) & 0xFF; + reader->data2[1] = (datal >> 16) & 0xFF; + reader->data2[2] = (datal >> 8) & 0xFF; + reader->data2[3] = (datal ) & 0xFF; + data1r++; + reader->timestmp2[0]=(data1r>>24)&0xFF; + reader->timestmp2[1]=(data1r>>16)&0xFF; + reader->timestmp2[2]=(data1r>>8)&0xFF; + reader->timestmp2[3]=(data1r)&0xFF; + memcpy(reader->ecmheader,cta_res + 18,4); + if(reader->cak7_seq <= 15) + { + uint8_t mdc_hash2[MDC2_DIGEST_LENGTH]; + memset(mdc_hash2,0x00,MDC2_DIGEST_LENGTH); + uint8_t check2[0x78]; + memset(check2, 0x00, 0x78); + memcpy(check2, reader->cardid, 4); + memcpy(check2 + 16, reader->kdt05_10, 0x68); + MDC2_CTX c2; + MDC2_Init(&c2); + MDC2_Update(&c2, check2, 0x78); + MDC2_Final(&(mdc_hash2[0]), &c2); + rdr_log_dump_dbg(reader, D_READER, reader->ideakey1, 16, "IDEAKEY1: "); + rdr_log_dump_dbg(reader, D_READER, mdc_hash2, 16, "MDC_HASH: "); + if(memcmp(mdc_hash2 + 1, reader->ideakey1 + 1, 14) == 0) + { + rdr_log(reader, "DT05_10 is correct"); + } + else + { + rdr_log(reader, "DT05_10 error - check MOD1"); + } + } + BN_CTX *ctx0 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx0); +#endif + BIGNUM *bnN0 = BN_CTX_get(ctx0); + BIGNUM *bnE0 = BN_CTX_get(ctx0); + BIGNUM *bnCT0 = BN_CTX_get(ctx0); + BIGNUM *bnPT0 = BN_CTX_get(ctx0); + BN_bin2bn(&reader->mod50[0], 0x50, bnN0); + BN_bin2bn(&reader->cak7expo[0], 0x11, bnE0); + BN_bin2bn(&reader->data50[0], 0x50, bnCT0); + BN_mod_exp(bnPT0, bnCT0, bnE0, bnN0, ctx0); + memset(reader->data, 0x00, sizeof(reader->data)); + BN_bn2bin(bnPT0, reader->data + (0x50 - BN_num_bytes(bnPT0))); + BN_CTX_end(ctx0); + BN_CTX_free(ctx0); + rdr_log_dump_dbg(reader, D_READER, reader->timestmp2, 4, "DATA1 CMD03:"); + memcpy(&reader->step1[0], d00ff, 4); + memcpy(&reader->step1[4], reader->data, 0x50); + memcpy(&reader->step1[4 + 0x50], reader->idird, 0x04); + memcpy(&reader->step1[4 + 4 + 0x50], reader->timestmp2, 0x04); + memcpy(&reader->step1[4 + 4 + 4 + 0x50], reader->data2, 0x04); + rdr_log_dump_dbg(reader, D_READER, reader->step1, sizeof(reader->step1), "STEP 1:"); + reader->pairtype = cta_res[13]; + if((reader->pairtype > 0x00) && (reader->pairtype < 0xC0)) + { + rdr_log(reader,"Card is starting in GLOBAL mode"); + if(!CAK7_cmd03_global(reader)) + {return ERROR;} + } + else if(reader->pairtype == 0xC0) + { + rdr_log(reader,"Card is starting in UNIQUE mode"); + if(!reader->mod2_length) + { + rdr_log(reader, "no mod2 defined"); + return ERROR; + } + if(!reader->key3460_length) + { + rdr_log(reader, "no key3460 defined"); + return ERROR; + } + if(!reader->key3310_length) + { + rdr_log(reader, "no key3310 defined"); + return ERROR; + } + if(!CAK7_cmd03_unique(reader)) + {return ERROR;} + } + else + { + rdr_log(reader,"Unknown Pairing Type"); + return ERROR; + } + return OK; } - static int32_t nagra3_card_init(struct s_reader *reader, ATR *newatr) { get_atr; - - memset(reader->hexserial, 0x00, 0x08); - - reader->public_exponent[0] = 0x01; - reader->public_exponent[1] = 0x00; - reader->public_exponent[2] = 0x01; - reader->public_exponent_length = 3; - - reader->irdid[0] = 0x64; - reader->irdid[1] = 0x65; - reader->irdid[2] = 0x6D; - reader->irdid[3] = 0x6F; - reader->irdid_length = 4; - + memset(reader->hexserial, 0, 8); reader->cak7_seq = 0; + reader->hasunique = 0; + memset(reader->ecmheader, 0, 4); cs_clear_entitlement(reader); - - if(memcmp(atr + 11, "DNASP4", 6) == 0) + if(memcmp(atr + 8, "DNASP4", 6) == 0) + { + if((memcmp(atr + 8, "DNASP400", 8) == 0) && !reader->cak7_mode) + { + return ERROR; + } + else + { + memcpy(reader->rom, atr + 8, 15); + rdr_log(reader,"Rom revision: %.15s", reader->rom); + } + } + else if(memcmp(atr + 11, "DNASP4", 6) == 0) { memcpy(reader->rom, atr + 11, 15); rdr_log(reader,"Rom revision: %.15s", reader->rom); @@ -415,29 +1330,89 @@ static int32_t nagra3_card_init(struct s_reader *reader, ATR *newatr) { return ERROR; } - - // check the completeness of the required CAK7 keys - if(reader->mod1_length && reader->irdid_length && reader->data50_length && reader->mod50_length && reader->key60_length && reader->exp60_length && reader->nuid_length) + reader->nprov = 1; + /*reader->nsa = 0; + reader->nemm84 = 0; + reader->nemm83u = 0; + reader->nemm83s = 0; + reader->nemm87 = 0;*/ + if(!reader->mod1_length) { - rdr_log_dbg(reader, D_READER, "All parameters for CAK7 global pairing are set."); + rdr_log(reader, "no MOD1 defined"); + return ERROR; } - else + if(!reader->key3588_length) { - rdr_log(reader, "ERROR: Not all required CAK7 parameters are set!"); - reader->card_status = CARD_FAILURE; - return ERROR; + rdr_log(reader, "no key3588 defined"); + return ERROR; + } + if(!reader->data50_length) + { + rdr_log(reader, "no data50 defined"); + return ERROR; + } + if(!reader->mod50_length) + { + rdr_log(reader, "no mod50 defined"); + return ERROR; + } + if(!reader->idird_length) + { + rdr_log(reader, "no idird defined"); + return ERROR; + } + CAK7GetDataType(reader, 0x02); + CAK7GetDataType(reader, 0x05); + if(!CAK7_GetCamKey(reader)) + {return ERROR;} + CAK7GetDataType(reader, 0x09); + char tmp[4 * 3 + 1]; + reader->nsa = 0; + reader->nemm84 = 0; + reader->nemm83u = 0; + reader->nemm83s = 0; + reader->nemm87 = 0; + CAK7GetDataType(reader, 0x04); + if(reader->forceemmg) + { + reader->emm82 = 1; + } + int i; + for(i = 1; i < reader->nprov; i++) + { + rdr_log(reader, "Prv.ID: %s", cs_hexdump(1, reader->prid[i], 4, tmp, sizeof(tmp))); + } + if(reader->cak7type != 3) + { + rdr_log(reader, "-----------------------------------------"); + rdr_log(reader, "| EMM Filters (PRIVATE!!) |"); + rdr_log(reader, "+---------------------------------------+"); + if(reader->emm82 == 1) + { + rdr_log(reader, "|emm82 |"); + } + char tmp7[48]; + for(i = 0; i < reader->nemm84; i++) + { + rdr_log(reader, "|emm84 : %s |", cs_hexdump(1, reader->emm84[i], 3, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm83u; i++) + { + rdr_log(reader, "|emm83U: %s |", cs_hexdump(1, reader->emm83u[i], 6, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm83s; i++) + { + rdr_log(reader, "|emm83S: %s |", cs_hexdump(1, reader->emm83s[i], 6, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm87; i++) + { + rdr_log(reader, "|emm87 : %s |", cs_hexdump(1, reader->emm87[i], 6, tmp7, sizeof(tmp7))); + } + rdr_log(reader, "-----------------------------------------"); } - - reader->nprov = 1; - - CAK7GetDataType(reader, DT05); - CAK7GetDataType(reader, SYSID_CAID); // sysid+caid - CAK7_getCamKey(reader); - rdr_log(reader, "ready for requests"); return OK; } - static int32_t nagra3_card_info(struct s_reader *reader) { char tmp[4 * 3 + 1]; @@ -446,73 +1421,157 @@ static int32_t nagra3_card_info(struct s_reader *reader) rdr_log_sensitive(reader, "SER: {%s}", cs_hexdump(1, reader->hexserial + 2, 4, tmp, sizeof(tmp))); rdr_log(reader, "CAID: %04X", reader->caid); rdr_log(reader, "Prv.ID: %s(sysid)", cs_hexdump(1, reader->prid[0], 4, tmp, sizeof(tmp))); - CAK7GetDataType(reader, IRDINFO); cs_clear_entitlement(reader); // reset the entitlements rdr_log(reader, "-----------------------------------------"); rdr_log(reader, "|id |tier |valid from |valid to |"); rdr_log(reader, "+----+--------+------------+------------+"); - CAK7GetDataType(reader, TIERS); + CAK7GetDataType(reader, 0x03); + CAK7GetDataType(reader, 0x0C); rdr_log(reader, "-----------------------------------------"); - uint8_t i; - for(i = 1; i < reader->nprov; i++) + return OK; +} +static int32_t fastreinit(struct s_reader *reader) +{ + ATR newatr[ATR_MAX_SIZE]; + memset(newatr, 0, 1); + if(ICC_Async_Activate(reader, newatr, 0)) { - rdr_log(reader, "Prv.ID: %s", cs_hexdump(1, reader->prid[i], 4, tmp, sizeof(tmp))); + return ERROR; + } + reader->cak7_seq = 0; + if(!CAK7_GetCamKey(reader)) + { + return ERROR; } - - struct timeb now; - cs_ftime(&now); - reader->last_refresh=now; - return OK; } - static void nagra3_post_process(struct s_reader *reader) { - if(reader->cak7_seq >= reader->cak7_restart) + if(reader->cak7_seq >= reader->needrestart) { - rdr_log(reader, "reinit necessary to reset command counter"); - CAK7_reinit(reader); + rdr_log(reader, "card needs FASTreinit to prevent crash"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } } else if((reader->cak7_camstate & 64) == 64) { - rdr_log_dbg(reader, D_READER, "renew Session Key: CAK7"); - add_job(reader->client, ACTION_READER_RENEW_SK, NULL, 0); //CAK7_getCamKey + rdr_log(reader, "negotiating new Session Key"); + if(!CAK7_GetCamKey(reader)) + { + rdr_log(reader, "negotiations failed - trying FASTreinit"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } } } - static int32_t nagra3_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) { def_resp; - + if(reader->cak7type == 3) + { + if(er->ecm[2] > 0x61 && er->ecm[7] == 0x5C && er->ecm[100] == 0x0B) + { + if(er->ecm[101] == 0x03 || er->ecm[101] == 0x04) + { + if(er->ecm[104] > reader->pairtype) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + if(er->ecm[104] == 0x80 && reader->pairtype == 0x80) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + } + if(er->ecm[101] == 0x04 && !reader->nuid_length) + { + rdr_log(reader, "reinit card with NUID"); + return ERROR; + } + } + } + else + { + if(er->ecm[2] > 0x86 && er->ecm[4] == 0x84 && er->ecm[137] == 0x0B) + { + if(er->ecm[138] == 0x03 || er->ecm[138] == 0x04) + { + if(er->ecm[141] > reader->pairtype) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + if(er->ecm[141] == 0x80 && reader->pairtype == 0x80) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + } + if(er->ecm[138] == 0x04 && !reader->nuid_length) + { + rdr_log(reader, "reinit card with NUID"); + return ERROR; + } + } + } uint8_t ecmreq[0xC0]; memset(ecmreq,0xCC,0xC0); - ecmreq[ 7] = 0x05; - ecmreq[ 8] = 0x8A; - ecmreq[ 9] = 0x00; - ecmreq[10] = 0x00; - ecmreq[11] = 0x00; - ecmreq[12] = 0x00; - ecmreq[13] = 0x01; - memcpy(&ecmreq[14], er->ecm + 4, er->ecm[4] + 1); - + if(reader->caid == 0x1830) + { + ecmreq[ 9] = 0x00; + ecmreq[10] = 0x00; + ecmreq[11] = 0x00; + ecmreq[12] = 0x00; + ecmreq[13] = 0x00; + } + else + { + ecmreq[ 9] = 0x04; + ecmreq[10] = reader->ecmheader[0]; + ecmreq[11] = reader->ecmheader[1]; + ecmreq[12] = reader->ecmheader[2]; + ecmreq[13] = reader->ecmheader[3]; + } + if(reader->cak7type == 3) + { + ecmreq[8] = er->ecm[7] + 6; + memcpy(&ecmreq[14], er->ecm + 7, er->ecm[7] + 1); + } + else + { + ecmreq[8] = er->ecm[4] + 6; + memcpy(&ecmreq[14], er->ecm + 4, er->ecm[4] + 1); + } + if((er->ecm[2] == 0xAC) && (er->ecm[3] == 0x05)) + { + ecmreq[15] = 0x0A; + } do_cak7_cmd(reader, cta_res, &cta_lr, ecmreq, sizeof(ecmreq), 0xB0); - - if(cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 1] != 0x00) + rdr_log_dump_dbg(reader, D_READER, cta_res, 0xB0, "Decrypted ECM Answer:"); + if((cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 1] != 0x00) || cta_lr == 0) { rdr_log(reader, "(ECM) Reader will be restart now cause: %02X %02X card answer!!!", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); reader->card_status = CARD_NEED_INIT; add_job(reader->client, ACTION_READER_RESTART, NULL, 0); } - - if(cta_res[27] == 0x5C) + else if(cta_res[27] != 0x00 && cta_res[27] != 0xCC) { + memcpy(reader->ecmheader, cta_res + 9, 4); + reader->cak7_camstate = cta_res[4]; uint8_t _cwe0[8]; uint8_t _cwe1[8]; - - if(cta_res[78] == 0x01) + if(cta_res[78] == 0x01 || reader->forcecwswap) { - rdr_log (reader,"Swap dcw is at use !"); memcpy(_cwe0,&cta_res[52], 0x08); memcpy(_cwe1,&cta_res[28], 0x08); } @@ -521,124 +1580,164 @@ static int32_t nagra3_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, str memcpy(_cwe0,&cta_res[28], 0x08); memcpy(_cwe1,&cta_res[52], 0x08); } - - if(!reader->cwekey_length) + if(cta_res[27] == 0x5C) { - rdr_log_dbg(reader, D_READER, "ERROR: CWPK is not set, can not decrypt CW"); - return ERROR; + if(!reader->cwekey0_length) + { + rdr_log(reader, "ERROR: CWPK is not set, can not decrypt CW"); + return ERROR; + } + des_ecb3_decrypt(_cwe0, reader->cwekey0); + des_ecb3_decrypt(_cwe1, reader->cwekey0); } - des_ecb3_decrypt(_cwe0, reader->cwekey); - des_ecb3_decrypt(_cwe1, reader->cwekey); - - int chkok = 1; - if(((_cwe0[0] + _cwe0[1] + _cwe0[2]) & 0xFF) != _cwe0[3]) + else if(cta_res[27] == 0x58) { - chkok = 0; - rdr_log_dbg(reader, D_READER, "CW0 checksum error [0]"); + des_ecb3_decrypt(_cwe0, reader->key3des); + des_ecb3_decrypt(_cwe1, reader->key3des); } - - if(((_cwe0[4] + _cwe0[5] + _cwe0[6]) & 0xFF) != _cwe0[7]) + rdr_log_dbg(reader, D_READER, "CW Decrypt ok"); + memcpy(ea->cw, _cwe0, 0x08); + memcpy(ea->cw + 8, _cwe1, 0x08); + return OK; + } + else if(cta_res[23] == 0x00) + { + memcpy(reader->ecmheader, cta_res + 9, 4); + reader->cak7_camstate = cta_res[4]; + if(reader->hasunique && reader->pairtype < 0xC0) { - chkok = 0; - rdr_log_dbg(reader, D_READER, "CW0 checksum error [1]"); + rdr_log(reader, "reinit card in Unique Pairing Mode"); } - - if(((_cwe1[0] + _cwe1[1] + _cwe1[2]) & 0xFF) != _cwe1[3]) + else { - chkok = 0; - rdr_log_dbg(reader, D_READER, "CW1 checksum error [0]"); + rdr_log(reader, "card has no right to decode this channel"); } - - if(((_cwe1[4] + _cwe1[5] + _cwe1[6]) & 0xFF) != _cwe1[7]) + } + else if(cta_res[23] == 0x04) + { + if(!reader->nuid_length) { - chkok = 0; - rdr_log_dbg(reader, D_READER, "CW1 checksum error [1]"); + rdr_log(reader, "reinit card with NUID"); } - - reader->cak7_camstate = cta_res[4]; - if(chkok == 1) + else { - rdr_log_dbg(reader, D_READER, "CW Decrypt ok"); - memcpy(ea->cw, _cwe0, 0x08); - memcpy(ea->cw + 8, _cwe1, 0x08); - return OK; + rdr_log(reader, "wrong OTP/OTA CSC values"); } } - + else + { + rdr_log(reader, "card got wrong ECM"); + } return ERROR; } - static int32_t nagra3_do_emm(struct s_reader *reader, EMM_PACKET *ep) { def_resp; - uint8_t emmreq[0xC0]; - memset(emmreq, 0xCC, 0xC0); - emmreq[ 7] = 0x05; - emmreq[ 8] = 0x8A; - emmreq[ 9] = 0x00; - emmreq[10] = 0x00; - emmreq[11] = 0x00; - emmreq[12] = 0x00; - emmreq[13] = 0x01; - memcpy(&emmreq[14], ep->emm + 9, ep->emm[9] + 1); - do_cak7_cmd(reader, cta_res, &cta_lr, emmreq, sizeof(emmreq), 0xB0); - - if(cta_lr == 0) - { - rdr_log_dbg(reader, D_READER, "card reinit necessary"); - CAK7_reinit(reader); - } - else if(cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 1] != 0x00) + if(ep->emm[0] == 0x90) { - rdr_log(reader, "(EMM) Reader will be restart now cause: %02X %02X card answer!!!", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); - CAK7_reinit(reader); + rdr_log(reader, "OSCam got your BoxEMM"); + char tmp[128]; + rdr_log(reader, "NUID: %s", cs_hexdump(1, reader->nuid, 4, tmp, sizeof(tmp))); + rdr_log(reader, "Index: %s", cs_hexdump(1, ep->emm + 10, 1, tmp, sizeof(tmp))); + rdr_log(reader, "eCWPK: %s", cs_hexdump(1, ep->emm + 11, 16, tmp, sizeof(tmp))); } else { - if(reader->cak7_seq >= reader->cak7_restart) + uint8_t emmreq[0xC0]; + memset(emmreq, 0xCC, 0xC0); + emmreq[ 7] = 0x05; + if(reader->caid == 0x1830) { - rdr_log_dbg(reader, D_READER, "reinit necessary to reset command counter"); - CAK7_reinit(reader); + emmreq[ 9] = 0x00; + emmreq[10] = 0x00; + emmreq[11] = 0x00; + emmreq[12] = 0x00; + emmreq[13] = 0x00; } - else if(cta_res[4] == 0x80) + else { - rdr_log_dbg(reader, D_READER, "EMM forced card to reinit"); - reader->card_status = CARD_NEED_INIT; - add_job(reader->client, ACTION_READER_RESTART, NULL, 0); - return OK; + emmreq[ 9] = 0x04; + emmreq[10] = reader->ecmheader[0]; + emmreq[11] = reader->ecmheader[1]; + emmreq[12] = reader->ecmheader[2]; + emmreq[13] = reader->ecmheader[3]; } - else if(cta_res[13] == 0x02) + if(reader->cak7type == 3) { - rdr_log_dbg(reader, D_READER, "Revision update - card reinit necessary"); - reader->card_status = CARD_NEED_INIT; - add_job(reader->client, ACTION_READER_RESTART, NULL, 0); - return OK; + int32_t i; + uint8_t *prov_id_ptr; + switch(ep->type) + { + case SHARED: + emmreq[8] = ep->emm[9] + 6; + prov_id_ptr = ep->emm + 3; + memcpy(&emmreq[14], ep->emm + 9, ep->emm[9] + 1); + break; + case UNIQUE: + emmreq[8] = ep->emm[12] + 6; + prov_id_ptr = ep->emm + 9; + memcpy(&emmreq[14], ep->emm + 12, ep->emm[12] + 1); + break; + case GLOBAL: + emmreq[8] = ep->emm[6] + 6; + prov_id_ptr = ep->emm + 3; + memcpy(&emmreq[14], ep->emm + 6, ep->emm[6] + 1); + break; + default: + rdr_log(reader, "EMM: Congratulations, you have discovered a new EMM on Merlin."); + rdr_log(reader, "This has not been decoded yet."); + return ERROR; + } + i = get_prov_index(reader, prov_id_ptr); + if(i == -1) + { + rdr_log(reader, "EMM: skipped since provider id doesnt match"); + return SKIPPED; + } } - else if((cta_res[4] & 64) == 64) + else { - rdr_log_dbg(reader, D_READER, "negotiating new Session Key"); - CAK7_getCamKey(reader); + emmreq[8] = ep->emm[9] + 6; + memcpy(&emmreq[14], ep->emm + 9, ep->emm[9] + 1); } - else if(cta_res[8] == 0x0E) + do_cak7_cmd(reader, cta_res, &cta_lr, emmreq, sizeof(emmreq), 0xB0); + if((cta_res[cta_lr-2] != 0x90 && cta_res[cta_lr-1] != 0x00) || cta_lr == 0) { - rdr_log_dbg(reader, D_READER, "card got wrong EMM"); - return OK; + rdr_log(reader, "(EMM) Reader will be restart now cause: %02X %02X card answer!!!", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); } - - struct timeb now; - cs_ftime(&now); - int64_t gone_now = comp_timeb(&now, &reader->emm_last); - int64_t gone_refresh = comp_timeb(&reader->emm_last, &reader->last_refresh); - if(((gone_now > (int64_t)3600*1000) && (gone_now < (int64_t)365*24*3600*1000)) || ((gone_refresh > (int64_t)12*3600*1000) && (gone_refresh < (int64_t)365*24*3600*1000))) + else { - reader->last_refresh=now; - add_job(reader->client, ACTION_READER_CARDINFO, NULL, 0); // refresh entitlement since it might have been changed! + memcpy(reader->ecmheader, cta_res + 9, 4); + if(reader->cak7_seq >= reader->needrestart) + { + rdr_log(reader, "card needs FASTreinit to prevent crash"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + else if((cta_res[4] & 64) == 64) + { + rdr_log(reader, "negotiating new Session Key"); + if(!CAK7_GetCamKey(reader)) + { + rdr_log(reader, "negotiations failed - trying FASTreinit"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + } } } - return OK; } - const struct s_cardsystem reader_nagracak7 = { .desc = "nagra merlin", @@ -651,5 +1750,4 @@ const struct s_cardsystem reader_nagracak7 = .get_emm_type = nagra_get_emm_type, .get_emm_filter = nagra_get_emm_filter, }; - #endif diff --git a/reader-seca.c b/reader-seca.c index 76826095d..33d451222 100644 --- a/reader-seca.c +++ b/reader-seca.c @@ -441,7 +441,14 @@ static int32_t seca_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struc if((cta_res[0] == 0x93) && (cta_res[1] == 0x02)) { write_cmd(ins3a, NULL); // get cw - snprintf(ea->msglog, MSGLOGSIZE, "unsubscribed 93 02"); + if(er->ecm[2] > 0x61 && er->ecm[7] == 0x5C && er->ecm[100] == 0x0B) + { + rdr_log(reader, "reinit card in CAK7 mode"); + } + else + { + snprintf(ea->msglog, MSGLOGSIZE, "unsubscribed 93 02"); + } return ERROR; } // exit if unsubscribed diff --git a/reader-viaccess.c b/reader-viaccess.c index 4aa6185c1..dbf916e73 100644 --- a/reader-viaccess.c +++ b/reader-viaccess.c @@ -6,7 +6,120 @@ #include "reader-common.h" #include "cscrypt/des.h" #include "oscam-work.h" - +typedef unsigned int uint; +typedef unsigned char byte; +uint8_t N98Init = 0; +uint KeyS[132]; +char SBoxInverse[] = { + 13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2, + 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0, + 12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7, + 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1, + 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1, + 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0, + 15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11, + 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 }; +uint getBit(uint number, int bt) +{ + return (number >> bt ) & 1; +} +void SBitslice(uint* Iarray, int offsetin, uint* Oarray, int offsetout, int row, char* box) +{ + int bi,input,l,output; + uint calcArray[4] = {0,0,0,0}; + for (bi = 0; bi < 32; bi++) + { + input = 0; + for (l = 0; l < 4; l++) + input += getBit(Iarray[l+offsetin],bi) << l; + output = box[input+16*row]; + for (l = 0; l < 4; l++) + calcArray[l] += getBit(output,l) << bi; + } + for (l = 0; l < 4; l++) + Oarray[l+offsetout] = calcArray[l]; +} +void xorBlock(uint* data, int offset) +{ + int i; + for (i = 0; i < 4; i++) + data[i] ^= KeyS[i + 4*offset]; +} +uint rotr(uint val, int nbits) +{ + return ((val >> nbits) + (val << (32 - nbits))); +} +void LTBitsliceInverse(uint* data) +{ + uint C0 = data[0]; + uint C1 = data[1]; + uint C2 = data[2]; + uint C3 = data[3]; + C2 = rotr(C2,22); + C0 = rotr(C0,5); + C2 ^= C3 ^ (C1 << 7); + C0 ^= C1 ^ C3; + C3 = rotr(C3,7); + C3 ^= C2 ^ (C0 << 3); + C1 = rotr(C1,1); + C1 ^= C0 ^ C2; + C2 = rotr(C2,3); + C0 = rotr(C0,13); + data[0] = C0; + data[1] = C1; + data[2] = C2; + data[3] = C3; +} +void N98_decrypt(byte* data) +{ + int r; // round + int i,j; + uint N98[4]; // make 32 bits words + for(i = 0; i < 4; i++) + N98[i] = data[4*i] + (data[4*i+1] << 8) + (data[4*i+2] << 16) + (data[4*i+3] << 24); + for(r = 31; r >= 0; r--) // decrypt, inverse order + { + if (r == 31) { + xorBlock(N98,32); + } else { + LTBitsliceInverse(N98); // inverse Linear Transform + } + SBitslice(N98,0,N98,0, (r & 7), SBoxInverse); // SBox inverse, bitslice mode + xorBlock(N98, r); + } + for(i = 0; i < 4; i++) // set bytes to caller's array + for(j = 0; j < 4; j++) + data[4*i+j] = (N98[i] >> 8*j) & 0xFF; +} +void MakeSubKeys(uint* KeySch, char key98Idx) +{ + int i,k; + char j; + uint w[140] = {0}; + char Sbox[128]; + // uint KeyS[132]; + // calc SBox , inverse of SBoxInverse + for(i = 0; i < 8; i++) //row + { + for(j = 0; j < 16; j++) // search for column + { + k=0; + while (SBoxInverse[k+16*i] != j) + k = k +1; + Sbox[j+16*i] = k; + } + } + switch (key98Idx) + { + case 0x21: + w[0]=0x6341F22E; w[1]=0x002E2D10; w[2]=0x181D7704; w[3]=0x1D93A0F3; w[4]=1; w[5]=0; w[6]=0; w[7]=0; + break; + } + for(i = 0; i < 132; i++) + w[i+8] = rotr(w[i+0] ^ w[i+3] ^ w[i+5] ^ w[i+7] ^ 0x9E3779B9 ^ i, 21); + for(i = 0; i < 33; i++) + SBitslice(w,4*i+8,KeySch,4*i, ((35-i) & 7), Sbox); // SBox , bitslice mode +} struct geo_cache { uint32_t provid; @@ -14,33 +127,27 @@ struct geo_cache uint8_t geo_len; int32_t number_ecm; }; - struct viaccess_data { struct geo_cache last_geo; uint8_t availkeys[CS_MAXPROV][16]; }; - struct via_date { uint16_t day_s : 5; uint16_t month_s : 4; uint16_t year_s : 7; - uint16_t day_e : 5; uint16_t month_e : 4; uint16_t year_e : 7; }; - static void parse_via_date(const uint8_t *buf, struct via_date *vd, int32_t fend) { uint16_t date; - date = (buf[0] << 8) | buf[1]; vd->day_s = date & 0x1f; vd->month_s = (date >> 5) & 0x0f; vd->year_s = (date >> 9) & 0x7f; - if(fend) { date = (buf[2] << 8) | buf[3]; @@ -49,55 +156,44 @@ static void parse_via_date(const uint8_t *buf, struct via_date *vd, int32_t fend vd->year_e = (date >> 9) & 0x7f; } } - struct emm_rass *find_rabuf(struct s_client *client, int32_t provid, uint8_t nano, int8_t add) { struct emm_rass *e; LL_ITER it; - if(!client->ra_buf) { client->ra_buf = ll_create("client->ra_buf"); } - it = ll_iter_create(client->ra_buf); - while((e = ll_iter_next(&it)) != NULL) { if(!add && e->provid == provid && e->emmlen != 0) { return e; } - if(add && e->provid == provid && e->emm[0] == nano) { return e; } } - if(!add) { return NULL; } - if(!cs_malloc(&e, sizeof(struct emm_rass))) { return NULL; } - e->provid = provid; ll_append(client->ra_buf, e); return e; } - static void show_class(struct s_reader *reader, const char *p, uint32_t provid, const uint8_t *b, int32_t l) { int32_t i, j; - // b -> via date (4 uint8_ts) b += 4; l -= 4; - j = l - 1; for(; j >= 0; j--) { @@ -109,7 +205,6 @@ static void show_class(struct s_reader *reader, const char *p, uint32_t provid, struct via_date vd; parse_via_date(b - 4, &vd, 1); cls = (l - (j + 1)) * 8 + i; - if(p) // just show class info, dont add entitlement! { rdr_log(reader, "%sclass: %02X, expiry date: %04d/%02d/%02d - %04d/%02d/%02d", p, cls, @@ -121,7 +216,6 @@ static void show_class(struct s_reader *reader, const char *p, uint32_t provid, rdr_log(reader, "class: %02X, expiry date: %04d/%02d/%02d - %04d/%02d/%02d", cls, vd.year_s + 1980, vd.month_s, vd.day_s, vd.year_e + 1980, vd.month_e, vd.day_e); - //convert time: time_t start_t, end_t; struct tm tm; @@ -130,7 +224,6 @@ static void show_class(struct s_reader *reader, const char *p, uint32_t provid, tm.tm_mon = vd.month_s - 1; // january is 0 in tm_mon tm.tm_mday = vd.day_s; start_t = cs_timegm(&tm); - tm.tm_year = vd.year_e + 80; //via year starts in 1980, tm_year starts in 1900 tm.tm_mon = vd.month_e - 1; // january is 0 in tm_mon tm.tm_mday = vd.day_e; @@ -141,15 +234,12 @@ static void show_class(struct s_reader *reader, const char *p, uint32_t provid, } } } - static int8_t add_find_class(struct s_reader *reader, uint32_t provid, const uint8_t *b, int32_t l, int8_t add) { int32_t i, j, freshdate = 0; - // b -> via date (4 uint8_ts) b += 4; l -= 4; - j = l - 1; for(; j >= 0; j--) { @@ -174,15 +264,13 @@ static int8_t add_find_class(struct s_reader *reader, uint32_t provid, const uin parse_via_date(b - 4, &vd, 1); time_t start_t, end_t; struct tm tm; - //convert time: memset(&tm, 0, sizeof(tm)); tm.tm_year = vd.year_s + 80; // via year starts in 1980, tm_year starts in 1900 tm.tm_mon = vd.month_s - 1; // january is 0 in tm_mon tm.tm_mday = vd.day_s; start_t = cs_timegm(&tm); - - tm.tm_year = vd.year_e + 80; // via year starts in 1980, tm_year starts in 1900 + tm.tm_year = vd.year_e + 80; //via year starts in 1980, tm_year starts in 1900 tm.tm_mon = vd.month_e - 1; // january is 0 in tm_mon tm.tm_mday = vd.day_e; end_t = cs_timegm(&tm); @@ -202,15 +290,12 @@ static int8_t add_find_class(struct s_reader *reader, uint32_t provid, const uin } } } - if(freshdate == 0) { return -2; } - return 1; // emmdate is fresh! } - static void show_subs(struct s_reader *reader, const uint8_t *emm) { switch(emm[0]) @@ -220,22 +305,18 @@ static void show_subs(struct s_reader *reader, const uint8_t *emm) show_class(reader, "nano A9: ", 1, emm + 2, emm[1]); break; } - case 0xA6: { char szGeo[256]; - memset(szGeo, 0, 256); cs_strncpy(szGeo, (char *)emm + 2, emm[1]); rdr_log(reader, "nano A6: geo %s", szGeo); break; } - case 0xB6: { uint8_t m; // modexp struct via_date vd; - m = emm[emm[1] + 1]; parse_via_date(emm + 2, &vd, 0); rdr_log(reader, "nano B6: modexp %d%d%d%d%d%d: %02d/%02d/%04d", @@ -250,12 +331,10 @@ static void show_subs(struct s_reader *reader, const uint8_t *emm) } } } - static int32_t chk_prov(struct s_reader *reader, uint8_t *id, uint8_t keynr) { struct viaccess_data *csystem_data = reader->csystem_data; int32_t i, j, rc; - for(rc = i = 0; (!rc) && (i < reader->nprov); i++) { if(!memcmp(&reader->prid[i][1], id, 3)) @@ -271,25 +350,18 @@ static int32_t chk_prov(struct s_reader *reader, uint8_t *id, uint8_t keynr) } return (rc); } - static int32_t get_maturity(struct s_reader *reader) { /* retrieve maturity rating on the card */ - def_resp; - uint8_t insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data uint8_t insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data - insac[2] = 0x06; write_cmd(insac, NULL); // request maturity rating - insb8[4] = 0x02; write_cmd(insb8, NULL); // read maturity rating nano + len - insb8[4] = cta_res[1]; write_cmd(insb8, NULL); // read maturity rating - reader->maturity = cta_res[cta_lr - 3] & 0x0F; if (reader->maturity < 0xF) { @@ -301,15 +373,12 @@ static int32_t get_maturity(struct s_reader *reader) } return 0; } - static int32_t unlock_parental(struct s_reader *reader) { /* disabling parental lock. assuming pin "0000" if no pin code is provided in the config */ - static const uint8_t inDPL[] = { 0xca, 0x24, 0x02, 0x00, 0x09 }; uint8_t cmDPL[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F }; def_resp; - if(strcmp(reader->pincode, "none")) { rdr_log(reader, "Using PIN %s", reader->pincode); @@ -322,7 +391,6 @@ static int32_t unlock_parental(struct s_reader *reader) rdr_log(reader, "Using PIN 0000!"); } write_cmd(inDPL, cmDPL); - if(!(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0)) { if(strcmp(reader->pincode, "none")) @@ -339,17 +407,14 @@ static int32_t unlock_parental(struct s_reader *reader) rdr_log(reader, "Parental lock disabled"); get_maturity(reader); } - return 0; } - int32_t hdSurEncBasicCrypt_D2_0F_11(int32_t Value, int32_t XorVal) { int32_t i = (Value << 13) - Value + 0x1B59; i = (i * Value) + 0x07CF; return (i ^ XorVal); } - int32_t hdSurEncCryptLookup_D2_0F_11(uint8_t Value, uint8_t AddrInd) { static const uint8_t lookup[] = @@ -518,7 +583,6 @@ int32_t hdSurEncCryptLookup_D2_0F_11(uint8_t Value, uint8_t AddrInd) uint8_t b = Value ^ hdSurEncBasicCrypt_D2_0F_11(Value, lookup[(((AddrInd * 2) + 0) * 256) + Value]); return (Value ^ hdSurEncBasicCrypt_D2_0F_11(b, lookup[(((AddrInd * 2) + 1) * 256) + b])); } - void hdSurEncPhase1_D2_0F_11(uint8_t *CWs) { static const uint8_t lookup1[] = @@ -531,14 +595,12 @@ void hdSurEncPhase1_D2_0F_11(uint8_t *CWs) static const int8_t lookup2[] = { 1, -1, -1, 1, -1, 2, 1, -2, -1, 1, 2, -2, 1, -2, -2, 4 }; static const int8_t CAddrIndex[] = { 0, 1, 3, 4 }; int32_t i, j, i1, i2, i3; - for(i = 3; i >= 0; --i) { for(j = 0; j <= 15; ++j) { CWs[j] = CWs[j] ^ hdSurEncBasicCrypt_D2_0F_11(j , lookup1 [(16 * i) + j]); } - uint8_t Buffer[16]; uint32_t k; for(i1 = 0; i1 <= 3; ++i1) @@ -554,7 +616,6 @@ void hdSurEncPhase1_D2_0F_11(uint8_t *CWs) } } memcpy(CWs, Buffer, 16); - // CW positions are mixed around here uint8_t a4[4]; for(i1 = 1; i1 <= 3; ++i1) @@ -563,11 +624,9 @@ void hdSurEncPhase1_D2_0F_11(uint8_t *CWs) { a4[i2] = i1 + (i2 * 4); } - for(i2 = 0; i2 <= i1 - 1; ++i2) // the given code in Func1_3 seems to be wrong here(3 instead of i1-1)! { uint8_t tmp = CWs[a4[0]]; - for(i3 = 1; i3 <= 3; ++i3) { CWs[a4[i3 - 1]] = CWs[a4[i3]]; @@ -575,24 +634,20 @@ void hdSurEncPhase1_D2_0F_11(uint8_t *CWs) CWs[a4[3]] = tmp; } } - for(i1 = 0; i1 <= 15; ++i1) { CWs[i1] = hdSurEncCryptLookup_D2_0F_11(CWs[i1], CAddrIndex[i1 & 3]); } } } - void hdSurEncPhase2_D2_0F_11_sub(uint8_t *CWa, uint8_t *CWb, uint8_t AddrInd) { uint8_t Buffer[8]; uint8_t tmp, i; - for(i = 0; i <= 7; ++i) { Buffer[i] = hdSurEncCryptLookup_D2_0F_11(CWb[i], AddrInd); } - // some bitshifting tmp = Buffer[7]; for(i = 7; i >= 1; --i) @@ -600,14 +655,12 @@ void hdSurEncPhase2_D2_0F_11_sub(uint8_t *CWa, uint8_t *CWb, uint8_t AddrInd) Buffer[i] = ((Buffer[1] >> 4) & 0xFF) | ((Buffer[i - 1] << 4) & 0xFF); } Buffer[0] = ((Buffer[0] >> 4) & 0xFF) | ((tmp << 4) & 0xFF); - // saving the result for(i = 0; i <= 7; ++i) { CWa[i] = CWa[i] ^ Buffer[i]; } } - void hdSurEncPhase2_D2_0F_11(uint8_t *CWs) { hdSurEncPhase2_D2_0F_11_sub(CWs, CWs + 8, 0); @@ -616,7 +669,6 @@ void hdSurEncPhase2_D2_0F_11(uint8_t *CWs) hdSurEncPhase2_D2_0F_11_sub(CWs + 8, CWs, 3); hdSurEncPhase2_D2_0F_11_sub(CWs, CWs + 8, 4); } - void CommonMain_1_D2_13_15(const uint8_t *datain, uint8_t *dataout) { const uint8_t Tab3[88] = { @@ -626,10 +678,8 @@ void CommonMain_1_D2_13_15(const uint8_t *datain, uint8_t *dataout) 0x09, 0x06, 0x06, 0x04, 0x09, 0x06, 0x06, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x02, 0x01, 0x02, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - int i1,i2; unsigned long bb; - for (i1 = 0; i1 < 11; i1++) { bb = 0; @@ -640,23 +690,17 @@ void CommonMain_1_D2_13_15(const uint8_t *datain, uint8_t *dataout) dataout[i1] = (bb & 0xFF); } } - - unsigned short CommonMain_2_D2_13_15(const uint8_t *data, const unsigned long num) { unsigned long bb1, bb2; - bb1 = num >> 3; bb2 = (data[bb1] << 24) + (data[bb1 + 1] << 16) + (data[bb1 + 2] << 8); - return ((bb2 >> (21 - (num & 7))) & 0x7FF); } - void CommonMain_3_D2_13_15(uint8_t *data0, uint8_t *data1, int nbrloop) { int i; unsigned long bb1, bb2; - bb1 = 0; bb2 = 0; for (i = nbrloop - 1; i >= 0; i--) @@ -669,10 +713,8 @@ void CommonMain_3_D2_13_15(uint8_t *data0, uint8_t *data1, int nbrloop) bb2 >>= 8; } } - void CommonMain_D2_13_15(const uint8_t *datain, uint8_t *dataout, int loopval) { - const uint8_t Tab0_Comp[0x800] = { 0x54,0x75,0x01,0x0C,0x7C,0xE2,0xC3,0xC2,0x5E,0x13,0x26,0xCA,0xB2,0xCD,0xB8,0x3D, 0x02,0x2C,0xE4,0x19,0x41,0x3D,0xE4,0x0F,0xEC,0xF1,0x45,0x83,0xE2,0xE2,0x72,0xF9, @@ -802,8 +844,6 @@ void CommonMain_D2_13_15(const uint8_t *datain, uint8_t *dataout, int loopval) 0xA1,0x9B,0x45,0x15,0x90,0x63,0x22,0x5D,0x68,0x4B,0xCF,0xFA,0x6A,0x06,0xF0,0x26, 0xAC,0x6C,0x3A,0x89,0x25,0xF3,0x5E,0x90,0x06,0x93,0xB6,0x35,0x0D,0x85,0x60,0x98, 0xBC,0x6E,0xF2,0xA5,0x17,0x29,0x70,0xD6,0xFF,0x0C,0xD0,0xC0,0x35,0xD7,0x4A,0xFD }; - - const uint8_t Tab1_Comp[11*8] = { 0x70,0x49,0xD7,0xE3,0xDF,0x3C,0x96,0x03,0x2A,0x70,0x82,0xA6,0x5F,0xDE,0xCC,0x0C, 0x2A,0x62,0x2A,0x3E,0xA4,0x0C,0x0A,0xAB,0x4F,0x06,0x5D,0xD4,0x14,0xAA,0xE1,0xC3, @@ -811,28 +851,23 @@ void CommonMain_D2_13_15(const uint8_t *datain, uint8_t *dataout, int loopval) 0x8F,0x85,0xC9,0x04,0x56,0xBA,0xEB,0x3F,0x42,0x9F,0xCB,0x66,0x55,0x45,0x1C,0x96, 0xFF,0x4D,0x35,0xDF,0x88,0x0E,0xDC,0xC8,0x4E,0x3F,0x81,0x74,0xD8,0x77,0x4C,0x8E, 0x00,0xC0,0x64,0x83,0x4E,0xBB,0xF0,0xB1 }; - unsigned short buff8[8]; uint8_t buff11[11 + 1]; // +1 to avoid func2 bug int i1, i2; buff11[11] = 0; - CommonMain_1_D2_13_15(datain, buff11); for (i1 = 0; i1 < 11; i1++) { buff11[i1] ^= Tab1_Comp[(loopval * 11) + i1]; } - for (i1 = 0; i1 < 8; i1++) { buff8[i1] = CommonMain_2_D2_13_15(buff11, i1 * 11); } - for (i1 = 0; i1 < 8; i1++) { dataout[i1] = Tab0_Comp[buff8[i1]]; } - i1 = 1; while (i1 < 8) { @@ -845,24 +880,20 @@ void CommonMain_D2_13_15(const uint8_t *datain, uint8_t *dataout, int loopval) i1 *= 2; } } - void Common_D2_13_15(uint8_t *cw0, const uint8_t *cw1, int loopval) { int i; uint8_t buff8[8]; - CommonMain_D2_13_15(cw1, buff8, loopval); for (i = 0; i < 8; i++) { cw0[i] ^= buff8[i]; } } - void ExchangeCWs(uint8_t *cw0, uint8_t *cw1) { int i; uint8_t b; - for (i = 0; i < 8; i++) { b = cw1[i]; @@ -870,12 +901,9 @@ void ExchangeCWs(uint8_t *cw0, uint8_t *cw1) cw0[i] = b; } } - - void hdSurEncPhase1_D2_13_15(uint8_t *cws) { int i; - for (i = 0; i <= 7; i++) { // Possible code @@ -890,11 +918,9 @@ void hdSurEncPhase1_D2_13_15(uint8_t *cws) } ExchangeCWs((uint8_t *) &cws[0], (uint8_t *) &cws[8]); } - void hdSurEncPhase2_D2_13_15(uint8_t *cws) { int i; - for (i = 7; i >= 0; i--) { // Possible code @@ -909,8 +935,6 @@ void hdSurEncPhase2_D2_13_15(uint8_t *cws) } ExchangeCWs((uint8_t *) &cws[8], (uint8_t *) &cws[0]); } - - static int32_t viaccess_card_init(struct s_reader *reader, ATR *newatr) { get_atr; @@ -926,25 +950,20 @@ static int32_t viaccess_card_init(struct s_reader *reader, ATR *newatr) static uint8_t ins8702_data[] = { 0x00, 0x00, 0x11}; static uint8_t ins8704[] = { 0x87, 0x04, 0x00, 0x00, 0x07 }; static uint8_t ins8706[] = { 0x87, 0x06, 0x00, 0x00, 0x04 }; - if((atr[1] != 0x77) || ((atr[2] != 0x18) && (atr[2] != 0x11) && (atr[2] != 0x19)) || ((atr[9] != 0x68) && (atr[9] != 0x6C) && (atr[9] != 0x64))) { return ERROR; } - write_cmd(insFAC, FacDat); if(!(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0)) { return ERROR; } - if(!cs_malloc(&reader->csystem_data, sizeof(struct viaccess_data))) { return ERROR; } - struct viaccess_data *csystem_data = reader->csystem_data; - write_cmd(insFAC, ins8702_data); if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) { @@ -959,7 +978,6 @@ static int32_t viaccess_card_init(struct s_reader *reader, ATR *newatr) } } } - reader->caid = 0x500; memset(reader->prid, 0xff, sizeof(reader->prid)); insac[2] = 0xa4; @@ -970,7 +988,6 @@ static int32_t viaccess_card_init(struct s_reader *reader, ATR *newatr) // rdr_log(reader, "[viaccess-reader] type: Viaccess, ver: %s serial: %llu", ver, b2ll(5, cta_res+2)); rdr_log_sensitive(reader, "type: Viaccess (%sstandard atr), caid: %04X, serial: {%llu}", atr[9] == 0x68 ? "" : "non-", reader->caid, (unsigned long long) b2ll(5, cta_res + 2)); - i = 0; insa4[2] = 0x00; write_cmd(insa4, NULL); // select issuer 0 @@ -985,37 +1002,31 @@ static int32_t viaccess_card_init(struct s_reader *reader, ATR *newatr) memcpy(&csystem_data->availkeys[i][0], cta_res + 10, 16); snprintf((char *)buf + cs_strlen((char *)buf), sizeof(buf) - cs_strlen((char *)buf), ",%06X", b2i(3, &reader->prid[i][1])); // rdr_log(reader, "[viaccess-reader] buf: %s", buf); - insac[2] = 0xa5; write_cmd(insac, NULL); // request sa insb8[4] = 0x06; write_cmd(insb8, NULL); // read sa memcpy(&reader->sa[i][0], cta_res + 2, 4); - insa4[2] = 0x02; write_cmd(insa4, NULL); // select next issuer i++; } reader->nprov = i; rdr_log(reader, "providers: %d (%s)", reader->nprov, buf + 1); - get_maturity(reader); if(cfg.ulparent) { unlock_parental(reader); } - rdr_log(reader, "ready for requests"); return OK; } - bool dcw_crc(uint8_t *dw) { int8_t i; for(i = 0; i < 16; i += 4) if(dw[i + 3] != ((dw[i] + dw[i + 1] + dw[i + 2]) & 0xFF)) { return 0; } return 1; } - static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) { def_resp; @@ -1024,7 +1035,6 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s uint8_t insf8[] = { 0xca, 0xf8, 0x00, 0x00, 0x00 }; // set geographic info static const uint8_t insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x12 }; // read dcw struct viaccess_data *csystem_data = reader->csystem_data; - // //XXX what is the 4th uint8_t for ?? int32_t ecm88Len = MIN(MAX_ECM_SIZE - 4, SCT_LEN(er->ecm) - 4); if(ecm88Len < 1) @@ -1040,32 +1050,29 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s int32_t rc = 0; int32_t hasD2 = 0; uint8_t hasE0 = 0; + int32_t has98 = 0; int32_t curEcm88len = 0; int32_t nanoLen = 0; uint8_t *nextEcm; uint8_t DE04[MAX_ECM_SIZE]; int32_t D2KeyID = 0; int32_t curnumber_ecm = 0; + uint8_t SubECM = 0; + char key98Idx = 0; // nanoD2 d2 02 0d 02 -> D2 nano, len 2 // 0b, 0f, 13 -> pre AES decrypt CW // 0d, 11, 15 -> post AES decrypt CW - int32_t nanoD2 = 0; // knowns D2 nanos: 0x0b ,0x0d ,0x0f ,0x11, 0x13, 0x15 - memset(DE04, 0, sizeof(DE04)); //fix dorcel de04 bug - nextEcm = ecm88Data; - while(ecm88Len > 0 && !rc) { - if(ecm88Data[0] == 0x00 && ecm88Data[1] == 0x00) { // nano 0x00 and len 0x00 aren't valid... something is obviously wrong with this ecm. rdr_log(reader, "ECM: Invalid ECM structure. Rejecting"); return ERROR; } - // 80 33 nano 80 (ecm) + len (33) if(ecm88Data[0] == 0x80) // nano 80, give ecm len { @@ -1074,12 +1081,10 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s ecm88Data += 2; ecm88Len -= 2; } - if(!curEcm88len) // there was no nano 80 -> simple ecm { curEcm88len = ecm88Len; } - // d2 02 0d 02 -> D2 nano, len 2, select the AES key to be used if(ecm88Data[0] == 0xd2) { @@ -1089,37 +1094,31 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s nanoD2 = 0x0b; rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x0b"); } - if(ecm88Data[2] == 0x0d) { nanoD2 = 0x0d; rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x0d"); } - if(ecm88Data[2] == 0x0f) { nanoD2 = 0x0f; rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x0f"); } - if(ecm88Data[2] == 0x11) { nanoD2 = 0x11; rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x11"); } - if(ecm88Data[2] == 0x13) { nanoD2 = 0x13; rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x13"); } - if(ecm88Data[2] == 0x15) { nanoD2 = 0x15; rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x15"); } - // use the d2 arguments to get the key # to be used int32_t len = ecm88Data[1] + 2; D2KeyID = ecm88Data[3]; @@ -1132,7 +1131,6 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s { hasD2 = 0; } - // 40 07 03 0b 00 -> nano 40, len =7 ident 030B00 (tntsat), key #0 <== we're pointing here // 09 -> use key #9 // 05 67 00 @@ -1141,10 +1139,8 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s uint8_t ident[3], keynr; uint8_t *ecmf8Data = 0; int32_t ecmf8Len = 0; - nanoLen = ecm88Data[1] + 2; keynr = ecm88Data[4] & 0x0F; - // 40 07 03 0b 00 -> nano 40, len =7 ident 030B00 (tntsat), key #0 <== we're pointing here // 09 -> use key #9 if(nanoLen > 5) @@ -1156,18 +1152,17 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s // as some card don't support this if(csystem_data->last_geo.number_ecm > 0) { - if(csystem_data->last_geo.number_ecm == curnumber_ecm && !(ecm88Data[nanoLen - 1] == 0x01)) + if(csystem_data->last_geo.number_ecm == curnumber_ecm && !(ecm88Data[nanoLen - 1] == 0x00)) // select permut 4, accept 01 and FF { keynr = ecm88Data[5]; rdr_log_dbg(reader, D_READER, "keyToUse = %02x, ECM ending with %02x", ecm88Data[5], ecm88Data[nanoLen - 1]); } else { - if(ecm88Data[nanoLen - 1] == 0x01) - { - rdr_log_dbg(reader, D_READER, "Skip ECM ending with = %02x for ecm number (%x) for provider %02x%02x%02x", - ecm88Data[nanoLen - 1], curnumber_ecm, ecm88Data[2], ecm88Data[3], ecm88Data[4]); - } + // if(ecm88Data[nanoLen - 1] == 0x00) // select permut 4 + // { + // rdr_log_dbg(reader, D_READER, "Skip ECM ending with = %02x for ecm number (%x) for provider %02x%02x%02x", ecm88Data[nanoLen - 1], curnumber_ecm, ecm88Data[2], ecm88Data[3], ecm88Data[4]); + // } rdr_log_dbg(reader, D_READER, "Skip ECM ending with = %02x for ecm number (%x)", ecm88Data[nanoLen - 1], curnumber_ecm); ecm88Data = nextEcm; ecm88Len -= curEcm88len; @@ -1180,11 +1175,9 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s rdr_log_dbg(reader, D_READER, "keyToUse = %02x", ecm88Data[5]); } } - memcpy(ident, &ecm88Data[2], sizeof(ident)); provid = b2i(3, ident); ident[2] &= 0xF0; - if(hasD2 && reader->aes_list) { // check that we have the AES key to decode the CW @@ -1194,32 +1187,51 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s return ERROR; } } - if(!chk_prov(reader, ident, keynr)) { rdr_log_dbg(reader, D_READER, "ECM: provider or key not found on card"); snprintf(ea->msglog, MSGLOGSIZE, "provider(%02x%02x%02x) or key(%d) not found on card", ident[0], ident[1], ident[2], keynr); return ERROR; } - + SubECM = ecm88Data[nanoLen - 1] ; // 01 permut 4 , FF no permut ecm88Data += nanoLen; ecm88Len -= nanoLen; curEcm88len -= nanoLen; - - // DE04 if(ecm88Data[0] == 0xDE && ecm88Data[1] == 0x04) { memcpy(DE04, &ecm88Data[0], 6); ecm88Data += 6; } - - // E0 (seen so far in logs: E0020002 or E0022002, but not in all cases delivers invalid cw so just detect!) - if(ecm88Data[0] == 0xE0 && ecm88Data[1] == 0x02) + if((ecm88Data[0] == 0xD9) && (ecm88Data[1] == 0x0A)) { - rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano E0 ECM detected!"); - hasE0=1; + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano D9/0A ECM detected!"); + if((ecm88Data[12] == 0xE0) && (ecm88Data[13] == 0x02) && (ecm88Data[15] == 0x02)) // accept parental data 0F for ecm88Data[2] + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano E0 ECM detected!"); + hasE0 = 1; + } + if(((ecm88Data[16] ==0x98) || (ecm88Data[19] == 0x98)) && ((ecm88Data[17] == 0x08) || (ecm88Data[20] == 0x08))) + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98 ECM detected!"); + has98 = 1; + key98Idx = ecm88Data[25]; + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98 Index : %02X",key98Idx); + } + } + else + { + if((ecm88Data[0] == 0xE0) && (ecm88Data[1] == 0x02) && (ecm88Data[3] == 0x02)) // accept parental data 0F for ecm88Data[2] + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano E0 ECM detected!"); + hasE0 = 1; + } + } + if((ecm88Data[4] == 0x98) && (ecm88Data[5] == 0x08)) + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98 ECM detected!"); + has98 = 1; + key98Idx = ecm88Data[0x0D]; } - if(csystem_data->last_geo.provid != provid) { csystem_data->last_geo.provid = provid; @@ -1227,7 +1239,6 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s csystem_data->last_geo.geo[0] = 0; write_cmd(insa4, ident); // set provider } - // Nano D2 0x0b, 0x0f, 0x13 -> pre AES decrypt CW if(hasD2 && (nanoD2 == 0x0b|| nanoD2 == 0x0f|| nanoD2 == 0x13)) { @@ -1243,38 +1254,33 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s ecm88DataCW = ecm88DataCW + cwStart + 2; must_exit = 1; } - cwStart++; + cwStart = cwStart + ecm88Data[cwStart + 1] + 2; // parse via nanos + // cwStart++; // error if EA 10 in nanos datas } - if(nanoD2 == 0x0f) { hdSurEncPhase1_D2_0F_11(ecm88DataCW); hdSurEncPhase2_D2_0F_11(ecm88DataCW); } - if(nanoD2 == 0x13) { hdSurEncPhase1_D2_13_15(ecm88DataCW); } - // use AES from list to decrypt CW rdr_log_dbg(reader, D_READER, "Decoding CW : using AES key id %d for provider %06x", D2KeyID, (provid & 0xFFFFF0)); if(aes_decrypt_from_list(reader->aes_list, 0x500, (uint32_t)(provid & 0xFFFFF0), D2KeyID, &ecm88DataCW[0], 16) == 0) { snprintf(ea->msglog, MSGLOGSIZE, "Missing AES key(%d)[aka E%X]",D2KeyID, D2KeyID); } - if(nanoD2 == 0x0f) { hdSurEncPhase1_D2_0F_11(ecm88DataCW); } - if(nanoD2 == 0x13) { hdSurEncPhase2_D2_13_15(ecm88DataCW); } } - while(ecm88Len > 1 && ecm88Data[0] < 0xA0) { nanoLen = ecm88Data[1] + 2; @@ -1287,7 +1293,6 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s curEcm88len -= nanoLen; ecm88Data += nanoLen; } - if(ecmf8Len) { if(csystem_data->last_geo.geo_len != ecmf8Len || memcmp(csystem_data->last_geo.geo, ecmf8Data, csystem_data->last_geo.geo_len)) @@ -1322,7 +1327,6 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s { write_cmd(ins88, (uint8_t *)ecm88Data); // request dcw } - write_cmd(insc0, NULL); // read dcw switch(cta_res[0]) { @@ -1332,21 +1336,18 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s memcpy(ea->cw, cta_res + 2, 8); rc = 1; } break; - case 0xe9: // odd if(cta_res[1] == 8) { memcpy(ea->cw + 8, cta_res + 2, 8); rc = 1; } break; - case 0xea: // complete if(cta_res[1] == 16) { memcpy(ea->cw, cta_res + 2, 16); rc = 1; } break; - default : ecm88Data = nextEcm; ecm88Len -= curEcm88len; @@ -1363,9 +1364,7 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s return ERROR; /*Lets interupt the loop and exit, because we don't know this ECM type.*/ } } - // Nano D2 0d, 11, 15 -> post AES decrypt CW - if(hasD2 && !dcw_crc(ea->cw) && (nanoD2 == 0x0d || nanoD2 == 0x11 || nanoD2 == 0x15)) { if(nanoD2 == 0x11) @@ -1373,72 +1372,81 @@ static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, s hdSurEncPhase1_D2_0F_11(ea->cw); hdSurEncPhase2_D2_0F_11(ea->cw); } - if(nanoD2 == 0x15) { hdSurEncPhase1_D2_13_15(ea->cw); } rdr_log_dbg(reader, D_READER, "Decoding CW : using AES key id %d for provider %06x", D2KeyID, (provid & 0xFFFFF0)); rc = aes_decrypt_from_list(reader->aes_list, 0x500, (uint32_t)(provid & 0xFFFFF0), D2KeyID, ea->cw, 16); - if(rc == 0) { snprintf(ea->msglog, MSGLOGSIZE, "Missing AES key(%d)[aka E%X]",D2KeyID, D2KeyID); } - if(nanoD2 == 0x11) { hdSurEncPhase1_D2_0F_11(ea->cw); } - if(nanoD2 == 0x15) { hdSurEncPhase2_D2_13_15(ea->cw); } } - + if (has98) + { + if (N98Init == 0) + { + MakeSubKeys(KeyS, key98Idx); + N98Init = 1; + } + uint8_t inp[16]; + memcpy(inp,ea->cw,16); + N98_decrypt(inp); + memcpy(ea->cw,inp,16); + } if (hasE0) { if (reader->initCA28) { - rdr_log_dbg(reader, D_READER, "Decrypting nano E0 encrypted cw."); - uint8_t returnedcw[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t returnedcw[16]; memcpy(returnedcw,ea->cw,16); - // Processing 3DES // Processing even cw des(returnedcw, reader->key_schedule1, 0); // decrypt des(returnedcw, reader->key_schedule2, 1); // crypt des(returnedcw, reader->key_schedule1, 0); // decrypt - // Processing odd cw des(returnedcw + 8, reader->key_schedule1, 0); // decrypt des(returnedcw + 8, reader->key_schedule2, 1); // crypt des(returnedcw + 8, reader->key_schedule1, 0); // decrypt - // returning value memcpy(ea->cw,returnedcw, 16); } else { - snprintf(ea->msglog, MSGLOGSIZE, "nano E0 detected, no valid boxkey / deskey defined: no decoding"); + snprintf(ea->msglog, MSGLOGSIZE, "Nano E0 detected, no valid boxkey + deskey defined: no decoding"); } } + if (SubECM == 1) + { + uint8_t rw[16]; + memcpy(rw, ea->cw, 16); + memcpy(ea->cw, rw+4, 4); + memcpy(ea->cw+4, rw, 4); + memcpy(ea->cw+8, rw+12, 4); + memcpy(ea->cw+12, rw+8, 4); + } return (rc ? OK : ERROR); } - static int32_t viaccess_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) { uint32_t provid = 0; rdr_log_dbg(rdr, D_EMM, "Entered viaccess_get_emm_type ep->emm[0]=%02x", ep->emm[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: @@ -1454,13 +1462,11 @@ static int32_t viaccess_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) { return 1; // let server decide! } - case 0x8A: case 0x8B: ep->type = GLOBAL; rdr_log_dbg(rdr, D_EMM, "GLOBAL"); return 1; - case 0x8C: case 0x8D: ep->type = SHARED; @@ -1468,13 +1474,11 @@ static int32_t viaccess_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) // 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); - // local reader int8_t i; for(i = 0; i < rdr->nprov; i++) @@ -1485,14 +1489,12 @@ static int32_t viaccess_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) } return (!memcmp(&rdr->sa[0][0], ep->hexserial, 3)); } /* fallthrough */ - default: ep->type = UNKNOWN; rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); return 1; } } - static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) { if(*emm_filters == NULL) @@ -1500,18 +1502,14 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em bool network = is_network_reader(rdr); int8_t device_emm = ((rdr->deviceemm > 0) ? 1 : 0); // set to 1 if device specific emms should be catched too const unsigned int max_filter_count = 4 + ((device_emm != 0 && rdr->nprov > 0) ? 1:0) + (3 * ((rdr->nprov > 0) ? (rdr->nprov - 1) : 0)); - if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) { return ERROR; } - struct s_csystem_emm_filter *filters = *emm_filters; *filter_count = 0; - int32_t idx = 0; int32_t prov; - if(rdr->nprov > 0 && device_emm == 1) { filters[idx].type = EMM_GLOBAL; // 8A or 8B no reassembly needed! @@ -1522,7 +1520,6 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em filters[idx].mask[3] = 0x80; idx++; } - // shared are most important put them on top, define first since viaccess produces a lot of filters! for(prov = 0; (prov < rdr->nprov); prov++) { @@ -1530,7 +1527,6 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em { continue; } - filters[idx].type = EMM_SHARED; // 8C or 8D always first part of shared, second part delivered by 8E! filters[idx].enabled = 1; filters[idx].filter[0] = 0x8C; @@ -1542,7 +1538,6 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em filters[idx].mask[5] = 0xF0; // ignore last digit since this is key on card indicator! } idx++; - filters[idx].type = EMM_SHARED; // 8E second part reassembly with 8c/8d needed! filters[idx].enabled = 1; filters[idx].filter[0] = 0x8E; @@ -1554,7 +1549,6 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em } idx++; } - // globals are less important, define last since viaccess produces a lot of filters! for(prov = 0; (prov < rdr->nprov); prov++) { @@ -1562,7 +1556,6 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em { continue; } - filters[idx].type = EMM_GLOBAL; // 8A or 8B no reassembly needed! filters[idx].enabled = 1; filters[idx].filter[0] = 0x8A; @@ -1580,7 +1573,6 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em } idx++; } - filters[idx].type = EMM_UNIQUE; filters[idx].enabled = 1; filters[idx].filter[0] = 0x88; @@ -1596,13 +1588,10 @@ static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_em memset(&filters[idx].mask[1], 0xFF, 3); } idx++; - *filter_count = idx; } - return OK; } - static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) { def_resp; @@ -1612,9 +1601,7 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) uint8_t ins18[] = { 0xca, 0x18, 0x01, 0x01, 0x00 }; // set subscription uint8_t ins1c[] = { 0xca, 0x1c, 0x01, 0x01, 0x00 }; // set subscription, encrypted struct viaccess_data *csystem_data = reader->csystem_data; - int32_t emmdatastart = 7; - if (ep->emm[1] == 0x01) // emm from cccam { emmdatastart = 12; @@ -1631,22 +1618,17 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) emmdatastart = 5; } } - if(ep->type == UNIQUE) { emmdatastart++; } - if(ep->type == GLOBAL && emmdatastart == 7) { emmdatastart -= 4; } - int32_t emmLen = SCT_LEN(ep->emm) - emmdatastart; int32_t rc = 0; - rdr_log_dump(reader, ep->emm, emmLen + emmdatastart, "RECEIVED EMM VIACCESS"); - int32_t emmUpToEnd; uint8_t *emmParsed = ep->emm + emmdatastart; int32_t provider_ok = 0; @@ -1661,17 +1643,13 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) uint8_t *nano9EData = 0; uint8_t *nanoF0Data = 0; uint8_t *nanoA9Data = 0; - for(emmUpToEnd = emmLen; (emmParsed[1] != 0) && (emmUpToEnd > 0); emmUpToEnd -= (2 + emmParsed[1]), emmParsed += (2 + emmParsed[1])) { rdr_log_dump(reader, emmParsed, emmParsed[1] + 2, "NANO"); - if(emmParsed[0] == 0x90 && emmParsed[1] == 0x03) { /* identification of the service operator */ - uint8_t soid[3], ident[3], i; - for(i = 0; i < 3; i++) { soid[i] = ident[i] = emmParsed[2 + i]; @@ -1679,7 +1657,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) ident[2] &= 0xF0; emm_provid = b2i(3, ident); keynr = soid[2] & 0x0F; - if(chk_prov(reader, ident, keynr)) { provider_ok = 1; @@ -1689,7 +1666,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) rdr_log(reader, "EMM: ignored since provider or key not present on card (%x, %x)", emm_provid, keynr); return SKIPPED; } - // check if the provider changes. If yes, set the new one. If not, don't... card will return an error if we do. if(csystem_data->last_geo.provid != emm_provid) { @@ -1706,22 +1682,17 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) csystem_data->last_geo.provid = 0; csystem_data->last_geo.geo_len = 0; csystem_data->last_geo.geo[0] = 0; - } else if(emmParsed[0] == 0x9e && emmParsed[1] == 0x20) { /* adf */ - if(!nano91Data) { /* adf is not crypted, so test it */ - uint8_t custwp; uint8_t *afd; - custwp = reader->sa[0][3]; afd = (uint8_t *)emmParsed + 2; - if(afd[31 - custwp / 8] & (1 << (custwp & 7))) { rdr_log_dbg(reader, D_READER, "emm for our card %08X", b2i(4, &reader->sa[0][0])); @@ -1732,10 +1703,8 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) return SKIPPED; } } - // memorize nano9EData = emmParsed; - } else if(emmParsed[0] == 0x81) { @@ -1783,7 +1752,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) return SKIPPED; } int8_t match = add_find_class(reader, emm_provid, emmParsed + 2, emmParsed[1], 0); - if(match == -2) { rdr_log(reader, "shared emm provid %06X all classes have entitlementdate already same or newer -> skipped!", emm_provid); @@ -1791,12 +1759,10 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) } nanoA9Data = emmParsed; } - memcpy(ins18Data + ins18Len, emmParsed, emmParsed[1] + 2); ins18Len += emmParsed [1] + 2; } } - if(!provider_ok) { rdr_log_dbg(reader, D_READER, "provider not found in emm, continue anyway"); @@ -1804,13 +1770,11 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) keynr = 1; ///return ERROR; } - if(!nanoF0Data) { rdr_log_dump(reader, ep->emm, ep->emmlen, "can't find 0xf0 in emm..."); return ERROR; // error } - if(nano9EData) { if(!nano91Data) @@ -1835,7 +1799,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) memcpy(insData, nano91Data, nano91Data[1] + 2); memcpy(insData + nano91Data[1] + 2, nano9EData, nano9EData[1] + 2); write_cmd(insf4, insData); - if((cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 2] != 0x91) || cta_res[cta_lr - 1] != 0x00) { rdr_log_dump(reader, insf4, 5, "set adf encrypted cmd:"); @@ -1845,7 +1808,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) } } } - if(!nano92Data) { // send subscription @@ -1855,7 +1817,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) memcpy(insData, ins18Data, ins18Len); memcpy(insData + ins18Len, nanoF0Data, nanoF0Data[1] + 2); write_cmd(ins18, insData); - if((cta_res[cta_lr - 2] == 0x90 || cta_res[cta_lr - 2] == 0x91) && cta_res[cta_lr - 1] == 0x00) { if(nanoA9Data) @@ -1869,7 +1830,6 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) { rdr_log_dump(reader, ins18, 5, "set subscription cmd:"); rdr_log_dump(reader, insData, ins18[4], "set subscription data:"); - if(!(cta_res[cta_lr -2] == 0x90 && cta_res[cta_lr - 1] == 0x40)) // dont throw softerror 9040 in log! { rdr_log(reader, "update error: %02X %02X", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); @@ -1879,41 +1839,55 @@ static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) rc = 2; // skipped } } - } else { // send subscription encrypted - if(!nano81Data) { rdr_log_dump(reader, ep->emm, ep->emmlen, "0x92 found, but can't find 0x81 in emm..."); return ERROR; // error } - ins1c[2] = nano9EData ? 0x01 : 0x00; // found 9E nano ? if(ep->type == UNIQUE) { ins1c[2] = 0x02; } - ins1c[3] = keynr; // key ins1c[4] = nano92Data[1] + 2 + nano81Data[1] + 2 + nanoF0Data[1] + 2; memcpy(insData, nano92Data, nano92Data[1] + 2); memcpy(insData + nano92Data[1] + 2, nano81Data, nano81Data[1] + 2); memcpy(insData + nano92Data[1] + 2 + nano81Data[1] + 2, nanoF0Data, nanoF0Data[1] + 2); write_cmd(ins1c, insData); - if((cta_res[cta_lr - 2] == 0x90 || cta_res[cta_lr - 2] == 0x91) && (cta_res[cta_lr - 1] == 0x00 || cta_res[cta_lr - 1] == 0x08)) { rdr_log(reader, "update successfully written"); } + if(cta_res[cta_lr - 2] == 0x98 && cta_res[cta_lr - 1] == 0x00 ) + { + static const uint8_t insFAC[] = { 0x87, 0x02, 0x00, 0x00, 0x03 }; // init FAC + static uint8_t ins8702_data[] = { 0x00, 0x00, 0x11}; + static uint8_t ins8704[] = { 0x87, 0x04, 0x00, 0x00, 0x07 }; + static uint8_t ins8706[] = { 0x87, 0x06, 0x00, 0x00, 0x04 }; + write_cmd(insFAC, ins8702_data); + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + write_cmd(ins8704, NULL); + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + write_cmd(ins8706, NULL); + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + csystem_data->last_geo.number_ecm = (cta_res[2] << 8) | (cta_res[3]); + rdr_log(reader, "using ecm #%x for long viaccess ecm", csystem_data->last_geo.number_ecm); + } + } + } + } rc = 1; } - return rc; } - static int32_t viaccess_card_info(struct s_reader *reader) { def_resp; @@ -1926,31 +1900,24 @@ static int32_t viaccess_card_info(struct s_reader *reader) uint8_t insa4[] = { 0xca, 0xa4, 0x00, 0x00, 0x00 }; // select issuer uint8_t insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x00 }; // read data item static const uint8_t ins24[] = { 0xca, 0x24, 0x00, 0x00, 0x09 }; // set pin - uint8_t cls[] = { 0x00, 0x21, 0xff, 0x9f}; + uint8_t prebook[] = {0x00,0x00,0x00,0xFF,0xFF,0xFF }; static const uint8_t pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04}; struct viaccess_data *csystem_data = reader->csystem_data; - csystem_data->last_geo.provid = 0; csystem_data->last_geo.geo_len = 0; csystem_data->last_geo.geo[0] = 0; - rdr_log(reader, "card detected"); - cs_clear_entitlement(reader); //reset the entitlements - // set pin write_cmd(ins24, pin); - insac[2] = 0xa4; write_cmd(insac, NULL); // request unique id insb8[4] = 0x07; write_cmd(insb8, NULL); // read unique id rdr_log_sensitive(reader, "serial: {%llu}", (unsigned long long) b2ll(5, cta_res + 2)); - insa4[2] = 0x00; write_cmd(insa4, NULL); // select issuer 0 - for(i = 1; (cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0); i++) { bool added = false; @@ -1960,13 +1927,11 @@ static int32_t viaccess_card_info(struct s_reader *reader) write_cmd(insc0, NULL); // show provider properties cta_res[2] &= 0xF0; l_provid = b2i(3, cta_res); - insac[2] = 0xa5; write_cmd(insac, NULL); // request sa insb8[4] = 0x06; write_cmd(insb8, NULL); // read sa l_sa = b2i(4, cta_res + 2); - insac[2] = 0xa7; write_cmd(insac, NULL); // request name insb8[4] = 0x02; @@ -1976,7 +1941,6 @@ static int32_t viaccess_card_info(struct s_reader *reader) write_cmd(insb8, NULL); // read name cta_res[l] = 0; trim((char *)cta_res); - if(cta_res[0]) { snprintf((char *)l_name, sizeof(l_name), ", name: %.55s", cta_res); @@ -1985,7 +1949,6 @@ static int32_t viaccess_card_info(struct s_reader *reader) { l_name[0] = 0; } - // read GEO insac[2] = 0xa6; write_cmd(insac, NULL); // request GEO @@ -1996,7 +1959,6 @@ static int32_t viaccess_card_info(struct s_reader *reader) insb8[4] = l; write_cmd(insb8, NULL); // read geo rdr_log_sensitive(reader, "provider: %d, id: {%06X%s}, sa: {%08X}, geo: %s", i, l_provid, l_name, l_sa, (l < 4) ? "empty" : cs_hexdump(1, cta_res, l, tmp, sizeof(tmp))); - // read classes subscription insac[2] = 0xa9; insac[4] = 4; @@ -2009,7 +1971,6 @@ static int32_t viaccess_card_info(struct s_reader *reader) cls[1] = tmpdate & 0xff; } write_cmd(insac, cls); // request class subs - while((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) { insb8[4] = 0x02; @@ -2026,13 +1987,30 @@ static int32_t viaccess_card_info(struct s_reader *reader) } } } - if(!added) { // add entitlement info for provid without class cs_add_entitlement(reader, reader->caid, l_provid, 0, 0, 0, 0, 5, 1); } - + // Read List of «pre-booked pay-per-view per programme» entitlements within the range [INUMB, FNUMB] + insac[2] = 0xaa; + insac[4] = 6; + write_cmd(insac, prebook ); // request class subs + while((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + insb8[4] = 0x08; + write_cmd(insb8, NULL); // read PVV + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) + { + l = cta_res[1]; + write_cmd(insb8, NULL); // read PPV + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00 || cta_res[cta_lr - 1] == 0x08)) + { + cs_add_entitlement(reader, reader->caid, l_provid, b2i(3, cta_res+2), 0 , 0, 0, 2, 1); + cs_add_entitlement(reader, reader->caid, l_provid, b2i(3, cta_res+5), 0 , 0, 0, 2, 1); + } + } + } insac[4] = 0; insa4[2] = 0x02; write_cmd(insa4, NULL); // select next provider @@ -2049,7 +2027,6 @@ static int32_t viaccess_card_info(struct s_reader *reader) uint8_t ins28_data[4]; memcpy(ins28_data, reader->boxkey, 4); write_cmd(ins28, ins28_data); // unlock card to reply on E002xxyy - if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) { rdr_log(reader, "CA 28 initialisation successful!"); @@ -2066,7 +2043,6 @@ static int32_t viaccess_card_info(struct s_reader *reader) //end process init CA 28 return OK; } - static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *client, EMM_PACKET *ep) { uint8_t *buffer = ep->emm; @@ -2075,13 +2051,11 @@ static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *cl int16_t k; int32_t prov, provid = 0; struct emm_rass *r_emm = NULL; - // Viaccess if(*len > 500) { return 0; } - switch(buffer[0]) { case 0x8c: @@ -2090,24 +2064,20 @@ static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *cl provid = b2i(3, ep->emm + 5); // extract provid from emm provid &= 0xFFFFF0; // last digit is dont care r_emm = find_rabuf(client, provid, (uint8_t) buffer[0], 1); - if(!r_emm) { cs_log("[viaccess] ERROR: Can't allocate EMM reassembly buffer."); return 0; } - if(!memcmp(&r_emm->emm, &buffer[0], *len)) // skip same shared emm, this make sure emmlen isnt replaced. emmlen = 0 means this shared emm has been used for reassembly { return 0; } - memset(&r_emm->emm[0], 0, sizeof(r_emm->emm)); // zero it! memcpy(&r_emm->emm[0], &buffer[0], *len); // put the fresh new shared emm r_emm->emmlen = *len; // put the emmlen indicating that this shared emm isnt being reassembled rdr_log_dump_dbg(rdr, D_EMM, r_emm->emm, r_emm->emmlen, "%s: received fresh emm-gh for provid %06X", __func__, provid); return 0; - case 0x8e: // emm-s part 2 for(prov = 0; prov < rdr->nprov ; prov++) @@ -2120,14 +2090,12 @@ static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *cl provid = b2i(4, ep->provid); // use provid from emm since we have nothing better! provid &= 0xFFFFF0; // last digit is dont care } - else { provid = b2i(4, rdr->prid[prov]); // get corresponding provid from reader since there is no provid in emm payload! provid &= 0xFFFFF0; // last digit is dont care } r_emm = find_rabuf(client, provid, 0, 0); // nano = don't care, the shared 8c or 8d not been written gets returned! - if(!r_emm || !r_emm->emmlen) { continue; // match but no emm-gh found for this provider @@ -2138,14 +2106,12 @@ static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *cl } } } - if(!r_emm || !r_emm->emmlen) { return 0; // stop -> no emm-gh found! } //extract nanos from emm-gh and emm-s uint8_t emmbuf[512]; - rdr_log_dbg(rdr, D_EMM, "%s: start extracting nanos", __func__); //extract from emm-gh for(i = 3; i < r_emm->emmlen; i += r_emm->emm[i + 1] + 2) @@ -2154,27 +2120,23 @@ static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *cl memcpy(emmbuf + pos, r_emm->emm + i, r_emm->emm[i + 1] + 2); pos += r_emm->emm[i + 1] + 2; } - if(buffer[2] == 0x2c) { //add 9E 20 nano + first 32 uint8_ts of emm content memcpy(emmbuf + pos, "\x9E\x20", 2); memcpy(emmbuf + pos + 2, buffer + 7, 32); pos += 34; - //add F0 08 nano + 8 subsequent uint8_ts of emm content memcpy(emmbuf + pos, "\xF0\x08", 2); memcpy(emmbuf + pos + 2, buffer + 39, 8); pos += 10; } - else if(buffer[2] == 0x34 && (provid >> 8) == 0x0702) { //add 9E 20 nano + first 32 uint8_ts of emm content memcpy(emmbuf + pos, "\x9E\x20", 2); memcpy(emmbuf + pos + 2, buffer + 7, 32); pos += 34; - //add F0 10 nano + 16 subsequent uint8_ts of emm content memcpy(emmbuf + pos, "\xF0\x10", 2); memcpy(emmbuf + pos + 2, buffer + 39, 16); @@ -2190,25 +2152,19 @@ static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *cl pos += buffer[k + 1] + 2; } } - rdr_log_dump_dbg(rdr, D_EMM, buffer, *len, "%s: %s emm-s", __func__, (buffer[2] == 0x2c) ? "fixed" : "variable"); - emm_sort_nanos(buffer + 7, emmbuf, pos); pos += 7; - //calculate emm length and set it on position 2 buffer[2] = pos - 3; - rdr_log_dump_dbg(rdr, D_EMM, r_emm->emm, r_emm->emmlen, "%s: emm-gh provid %06X", __func__, provid); rdr_log_dump_dbg(rdr, D_EMM, buffer, pos, "%s: assembled emm", __func__); - *len = pos; r_emm->emmlen = 0; // mark this shared 8c or 8d as being used for reassembly and send to reader! break; } return 1; } - const struct s_cardsystem reader_viaccess = { .desc = "viaccess", @@ -2221,5 +2177,4 @@ const struct s_cardsystem reader_viaccess = .get_emm_type = viaccess_get_emm_type, .get_emm_filter = viaccess_get_emm_filter, }; - #endif diff --git a/reader-videoguard-common.c b/reader-videoguard-common.c index 9875bf833..5ebf755a7 100644 --- a/reader-videoguard-common.c +++ b/reader-videoguard-common.c @@ -64,7 +64,10 @@ void set_known_card_info(struct s_reader *reader, const uint8_t *atr, const uint 22, 1997, 0, NDS2, "VideoGuard Sky Italia (0919)" }, { { 0x3F, 0xFF, 0x14, 0x25, 0x03, 0x10, 0x80, 0x41, 0xB0, 0x01, 0x69, 0xFF, 0x4A, 0x50, 0x70, 0x00, 0x00, 0x5A, 0x4A, 0x01, 0x00, 0x00 }, - 22, 2004, 0, NDS2, "VideoGuard Dolce Romania (092F)" }, + 22, 2004, 0, NDS2, "VideoGuard Telekom (Dolce TV) Romania (092F)" }, + + { { 0x3F, 0xFD, 0x13, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0F, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x5A, 0x4A, 0x03 }, + 21, 2004, 0, NDS2, "VideoGuard Telekom (Dolce TV) Romania (0952)" }, { { 0x3F, 0xFF, 0x14, 0x25, 0x03, 0x10, 0x80, 0x41, 0xB0, 0x01, 0x69, 0xFF, 0x4A, 0x50, 0x70, 0x00, 0x00, 0x5A, 0x4B, 0x01, 0x00, 0x00 }, 22, 2004, 0, NDS2, "VideoGuard Viasat Ukraine (0931)" }, @@ -120,8 +123,32 @@ void set_known_card_info(struct s_reader *reader, const uint8_t *atr, const uint { { 0x3F, 0xFD, 0x11, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x05, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x00, 0x00, 0x41, 0x5A, 0x03 }, 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0910)" }, + { { 0x3F, 0xFD, 0x12, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x05, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x00, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0910) FastMode" }, + + { { 0x3F, 0xFD, 0x13, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x05, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x00, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0910) FastMode" }, + + { { 0x3F, 0xFD, 0x14, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x05, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x00, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0910) FastMode" }, + { { 0x3F, 0xFD, 0x15, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x05, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x00, 0x00, 0x41, 0x5A, 0x03 }, - 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0910)" }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0910) FastMode" }, + + { { 0x3F, 0xFD, 0x11, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x10, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0913)"}, + + { { 0x3F, 0xFD, 0x12, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x10, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0913) FastMode"}, + + { { 0x3F, 0xFD, 0x13, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x10, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0913) FastMode"}, + + { { 0x3F, 0xFD, 0x14, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x10, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0913) FastMode"}, + + { { 0x3F, 0xFD, 0x15, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x10, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x41, 0x5A, 0x03 }, + 21, 2004, 50, NDS2, "VideoGuard Astro Malaysia (0913) FastMode"}, { { 0x3F, 0xFF, 0x11, 0x25, 0x03, 0x10, 0x80, 0x41, 0xB0, 0x06, 0x69, 0xFF, 0x4A, 0x50, 0x70, 0x00, 0x00, 0x41, 0x5A, 0x01, 0x00, 0x11 }, 22, 2004, 50, NDS2, "VideoGuard Astro Malaysia (09AC)" }, @@ -260,51 +287,51 @@ void set_known_card_info(struct s_reader *reader, const uint8_t *atr, const uint { { 0x3F, 0xFF, 0x14, 0x25, 0x03, 0x10, 0x80, 0x41, 0xB0, 0x07, 0x69, 0xFF, 0x4A, 0x50, 0x70, 0x80, 0x00, 0x58, 0x45, 0x01, 0x00, 0x14 }, 22, 2004, 0, NDS2, "VideoGuard OTE TV Sat (09BE)" }, - + { { 0x3F, 0xFD, 0x11, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (0927)" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0002 Card (0927)" }, { { 0x3F, 0xFD, 0x12, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0002 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x13, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0002 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x14, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0002 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x15, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0002 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x11, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x54, 0x33, 0x03 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3TV (0927)" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0001 Card (0927)" }, { { 0x3F, 0xFD, 0x12, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x54, 0x33, 0x03 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3TV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0001 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x13, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x54, 0x33, 0x03 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3TV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0001 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x14, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x54, 0x33, 0x03 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3TV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0001 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x15, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x41, 0xB0, 0x0B, 0x69, 0xFF, 0x4A, 0x50, 0xD0, 0x80, 0x00, 0x54, 0x33, 0x03 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3TV (0927) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0001 Card (0927) FastMode" }, { { 0x3F, 0xFD, 0x11, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x55, 0xB0, 0x02, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (09BF)" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0004 Card (09BF)" }, { { 0x3F, 0xFD, 0x12, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x55, 0xB0, 0x02, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (09BF) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0004 Card (09BF) FastMode" }, { { 0x3F, 0xFD, 0x13, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x55, 0xB0, 0x02, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (09BF) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0004 Card (09BF) FastMode" }, { { 0x3F, 0xFD, 0x14, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x55, 0xB0, 0x02, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (09BF) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0004 Card (09BF) FastMode" }, { { 0x3F, 0xFD, 0x15, 0x25, 0x02, 0x50, 0x80, 0x0F, 0x55, 0xB0, 0x02, 0x69, 0xFF, 0x4A, 0x50, 0xF0, 0x80, 0x00, 0x33, 0x54, 0x83 }, - 21, 2008, 0, NDS2, "VideoGuard TrueVisions T3VV (09BF) FastMode" }, + 21, 2008, 0, NDS2, "VideoGuard TrueVisions 0004 Card (09BF) FastMode" }, // NDS Version Unknown as Yet { { 0x3F, 0x7F, 0x13, 0x25, 0x02, 0x40, 0xB0, 0x12, 0x69, 0xFF, 0x4A, 0x50, 0x90, 0x41, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00 }, diff --git a/reader-videoguard2.c b/reader-videoguard2.c index 6ad95bca6..c8bb8bcc3 100644 --- a/reader-videoguard2.c +++ b/reader-videoguard2.c @@ -2,7 +2,6 @@ #ifdef READER_VIDEOGUARD #include "cscrypt/md5.h" #include "cscrypt/des.h" -#include "cscrypt/aes.h" #include "oscam-work.h" #include "reader-common.h" #include "reader-videoguard-common.h" @@ -11,7 +10,7 @@ static void do_post_dw_hash(struct s_reader *reader, uint8_t *cw, const uint8_t *ecm_header_data) { int32_t i, ecmi, ecm_header_count; - uint8_t buffer[0x85]; // original 0x80 but with 0x7D mask applied +8 bytes cw it was still to small + uint8_t buffer[0x85]; // original 0x80 but with 0x7D mask applied +8 bytes cw it was still too small uint8_t md5tmp[MD5_DIGEST_LENGTH]; static const uint16_t Hash3[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xF861, 0xCB52 }; @@ -894,17 +893,6 @@ static int32_t videoguard2_card_init(struct s_reader *reader, ATR *newatr) return ERROR; } - int d37423_ok = 0; - static const uint8_t ins7403[5] = { 0xD0, 0x74, 0x03, 0x00, 0x00 }; // taken from v13 boot log - if(do_cmd(reader, ins7403, NULL, NULL, cta_res) < 0) - { - rdr_log(reader, "classD0 ins7403: failed"); - } - else - { - d37423_ok = (cta_res[2] >> 5) & 1; - } - if(reader->ins7E11[0x01]) // the position of the ins7E is taken from v13 log { uint8_t ins742b[5] = { 0xD0, 0x74, 0x2b, 0x00, 0x00 }; @@ -1054,6 +1042,17 @@ static int32_t videoguard2_card_init(struct s_reader *reader, ATR *newatr) return ERROR; } + int d37423_ok = 0; + static const uint8_t ins7403[5] = { 0xD1, 0x74, 0x03, 0x00, 0x00 }; // taken from v13 boot log + if(do_cmd(reader, ins7403, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins7403: failed"); + } + else + { + d37423_ok = (cta_res[2] >> 5) & 1; + } + // new ins74 present at boot if(d37423_ok) // from ins7403 answer { @@ -1063,19 +1062,19 @@ static int32_t videoguard2_card_init(struct s_reader *reader, ATR *newatr) rdr_log(reader, "classD1 ins7423: failed"); } } -/* - static const uint8_t ins742A[5] = { 0xD1, 0x74, 0x2A, 0x00, 0x00 }; + + static const uint8_t ins742A[5] = { 0xD0, 0x74, 0x2A, 0x00, 0x00 }; if(do_cmd(reader, ins742A, NULL, NULL, cta_res) < 0) { - rdr_log(reader, "classD1 ins742A: failed"); + rdr_log(reader, "classD0 ins742A: failed"); } - static const uint8_t ins741C[5] = { 0xD1, 0x74, 0x1C, 0x00, 0x00 }; - if(do_cmd(reader, ins741C, NULL, NULL, cta_res) < 0) + static const uint8_t ins741B[5] = { 0xD1, 0x74, 0x1B, 0x00, 0x00 }; + if(do_cmd(reader, ins741B, NULL, NULL, cta_res) < 0) { - rdr_log(reader, "classD1 ins741C: failed"); + rdr_log(reader, "classD1 ins741B: failed"); } -*/ + static const uint8_t ins4Ca[5] = { 0xD1, 0x4C, 0x00, 0x00, 0x00 }; payload4C[4] = 0x83; @@ -1111,18 +1110,19 @@ static int32_t videoguard2_card_init(struct s_reader *reader, ATR *newatr) } // get PIN settings - static const uint8_t ins7411[5] = { 0xD1, 0x74, 0x11, 0x00, 0x00 }; + static const uint8_t ins7411[5] = { 0xD3, 0x74, 0x11, 0x00, 0x00 }; uint8_t payload2e4[4]; - if(do_cmd(reader, ins7411, NULL, NULL, cta_res) < 0) + uint8_t rbuff[264]; + if(do_cmd(reader, ins7411, NULL, rbuff, cta_res) < 0) { - rdr_log(reader, "classD1 ins7411: unable to get PIN"); + rdr_log(reader, "classD3 ins7411: unable to get PIN"); return ERROR; } else { memset(payload2e4, 0, 4); - memcpy(payload2e4, cta_res + 2, 4); - reader->VgPin = (cta_res[4] << 8) + cta_res[5]; + memcpy(payload2e4, rbuff + 7, 4); + reader->VgPin = (rbuff[9] << 8) + rbuff[10]; rdr_log(reader, "Pincode read: %04hu", reader->VgPin); } @@ -1168,15 +1168,12 @@ static int32_t videoguard2_card_init(struct s_reader *reader, ATR *newatr) } // fix for 09ac cards - uint8_t dimeno_magic[0x10] = { 0xF9, 0xFB, 0xCD, 0x5A, 0x76, 0xB5, 0xC4, 0x5C, - 0xC8, 0x2E, 0x1D, 0xE1, 0xCC, 0x5B, 0x6B, 0x02 }; - + uint8_t dimeno_magic[0x10] = { 0xF9, 0xFB, 0xCD, 0x5A, 0x76, 0xB5, 0xC4, 0x5C, 0xC8, 0x2E, 0x1D, 0xE1, 0xCC, 0x5B, 0x6B, 0x02 }; int32_t a; for(a = 0; a < 4; a++) { dimeno_magic[a] = dimeno_magic[a] ^ boxID[a]; } - AES_set_decrypt_key(dimeno_magic, 128, &(csystem_data->astrokey)); rdr_log(reader, "type: %s, caid: %04X", csystem_data->card_desc, reader->caid); @@ -1284,7 +1281,7 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er memcpy(buff_55, t_body, 1 ); break; - case 0x56: // tag data for aes + case 0x56: // tag data for aes memcpy(buff_56, t_body, 8); break; @@ -1321,7 +1318,7 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er rdr_log(reader, "classD3 ins54: no cw --> Card isn't active"); test_0F = 0; } - else // These Messages are only nessensary if the Card is active + else // These Messages are only necessary if the Card is active { if((buff_0F[1] >> 4) & 1) // case 0f_0x xx 10 xx xx xx xx { @@ -1368,22 +1365,22 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er // copy cw1 in place memcpy(ea->cw + 0, rbuff + 5, 8); + //case 55_01 xx where bit3==1, bit2==0, bit1==0, and bit0==1, ins7e and CW-Overcrypt may not required + if(((buff_55[0] >> 3) & 1) && (~((buff_55[0] >> 2) & 1)) && (~((buff_55[0] >> 1) & 1)) && (buff_55[0] & 1)) + { + rdr_log_dbg(reader, D_READER, "classD3 ins54: Tag55_01 = %02X, ins7e and CW-overcrypt may not required", buff_55[0]); + } + // case 55_01 xx where bit0==1, CW is crypted if(buff_55[0] & 1) { - if((buff_55[0] >> 3) & 1) //case 55_01 xx where bit3==1, CW-Overcrypt may not required - { - rdr_log_dbg(reader, D_READER, "classD3 ins54: Tag55_01 = %02X, CW-overcrypt may not required", buff_55[0]); - } if(~((buff_55[0] >> 2) & 1)) //case 55_01 xx where bit2==0 { if((buff_55[0] >> 1) & 1) //case 55_01 xx where bit1==1, unique Pairing { rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, trying to decrypt unique pairing mode 0x%02X", buff_55[0]); - if((buff_56[0]|buff_56[1]|buff_56[2]|buff_56[3]|buff_56[4]|buff_56[5]|buff_56[6]|buff_56[7]) != 0) { //when 56_08 is non-zero use AES (mini-patch by para) - rdr_log_dbg(reader, D_READER, "crypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], - buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); + if((buff_56[0]|buff_56[1]|buff_56[2]|buff_56[3]|buff_56[4]|buff_56[5]|buff_56[6]|buff_56[7]) != 0) { //when 56_08 is non-zero use AES + rdr_log_dbg(reader, D_READER, "encrypted AES buffer is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); uint8_t aesbuf[0x10]; uint8_t keybuf[0x10]; AES_KEY aeskey; @@ -1391,12 +1388,14 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er memcpy(aesbuf + 8, buff_56, 8); memcpy(keybuf, &(reader->k1_unique), 16); if(reader->k1_unique[16] == 0x10) { - rdr_log_dbg(reader, D_READER, "use k1(AES) for CW decryption in unique pairing mode"); + rdr_log_dbg(reader, D_READER, "use k1(AES) for AES buffer decryption in unique pairing mode"); AES_set_decrypt_key(keybuf, 128, &aeskey); AES_decrypt(aesbuf, aesbuf, &aeskey); - rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], - aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15], aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + } memcpy(ea->cw + 0, aesbuf, 8); } else { @@ -1438,9 +1437,7 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er { rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, trying to decrypt generic pairing mode 0x%02X", buff_55[0]); if((buff_56[0]|buff_56[1]|buff_56[2]|buff_56[3]|buff_56[4]|buff_56[5]|buff_56[6]|buff_56[7]) != 0) { //when 56_08 is non-zero use AES - rdr_log_dbg(reader, D_READER, "crypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], - buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); + rdr_log_dbg(reader, D_READER, "encrypted AES buffer is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); uint8_t aesbuf[0x10]; uint8_t keybuf[0x10]; AES_KEY aeskey; @@ -1448,16 +1445,18 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er memcpy(aesbuf + 8, buff_56, 8); memcpy(keybuf, &(reader->k1_generic), 16); if(reader->k1_generic[16] == 0x10) { - rdr_log_dbg(reader, D_READER, "use k1(AES) for CW decryption in generic pairing mode"); + rdr_log_dbg(reader, D_READER, "use k1(AES) for AES buffer decryption in generic pairing mode"); AES_set_decrypt_key(keybuf, 128, &aeskey); AES_decrypt(aesbuf, aesbuf, &aeskey); - rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], - aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15], aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + } memcpy(ea->cw + 0, aesbuf, 8); } else { - rdr_log_dbg(reader, D_READER, "k1 for generic pairing mode is not set"); + rdr_log_dbg(reader, D_READER, "k1 for generic pairing mode is not set correctly"); return ERROR; } } @@ -1492,7 +1491,7 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er } } } - else //unknown pairing mode + else //unknown pairing mode { rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, unknown pairing mode 0x%02X", buff_55[0]); if(er->ecm[0] & 1){ //log crypted CW @@ -1507,11 +1506,19 @@ static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er // case 55_01 xx where bit2==1, old dimeno_PostProcess_Decrypt(reader, rbuff, ea->cw); if((buff_55[0] >> 2) & 1) { - uint8_t buffer[0x10]; - memcpy(buffer, rbuff + 5, 8); - memcpy(buffer + 8, buff_56, 8); - AES_decrypt(buffer, buffer, &(csystem_data->astrokey)); - memcpy(ea->cw + 0, buffer, 8); // copy calculated CW in right place + rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, trying to decrypt AES boxkey mode 0x%02X", buff_55[0]); + rdr_log_dbg(reader, D_READER, "encrypted AES buffer is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); + uint8_t aesbuf[0x10]; + memcpy(aesbuf, rbuff + 5, 8); + memcpy(aesbuf + 8, buff_56, 8); + rdr_log_dbg(reader, D_READER, "use dimeno magic for AES buffer decryption"); + AES_decrypt(aesbuf, aesbuf, &(csystem_data->astrokey)); + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15], aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + } + memcpy(ea->cw + 0, aesbuf, 8); // copy calculated CW in right place } if(new_len != lenECMpart2) diff --git a/webif/config/dvbapi.html b/webif/config/dvbapi.html index 92e49ced4..730145f87 100644 --- a/webif/config/dvbapi.html +++ b/webif/config/dvbapi.html @@ -64,4 +64,5 @@ - \ No newline at end of file + +##TPLDEMUXERFIX## diff --git a/webif/config/dvbapi_demuxerfix.html b/webif/config/dvbapi_demuxerfix.html new file mode 100644 index 000000000..55ff073b5 --- /dev/null +++ b/webif/config/dvbapi_demuxerfix.html @@ -0,0 +1,2 @@ + Refers to Stream Relay + Fix Demuxer: diff --git a/webif/config/streamrelay.html b/webif/config/streamrelay.html index 6fe538713..af860a9bb 100644 --- a/webif/config/streamrelay.html +++ b/webif/config/streamrelay.html @@ -14,13 +14,6 @@ Source Stream Port: Source Stream User: Source Stream Password: +##TPLSTREAMCLIENTSOURCEHOST## Relay Port: - ECM fix delay: - Process EMM from stream: - - - - \ No newline at end of file + Relay Buffer Time: diff --git a/webif/config/streamrelay_streamclientsourcehost.html b/webif/config/streamrelay_streamclientsourcehost.html new file mode 100644 index 000000000..e782c80fe --- /dev/null +++ b/webif/config/streamrelay_streamclientsourcehost.html @@ -0,0 +1 @@ + Use stream client as Source Stream Host: diff --git a/webif/include/jquery.js b/webif/include/jquery.js index e83647587..7f37b5d99 100644 --- a/webif/include/jquery.js +++ b/webif/include/jquery.js @@ -1,5 +1,2 @@ -/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; -}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("