Index: CMakeLists.txt =================================================================== --- CMakeLists.txt (revision 11398) +++ CMakeLists.txt (working copy) @@ -101,6 +101,7 @@ ${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} @@ -110,6 +111,7 @@ ${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") @@ -420,6 +422,13 @@ # Manage config.h based on command line parameters # Manipulate config file based on given parameters and read unset parameters +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_EMU OUTPUT_VARIABLE CONFIG_WITH_EMU OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) + add_definitions ("-DWITH_EMU") + set (WITH_EMU "1") + message(STATUS " EMU is added by config compiling with EMU") +endif(CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --show-valid OUTPUT_VARIABLE config_vars_string OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX MATCHALL "[A-Z0-9_]+" config_vars ${config_vars_string}) @@ -449,6 +458,7 @@ 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) @@ -498,7 +508,7 @@ set (exe_name "oscam") add_executable (${exe_name} ${exe_srcs} ${exe_hdrs}) -target_link_libraries (${exe_name} ${csoscam} ${csmodules} ${csreaders} csctapi cscrypt minilzo) +target_link_libraries (${exe_name} ${csoscam} ${csmodules} ${csreaders} csctapi cscrypt minilzo ffdecsa) if(HAVE_LIBRT AND HAVE_LIBUSB) if (LIBUSBDIR) set (libusb_link "imp_libusb") @@ -647,6 +657,11 @@ execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine COMMAND tr -d '\n' OUTPUT_VARIABLE CS_TARGET) add_definitions ("-D'CS_TARGET=\"${CS_TARGET}\"'") #----------------------- global compile and link options ------------------------------ +#enable sse2 on x86 +if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3") +endif (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + # disable warning about unused but set variables in gcc 4.6+ if (CMAKE_COMPILER_IS_GNUCC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) @@ -731,6 +746,22 @@ #-------------------------------------------------------------------------------- +if (NOT OSCamOperatingSystem MATCHES "Mac OS X") +if (NOT DEFINED ENV{ANDROID_NDK}) +if (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) + if(WITH_EMU) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process(COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) + else(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process(COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) + endif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process(COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/utils/SoftCam.Key) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default" ) + endif(WITH_EMU) +endif (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) +endif (NOT DEFINED ENV{ANDROID_NDK}) +endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + #----------------------- installation ----------------------------- file (GLOB config_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/oscam.*") @@ -819,4 +850,8 @@ endif(STATICLIBUSB AND NOT LIBUSBDIR) endif (HAVE_LIBUSB) +if (WITH_EMU) + message(STATUS " Compile with EMU support") +endif (WITH_EMU) + message (STATUS "") Index: Makefile =================================================================== --- Makefile (revision 11398) +++ Makefile (working copy) @@ -65,6 +65,13 @@ LDFLAGS = -Wl,--gc-sections +TARGETHELP := $(shell $(CC) --target-help 2>&1) +ifneq (,$(findstring sse2,$(TARGETHELP))) +override CFLAGS += -fexpensive-optimizations -mmmx -msse -msse2 -msse3 +else +override CFLAGS += -fexpensive-optimizations +endif + # The linker for powerpc have bug that prevents --gc-sections from working # Check for the linker version and if it matches disable --gc-sections # For more information about the bug see: @@ -268,6 +275,21 @@ SRC-$(CONFIG_MODULE_CCCAM) += module-cccam.c SRC-$(CONFIG_MODULE_CCCSHARE) += module-cccshare.c SRC-$(CONFIG_MODULE_CONSTCW) += module-constcw.c +SRC-$(CONFIG_WITH_EMU) += module-emulator.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-osemu.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-stream.c +SRC-$(CONFIG_WITH_EMU) += ffdecsa/ffdecsa.c +UNAME := $(shell uname -s) +ifneq ($(UNAME),Darwin) +ifndef ANDROID_NDK +ifndef ANDROID_STANDALONE_TOOLCHAIN +ifeq "$(CONFIG_WITH_EMU)" "y" +TOUCH_SK := $(shell touch SoftCam.Key) +override LDFLAGS += -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default +endif +endif +endif +endif SRC-$(CONFIG_CS_CACHEEX) += module-csp.c SRC-$(CONFIG_CW_CYCLE_CHECK) += module-cw-cycle-check.c SRC-$(CONFIG_WITH_AZBOX) += module-dvbapi-azbox.c @@ -365,7 +387,7 @@ # starts the compilation. all: @./config.sh --use-flags "$(USE_FLAGS)" --objdir "$(OBJDIR)" --make-config.mak - @-mkdir -p $(OBJDIR)/cscrypt $(OBJDIR)/csctapi $(OBJDIR)/minilzo $(OBJDIR)/webif + @-mkdir -p $(OBJDIR)/cscrypt $(OBJDIR)/csctapi $(OBJDIR)/minilzo $(OBJDIR)/ffdecsa $(OBJDIR)/webif @-printf "\ +-------------------------------------------------------------------------------\n\ | OSCam ver: $(VER) rev: $(SVN_REV) target: $(TARGET)\n\ Index: config.h =================================================================== --- config.h (revision 11398) +++ config.h (working copy) @@ -1,6 +1,7 @@ #ifndef CONFIG_H_ #define CONFIG_H_ +#define WITH_EMU 1 #define WEBIF 1 #define WEBIF_LIVELOG 1 #define WEBIF_JQUERY 1 Index: config.sh =================================================================== --- config.sh (revision 11398) +++ config.sh (working copy) @@ -1,6 +1,6 @@ #!/bin/sh -addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY TOUCH WITH_SSL HAVE_DVBAPI READ_SDT_CHARSETS IRDETO_GUESSING CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT" +addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY TOUCH WITH_SSL HAVE_DVBAPI READ_SDT_CHARSETS IRDETO_GUESSING CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_EMU" 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" readers="READER_NAGRA 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" @@ -24,6 +24,7 @@ # CONFIG_LEDSUPPORT=n CONFIG_CLOCKFIX=y # CONFIG_IPV6SUPPORT=n +CONFIG_WITH_EMU=y # CONFIG_MODULE_CAMD33=n CONFIG_MODULE_CAMD35=y CONFIG_MODULE_CAMD35_TCP=y @@ -289,12 +290,15 @@ update_deps() { # Calculate dependencies - enabled_any $(get_opts readers) $(get_opts card_readers) && enable_opt WITH_CARDREADER >/dev/null - disabled_all $(get_opts readers) $(get_opts card_readers) && disable_opt WITH_CARDREADER >/dev/null + enabled_any $(get_opts readers) $(get_opts card_readers) WITH_EMU && enable_opt WITH_CARDREADER >/dev/null + disabled_all $(get_opts readers) $(get_opts card_readers) WITH_EMU && disable_opt WITH_CARDREADER >/dev/null disabled WEBIF && disable_opt WEBIF_LIVELOG >/dev/null disabled WEBIF && disable_opt WEBIF_JQUERY >/dev/null enabled MODULE_CCCSHARE && enable_opt MODULE_CCCAM >/dev/null enabled_any CARDREADER_DB2COM CARDREADER_MP35 CARDREADER_SC8IN1 CARDREADER_STINGER && enable_opt CARDREADER_PHOENIX >/dev/null + enabled WITH_EMU && enable_opt READER_VIACCESS >/dev/null + enabled WITH_EMU && enable_opt READER_DRE >/dev/null + enabled WITH_EMU && enable_opt MODULE_NEWCAMD >/dev/null } list_config() { @@ -344,9 +348,9 @@ not_have_flag USE_LIBCRYPTO && echo "CONFIG_LIB_AES=y" || echo "# CONFIG_LIB_AES=n" enabled MODULE_CCCAM && echo "CONFIG_LIB_RC6=y" || echo "# CONFIG_LIB_RC6=n" not_have_flag USE_LIBCRYPTO && enabled MODULE_CCCAM && echo "CONFIG_LIB_SHA1=y" || echo "# CONFIG_LIB_SHA1=n" - enabled_any READER_DRE MODULE_SCAM READER_VIACCESS && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" - enabled_any MODULE_CCCAM READER_NAGRA READER_SECA && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" - not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" + enabled_any READER_DRE MODULE_SCAM READER_VIACCESS WITH_EMU && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" + enabled_any MODULE_CCCAM READER_NAGRA READER_SECA WITH_EMU && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" + not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA WITH_EMU && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" } make_config_c() { @@ -457,6 +461,7 @@ LEDSUPPORT "LED support" $(check_test "LEDSUPPORT") \ CLOCKFIX "Clockfix (disable on old systems!)" $(check_test "CLOCKFIX") \ IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \ + WITH_EMU "Emulator support" $(check_test "WITH_EMU") \ 2> ${tempfile} opt=${?} Index: cscrypt/aes.c =================================================================== --- cscrypt/aes.c (revision 11398) +++ cscrypt/aes.c (working copy) @@ -1250,4 +1250,71 @@ rk[3]; PUTU32(out + 12, s3); } + +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc) +{ + unsigned long n; + unsigned long len = length; + unsigned char tmp[AES_BLOCK_SIZE]; + + assert(in && out && key && ivec); + assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc)); + + if(AES_ENCRYPT == enc) + { + while(len >= AES_BLOCK_SIZE) + { + for(n=0; n < AES_BLOCK_SIZE; ++n) + tmp[n] = in[n] ^ ivec[n]; + + AES_encrypt(tmp, out, key); + memcpy(ivec, out, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + + if(len) + { + for(n=0; n < len; ++n) + tmp[n] = in[n] ^ ivec[n]; + + for(n=len; n < AES_BLOCK_SIZE; ++n) + tmp[n] = ivec[n]; + + AES_encrypt(tmp, tmp, key); + memcpy(out, tmp, AES_BLOCK_SIZE); + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } + else + { + while(len >= AES_BLOCK_SIZE) + { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(in, out, key); + + for(n=0; n < AES_BLOCK_SIZE; ++n) + out[n] ^= ivec[n]; + + memcpy(ivec, tmp, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + + if(len) + { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, tmp, key); + + for(n=0; n < len; ++n) + out[n] ^= ivec[n]; + + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } +} #endif Index: cscrypt/aes.h =================================================================== --- cscrypt/aes.h (revision 11398) +++ cscrypt/aes.h (working copy) @@ -42,6 +42,9 @@ void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key); +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc); #endif /* !HEADER_AES_H */ #endif Index: cscrypt/md5.c =================================================================== --- cscrypt/md5.c (revision 11398) +++ cscrypt/md5.c (working copy) @@ -25,13 +25,6 @@ #if !defined(WITH_SSL) && !defined(WITH_LIBCRYPTO) -typedef struct MD5Context -{ - uint32_t buf[4]; - uint32_t bits[2]; - uint32_t in[16]; -} MD5_CTX; - #ifdef __i386__ #define byteReverse(a, b) #else @@ -155,7 +148,7 @@ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -static void MD5_Init(MD5_CTX *ctx) +void MD5_Init(MD5_CTX *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; @@ -170,7 +163,7 @@ * Update context to reflect the concatenation of another buffer full * of bytes. */ -static void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, unsigned int len) +void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, unsigned int len) { uint32_t t; @@ -219,7 +212,7 @@ * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ -static void MD5_Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) +void MD5_Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) { unsigned count; unsigned char *p; Index: cscrypt/md5.h =================================================================== --- cscrypt/md5.h (revision 11398) +++ cscrypt/md5.h (working copy) @@ -7,8 +7,16 @@ #define MD5_DIGEST_LENGTH 16 unsigned char *MD5(const unsigned char *input, unsigned long len, unsigned char *output_hash); -#endif -char *__md5_crypt(const char *text_pass, const char *salt, char *crypted_passwd); +typedef struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint32_t in[16]; +} MD5_CTX; +void MD5_Init(MD5_CTX *ctx); +void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, unsigned int len); +void MD5_Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx); #endif +char *__md5_crypt(const char *text_pass, const char *salt, char *crypted_passwd); +#endif Index: csctapi/cardreaders.h =================================================================== --- csctapi/cardreaders.h (revision 11398) +++ csctapi/cardreaders.h (working copy) @@ -14,5 +14,6 @@ extern const struct s_cardreader cardreader_stapi; extern const struct s_cardreader cardreader_stinger; extern const struct s_cardreader cardreader_drecas; +extern const struct s_cardreader cardreader_emu; #endif Index: ffdecsa/CMakeLists.txt =================================================================== --- ffdecsa/CMakeLists.txt (revision 0) +++ ffdecsa/CMakeLists.txt (working copy) @@ -0,0 +1,8 @@ +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}) Index: ffdecsa/COPYING =================================================================== --- ffdecsa/COPYING (revision 0) +++ ffdecsa/COPYING (working copy) @@ -0,0 +1,339 @@ + 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. Index: ffdecsa/Makefile =================================================================== --- ffdecsa/Makefile (revision 0) +++ ffdecsa/Makefile (working copy) @@ -0,0 +1,2 @@ +parent: + @$(MAKE) --no-print-directory -C .. Index: ffdecsa/ffdecsa.c =================================================================== --- ffdecsa/ffdecsa.c (revision 0) +++ ffdecsa/ffdecsa.c (working copy) @@ -0,0 +1,926 @@ +/* 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() Index: ffdecsa/parallel_064_long.h =================================================================== --- ffdecsa/parallel_064_long.h (revision 0) +++ ffdecsa/parallel_064_long.h (working copy) @@ -0,0 +1,39 @@ +/* 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" Index: ffdecsa/parallel_128_sse2.h =================================================================== --- ffdecsa/parallel_128_sse2.h (revision 0) +++ ffdecsa/parallel_128_sse2.h (working copy) @@ -0,0 +1,82 @@ +/* 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" Index: ffdecsa/parallel_generic.h =================================================================== --- ffdecsa/parallel_generic.h (revision 0) +++ ffdecsa/parallel_generic.h (working copy) @@ -0,0 +1,102 @@ +/* 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 Index: ffdecsa/parallel_std_def.h =================================================================== --- ffdecsa/parallel_std_def.h (revision 0) +++ ffdecsa/parallel_std_def.h (working copy) @@ -0,0 +1,29 @@ +/* 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)) Index: ffdecsa/stream.c =================================================================== --- ffdecsa/stream.c (revision 0) +++ ffdecsa/stream.c (working copy) @@ -0,0 +1,906 @@ +/* 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 + +} + Index: globals.h =================================================================== --- globals.h (revision 11398) +++ globals.h (working copy) @@ -389,17 +389,17 @@ #define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values! // Support for multiple CWs per channel and other encryption algos -//#define WITH_EXTENDED_CW 1 +#define WITH_EXTENDED_CW 1 #if defined(READER_DRE) || defined(READER_DRECAS) || defined(READER_VIACCESS) #define MAX_ECM_SIZE 1024 #define MAX_EMM_SIZE 1024 #else -#define MAX_ECM_SIZE 596 -#define MAX_EMM_SIZE 512 +#define MAX_ECM_SIZE 1024 +#define MAX_EMM_SIZE 1024 #endif -#define CS_EMMCACHESIZE 512 //nr of EMMs that each reader will cache +#define CS_EMMCACHESIZE 1024 //nr of EMMs that each reader will cache #define MSGLOGSIZE 64 //size of string buffer for a ecm to return messages #define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine @@ -431,6 +431,7 @@ #define R_SMART 0x7 // Smartreader+ #define R_PCSC 0x8 // PCSC #define R_DRECAS 0x9 // Reader DRECAS +#define R_EMU 0x17 // Reader EMU /////////////////// proxy readers after R_CS378X #define R_CAMD35 0x20 // Reader cascading camd 3.5x #define R_CAMD33 0x21 // Reader cascading camd 3.3x @@ -858,6 +859,13 @@ uint32_t class; // the class needed for some systems time_t start; // startdate time_t end; // enddate +#ifdef WITH_EMU + bool isKey; + bool isData; + char name[8]; + uint8_t *key; + uint32_t keyLength; +#endif } S_ENTITLEMENT; struct s_client ; @@ -982,6 +990,7 @@ void (*post_process)(struct s_reader *); int32_t (*get_emm_type)(struct emm_packet_t *, struct s_reader *); int32_t (*get_emm_filter)(struct s_reader *, struct s_csystem_emm_filter **, unsigned int *); + int32_t (*get_emm_filter_adv)(struct s_reader *, struct s_csystem_emm_filter **, unsigned int *, uint16_t, uint32_t, uint16_t); int32_t (*get_tunemm_filter)(struct s_reader *, struct s_csystem_emm_filter **, unsigned int *); }; @@ -1714,6 +1723,14 @@ #ifdef MODULE_GHTTP uint8_t ghttp_use_ssl; #endif +#ifdef WITH_EMU + FTAB emu_auproviders; // AU providers for Emu reader + int8_t emu_datecodedenabled; // date-coded keys for BISS + char *extee36; // path to "ee36.bin" - Set by the user via the webif + char *extee56; // path to "ee56.bin" - Set by the user via the webif + uint8_t dre36_force_group; + uint8_t dre56_force_group; +#endif uint8_t cnxlastecm; // == 0 - las ecm has not been paired ecm, > 0 last ecm has been paired ecm LLIST *emmstat; //emm stats CS_MUTEX_LOCK emmstat_lock; @@ -2161,6 +2178,7 @@ int8_t dvbapi_read_sdt; int8_t dvbapi_write_sdt_prov; int8_t dvbapi_extended_cw_api; + int8_t dvbapi_extended_cw_pids; // pid limiter #endif #ifdef CS_ANTICASC @@ -2207,6 +2225,18 @@ IN_ADDR_T scam_srvip; 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; +#endif + int32_t max_cache_time; //seconds ecms are stored in ecmcwcache int32_t max_hitcache_time; //seconds hits are stored in cspec_hitcache (to detect dyn wait_time) @@ -2388,4 +2418,8 @@ static inline bool caid_is_dre(uint16_t caid) { return caid == 0x4AE0 || caid == 0x4AE1 || caid == 0x2710;} const char *get_cardsystem_desc_by_caid(uint16_t caid); +#ifdef WITH_EMU +FILTER* get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid); +#endif + #endif Index: module-dvbapi.c =================================================================== --- module-dvbapi.c (revision 11398) +++ module-dvbapi.c (working copy) @@ -1471,7 +1471,15 @@ } if(match) { - csystem = get_cardsystem_by_caid(caid); + if(rdr->typ == R_EMU) + { + csystem = rdr->csystem; + } + else + { + csystem = get_cardsystem_by_caid(caid); + } + if(csystem) { if(caid != ncaid) @@ -1490,7 +1498,14 @@ } else if (csystem->get_emm_filter) { - csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); + if(rdr->typ == R_EMU) + { + csystem->get_emm_filter_adv(rdr, &dmx_filter, &filter_count, caid, provid, demux[demux_index].program_number); + } + else + { + csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); + } } } else @@ -2076,6 +2091,8 @@ er->vpid = demux[demux_id].ECMpids[pid].VPID; er->pmtpid = demux[demux_id].pmtpid; er->onid = demux[demux_id].onid; + er->tsid = demux[demux_id].tsid; + er->ens = demux[demux_id].enigma_namespace; er->msgid = msgid; #ifdef WITH_STAPI5 @@ -2113,19 +2130,30 @@ if(caid_is_fake(demux[demux_id].ECMpids[pid].CAID) || caid_is_biss(demux[demux_id].ECMpids[pid].CAID)) { int32_t j, n; - er->ecmlen = 5; + er->ecmlen = 7; er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81 er->ecm[1] = 0x00; - er->ecm[2] = 0x02; + er->ecm[2] = 0x04; i2b_buf(2, er->srvid, er->ecm + 3); + i2b_buf(2, er->pmtpid, er->ecm + 5); - for(j = 0, n = 5; j < demux[demux_id].STREAMpidcount; j++, n += 2) + for(j = 0, n = 7; j < demux[demux_id].STREAMpidcount; j++, n += 2) { i2b_buf(2, demux[demux_id].STREAMpids[j], er->ecm + n); er->ecm[2] += 2; er->ecmlen += 2; } + er->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging + er->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid + + i2b_buf(2, er->tsid, er->ecm + 3 + er->ecm[2]); // place tsid after the last stream pid + i2b_buf(2, er->onid, er->ecm + 3 + er->ecm[2] + 2); // place onid right after tsid + i2b_buf(4, er->ens, er->ecm + 3 + er->ecm[2] + 4); // place namespace at the end of the ecm + + er->ecm[2] += 8; + er->ecmlen += 8; + cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X", demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].PROVID, demux[demux_id].ECMpids[pid].ECM_PID, demux[demux_id].pmtpid, demux[demux_id].ECMpids[pid].VPID); @@ -3192,6 +3220,7 @@ { uint32_t i = 0, start_descrambling = 0; int32_t j = 0; + int32_t max_pids = 64; int32_t demux_id = -1; uint16_t demux_index, adapter_index, pmtpid; uint32_t ca_mask; @@ -3354,6 +3383,12 @@ uint32_t es_info_length = 0, vpid = 0; struct s_dvbapi_priority *addentry; + // pid limiter for PowerVu + if(demux[demux_id].ECMpids[0].CAID >> 8 == 0x0E) + { + max_pids = cfg.dvbapi_extended_cw_pids; + } + for(i = program_info_length + program_info_start; i + 4 < length; i += es_info_length + 5) { uint8_t stream_type = buffer[i]; @@ -3361,7 +3396,7 @@ uint8_t is_audio = 0; es_info_length = b2i(2, buffer + i +3)&0x0FFF; - if(demux[demux_id].STREAMpidcount < ECM_PIDS) + if(demux[demux_id].STREAMpidcount < max_pids) // was "ECM_PIDS" (pid limiter) { demux[demux_id].STREAMpids[demux[demux_id].STREAMpidcount] = elementary_pid; @@ -4375,6 +4410,7 @@ if(filtertype == TYPE_ECM) { uint32_t chid = 0x10000; + int8_t pvu_skip = 0; ECM_REQUEST *er; if(len != 0) // len = 0 receiver encountered an internal bufferoverflow! @@ -4401,9 +4437,24 @@ return; } - if(curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) // wait for odd / even ecm change (only not for irdeto!) + if(curpid->CAID >> 8 == 0x0E) { - + pvu_skip = 1; + + if(sctlen - 11 > buffer[9]) + { + if(buffer[11 + buffer[9]] > curpid->pvu_counter || (curpid->pvu_counter == 255 && buffer[11 + buffer[9]] == 0) + || ((curpid->pvu_counter - buffer[11 + buffer[9]]) > 5)) + { + curpid->pvu_counter = buffer[11 + buffer[9]]; + pvu_skip = 0; + } + } + } + + if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip) // wait for odd / even ecm change (only not for irdeto!) + { + if(!(er = get_ecmtask())) { return; @@ -4685,6 +4736,40 @@ dvbapi_stop_filternum(demux_id, filter_num, msgid); return; } + +#ifdef WITH_EMU + if((demux[demux_id].demux_fd[filter_num].caid>>8) == 0x10) + { + uint32_t i; + uint32_t emmhash; + + if(sctlen < 4) + { + return; + } + + for(i=0; i+2caid>>8 != 0x0E || !cfg.emu_stream_relay_enabled) +#endif switch(selected_api) { #if defined(WITH_STAPI) || defined(WITH_STAPI5) Index: module-dvbapi.h =================================================================== --- module-dvbapi.h (revision 11398) +++ module-dvbapi.h (working copy) @@ -136,6 +136,7 @@ int8_t useMultipleIndices; uint32_t streams; uint32_t cadata; + int16_t pvu_counter; }; typedef struct filter_s @@ -158,6 +159,9 @@ uint32_t SlotHandle[10]; uint32_t BufferHandle[10]; #endif +#ifdef WITH_EMU + uint32_t cadata; +#endif } FILTERTYPE; struct s_emmpids Index: module-emulator-osemu.c =================================================================== --- module-emulator-osemu.c (revision 0) +++ module-emulator-osemu.c (working copy) @@ -0,0 +1,6461 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" +#include "ffdecsa/ffdecsa.h" +#include "cscrypt/bn.h" +#include "cscrypt/des.h" +#include "cscrypt/idea.h" +#include "cscrypt/md5.h" + +#ifdef WITH_EMU +#include "oscam-aes.h" +#include "oscam-string.h" +#include "oscam-config.h" +#include "oscam-conf-chk.h" +#include "oscam-time.h" +#include "module-newcamd-des.h" +#include "reader-dre-common.h" +// from reader-viaccess.c: +void hdSurEncPhase1_D2_0F_11(uint8_t *CWs); +void hdSurEncPhase2_D2_0F_11(uint8_t *CWs); +void hdSurEncPhase1_D2_13_15(uint8_t *cws); +void hdSurEncPhase2_D2_13_15(uint8_t *cws); +#else +#include "cscrypt/viades.h" +#include "via3surenc.h" +#include "dre2overcrypt.h" +#endif + +#include "module-emulator-osemu.h" +#include "module-emulator-stream.h" + +// Version info +uint32_t GetOSemuVersion(void) +{ + return atoi("$Version: 766 $"+10); +} + +/* + * Key DB + * + * The Emu reader gets keys from the OSCcam-Emu binary and the "SoftCam.Key" file. + * + * The keys are stored in structures of type "KeyDataContainer", one per CAS. Each + * container points to a dynamically allocated array of type "KeyData", which holds + * the actual keys. The array initially holds up to 64 keys (64 * KeyData), and it + * is expanded by 16 every time it's filled with keys. The "KeyDataContainer" also + * includes info about the number of keys it contains ("KeyCount") and the maximum + * number of keys it can store ("KeyMax"). + * + * The "KeyData" structure on the other hand, stores the actual key information, + * including the "identifier", "provider", "keyName", "key" and "keyLength". There + * is also a "nextKey" pointer to a similar "KeyData" structure which is only used + * for Irdeto multiple keys, in a linked list style structure. For all other CAS, + * the "nextKey" is a "NULL" pointer. + * + * For storing keys, the "SetKey" function is used. Duplicate keys are not allowed. + * When storing a key that is already present in the database, its "key" value is + * updated with the new one. For reading keys from the database, the "FindKey" + * function is used. To delete all keys in a container, the "DeleteKeysInContainer" + * function can be called. +*/ + +static char *emu_keyfile_path = NULL; + +void set_emu_keyfile_path(const char *path) +{ + if(emu_keyfile_path != NULL) { + free(emu_keyfile_path); + } + emu_keyfile_path = (char*)malloc(strlen(path)+1); + if(emu_keyfile_path == NULL) { + return; + } + memcpy(emu_keyfile_path, path, strlen(path)); + emu_keyfile_path[strlen(path)] = 0; +} + +int32_t CharToBin(uint8_t *out, const char *in, uint32_t inLen) +{ + uint32_t i, tmp; + for(i=0; i= fileNameLen && strcasecmp(path+pathLength-fileNameLen, EMU_KEY_FILENAME) == 0) { + // cut file name + path[pathLength-fileNameLen] = '\0'; + } + + pathLength = strlen(path); + if(path[pathLength-1] == '/' || path[pathLength-1] == '\\') { + // cut trailing / + path[pathLength-1] = '\0'; + } + + pDir = opendir(path); + if (pDir == NULL) { + cs_log("Cannot open key file path: %s", path); + free(path); + return; + } + + while((pDirent = readdir(pDir)) != NULL) { + if(strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0) { + strncpy(filename, pDirent->d_name, sizeof(filename)); + break; + } + } + closedir(pDir); + + if(pDirent == NULL) { + strncpy(filename, EMU_KEY_FILENAME, sizeof(filename)); + } + + pathLength = strlen(path)+1+strlen(filename)+1; + filepath = (char*)malloc(pathLength); + if(filepath == NULL) { + free(path); + return; + } + snprintf(filepath, pathLength, "%s/%s", path, filename); + free(path); + + cs_log("Writing key file: %s", filepath); + + file = fopen(filepath, "a"); + free(filepath); + if(file == NULL) { + return; + } + + Date2Str(dateText, sizeof(dateText), 0, 1); + + keyValue = (char*)malloc((keyLength*2)+1); + if(keyValue == NULL) { + fclose(file); + return; + } + cs_hexdump(0, key, keyLength, keyValue, (keyLength*2)+1); + + if(comment) + { + snprintf(line, sizeof(line), "\n%c %.4X %s %s ; added by OSEmu %s %s\n", identifier, provider, keyName, keyValue, dateText, comment); + } + else + { + snprintf(line, sizeof(line), "\n%c %.4X %s %s ; added by OSEmu %s\n", identifier, provider, keyName, keyValue, dateText); + } + + cs_log("Key written: %c %.4X %s %s", identifier, provider, keyName, keyValue); + + free(keyValue); + + fwrite(line, strlen(line), 1, file); + fclose(file); +} + +int32_t SetKey(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength, + uint8_t writeKey, char *comment, struct s_reader *rdr) +{ + uint32_t i, j; + uint8_t *tmpKey = NULL; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData, *newKeyData; + identifier = (char)toupper((int)identifier); + + KeyDB = GetKeyContainer(identifier); + if(KeyDB == NULL) { + return 0; + } + + keyName = strtoupper(keyName); + + if (identifier == 'F') // Prepare BISS keys before saving to the db + { + // Convert legacy BISS "00" & "01" keynames + if (0 == strcmp(keyName, "00") || 0 == strcmp(keyName, "01")) + { + keyName = "00000000"; + } + + // All keyNames should have a length of 8 after converting + if (strlen(keyName) != 8) + { + cs_log("WARNING: Wrong key format in %s: F %08X %s", EMU_KEY_FILENAME, provider, keyName); + return 0; + } + + // Verify date-coded keyName (if enabled), ignoring old (expired) keys + if (rdr->emu_datecodedenabled) + { + char timeStr[9]; + Date2Str(timeStr, sizeof(timeStr), 0, 3); + + // Reject old date-coded keys, but allow our "00000000" evergreen label + if (strcmp("00000000", keyName) != 0 && strcmp(timeStr, keyName) >= 0) + { + return 0; + } + } + } + + // fix checksum for BISS keys with a length of 6 + if (identifier == 'F' && keyLength == 6) + { + tmpKey = (uint8_t*)malloc(8*sizeof(uint8_t)); + if(tmpKey == NULL) { + return 0; + } + + tmpKey[0] = orgKey[0]; + tmpKey[1] = orgKey[1]; + tmpKey[2] = orgKey[2]; + tmpKey[3] = ((orgKey[0] + orgKey[1] + orgKey[2]) & 0xff); + tmpKey[4] = orgKey[3]; + tmpKey[5] = orgKey[4]; + tmpKey[6] = orgKey[5]; + tmpKey[7] = ((orgKey[3] + orgKey[4] + orgKey[5]) & 0xff); + + keyLength = 8; + } + else // All keys with a length of 8, including BISS + { + tmpKey = (uint8_t*)malloc(keyLength*sizeof(uint8_t)); + if(tmpKey == NULL) { + return 0; + } + + memcpy(tmpKey, orgKey, keyLength); + } + + // fix patched mgcamd format for Irdeto + if(identifier == 'I' && provider < 0xFFFF) { + provider = provider<<8; + } + + // key already exists on db, update its value + for(i=0; ikeyCount; i++) { + + if(KeyDB->EmuKeys[i].provider != provider) { + continue; + } + + // Don't match keyName (i.e. expiration date) for BISS + if(identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName)) { + continue; + } + + // allow multiple keys for Irdeto + if(identifier == 'I') + { + // reject duplicates + tmpKeyData = &KeyDB->EmuKeys[i]; + do { + if(memcmp(tmpKeyData->key, tmpKey, tmpKeyData->keyLength < keyLength ? tmpKeyData->keyLength : keyLength) == 0) { + free(tmpKey); + return 0; + } + tmpKeyData = tmpKeyData->nextKey; + } + while(tmpKeyData != NULL); + + // add new key + newKeyData = (KeyData*)malloc(sizeof(KeyData)); + if(newKeyData == NULL) { + free(tmpKey); + return 0; + } + newKeyData->identifier = identifier; + newKeyData->provider = provider; + if(strlen(keyName) < EMU_MAX_CHAR_KEYNAME) { + strncpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + else { + memcpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + newKeyData->keyName[EMU_MAX_CHAR_KEYNAME-1] = 0; + newKeyData->key = tmpKey; + newKeyData->keyLength = keyLength; + newKeyData->nextKey = NULL; + + tmpKeyData = &KeyDB->EmuKeys[i]; + j = 0; + while(tmpKeyData->nextKey != NULL) { + if(j == 0xFE) + { + break; + } + tmpKeyData = tmpKeyData->nextKey; + j++; + } + if(tmpKeyData->nextKey) + { + NULLFREE(tmpKeyData->nextKey->key); + NULLFREE(tmpKeyData->nextKey); + } + tmpKeyData->nextKey = newKeyData; + + if(writeKey) { + WriteKeyToFile(identifier, provider, keyName, tmpKey, keyLength, comment); + } + } + else // identifier != 'I' + { + free(KeyDB->EmuKeys[i].key); + KeyDB->EmuKeys[i].key = tmpKey; + KeyDB->EmuKeys[i].keyLength = keyLength; + + if (identifier == 'F') // Update keyName (i.e. expiration date) for BISS + { + strncpy(KeyDB->EmuKeys[i].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + if(writeKey) { + WriteKeyToFile(identifier, provider, keyName, tmpKey, keyLength, comment); + } + } + + return 1; + } + + // key does not exist on db + if(KeyDB->keyCount+1 > KeyDB->keyMax) + { + if(KeyDB->EmuKeys == NULL) // db is empty + { + KeyDB->EmuKeys = (KeyData*)malloc(sizeof(KeyData)*(KeyDB->keyMax+64)); + if(KeyDB->EmuKeys == NULL) { + free(tmpKey); + return 0; + } + KeyDB->keyMax+=64; + } + else // db is full, expand it + { + tmpKeyData = (KeyData*)realloc(KeyDB->EmuKeys, sizeof(KeyData)*(KeyDB->keyMax+16)); + if(tmpKeyData == NULL) { + free(tmpKey); + return 0; + } + KeyDB->EmuKeys = tmpKeyData; + KeyDB->keyMax+=16; + } + } + + KeyDB->EmuKeys[KeyDB->keyCount].identifier = identifier; + KeyDB->EmuKeys[KeyDB->keyCount].provider = provider; + if(strlen(keyName) < EMU_MAX_CHAR_KEYNAME) { + strncpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + else { + memcpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + KeyDB->EmuKeys[KeyDB->keyCount].keyName[EMU_MAX_CHAR_KEYNAME-1] = 0; + KeyDB->EmuKeys[KeyDB->keyCount].key = tmpKey; + KeyDB->EmuKeys[KeyDB->keyCount].keyLength = keyLength; + KeyDB->EmuKeys[KeyDB->keyCount].nextKey = NULL; + KeyDB->keyCount++; + + if(writeKey) { + WriteKeyToFile(identifier, provider, keyName, tmpKey, keyLength, comment); + } + + return 1; +} + +int32_t FindKey(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, uint8_t *key, + uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef, uint8_t matchLength, uint32_t *getProvider) +{ + uint32_t i; + uint16_t j; + uint8_t provider_matching_key_count = 0; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData; + + KeyDB = GetKeyContainer(identifier); + if(KeyDB == NULL) { + return 0; + } + + for(i=0; ikeyCount; i++) { + + if((KeyDB->EmuKeys[i].provider & ~providerIgnoreMask) != provider) { + continue; + } + + // Don't match keyName (i.e. expiration date) for BISS + if(identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName)) { + continue; + } + + //matchLength cannot be used when multiple keys are allowed + //for a single provider/keyName combination. + //Currently this is only the case for Irdeto keys. + if(matchLength && KeyDB->EmuKeys[i].keyLength != maxKeyLength) { + continue; + } + + if(providerIgnoreMask) { + if(provider_matching_key_count < keyRef) { + provider_matching_key_count++; + continue; + } + else { + keyRef = 0; + } + } + + tmpKeyData = &KeyDB->EmuKeys[i]; + + j = 0; + while(jnextKey != NULL) { + j++; + tmpKeyData = tmpKeyData->nextKey; + } + + if(j == keyRef) { + memcpy(key, tmpKeyData->key, tmpKeyData->keyLength > maxKeyLength ? maxKeyLength : tmpKeyData->keyLength); + if(tmpKeyData->keyLength < maxKeyLength) { + memset(key+tmpKeyData->keyLength, 0, maxKeyLength - tmpKeyData->keyLength); + } + + if (identifier == 'F') // Report the keyName of found key back to BissGetKey() + { + strncpy(keyName, tmpKeyData->keyName, EMU_MAX_CHAR_KEYNAME); + } + + if(getProvider != NULL) { + (*getProvider) = tmpKeyData->provider; + } + return 1; + } + else { + break; + } + } + + if (isCriticalKey) + { + cs_log("Key not found: %c %X %s", identifier, provider, keyName); + } + + return 0; +} + +static int32_t UpdateKey(char identifier, uint32_t provider, char *keyName, uint8_t *key, uint32_t keyLength, uint8_t writeKey, char *comment) +{ + uint32_t keyRef = 0; + uint8_t *tmpKey = (uint8_t*)malloc(sizeof(uint8_t)*keyLength); + if(tmpKey == NULL) + { + return 0; + } + + while(FindKey(identifier, provider, 0, keyName, tmpKey, keyLength, 0, keyRef, 0, NULL)) + { + if(memcmp(tmpKey, key, keyLength) == 0) + { + free(tmpKey); + return 0; + } + + keyRef++; + } + + free(tmpKey); + return SetKey(identifier, provider, keyName, key, keyLength, writeKey, comment, NULL); +} + +static int32_t UpdateKeysByProviderMask(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, uint8_t *key, + uint32_t keyLength, char *comment) +{ + int32_t ret = 0; + uint32_t foundProvider = 0; + uint32_t keyRef = 0; + uint8_t *tmpKey = (uint8_t*)malloc(sizeof(uint8_t)*keyLength); + if(tmpKey == NULL) + { + return 0; + } + + while(FindKey(identifier, (provider & ~providerIgnoreMask), providerIgnoreMask, keyName, tmpKey, keyLength, 0, keyRef, 0, &foundProvider)) + { + keyRef++; + + if(memcmp(tmpKey, key, keyLength) == 0) + { + continue; + } + + if(SetKey(identifier, foundProvider, keyName, key, keyLength, 1, comment, NULL)) + { + ret = 1; + } + } + + free(tmpKey); + return ret; +} + +int32_t DeleteKeysInContainer(char identifier) +{ + // Deletes all keys stored in memory for the specified identifier, + // but keeps the container itself, re-initialized at { NULL, 0, 0 }. + // Returns the count of deleted keys. + + uint32_t oldKeyCount, i; + KeyData *tmpKeyData; + KeyDataContainer *KeyDB = GetKeyContainer(identifier); + + if (KeyDB == NULL || KeyDB->EmuKeys == NULL || KeyDB->keyCount == 0) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + // For Irdeto multiple keys only (linked list structure) + while (KeyDB->EmuKeys[i].nextKey != NULL) + { + tmpKeyData = KeyDB->EmuKeys[i].nextKey; + KeyDB->EmuKeys[i].nextKey = KeyDB->EmuKeys[i].nextKey->nextKey; + free(tmpKeyData->key); // Free key + free(tmpKeyData); // Free KeyData + } + + // For single keys (all identifiers, including Irdeto) + free(KeyDB->EmuKeys[i].key); // Free key + } + + // Free the KeyData array + NULLFREE(KeyDB->EmuKeys); + oldKeyCount = KeyDB->keyCount; + KeyDB->keyCount = 0; + KeyDB->keyMax = 0; + + return oldKeyCount; +} + +void clear_emu_keydata(void) +{ + uint32_t total = 0; + + total = CwKeys.keyCount; + total += ViKeys.keyCount; + total += NagraKeys.keyCount; + total += IrdetoKeys.keyCount; + total += NDSKeys.keyCount; + total += BissKeys.keyCount; + total += PowervuKeys.keyCount; + total += DreKeys.keyCount; + total += TandbergKeys.keyCount; + + if (total != 0) + { + cs_log("Freeing keys in memory: W:%d V:%d N:%d I:%d S:%d F:%d P:%d D:%d T:%d", \ + CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, \ + IrdetoKeys.keyCount, NDSKeys.keyCount, BissKeys.keyCount, \ + PowervuKeys.keyCount, DreKeys.keyCount, TandbergKeys.keyCount); + + DeleteKeysInContainer('W'); + DeleteKeysInContainer('V'); + DeleteKeysInContainer('N'); + DeleteKeysInContainer('I'); + DeleteKeysInContainer('S'); + DeleteKeysInContainer('F'); + DeleteKeysInContainer('P'); + DeleteKeysInContainer('D'); + DeleteKeysInContainer('T'); + } +} + +uint8_t read_emu_keyfile(struct s_reader *rdr, const char *opath) +{ + char line[1200], keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026]; + uint32_t pathLength, provider, keyLength; + uint8_t *key; + struct dirent *pDirent; + DIR *pDir; + char *path, *filepath, filename[EMU_KEY_FILENAME_MAX_LEN+1]; + FILE *file = NULL; + char identifier; + uint8_t fileNameLen = strlen(EMU_KEY_FILENAME); + + pathLength = strlen(opath); + path = (char*)malloc(pathLength+1); + if(path == NULL) { + return 0; + } + strncpy(path, opath, pathLength+1); + + pathLength = strlen(path); + if(pathLength >= fileNameLen && strcasecmp(path+pathLength-fileNameLen, EMU_KEY_FILENAME) == 0) { + // cut file name + path[pathLength-fileNameLen] = '\0'; + } + + pathLength = strlen(path); + if(path[pathLength-1] == '/' || path[pathLength-1] == '\\') { + // cut trailing / + path[pathLength-1] = '\0'; + } + + pDir = opendir(path); + if (pDir == NULL) { + cs_log("Cannot open key file path: %s", path); + free(path); + return 0; + } + + while((pDirent = readdir(pDir)) != NULL) { + if(strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0) { + strncpy(filename, pDirent->d_name, sizeof(filename)); + break; + } + } + closedir(pDir); + + if(pDirent == NULL) { + cs_log("Key file not found in: %s", path); + free(path); + return 0; + } + + pathLength = strlen(path)+1+strlen(filename)+1; + filepath = (char*)malloc(pathLength); + if(filepath == NULL) { + free(path); + return 0; + } + snprintf(filepath, pathLength, "%s/%s", path, filename); + free(path); + + cs_log("Reading key file: %s", filepath); + + file = fopen(filepath, "r"); + free(filepath); + if(file == NULL) { + return 0; + } + + set_emu_keyfile_path(opath); + + while(fgets(line, 1200, file)) { + if(sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4) { + continue; + } + + keyLength = strlen(keyString)/2; + key = (uint8_t*)malloc(keyLength); + if(key == NULL) { + fclose(file); + return 0; + } + + if (CharToBin(key, keyString, strlen(keyString))) // Conversion OK + { + SetKey(identifier, provider, keyName, key, keyLength, 0, NULL, rdr); + } + else // Non-hex characters in keyString + { + if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc. + identifier != '=' && identifier != '-' && + identifier != ' ') && + !(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines + { + // Alert user regarding faulty line + cs_log("WARNING: non-hex value in %s at %c %04X %s %s", EMU_KEY_FILENAME, identifier, provider, keyName, keyString); + } + } + free(key); + } + fclose(file); + + return 1; +} + +#if !defined(__APPLE__) && !defined(__ANDROID__) +extern uint8_t SoftCamKey_Data[] __asm__("_binary_SoftCam_Key_start"); +extern uint8_t SoftCamKey_DataEnd[] __asm__("_binary_SoftCam_Key_end"); + +void read_emu_keymemory(struct s_reader *rdr) +{ + char *keyData, *line, *saveptr, keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026]; + uint32_t provider, keyLength; + uint8_t *key; + char identifier; + + keyData = (char*)malloc(SoftCamKey_DataEnd-SoftCamKey_Data+1); + if(keyData == NULL) { + return; + } + memcpy(keyData, SoftCamKey_Data, SoftCamKey_DataEnd-SoftCamKey_Data); + keyData[SoftCamKey_DataEnd-SoftCamKey_Data] = 0x00; + + line = strtok_r(keyData, "\n", &saveptr); + while(line != NULL) { + if(sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4) { + line = strtok_r(NULL, "\n", &saveptr); + continue; + } + keyLength = strlen(keyString)/2; + key = (uint8_t*)malloc(keyLength); + if(key == NULL) { + free(keyData); + return; + } + + if (CharToBin(key, keyString, strlen(keyString))) // Conversion OK + { + SetKey(identifier, provider, keyName, key, keyLength, 0, NULL, rdr); + } + else // Non-hex characters in keyString + { + if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc. + identifier != '=' && identifier != '-' && + identifier != ' ') && + !(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines + { + // Alert user regarding faulty line + cs_log("WARNING: non-hex value in internal keyfile at %c %04X %s %s", identifier, provider, keyName, keyString); + } + } + free(key); + line = strtok_r(NULL, "\n", &saveptr); + } + free(keyData); +} +#endif + +void read_emu_eebin(const char *path, const char *name) +{ + char tmp[256]; + FILE *file = NULL; + uint8_t i, buffer[64][32], dummy[2][32]; + uint32_t prvid; + + // Set path + if (path != NULL) + { + snprintf(tmp, 256, "%s%s", path, name); + } + else // No path set, use SoftCam.Keys's path + { + snprintf(tmp, 256, "%s%s", emu_keyfile_path, name); + } + + // Read file to buffer + if ((file = fopen(tmp, "rb")) != NULL) + { + cs_log("Reading key file: %s", tmp); + + if (fread(buffer, 1, sizeof(buffer), file) != sizeof(buffer)) + { + cs_log("Corrupt key file: %s", tmp); + fclose(file); + return; + } + + fclose(file); + } + else + { + if (path != NULL) + { + cs_log("Cannot open key file: %s", tmp); + } + + return; + } + + // Save keys to db + memset(dummy[0], 0x00, 32); + memset(dummy[1], 0xFF, 32); + prvid = (strncmp(name, "ee36.bin", 9) == 0) ? 0x4AE111 : 0x4AE114; + + for (i = 0; i < 32; i++) // Set "3B" type keys + { + // Write keys if they have "real" values + if ((memcmp(buffer[i], dummy[0], 32) !=0) && (memcmp(buffer[i], dummy[1], 32) != 0)) + { + snprintf(tmp, 5, "3B%02X", i); + SetKey('D', prvid, tmp, buffer[i], 32, 0, NULL, NULL); + } + } + + for (i = 0; i < 32; i++) // Set "56" type keys + { + // Write keys if they have "real" values + if ((memcmp(buffer[32 + i], dummy[0], 32) !=0) && (memcmp(buffer[32 + i], dummy[1], 32) != 0)) + { + snprintf(tmp, 5, "56%02X", i); + SetKey('D', prvid, tmp, buffer[32 + i], 32, 0, NULL, NULL); + } + } +} + +void read_emu_deskey(uint8_t *dreOverKey, uint8_t len) +{ + uint8_t i; + + if (len == 128) + { + cs_log("Reading DreCrypt overcrypt (ADEC) key"); + + for (i = 0; i < 16; i++) + { + SetKey('D', i, "OVER", dreOverKey + (i * 8), 8, 0, NULL, NULL); + } + } + else if ((len != 0 && len < 128) || len > 128) + { + cs_log("DreCrypt overcrypt (ADEC) key has wrong length"); + } +} + +// Shared functions + +static inline uint16_t GetEcmLen(const uint8_t *ecm) +{ + return (((ecm[1] & 0x0f)<< 8) | ecm[2]) +3; +} + +static void ReverseMem(uint8_t *in, int32_t len) +{ + uint8_t temp; + int32_t i; + for(i = 0; i < (len / 2); i++) { + temp = in[i]; + in[i] = in[len - i - 1]; + in[len - i - 1] = temp; + } +} + +static void ReverseMemInOut(uint8_t *out, const uint8_t *in, int32_t n) +{ + if(n>0) { + out+=n; + do { + *(--out)=*(in++); + } + while(--n); + } +} + +static int8_t EmuRSAInput(BIGNUM *d, const uint8_t *in, int32_t n, int8_t le) +{ + int8_t result = 0; + + if(le) { + uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*n); + if(tmp == NULL) { + return 0; + } + ReverseMemInOut(tmp,in,n); + result = BN_bin2bn(tmp,n,d)!=0; + free(tmp); + } + else { + result = BN_bin2bn(in,n,d)!=0; + } + return result; +} + +static int32_t EmuRSAOutput(uint8_t *out, int32_t n, BIGNUM *r, int8_t le) +{ + int32_t s = BN_num_bytes(r); + if(s>n) { + uint8_t *buff = (uint8_t *)malloc(sizeof(uint8_t)*s); + if(buff == NULL) { + return 0; + } + BN_bn2bin(r,buff); + memcpy(out,buff+s-n,n); + free(buff); + } + else if(s> 1) & 0xFE)); + key[2] = ((tmpKey[1] << 6) | ((tmpKey[2] >> 2) & 0xFE)); + key[3] = ((tmpKey[2] << 5) | ((tmpKey[3] >> 3) & 0xFE)); + key[4] = ((tmpKey[3] << 4) | ((tmpKey[4] >> 4) & 0xFE)); + key[5] = ((tmpKey[4] << 3) | ((tmpKey[5] >> 5) & 0xFE)); + key[6] = ((tmpKey[5] << 2) | ((tmpKey[6] >> 6) & 0xFE)); + key[7] = (tmpKey[6] << 1); + + for (i = 0; i < 8; i++) + { + parity = 1; + for (j = 1; j < 8; j++) if ((key[i] >> j) & 0x1) { parity = ~parity & 0x01; } + key[i] |= parity; + } +} + +// Cryptoworks EMU +static int8_t GetCwKey(uint8_t *buf,uint32_t ident, uint8_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey) +{ + + char keyName[EMU_MAX_CHAR_KEYNAME]; + uint32_t tmp; + + if((ident >> 4) == 0xD02A) { + keyIndex &=0xFE; // map to even number key indexes + } + if((ident >> 4) == 0xD00C) { + ident = 0x0D00C0; // map provider C? to C0 + } + else if(keyIndex == 6 && ((ident >> 8) == 0x0D05)) { + ident = 0x0D0504; // always use provider 04 system key + } + + tmp = keyIndex; + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.2X", tmp); + if(FindKey('W', ident, 0, keyName, buf, keyLength, isCriticalKey, 0, 0, NULL)) { + return 1; + } + + return 0; +} + +static const uint8_t cw_sbox1[64] = { + 0xD8,0xD7,0x83,0x3D,0x1C,0x8A,0xF0,0xCF,0x72,0x4C,0x4D,0xF2,0xED,0x33,0x16,0xE0, + 0x8F,0x28,0x7C,0x82,0x62,0x37,0xAF,0x59,0xB7,0xE0,0x00,0x3F,0x09,0x4D,0xF3,0x94, + 0x16,0xA5,0x58,0x83,0xF2,0x4F,0x67,0x30,0x49,0x72,0xBF,0xCD,0xBE,0x98,0x81,0x7F, + 0xA5,0xDA,0xA7,0x7F,0x89,0xC8,0x78,0xA7,0x8C,0x05,0x72,0x84,0x52,0x72,0x4D,0x38 +}; +static const uint8_t cw_sbox2[64] = { + 0xD8,0x35,0x06,0xAB,0xEC,0x40,0x79,0x34,0x17,0xFE,0xEA,0x47,0xA3,0x8F,0xD5,0x48, + 0x0A,0xBC,0xD5,0x40,0x23,0xD7,0x9F,0xBB,0x7C,0x81,0xA1,0x7A,0x14,0x69,0x6A,0x96, + 0x47,0xDA,0x7B,0xE8,0xA1,0xBF,0x98,0x46,0xB8,0x41,0x45,0x9E,0x5E,0x20,0xB2,0x35, + 0xE4,0x2F,0x9A,0xB5,0xDE,0x01,0x65,0xF8,0x0F,0xB2,0xD2,0x45,0x21,0x4E,0x2D,0xDB +}; +static const uint8_t cw_sbox3[64] = { + 0xDB,0x59,0xF4,0xEA,0x95,0x8E,0x25,0xD5,0x26,0xF2,0xDA,0x1A,0x4B,0xA8,0x08,0x25, + 0x46,0x16,0x6B,0xBF,0xAB,0xE0,0xD4,0x1B,0x89,0x05,0x34,0xE5,0x74,0x7B,0xBB,0x44, + 0xA9,0xC6,0x18,0xBD,0xE6,0x01,0x69,0x5A,0x99,0xE0,0x87,0x61,0x56,0x35,0x76,0x8E, + 0xF7,0xE8,0x84,0x13,0x04,0x7B,0x9B,0xA6,0x7A,0x1F,0x6B,0x5C,0xA9,0x86,0x54,0xF9 +}; +static const uint8_t cw_sbox4[64] = { + 0xBC,0xC1,0x41,0xFE,0x42,0xFB,0x3F,0x10,0xB5,0x1C,0xA6,0xC9,0xCF,0x26,0xD1,0x3F, + 0x02,0x3D,0x19,0x20,0xC1,0xA8,0xBC,0xCF,0x7E,0x92,0x4B,0x67,0xBC,0x47,0x62,0xD0, + 0x60,0x9A,0x9E,0x45,0x79,0x21,0x89,0xA9,0xC3,0x64,0x74,0x9A,0xBC,0xDB,0x43,0x66, + 0xDF,0xE3,0x21,0xBE,0x1E,0x16,0x73,0x5D,0xA2,0xCD,0x8C,0x30,0x67,0x34,0x9C,0xCB +}; +static const uint8_t AND_bit1[8] = {0x00,0x40,0x04,0x80,0x21,0x10,0x02,0x08}; +static const uint8_t AND_bit2[8] = {0x80,0x08,0x01,0x40,0x04,0x20,0x10,0x02}; +static const uint8_t AND_bit3[8] = {0x82,0x40,0x01,0x10,0x00,0x20,0x04,0x08}; +static const uint8_t AND_bit4[8] = {0x02,0x10,0x04,0x40,0x80,0x08,0x01,0x20}; + +static void CW_SWAP_KEY(uint8_t *key) +{ + uint8_t k[8]; + memcpy(k, key, 8); + memcpy(key, key + 8, 8); + memcpy(key + 8, k, 8); +} + +static void CW_SWAP_DATA(uint8_t *k) +{ + uint8_t d[4]; + memcpy(d, k + 4, 4); + memcpy(k + 4 ,k ,4); + memcpy(k, d, 4); +} + +static void CW_DES_ROUND(uint8_t *d, uint8_t *k) +{ + uint8_t aa[44] = {1,0,3,1,2,2,3,2,1,3,1,1,3,0,1,2,3,1,3,2,2,0,7,6,5,4,7,6,5,7,6,5,6,7,5,7,5,7,6,6,7,5,4,4}; + uint8_t bb[44] = {0x80,0x08,0x10,0x02,0x08,0x40,0x01,0x20,0x40,0x80,0x04,0x10,0x04,0x01,0x01,0x02,0x20,0x20,0x02,0x01, + 0x80,0x04,0x02,0x02,0x08,0x02,0x10,0x80,0x01,0x20,0x08,0x80,0x01,0x08,0x40,0x01,0x02,0x80,0x10,0x40,0x40,0x10,0x08,0x01 + }; + uint8_t ff[4] = {0x02,0x10,0x04,0x04}; + uint8_t l[24] = {0,2,4,6,7,5,3,1,4,5,6,7,7,6,5,4,7,4,5,6,4,7,6,5}; + + uint8_t des_td[8], i, o, n, c = 1, m = 0, r = 0, *a = aa, *b = bb, *f = ff, *p1 = l, *p2 = l+8, *p3 = l+16; + + for (m = 0; m < 2; m++) { + for(i = 0; i < 4; i++) { + des_td[*p1++] = + (m) ? ((d[*p2++]*2) & 0x3F) | ((d[*p3++] & 0x80) ? 0x01 : 0x00): (d[*p2++]/2) | ((d[*p3++] & 0x01) ? 0x80 : 0x00); + } + } + + for (i = 0; i < 8; i++) { + c = (c) ? 0 : 1; + r = (c) ? 6 : 7; + n = (i) ? i-1 : 1; + o = (c) ? ((k[n] & *f++) ? 1 : 0) : des_td[n]; + for (m = 1; m < r; m++) { + o = (c) ? (o*2) | ((k[*a++] & *b++) ? 0x01 : 0x00) : (o/2) | ((k[*a++] & *b++) ? 0x80 : 0x00); + } + n = (i) ? n+1 : 0; + des_td[n] = (c) ? des_td[n] ^ o : (o ^ des_td[n] )/4; + } + + for( i = 0; i < 8; i++) { + d[0] ^= (AND_bit1[i] & cw_sbox1[des_td[i]]); + d[1] ^= (AND_bit2[i] & cw_sbox2[des_td[i]]); + d[2] ^= (AND_bit3[i] & cw_sbox3[des_td[i]]); + d[3] ^= (AND_bit4[i] & cw_sbox4[des_td[i]]); + } + + CW_SWAP_DATA(d); +} + +static void CW_48_Key(uint8_t *inkey, uint8_t *outkey, uint8_t algotype) +{ + uint8_t Round_Counter, i = 8, *key128 = inkey, *key48 = inkey + 0x10; + Round_Counter = 7 - (algotype & 7); + + memset(outkey, 0, 16); + memcpy(outkey, key48, 6); + + for( ; i > Round_Counter; i--) { + if (i > 1) { + outkey[i-2] = key128[i]; + } + } +} + +static void CW_LS_DES_KEY(uint8_t *key,uint8_t Rotate_Counter) +{ + uint8_t round[] = {1,2,2,2,2,2,2,1,2,2,2,2,2,2,1,1}; + uint8_t i, n; + uint16_t k[8]; + + n = round[Rotate_Counter]; + + for (i = 0; i < 8; i++) { + k[i] = key[i]; + } + + for (i = 1; i < n + 1; i++) { + k[7] = (k[7]*2) | ((k[4] & 0x008) ? 1 : 0); + k[6] = (k[6]*2) | ((k[7] & 0xF00) ? 1 : 0); + k[7] &=0xff; + k[5] = (k[5]*2) | ((k[6] & 0xF00) ? 1 : 0); + k[6] &=0xff; + k[4] = ((k[4]*2) | ((k[5] & 0xF00) ? 1 : 0)) & 0xFF; + k[5] &= 0xff; + k[3] = (k[3]*2) | ((k[0] & 0x008) ? 1 : 0); + k[2] = (k[2]*2) | ((k[3] & 0xF00) ? 1 : 0); + k[3] &= 0xff; + k[1] = (k[1]*2) | ((k[2] & 0xF00) ? 1 : 0); + k[2] &= 0xff; + k[0] = ((k[0]*2) | ((k[1] & 0xF00) ? 1 : 0)) & 0xFF; + k[1] &= 0xff; + } + for (i = 0; i < 8; i++) { + key[i] = (uint8_t) k[i]; + } +} + +static void CW_RS_DES_KEY(uint8_t *k, uint8_t Rotate_Counter) +{ + uint8_t i,c; + for (i = 1; i < Rotate_Counter+1; i++) { + c = (k[3] & 0x10) ? 0x80 : 0; + k[3] /= 2; + if (k[2] & 1) { + k[3] |= 0x80; + } + k[2] /= 2; + if (k[1] & 1) { + k[2] |= 0x80; + } + k[1] /= 2; + if (k[0] & 1) { + k[1] |= 0x80; + } + k[0] /= 2; + k[0] |= c ; + c = (k[7] & 0x10) ? 0x80 : 0; + k[7] /= 2; + if (k[6] & 1) { + k[7] |= 0x80; + } + k[6] /= 2; + if (k[5] & 1) { + k[6] |= 0x80; + } + k[5] /= 2; + if (k[4] & 1) { + k[5] |= 0x80; + } + k[4] /= 2; + k[4] |= c; + } +} + +static void CW_RS_DES_SUBKEY(uint8_t *k, uint8_t Rotate_Counter) +{ + uint8_t round[] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; + CW_RS_DES_KEY(k, round[Rotate_Counter]); +} + +static void CW_PREP_KEY(uint8_t *key ) +{ + uint8_t DES_key[8],j; + int32_t Round_Counter = 6,i,a; + key[7] = 6; + memset(DES_key, 0 , 8); + do { + a = 7; + i = key[7]; + j = key[Round_Counter]; + do { + DES_key[i] = ( (DES_key[i] * 2) | ((j & 1) ? 1: 0) ) & 0xFF; + j /=2; + i--; + if (i < 0) { + i = 6; + } + a--; + } + while (a >= 0); + key[7] = i; + Round_Counter--; + } + while ( Round_Counter >= 0 ); + a = DES_key[4]; + DES_key[4] = DES_key[6]; + DES_key[6] = a; + DES_key[7] = (DES_key[3] * 16) & 0xFF; + memcpy(key,DES_key,8); + CW_RS_DES_KEY(key,4); +} + +static void CW_L2DES(uint8_t *data, uint8_t *key, uint8_t algo) +{ + uint8_t i, k0[22], k1[22]; + memcpy(k0,key,22); + memcpy(k1,key,22); + CW_48_Key(k0, k1,algo); + CW_PREP_KEY(k1); + for (i = 0; i< 2; i++) { + CW_LS_DES_KEY( k1,15); + CW_DES_ROUND( data ,k1); + } +} + +static void CW_R2DES(uint8_t *data, uint8_t *key, uint8_t algo) +{ + uint8_t i, k0[22],k1[22]; + memcpy(k0,key,22); + memcpy(k1,key,22); + CW_48_Key(k0, k1, algo); + CW_PREP_KEY(k1); + for (i = 0; i< 2; i++) { + CW_LS_DES_KEY(k1,15); + } + for (i = 0; i< 2; i++) { + CW_DES_ROUND( data ,k1); + CW_RS_DES_SUBKEY(k1,1); + } + CW_SWAP_DATA(data); +} + +static void CW_DES(uint8_t *data, uint8_t *inkey, uint8_t m) +{ + uint8_t key[22], i; + memcpy(key, inkey + 9, 8); + CW_PREP_KEY( key ); + for (i = 16; i > 0; i--) { + if (m == 1) { + CW_LS_DES_KEY(key, (uint8_t) (i-1)); + } + CW_DES_ROUND( data ,key); + if (m == 0) { + CW_RS_DES_SUBKEY(key, (uint8_t) (i-1)); + } + } +} + +static void CW_DEC_ENC(uint8_t *d, uint8_t *k, uint8_t a,uint8_t m) +{ + uint8_t n = m & 1; + CW_L2DES(d , k, a); + CW_DES (d , k, n); + CW_R2DES(d , k, a); + if (m & 2) { + CW_SWAP_KEY(k); + } +} + +static void Cryptoworks3DES(uint8_t *data, uint8_t *key) +{ + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key+8, ks2); + + des(data, ks1, 0); + des(data, ks2, 1); + des(data, ks1, 0); +} + +static uint8_t CryptoworksProcessNano80(uint8_t *data, uint32_t caid, int32_t provider, uint8_t *opKey, uint8_t nanoLength, uint8_t nano80Algo) +{ + int32_t i, j; + uint8_t key[16], desKey[16], t[8], dat1[8], dat2[8], k0D00C000[16]; + if(nanoLength < 11) { + return 0; + } + if(caid == 0x0D00 && provider != 0xA0 && !GetCwKey(k0D00C000, 0x0D00C0, 0, 16, 1)) { + return 0; + } + + if(nano80Algo > 1) { + return 0; + } + + memset(t, 0, 8); + memcpy(dat1, data, 8); + + if(caid == 0x0D00 && provider != 0xA0) { + memcpy(key, k0D00C000, 16); + } + else { + memcpy(key, opKey, 16); + } + Cryptoworks3DES(data, key); + memcpy(desKey, data, 8); + + memcpy(data, dat1, 8); + if(caid == 0x0D00 && provider != 0xA0) { + memcpy(key, &k0D00C000[8], 8); + memcpy(&key[8], k0D00C000, 8); + } + else { + memcpy(key, &opKey[8], 8); + memcpy(&key[8], opKey, 8); + } + Cryptoworks3DES(data, key); + memcpy(&desKey[8], data, 8); + + for(i=8; i+7 7) { + if (first) { + CW_L2DES(signature, key, algo); + } + CW_DES(signature, key, 1); + + sigPos = 0; + first = 0; + } + } + if(sigPos > 0) { + CW_DES(signature, key, 1); + } + CW_R2DES(signature, key, algo); +} + +static void CryptoworksDecryptDes(uint8_t *data, uint8_t algo, uint8_t *key) +{ + int32_t i; + uint8_t k[22], t[8]; + + algo &= 7; + if(algo<7) { + CW_DEC_ENC(data, key, algo, 0); + } + else { + memcpy(k, key, 22); + for(i=0; i<3; i++) { + CW_DEC_ENC(data, k, algo, i&1); + memcpy(t,k,8); + memcpy(k,k+8,8); + memcpy(k+8,t,8); + } + } +} + +static int8_t CryptoworksECM(uint32_t caid, uint8_t *ecm, uint8_t *cw) +{ + uint32_t ident; + uint8_t keyIndex = 0, nanoLength, newEcmLength, key[22], signature[8], nano80Algo = 1; + int32_t provider = -1; + uint16_t i, j, ecmLen = GetEcmLen(ecm); + + if(ecmLen < 8) { + return 1; + } + if(ecm[7] != ecmLen - 8) { + return 1; + } + + memset(key, 0, 22); + + for(i = 8; i+1 < ecmLen; i += ecm[i+1] + 2) { + if(ecm[i] == 0x83 && i+2 < ecmLen) { + provider = ecm[i+2] & 0xFC; + keyIndex = ecm[i+2] & 3; + keyIndex = keyIndex ? 1 : 0; + } + else if(ecm[i] == 0x84 && i+3 < ecmLen) { + //nano80Provider = ecm[i+2] & 0xFC; + //nano80KeyIndex = ecm[i+2] & 3; + //nano80KeyIndex = nano80KeyIndex ? 1 : 0; + nano80Algo = ecm[i+3]; + } + } + + if(provider < 0) { + switch(caid) { + case 0x0D00: + provider = 0xC0; + break; + case 0x0D02: + provider = 0xA0; + break; + case 0x0D03: + provider = 0x04; + break; + case 0x0D05: + provider = 0x04; + break; + default: + return 1; + } + } + + ident = (caid << 8) | provider; + if(!GetCwKey(key, ident, keyIndex, 16, 1)) { + return 2; + } + if(!GetCwKey(&key[16], ident, 6, 6, 1)) { + return 2; + } + + for(i = 8; i+1 < ecmLen; i += ecm[i+1] + 2) { + if(ecm[i] == 0x80 && i+2+7 < ecmLen && i+2+ecm[i+1] <= ecmLen + && (provider == 0xA0 || provider == 0xC0 || provider == 0xC4 || provider == 0xC8)) { + nanoLength = ecm[i+1]; + newEcmLength = CryptoworksProcessNano80(ecm+i+2, caid, provider, key, nanoLength, nano80Algo); + if(newEcmLength == 0 || newEcmLength > ecmLen-(i+2+3)) { + return 1; + } + ecm[i+2+3] = 0x81; + ecm[i+2+4] = 0x70; + ecm[i+2+5] = newEcmLength; + ecm[i+2+6] = 0x81; + ecm[i+2+7] = 0xFF; + return CryptoworksECM(caid, ecm+i+2+3, cw); + } + } + + if(ecmLen - 15 < 1) { + return 1; + } + CryptoworksSignature(ecm + 5, ecmLen - 15, key, signature); + for(i = 8; i+1 < ecmLen; i += ecm[i+1]+2) { + switch(ecm[i]) { + case 0xDA: + case 0xDB: + case 0xDC: + if(i+2+ecm[i+1] > ecmLen) { + break; + } + for(j=0; j+7 ecmLen) { + break; + } + if(memcmp(&ecm[i+2], signature, 8)) { + return 6; + } + break; + } + } + + for(i = 8; i+1 < ecmLen; i += ecm[i+1]+2) { + switch(ecm[i]) { + case 0xDB: + if(i+2+ecm[i+1] <= ecmLen && ecm[i+1] == 16) { + memcpy(cw, &ecm[i+2], 16); + return 0; + } + break; + } + } + + return 5; +} + +// SoftNDS EMU +static const uint8_t nds_const[]= {0x0F,0x1E,0x2D,0x3C,0x4B,0x5A,0x69,0x78,0x87,0x96,0xA5,0xB4,0xC3,0xD2,0xE1,0xF0}; + +uint8_t viasat_const[]= { + 0x15,0x85,0xC5,0xE4,0xB8,0x52,0xEC,0xF7,0xC3,0xD9,0x08,0xBA,0x22,0x4A,0x66,0xF2, + 0x82,0x15,0x4F,0xB2,0x18,0x48,0x63,0x97,0xDC,0x19,0xD8,0x51,0x9A,0x39,0xFC,0xCA, + 0x1C,0x24,0xD0,0x65,0xA9,0x66,0x2D,0xD6,0x53,0x3B,0x86,0xBA,0x40,0xEA,0x4C,0x6D, + 0xD9,0x1E,0x41,0x14,0xFE,0x15,0xAF,0xC3,0x18,0xC5,0xF8,0xA7,0xA8,0x01,0x00,0x01, +}; + +static int8_t SoftNDSECM(uint16_t caid, uint8_t *ecm, uint8_t *dw) +{ + int32_t i; + uint8_t *tDW, irdEcmLen, offsetCw = 0, offsetP2 = 0; + uint8_t digest[16], md5_const[64]; + MD5_CTX mdContext; + uint16_t ecmLen = GetEcmLen(ecm); + + if(ecmLen < 7) { + return 1; + } + + if(ecm[3] != 0x00 || ecm[4] != 0x00 || ecm[5] != 0x01) { + return 1; + } + + irdEcmLen = ecm[6]; + if(irdEcmLen < (10+3+8+4) || irdEcmLen+6 >= ecmLen) { + return 1; + } + + for(i=0; 10+i+2 < irdEcmLen; i++) { + if(ecm[17+i] == 0x0F && ecm[17+i+1] == 0x40 && ecm[17+i+2] == 0x00) { + offsetCw = 17+i+3; + offsetP2 = offsetCw+9; + } + } + + if(offsetCw == 0 || offsetP2 == 0) { + return 1; + } + + if(offsetP2-7+4 > irdEcmLen) { + return 1; + } + + if(caid == 0x090F || caid == 0x093E) { + memcpy(md5_const, viasat_const, 64); + } + else if(!FindKey('S', caid, 0, "00", md5_const, 64, 1, 0, 0, NULL)) { + return 2; + } + + memset(dw,0,16); + tDW = &dw[ecm[0] == 0x81 ? 8 : 0]; + + MD5_Init(&mdContext); + MD5_Update(&mdContext, ecm+7, 10); + MD5_Update(&mdContext, ecm+offsetP2, 4); + MD5_Update(&mdContext, md5_const, 64); + MD5_Update(&mdContext, nds_const, 16); + MD5_Final(digest, &mdContext); + + for (i=0; i<8; i++) { + tDW[i] = digest[i+8] ^ ecm[offsetCw+i]; + } + + if(((tDW[0]+tDW[1]+tDW[2])&0xFF)-tDW[3]) { + return 6; + } + if(((tDW[4]+tDW[5]+tDW[6])&0xFF)-tDW[7]) { + return 6; + } + + return 0; +} + +// Viaccess EMU +static int8_t GetViaKey(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey) +{ + + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + if(FindKey('V', ident, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL)) { + return 1; + } + + if(ident == 0xD00040 && FindKey('V', 0x030B00, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL)) { + return 1; + } + + return 0; +} + +static void Via1Mod(const uint8_t* key2, uint8_t* data) +{ + int32_t kb, db; + for (db=7; db>=0; db--) { + for (kb=7; kb>3; kb--) { + int32_t a0=kb^db; + int32_t pos=7; + if (a0&4) { + a0^=7; + pos^=7; + } + a0=(a0^(kb&3)) + (kb&3); + if (!(a0&4)) { + data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF)); + } + } + } + for (db=0; db<8; db++) { + for (kb=0; kb<4; kb++) { + int32_t a0=kb^db; + int32_t pos=7; + if (a0&4) { + a0^=7; + pos^=7; + } + a0=(a0^(kb&3)) + (kb&3); + if (!(a0&4)) { + data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF)); + } + } + } +} + +static void Via1Decode(uint8_t *data, uint8_t *key) +{ + Via1Mod(key+8, data); + nc_des(key, DES_ECM_CRYPT, data); + Via1Mod(key+8, data); +} + +static void Via1Hash(uint8_t *data, uint8_t *key) +{ + Via1Mod(key+8, data); + nc_des(key, DES_ECM_HASH, data); + Via1Mod(key+8, data); +} + +static inline void Via1DoHash(uint8_t *hashbuffer, uint8_t *pH, uint8_t data, uint8_t *hashkey) +{ + hashbuffer[*pH] ^= data; + (*pH)++; + + if(*pH == 8) { + Via1Hash(hashbuffer, hashkey); + *pH = 0; + } +} + +static int8_t Via1Decrypt(uint8_t* ecm, uint8_t* dw, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t work_key[16]; + uint8_t *data, *des_data1, *des_data2; + uint16_t ecmLen = GetEcmLen(ecm); + int32_t msg_pos; + int32_t encStart = 0, hash_start, i; + uint8_t signature[8], hashbuffer[8], prepared_key[16], hashkey[16]; + uint8_t tmp, k, pH, foundData = 0; + + if (ident == 0) { + return 4; + } + memset(work_key, 0, 16); + if(!GetViaKey(work_key, ident, '0', desKeyIndex, 8, 1)) { + return 2; + } + + if(ecmLen < 11) { + return 1; + } + data = ecm+9; + des_data1 = dw; + des_data2 = dw+8; + + msg_pos = 0; + pH = 0; + memset(hashbuffer, 0, sizeof(hashbuffer)); + memcpy(hashkey, work_key, sizeof(hashkey)); + memset(signature, 0, 8); + + while(9+msg_pos+2 < ecmLen) { + switch (data[msg_pos]) { + case 0xea: + if(9+msg_pos+2+15 < ecmLen) { + encStart = msg_pos + 2; + memcpy(des_data1, &data[msg_pos+2], 8); + memcpy(des_data2, &data[msg_pos+2+8], 8); + foundData |= 1; + } + break; + case 0xf0: + if(9+msg_pos+2+7 < ecmLen) { + memcpy(signature, &data[msg_pos+2], 8); + foundData |= 2; + } + break; + } + msg_pos += data[msg_pos+1]+2; + } + + if(foundData != 3) { + return 1; + } + + pH=i=0; + + if(data[0] == 0x9f && 10+data[1] <= ecmLen) { + Via1DoHash(hashbuffer, &pH, data[i++], hashkey); + Via1DoHash(hashbuffer, &pH, data[i++], hashkey); + + for (hash_start=0; hash_start < data[1]; hash_start++) { + Via1DoHash(hashbuffer, &pH, data[i++], hashkey); + } + + while (pH != 0) { + Via1DoHash(hashbuffer, &pH, 0, hashkey); + } + } + + if (work_key[7] == 0) { + for (; i < encStart + 16; i++) { + Via1DoHash(hashbuffer, &pH, data[i], hashkey); + } + memcpy(prepared_key, work_key, 8); + } + else { + prepared_key[0] = work_key[2]; + prepared_key[1] = work_key[3]; + prepared_key[2] = work_key[4]; + prepared_key[3] = work_key[5]; + prepared_key[4] = work_key[6]; + prepared_key[5] = work_key[0]; + prepared_key[6] = work_key[1]; + prepared_key[7] = work_key[7]; + memcpy(prepared_key+8, work_key+8, 8); + + if (work_key[7] & 1) { + for (; i < encStart; i++) { + Via1DoHash(hashbuffer, &pH, data[i], hashkey); + } + + k = ((work_key[7] & 0xf0) == 0) ? 0x5a : 0xa5; + + for (i=0; i<8; i++) { + tmp = des_data1[i]; + des_data1[i] = (k & hashbuffer[pH] ) ^ tmp; + Via1DoHash(hashbuffer, &pH, tmp, hashkey); + } + + for (i = 0; i < 8; i++) { + tmp = des_data2[i]; + des_data2[i] = (k & hashbuffer[pH] ) ^ tmp; + Via1DoHash(hashbuffer, &pH, tmp, hashkey); + } + } + else { + for (; i < encStart + 16; i++) { + Via1DoHash(hashbuffer, &pH, data[i], hashkey); + } + } + } + Via1Decode(des_data1, prepared_key); + Via1Decode(des_data2, prepared_key); + Via1Hash(hashbuffer, hashkey); + if(memcmp(signature, hashbuffer, 8)) { + return 6; + } + return 0; +} + +static int8_t Via26ProcessDw(uint8_t *indata, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t pv1,pv2, i; + uint8_t Tmp[8], T1Key[300], P1Key[8], KeyDes1[16], KeyDes2[16], XorKey[8]; + uint32_t ks1[32], ks2[32]; + + if(!GetViaKey(T1Key, ident, 'T', 1, 300, 1)) { + return 2; + } + if(!GetViaKey(P1Key, ident, 'P', 1, 8, 1)) { + return 2; + } + if(!GetViaKey(KeyDes1, ident, 'D', 1, 16, 1)) { + return 2; + } + if(!GetViaKey(KeyDes2, ident, '0', desKeyIndex, 16, 1)) { + return 2; + } + if(!GetViaKey(XorKey, ident, 'X', 1, 8, 1)) { + return 2; + } + + for (i=0; i<8; i++) { + pv1 = indata[i]; + Tmp[i] = T1Key[pv1]; + } + for (i=0; i<8; i++) { + pv1 = P1Key[i]; + pv2 = Tmp[pv1]; + indata[i]=pv2; + } + + des_set_key(KeyDes1, ks1); + des(indata, ks1, 1); + + for (i=0; i<8; i++) { + indata[i] ^= XorKey[i]; + } + + des_set_key(KeyDes2, ks1); + des_set_key(KeyDes2+8, ks2); + des(indata, ks1, 0); + des(indata, ks2, 1); + des(indata, ks1, 0); + + for (i=0; i<8; i++) { + indata[i] ^= XorKey[i]; + } + + des_set_key(KeyDes1, ks1); + des(indata, ks1, 0); + + for (i=0; i<8; i++) { + pv1 = indata[i]; + pv2 = P1Key[i]; + Tmp[pv2] = pv1; + } + for (i=0; i<8; i++) { + pv1 = Tmp[i]; + pv2 = T1Key[pv1]; + indata[i] = pv2; + } + return 0; +} + +static int8_t Via26Decrypt(uint8_t* source, uint8_t* dw, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t tmpData[8], C1[8]; + uint8_t *pXorVector; + int32_t i,j; + + if (ident == 0) { + return 4; + } + if(!GetViaKey(C1, ident, 'C', 1, 8, 1)) { + return 2; + } + + for (i=0; i<2; i++) { + memcpy(tmpData, source+ i*8, 8); + Via26ProcessDw(tmpData, ident, desKeyIndex); + if (i!=0) { + pXorVector = source; + } + else { + pXorVector = &C1[0]; + } + for (j=0; j<8; j++) { + dw[i*8+j] = tmpData[j]^pXorVector[j]; + } + } + return 0; +} + +static void Via3Core(uint8_t *data, uint8_t Off, uint32_t ident, uint8_t* XorKey, uint8_t* T1Key) +{ + uint8_t i; + uint32_t lR2, lR3, lR4, lR6, lR7; + + switch (ident) { + case 0x032820: { + for (i=0; i<4; i++) { + data[i]^= XorKey[(Off+i) & 0x07]; + } + lR2 = (data[0]^0xBD)+data[0]; + lR3 = (data[3]^0xEB)+data[3]; + lR2 = (lR2-lR3)^data[2]; + lR3 = ((0x39*data[1])<<2); + data[4] = (lR2|lR3)+data[2]; + lR3 = ((((data[0]+6)^data[0]) | (data[2]<<1))^0x65)+data[0]; + lR2 = (data[1]^0xED)+data[1]; + lR7 = ((data[3]+0x29)^data[3])*lR2; + data[5] = lR7+lR3; + lR2 = ((data[2]^0x33)+data[2]) & 0x0A; + lR3 = (data[0]+0xAD)^data[0]; + lR3 = lR3+lR2; + lR2 = data[3]*data[3]; + lR7 = (lR2 | 1) + data[1]; + data[6] = (lR3|lR7)+data[1]; + lR3 = data[1] & 0x07; + lR2 = (lR3-data[2]) & (data[0] | lR2 |0x01); + data[7] = lR2+data[3]; + for (i=0; i<4; i++) { + data[i+4] = T1Key[data[i+4]]; + } + } + break; + case 0x030B00: { + for (i=0; i<4; i++) { + data[i]^= XorKey[(Off+i) & 0x07]; + } + lR6 = (data[3] + 0x6E) ^ data[3]; + lR6 = (lR6*(data[2] << 1)) + 0x17; + lR3 = (data[1] + 0x77) ^ data[1]; + lR4 = (data[0] + 0xD7) ^ data[0]; + data[4] = ((lR4 & lR3) | lR6) + data[0]; + lR4 = ((data[3] + 0x71) ^ data[3]) ^ 0x90; + lR6 = (data[1] + 0x1B) ^ data[1]; + lR4 = (lR4*lR6) ^ data[0]; + data[5] = (lR4 ^ (data[2] << 1)) + data[1]; + lR3 = (data[3] * data[3])| 0x01; + lR4 = (((data[2] ^ 0x35) + data[2]) | lR3) + data[2]; + lR6 = data[1] ^ (data[0] + 0x4A); + data[6] = lR6 + lR4; + lR3 = (data[0] * (data[2] << 1)) | data[1]; + lR4 = 0xFE - data[3]; + lR3 = lR4 ^ lR3; + data[7] = lR3 + data[3]; + for (i=0; i<4; i++) { + data[4+i] = T1Key[data[4+i]]; + } + } + break; + default: + break; + } +} + +static void Via3Fct1(uint8_t *data, uint32_t ident, uint8_t* XorKey, uint8_t* T1Key) +{ + uint8_t t; + Via3Core(data, 0, ident, XorKey, T1Key); + + switch (ident) { + case 0x032820: { + t = data[4]; + data[4] = data[7]; + data[7] = t; + } + break; + case 0x030B00: { + t = data[5]; + data[5] = data[7]; + data[7] = t; + } + break; + default: + break; + } +} + +static void Via3Fct2(uint8_t *data, uint32_t ident, uint8_t* XorKey, uint8_t* T1Key) +{ + uint8_t t; + Via3Core(data, 4, ident, XorKey, T1Key); + + switch (ident) { + case 0x032820: { + t = data[4]; + data[4] = data[7]; + data[7] = data[5]; + data[5] = data[6]; + data[6] = t; + } + break; + case 0x030B00: { + t = data[6]; + data[6] = data[7]; + data[7] = t; + } + break; + default: + break; + } +} + +static int8_t Via3ProcessDw(uint8_t *data, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t i; + uint8_t tmp[8], T1Key[300], P1Key[8], KeyDes[16], XorKey[8]; + uint32_t ks1[32], ks2[32]; + + if(!GetViaKey(T1Key, ident, 'T', 1, 300, 1)) { + return 2; + } + if(!GetViaKey(P1Key, ident, 'P', 1, 8, 1)) { + return 2; + } + if(!GetViaKey(KeyDes, ident, '0', desKeyIndex, 16, 1)) { + return 2; + } + if(!GetViaKey(XorKey, ident, 'X', 1, 8, 1)) { + return 2; + } + + for (i=0; i<4; i++) { + tmp[i] = data[i+4]; + } + Via3Fct1(tmp, ident, XorKey, T1Key); + for (i=0; i<4; i++) { + tmp[i] = data[i]^tmp[i+4]; + } + Via3Fct2(tmp, ident, XorKey, T1Key); + for (i=0; i<4; i++) { + tmp[i]^= XorKey[i+4]; + } + for (i=0; i<4; i++) { + data[i] = data[i+4]^tmp[i+4]; + data[i+4] = tmp[i]; + } + + des_set_key(KeyDes, ks1); + des_set_key(KeyDes+8, ks2); + + des(data, ks1, 0); + des(data, ks2, 1); + des(data, ks1, 0); + + for (i=0; i<4; i++) { + tmp[i] = data[i+4]; + } + Via3Fct2(tmp, ident, XorKey, T1Key); + for (i=0; i<4; i++) { + tmp[i] = data[i]^tmp[i+4]; + } + Via3Fct1(tmp, ident, XorKey, T1Key); + for (i=0; i<4; i++) { + tmp[i]^= XorKey[i]; + } + for (i=0; i<4; i++) { + data[i] = data[i+4]^tmp[i+4]; + data[i+4] = tmp[i]; + } + return 0; +} + +static void Via3FinalMix(uint8_t *dw) +{ + uint8_t tmp[4]; + + memcpy(tmp, dw, 4); + memcpy(dw, dw + 4, 4); + memcpy(dw + 4, tmp, 4); + + memcpy(tmp, dw + 8, 4); + memcpy(dw + 8, dw + 12, 4); + memcpy(dw + 12, tmp, 4); +} + +static int8_t Via3Decrypt(uint8_t* source, uint8_t* dw, uint32_t ident, uint8_t desKeyIndex, uint8_t aesKeyIndex, uint8_t aesMode, int8_t doFinalMix) +{ + int8_t aesAfterCore = 0; + int8_t needsAES = (aesKeyIndex != 0xFF); + uint8_t tmpData[8], C1[8]; + uint8_t *pXorVector; + char aesKey[16]; + int32_t i, j; + + if(ident == 0) { + return 4; + } + if(!GetViaKey(C1, ident, 'C', 1, 8, 1)) { + return 2; + } + if(needsAES && !GetViaKey((uint8_t*)aesKey, ident, 'E', aesKeyIndex, 16, 1)) { + return 2; + } + if(aesMode == 0x0D || aesMode == 0x11 || aesMode == 0x15) { + aesAfterCore = 1; + } + + if(needsAES && !aesAfterCore) { + if(aesMode == 0x0F) { + hdSurEncPhase1_D2_0F_11(source); + hdSurEncPhase2_D2_0F_11(source); + } + else if(aesMode == 0x13) { + hdSurEncPhase1_D2_13_15(source); + } + struct aes_keys aes; + aes_set_key(&aes, aesKey); + aes_decrypt(&aes, source, 16); + if(aesMode == 0x0F) { + hdSurEncPhase1_D2_0F_11(source); + } + else if(aesMode == 0x13) { + hdSurEncPhase2_D2_13_15(source); + } + } + + for(i=0; i<2; i++) { + memcpy(tmpData, source+i*8, 8); + Via3ProcessDw(tmpData, ident, desKeyIndex); + if (i!=0) { + pXorVector = source; + } + else { + pXorVector = &C1[0]; + } + for (j=0; j<8; j++) { + dw[i*8+j] = tmpData[j]^pXorVector[j]; + } + } + + if(needsAES && aesAfterCore) { + if(aesMode == 0x11) { + hdSurEncPhase1_D2_0F_11(dw); + hdSurEncPhase2_D2_0F_11(dw); + } + else if(aesMode == 0x15) { + hdSurEncPhase1_D2_13_15(dw); + } + struct aes_keys aes; + aes_set_key(&aes, aesKey); + aes_decrypt(&aes, dw, 16); + if(aesMode == 0x11) { + hdSurEncPhase1_D2_0F_11(dw); + } + if(aesMode == 0x15) { + hdSurEncPhase2_D2_13_15(dw); + } + } + + if(ident == 0x030B00) { + if(doFinalMix) { + Via3FinalMix(dw); + } + if(!isValidDCW(dw)) { + return 6; + } + } + return 0; +} + +static int8_t ViaccessECM(uint8_t *ecm, uint8_t *dw) +{ + uint32_t currentIdent = 0; + uint8_t nanoCmd = 0, nanoLen = 0, version = 0, providerKeyLen = 0, desKeyIndex = 0, aesMode = 0, aesKeyIndex = 0xFF; + int8_t doFinalMix = 0, result = 1; + uint16_t i = 0, keySelectPos = 0, ecmLen = GetEcmLen(ecm); + + for (i=4; i+2 ecmLen) { + return 1; + } + + switch (nanoCmd) { + case 0x40: + if (nanoLen < 0x03) { + break; + } + version = ecm[i]; + if (nanoLen == 3) { + currentIdent = ((ecm[i]<<16)|(ecm[i+1]<<8))|(ecm[i+2]&0xF0); + desKeyIndex = ecm[i+2]&0x0F; + keySelectPos = i+3; + } + else { + currentIdent = (ecm[i]<<16)|(ecm[i+1]<<8)|((ecm[i+2]>>4)&0x0F); + desKeyIndex = ecm[i+3]; + keySelectPos = i+4; + } + providerKeyLen = nanoLen; + break; + case 0x90: + if (nanoLen < 0x03) { + break; + } + version = ecm[i]; + currentIdent = ((ecm[i]<<16)|(ecm[i+1]<<8))|(ecm[i+2]&0xF0); + desKeyIndex = ecm[i+2]&0x0F; + keySelectPos = i+4; + if((version == 3) && (nanoLen > 3)) { + desKeyIndex = ecm[i+(nanoLen-4)]&0x0F; + } + providerKeyLen = nanoLen; + break; + case 0x80: + nanoLen = 0; + break; + case 0xD2: + if (nanoLen < 0x02) { + break; + } + aesMode = ecm[i]; + aesKeyIndex = ecm[i+1]; + break; + case 0xDD: + nanoLen = 0; + break; + case 0xEA: + if (nanoLen < 0x10) { + break; + } + + if (version < 2) { + return Via1Decrypt(ecm, dw, currentIdent, desKeyIndex); + } + else if (version == 2) { + return Via26Decrypt(ecm + i, dw, currentIdent, desKeyIndex); + } + else if (version == 3) { + doFinalMix = 0; + if (currentIdent == 0x030B00 && providerKeyLen>3) { + if(keySelectPos+2 >= ecmLen) { + break; + } + if (ecm[keySelectPos] == 0x05 && ecm[keySelectPos+1] == 0x67 && (ecm[keySelectPos+2] == 0x00 || ecm[keySelectPos+2] == 0x01)) { + if(ecm[keySelectPos+2] == 0x01) { + doFinalMix = 1; + } + } + else { + break; + } + } + return Via3Decrypt(ecm + i, dw, currentIdent, desKeyIndex, aesKeyIndex, aesMode, doFinalMix); + } + break; + default: + break; + } + i += nanoLen; + } + return result; +} + +// Nagra EMU +static int8_t GetNagraKey(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint8_t isCriticalKey) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + if(FindKey('N', ident, 0, keyStr, buf, keyName == 'M' ? 64 : 16, isCriticalKey, 0, 0, NULL)) { + return 1; + } + + return 0; +} + +static int8_t Nagra2Signature(const uint8_t *vkey, const uint8_t *sig, const uint8_t *msg, int32_t len) +{ + uint8_t buff[16]; + uint8_t iv[8]; + int32_t i,j; + + memcpy(buff,vkey,sizeof(buff)); + for(i=0; i+7=0; j--) { + buff[j+8]^=msg[i+j]; + } + } + buff[8]&=0x7F; + return (memcmp(sig, buff+8, 8) == 0); +} + +static int8_t DecryptNagra2ECM(uint8_t *in, uint8_t *out, const uint8_t *key, int32_t len, const uint8_t *vkey, uint8_t *keyM) +{ + BIGNUM *exp, *mod; + uint8_t iv[8]; + int32_t i = 0, sign = in[0] & 0x80; + uint8_t binExp = 3; + int8_t result = 1; + + exp = BN_new(); + mod = BN_new(); + BN_bin2bn(&binExp, 1, exp); + BN_bin2bn(keyM, 64, mod); + + if(EmuRSA(out,in+1,64,exp,mod,1)<=0) { + BN_free(exp); + BN_free(mod); + return 0; + } + out[63]|=sign; + if(len>64) { + memcpy(out+64,in+65,len-64); + } + + memset(iv,0,sizeof(iv)); + if(in[0]&0x04) { + uint8_t key1[8], key2[8]; + ReverseMemInOut(key1,&key[0],8); + ReverseMemInOut(key2,&key[8],8); + + for(i=7; i>=0; i--) { + ReverseMem(out+8*i,8); + } + des_ede2_cbc_decrypt(out, iv, key1, key2, len); + for(i=7; i>=0; i--) { + ReverseMem(out+8*i,8); + } + } + else { + IDEA_KEY_SCHEDULE ek; + idea_set_encrypt_key(key, &ek); + idea_cbc_encrypt(out, out, len&~7, &ek, iv, IDEA_DECRYPT); + } + + ReverseMem(out,64); + if(result && EmuRSA(out,out,64,exp,mod,0)<=0) { + result = 0; + } + if(result && vkey && !Nagra2Signature(vkey,out,out+8,len-8)) { + result = 0; + } + + BN_free(exp); + BN_free(mod); + return result; +} + +static int8_t Nagra2ECM(uint8_t *ecm, uint8_t *dw) +{ + uint32_t ident, identMask, tmp1, tmp2, tmp3; + uint8_t cmdLen, ideaKeyNr, *dec, ideaKey[16], vKey[16], m1Key[64], mecmAlgo = 0; + int8_t useVerifyKey = 0; + int32_t l=0, s; + uint16_t i = 0, ecmLen = GetEcmLen(ecm); + + if(ecmLen < 8) { + return 1; + } + cmdLen = ecm[4] - 5; + ident = (ecm[5] << 8) + ecm[6]; + ideaKeyNr = (ecm[7]&0x10)>>4; + if(ideaKeyNr) { + ideaKeyNr = 1; + } + if(ident == 1283 || ident == 1285 || ident == 1297) { + ident = 1281; + } + if(cmdLen <= 63 || ecmLen < cmdLen + 10) { + return 1; + } + + if(!GetNagraKey(ideaKey, ident, '0', ideaKeyNr, 1)) { + return 2; + } + if(GetNagraKey(vKey, ident, 'V', 0, 0)) { + useVerifyKey = 1; + } + if(!GetNagraKey(m1Key, ident, 'M', 1, 1)) { + return 2; + } + ReverseMem(m1Key, 64); + + dec = (uint8_t*)malloc(sizeof(uint8_t)*cmdLen); + if(dec == NULL) { + return 7; + } + if(!DecryptNagra2ECM(ecm+9, dec, ideaKey, cmdLen, useVerifyKey?vKey:0, m1Key)) { + free(dec); + return 1; + } + + for(i=(dec[14]&0x10)?16:20; i0) { + return 1; + } + + identMask = ident & 0xFF00; + if (identMask == 0x1100 || identMask == 0x500 || identMask == 0x3100) { + memcpy(&tmp1, dw, 4); + memcpy(&tmp2, dw + 4, 4); + memcpy(&tmp3, dw + 12, 4); + memcpy(dw, dw + 8, 4); + memcpy(dw + 4, &tmp3, 4); + memcpy(dw + 8, &tmp1, 4); + memcpy(dw + 12, &tmp2, 4); + } + return 0; +} + +// Irdeto EMU +static int8_t GetIrdetoKey(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint8_t isCriticalKey, uint32_t *keyRef) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + + if(*keyRef > 0xFF) + { + return 0; + } + + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + if(FindKey('I', ident, 0, keyStr, buf, 16, *keyRef > 0 ? 0 : isCriticalKey, *keyRef, 0, NULL)) { + (*keyRef)++; + return 1; + } + + return 0; +} + +static void Irdeto2Encrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len) +{ + const uint8_t *tmp = seed;; + int32_t i; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key+8, ks2); + + len&=~7; + + for(i=0; i+7> 16; + + switch (pos) + { + case 29: // Rascom QAF 1R + case 31: // Eutelsat 3B + { + pos = 30; + break; + } + + case 49: + case 50: // SES 5 + { + pos = 48; // Astra 4A + break; + } + + case 215: + { + pos = 216; // Eutelsat 21B + break; + } + + case 285: // Astra 2E + { + pos = 282; // Astra 2F/2G + break; + } + + case 328: // Intelsat 28 + case 329: + case 331: // Eutelsat 33C + { + pos = 330; + break; + } + + case 359: // Eutelsat 36B + case 361: // Express AMU1 + { + pos = 360; + break; + } + + case 451: // Intelsat 904 + { + pos = 450; // Intelsat 12 + break; + } + + case 550: + case 551: // G-Sat 8/16 + { + pos = 549; // Yamal 402 + break; + } + + case 748: + case 749: // ABS 2A + { + pos = 750; + break; + } + + case 848: // Horizons 2 + case 852: // Intelsat 15 + { + pos = 850; + break; + } + + case 914: // Mesasat 3a + { + pos = 915; // Mesasat 3/3b + break; + } + + case 934: // G-Sat 17 + case 936: // Insat 4B + { + pos = 935; // G-Sat 15 + break; + } + + case 3600 - 911: // Nimiq 6 + { + pos = 3600 - 910; // Galaxy 17 + } + + case 3600 - 870: // SES 2 + case 3600 - 872: // TKSat 1 + { + pos = 3600 - 871; + break; + } + + case 3600 - 432: // Sky Brasil 1 + case 3600 - 430: // Intelsat 11 + { + pos = 3600 - 431; + break; + } + + case 3600 - 376: // Telstar 11N + case 3600 - 374: // NSS 10 + { + pos = 3600 - 375; + break; + } + + case 3600 - 359: // Hispasat 36W-1 + { + pos = 3600 - 360; // Eutelsat 36 West A + break; + } + + case 3600 - 81: // Eutelsat 8 West B + { + pos = 3600 - 80; + break; + } + + case 3600 - 73: // Eutelsat 7 West A + case 3600 - 72: + case 3600 - 71: + { + pos = 3600 - 70; // Nilesat 201 + break; + } + + case 3600 - 10: // Intelsat 10-02 + case 3600 - 9: // Thor 6 + case 3600 - 7: // Thor 7 + case 3600 - 6: // Thor 7 + { + pos = 3600 - 8; // Thor 5 + break; + } + } + + *namespace = (*namespace & 0xF000FFFF) | (pos << 16); +} + +static void BissAnnotate(char *buf, uint8_t len, const uint8_t *ecm, uint16_t ecmLen, uint32_t hash, int8_t isNamespaceHash, int8_t datecoded) +{ + // Extract useful information to append to the "Example key ..." message. + // + // For feeds, the orbital position & frequency are usually embedded in the namespace. + // See https://github.com/openatv/enigma2/blob/master/lib/dvb/frontend.cpp#L496 + // hash = (sat.orbital_position << 16); + // hash |= ((sat.frequency/1000)&0xFFFF)|((sat.polarisation&1) << 15); + // + // If the onid & tsid appear to be a unique DVB identifier, enigma2 strips the frequency + // from our namespace. See https://github.com/openatv/enigma2/blob/master/lib/dvb/scan.cpp#L59 + // In that case, our annotation contains the onid:tsid:sid triplet in lieu of frequency. + // + // For the universal case, we print the number of elementary stream pids & pmtpid. + // The sid and current time are included for all. Examples: + // + // F 1A2B3C4D 00000000 XXXXXXXXXXXXXXXX ; 110.5W 12345H sid:0001 added: 2017-10-17 @ 13:14:15 // namespace + // F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; 33.5E ABCD:9876:1234 added: 2017-10-17 @ 13:14:15 // stripped namespace + // F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; av:5 pmt:0134 sid:0001 added: 2017-10-17 @ 13:14:15 // universal + + uint8_t pidcount; + uint16_t frequency, degrees, pmtpid, srvid, tsid, onid; + uint32_t ens; + char compass, polarisation, timeStr1[9], timeStr2[19]; + + if (datecoded) + { + Date2Str(timeStr1, sizeof(timeStr1), 4, 3); + } + else + { + snprintf(timeStr1, sizeof(timeStr1), "00000000"); + } + + Date2Str(timeStr2, sizeof(timeStr2), 0, 2); + + if (isNamespaceHash) // Namespace hash + { + ens = b2i(4, ecm + ecmLen - 4); // Namespace will be the last 4 bytes of the ecm + degrees = (ens >> 16) & 0x0FFF; // Remove not-a-pid flag + + if (degrees > 1800) + { + degrees = 3600 - degrees; + compass = 'W'; + } + else + { + compass = 'E'; + } + + if (0 == (ens & 0xFFFF)) // Stripped namespace hash + { + srvid = b2i(2, ecm + 3); + tsid = b2i(2, ecm + ecmLen - 8); + onid = b2i(2, ecm + ecmLen - 6); + // Printing degree sign "\u00B0" requires c99 standard + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %04X:%04X:%04X added: %s", + hash, timeStr1, degrees / 10.0, compass, onid, tsid, srvid, timeStr2); + } + else // Full namespace hash + { + srvid = b2i(2, ecm + 3); + frequency = ens & 0x7FFF; // Remove polarity bit + polarisation = ens & 0x8000 ? 'V' : 'H'; + // Printing degree sign "\u00B0" requires c99 standard + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %5d%c sid:%04X added: %s", + hash, timeStr1, degrees / 10.0, compass, frequency, polarisation, srvid, timeStr2); + } + } + else // Universal hash + { + srvid = b2i(2, ecm + 3); + pmtpid = b2i(2, ecm + 5); + pidcount = (ecmLen - 15) / 2; // video + audio pids count + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; av:%d pmt:%04X sid:%04X added: %s", + hash, timeStr1, pidcount, pmtpid, srvid, timeStr2); + } +} + +static int8_t BissIsCommonHash(uint32_t hash) +{ + // Check universal hash against a number of commnon universal + // hashes in order to warn users about potential key clashes + + switch (hash) + { + case 0xBAFCD9FD: // 0001 0020 0200 1010 1020 (most common hash) + return 1; + case 0xA6A4FBD4: // 0001 0800 0200 1010 1020 + return 1; + case 0xEFAB7A4D: // 0001 0800 1010 1020 0200 + return 1; + case 0x83FA15D1: // 0001 0020 0134 0100 0101 + return 1; + case 0x58934C38: // 0001 0800 1010 1020 1030 0200 + return 1; + case 0x2C3CEC17: // 0001 0020 0134 0100 + return 1; + case 0x73DF7F7E: // 0001 0020 0200 1010 1020 1030 + return 1; + case 0xAFA85BC8: // 0001 0020 0021 0022 0023 + return 1; + case 0x8C51F31D: // 0001 0800 0200 1010 1020 1030 1040 + return 1; + case 0xE2F9BD29: // 0001 0800 0200 1010 1020 1030 + return 1; + case 0xB9EBE0FF: // 0001 0100 0200 1010 1020 (less common hash) + return 1; + default: + return 0; + } +} + +static int8_t BissIsValidNamespace(uint32_t namespace) +{ + // Note to developers: + // If we ever have a satellite at 0.0E, edit to allow stripped namespace + // '0xA0000000' with an additional test on tsid and onid being != 0 + + uint16_t orbital, frequency; + + orbital = (namespace >> 16) & 0x0FFF; + frequency = namespace & 0x7FFF; + + if ((namespace & 0xA0000000) != 0xA0000000) return 0; // Value isn't flagged as namespace + if (namespace == 0xA0000000) return 0; // Empty namespace + if (orbital > 3599) return 0; // Allow only DVB-S + if (frequency == 0) return 1; // Stripped namespace + if (frequency >= 3400 && frequency <= 4200) return 1; // Super extended C band + if (frequency >= 10700 && frequency <= 12750) return 1; // Ku band Europe + + return 0; +} + +static int8_t BissGetKey(uint32_t provider, uint8_t *key, int8_t dateCoded, int8_t printMsg) +{ + // If date-coded keys are enabled in the webif, this function evaluates the expiration date + // of the keys found. Expired keys are not sent to BissECM(). If date-coded keys are disabled, + // then all keys found are sent without any evaluation. It takes the "provider" as input and + // outputs the "key". Returns 0 (Key not found, or expired) or 1 (Key found). + + // printMsg: 0 => No message + // printMsg: 1 => Print message only if key is found + // printMsg: 2 => Always print message, regardless if key is found or not + + char keyExpDate[9] = "00000000"; + + if (FindKey('F', provider, 0, keyExpDate, key, 8, 0, 0, 0, NULL)) // Key found + { + if (dateCoded) // Date-coded keys are enabled, evaluate expiration date + { + char currentDate[9]; + Date2Str(currentDate, sizeof(currentDate), 0, 3); + + if (strncmp("00000000", keyExpDate, 9) == 0 || strncmp(currentDate, keyExpDate, 9) < 0) // Evergreen or not expired + { + if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate); + return 1; + } + else // Key expired + { + key = NULL; // Make sure we don't send any expired key + if (printMsg == 2) cs_log("Key expired: F %08X %s", provider, keyExpDate); + return 0; + } + } + else // Date-coded keys are disabled, don't evaluate expiration date + { + if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate); + return 1; + } + } + else // Key not found + { + if (printMsg == 2) cs_log("Key not found: F %08X", provider); + return 0; + } +} + +static int8_t BissECM(struct s_reader *rdr, const uint8_t *ecm, int16_t ecmDataLen, uint8_t *dw, uint16_t srvid, uint16_t ecmpid) +{ + // Oscam's fake ecm consists of [sid] [pmtpid] [pid1] [pid2] ... [pidx] [tsid] [onid] [namespace] + // + // On enigma boxes tsid, onid and namespace should be non zero, while on non-enigma + // boxes they are usually all zero. + // The emulator creates a unique channel hash using srvid and enigma namespace or + // srvid, tsid, onid and namespace (in case of namespace without frequency) and + // another weaker (not unique) hash based on every pid of the channel. This universal + // hash should be available on all types of stbs (enigma and non-enigma). + + // Flags inside [namespace] + // + // emu r748- : no namespace, no flag + // emu r749 : 0x80000000 (full namespase), 0xC0000000 (stripped namespace, injected with tsid^onid^ecmpid^0x1FFF) + // emu r752+ : 0xA0000000 (pure namespace, either full, stripped, or null) + + // Key searches are made in order: + // Highest priority / tightest test first + // Lowest priority / loosest test last + // + // 1st: namespace hash (only on enigma boxes) + // 2nd: universal hash (all box types with emu r752+) + // 3rd: valid tsid, onid combination + // 4th: faulty ecmpid (other than 0x1FFF) + // 5th: reverse order pid (audio, video, pmt pids) + // 6th: standard BISS ecmpid (0x1FFF) + // 7th: default "All Feeds" key + + // If enabled in the webif, a date based key search can be performed. If the expiration + // date has passed, the key is not sent from BissGetKey(). This search method is only + // used in the namespace hash, universal hash and the default "All Feeds" key. + + uint8_t ecmCopy[EMU_MAX_ECM_LEN]; + uint16_t ecmLen = 0, pid = 0; + uint32_t i, ens = 0, hash = 0; + char tmpBuffer1[17], tmpBuffer2[90] = "0", tmpBuffer3[90] = "0"; + + if (ecmDataLen >= 3) + { + ecmLen = GetEcmLen(ecm); + } + + // First try using the unique namespace hash (enigma only) + if (ecmLen >= 13 && ecmLen <= ecmDataLen) // ecmLen >= 13, allow patching the ecmLen for r749 ecms + { + memcpy(ecmCopy, ecm, ecmLen); + ens = b2i(4, ecm + ecmLen - 4); // Namespace will be the last 4 bytes + + if (BissIsValidNamespace(ens)) // An r752+ extended ecm with valid namespace + { + BissUnifyOrbitals(&ens); + i2b_buf(4, ens, ecmCopy + ecmLen - 4); + + for (i = 0; i < 5; i++) // Find key matching hash made with frequency modified to: f+0, then f-1, f+1, f-2, lastly f+2 + { + ecmCopy[ecmLen - 1] = (i & 1) ? ecmCopy[ecmLen - 1] - i : ecmCopy[ecmLen - 1] + i; // frequency +/- 1, 2 MHz + + if (0 != (ens & 0xFFFF)) // Full namespace - Calculate hash with srvid and namespace only + { + i2b_buf(2, srvid, ecmCopy + ecmLen - 6); // Put [srvid] right before [namespace] + hash = crc32(0x2600, ecmCopy + ecmLen - 6, 6); + } + else // Namespace without frequency - Calculate hash with srvid, tsid, onid and namespace + { + i2b_buf(2, srvid, ecmCopy + ecmLen - 10); // Put [srvid] right before [tsid] [onid] [namespace] sequence + hash = crc32(0x2600, ecmCopy + ecmLen - 10, 10); + } + + if (BissGetKey(hash, dw, rdr->emu_datecodedenabled, i == 0 ? 2 : 1)) // Do not print "key not found" for frequency off by 1, 2 + { + memcpy(dw + 8, dw, 8); + return 0; + } + + if (i == 0) // No key found matching our hash: create example SoftCam.Key BISS line for the live log + { + BissAnnotate(tmpBuffer2, sizeof(tmpBuffer2), ecmCopy, ecmLen, hash, 1, rdr->emu_datecodedenabled); + } + + if (0 == (ens & 0xFFFF)) // Namespace without frequency - Do not iterate + { + break; + } + } + } + + if ((ens & 0xA0000000) == 0x80000000) // r749 ecms only (exclude r752+ ecms) + { + cs_log("Hey! Network buddy, you need to upgrade your OSCam-Emu"); + ecmCopy[ecmLen] = 0xA0; // Patch ecm to look like r752+ + ecmLen += 4; + ecmDataLen += 4; + } + } + + // Try using the universal channel hash (namespace not available) + if (ecmLen >= 17 && ecmLen <= ecmDataLen) // ecmLen >= 17, length of r749 ecms has been patched to match r752+ ecms + { + ens = b2i(4, ecmCopy + ecmLen - 4); // Namespace will be last 4 bytes + + if ((ens & 0xE0000000) == 0xA0000000) // We have an r752+ style ecm which contains pmtpid + { + memcpy(ecmCopy, ecm, ecmLen - 8); // Make a new ecmCopy from the original ecm as the old ecmCopy may be altered in namespace hash (skip [tsid] [onid] [namespace]) + hash = crc32(0x2600, ecmCopy + 3, ecmLen - 3 - 8); // ecmCopy doesn't have [tsid] [onid] [namespace] part + + if (BissGetKey(hash, dw, rdr->emu_datecodedenabled, 2)) // Key found + { + memcpy(dw + 8, dw, 8); + return 0; + } + + // No key found matching our hash: create example SoftCam.Key BISS line for the live log + BissAnnotate(tmpBuffer3, sizeof(tmpBuffer3), ecmCopy, ecmLen, hash, 0, rdr->emu_datecodedenabled); + } + } + + // Try using only [tsid][onid] (useful when many channels on a transpoder use the same key) + if (ecmLen >= 17 && ecmLen <= ecmDataLen) // ecmLen >= 17, length of r749 ecms has been patched to match r752+ ecms + { + ens = b2i(4, ecmCopy + ecmLen - 4); // Namespace will be last 4 bytes + + // We have an r752+ style ecm with stripped namespace, thus a valid [tsid][onid] combo to use as provider + if ((ens & 0xE000FFFF) == 0xA0000000 && BissGetKey(b2i(4, ecm + ecmLen - 8), dw, 0, 2)) + { + memcpy(dw + 8, dw, 8); + return 0; + } + + if ((ens & 0xE0000000) == 0xA0000000) // Strip [tsid] [onid] [namespace] on r752+ ecms + { + ecmLen -= 8; + ecmDataLen -= 8; + } + } + + // Try using ecmpid if it seems to be faulty (should be 0x1FFF always for BISS) + if (ecmpid != 0x1FFF && ecmpid != 0) + { + if (BissGetKey((srvid << 16) | ecmpid, dw, 0, 2)) + { + memcpy(dw + 8, dw, 8); + return 0; + } + } + + // Try to get the pid from oscam's fake ecm (only search [pid1] [pid2] ... [pidx] to be compatible with emu r748-) + if (ecmLen >= 7 && ecmLen <= ecmDataLen) // Use >= for radio channels with just one (audio) pid + { + // Reverse search order: last pid in list first + // Better identifies channels where they share identical video pid but have variable counts of audio pids + for (i = ecmLen - 2; i >= 5; i -= 2) + { + pid = b2i(2, ecm + i); + + if (BissGetKey((srvid << 16) | pid, dw, 0, 2)) + { + memcpy(dw + 8, dw, 8); + return 0; + } + } + } + + // Try using the standard BISS ecm pid + if (ecmpid == 0x1FFF || ecmpid == 0) + { + if (BissGetKey((srvid << 16) | 0x1FFF, dw, 0, 2)) + { + memcpy(dw + 8, dw, 8); + return 0; + } + } + + // Default BISS key for events with many feeds sharing same key + if (ecmpid != 0 && BissGetKey(0xA11FEED5, dw, rdr->emu_datecodedenabled, 2)) // Limit to local ecms, block netwotk ecms + { + memcpy(dw + 8, dw, 8); + cs_hexdump(0, dw, 8, tmpBuffer1, sizeof(tmpBuffer1)); + cs_log("No specific match found. Using 'All Feeds' key: %s", tmpBuffer1); + return 0; + } + + // Print example key lines for available hash search methods, if no key is found + if (strncmp(tmpBuffer2, "0", 2)) cs_log("Example key based on namespace hash: %s", tmpBuffer2); + if (strncmp(tmpBuffer3, "0", 2)) cs_log("Example key based on universal hash: %s", tmpBuffer3); + + // Check if universal hash is common and warn user + if (BissIsCommonHash(hash)) cs_log("Feed has commonly used pids, universal hash clashes in SoftCam.Key are likely!"); + + return 2; +} + +// PowerVu Emu +static uint8_t PowervuCrc8Calc(uint8_t *data, int len) +{ + int i; + uint8_t crc = 0x00; + uint8_t crcTable[256] = {0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3}; + + for(i = 0; i < len; i++) + { + crc = crcTable[data[i] ^ crc]; + } + + return crc; +} + +static void PowervuPadData(uint8_t *data, int len, uint8_t *dataPadded) +{ + int i; + uint8_t pad[] = {0x01, 0x02, 0x22, 0x04, 0x20, 0x2A, 0x1F, 0x03, 0x04, 0x06, 0x02, 0x0C, 0x2B, 0x2B, 0x01, 0x7B}; + + for(i = 0; i < len; i++) + { + dataPadded[i] = data[i]; + } + + dataPadded[len] = 0x01; + + for(i = len + 1; i < 0x2F; i++) + { + dataPadded[i] = 0x00; + } + + dataPadded[0x2F] = len; + + for(i = 0; i < 0x10; i++) + { + dataPadded[0x30 + i] = pad[i]; + } +} + +static void PowervuHashMode01CustomMD5(uint8_t *data, uint8_t *hash) +{ + int i, j, s; + uint32_t a, b, c, d, f, g; + + uint32_t T[] = {0x783E16F6, 0xC267AC13, 0xA2B17F12, 0x6B8A31A4, 0xF910654D, 0xB702DBCB, 0x266CEF60, 0x5145E47C, + 0xB92E00D6, 0xE80A4A64, 0x8A07FA77, 0xBA7D89A9, 0xEBED8022, 0x653AAF2B, 0xF118B03B, 0x6CC16544, + 0x96EB6583, 0xF4E27E35, 0x1ABB119E, 0x068D3EF2, 0xDAEAA8A5, 0x3C312A3D, 0x59538388, 0xA100772F, + 0xAB0165CE, 0x979959E7, 0x5DD8F53D, 0x189662BA, 0xFD021A9C, 0x6BC2D338, 0x1EFF667E, 0x40C66888, + 0x6E9F07FF, 0x0CEF442F, 0x82D20190, 0x4E8CAEAC, 0x0F7CB305, 0x2E73FBE7, 0x1CE884A2, 0x7A60BD52, + 0xC348B30D, 0x081CE3AA, 0xA12220E7, 0x38C7EC79, 0xCBD8DD3A, 0x62B4FBA5, 0xAD2A63DB, 0xE4D0852E, + 0x53DE980F, 0x9C8DDA59, 0xA6B4CEDE, 0xB48A7692, 0x0E2C46A4, 0xEB9367CB, 0x165D72EE, 0x75532B45, + 0xB9CA8E97, 0x08C8837B, 0x966F917B, 0x527515B4, 0xF27A5E5D, 0xB71E6267, 0x7603D7E6, 0x9837DD69}; // CUSTOM T + + uint8_t r[] = {0x06, 0x0A, 0x0F, 0x15, 0x05, 0x09, 0x0E, 0x14, 0x04, 0x0B, 0x10, 0x17, 0x07, 0x0C, 0x11, 0x16}; // STANDARD REORDERED + + uint8_t tIdxInit[] = {0, 1, 5, 0}; // STANDARD + uint8_t tIdxIncr[] = {1, 5, 3, 7}; // STANDARD + + uint32_t h[] = {0xEAD81D2E, 0xCE4DC6E9, 0xF9B5C301, 0x10325476}; // CUSTOM h0, h1, h2 STANDARD h3 + uint32_t dataLongs[0x10]; + + for(i = 0; i < 0x10; i++) + { + dataLongs[i] = (data[4 * i + 0] << 0) + (data[4 * i + 1] << 8) + (data[4 * i + 2] << 16) + (data[4 * i + 3] << 24); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + + for(i = 0; i < 4; i++) + { + g = tIdxInit[i]; + + for(j = 0; j < 16; j++) + { + if(i == 0) + { + f = (b & c) | (~b & d); + } + else if(i == 1) + { + f = (b & d) | (~d & c); + } + else if(i == 2) + { + f = (b ^ c ^ d); + } + else if (i == 3) + { + f = (~d | b) ^ c; + } + + f = dataLongs[g] + a + T[16 * i + j] + f; + + s = r[4 * i + (j & 3)]; + f = (f << s) | (f >> (32 - s)); + + a = d; + d = c; + c = b; + b += f; + + g = (g + tIdxIncr[i]) & 0xF; + } + } + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + + for(i = 0; i < 4; i++) + { + hash[4 * i + 0] = h[i] >> 0; + hash[4 * i + 1] = h[i] >> 8; + hash[4 * i + 2] = h[i] >> 16; + hash[4 * i + 3] = h[i] >> 24; + } +} + +static void PowervuHashMode02(uint8_t *data, uint8_t *hash) +{ + int i; + uint32_t a, b, c, d, e, f, tmp; + uint32_t h[] = {0x81887F3A, 0x36CCA480, 0x99056FB1, 0x79705BAE}; + uint32_t dataLongs[0x50]; + + for (i = 0; i < 0x10; i++) + { + dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) + (data[4 * i + 2] << 8) + (data[4 * i + 3] << 0); + } + + for (i = 0; i < 0x40; i++) + { + dataLongs[0x10 + i] = dataLongs[0x10 + i - 2]; + dataLongs[0x10 + i] ^= dataLongs[0x10 + i - 7]; + dataLongs[0x10 + i] ^= dataLongs[0x10 + i - 13]; + dataLongs[0x10 + i] ^= dataLongs[0x10 + i - 16]; + } + + a = dataLongs[0]; + b = dataLongs[1]; + c = dataLongs[2]; + d = dataLongs[3]; + e = dataLongs[4]; + + for (i = 0; i < 0x50; i++) + { + if (i < 0x15) f = (b & c) | (~b & d); + else if (i < 0x28) f = (b ^ c ^ d); + else if (i < 0x3D) f = (b & c) | (c & d) | (b & d); + else if (i < 0x50) f = (b ^ c ^ d); + + tmp = a; + a = e + f + (a << 5) + (a >> 27) + h[i / 0x14] + dataLongs[i]; + e = d; + d = c; + c = (b << 30) + (b >> 2); + b = tmp; + } + + dataLongs[0] += a; + dataLongs[1] += b; + dataLongs[2] += c; + dataLongs[3] += d; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = dataLongs[i] >> 24; + hash[4 * i + 1] = dataLongs[i] >> 16; + hash[4 * i + 2] = dataLongs[i] >> 8; + hash[4 * i + 3] = dataLongs[i] >> 0; + } +} + +static void PowervuHashMode03(uint8_t *data, uint8_t *hash) +{ + int i, j, k, s, s2, tmp; + uint32_t a, b, c, d, f, g; + uint32_t a2, b2, c2, d2, f2, g2; + + uint32_t T[] = { 0xC88F3F2E, 0x967506BA, 0xDA877A7B, 0x0DECCDFE }; + uint32_t T2[] = { 0x01F42668, 0x39C7CDA5, 0xD490E2FE, 0x9965235D }; + + uint8_t r[] = { 0x0B, 0x0E, 0x0F, 0x0C, 0x05, 0x08, 0x07, 0x09, 0x0B, 0x0D, 0x0E, 0x0F, 0x06, 0x07, 0x09, 0x08, + 0x07, 0x06, 0x08, 0x0D, 0x0B, 0x09, 0x07, 0x0F, 0x07, 0x0C, 0x0F, 0x09, 0x0B, 0x07, 0x0D, 0x0C }; + + uint8_t tIdxIncr[] = { 0x07, 0x04, 0x0D, 0x01, 0x0A, 0x06, 0x0F, 0x03, 0x0C, 0x00, 0x09, 0x05, 0x02, 0x0E, 0x0B, 0x08, + 0x05, 0x0D, 0x02, 0x00, 0x04, 0x09, 0x03, 0x08, 0x01, 0x0A, 0x07, 0x0B, 0x06, 0x0F, 0x0C, 0x0E }; + + uint32_t h[] = { 0xC8616857, 0x9D3F5B8E, 0x4D7B8F76, 0x97BC8D80 }; + + uint32_t dataLongs[0x50]; + uint32_t result[4]; + + for (i = 0; i < 0x10; i++) + { + dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) + (data[4 * i + 2] << 8) + (data[4 * i + 3] << 0); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + + a2 = h[3]; + b2 = h[2]; + c2 = h[1]; + d2 = h[0]; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 16; j++) + { + tmp = j; + + for (k = 0; k < i; k++) + { + tmp = tIdxIncr[tmp]; + } + + g = 0x0F - tmp; + g2 = tmp; + + if (i == 0) f = (b & d) | (~d & c); + else if (i == 1) f = (~c | b) ^ d; + else if (i == 2) f = (~b & d) | (b & c); + else if (i == 3) f = (b ^ c ^ d); + + if (i == 0) f2 = (b2 ^ c2 ^ d2); + else if (i == 1) f2 = (~b2 & d2) | (b2 & c2); + else if (i == 2) f2 = (~c2 | b2) ^ d2; + else if (i == 3) f2 = (b2 & d2) | (~d2 & c2); + + f = dataLongs[g] + a + T[i] + f; + s = r[0x0F + (((i & 1) ^ 1) << 4) - j]; + f = (f << s) | (f >> (32 - s)); + + f2 = dataLongs[g2] + a2 + T2[i] + f2; + s2 = r[((i & 1) << 4) + j]; + f2 = (f2 << s2) | (f2 >> (32 - s2)); + + a = d; + d = (c << 10) | (c >> 22); + c = b; + b = f; + + a2 = d2; + d2 = (c2 << 10) | (c2 >> 22); + c2 = b2; + b2 = f2; + } + } + + result[0] = h[3] + b + a2; + result[1] = h[2] + c + b2; + result[2] = h[1] + d + c2; + result[3] = h[0] + a + d2; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = result[i] >> 0; + hash[4 * i + 1] = result[i] >> 8; + hash[4 * i + 2] = result[i] >> 16; + hash[4 * i + 3] = result[i] >> 24; + } +} + +uint8_t table04[] = { 0x02, 0x03, 0x07, 0x0B, 0x0D, 0x08, 0x00, 0x01, 0x2B, 0x2D, 0x28, 0x20, 0x21, 0x0A, 0x0C, 0x0E, + 0x22, 0x36, 0x23, 0x27, 0x29, 0x24, 0x25, 0x26, 0x2A, 0x3C, 0x3E, 0x3F, 0x0F, 0x2C, 0x2E, 0x2F, + 0x12, 0x13, 0x17, 0x1B, 0x1C, 0x18, 0x10, 0x11, 0x19, 0x14, 0x15, 0x16, 0x1A, 0x09, 0x04, 0x05, + 0x32, 0x33, 0x37, 0x3B, 0x06, 0x1C, 0x1E, 0x1F, 0x3D, 0x38, 0x30, 0x31, 0x39, 0x34, 0x35, 0x3A }; + +uint8_t table05[] = { 0x08, 0x09, 0x0A, 0x03, 0x04, 0x3F, 0x27, 0x28, 0x29, 0x2A, 0x05, 0x0B, 0x1B, 0x1C, 0x1C, 0x1E, + 0x20, 0x0C, 0x0D, 0x22, 0x23, 0x24, 0x00, 0x01, 0x02, 0x06, 0x07, 0x25, 0x26, 0x0E, 0x0F, 0x21, + 0x10, 0x11, 0x12, 0x2E, 0x2F, 0x13, 0x14, 0x15, 0x2B, 0x2C, 0x2D, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x30, 0x31, 0x37, 0x3B, 0x3C, 0x3D, 0x3E, 0x1F, 0x38, 0x39, 0x32, 0x33, 0x34, 0x35, 0x36, 0x3A }; + +uint8_t table06[] = { 0x00, 0x01, 0x02, 0x06, 0x07, 0x08, 0x03, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x04, 0x05, 0x09, 0x0D, + 0x20, 0x21, 0x22, 0x26, 0x27, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x10, 0x11, 0x12, 0x16, 0x17, 0x28, + 0x18, 0x13, 0x14, 0x15, 0x19, 0x1C, 0x1A, 0x1B, 0x1C, 0x1E, 0x1F, 0x23, 0x24, 0x25, 0x29, 0x2D, + 0x30, 0x31, 0x32, 0x36, 0x37, 0x38, 0x33, 0x34, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x35, 0x39, 0x3D }; + +uint8_t table09[] = { 0x20, 0x21, 0x24, 0x22, 0x23, 0x2A, 0x2B, 0x33, 0x35, 0x38, 0x39, 0x36, 0x2D, 0x2C, 0x2E, 0x2F, + 0x00, 0x01, 0x04, 0x02, 0x25, 0x28, 0x08, 0x09, 0x06, 0x07, 0x0A, 0x0B, 0x0D, 0x0C, 0x0E, 0x0F, + 0x10, 0x11, 0x14, 0x12, 0x13, 0x15, 0x19, 0x16, 0x29, 0x26, 0x03, 0x17, 0x1A, 0x1C, 0x1C, 0x1E, + 0x30, 0x31, 0x34, 0x32, 0x37, 0x3A, 0x3B, 0x3D, 0x3C, 0x3E, 0x3F, 0x1B, 0x05, 0x18, 0x27, 0x1F }; + +static void PowervuHashModes04to0ATables(uint8_t *data, uint8_t *hash, uint8_t *table) +{ + int i; + + for (i = 0; i < 0x10; i++) + { + hash[i] = table[i]; + hash[i] ^= data[table[i]]; + hash[i] ^= table[0x10 + i]; + hash[i] ^= data[table[0x10 + i]]; + hash[i] ^= table[0x20 + i]; + hash[i] ^= data[table[0x20 + i]]; + hash[i] ^= table[0x30 + i]; + hash[i] ^= data[table[0x30 + i]]; + } +} + +static void PowervuCreateHash(uint8_t *data, int len, uint8_t *hash, int mode) +{ + uint8_t dataPadded[0x40]; + + PowervuPadData(data, len, dataPadded); + + switch(mode) + { + case 1: + PowervuHashMode01CustomMD5(dataPadded, hash); + break; + + case 2: + PowervuHashMode02(dataPadded, hash); + break; + + case 3: + PowervuHashMode03(dataPadded, hash); + break; + + case 4: + PowervuHashModes04to0ATables(dataPadded, hash, table04); + break; + + case 5: + PowervuHashModes04to0ATables(dataPadded, hash, table05); + break; + + case 6: + PowervuHashModes04to0ATables(dataPadded, hash, table06); + break; + + case 9: + PowervuHashModes04to0ATables(dataPadded, hash, table09); + break; + + default: + cs_log("A new hash mode [%d] is in use.", mode); + break; + } +} + +static void PowervuCreateDataEcmEmm(uint8_t *emmEcm, uint8_t *pos, int lenHeader, int len, uint8_t *data) +{ + int i; + + for(i = 0; i < len; i++) + { + data[i] = emmEcm[lenHeader + pos[i]]; + } +} + +static uint8_t PowervuCreateDataCw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, uint8_t val, uint8_t *seedEcmCw, uint8_t *data) +{ + int i; + + for(i = 0; i < lenSeed; i++) + { + data[i] = seed[i]; + } + + for(i = 0; i < 7; i++) + { + data[lenSeed + i] = baseCw[i]; + } + + data[lenSeed + 7] = val; + + for(i = 0; i < 16; i++) + { + data[lenSeed + 7 + 1 + i] = seedEcmCw[i]; + } + + return lenSeed + 7 + 1 + 0x10; +} + +static uint8_t PowervuUnmaskEcm(uint8_t *ecm, uint8_t *seedEcmCw) +{ + int i, l; + + uint8_t sourcePos[] = {0x04, 0x05, 0x06, 0x07, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x17, 0x1C, 0x1D, 0x1F, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x29, 0x2C, 0x2D, 0x2E}; + uint8_t destPos[] = {0x08, 0x09, 0x11, 0x18, 0x19, 0x1A, 0x1B, 0x1E, 0x20, 0x21, 0x22, 0x28, 0x2A, 0x2B, 0x2F, 0x30}; + uint8_t seedCwPos[] = {0x07, 0x0A, 0x04, 0x0D, 0x05, 0x0E, 0x06, 0x0B, 0x10, 0x0C, 0x0F}; + + uint8_t data[0x18]; + uint8_t mask[0x10]; + uint8_t hashModeEcm; + uint8_t hashModeCw; + uint32_t crc; + + // Create seed for CW decryption + memset(seedEcmCw, 0, 0x10); + + int extraBytesLen = ecm[9]; + int startOffset = extraBytesLen + 0x0A; + + for (i = 0; i < 0x0B; i++) + { + seedEcmCw[i] = ecm[startOffset + seedCwPos[i]]; + } + + // Read hash mode CW + hashModeCw = ecm[28 + extraBytesLen] ^ PowervuCrc8Calc(seedEcmCw, 0x10); + + // Create mask for ECM decryption + PowervuCreateDataEcmEmm(ecm, sourcePos, startOffset, 0x18, data); + + hashModeEcm = ecm[8] ^ PowervuCrc8Calc(data, 0x18); + + PowervuCreateHash(data, 0x18, mask, hashModeEcm); + + // Fix header + ecm[3] &= 0x0F; + ecm[3] |= 0x30; + ecm[8] = 0x00; + ecm[28 + extraBytesLen] = 0x00; + + // Unmask body + for (i = 0; i < 0x10; i++) + { + ecm[startOffset + destPos[i]] ^= mask[i & 0x0F]; + } + + // Fix CRC (optional) + l = (((ecm[1] << 8) + ecm[2]) & 0xFFF) + 3 - 4; + + crc = fletcher_crc32(ecm, l); + + ecm[l + 0] = crc >> 24; + ecm[l + 1] = crc >> 16; + ecm[l + 2] = crc >> 8; + ecm[l + 3] = crc >> 0; + + return hashModeCw; +} + +static void PowervuCreateCw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, uint8_t val, + uint8_t *seedEcmCw, uint8_t *cw, int modeDesCsa, int hashMode) +{ + uint8_t tableFixParity[] = {0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07, 0x08, 0x08, 0x0B, 0x0B, 0x0D, 0x0D, 0x0E, 0x0E, + 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16, 0x19, 0x19, 0x1A, 0x1A, 0x1C, 0x1C, 0x1F, 0x1F, + 0x20, 0x20, 0x23, 0x23, 0x25, 0x25, 0x26, 0x26, 0x29, 0x29, 0x2A, 0x2A, 0x2C, 0x2C, 0x2F, 0x2F, + 0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37, 0x38, 0x38, 0x3B, 0x3B, 0x3D, 0x3D, 0x3E, 0x3E, + 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46, 0x49, 0x49, 0x4A, 0x4A, 0x4C, 0x4C, 0x4F, 0x4F, + 0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57, 0x58, 0x58, 0x5B, 0x5B, 0x5D, 0x5D, 0x5E, 0x5E, + 0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67, 0x68, 0x68, 0x6B, 0x6B, 0x6D, 0x6D, 0x6E, 0x6E, + 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76, 0x79, 0x79, 0x7A, 0x7A, 0x7C, 0x7C, 0x7F, 0x7F, + 0x80, 0x80, 0x83, 0x83, 0x85, 0x85, 0x86, 0x86, 0x89, 0x89, 0x8A, 0x8A, 0x8C, 0x8C, 0x8F, 0x8F, + 0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97, 0x98, 0x98, 0x9B, 0x9B, 0x9D, 0x9D, 0x9E, 0x9E, + 0xA1, 0xA1, 0xA2, 0xA2, 0xA4, 0xA4, 0xA7, 0xA7, 0xA8, 0xA8, 0xAB, 0xAB, 0xAD, 0xAD, 0xAE, 0xAE, + 0xB0, 0xB0, 0xB3, 0xB3, 0xB5, 0xB5, 0xB6, 0xB6, 0xB9, 0xB9, 0xBA, 0xBA, 0xBC, 0xBC, 0xBF, 0xBF, + 0xC1, 0xC1, 0xC2, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7, 0xC8, 0xC8, 0xCB, 0xCB, 0xCD, 0xCD, 0xCE, 0xCE, + 0xD0, 0xD0, 0xD3, 0xD3, 0xD5, 0xD5, 0xD6, 0xD6, 0xD9, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDF, 0xDF, + 0xE0, 0xE0, 0xE3, 0xE3, 0xE5, 0xE5, 0xE6, 0xE6, 0xE9, 0xE9, 0xEA, 0xEA, 0xEC, 0xEC, 0xEF, 0xEF, + 0xF1, 0xF1, 0xF2, 0xF2, 0xF4, 0xF4, 0xF7, 0xF7, 0xF8, 0xF8, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE}; + + uint8_t data[0x1C]; + uint8_t hash[0x10]; + uint8_t lenData; + int i; + + lenData = PowervuCreateDataCw(seed, lenSeed, baseCw, val, seedEcmCw, data); + PowervuCreateHash(data, lenData, hash, hashMode); + + for(i = 0; i < 8; i++) + { + cw[i] = hash[i]; + } + + if(modeDesCsa == 0) // DES - Fix Parity Bits + { + for(i = 0; i < 8; i++) + { + cw[i] = tableFixParity[cw[i]]; + } + } + else if(modeDesCsa == 1) // CSA - Fix Checksums + { + cw[3] = cw[0] + cw[1] + cw[2]; + cw[7] = cw[4] + cw[5] + cw[6]; + } +} + +static int8_t GetPowervuKey(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey, uint32_t keyRef) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + if(FindKey('P', ident, 0xFFFF0000, keyStr, buf, keyLength, isCriticalKey, keyRef, 0, NULL)) { + return 1; + } + + return 0; +} + +static int8_t GetPowervuEmmKey(uint8_t *buf, uint32_t ident, char *keyName, uint32_t keyLength, uint8_t isCriticalKey, uint32_t keyRef, uint32_t *getProvider) +{ + if(FindKey('P', ident, 0xFFFFFFFF, keyName, buf, keyLength, isCriticalKey, keyRef, 0, getProvider)) { + return 1; + } + + return 0; +} + +static const uint8_t PowerVu_A0_S_1[16] = {0x33, 0xA4, 0x44, 0x3C, 0xCA, 0x2E, 0x75, 0x7B, 0xBC, 0xE6, 0xE5, 0x35, 0xA0, 0x55, 0xC9, 0xA2}; +static const uint8_t PowerVu_A0_S_2[16] = {0x5A, 0xB0, 0x2C, 0xBC, 0xDA, 0x32, 0xE6, 0x92, 0x40, 0x53, 0x6E, 0xF9, 0x69, 0x11, 0x1E, 0xFB}; +static const uint8_t PowerVu_A0_S_3[16] = {0x4E, 0x18, 0x9B, 0x19, 0x79, 0xFB, 0x01, 0xFA, 0xE3, 0xE1, 0x28, 0x3D, 0x32, 0xE4, 0x92, 0xEA}; +static const uint8_t PowerVu_A0_S_4[16] = {0x05, 0x6F, 0x37, 0x66, 0x35, 0xE1, 0x58, 0xD0, 0xB4, 0x6A, 0x97, 0xAE, 0xD8, 0x91, 0x27, 0x56}; +static const uint8_t PowerVu_A0_S_5[16] = {0x7B, 0x26, 0xAD, 0x34, 0x3D, 0x77, 0x39, 0x51, 0xE0, 0xE0, 0x48, 0x8C, 0x39, 0xF5, 0xE8, 0x47}; +static const uint8_t PowerVu_A0_S_6[16] = {0x74, 0xFA, 0x4D, 0x79, 0x42, 0x39, 0xD1, 0xA4, 0x99, 0xA3, 0x97, 0x07, 0xDF, 0x14, 0x3A, 0xC4}; +static const uint8_t PowerVu_A0_S_7[16] = {0xC6, 0x1E, 0x3C, 0x24, 0x11, 0x08, 0x5D, 0x6A, 0xEB, 0x97, 0xB9, 0x25, 0xA7, 0xFA, 0xE9, 0x1A}; +static const uint8_t PowerVu_A0_S_8[16] = {0x9A, 0xAD, 0x72, 0xD7, 0x7C, 0x68, 0x3B, 0x55, 0x1D, 0x4A, 0xA2, 0xB0, 0x38, 0xB9, 0x56, 0xD0}; +static const uint8_t PowerVu_A0_S_9[32] = {0x61, 0xDA, 0x5F, 0xB7, 0xEB, 0xC6, 0x3F, 0x6C, 0x09, 0xF3, 0x64, 0x38, 0x33, 0x08, 0xAA, 0x15, + 0xCC, 0xEF, 0x22, 0x64, 0x01, 0x2C, 0x12, 0xDE, 0xF4, 0x6E, 0x3C, 0xCD, 0x1A, 0x64, 0x63, 0x7C + }; + +static const uint8_t PowerVu_00_S_1[16] = {0x97, 0x13, 0xEB, 0x6B, 0x04, 0x5E, 0x60, 0x3A, 0xD9, 0xCC, 0x91, 0xC2, 0x5A, 0xFD, 0xBA, 0x0C}; +static const uint8_t PowerVu_00_S_2[16] = {0x61, 0x3C, 0x03, 0xB0, 0xB5, 0x6F, 0xF8, 0x01, 0xED, 0xE0, 0xE5, 0xF3, 0x78, 0x0F, 0x0A, 0x73}; +static const uint8_t PowerVu_00_S_3[16] = {0xFD, 0xDF, 0xD2, 0x97, 0x06, 0x14, 0x91, 0xB5, 0x36, 0xAD, 0xBC, 0xE1, 0xB3, 0x00, 0x66, 0x41}; +static const uint8_t PowerVu_00_S_4[16] = {0x8B, 0xD9, 0x18, 0x0A, 0xED, 0xEE, 0x61, 0x34, 0x1A, 0x79, 0x80, 0x8C, 0x1E, 0x7F, 0xC5, 0x9F}; +static const uint8_t PowerVu_00_S_5[16] = {0xB0, 0xA1, 0xF2, 0xB8, 0xEA, 0x72, 0xDD, 0xD3, 0x30, 0x65, 0x2B, 0x1E, 0xE9, 0xE1, 0x45, 0x29}; +static const uint8_t PowerVu_00_S_6[16] = {0x5D, 0xCA, 0x53, 0x75, 0xB2, 0x24, 0xCE, 0xAF, 0x21, 0x54, 0x9E, 0xBE, 0x02, 0xA9, 0x4C, 0x5D}; +static const uint8_t PowerVu_00_S_7[16] = {0x42, 0x66, 0x72, 0x83, 0x1B, 0x2D, 0x22, 0xC9, 0xF8, 0x4D, 0xBA, 0xCD, 0xBB, 0x20, 0xBD, 0x6B}; +static const uint8_t PowerVu_00_S_8[16] = {0xC4, 0x0C, 0x6B, 0xD3, 0x6D, 0x94, 0x7E, 0x53, 0xCE, 0x96, 0xAC, 0x40, 0x2C, 0x7A, 0xD3, 0xA9}; +static const uint8_t PowerVu_00_S_9[32] = {0x31, 0x82, 0x4F, 0x9B, 0xCB, 0x6F, 0x9D, 0xB7, 0xAE, 0x68, 0x0B, 0xA0, 0x93, 0x15, 0x32, 0xE2, + 0xED, 0xE9, 0x47, 0x29, 0xC2, 0xA8, 0x92, 0xEF, 0xBA, 0x27, 0x22, 0x57, 0x76, 0x54, 0xC0, 0x59, + }; + +static uint8_t PowervuSbox(uint8_t *input, uint8_t mode) +{ + uint8_t s_index, bit, last_index, last_bit; + uint8_t const *Sbox1, *Sbox2, *Sbox3, *Sbox4, *Sbox5, *Sbox6, *Sbox7, *Sbox8, *Sbox9; + + if(mode) + { + Sbox1 = PowerVu_A0_S_1; + Sbox2 = PowerVu_A0_S_2; + Sbox3 = PowerVu_A0_S_3; + Sbox4 = PowerVu_A0_S_4; + Sbox5 = PowerVu_A0_S_5; + Sbox6 = PowerVu_A0_S_6; + Sbox7 = PowerVu_A0_S_7; + Sbox8 = PowerVu_A0_S_8; + Sbox9 = PowerVu_A0_S_9; + } + else + { + Sbox1 = PowerVu_00_S_1; + Sbox2 = PowerVu_00_S_2; + Sbox3 = PowerVu_00_S_3; + Sbox4 = PowerVu_00_S_4; + Sbox5 = PowerVu_00_S_5; + Sbox6 = PowerVu_00_S_6; + Sbox7 = PowerVu_00_S_7; + Sbox8 = PowerVu_00_S_8; + Sbox9 = PowerVu_00_S_9; + } + + bit = (GetBit(input[2],0)<<2) | (GetBit(input[3],4)<<1) | (GetBit(input[5],3)); + s_index = (GetBit(input[0],0)<<3) | (GetBit(input[2],6)<<2) | (GetBit(input[2],4)<<1) | (GetBit(input[5],7)); + last_bit = GetBit(Sbox1[s_index],7-bit); + + bit = (GetBit(input[5],0)<<2) | (GetBit(input[4],0)<<1) | (GetBit(input[6],2)); + s_index = (GetBit(input[2],1)<<3) | (GetBit(input[2],2)<<2) | (GetBit(input[5],5)<<1) | (GetBit(input[5],1)); + last_bit = last_bit | (GetBit(Sbox2[s_index],7-bit)<<1); + + bit = (GetBit(input[6],0)<<2) | (GetBit(input[1],7)<<1) | (GetBit(input[6],7)); + s_index = (GetBit(input[1],3)<<3) | (GetBit(input[3],7)<<2) | (GetBit(input[1],5)<<1) | (GetBit(input[5],2)); + last_bit = last_bit | (GetBit(Sbox3[s_index], 7-bit)<<2); + + bit = (GetBit(input[1],0)<<2) | (GetBit(input[2],7)<<1) | (GetBit(input[2],5)); + s_index = (GetBit(input[6],3)<<3) | (GetBit(input[6],4)<<2) | (GetBit(input[6],6)<<1) | (GetBit(input[3],5)); + last_index = GetBit(Sbox4[s_index], 7-bit); + + bit = (GetBit(input[3],3)<<2) | (GetBit(input[4],6)<<1) | (GetBit(input[3],2)); + s_index = (GetBit(input[3],1)<<3) | (GetBit(input[4],5)<<2) | (GetBit(input[3],0)<<1) | (GetBit(input[4],7)); + last_index = last_index | (GetBit(Sbox5[s_index], 7-bit)<<1); + + bit = (GetBit(input[5],4)<<2) | (GetBit(input[4],4)<<1) | (GetBit(input[1],2)); + s_index = (GetBit(input[2],3)<<3) | (GetBit(input[6],5)<<2) | (GetBit(input[1],4)<<1) | (GetBit(input[4],1)); + last_index = last_index | (GetBit(Sbox6[s_index], 7-bit)<<2); + + bit = (GetBit(input[0],6)<<2) | (GetBit(input[0],7)<<1) | (GetBit(input[0],4)); + s_index = (GetBit(input[0],5)<<3) | (GetBit(input[0],3)<<2) | (GetBit(input[0],1)<<1) | (GetBit(input[0],2)); + last_index = last_index | (GetBit(Sbox7[s_index], 7-bit)<<3); + + bit = (GetBit(input[4],2)<<2) | (GetBit(input[4],3)<<1) | (GetBit(input[1],1)); + s_index = (GetBit(input[1],6)<<3) | (GetBit(input[6],1)<<2) | (GetBit(input[5],6)<<1) | (GetBit(input[3],6)); + last_index = last_index | (GetBit(Sbox8[s_index], 7-bit)<<4); + + return (GetBit(Sbox9[last_index&0x1f],7-last_bit)&1) ? 1: 0; +} + +static void PowervuDecrypt(uint8_t *data, uint32_t length, uint8_t *key, uint8_t sbox) +{ + uint32_t i; + int32_t j, k; + uint8_t curByte, tmpBit; + + for(i = 0; i < length; i++) + { + curByte = data[i]; + + for(j = 7; j >= 0; j--) + { + data[i] = SetBit(data[i], j, (GetBit(curByte, j)^PowervuSbox(key, sbox))^GetBit(key[0], 7)); + + tmpBit = GetBit(data[i], j)^(GetBit(key[6], 0)); + if (tmpBit) + { + key[3] ^= 0x10; + } + + for (k = 6; k > 0; k--) + { + key[k] = (key[k]>>1) | (key[k-1]<<7); + } + key[0] = (key[0]>>1); + + key[0] = SetBit(key[0], 7, tmpBit); + } + } +} + +#define PVU_CW_VID 0 // VIDeo +#define PVU_CW_HSD 1 // High Speed Data +#define PVU_CW_A1 2 // Audio 1 +#define PVU_CW_A2 3 // Audio 2 +#define PVU_CW_A3 4 // Audio 3 +#define PVU_CW_A4 5 // Audio 4 +#define PVU_CW_UTL 6 // UTiLity +#define PVU_CW_VBI 7 // Vertical Blanking Interval + +#define PVU_CONVCW_VID_ECM 0x80 // VIDeo +#define PVU_CONVCW_HSD_ECM 0x40 // High Speed Data +#define PVU_CONVCW_A1_ECM 0x20 // Audio 1 +#define PVU_CONVCW_A2_ECM 0x10 // Audio 2 +#define PVU_CONVCW_A3_ECM 0x08 // Audio 3 +#define PVU_CONVCW_A4_ECM 0x04 // Audio 4 +#define PVU_CONVCW_UTL_ECM 0x02 // UTiLity +#define PVU_CONVCW_VBI_ECM 0x01 // Vertical Blanking Interval + +static uint8_t PowervuGetConvcwIndex(uint8_t ecmTag) +{ + switch(ecmTag) + { + case PVU_CONVCW_VID_ECM: + return PVU_CW_VID; + + case PVU_CONVCW_HSD_ECM: + return PVU_CW_HSD; + + case PVU_CONVCW_A1_ECM: + return PVU_CW_A1; + + case PVU_CONVCW_A2_ECM: + return PVU_CW_A2; + + case PVU_CONVCW_A3_ECM: + return PVU_CW_A3; + + case PVU_CONVCW_A4_ECM: + return PVU_CW_A4; + + case PVU_CONVCW_UTL_ECM: + return PVU_CW_UTL; + + case PVU_CONVCW_VBI_ECM: + return PVU_CW_VBI; + + default: + return PVU_CW_VBI; + } +} + +static uint16_t PowervuGetSeedIV(uint8_t seedType, uint8_t *ecm) +{ + switch(seedType) + { + case PVU_CW_VID: + return ((ecm[0x10] & 0x1F) <<3) | 0; + case PVU_CW_HSD: + return ((ecm[0x12] & 0x1F) <<3) | 2; + case PVU_CW_A1: + return ((ecm[0x11] & 0x3F) <<3) | 1; + case PVU_CW_A2: + return ((ecm[0x13] & 0x3F) <<3) | 1; + case PVU_CW_A3: + return ((ecm[0x19] & 0x3F) <<3) | 1; + case PVU_CW_A4: + return ((ecm[0x1A] & 0x3F) <<3) | 1;; + case PVU_CW_UTL: + return ((ecm[0x14] & 0x0F) <<3) | 4; + case PVU_CW_VBI: + return (((ecm[0x15] & 0xF8)>>3)<<3) | 5; + default: + return 0; + } +} + +static uint8_t PowervuExpandSeed(uint8_t seedType, uint8_t *seed) +{ + uint8_t seedLength = 0, i; + + switch(seedType) + { + case PVU_CW_VID: + case PVU_CW_HSD: + seedLength = 4; + break; + case PVU_CW_A1: + case PVU_CW_A2: + case PVU_CW_A3: + case PVU_CW_A4: + seedLength = 3; + break; + case PVU_CW_UTL: + case PVU_CW_VBI: + seedLength = 2; + break; + default: + return seedLength; + } + + for(i=seedLength; i<7; i++) + { + seed[i] = seed[i%seedLength]; + } + + return seedLength; +} + +static void PowervuCalculateSeed(uint8_t seedType, uint8_t *ecm, uint8_t *seedBase, uint8_t *key, uint8_t *seed, uint8_t sbox) +{ + uint16_t tmpSeed; + + tmpSeed = PowervuGetSeedIV(seedType, ecm+23); + seed[0] = (tmpSeed >> 2) & 0xFF; + seed[1] = ((tmpSeed & 0x3) << 6) | (seedBase[0] >> 2); + seed[2] = ( seedBase[0] << 6) | (seedBase[1] >> 2); + seed[3] = ( seedBase[1] << 6) | (seedBase[2] >> 2); + seed[4] = ( seedBase[2] << 6) | (seedBase[3] >> 2); + seed[5] = ( seedBase[3] << 6); + + PowervuDecrypt(seed, 6, key, sbox); + + seed[0] = (seed[1] << 2) | (seed[2] >> 6); + seed[1] = (seed[2] << 2) | (seed[3] >> 6); + seed[2] = (seed[3] << 2) | (seed[4] >> 6); + seed[3] = (seed[4] << 2) | (seed[5] >> 6); +} + +static void PowervuCalculateCw(uint8_t seedType, uint8_t *seed, uint8_t csaUsed, uint8_t *convolvedCw, + uint8_t *cw, uint8_t *baseCw, uint8_t *seedEcmCw, uint8_t hashModeCw, + uint8_t needsUnmasking, uint8_t xorMode) +{ + int32_t k; + uint8_t seedLength, val = 0; + + seedLength = PowervuExpandSeed(seedType, seed); + + if(csaUsed) + { + if(!needsUnmasking || (hashModeCw == 0)) + { + for(k = 0; k < 7; k++) + { + seed[k] ^= baseCw[k]; + } + + cw[0] = seed[0] ^ convolvedCw[0]; + cw[1] = seed[1] ^ convolvedCw[1]; + cw[2] = seed[2] ^ convolvedCw[2]; + cw[3] = seed[3] ^ convolvedCw[3]; + cw[4] = seed[3] ^ convolvedCw[4]; + cw[5] = seed[4] ^ convolvedCw[5]; + cw[6] = seed[5] ^ convolvedCw[6]; + cw[7] = seed[6] ^ convolvedCw[7]; + } + } + else + { + if(xorMode == 0) + { + for(k = 0; k < 7; k++) + { + cw[k] = seed[k] ^ baseCw[k]; + } + } + + if(xorMode == 1) + { + for(k = 0; k < 3; k++) + { + cw[k] = seed[k] ^ baseCw[k]; + } + + for(k = 3; k < 7; k++) + { + cw[k] = baseCw[k]; + } + } + + ExpandDesKey(cw); + } + + if(needsUnmasking && (hashModeCw > 0)) + { + switch(seedType) + { + case PVU_CW_VID: + val = 0; + break; + + case PVU_CW_A1: + case PVU_CW_A2: + case PVU_CW_A3: + case PVU_CW_A4: + val = 1; + break; + + case PVU_CW_HSD: + val = 2; + break; + + case PVU_CW_UTL: + val = 4; + break; + + case PVU_CW_VBI: + val = 5; + break; + } + PowervuCreateCw(seed, seedLength, baseCw, val, seedEcmCw, cw, csaUsed, hashModeCw); + } +} + +#ifdef WITH_EMU +int8_t PowervuECM(uint8_t *ecm, uint8_t *dw, uint16_t srvid, emu_stream_client_key_data *cdata, EXTENDED_CW* cw_ex) +#else +int8_t PowervuECM(uint8_t *ecm, uint8_t *dw, emu_stream_client_key_data *cdata) +#endif +{ + int8_t ret = 1; + uint16_t ecmLen = GetEcmLen(ecm); + uint32_t ecmCrc32; + uint8_t nanoCmd, nanoChecksum, keyType, fixedKey, oddKey, bid, csaUsed; + uint16_t nanoLen; + uint32_t channelId, ecmSrvid, keyIndex; + uint32_t i, j, k; + uint8_t convolvedCw[8][8]; + uint8_t ecmKey[7], tmpEcmKey[7], seedBase[4], baseCw[7], seed[8][8], cw[8][8]; + uint8_t decrypt_ok; + uint8_t ecmPart1[14], ecmPart2[27]; + uint8_t sbox; + uint32_t keyRef1, keyRef2; + uint8_t calculateAllCws; + uint8_t seedEcmCw[0x10]; + uint8_t hashModeCw = 0, needsUnmasking, xorMode; +#ifdef WITH_EMU + uint8_t *dwp; + 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)); +#endif + + if(ecmLen < 7) + { + return 1; + } + + needsUnmasking = (ecm[3] & 0xF0) == 0x50; + + if(needsUnmasking) + { + hashModeCw = PowervuUnmaskEcm(ecm, seedEcmCw); + } + + ecmCrc32 = b2i(4, ecm+ecmLen-4); + + if(fletcher_crc32(ecm, ecmLen-4) != ecmCrc32) + { + return 8; + } + ecmLen -= 4; + + for(i = 0; i < 8; i++) { + memset(convolvedCw[i], 0, 8); + } + + for(i = 3; i+3 < ecmLen; ) { + nanoLen = (((ecm[i] & 0x0f) << 8) | ecm[i+1]); + i += 2; + if(nanoLen > 0) + { + nanoLen--; + } + nanoCmd = ecm[i++]; + if(i+nanoLen > ecmLen) { + return 1; + } + + switch (nanoCmd) { + case 0x27: + if(nanoLen < 15) + { + break; + } + + nanoChecksum = 0; + for(j = 4; j < 15; j++) + { + nanoChecksum += ecm[i+j]; + } + + if(nanoChecksum != 0) + { + break; + } + + keyType = PowervuGetConvcwIndex(ecm[i+4]); + memcpy(convolvedCw[keyType], &ecm[i+6], 8); + break; + + default: + break; + } + i += nanoLen; + } + + for(i = 3; i+3 < ecmLen; ) { + nanoLen = (((ecm[i] & 0x0f) << 8) | ecm[i+1]); + i += 2; + if(nanoLen > 0) + { + nanoLen--; + } + nanoCmd = ecm[i++]; + if(i+nanoLen > ecmLen) { + return 1; + } + + switch (nanoCmd) { + case 0x20: + if(nanoLen < 54) + { + break; + } + + i += ecm[i + 3]; // Extra Data Length + + csaUsed = GetBit(ecm[i+7], 7); + fixedKey = !GetBit(ecm[i+6], 5); + oddKey = GetBit(ecm[i+6], 4); + xorMode = GetBit(ecm[i+6], 0); + bid = (GetBit(ecm[i+7], 1) << 1) | GetBit(ecm[i+7], 0); + sbox = GetBit(ecm[i+6], 3); + + keyIndex = (fixedKey << 3) | (bid << 2) | oddKey; + channelId = b2i(2, ecm+i+23); + ecmSrvid = (channelId >> 4) | ((channelId & 0xF) << 12); + + decrypt_ok = 0; + + memcpy(ecmPart1, ecm+i+8, 14); + memcpy(ecmPart2, ecm+i+27, 27); + + keyRef1 = 0; + keyRef2 = 0; + + do + { + if(!GetPowervuKey(ecmKey, ecmSrvid, '0', keyIndex, 7, 0, keyRef1++)) + { + if(!GetPowervuKey(ecmKey, channelId, '0', keyIndex, 7, 0, keyRef2++)) + { + cs_log("Key not found: P %04X 0%X", ecmSrvid, keyIndex); + return 2; + } + } + + PowervuDecrypt(ecm+i+8, 14, ecmKey, sbox); + if((ecm[i+6] != ecm[i+6+7]) || (ecm[i+6+8] != ecm[i+6+15])) + { + memcpy(ecm+i+8, ecmPart1, 14); + continue; + } + + memcpy(tmpEcmKey, ecmKey, 7); + + PowervuDecrypt(ecm+i+27, 27, ecmKey, sbox); + if((ecm[i+23] != ecm[i+23+29]) || (ecm[i+23+1] != ecm[i+23+30])) + { + memcpy(ecm+i+8, ecmPart1, 14); + memcpy(ecm+i+27, ecmPart2, 27); + continue; + } + + decrypt_ok = 1; + } + while(!decrypt_ok); + + memcpy(seedBase, ecm+i+6+2, 4); + +#ifdef WITH_EMU + if(cdata == NULL) + { + SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); + for(j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++) + { + if(!stream_server_has_ecm[j] && emu_stream_cur_srvid[j] == srvid) + { + update_global_key = 1; + update_global_keys[j] = 1; + } + } + SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex); + } + + if(cdata != NULL || update_global_key || cw_ex != NULL) +#else + if(cdata != NULL) +#endif + { + // Calculate all seeds + for(j = 0; j < 8; j++) + { + memcpy(ecmKey, tmpEcmKey, 7); + PowervuCalculateSeed(j, ecm+i, seedBase, ecmKey, seed[j], sbox); + } + } + else + { + // Calculate only video seed + memcpy(ecmKey, tmpEcmKey, 7); + PowervuCalculateSeed(PVU_CW_VID, ecm+i, seedBase, ecmKey, seed[PVU_CW_VID], sbox); + } + + memcpy(baseCw, ecm+i+6+8, 7); + +#ifdef WITH_EMU + calculateAllCws = cdata != NULL || update_global_key || cw_ex != NULL; +#else + calculateAllCws = cdata != NULL; +#endif + if(calculateAllCws) + { + // Calculate all cws + for(j = 0; j < 8; j++) + { + PowervuCalculateCw(j, seed[j], csaUsed, convolvedCw[j], cw[j], baseCw, + seedEcmCw, hashModeCw, needsUnmasking, xorMode); + + if(csaUsed) + { + for(k = 0; k < 8; k += 4) { + cw[j][k + 3] = ((cw[j][k] + cw[j][k + 1] + cw[j][k + 2]) & 0xff); + } + } + } + +#ifdef WITH_EMU + if(update_global_key) + { + for(j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++) + { + if(update_global_keys[j]) + { + cw_item = (emu_stream_cw_item*)malloc(sizeof(emu_stream_cw_item)); + if(cw_item != NULL) + { + cw_item->csa_used = csaUsed; + cw_item->is_even = ecm[0] == 0x80 ? 1 : 0; + cs_ftime(&cw_item->write_time); + add_ms_to_timeb(&cw_item->write_time, cfg.emu_stream_ecm_delay); + memcpy(cw_item->cw, cw, sizeof(cw)); + ll_append(ll_emu_stream_delayed_keys[j], cw_item); + } + } + } + } + + if(cdata != NULL) + { +#endif + for(j = 0; j < 8; 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]); } + else + { set_odd_control_word(cdata->pvu_csa_ks[j], cw[j]); } + + cdata->pvu_csa_used = 1; + } + else + { + if(ecm[0] == 0x80) + { des_set_key(cw[j], cdata->pvu_des_ks[j][0]); } + else + { des_set_key(cw[j], cdata->pvu_des_ks[j][1]); } + + cdata->pvu_csa_used = 0; + } + } +#ifdef WITH_EMU + } + + if(cw_ex != NULL) + { + cw_ex->mode = CW_MODE_MULTIPLE_CW; + + if(csaUsed) + { + cw_ex->algo = CW_ALGO_CSA; + cw_ex->algo_mode = CW_ALGO_MODE_ECB; + } + else + { + cw_ex->algo = CW_ALGO_DES; + cw_ex->algo_mode = CW_ALGO_MODE_ECB; + } + + for(j = 0; j < 4; j++) + { + dwp = cw_ex->audio[j]; + + memset(dwp, 0, 16); + + if(ecm[0] == 0x80) + { + memcpy(dwp, cw[PVU_CW_A1+j], 8); + + if(csaUsed) + { + for(k = 0; k < 8; k += 4) + { + dwp[k + 3] = ((dwp[k] + dwp[k + 1] + dwp[k + 2]) & 0xff); + } + } + } + else + { + memcpy(&dwp[8], cw[PVU_CW_A1+j], 8); + + if(csaUsed) + { + for(k = 8; k < 16; k += 4) + { + dwp[k + 3] = ((dwp[k] + dwp[k + 1] + dwp[k + 2]) & 0xff); + } + } + } + } + + dwp = cw_ex->data; + + memset(dwp, 0, 16); + + if(ecm[0] == 0x80) + { + memcpy(dwp, cw[PVU_CW_HSD], 8); + + if(csaUsed) + { + for(k = 0; k < 8; k += 4) + { + dwp[k + 3] = ((dwp[k] + dwp[k + 1] + dwp[k + 2]) & 0xff); + } + } + } + else + { + memcpy(&dwp[8], cw[PVU_CW_HSD], 8); + + if(csaUsed) + { + for(k = 8; k < 16; k += 4) + { + dwp[k + 3] = ((dwp[k] + dwp[k + 1] + dwp[k + 2]) & 0xff); + } + } + } + } +#endif + } + else + { + // Calculate only video cw + PowervuCalculateCw(PVU_CW_VID, seed[PVU_CW_VID], csaUsed, convolvedCw[PVU_CW_VID], cw[PVU_CW_VID], baseCw, + seedEcmCw, hashModeCw, needsUnmasking, xorMode); + } + + memset(dw, 0, 16); + + if(ecm[0] == 0x80) + { + memcpy(dw, cw[PVU_CW_VID], 8); + + if(csaUsed) + { + for(k = 0; k < 8; k += 4) + { + dw[k + 3] = ((dw[k] + dw[k + 1] + dw[k + 2]) & 0xff); + } + } + } + else + { + memcpy(&dw[8], cw[PVU_CW_VID], 8); + + if(csaUsed) + { + for(k = 8; k < 16; k += 4) + { + dw[k + 3] = ((dw[k] + dw[k + 1] + dw[k + 2]) & 0xff); + } + } + } + + return 0; + + default: + break; + } + i += nanoLen; + } + + return ret; +} + + +// Drecrypt EMU +static void DREover(const uint8_t *ECMdata, uint8_t *dw) +{ + uint8_t key[8]; + uint32_t key_schedule[32]; + + if (ECMdata[2] >= (43 + 4) && ECMdata[40] == 0x3A && ECMdata[41] == 0x4B) + { + if (!FindKey('D', ECMdata[42] & 0x0F, 0, "OVER", key, 8, 1, 0, 0, NULL)) + { + return; + } + + des_set_key(key, key_schedule); + + des(dw, key_schedule, 0); // even dw post-process + des(dw + 8, key_schedule, 0); // odd dw post-process + } +} + +static uint32_t DreGostDec(uint32_t inData) +{ + static uint8_t Sbox[128] = + { + 0x0E,0x04,0x0D,0x01,0x02,0x0F,0x0B,0x08,0x03,0x0A,0x06,0x0C,0x05,0x09,0x00,0x07, + 0x0F,0x01,0x08,0x0E,0x06,0x0B,0x03,0x04,0x09,0x07,0x02,0x0D,0x0C,0x00,0x05,0x0A, + 0x0A,0x00,0x09,0x0E,0x06,0x03,0x0F,0x05,0x01,0x0D,0x0C,0x07,0x0B,0x04,0x02,0x08, + 0x07,0x0D,0x0E,0x03,0x00,0x06,0x09,0x0A,0x01,0x02,0x08,0x05,0x0B,0x0C,0x04,0x0F, + 0x02,0x0C,0x04,0x01,0x07,0x0A,0x0B,0x06,0x08,0x05,0x03,0x0F,0x0D,0x00,0x0E,0x09, + 0x0C,0x01,0x0A,0x0F,0x09,0x02,0x06,0x08,0x00,0x0D,0x03,0x04,0x0E,0x07,0x05,0x0B, + 0x04,0x0B,0x02,0x0E,0x0F,0x00,0x08,0x0D,0x03,0x0C,0x09,0x07,0x05,0x0A,0x06,0x01, + 0x0D,0x02,0x08,0x04,0x06,0x0F,0x0B,0x01,0x0A,0x09,0x03,0x0E,0x05,0x00,0x0C,0x07 + }; + uint8_t i, j; + + for(i = 0; i < 8; i++) + { + j = (inData >> 28) & 0x0F; + inData = (inData << 4) | (Sbox[i * 16 + j] & 0x0F); + } + + inData = (inData << 11) | (inData >> 21); + + return (inData); +} + +static void DrecryptDecrypt(uint8_t *Data, uint8_t *Key) // DRE GOST 28147-89 CORE +{ + int i, j; + uint32_t L_part = 0, R_part = 0, temp = 0; + + for(i = 0; i < 4; i++) L_part = (L_part << 8) | (Data[i] & 0xFF), R_part = (R_part << 8) | (Data[i + 4] & 0xFF); + + for(i = 0; i < 4; i++) + { + temp = ((Key[i*8+0] & 0xFF) << 24) | ((Key[i*8+1] & 0xFF) << 16) | ((Key[i*8+2] & 0xFF) << 8) | (Key[i*8+3] & 0xFF); + R_part ^= DreGostDec(temp + L_part); + temp = ((Key[i*8+4] & 0xFF) << 24) | ((Key[i*8+5] & 0xFF) << 16) | ((Key[i*8+6] & 0xFF) << 8) | (Key[i*8+7] & 0xFF); + L_part ^= DreGostDec(temp + R_part); + } + + for(j = 0; j < 3; j++) + { + for(i = 3; i >= 0; i--) + { + temp = ((Key[i*8+4] & 0xFF) << 24) | ((Key[i*8+5] & 0xFF) << 16) | ((Key[i*8+6] & 0xFF) << 8) | (Key[i*8+7] & 0xFF); + R_part ^= DreGostDec(temp + L_part); + temp = ((Key[i*8+0] & 0xFF) << 24) | ((Key[i*8+1] & 0xFF) << 16) | ((Key[i*8+2] & 0xFF) << 8) | (Key[i*8+3] & 0xFF); + L_part ^= DreGostDec(temp + R_part); + } + } + + for(i = 0; i < 4; i++) Data[i] = (R_part >> i*8) & 0xFF, Data[i+4] = (L_part >> i*8) & 0xFF; +} + +static void DrecryptPostCw(uint8_t* ccw) +{ + uint32_t i, j; + uint8_t tmp[4]; + + for(i = 0; i < 4; i++) + { + for(j = 0; j < 4; j++) + { + tmp[j] = ccw[3 - j]; + } + + for(j = 0; j < 4; j++) + { + ccw[j] = tmp[j]; + } + + ccw += 4; + } +} + +static void DrecryptSwap(uint8_t* ccw) +{ + uint32_t tmp1, tmp2; + + memcpy(&tmp1, ccw, 4); + memcpy(&tmp2, ccw + 4, 4); + + memcpy(ccw, ccw + 8, 8); + + memcpy(ccw + 8 , &tmp1, 4); + memcpy(ccw + 8 + 4, &tmp2, 4); +} + +static int8_t Drecrypt2ECM(uint32_t provId, uint8_t *ecm, uint8_t *dw) +{ + uint8_t ecmDataLen, ccw[16], key[32]; + uint16_t ecmLen, overcryptId; + char keyName[EMU_MAX_CHAR_KEYNAME]; + + ecmLen = GetEcmLen(ecm); + + if (ecmLen < 3) + { + return 1; // Not supported + } + + ecmDataLen = ecm[2]; + + if (ecmLen < ecmDataLen + 3) + { + return 4; // Corrupt data + } + + switch (provId & 0xFF) + { + case 0x11: + { + if (ecm[3] == 0x56) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X%02X", ecm[6], ecm[5]); + + if (!FindKey('D', 0x4AE111, 0, keyName, key, 32, 1, 0, 0, NULL)) + { + return 2; + } + } + else + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X%02X", ecm[6], ecm[3]); + + if (!FindKey('D', 0x4AE111, 0, keyName, key, 32, 1, 0, 0, NULL)) + { + return 2; + } + } + + break; + } + + case 0x14: + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X%02X", ecm[6], ecm[5]); + + if (!FindKey('D', 0x4AE114, 0, keyName, key, 32, 1, 0, 0, NULL)) + { + return 2; + } + + break; + } + + default: + return 1; + } + + memcpy(ccw, ecm + 13, 16); + + DrecryptPostCw(key); + DrecryptPostCw(key + 16); + + DrecryptDecrypt(ccw, key); + DrecryptDecrypt(ccw + 8, key); + + if (ecm[2] >= 46 && ecm[43] == 1 && provId == 0x11) + { + DrecryptSwap(ccw); + overcryptId = b2i(2, &ecm[44]); + + Drecrypt2OverCW(overcryptId, ccw); + + if (isValidDCW(ccw)) + { + memcpy(dw, ccw, 16); + return 0; + } + + return 9; // ICG error + } + + DREover(ecm, ccw); + + if (isValidDCW(ccw)) + { + DrecryptSwap(ccw); + memcpy(dw, ccw, 16); + return 0; + } + + return 1; +} + +// Tandberg EMU +static uint16_t TandbergChecksum(uint8_t *data, uint8_t length) +{ + // ECM and EMM checksum calculation + // 1. Combine data in 2 byte groups + // 2. Add them together + // 3. Multiply result by itself (power of 7) + // 4. XOR with fixed value 0x17E3 + + uint8_t i; + uint16_t checksum = 0; + + for(i = 0; i < length; i += 2) + { + checksum += (data[i] << 8) | data[i + 1]; + } + + checksum = checksum * checksum * checksum * checksum * checksum * checksum * checksum; + checksum ^= 0x17E3; + + return checksum; +} + +static int8_t GetTandbergKey(uint32_t keyIndex, char *keyName, uint8_t *key, uint32_t keyLength) +{ + // keyIndex: ecm keys --> entitlementId + // emm keys --> aeskeyIndex + // aes keys --> keyIndex + + // keyName: ecm keys --> "01" + // emm keys --> "MK" or "MK01" + // aes keys --> "AES" + + return FindKey('T', keyIndex, 0, keyName, key, keyLength, 1, 0, 0, NULL); +} + +static int8_t TandbergECM(uint8_t *ecm, uint8_t *dw) +{ + uint8_t nanoType, nanoLength; + uint8_t* nanoData; + uint32_t pos = 3; + uint32_t entitlementId; + uint32_t ks[32]; + uint8_t ecmKey[8]; + uint16_t ecmLen = GetEcmLen(ecm); + + if(ecmLen < 5) + { + return 1; + } + + do + { + nanoType = ecm[pos]; + nanoLength = ecm[pos+1]; + + if(pos + 2 + nanoLength > ecmLen) + { + break; + } + + nanoData = ecm + pos + 2; + + // ECM validation + uint16_t payloadChecksum = (nanoData[nanoLength - 2] << 8) | nanoData[nanoLength - 1]; + uint16_t calculatedChecksum = TandbergChecksum(nanoData, nanoLength - 2); + + if(calculatedChecksum != payloadChecksum) + { + cs_log("ECM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum); + return 8; + } + // End of ECM validation + + switch(nanoType) + { + case 0xEC: // Director v6 (September 2017) + { + if(nanoLength != 0x28) + { + cs_log("WARNING: nanoType EC length (%d) != %d", nanoLength, 0x28); + break; + } + + entitlementId = b2i(4, nanoData); + + if(!GetTandbergKey(entitlementId, "01", ecmKey, 8)) + { + return 2; + } + + cs_log("Active entitlement %.4X", entitlementId); + + // Step 1 - Decrypt DES CBC with ecmKey and iv = { 0 } (equal to nanoED) + uint8_t encryptedData[32] = { 0 }; + memcpy(encryptedData, nanoData + 6, 32); + + uint8_t iv[8] = { 0 }; + des_cbc_decrypt(encryptedData, iv, ecmKey, 32); + + uint8_t nanoMode = nanoData[5]; + + if ((nanoMode & 0x20) == 0) // Old algo + { + // Step 2 - Create CW (equal to nano ED) + dw[0] = encryptedData[0x05]; + dw[1] = encryptedData[0x19]; + dw[2] = encryptedData[0x1D]; + + dw[4] = encryptedData[0x0B]; + dw[5] = encryptedData[0x12]; + dw[6] = encryptedData[0x1A]; + + dw[8] = encryptedData[0x16]; + dw[9] = encryptedData[0x03]; + dw[10] = encryptedData[0x11]; + + dw[12] = encryptedData[0x18]; + dw[13] = encryptedData[0x10]; + dw[14] = encryptedData[0x0E]; + + return 0; + } + else // New algo (overencryption with AES) + { + // Step 2 - Prepare data for AES (it is like the creation of CW in nanoED but swapped each 8 bytes) + uint8_t dataEC[16] = { 0 }; + + dataEC[0] = encryptedData[0x02]; + dataEC[1] = encryptedData[0x0E]; + dataEC[2] = encryptedData[0x10]; + dataEC[3] = encryptedData[0x18]; + dataEC[4] = encryptedData[0x09]; + dataEC[5] = encryptedData[0x11]; + dataEC[6] = encryptedData[0x03]; + dataEC[7] = encryptedData[0x16]; + + dataEC[8] = encryptedData[0x13]; + dataEC[9] = encryptedData[0x1A]; + dataEC[10] = encryptedData[0x12]; + dataEC[11] = encryptedData[0x0B]; + dataEC[12] = encryptedData[0x04]; + dataEC[13] = encryptedData[0x1D]; + dataEC[14] = encryptedData[0x19]; + dataEC[15] = encryptedData[0x05]; + + // Step 3 - Decrypt AES CBC with new aesKey and iv 2EBD816A5E749A708AE45ADDD84333DE + uint8_t aesKeyIndex = nanoMode & 0x1F; // 32 possible AES keys + uint8_t aesKey[16] = { 0 }; + + if(!GetTandbergKey(aesKeyIndex, "AES", aesKey, 16)) + { + return 2; + } + + struct aes_keys aes; + aes_set_key(&aes, (char *)aesKey); + + uint8_t ivAes[16] = { 0x2E, 0xBD, 0x81, 0x6A, 0x5E, 0x74, 0x9A, 0x70, 0x8A, 0xE4, 0x5A, 0xDD, 0xD8, 0x43, 0x33, 0xDE }; + aes_cbc_decrypt(&aes, dataEC, 16, ivAes); + + // Step 4 - Create CW (a simple swap) + uint8_t offset; + for (offset = 0; offset < 16; offset++) + { + dw[offset] = dataEC[15 - offset]; + } + + return 0; + } + } + + case 0xED: // ECM_TAG_CW_DESCRIPTOR + { + if(nanoLength != 0x26) + { + cs_log("WARNING: nanoType ED length (%d) != %d", nanoLength, 0x26); + break; + } + + entitlementId = b2i(4, nanoData); + + if(!GetTandbergKey(entitlementId, "01", ecmKey, 8)) + { + return 2; + } + + cs_log("Active entitlement %.4X", entitlementId); + + uint8_t encryptedData[32] = { 0 }; + memcpy(encryptedData, nanoData + 4, 32); + + uint8_t iv[8] = { 0 }; + des_cbc_decrypt(encryptedData, iv, ecmKey, 32); + + dw[0] = encryptedData[0x05]; + dw[1] = encryptedData[0x19]; + dw[2] = encryptedData[0x1D]; + dw[4] = encryptedData[0x0B]; + dw[5] = encryptedData[0x12]; + dw[6] = encryptedData[0x1A]; + dw[8] = encryptedData[0x16]; + dw[9] = encryptedData[0x03]; + dw[10] = encryptedData[0x11]; + dw[12] = encryptedData[0x18]; + dw[13] = encryptedData[0x10]; + dw[14] = encryptedData[0x0E]; + + return 0; + } + + case 0xEE: // ECM_TAG_CW_DESCRIPTOR + { + if(nanoLength != 0x16) + { + cs_log("WARNING: nanoType EE length (%d) != %d", nanoLength, 0x16); + break; + } + + entitlementId = b2i(4, nanoData); + + if(!GetTandbergKey(entitlementId, "01", ecmKey, 8)) + { + return 2; + } + + cs_log("Active entitlement %.4X", entitlementId); + + memcpy(dw, nanoData + 4 + 8, 8); // even + memcpy(dw + 8, nanoData + 4, 8); // odd + + des_set_key(ecmKey, ks); + + des(dw, ks, 0); + des(dw + 8, ks, 0); + + return 0; + } + + default: + cs_log("WARNING: nanoType %.2X not supported", nanoType); + break; + } + + pos += 2 + nanoLength; + + } while (pos < ecmLen); + + return 1; +} + +const char* GetProcessECMErrorReason(int8_t result) +{ + switch(result) { + case 0: + return "No error"; + case 1: + return "ECM not supported"; + case 2: + return "Key not found"; + case 3: + return "Nano80 problem"; + case 4: + return "Corrupt data"; + case 5: + return "CW not found"; + case 6: + return "CW checksum error"; + case 7: + return "Out of memory"; + case 8: + return "ECM checksum error"; + case 9: + return "ICG error"; + default: + return "Unknown"; + } +} + +/* Error codes +0 OK +1 ECM not supported +2 Key not found +3 Nano80 problem +4 Corrupt data +5 CW not found +6 CW checksum error +7 Out of memory +8 ECM checksum error +9 ICG error +*/ +#ifdef WITH_EMU +int8_t ProcessECM(struct s_reader *rdr, int16_t ecmDataLen, uint16_t caid, uint32_t provider, const uint8_t *ecm, + uint8_t *dw, uint16_t srvid, uint16_t ecmpid, EXTENDED_CW* cw_ex) +#else +int8_t ProcessECM(struct s_reader *rdr, int16_t ecmDataLen, uint16_t caid, uint32_t provider, const uint8_t *ecm, + uint8_t *dw, uint16_t srvid, uint16_t ecmpid) +#endif +{ + int8_t result = 1, i; + uint8_t ecmCopy[EMU_MAX_ECM_LEN]; + uint16_t ecmLen = 0; + + if(ecmDataLen < 3) { + // accept requests without ecm only for biss + if((caid>>8) != 0x26 && caid != 0xFFFF) { + return 1; + } + } + else { + ecmLen = GetEcmLen(ecm); + } + + if(ecmLen > ecmDataLen) { + return 1; + } + + if(ecmLen > EMU_MAX_ECM_LEN) { + return 1; + } + memcpy(ecmCopy, ecm, ecmLen); + + if((caid >> 8) == 0x0D) { + result = CryptoworksECM(caid, ecmCopy, dw); + } + else if((caid >> 8) == 0x09) { + result = SoftNDSECM(caid, ecmCopy, dw); + } + else if(caid == 0x0500) { + result = ViaccessECM(ecmCopy, dw); + } + else if((caid >> 8) == 0x18) { + result = Nagra2ECM(ecmCopy, dw); + } + else if((caid >> 8) == 0x06) { + result = Irdeto2ECM(caid, ecmCopy, dw); + } + else if((caid >> 8) == 0x26 || caid == 0xFFFF) { + result = BissECM(rdr, ecm, ecmDataLen, dw, srvid, ecmpid); + } + else if((caid >> 8) == 0x0E) { +#ifdef WITH_EMU + result = PowervuECM(ecmCopy, dw, srvid, NULL, cw_ex); +#else + result = PowervuECM(ecmCopy, dw, NULL); +#endif + } + else if(caid == 0x4AE1) { + result = Drecrypt2ECM(provider, ecmCopy, dw); + } + else if((caid >> 8) == 0x10) { + result = TandbergECM(ecmCopy, dw); + } + + // fix dcw checksum + if(result == 0 && !((caid >> 8) == 0x0E)) { + for(i = 0; i < 16; i += 4) { + dw[i + 3] = ((dw[i] + dw[i + 1] + dw[i + 2]) & 0xff); + } + } + + if(result != 0) { + cs_log("ECM failed: %s", GetProcessECMErrorReason(result)); + } + + return result; +} + +// Viaccess EMM EMU +static int8_t ViaccessEMM(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t nanoCmd = 0, subNanoCmd = 0, *tmp; + uint16_t i = 0, j = 0, k = 0, emmLen = GetEcmLen(emm); + uint8_t ecmKeys[6][16], keyD0[2], emmKey[16], emmXorKey[16], provName[17]; + uint8_t ecmKeyCount = 0, emmKeyIndex = 0, aesMode = 0x0D; + uint8_t nanoLen = 0, subNanoLen = 0, haveEmmXorKey = 0, haveNewD0 = 0; + uint32_t ui1, ui2, ui3, ecmKeyIndex[6], provider = 0, ecmProvider = 0; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + struct aes_keys aes; + + memset(keyD0, 0, 2); + memset(ecmKeyIndex, 0, sizeof(uint32_t)*6); + + for(i=3; i+2 emmLen) { + return 1; + } + + switch(nanoCmd) { + case 0x90: { + if(nanoLen < 3) { + break; + } + ui1 = emm[i+2]; + ui2 = emm[i+1]; + ui3 = emm[i]; + provider = (ui1 | (ui2 << 8) | (ui3 << 16)); + if(provider == 0x00D00040) { + ecmProvider = 0x030B00; + } + else { + return 1; + } + break; + } + case 0xD2: { + if(nanoLen < 2) { + break; + } + emmKeyIndex = emm[i+1]; + break; + } + case 0x41: { + if(nanoLen < 1) { + break; + } + if(!GetViaKey(emmKey, provider, 'M', emmKeyIndex, 16, 1)) { + return 2; + } + memset(provName, 0, 17); + memset(emmXorKey, 0, 16); + k = nanoLen < 16 ? nanoLen : 16; + memcpy(provName, &emm[i], k); + aes_set_key(&aes, (char*)emmKey); + aes_decrypt(&aes, emmXorKey, 16); + for(j=0; j<16; j++) { + provName[j] ^= emmXorKey[j]; + } + provName[k] = 0; + + if(strcmp((char*)provName, "TNTSAT") != 0 && strcmp((char*)provName, "TNTSATPRO") != 0 + &&strcmp((char*)provName, "CSAT V") != 0) { + return 1; + } + break; + } + case 0xBA: { + if(nanoLen < 2) { + break; + } + GetViaKey(keyD0, ecmProvider, 'D', 0, 2, 0); + ui1 = (emm[i] << 8) | emm[i+1]; + if( (uint32_t)((keyD0[0] << 8) | keyD0[1]) < ui1 || (keyD0[0] == 0x00 && keyD0[1] == 0x00)) { + keyD0[0] = emm[i]; + keyD0[1] = emm[i+1]; + haveNewD0 = 1; + break; + } + return 0; + } + case 0xBC: { + break; + } + case 0x43: { + if(nanoLen < 16) { + break; + } + memcpy(emmXorKey, &emm[i], 16); + haveEmmXorKey = 1; + break; + } + case 0x44: { + if(nanoLen < 3) { + break; + } + if (!haveEmmXorKey) { + memset(emmXorKey, 0, 16); + } + tmp = (uint8_t*)malloc(((nanoLen/16)+1)*16*sizeof(uint8_t)); + if(tmp == NULL) { + return 7; + } + memcpy(tmp, &emm[i], nanoLen); + aes_set_key(&aes, (char*)emmKey); + for(j=0; j 5) { + break; + } + for(j=i; j+2 i+nanoLen) { + break; + } + switch(subNanoCmd) { + case 0xD2: { + if(nanoLen < 2) { + break; + } + aesMode = emm[j]; + emmKeyIndex = emm[j+1]; + break; + } + case 0x01: { + if(nanoLen < 17) { + break; + } + ecmKeyIndex[ecmKeyCount] = emm[j]; + memcpy(&ecmKeys[ecmKeyCount], &emm[j+1], 16); + if(!GetViaKey(emmKey, provider, 'M', emmKeyIndex, 16, 1)) { + break; + } + + if(aesMode == 0x0F || aesMode == 0x11) { + hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]); + hdSurEncPhase2_D2_0F_11(ecmKeys[ecmKeyCount]); + } + else if(aesMode == 0x13 || aesMode == 0x15) { + hdSurEncPhase1_D2_13_15(ecmKeys[ecmKeyCount]); + } + aes_set_key(&aes, (char*)emmKey); + aes_decrypt(&aes, ecmKeys[ecmKeyCount], 16); + if(aesMode == 0x0F || aesMode == 0x11) { + hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]); + } + else if(aesMode == 0x13 || aesMode == 0x15) { + hdSurEncPhase2_D2_13_15(ecmKeys[ecmKeyCount]); + } + + ecmKeyCount++; + break; + } + default: + break; + } + j += subNanoLen; + } + break; + } + case 0xF0: { + if(nanoLen != 4) { + break; + } + ui1 = ((emm[i+2] << 8) | (emm[i+1] << 16) | (emm[i] << 24) | emm[i+3]); + if(fletcher_crc32(emm + 3, emmLen - 11) != ui1) { + return 4; + } + + if(haveNewD0) { + + SetKey('V', ecmProvider, "D0", keyD0, 2, 1, NULL, NULL); + + for(j=0; j>2); + SetKey('I', ident, keyName, &emm[i+3], 16, 1, NULL, NULL); + + (*keysAdded)++; + cs_hexdump(0, &emm[i+3], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue); + } + } + i+=l; + } + + if(*keysAdded > 0) { + return 0; + } + } + + return 1; +} + +static int8_t Irdeto2DoEMMTypePMK(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK, + uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded) +{ + uint32_t end, i, l, j; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + + Irdeto2Decrypt(&emm[startOffset], keyIV, keySeed, length); + + i = 13; + end = startOffset + (length-8 < 0 ? 0 : length-8); + + while(i 0) { + return 0; + } + } + + return 1; +} + +static const uint8_t fausto_xor[16] = { 0x22, 0x58, 0xBD, 0x85, 0x2E, 0x8E, 0x52, 0x80, 0xA3, 0x79, 0x98, 0x69, 0x68, 0xE2, 0xD8, 0x4D }; + +static int8_t Irdeto2EMM(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded) +{ + uint8_t length, okeySeed[16], keySeed[16], keyIV[16], keyPMK[16], startOffset, emmType; + uint32_t ident; + uint32_t keySeedRef, keyIVRef, keyPMK0Ref, keyPMK1Ref, keyPMK0ERef, keyPMK1ERef; + uint8_t emmCopy[EMU_MAX_EMM_LEN], *emm = oemm; + uint16_t emmLen = GetEcmLen(emm); + + if(emmLen < 11) { + return 1; + } + + if(emm[3] == 0xC3 || emm[3] == 0xCB) { + emmType = 2; + startOffset = 11; + } + else { + emmType = 1; + startOffset = 10; + } + + ident = emm[startOffset-2] | caid << 8; + length = emm[startOffset-1]; + + + if(emmLen < length+startOffset) { + return 1; + } + + keySeedRef = 0; + while(GetIrdetoKey(okeySeed, ident, 'M', emmType == 1 ? 0 : 0xA, 1, &keySeedRef)) { + keyIVRef = 0; + while(GetIrdetoKey(keyIV, ident, 'M', 2, 1, &keyIVRef)) { + + keyPMK0Ref = 0; + keyPMK1Ref = 0; + keyPMK0ERef = 0; + keyPMK1ERef = 0; + + while(GetIrdetoKey(keyPMK, ident, 'M', emmType == 1 ? 3 : 0xB, 1, &keyPMK0Ref)) { + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + if(emmType == 1) { + if(Irdeto2DoEMMTypeOP(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) { + return 0; + } + } + else { + if(Irdeto2DoEMMTypePMK(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) { + return 0; + } + } + } + + if(emmType == 1) { + while(GetIrdetoKey(keyPMK, ident, 'M', 4, 1, &keyPMK1Ref)) { + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + if(Irdeto2DoEMMTypeOP(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) { + return 0; + } + } + + while(GetIrdetoKey(keyPMK, ident, 'M', 5, 1, &keyPMK0ERef)) { + xxor(keyPMK, 16, keyPMK, fausto_xor); + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + if(Irdeto2DoEMMTypeOP(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) { + return 0; + } + } + + while(GetIrdetoKey(keyPMK, ident, 'M', 6, 1, &keyPMK1ERef)) { + xxor(keyPMK, 16, keyPMK, fausto_xor); + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + if(Irdeto2DoEMMTypeOP(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) { + return 0; + } + } + } + + if(keyPMK0Ref == 0 && keyPMK1Ref == 0 && keyPMK0ERef == 0 && keyPMK1ERef == 0) { + return 2; + } + } + if(keyIVRef == 0) { + return 2; + } + } + if(keySeedRef == 0) { + return 2; + } + + return 1; +} + +int32_t GetIrdeto2Hexserial(uint16_t caid, uint8_t *hexserial) +{ + uint32_t i, len; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData; + + KeyDB = GetKeyContainer('I'); + if(KeyDB == NULL) { + return 0; + } + + for(i=0; ikeyCount; i++) { + + if(KeyDB->EmuKeys[i].provider>>8 != caid) { + continue; + } + if(strcmp(KeyDB->EmuKeys[i].keyName, "MC")) { + continue; + } + + tmpKeyData = &KeyDB->EmuKeys[i]; + + len = tmpKeyData->keyLength; + if(len > 3) + { len = 3; } + + memcpy(hexserial+(3-len), tmpKeyData->key, len); + return 1; + } + + return 0; +} + + +// PowerVu EMM EMU +static void PowervuUnmaskEmm(uint8_t *emm) +{ + int i, l; + + uint8_t sourcePos[] = {0x03, 0x0C, 0x0D, 0x11, 0x15, 0x18, 0x1D, 0x1F, 0x25, 0x2A, 0x32, 0x35, 0x3A, 0x3B, 0x3E, + 0x42, 0x47, 0x48, 0x53, 0x58, 0x5C, 0x61, 0x66, 0x69, 0x71, 0x72, 0x78, 0x7B, 0x81, 0x84}; + + uint8_t destPos[] = {0x02, 0x08, 0x0B, 0x0E, 0x13, 0x16, 0x1E, 0x23, 0x28, 0x2B, 0x2F, 0x33, 0x38, 0x3C, 0x40, + 0x44, 0x4A, 0x4D, 0x54, 0x57, 0x5A, 0x63, 0x68, 0x6A, 0x70, 0x75, 0x76, 0x7D, 0x82, 0x85}; + + uint8_t data[0x1E]; + uint8_t hashModeEmm; + uint8_t mask[0x10]; + uint32_t crc; + + // Create Mask for ECM decryption + PowervuCreateDataEcmEmm(emm, sourcePos, 0x13, 0x1E, data); + + hashModeEmm = emm[8] ^ PowervuCrc8Calc(data, 0x1E); + + PowervuCreateHash(data, 0x1E, mask, hashModeEmm); + + // Fix Header + emm[3] &= 0x0F; + emm[3] |= 0x10; + emm[8] = 0x00; + + // Unmask Body + for(i = 0; i < 0x1E; i++) + { + emm[0x13 + destPos[i]] ^= mask[i & 0x0F]; + } + + // Fix CRC (optional) + l = (((emm[1] << 8) + emm[2]) & 0xFFF) + 3 - 4; + crc = fletcher_crc32(emm, l); + + emm[l + 0] = crc >> 24; + emm[l + 1] = crc >> 16; + emm[l + 2] = crc >> 8; + emm[l + 3] = crc >> 0; +} + +static int8_t PowervuEMM(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t emmInfo, emmType, decryptOk = 0; + uint16_t emmLen = GetEcmLen(emm); + uint32_t i, uniqueAddress, groupId, keyRef = 0; + //uint32_t emmCrc32; + uint8_t emmKey[7], tmpEmmKey[7], tmp[26]; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[16]; + char uaInfo[4+8+1]; + + if(emmLen < 50) + { + return 1; + } + + // Check if unmasking is needed + if((emm[3] & 0xF0) == 0x50) + { + PowervuUnmaskEmm(emm); + } + + // looks like checksum does not work for all EMMs + //emmCrc32 = b2i(4, emm+emmLen-4); + // + //if(fletcher_crc32(emm, emmLen-4) != emmCrc32) + //{ + // return 8; + //} + emmLen -= 4; + + uniqueAddress = b2i(4, emm+12); + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.8X", uniqueAddress); + + do + { + if(!GetPowervuEmmKey(emmKey, 0, keyName, 7, 0, keyRef++, &groupId)) + { + cs_log_dbg(D_EMM, "EMM error: AU key for UA %s is missing", keyName); + return 2; + } + + for(i=19; i+27<=emmLen; i+=27) { + emmInfo = emm[i]; + + if(!GetBit(emmInfo, 7)) + { + continue; + } + + //keyNb = emm[i] & 0x0F; + + memcpy(tmp, emm+i+1, 26); + memcpy(tmpEmmKey, emmKey, 7); + PowervuDecrypt(emm+i+1, 26, tmpEmmKey, 0); + + if((emm[13] != emm[i+24]) || (emm[14] != emm[i+25]) || (emm[15] != emm[i+26])) + { + memcpy(emm+i+1, tmp, 26); + memcpy(tmpEmmKey, emmKey, 7); + PowervuDecrypt(emm+i+1, 26, tmpEmmKey, 1); + + if((emm[13] != emm[i+24]) || (emm[14] != emm[i+25]) || (emm[15] != emm[i+26])) + { + memcpy(emm+i+1, tmp, 26); + memcpy(tmpEmmKey, emmKey, 7); + continue; + } + } + + decryptOk = 1; + + emmType = emm[i+2] & 0x7F; + if(emmType > 1) + { + continue; + } + + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.2X", emmType); + snprintf(uaInfo, sizeof(uaInfo), "UA: %08X", uniqueAddress); + + if(emm[i+3] == 0 && emm[i+4] == 0) + { + cs_hexdump(0, &emm[i+3], 7, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: P %.4X**** %s %s -> REJECTED (looks invalid) UA: %.8X", groupId, keyName, keyValue, uniqueAddress); + continue; + } + + UpdateKeysByProviderMask('P', groupId<<16, 0x0000FFFF, keyName, &emm[i+3], 7, uaInfo); + + (*keysAdded)++; + cs_hexdump(0, &emm[i+3], 7, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: P %.4X**** %s %s ; UA: %.8X", groupId, keyName, keyValue, uniqueAddress); + } + + } while(!decryptOk); + + return 0; +} + +int32_t GetPowervuHexserials(uint16_t srvid, uint8_t hexserials[][4], int32_t length, int32_t* count) +{ + //srvid == 0xFFFF -> get all + + uint32_t i, j; + uint32_t groupid; + int32_t len, k; + KeyDataContainer *KeyDB; + uint8_t tmp[4]; + int8_t alreadyAdded; + + KeyDB = GetKeyContainer('P'); + if(KeyDB == NULL) + { return 0; } + + (*count) = 0; + + for(i=0; ikeyCount && (*count)EmuKeys[i].provider <= 0x0000FFFF) // skip au keys + { continue; } + + if(srvid != 0xFFFF && (KeyDB->EmuKeys[i].provider & 0x0000FFFF) != srvid) + { continue; } + + groupid = KeyDB->EmuKeys[i].provider>>16; + + for(j=0; jkeyCount && (*count)EmuKeys[j].provider != groupid) // search au key with groupip + { continue; } + + len = strlen(KeyDB->EmuKeys[j].keyName); + + if(len < 3) + { continue;} + + if(len > 8) + { len = 8; } + + memset(tmp, 0, 4); + CharToBin(tmp+(4-(len/2)), KeyDB->EmuKeys[j].keyName, len); + + for(k=0, alreadyAdded=0; k<*count; k++) + { + if(!memcmp(hexserials[k], tmp, 4)) + { + alreadyAdded = 1; + break; + } + } + + if(!alreadyAdded) + { + memcpy(hexserials[*count], tmp, 4); + (*count)++; + } + } + + } + + return 1; +} + +// Drecrypt EMM EMU +static int8_t DrecryptGetEmmKey(uint8_t *buf, uint32_t keyIdent, uint16_t keyName, uint8_t isCriticalKey) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "MK%04X", keyName); + return FindKey('D', keyIdent, 0, keyStr, buf, 32, isCriticalKey, 0, 0, NULL); +} + +static void DrecryptWriteEebin(const char *path, const char *name) +{ + char tmp[256]; + FILE *file = NULL; + uint8_t i, buffer[64][32]; + uint32_t prvid; + + // Set path + if (path != NULL) + { + snprintf(tmp, 256, "%s%s", path, name); + } + else // No path set, use SoftCam.Keys's path + { + snprintf(tmp, 256, "%s%s", emu_keyfile_path, name); + } + + if ((file = fopen(tmp, "wb")) != NULL) + { + cs_log("Writing key file: %s", tmp); + } + else + { + cs_log("Error writing key file: %s", tmp); + return; + } + + // Load keys from db to buffer + prvid = (strncmp(name, "ee36.bin", 9) == 0) ? 0x4AE111 : 0x4AE114; + + for (i = 0; i < 32; i++) // Load "3B" type keys + { + snprintf(tmp, 5, "3B%02X", i); + if (!FindKey('D', prvid, 0, tmp, buffer[i], 32, 0, 0, 0, NULL)) + { + memset(buffer[i], 0xFF, 32); + } + } + + for (i = 0; i < 32; i++) // Load "56" type keys + { + snprintf(tmp, 5, "56%02X", i); + if (!FindKey('D', prvid, 0, tmp, buffer[32 + i], 32, 0, 0, 0, NULL)) + { + memset(buffer[32 + i], 0xFF, 32); + } + } + + // Write buffer to ee.bin file + fwrite(buffer, 1, sizeof(buffer), file); + fclose(file); +} + +static int8_t DrecryptProcessEMM(struct s_reader *rdr, uint32_t provId, uint8_t *emm, uint32_t *keysAdded) +{ + uint16_t emmLen, emmDataLen; + uint32_t i, keyIdent; + uint16_t keyName; + uint8_t emmKey[32]; + uint8_t *curECMkey3B = NULL, *curECMkey56 = NULL; + uint8_t keynum =0, keyidx = 0, keyclass = 0, key1offset, key2offset; + char newKeyName[EMU_MAX_CHAR_KEYNAME], curKeyName[EMU_MAX_CHAR_KEYNAME], keyValue[100]; + + emmDataLen = GetEcmLen(emm); + emmLen = ((emm[1] & 0xF) << 8) | emm[2]; + + if (emmDataLen < emmLen + 3) + { + return 4; // Corrupt data + } + + if (emm[0] == 0x91) + { + Drecrypt2OverEMM(emm); + return 0; + } + else if (emm[0] == 0x82) + { + ReasmEMM82(emm); + return 0; + } + else if (emm[0] != 0x86) + { + return 1; // Not supported + } + + // Emm type 0x86 only + switch (emm[4]) + { + case 0x02: + keynum = 0x2C; + keyidx = 0x30; + keyclass = 0x26; + key1offset = 0x35; + key2offset = 0x6D; + break; + + case 0x4D: + keynum = 0x61; + keyidx = 0x60; + keyclass = 0x05; + key1offset = 0x62; + key2offset = 0x8B; + break; + + default: + return 1; // Not supported + } + + switch (provId & 0xFF) + { + case 0x11: + { + snprintf(curKeyName, EMU_MAX_CHAR_KEYNAME, "3B%02X", emm[keyclass]); + FindKey('D', 0x4AE111, 0, curKeyName, curECMkey3B, 32, 0, 0, 0, NULL); + + snprintf(curKeyName, EMU_MAX_CHAR_KEYNAME, "56%02X", emm[keyclass]); + FindKey('D', 0x4AE111, 0, curKeyName, curECMkey56, 32, 0, 0, 0, NULL); + + break; + } + + case 0x14: + { + snprintf(curKeyName, EMU_MAX_CHAR_KEYNAME, "3B%02X", emm[keyclass]); + FindKey('D', 0x4AE114, 0, curKeyName, curECMkey3B, 32, 0, 0, 0, NULL); + + snprintf(curKeyName, EMU_MAX_CHAR_KEYNAME, "56%02X", emm[keyclass]); + FindKey('D', 0x4AE114, 0, curKeyName, curECMkey56, 32, 0, 0, 0, NULL); + + break; + } + + default: + return 9; // Wrong provider + } + + keyIdent = (0x4AE1 << 8) | provId; + keyName = (emm[3] << 8) | emm[keynum]; + + if (!DrecryptGetEmmKey(emmKey, keyIdent, keyName, 1)) + { + return 2; + } + + // Key #1 + for (i = 0; i < 4; i++) + { + DrecryptDecrypt(&emm[key1offset + (i * 8)], emmKey); + } + + // Key #2 + for (i = 0; i < 4; i++) + { + DrecryptDecrypt(&emm[key2offset + (i * 8)], emmKey); + } + + // Key #1 + keyName = emm[keyidx] << 8 | emm[keyclass]; + snprintf(newKeyName, EMU_MAX_CHAR_KEYNAME, "%.4X", keyName); + + if (memcmp(&emm[key1offset], emm[keyidx] == 0x3b ? curECMkey3B : curECMkey56, 32) != 0) + { + memcpy(emm[keyidx] == 0x3b ? curECMkey3B : curECMkey56, &emm[key1offset], 32); + SetKey('D', keyIdent, newKeyName, &emm[key1offset], 32, 0, NULL, NULL); + (*keysAdded)++; + + cs_hexdump(0, &emm[key1offset], 32, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: D %.6X %s %s class %02X", keyIdent, newKeyName, keyValue, emm[keyclass]); + } + else + { + cs_log("Key %.6X %s already exists", keyIdent, newKeyName); + } + + // Key #2 + keyName = (emm[keyidx] == 0x56 ? 0x3B00 : 0x5600) | emm[keyclass]; + snprintf(newKeyName, EMU_MAX_CHAR_KEYNAME, "%.4X", keyName); + + if (memcmp(&emm[key2offset], emm[keyidx] == 0x3b ? curECMkey56 : curECMkey3B, 32) != 0) + { + memcpy(emm[keyidx] == 0x3b ? curECMkey56 : curECMkey3B, &emm[key2offset], 32); + SetKey('D', keyIdent, newKeyName, &emm[key2offset], 32, 0, NULL, NULL); + (*keysAdded)++; + + cs_hexdump(0, &emm[key2offset], 32, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: D %.6X %s %s class %02X", keyIdent, newKeyName, keyValue, emm[keyclass]); + } + else + { + cs_log("Key %.6X %s already exists", keyIdent, newKeyName); + } + + if (*keysAdded > 0) // Write new ecm keys to ee.bin file + { + switch (provId & 0xFF) + { + case 0x11: + DrecryptWriteEebin(rdr->extee36, "ee36.bin"); + break; + + case 0x14: + DrecryptWriteEebin(rdr->extee56, "ee56.bin"); + break; + + default: + cs_log("Provider %02X doesn't have a matching ee.bin file", provId & 0xFF); + break; + } + } + + return 0; +} + +static int8_t Drecrypt2EMM(struct s_reader *rdr, uint32_t provId, uint8_t *emm, uint32_t *keysAdded) +{ + int8_t result = DrecryptProcessEMM(rdr, provId, emm, keysAdded); + + if (result == 2) + { + uint8_t keynum = 0, emmkey; + uint32_t i; + KeyDataContainer *KeyDB = GetKeyContainer('D'); + + if (KeyDB == NULL) + { + return result; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + if (KeyDB->EmuKeys[i].provider != ((0x4AE1 << 8) | provId)) + { + continue; + } + + if (strlen(KeyDB->EmuKeys[i].keyName) < 6) + { + continue; + } + + if (memcmp(KeyDB->EmuKeys[i].keyName, "MK", 2)) + { + continue; + } + + CharToBin(&keynum, KeyDB->EmuKeys[i].keyName + 4, 2); + emmkey = (emm[4] == 0x4D) ? emm[0x61] : emm[0x2C]; + + if (keynum == emmkey) + { + if (provId == 0x11) + { + CharToBin(&rdr->dre36_force_group, KeyDB->EmuKeys[i].keyName + 2, 2); + } + else + { + CharToBin(&rdr->dre56_force_group, KeyDB->EmuKeys[i].keyName + 2, 2); + } + + break; + } + } + } + + return result; +} + +int32_t GetDrecryptHexserials(uint16_t caid, uint32_t provid, uint8_t *hexserials, int32_t length, int32_t *count) +{ + uint32_t i; + KeyDataContainer *KeyDB = GetKeyContainer('D'); + + if (KeyDB == NULL) + { + return 0; + } + + (*count) = 0; + + for (i = 0; i < KeyDB->keyCount && (*count) < length; i++) + { + + if (KeyDB->EmuKeys[i].provider != ((caid << 8) | provid)) + { + continue; + } + + if (strlen(KeyDB->EmuKeys[i].keyName) < 6) + { + continue; + } + + if (memcmp(KeyDB->EmuKeys[i].keyName, "MK", 2)) + { + continue; + } + + CharToBin(&hexserials[(*count)], KeyDB->EmuKeys[i].keyName + 2, 2); + + (*count)++; + } + + return 1; +} + +// Tandberg EMM EMU +static uint8_t MixTable[] = +{ + 0x12,0x78,0x4B,0x19,0x13,0x80,0x2F,0x84, + 0x86,0x4C,0x09,0x53,0x15,0x79,0x6B,0x49, + 0x10,0x4D,0x33,0x43,0x18,0x37,0x83,0x38, + 0x82,0x1B,0x6E,0x24,0x2A,0x85,0x3C,0x3D, + 0x5A,0x58,0x55,0x5D,0x20,0x41,0x65,0x51, + 0x0C,0x45,0x63,0x7F,0x0F,0x46,0x21,0x7C, + 0x2C,0x61,0x7E,0x0A,0x42,0x57,0x35,0x16, + 0x87,0x3B,0x4F,0x40,0x34,0x22,0x26,0x74, + 0x32,0x69,0x44,0x7A,0x6A,0x6D,0x0D,0x56, + 0x23,0x2B,0x5C,0x72,0x76,0x36,0x28,0x25, + 0x2E,0x52,0x5B,0x6C,0x7D,0x30,0x0B,0x5E, + 0x47,0x1F,0x7B,0x31,0x3E,0x11,0x77,0x1E, + 0x60,0x75,0x54,0x27,0x50,0x17,0x70,0x59, + 0x1A,0x2D,0x4A,0x67,0x3A,0x5F,0x68,0x08, + 0x4E,0x3F,0x29,0x6F,0x81,0x71,0x39,0x64, + 0x48,0x66,0x73,0x14,0x0E,0x1D,0x62,0x1C +}; + +void TandbergRotateBytes(unsigned char *in, int n) +{ + if(n > 1) + { + unsigned char *e = in + n - 1; + do + { + unsigned char temp = *in; + *in++ = *e; + *e-- = temp; + } + while (in < e); + } +} + +static void TandbergECMKeyDecrypt(uint8_t* emmKey, uint8_t* tagData, uint8_t* ecmKey) +{ + TandbergRotateBytes(emmKey, 8); + uint8_t iv[8] = { 0 }; + uint8_t* payLoad = tagData + 4 + 5; + des_cbc_decrypt(payLoad, iv, emmKey, 16); + + ecmKey[0] = payLoad[0x0F]; + ecmKey[1] = payLoad[0x01]; + ecmKey[2] = payLoad[0x0B]; + ecmKey[3] = payLoad[0x03]; + ecmKey[4] = payLoad[0x0E]; + ecmKey[5] = payLoad[0x04]; + ecmKey[6] = payLoad[0x0A]; + ecmKey[7] = payLoad[0x08]; +} + +static int8_t TandbergParseEMMNanoTags(uint8_t* data, uint32_t length, uint8_t keyIndex, uint32_t *keysAdded) +{ + uint8_t tagType, tagLength, blockIndex; + uint32_t pos = 0, entitlementId; + int32_t i, k; + uint32_t ks[32]; + uint8_t* tagData; + uint8_t emmKey[8]; + char keyValue[17]; + uint8_t tagDataDecrypted[0x10][8]; + + if(length < 2) + { + return 1; + } + + while(pos < length) + { + tagType = data[pos]; + tagLength = data[pos+1]; + + if(pos + 2 + tagLength > length) + { + return 1; + } + + tagData = data + pos + 2; + + switch(tagType) + { + case 0xE4: // EMM_TAG_SECURITY_TABLE_DESCRIPTOR (ram emm keys) + { + uint8_t tagMode = data[pos + 2]; + + switch(tagMode) + { + case 0x01: // keySet 01 (MK01) + { + if(tagLength != 0x8A) + { + cs_log("WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x8A); + break; + } + + if(!GetTandbergKey(keyIndex, "MK01", emmKey, 8)) + { + break; + } + + uint8_t iv[8] = { 0 }; + uint8_t* tagPayload = tagData + 2; + des_cbc_decrypt(tagPayload, iv, emmKey, 136); + + for (k = 0; k < 0x10; k++) // loop 0x10 keys + { + for (i = 0; i < 8; i++) // loop 8 bytes of key + { + tagDataDecrypted[k][i] = tagPayload[MixTable[8*k + i]]; + } + } + + blockIndex = tagData[1] & 0x03; + + for(i = 0; i < 0x10; i++) + { + SetKey('T', (blockIndex << 4) + i, "MK01", tagDataDecrypted[i], 8, 0, NULL, NULL); + } + } + break; + + case 0xFF: // keySet FF (MK) + { + if(tagLength != 0x82) + { + cs_log("WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x82); + break; + } + + blockIndex = tagData[1] & 0x03; + + if(!GetTandbergKey(keyIndex, "MK", emmKey, 8)) + { + break; + } + + des_set_key(emmKey, ks); + + for(i = 0; i < 0x10; i++) + { + des(tagData + 2 + (i*8), ks, 0); + } + + for(i = 0; i < 0x10; i++) + { + SetKey('T', (blockIndex << 4) + i, "MK", tagData + 2 + (i*8), 8, 0, NULL, NULL); + } + } + break; + + default: + cs_log("WARNING: nanoTag E4 mode %.2X not supported", tagMode); + break; + } + break; + } + + case 0xE1: // EMM_TAG_EVENT_ENTITLEMENT_DESCRIPTOR (ecm keys) + { + uint8_t tagMode = data[pos + 2 + 4]; + + switch(tagMode) + { + case 0x00: // ecm keys from mode FF + { + if(tagLength != 0x12) + { + cs_log("WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x12); + break; + } + + entitlementId = b2i(4, tagData); + + if(!GetTandbergKey(keyIndex, "MK", emmKey, 8)) + { + break; + } + + des_set_key(emmKey, ks); + des(tagData + 4 + 5, ks, 0); + + if((tagData + 4 + 5 + 7) != 0x00) // check if key looks valid (last byte 0x00) + { + break; + } + + if(UpdateKey('T', entitlementId, "01", tagData + 4 + 5, 8, 1, NULL)) + { + (*keysAdded)++; + cs_hexdump(0, tagData + 4 + 5, 8, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue); + } + } + break; + + case 0x01: // ecm keys from mode 01 + { + if(tagLength != 0x1A) + { + cs_log("WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x1A); + break; + } + + entitlementId = b2i(4, tagData); + + if(!GetTandbergKey(keyIndex, "MK01", emmKey, 8)) + { + break; + } + + uint8_t ecmKey[8] = { 0 }; + TandbergECMKeyDecrypt(emmKey, tagData, ecmKey); + + if(ecmKey[7] != 0x00) // check if key looks valid (last byte 0x00) + { + break; + } + + if(UpdateKey('T', entitlementId, "01", ecmKey, 8, 1, NULL)) + { + (*keysAdded)++; + cs_hexdump(0, ecmKey, 8, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue); + } + } + break; + + default: + cs_log("WARNING: nanoTag E1 mode %.2X not supported", tagMode); + break; + } + break; + } + + default: + cs_log("WARNING: nanoTag %.2X not supported", tagType); + break; + } + + pos += 2 + tagLength; + } + + return 0; +} + +static int8_t TandbergParseEMMNanoData(uint8_t* data, uint32_t* nanoLength, uint32_t maxLength, uint8_t keyIndex, uint32_t *keysAdded) +{ + uint32_t pos = 0; + uint16_t sectionLength; + int8_t ret = 0; + + if(maxLength < 2) + { + (*nanoLength) = 0; + return 1; + } + + sectionLength = ((data[pos]<<8) | data[pos+1]) & 0x0FFF; + + if(pos + 2 + sectionLength > maxLength) + { + (*nanoLength) = pos; + return 1; + } + + ret = TandbergParseEMMNanoTags(data + pos + 2, sectionLength, keyIndex, keysAdded); + + pos += 2 + sectionLength; + + (*nanoLength) = pos; + return ret; +} + +static int8_t TandbergEMM(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t keyIndex, ret = 0; + uint16_t emmLen = GetEcmLen(emm); + uint32_t pos = 3; + uint32_t permissionDataType; + uint32_t nanoLength = 0; + + while (pos < emmLen && !ret) + { + permissionDataType = emm[pos]; + + switch(permissionDataType) + { + case 0x00: + { + break; + } + + case 0x01: + { + pos += 0x0A; + break; + } + + case 0x02: + { + pos += 0x26; + break; + } + + default: + cs_log("ERROR: unknown permissionDataType %.2X (pos: %d)", permissionDataType, pos); + return 1; + } + + if(pos+6 >= emmLen) + { + break; + } + + keyIndex = emm[pos+1]; + + // EMM validation + // Copy payload checksum bytes and then set them to zero, + // so they do not affect the calculated checksum. + uint16_t payloadChecksum = (emm[pos + 2] << 8) | emm[pos + 3]; + memset(emm + pos + 2, 0, 2); + uint16_t calculatedChecksum = TandbergChecksum(emm + 3, emmLen - 3); + + if(calculatedChecksum != payloadChecksum) + { + cs_log("EMM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum); + return 8; + } + // End of EMM validation + + pos += 0x04; + ret = TandbergParseEMMNanoData(emm + pos, &nanoLength, emmLen - pos, keyIndex, keysAdded); + pos += nanoLength; + } + + return ret; +} + +const char* GetProcessEMMErrorReason(int8_t result) +{ + switch(result) { + case 0: + return "No error"; + case 1: + return "EMM not supported"; + case 2: + return "Key not found"; + case 3: + return "Nano80 problem"; + case 4: + return "Corrupt data"; + case 5: + return "Unknown"; + case 6: + return "Checksum error"; + case 7: + return "Out of memory"; + case 8: + return "EMM checksum error"; + case 9: + return "Wrong provider"; + default: + return "Unknown"; + } +} + +int8_t ProcessEMM(struct s_reader *rdr, uint16_t caid, uint32_t provider, const uint8_t *emm, uint32_t *keysAdded) +{ + int8_t result = 1; + uint8_t emmCopy[EMU_MAX_EMM_LEN]; + uint16_t emmLen = GetEcmLen(emm); + + if(emmLen > EMU_MAX_EMM_LEN) { + return 1; + } + memcpy(emmCopy, emm, emmLen); + *keysAdded = 0; + + if(caid==0x0500) { + result = ViaccessEMM(emmCopy, keysAdded); + } + else if((caid>>8)==0x06) { + result = Irdeto2EMM(caid, emmCopy, keysAdded); + } + else if((caid>>8)==0x0E) { + result = PowervuEMM(emmCopy, keysAdded); + } + else if(caid==0x4AE1) { + result = Drecrypt2EMM(rdr, provider, emmCopy, keysAdded); + } + else if((caid>>8)==0x10) { + result = TandbergEMM(emmCopy, keysAdded); + } + + if(result != 0) { + cs_log_dbg(D_EMM,"EMM failed: %s", GetProcessEMMErrorReason(result)); + } + + return result; +} Index: module-emulator-osemu.h =================================================================== --- module-emulator-osemu.h (revision 0) +++ module-emulator-osemu.h (working copy) @@ -0,0 +1,127 @@ +#include "globals.h" +#include "module-emulator-stream.h" + +#ifndef EMULATOR_H_ +#define EMULATOR_H_ + +#define EMU_MAX_CHAR_KEYNAME 12 +#define EMU_KEY_FILENAME "SoftCam.Key" +#define EMU_KEY_FILENAME_MAX_LEN 31 +#define EMU_MAX_ECM_LEN MAX_ECM_SIZE +#define EMU_MAX_EMM_LEN MAX_EMM_SIZE + +typedef struct KeyData KeyData; + +struct KeyData +{ + char identifier; + uint32_t provider; + char keyName[EMU_MAX_CHAR_KEYNAME]; + uint8_t *key; + uint32_t keyLength; + KeyData *nextKey; +}; + +typedef struct +{ + KeyData *EmuKeys; + uint32_t keyCount; + uint32_t keyMax; +} KeyDataContainer; + +extern KeyDataContainer CwKeys; +extern KeyDataContainer ViKeys; +extern KeyDataContainer NagraKeys; +extern KeyDataContainer IrdetoKeys; +extern KeyDataContainer NDSKeys; +extern KeyDataContainer BissKeys; +extern KeyDataContainer PowervuKeys; +extern KeyDataContainer DreKeys; +extern KeyDataContainer TandbergKeys; +extern uint8_t viasat_const[]; + +uint32_t GetOSemuVersion(void); + +void set_emu_keyfile_path(const char *path); +void clear_emu_keydata(void); +uint8_t read_emu_keyfile(struct s_reader *rdr, const char *path); + +#if !defined(__APPLE__) && !defined(__ANDROID__) +void read_emu_keymemory(struct s_reader *rdr); +#endif + +void read_emu_eebin(const char *path, const char *name); +void read_emu_deskey(uint8_t *dreOverKey, uint8_t len); + +int32_t CharToBin(uint8_t *out, const char *in, uint32_t inLen); + +/* Error codes +0 OK +1 ECM not supported +2 Key not found +3 Nano80 problem +4 Corrupt data +5 CW not found +6 CW checksum error +7 Out of memory +8 ECM checksum error +9 ICG error +*/ +#ifdef WITH_EMU +int8_t ProcessECM(struct s_reader *rdr, int16_t ecmDataLen, uint16_t caid, uint32_t provider, + const uint8_t *ecm, uint8_t *dw, uint16_t srvid, uint16_t ecmpid, EXTENDED_CW* cw_ex); +#else +int8_t ProcessECM(struct s_reader *rdr, int16_t ecmDataLen, uint16_t caid, uint32_t provider, + const uint8_t *ecm, uint8_t *dw, uint16_t srvid, uint16_t ecmpid); +#endif + +const char* GetProcessECMErrorReason(int8_t result); + +/* Error codes +0 OK +1 EMM not supported +2 Key not found +3 Nano80 problem +4 Corrupt data +5 +6 Checksum error +7 Out of memory +*/ +int8_t ProcessEMM(struct s_reader *rdr, uint16_t caid, uint32_t provider, const uint8_t *emm, uint32_t *keysAdded); + +const char* GetProcessEMMErrorReason(int8_t result); + +// hexserial must be of type "uint8_t hexserial[3]" +// returns 0 on error, 1 on success +int32_t GetIrdeto2Hexserial(uint16_t caid, uint8_t* hexserial); + +// hexserials must be of type "uint8_t hexserials[length][4]" +// if srvid == 0xFFFF all serials are returned (no srvid filtering) +// returns 0 on error, 1 on success +int32_t GetPowervuHexserials(uint16_t srvid, uint8_t hexserials[][4], int32_t length, int32_t* count); + +// hexserials must be of type "uint8_t hexserials[length]" +// returns 0 on error, 1 on success +int32_t GetDrecryptHexserials(uint16_t caid, uint32_t provid, uint8_t *hexserials, int32_t length, int32_t* count); + +#define PVU_CW_VID 0 // VIDeo +#define PVU_CW_HSD 1 // High Speed Data +#define PVU_CW_A1 2 // Audio 1 +#define PVU_CW_A2 3 // Audio 2 +#define PVU_CW_A3 4 // Audio 3 +#define PVU_CW_A4 5 // Audio 4 +#define PVU_CW_UTL 6 // UTiLity +#define PVU_CW_VBI 7 // Vertical Blanking Interval + +#ifdef WITH_EMU +int8_t PowervuECM(uint8_t *ecm, uint8_t *dw, uint16_t srvid, emu_stream_client_key_data *cdata, EXTENDED_CW* cw_ex); +#else +int8_t PowervuECM(uint8_t *ecm, uint8_t *dw, emu_stream_client_key_data *cdata); +#endif + +#ifdef WITH_EMU +int32_t FindKey(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, uint8_t *key, + uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef, uint8_t matchLength, uint32_t *getProvider); +#endif + +#endif Index: module-emulator-stream.c =================================================================== --- module-emulator-stream.c (revision 0) +++ module-emulator-stream.c (working copy) @@ -0,0 +1,1105 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" +#include "cscrypt/des.h" + +#ifdef WITH_EMU +#include "oscam-string.h" +#include "oscam-config.h" +#include "oscam-time.h" +#include "oscam-net.h" + +extern int32_t exit_oscam; +#endif + +#include "ffdecsa/ffdecsa.h" +#include "module-emulator-osemu.h" +#include "module-emulator-stream.h" + +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]; + +#ifdef WITH_EMU +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]; +#endif + +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 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) +{ + uint8_t* data = cdata->pat_data; + uint16_t section_length = SCT_LEN(data); + uint16_t srvid; + int32_t i; + + for(i=8; i+7srvid == srvid) + { + cdata->pmt_pid = b2i(2, data+i+2) & 0x1FFF; + cs_log_dbg(D_READER, "Stream %i found pmt pid: 0x%04X (%i)",cdata->connid, cdata->pmt_pid, cdata->pmt_pid); + break; + } + } +} + +static void ParsePMTData(emu_stream_client_data *cdata) +{ + uint8_t* data = cdata->pmt_data; + + uint16_t section_length = SCT_LEN(data); + int32_t i; + uint16_t program_info_length = 0, es_info_length = 0; + uint8_t descriptor_tag = 0, descriptor_length = 0; + uint8_t stream_type; + uint16_t stream_pid, caid; + + cdata->pcr_pid = b2i(2, data+8) &0x1FFF; + if(cdata->pcr_pid != 0x1FFF) + { + cs_log_dbg(D_READER, "Stream %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; } + + 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>>8 == 0x0E) + { + cdata->ecm_pid = b2i(2, data+i+4) &0x1FFF; + cs_log_dbg(D_READER, "Stream %i found ecm pid: 0x%04X (%i)", cdata->connid, cdata->ecm_pid, cdata->ecm_pid); + break; + } + } + } + + for(i=12+program_info_length; i+4video_pid = stream_pid; + cs_log_dbg(D_READER, "Stream %i found video pid: 0x%04X (%i)",cdata->connid, stream_pid, stream_pid); + } + + else if(stream_type == 0x03 || stream_type == 0x04 || stream_type == 0x05 || stream_type == 0x06 || + stream_type == 0x0F || stream_type == 0x11 || (stream_type >= 0x81 && stream_type <= 0x87) || stream_type == 0x8A) + { + if(cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) + { continue; } + + cdata->audio_pids[cdata->audio_pid_count] = stream_pid; + cdata->audio_pid_count++; + cs_log_dbg(D_READER, "Stream %i found audio pid: 0x%04X (%i)", cdata->connid, stream_pid, stream_pid); + } + } +} + +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); + uint16_t emm_pid = b2i(2, data + i +4)&0x1FFF; + + if(caid>>8 == 0x0E) + { + cdata->emm_pid = emm_pid; + cs_log_dbg(D_READER, "Stream %i found audio pid: 0x%04X (%i)", cdata->connid, emm_pid, emm_pid); + break; + } + } +} + +static void ParseEMMData(emu_stream_client_data *cdata) +{ + uint8_t* data = cdata->emm_data; + uint32_t keysAdded = 0; + + ProcessEMM(NULL, 0x0E00, 0, data, &keysAdded); + + if(keysAdded) + { + cs_log("Stream %i found %i keys", cdata->connid, keysAdded); + } +} + +static void ParseECMData(emu_stream_client_data *cdata) +{ + uint8_t* data = cdata->ecm_data; + uint16_t section_length = SCT_LEN(data); + uint8_t dcw[16]; + + if(section_length < 0xb) + { return; } + + if(data[0xb] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[0xb] == 0) + || ((cdata->ecm_nb - data[0xb]) > 5)) + { + cdata->ecm_nb = data[0xb]; +#ifdef WITH_EMU + PowervuECM(data, dcw, cdata->srvid, &cdata->key, NULL); +#else + PowervuECM(data, dcw, &cdata->key); +#endif + } +} + +static void ParseTSPackets(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + uint32_t i, j, k; + uint32_t tsHeader; + uint16_t pid, offset; + uint8_t scramblingControl, payloadStart, oddeven; + int8_t oddKeyUsed; + uint32_t *deskey; + uint8_t *pdata; + uint8_t *packetClusterA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS][64]; // separate cluster arrays for video and each audio track + uint8_t *packetClusterV[256]; + void *csakeyA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0}; + void *csakeyV = 0; + emu_stream_client_key_data *keydata; + uint32_t scrambled_packets = 0; + uint32_t scrambled_packetsA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0}; + packetClusterV[0] = NULL; + 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 + + for(i=0; i> 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(pid == 1) + { + // set to null pid + stream_buf[i+1] |= 0x1f; + stream_buf[i+2] = 0xff; + + if(emu_stream_emm_enabled && !data->emm_pid) + { + 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) + { + // 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(pid == 0 && !data->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(!data->ecm_pid && pid == data->pmt_pid) + { + 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; + } + + if(data->ecm_pid && pid == data->ecm_pid) + { +#ifdef WITH_EMU + stream_server_has_ecm[data->connid] = 1; +#endif + + // 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; + +#ifdef WITH_EMU + 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 + { +#endif + keydata = &data->key; +#ifdef WITH_EMU + } +#endif + + 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; + + for(j = i+packetSize; j < bufLength; j += packetSize) // Now iterate through the rest of the packets and create clusters for batch decryption + { + 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; + } + + for(k = 0; k < data->audio_pid_count; k++) // Check for audio tracks and create single packet clusters + { + 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 + { + if(scrambled_packets > 1) // last packet was a video packet, so set end of cluster to end of last packet + { + 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(scrambled_packetsA[k]) // if audio track has scrambled packets, set null to mark end and decrypt + { + 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; + } + +#ifdef WITH_EMU + if(!stream_server_has_ecm[data->connid]) + { + SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[data->connid]); + } +#endif + } +} + +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, strlen(http_buf), 0) == -1) + { return -1; } + + return streamfd; +} + +static void stream_client_disconnect(emu_stream_client_conn_data *conndata) +{ + int32_t i; + +#ifdef WITH_EMU + 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); +#endif + + SAFE_MUTEX_LOCK(&emu_stream_server_mutex); + for(i=0; iconnfd) + { + 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) +{ +#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 + + emu_stream_client_conn_data *conndata = (emu_stream_client_conn_data *)arg; + char *http_buf, stream_path[255], stream_path_copy[255]; + int32_t streamfd; + int32_t clientStatus, streamStatus; + uint8_t *stream_buf; + uint16_t packetCount = 0, packetSize = 0, startOffset = 0; + uint32_t remainingDataPos, remainingDataLength; + int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait; + int32_t bytesRead = 0; + emu_stream_client_data *data; + int8_t streamConnectErrorCount = 0; + int8_t streamDataErrorCount = 0; + int32_t i, srvidtmp; + char *saveptr, *token; + char http_version[4]; + int32_t http_status_code = 0; + + 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); + + for(i=0; token != NULL && i<3; i++) + { + token = strtok_r(NULL, ":", &saveptr); + if(token == NULL) + { break; } + } + if(token != NULL) + { + if(sscanf(token, "%x", &srvidtmp) < 1) + { + token = NULL; + } + else + { + data->srvid = srvidtmp & 0xFFFF; + } + } + + if(token == NULL) + { + NULLFREE(http_buf); + NULLFREE(stream_buf); + NULLFREE(data); + stream_client_disconnect(conndata); + return NULL; + } + +#ifdef WITH_EMU + 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); +#endif + + cs_log("Stream client %i request %s", conndata->connid, stream_path); + + 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, strlen(http_buf), 0); + + data->connid = conndata->connid; + + 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; + if(stream_buf[0] != 0x47 || packetSize == 0) // only search if not starting on ts packet or unknown packet size + { + SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset); + } + if(packetSize == 0) + { + bytesRead = 0; + } + else + { + packetCount = ((bytesRead-startOffset) / packetSize); + + 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; + } + +#ifdef WITH_EMU + SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); + for(i=0; iconnfd = 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; +} + +#ifdef WITH_EMU +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; iwrite_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; +} +#endif + +void stop_stream_server(void) +{ + int32_t i; + + SAFE_MUTEX_LOCK(&emu_stream_server_mutex); + for(i=0; ihexserial[3] = hversion[0]; + rdr->hexserial[4] = hversion[1]; +} + +static void set_prids(struct s_reader *rdr) +{ + int32_t i, j; + + rdr->nprov = 0; + + for (i = 0; (i < rdr->emu_auproviders.nfilts) && (rdr->nprov < CS_MAXPROV); i++) + { + for (j = 0; (j < rdr->emu_auproviders.filts[i].nprids) && (rdr->nprov < CS_MAXPROV); j++) + { + i2b_buf(4, rdr->emu_auproviders.filts[i].prids[j], rdr->prid[i]); + rdr->nprov++; + } + } +} + +static void emu_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint8_t *key, char *keyName, uint32_t keyLength, uint8_t isData) +{ + if (!rdr->ll_entitlements) + { + rdr->ll_entitlements = ll_create("ll_entitlements"); + } + + S_ENTITLEMENT *item; + if (cs_malloc(&item, sizeof(S_ENTITLEMENT))) + { + // fill item + item->caid = caid; + item->provid = provid; + item->id = 0; + item->class = 0; + item->start = 0; + item->end = 2147472000; + item->type = 0; + item->isKey = 1; + memcpy(item->name, keyName, 8); + item->key = key; + item->keyLength = keyLength; + item->isData = isData; + + // add item + ll_append(rdr->ll_entitlements, item); + } +} + +static void refresh_entitlements(struct s_reader *rdr) +{ + uint32_t i; + KeyData *tmpKeyData; + + cs_clear_entitlement(rdr); + + for (i = 0; i < CwKeys.keyCount; i++) + { + emu_add_entitlement(rdr, CwKeys.EmuKeys[i].provider >> 8, CwKeys.EmuKeys[i].provider & 0xFF, + CwKeys.EmuKeys[i].key, CwKeys.EmuKeys[i].keyName, CwKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < ViKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x500, ViKeys.EmuKeys[i].provider, ViKeys.EmuKeys[i].key, + ViKeys.EmuKeys[i].keyName, ViKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < NagraKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x1801, NagraKeys.EmuKeys[i].provider, NagraKeys.EmuKeys[i].key, + NagraKeys.EmuKeys[i].keyName, NagraKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < IrdetoKeys.keyCount; i++) + { + tmpKeyData = &IrdetoKeys.EmuKeys[i]; + do + { + emu_add_entitlement(rdr, tmpKeyData->provider >> 8, tmpKeyData->provider & 0xFF, + tmpKeyData->key, tmpKeyData->keyName, tmpKeyData->keyLength, 0); + + tmpKeyData = tmpKeyData->nextKey; + } + while (tmpKeyData != NULL); + } + + for (i = 0; i < NDSKeys.keyCount; i++) + { + emu_add_entitlement(rdr, NDSKeys.EmuKeys[i].provider, 0, NDSKeys.EmuKeys[i].key, + NDSKeys.EmuKeys[i].keyName, NDSKeys.EmuKeys[i].keyLength, 0); + } + + emu_add_entitlement(rdr, 0x090F, 0, viasat_const, "00", 64, 1); + emu_add_entitlement(rdr, 0x093E, 0, viasat_const, "00", 64, 1); + + for (i = 0; i < BissKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x2600, BissKeys.EmuKeys[i].provider, BissKeys.EmuKeys[i].key, + BissKeys.EmuKeys[i].keyName, BissKeys.EmuKeys[i].keyLength, 0); + } + + emu_add_entitlement(rdr, 0xFFFF, 0, &oneByte, "00", 1, 1); + + for (i = 0; i < PowervuKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x0E00, PowervuKeys.EmuKeys[i].provider, PowervuKeys.EmuKeys[i].key, + PowervuKeys.EmuKeys[i].keyName, PowervuKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < DreKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x4AE1, DreKeys.EmuKeys[i].provider, DreKeys.EmuKeys[i].key, + DreKeys.EmuKeys[i].keyName, DreKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < TandbergKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x1010, TandbergKeys.EmuKeys[i].provider, TandbergKeys.EmuKeys[i].key, + TandbergKeys.EmuKeys[i].keyName, TandbergKeys.EmuKeys[i].keyLength, 0); + } +} + +static int32_t emu_do_ecm(struct s_reader *rdr, const struct ecm_request_t *er, struct s_ecm_answer *ea) +{ + + if (!ProcessECM(rdr, er->ecmlen, er->caid, er->prid, er->ecm, ea->cw, er->srvid, er->pid, &ea->cw_ex)) + { + return CS_OK; + } + + return CS_ERROR; +} + +static int32_t emu_do_emm(struct s_reader *rdr, struct emm_packet_t *emm) +{ + uint32_t keysAdded = 0; + + if (emm->emmlen < 3) + { + return CS_ERROR; + } + + if (SCT_LEN(emm->emm) > emm->emmlen) + { + return CS_ERROR; + } + + if (!ProcessEMM(rdr, b2i(2, emm->caid), b2i(4, emm->provid), emm->emm, &keysAdded)) + { + if (keysAdded > 0) + { + refresh_entitlements(rdr); + } + + return CS_OK; + } + + return CS_ERROR; +} + +static int32_t emu_card_info(struct s_reader *rdr) +{ + // Delete keys from Emu's memory + clear_emu_keydata(); + + // Read keys built in the OSCam-Emu binary +#if !defined(__APPLE__) && !defined(__ANDROID__) + read_emu_keymemory(rdr); +#endif + + // Read keys from SoftCam.Key file + set_emu_keyfile_path(cs_confdir); + + if (!read_emu_keyfile(rdr, cs_confdir)) + { + if (read_emu_keyfile(rdr, "/var/keys/")) + { + set_emu_keyfile_path("/var/keys/"); + } + } + + // Load keys from external files (set via the webif or the reader config directly) + read_emu_eebin(rdr->extee36, "ee36.bin"); // Read "ee36.bin" + read_emu_eebin(rdr->extee56, "ee56.bin"); // Read "ee56.bin" + read_emu_deskey(rdr->des_key, rdr->des_key_length); // Read overcrypt keys for DreCrypt ADEC + + cs_log("Total keys in memory: W:%d V:%d N:%d I:%d S:%d F:%d P:%d D:%d T:%d", \ + CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, \ + IrdetoKeys.keyCount, NDSKeys.keyCount, BissKeys.keyCount, \ + PowervuKeys.keyCount, DreKeys.keyCount, TandbergKeys.keyCount); + + // Inform OSCam about all available keys. + // This is used for listing the "entitlements" in the webif's reader page. + refresh_entitlements(rdr); + + set_prids(rdr); + + set_hexserial_to_version(rdr); + + return CS_OK; +} + +/* +static int32_t emu_card_init(struct s_reader *UNUSED(rdr), struct s_ATR *UNUSED(atr)) +{ + return CS_ERROR; +} +*/ + +int32_t emu_get_via3_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + uint32_t provid = 0; + + if(ep->emm[3] == 0x90 && ep->emm[4] == 0x03) + { + provid = b2i(3, ep->emm+5); + provid &=0xFFFFF0; + i2b_buf(4, provid, ep->provid); + } + + switch(ep->emm[0]) + { + case 0x88: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, 4); + rdr_log_dbg(rdr, D_EMM, "UNIQUE"); + return 1; + + case 0x8A: + case 0x8B: + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + return 1; + + case 0x8C: + case 0x8D: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED (part)"); + // We need those packets to pass otherwise we would never + // be able to complete EMM reassembly + return 1; + + case 0x8E: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED"); + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 3, 3); + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +int32_t emu_get_ird2_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + int32_t l = (ep->emm[3] & 0x07); + int32_t base = (ep->emm[3] >> 3); + char dumprdrserial[l * 3], dumpemmserial[l * 3]; + + switch(l) + { + + case 0: + // global emm, 0 bytes addressed + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL base = %02x", base); + return 1; + + case 2: + // shared emm, 2 bytes addressed + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED l = %d ep = {%s} rdr = {%s} base = %02x", l, + dumpemmserial, dumprdrserial, base); + return 1; + + case 3: + // unique emm, 3 bytes addressed + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE l = %d ep = {%s} rdr = {%s} base = %02x", l, + dumpemmserial, dumprdrserial, base); + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +int32_t emu_get_pvu_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + if(ep->emm[0] == 0x82) + { + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 12, 4); + } + else + { + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + } + return 1; +} + +int32_t emu_get_dre2_emm_type(EMM_PACKET *ep, struct s_reader *UNUSED(rdr)) +{ + switch (ep->emm[0]) + { + case 0x82: + ep->type = GLOBAL; + return 1; + + case 0x86: + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + ep->hexserial[0] = ep->emm[3]; + return 1; + + //case 0x87: + // ep->type = UNIQUE; + // return 1; //FIXME: no filling of ep->hexserial + + case 0x88: + ep->type = UNIQUE; + return 1; //FIXME: no filling of ep->hexserial + + case 0x91: + ep->type = GLOBAL; + return 1; + + default: + ep->type = UNKNOWN; + return 1; + } +} + +int32_t emu_get_tan_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + if(ep->emm[0] == 0x82 || ep->emm[0] == 0x83) + { + ep->type = GLOBAL; + } + else + { + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + } + return 1; +} + +static int32_t emu_get_emm_type(struct emm_packet_t *ep, struct s_reader *rdr) +{ + switch(b2i(2, ep->caid)>>8) + { + case 0x05: + return emu_get_via3_emm_type(ep, rdr); + + case 0x06: + return emu_get_ird2_emm_type(ep, rdr); + + case 0x0E: + return emu_get_pvu_emm_type(ep, rdr); + + case 0x4A: + return emu_get_dre2_emm_type(ep, rdr); + + case 0x10: + return emu_get_tan_emm_type(ep, rdr); + + default: + break; + } + + return CS_ERROR; +} + +FILTER* get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid) +{ + int32_t i; + + for(i = 0; i < rdr->emu_auproviders.nfilts; i++) + { + if(caid == rdr->emu_auproviders.filts[i].caid) + { + return &rdr->emu_auproviders.filts[i]; + } + } + + return NULL; +} + +static int32_t emu_get_via3_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 1; + + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return CS_ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8A; + filters[idx].mask[0] = 0xFE; + filters[idx].filter[3] = 0x80; + filters[idx].mask[3] = 0x80; + idx++; + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_ird2_emm_filter(struct s_reader* rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t caid, uint32_t UNUSED(provid)) +{ + uint8_t hexserial[3], prid[4]; + FILTER* emu_provids; + int8_t have_provid = 0, have_serial = 0; + int32_t i; + + if(GetIrdeto2Hexserial(caid, hexserial)) + { have_serial = 1; } + + emu_provids = get_emu_prids_for_caid(rdr, caid); + if(emu_provids != NULL && emu_provids->nprids > 0) + { have_provid = 1; } + + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = have_serial + (2*(have_provid ? emu_provids->nprids : 0)); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return CS_ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + unsigned int idx = 0; + + if(have_serial) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], hexserial, 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + } + + for(i=0; have_provid && inprids; i++) + { + i2b_buf(4, emu_provids->prids[i], prid); + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &prid[1], 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFA; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &prid[1], 2); + memset(&filters[idx].mask[2], 0xFF, 2); + idx++; + } + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_pvu_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid), uint16_t srvid) +{ + uint8_t hexserials[16][4]; + int32_t i, count = 0; + + if(!GetPowervuHexserials(srvid, hexserials, 16, &count)) + { return CS_ERROR; } + + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = count; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return CS_ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + for(i=0; i>8) + { + case 0x05: + return emu_get_via3_emm_filter(rdr, emm_filters, filter_count, caid, provid); + + case 0x06: + return emu_get_ird2_emm_filter(rdr, emm_filters, filter_count, caid, provid); + + case 0x0E: + return emu_get_pvu_emm_filter(rdr, emm_filters, filter_count, caid, provid, srvid); + + case 0x4A: + return emu_get_dre2_emm_filter(rdr, emm_filters, filter_count, caid, provid); + + case 0x10: + return emu_get_tan_emm_filter(rdr, emm_filters, filter_count, caid, provid); + + default: + break; + } + + return CS_ERROR; +} + +const struct s_cardsystem reader_emu = +{ + .desc = "emu", + .caids = (uint16_t[]){ 0x0D, 0x09, 0x0500, 0x18, 0x06, 0x26, 0xFFFF, 0x0E, 0x4A, 0x10, 0 }, + .do_ecm = emu_do_ecm, + .do_emm = emu_do_emm, + .card_info = emu_card_info, + //.card_init = emu_card_init, // apparently this is not needed at all + .get_emm_type = emu_get_emm_type, + .get_emm_filter = emu_get_emm_filter, // needed to pass checks + .get_emm_filter_adv = emu_get_emm_filter_adv, +}; + +/* + * Create the Emu virtual "device" part. This is of type s_cardreader. + * Similar structures are found in the csctapi (Card System Card Terminal API) + * folder for every IFD (InterFace Device), aka smart card reader. + * Since we have no hardware to initialize, we start our Stream Relay server + * with the emu_reader_init() function. + * At Emu shutdown, we remove keys from memory with the emu_close() function. +*/ + +#define CR_OK 0 +#define CR_ERROR 1 + +static int32_t emu_reader_init(struct s_reader *UNUSED(reader)) +{ + int32_t i; + char authtmp[128]; + + if (cfg.emu_stream_relay_enabled && (stream_server_thread_init == 0)) + { + stream_server_thread_init = 1; + SAFE_MUTEX_INIT(&emu_fixed_key_srvid_mutex, NULL); + + for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) + { + SAFE_MUTEX_INIT(&emu_fixed_key_data_mutex[i], NULL); + ll_emu_stream_delayed_keys[i] = ll_create("ll_emu_stream_delayed_keys"); + memset(&emu_fixed_key_data[i], 0, sizeof(emu_stream_client_key_data)); + } + + start_thread("stream_key_delayer", stream_key_delayer, NULL, NULL, 1, 1); + cs_log("Stream key delayer initialized"); + + 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, 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"); + } + + return CR_OK; +} + +static int32_t emu_close(struct s_reader *UNUSED(reader)) +{ + cs_log("Reader is shutting down"); + + // Delete keys from Emu's memory + clear_emu_keydata(); + + return CR_OK; +} + +static int32_t emu_get_status(struct s_reader *UNUSED(reader), int32_t *in) { *in = 1; return CR_OK; } +static int32_t emu_activate(struct s_reader *UNUSED(reader), struct s_ATR *UNUSED(atr)) { return CR_OK; } +static int32_t emu_transmit(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(expectedlen), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; } +static int32_t emu_receive(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; } +static int32_t emu_write_settings(struct s_reader *UNUSED(reader), struct s_cardreader_settings *UNUSED(s)) { return CR_OK; } +static int32_t emu_card_write(struct s_reader *UNUSED(pcsc_reader),const uchar *UNUSED(buf) ,uint8_t *UNUSED(cta_res), uint16_t *UNUSED(cta_lr),int32_t UNUSED(l)) { return CR_OK; } +static int32_t emu_set_protocol(struct s_reader *UNUSED(rdr),uint8_t *UNUSED(params),uint32_t *UNUSED(length), uint32_t UNUSED(len_request)) { return CR_OK; } + +const struct s_cardreader cardreader_emu = +{ + .desc = "emu", + .typ = R_EMU, + .skip_extra_atr_parsing = 1, + .reader_init = emu_reader_init, + .get_status = emu_get_status, + .activate = emu_activate, + .transmit = emu_transmit, + .receive = emu_receive, + .close = emu_close, + .write_settings = emu_write_settings, + .card_write = emu_card_write, + .set_protocol = emu_set_protocol, +}; + +void add_emu_reader(void) +{ + // This function is called inside oscam.c and creates an emu [reader] with default + // settings in oscam.server file. If an emu [reader] already exists, it uses that. + + LL_ITER itr; + struct s_reader *rdr; + int8_t haveEmuReader = 0; + char *emuName = "emulator"; + char *ctab, *ftab, *emu_auproviders; + + // Check if emu [reader] entry already exists in oscam.server file and get it + itr = ll_iter_create(configured_readers); + while ((rdr = ll_iter_next(&itr))) + { + if (rdr->typ == R_EMU) + { + haveEmuReader = 1; + break; + } + } + + rdr = NULL; + + // If there's no emu [reader] in oscam.server, create one with default settings + if (!haveEmuReader) + { + if (!cs_malloc(&rdr, sizeof(struct s_reader))) + { + return; + } + + reader_set_defaults(rdr); + + rdr->enable = 1; + rdr->typ = R_EMU; + strncpy(rdr->label, emuName, strlen(emuName)); + strncpy(rdr->device, emuName, strlen(emuName)); + + // CAIDs + ctab = strdup("090F,0500,1801,0604,2600,FFFF,0E00,4AE1,1010"); + chk_caidtab(ctab, &rdr->ctab); + NULLFREE(ctab); + + // Idents + ftab = strdup("090F:000000;" + "0500:000000,023800,021110,007400,007800;" + "1801:000000,007301,001101,002111;" + "0604:000000;" + "2600:000000;" + "FFFF:000000;" + "0E00:000000;" + "4AE1:000011,000014,0000FE;" + "1010:000000;" + ); + chk_ftab(ftab, &rdr->ftab); + NULLFREE(ftab); + + // AU providers + emu_auproviders = strdup("0604:010200;0E00:000000;4AE1:000011,000014,0000FE;1010:000000;"); + chk_ftab(emu_auproviders, &rdr->emu_auproviders); + NULLFREE(emu_auproviders); + + // EMM cache + rdr->cachemm = 2; + rdr->rewritemm = 1; + rdr->logemm = 2; + rdr->deviceemm = 1; + + // User group + rdr->grp = 0x1ULL; + + // Add the "device" part to our emu reader + rdr->crdr = &cardreader_emu; + + reader_fixups_fn(rdr); + ll_append(configured_readers, rdr); + } + + // Set DVB Api delayer option +#ifdef HAVE_DVBAPI + if (cfg.dvbapi_enabled && cfg.dvbapi_delayer < 60) + { + cfg.dvbapi_delayer = 60; + } +#endif + + cs_log("OSCam-Emu version %d", GetOSemuVersion()); +} Index: module-newcamd-des.c =================================================================== --- module-newcamd-des.c (revision 11398) +++ module-newcamd-des.c (working copy) @@ -5,10 +5,7 @@ #define DES_IP 1 #define DES_IP_1 2 #define DES_RIGHT 4 -#define DES_HASH 8 -#define DES_ECM_CRYPT 0 -#define DES_ECM_HASH DES_HASH #define DES_ECS2_DECRYPT (DES_IP | DES_IP_1 | DES_RIGHT) #define DES_ECS2_CRYPT (DES_IP | DES_IP_1) @@ -356,7 +353,7 @@ swap(data - 4, data); } -static void nc_des(unsigned char key[], unsigned char mode, unsigned char data[]) +void nc_des(unsigned char key[], unsigned char mode, unsigned char data[]) { unsigned char i; unsigned char left[8]; Index: module-newcamd-des.h =================================================================== --- module-newcamd-des.h (revision 11398) +++ module-newcamd-des.h (working copy) @@ -1,8 +1,15 @@ #ifndef MODULE_NEWCAMD_DES_H_ #define MODULE_NEWCAMD_DES_H_ +#define DES_HASH 8 + +#define DES_ECM_CRYPT 0 +#define DES_ECM_HASH DES_HASH + int nc_des_encrypt(unsigned char *buffer, int len, unsigned char *deskey); int nc_des_decrypt(unsigned char *buffer, int len, unsigned char *deskey); unsigned char *nc_des_login_key_get(unsigned char *key1, unsigned char *key2, int len, unsigned char *des16); + void nc_des(unsigned char key[], unsigned char mode, unsigned char data[]); + #endif Index: module-newcamd.c =================================================================== --- module-newcamd.c (revision 11398) +++ module-newcamd.c (working copy) @@ -919,6 +919,13 @@ // set userfilter for au enabled clients if(aureader) { +#ifdef WITH_EMU + if(aureader->typ == R_EMU) + { + usr_filter = * get_emu_prids_for_caid(aureader, cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid); + } + else +#endif mk_user_au_ftab(aureader, &usr_filter); } @@ -948,6 +955,13 @@ else { memset(&mbuf[8], 0, 6); } //mbuf[8] - mbuf[13] +#ifdef WITH_EMU + if(aureader && aureader->typ == R_EMU && caid_is_dre(pufilt->caid)) + { + mbuf[10] = aureader->dre36_force_group; + } +#endif + mbuf[14] = pufilt->nprids; for(j = 0; j < pufilt->nprids; j++) { @@ -973,7 +987,7 @@ int32_t k, found; uint32_t rprid; found = 0; - if(pufilt->caid == aureader->caid) + if(pufilt->caid == aureader->caid && aureader->typ != R_EMU) { for(k = 0; (k < aureader->nprov); k++) { @@ -999,6 +1013,32 @@ } } } +#ifdef WITH_EMU + else if(aureader->typ == R_EMU) + { + if(caid_is_dre(pufilt->caid)) + { + found = 1; + memset(&mbuf[22 + 11 * j] ,0 ,4); + switch((uchar)(pufilt->prids[j])) + { + case 0x11: + mbuf[22 + 11 * j] = aureader->dre36_force_group; + break; + case 0x14: + mbuf[22 + 11 * j] = aureader->dre56_force_group; + break; + case 0xfe: + mbuf[22 + 11 * j] = 0xED; + mbuf[25 + 11 * j] = 0x02; + break; + default: + found = 0; + } + } + } +#endif + if(!found) { mbuf[22 + 11 * j] = 0x00; Index: module-stat.c =================================================================== --- module-stat.c (revision 11398) +++ module-stat.c (working copy) @@ -903,7 +903,7 @@ uint16_t get_rdr_caid(struct s_reader *rdr) { - if(is_network_reader(rdr)) + if(is_network_reader(rdr) || rdr->typ == R_EMU) { return 0; //reader caid is not real caid } @@ -1301,7 +1301,7 @@ for(ea = er->matching_rdr; ea; ea = ea->next) { rdr = ea->reader; - if(is_network_reader(rdr)) //reader caid is not real caid + if(is_network_reader(rdr) || rdr->typ == R_EMU) //reader caid is not real caid { prv = ea; continue; // proxy can convert or reject Index: module-webif-tpl.c =================================================================== --- module-webif-tpl.c (revision 11398) +++ module-webif-tpl.c (working copy) @@ -457,6 +457,7 @@ check_conf(WITH_SSL, ptr2); check_conf(WITH_STAPI, ptr2); check_conf(WITH_STAPI5, ptr2); + check_conf(WITH_EMU, ptr2); } // for if(ok == 0) { Index: module-webif.c =================================================================== --- module-webif.c (revision 11398) +++ module-webif.c (working copy) @@ -101,6 +101,7 @@ #define MNU_CFG_LCD 14 #define MNU_CFG_MONITOR 15 #define MNU_CFG_WEBIF 16 +#define MNU_CFG_STREAMRELAY 17 /* constants for files.html submenuactivating */ #define MNU_CFG_FVERSION 0 @@ -134,8 +135,9 @@ #define MNU_GBX_FSTAINF 27 #define MNU_GBX_FEXPINF 28 #define MNU_GBX_INFOLOG 29 +#define MNU_CFG_FSOFTCAMKEY 30 -#define MNU_CFG_TOTAL_ITEMS 30 // sum of items above. Use it for "All inactive" in function calls too. +#define MNU_CFG_TOTAL_ITEMS 31 // sum of config or files items above. Use it for "All inactive" in function calls too. static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value) { if (no_data) @@ -1216,6 +1218,34 @@ } #endif +#ifdef WITH_EMU +#include "module-emulator-stream.h" + +static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_STREAMRELAY); + + 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, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + return tpl_getTpl(vars, "CONFIGSTREAMRELAY"); +} +#endif + #ifdef MODULE_CCCAM static char *send_oscam_config_cccam(struct templatevars *vars, struct uriparams *params) { @@ -1554,6 +1584,10 @@ tpl_printf(vars, TPLADD, "TMP", "EXTENDEDCWAPISELECTED%d", cfg.dvbapi_extended_cw_api); tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + //extended_cw_pids (pid limiter) + tpl_printf(vars, TPLADD, "TMP", "EXTENDEDCWPIDSSELECTED%d", cfg.dvbapi_extended_cw_pids); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + //write_sdt_prov if(cfg.dvbapi_write_sdt_prov > 0) { tpl_addVar(vars, TPLADD, "WRITESDTPROVCHECKED", "checked"); } @@ -1631,6 +1665,9 @@ #ifdef MODULE_SCAM else if(!strcmp(part, "scam")) { return send_oscam_config_scam(vars, params); } #endif +#ifdef WITH_EMU + else if(!strcmp(part, "streamrelay")) { return send_oscam_config_streamrelay(vars, params); } +#endif #ifdef MODULE_CCCAM else if(!strcmp(part, "cccam")) { return send_oscam_config_cccam(vars, params); } #endif @@ -2093,7 +2130,7 @@ chk_reader("services", servicelabels, rdr); chk_reader("lb_whitelist_services", servicelabelslb, rdr); - if(is_network_reader(rdr)) //physical readers make trouble if re-started + if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started { restart_cardreader(rdr, 1); } @@ -2671,6 +2708,31 @@ tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript); #endif +#ifdef WITH_EMU + //emu_auproviders + value = mk_t_ftab(&rdr->emu_auproviders); + tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value); + free_mk_t(value); + + // Date-coded BISS keys + if(!apicall) + { + tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0"); + } + + //extee + tpl_addVar(vars, TPLADD, "EXTEE36", rdr->extee36); + tpl_addVar(vars, TPLADD, "EXTEE56", rdr->extee56); + + //dre force group + tpl_printf(vars, TPLADD, "DRE36FORCEGROUP","%02X", rdr->dre36_force_group); + tpl_printf(vars, TPLADD, "DRE56FORCEGROUP","%02X", rdr->dre56_force_group); +#endif + tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0)); // Show only parameters which needed for the reader @@ -2692,6 +2754,9 @@ case R_CAMD35 : tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT")); break; + case R_EMU : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT")); + break; case R_CS378X : tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT")); break; @@ -4414,9 +4479,38 @@ tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "

New Structure:
"); char tbuffer[83]; +#ifdef WITH_EMU + char keyBuffer[1024]; +#endif int jsondelimiter = 0; while((item = ll_iter_next(&itr))) { +#ifdef WITH_EMU + if(item->isKey) { + tpl_addVar(vars, TPLADD, "ENTSTARTDATE", ""); + tpl_addVar(vars, TPLADD, "ENTENDDATE", ""); + cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer)); + tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid"); + tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); + if(item->caid == 0x2600) { + tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid); + } + else { + tpl_printf(vars, TPLADD, "ENTPROVID", "%06X", item->provid); + } + tpl_addVar(vars, TPLADD, "ENTID", item->name); + tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer); + if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); } + else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); } + tpl_addVar(vars, TPLADD, "ENTRESNAME", ""); + + if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) + { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } + + continue; + } +#endif + localtime_r(&item->start, &start_t); localtime_r(&item->end, &end_t); @@ -4892,6 +4986,9 @@ #else filtered = (type == cl->typ); #endif +#ifdef WITH_EMU + if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1; +#endif } } @@ -6336,6 +6433,9 @@ { "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28 { "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29 #endif +#ifdef WITH_EMU + { "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30 +#endif { NULL, 0, 0 }, }; @@ -6806,7 +6906,7 @@ else if(!proxy && rdr->csystem_active) // local active reader { csystem = rdr->csystem; - caid = rdr->caid; + if(rdr->typ != R_EMU) caid = rdr->caid; } if(csystem) @@ -7813,8 +7913,8 @@ memcpy(*result + bufsize, buf2, n); bufsize += n; - //max request size 100kb - if(bufsize > 102400) + //max request size 200kb + if(bufsize > 204800) { cs_log("error: too much data received from %s", cs_inet_ntoa(in)); NULLFREE(*result); Index: oscam-aes.c =================================================================== --- oscam-aes.c (revision 11398) +++ oscam-aes.c (working copy) @@ -37,6 +37,16 @@ } } +void aes_cbc_encrypt(struct aes_keys *aes, uchar *buf, int32_t n, uchar *iv) +{ + AES_cbc_encrypt(buf, buf, n, &aes->aeskey_encrypt, iv, AES_ENCRYPT); +} + +void aes_cbc_decrypt(struct aes_keys *aes, uchar *buf, int32_t n, uchar *iv) +{ + AES_cbc_encrypt(buf, buf, n, &aes->aeskey_decrypt, iv, AES_DECRYPT); +} + /* Creates an AES_ENTRY and adds it to the given linked list. */ void add_aes_entry(AES_ENTRY **list, uint16_t caid, uint32_t ident, int32_t keyid, uchar *aesKey) { Index: oscam-aes.h =================================================================== --- oscam-aes.h (revision 11398) +++ oscam-aes.h (working copy) @@ -5,6 +5,8 @@ bool aes_set_key_alloc(struct aes_keys **aes, char *key); void aes_decrypt(struct aes_keys *aes, uchar *buf, int32_t n); void aes_encrypt_idx(struct aes_keys *aes, uchar *buf, int32_t n); +void aes_cbc_encrypt(struct aes_keys *aes, uchar *buf, int32_t n, uchar *iv); +void aes_cbc_decrypt(struct aes_keys *aes, uchar *buf, int32_t n, uchar *iv); void add_aes_entry(AES_ENTRY **list, uint16_t caid, uint32_t ident, int32_t keyid, uchar *aesKey); void parse_aes_entry(AES_ENTRY **list, char *label, char *value); Index: oscam-chk.c =================================================================== --- oscam-chk.c (revision 11398) +++ oscam-chk.c (working copy) @@ -758,7 +758,7 @@ return 0; } - if(!is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF))) + if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF))) { if (!rdr->csystem) return 0; @@ -794,7 +794,7 @@ } //Checking ident: - if(!chk_rfilter(er, rdr)) + if(!(rdr->typ == R_EMU && (er->caid>>8 == 0x26 || er->caid == 0xFFFF)) && !chk_rfilter(er, rdr)) { cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label); return (0); @@ -853,7 +853,7 @@ } //Checking entitlements: - if(ll_count(rdr->ll_entitlements) > 0) + if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU)) { LL_ITER itr = ll_iter_create(rdr->ll_entitlements); S_ENTITLEMENT *item; @@ -1024,7 +1024,7 @@ int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid) { - if(is_network_reader(rdr)) + if(is_network_reader(rdr) || rdr->typ == R_EMU) { return 1; //reader caid is not real caid } Index: oscam-config-global.c =================================================================== --- oscam-config-global.c (revision 11398) +++ oscam-config-global.c (working copy) @@ -854,6 +854,30 @@ #else static const struct config_list scam_opts[] = { DEF_LAST_OPT }; #endif + +#ifdef WITH_EMU +static bool streamrelay_should_save_fn(void *UNUSED(var)) +{ + return 1; +} +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_LAST_OPT +}; +#else +static const struct config_list streamrelay_opts[] = { DEF_LAST_OPT }; +#endif + + #ifdef MODULE_RADEGAST static bool radegast_should_save_fn(void *UNUSED(var)) { @@ -1236,6 +1260,7 @@ 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), + DEF_OPT_INT8("extended_cw_pids", OFS(dvbapi_extended_cw_pids), 64), // pid limiter DEF_OPT_FUNC("boxtype" , OFS(dvbapi_boxtype), dvbapi_boxtype_fn), DEF_OPT_FUNC("services" , OFS(dvbapi_sidtabs.ok), dvbapi_services_fn), // OBSOLETE OPTIONS @@ -1289,6 +1314,7 @@ { "cccam", cccam_opts }, { "pandora", pandora_opts }, { "scam", scam_opts }, + { "streamrelay", streamrelay_opts }, { "dvbapi", dvbapi_opts }, { "monitor", monitor_opts }, { "webif", webif_opts }, Index: oscam-config-reader.c =================================================================== --- oscam-config-reader.c (revision 11398) +++ oscam-config-reader.c (working copy) @@ -109,6 +109,7 @@ { "newcamd525", R_NEWCAMD }, { "newcamd524", R_NEWCAMD }, { "drecas", R_DRECAS }, + { "emu", R_EMU }, { NULL , 0 } }, *p; int i; @@ -448,6 +449,9 @@ if(ftab_type & FTAB_FBPCAID) { rdr = container_of(setting, struct s_reader, fallback_percaid); } if(ftab_type & FTAB_LOCALCARDS) { rdr = container_of(setting, struct s_reader, localcards); } if(ftab_type & FTAB_IGNCHKSMCAID){ rdr = container_of(setting, struct s_reader, disablecrccws_only_for); } +#ifdef WITH_EMU + if(ftab_type & FTAB_EMUAU) { rdr = container_of(setting, struct s_reader, emu_auproviders); } +#endif if(rdr) { rdr->changes_since_shareupdate = 1; } } @@ -779,7 +783,7 @@ } -static void reader_fixups_fn(void *var) +void reader_fixups_fn(void *var) { struct s_reader *rdr = var; #ifdef WITH_LB @@ -923,6 +927,14 @@ #ifdef READER_DRECAS DEF_OPT_STR("stmkeys" , OFS(stmkeys), NULL), #endif +#ifdef WITH_EMU + DEF_OPT_FUNC_X("emu_auproviders" , OFS(emu_auproviders), ftab_fn, FTAB_READER | FTAB_EMUAU), + DEF_OPT_INT8("emu_datecodedenabled" , OFS(emu_datecodedenabled), 0), + DEF_OPT_STR("extee36" , OFS(extee36), NULL), + DEF_OPT_STR("extee56" , OFS(extee56), NULL), + DEF_OPT_HEX("dre36_force_group" , OFS(dre36_force_group), 1), + DEF_OPT_HEX("dre56_force_group" , OFS(dre56_force_group), 1), +#endif DEF_OPT_INT8("deprecated" , OFS(deprecated), 0), DEF_OPT_INT8("audisabled" , OFS(audisabled), 0), DEF_OPT_FUNC("auprovid" , 0, auprovid_fn), Index: oscam-config.h =================================================================== --- oscam-config.h (revision 11398) +++ oscam-config.h (working copy) @@ -22,6 +22,7 @@ int32_t free_readerdb(void); int32_t write_server(void); void reload_readerdb(void); +void reader_fixups_fn(void *var); void chk_sidtab(char *token, char *value, struct s_sidtab *sidtab); int32_t init_sidtab(void); @@ -63,7 +64,8 @@ FTAB_FBPCAID = 0x10, FTAB_LOCALCARDS = 0x20, FTAB_IGNCHKSMCAID = 0x40, - FTAB_IGNCRCCEX4USERONLYFOR = 0x80 + FTAB_IGNCRCCEX4USERONLYFOR = 0x80, + FTAB_EMUAU = 0x100 }; void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE *f); Index: oscam-ecm.c =================================================================== --- oscam-ecm.c (revision 11398) +++ oscam-ecm.c (working copy) @@ -1584,7 +1584,7 @@ if(reader && cw && rc < E_NOTFOUND) { - if(cfg.disablecrccws == 0 && reader->disablecrccws == 0) + if(cfg.disablecrccws == 0 && reader->disablecrccws == 0 && ((er->caid >> 8) != 0x0E)) { uint8_t selectedForIgnChecksum = chk_if_ignore_checksum(er, cfg.disablecrccws, &cfg.disablecrccws_only_for) + chk_if_ignore_checksum(er, reader->disablecrccws, &reader->disablecrccws_only_for); Index: oscam-emm.c =================================================================== --- oscam-emm.c (revision 11398) +++ oscam-emm.c (working copy) @@ -50,7 +50,14 @@ unsigned int j, filter_count = 0; // Call cardsystems emm filter - csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); + if(rdr->typ == R_EMU) + { + return 1; //valid emm + } + else + { + csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); + } // Only check matching emmtypes: uint8_t org_emmtype; @@ -209,6 +216,24 @@ rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X fixup to %06X (ignoring last digit)", reader->auprovid, prid); } +#ifdef WITH_EMU + if(reader->typ == R_EMU) + { + FILTER* emu_provids = get_emu_prids_for_caid(reader, caid); + if(emu_provids != NULL) + { + for(i = 0; i < emu_provids->nprids; i++) + { + if(provid == emu_provids->prids[i]) + { + return 1; + } + } + } + return 0; + } +#endif + if(prid == provid) { rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X matching with emm provid = %06X -> SEND!", prid, provid); Index: oscam-string.c =================================================================== --- oscam-string.c (revision 11398) +++ oscam-string.c (working copy) @@ -510,6 +510,62 @@ return crc ^ 0xffffffffL; } +static uint32_t fletcher_crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4}; + +uint32_t fletcher_crc32(uint8_t *data, uint32_t len) +{ + uint32_t i; + uint32_t crc = 0xffffffff; + + for (i=0; i> 24) ^ *data++) & 0xff]; + + return crc; +} + static uint16_t ccitt_crc_table [256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, Index: oscam-string.h =================================================================== --- oscam-string.h (revision 11398) +++ oscam-string.h (working copy) @@ -37,6 +37,7 @@ void get_random_bytes(uint8_t *dst, uint32_t dst_len); uint32_t crc32(uint32_t crc, const uint8_t *buf, uint32_t len); +uint32_t fletcher_crc32(uint8_t *data, uint32_t len); uint16_t ccitt_crc(uint8_t *data, size_t length, uint16_t seed, uint16_t final); uint32_t jhash(const char *key, size_t len); Index: oscam.c =================================================================== --- oscam.c (revision 11398) +++ oscam.c (working copy) @@ -41,6 +41,11 @@ #include "reader-common.h" #include "module-gbox.h" +#ifdef WITH_EMU + void add_emu_reader(void); + void stop_stream_server(void); +#endif + #ifdef WITH_SSL #include #include @@ -421,6 +426,7 @@ write_conf(CW_CYCLE_CHECK, "CW Cycle Check support"); write_conf(LCDSUPPORT, "LCD support"); write_conf(LEDSUPPORT, "LED support"); + write_conf(WITH_EMU, "Emulator support"); switch (cs_getclocktype()) { case CLOCK_TYPE_UNKNOWN : write_conf(CLOCKFIX, "Clockfix with UNKNOWN clock"); break; case CLOCK_TYPE_REALTIME : write_conf(CLOCKFIX, "Clockfix with realtime clock"); break; @@ -1639,6 +1645,9 @@ #ifdef CARDREADER_STINGER &cardreader_stinger, #endif +#ifdef WITH_EMU + &cardreader_emu, +#endif NULL }; @@ -1810,6 +1819,9 @@ init_sidtab(); init_readerdb(); +#ifdef WITH_EMU + add_emu_reader(); +#endif cfg.account = init_userdb(); init_signal(); init_provid(); @@ -1897,6 +1909,9 @@ if(!cfg.gsms_dis) { stop_sms_sender(); } #endif +#ifdef WITH_EMU + stop_stream_server(); +#endif webif_close(); azbox_close(); coolapi_close_all(); Index: reader-common.c =================================================================== --- reader-common.c (revision 11398) +++ reader-common.c (working copy) @@ -15,6 +15,7 @@ #include "reader-common.h" //#include "csctapi/atr.h" #include "csctapi/icc_async.h" +#include "readers.h" extern const struct s_cardsystem *cardsystems[]; extern char *RDR_CD_TXT[]; @@ -142,6 +143,19 @@ static int32_t reader_get_cardsystem(struct s_reader *reader, ATR *atr) { int32_t i; + +#ifdef WITH_EMU + if(reader->typ == R_EMU) + { + NULLFREE(reader->csystem_data); + rdr_log(reader, "found card system %s", reader_emu.desc); + reader->csystem = &reader_emu; + reader->csystem_active = true; + led_status_found_cardsystem(); + return (reader->csystem_active); + } +#endif + for(i = 0; cardsystems[i]; i++) { NULLFREE(reader->csystem_data); Index: readers.h =================================================================== --- readers.h (revision 11398) +++ readers.h (working copy) @@ -16,5 +16,6 @@ extern const struct s_cardsystem reader_bulcrypt; extern const struct s_cardsystem reader_griffin; extern const struct s_cardsystem reader_dgcrypt; +extern const struct s_cardsystem reader_emu; #endif Index: webif/config/dvbapi.html =================================================================== --- webif/config/dvbapi.html (revision 11398) +++ webif/config/dvbapi.html (working copy) @@ -53,7 +53,7 @@ Write detected prov name to srvid: - \ No newline at end of file + + + Max pids for extended CWs + + + + \ No newline at end of file Index: webif/config/menu.html =================================================================== --- webif/config/menu.html (revision 11398) +++ webif/config/menu.html (working copy) @@ -16,6 +16,7 @@ ##TPLCONFIGMENUDVBAPI## ##TPLCONFIGMENULCD## ##TPLCONFIGMENUMONITOR## +##TPLCONFIGMENUSTREAMRELAY##
  • WebIf
  • Index: webif/config/menu_streamrelay.html =================================================================== --- webif/config/menu_streamrelay.html (revision 0) +++ webif/config/menu_streamrelay.html (working copy) @@ -0,0 +1 @@ +
  • Stream Relay
  • Index: webif/config/streamrelay.html =================================================================== --- webif/config/streamrelay.html (revision 0) +++ webif/config/streamrelay.html (working copy) @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file Index: webif/files/menu.html =================================================================== --- webif/files/menu.html (revision 11398) +++ webif/files/menu.html (working copy) @@ -10,7 +10,7 @@
  • logfile
  • userfile
  • ##TPLFILEMENUGBOX## -
  • other files +
  • other files
  • Index: webif/files/menu_softcamkey.html =================================================================== --- webif/files/menu_softcamkey.html (revision 0) +++ webif/files/menu_softcamkey.html (working copy) @@ -0,0 +1 @@ +
  • SoftCam.Key
  • Index: webif/pages_index.txt =================================================================== --- webif/pages_index.txt (revision 11398) +++ webif/pages_index.txt (working copy) @@ -80,11 +80,13 @@ CONFIGMENUNEWCAMD config/menu_newcamd.html MODULE_NEWCAMD CONFIGMENURADEGAST config/menu_radegast.html MODULE_RADEGAST CONFIGMENUSCAM config/menu_scam.html MODULE_SCAM +CONFIGMENUSTREAMRELAY config/menu_streamrelay.html WITH_EMU CONFIGMENUSERIAL config/menu_serial.html MODULE_SERIAL CONFIGMONITOR config/monitor.html MODULE_MONITOR CONFIGNEWCAMD config/newcamd.html MODULE_NEWCAMD CONFIGRADEGAST config/radegast.html MODULE_RADEGAST CONFIGSCAM config/scam.html MODULE_SCAM +CONFIGSTREAMRELAY config/streamrelay.html WITH_EMU CONFIGSERIAL config/serial.html MODULE_SERIAL CONFIGSERIALDEVICEBIT config/serial_devices.html MODULE_SERIAL CONFIGWEBIF config/webif.html @@ -118,6 +120,7 @@ FILEMENUFAKECWS files/menu_fakecws.html CS_CACHEEX FILEMENUGBOX files/menu_gbox.html MODULE_GBOX FILEMENUTWIN files/menu_twin.html MODULE_SERIAL +FILEMENUSOFTCAMKEY files/menu_softcamkey.html WITH_EMU AUTOCONF ghttp/autoconf.html MODULE_GHTTP PREAUTOCONF ghttp/pre_autoconf.html MODULE_GHTTP @@ -182,6 +185,7 @@ READEREDITCACHEEXBIT readerconfig/readerconfig_cacheexbit.html CS_CACHEEX READERCONFIGCAMD35BIT readerconfig/readerconfig_camd35bit.html MODULE_CAMD35 READERCONFIGCCCAMBIT readerconfig/readerconfig_cccambit.html MODULE_CCCAM +READERCONFIGEMUBIT readerconfig/readerconfig_emubit.html WITH_EMU READERCONFIGCS378XBIT readerconfig/readerconfig_cs378xbit.html MODULE_CAMD35_TCP READERCONFIGGBOXBIT readerconfig/readerconfig_gboxbit.html MODULE_GBOX READERCONFIGGHTTPBIT readerconfig/readerconfig_ghttpbit.html MODULE_GHTTP Index: webif/readerconfig/readerconfig_emubit.html =================================================================== --- webif/readerconfig/readerconfig_emubit.html (revision 0) +++ webif/readerconfig/readerconfig_emubit.html (working copy) @@ -0,0 +1,6 @@ + + + + + +
    Edit Stream Relay Config
    Mode (requires OSCam restart) + +
    Source Stream Host:
    Source Stream Port:
    Source Stream User:
    Source Stream Password:
    Relay Port:
    ECM fix delay:
    Process EMM from stream: + +
    AU providers:
    [BISS] Enable date-coded keys:
    [Drecrypt] Path to ee36.bin:
    [Drecrypt] Path to ee56.bin:
    [Drecrypt] Center force group:
    [Drecrypt] Siberia force group: