From ef385087a8736c312ba0c718a32a217c8e5b870d Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 18 Dec 2018 14:01:33 +0100 Subject: [PATCH 1/3] Implemented des functions + XcCryptService --- build/win32/Cxbx.vcxproj | 2 + build/win32/Cxbx.vcxproj.filters | 6 + src/common/crypto/EmuDes.cpp | 660 ++++++++++++++++++++++++++ src/common/crypto/EmuDes.h | 73 +++ src/core/kernel/exports/EmuKrnlXc.cpp | 58 ++- 5 files changed, 774 insertions(+), 25 deletions(-) create mode 100644 src/common/crypto/EmuDes.cpp create mode 100644 src/common/crypto/EmuDes.h diff --git a/build/win32/Cxbx.vcxproj b/build/win32/Cxbx.vcxproj index 5a83890d8..a939b59dd 100644 --- a/build/win32/Cxbx.vcxproj +++ b/build/win32/Cxbx.vcxproj @@ -198,6 +198,7 @@ + @@ -342,6 +343,7 @@ + diff --git a/build/win32/Cxbx.vcxproj.filters b/build/win32/Cxbx.vcxproj.filters index febe32b64..f72f03b0f 100644 --- a/build/win32/Cxbx.vcxproj.filters +++ b/build/win32/Cxbx.vcxproj.filters @@ -632,6 +632,9 @@ core\HLE + + Emulator + @@ -812,5 +815,8 @@ Hardware\Video + + Emulator + \ No newline at end of file diff --git a/src/common/crypto/EmuDes.cpp b/src/common/crypto/EmuDes.cpp new file mode 100644 index 000000000..faa294628 --- /dev/null +++ b/src/common/crypto/EmuDes.cpp @@ -0,0 +1,660 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * .,-::::: .,:: .::::::::. .,:: .: +// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;; +// * [[[ '[[,,[[' [[[__[[\. '[[,,[[' +// * $$$ Y$$$P $$""""Y$$ Y$$$P +// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo, +// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm, +// * +// * common->crypto->EmuDes.cpp +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them 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 recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018 ergo720 +// * +// * All rights reserved +// * +// ****************************************************************** + +// Acknowledgment: ReactOS (GPLv2) +// https://github.com/reactos/reactos + +// Changed from ReactOS: we never swap the subkeys in order to generate a decryption key schedule. Instead, we opt to simply read the subkeys +// in reverse order during a decryption operation. This is necessary because XcKeyTable doesn't provide an "operation" argument, that is, it +// always generates an encryption key schedule. + +// NOTE: this des implementation doesn't produce exactly the same chipertext produced by the Xbox. I found that the implementation of Eric Young +// used in OpenSSL does instead, but we can't use it since it's under the Apache 2.0 license, which is incompatible with GPLv2. For reference, +// the DES-YOUN.zip package at https://www.schneier.com/books/applied_cryptography/source.html contains a previous version of the same code under +// the GPLv2 but again it doesn't produce the same chipertext, and modifying it to make it so would make it identical to the OpenSSL code, which +// is probably a violation of the license, so I won't do it. In practice, as long as the code correctly decrypts the chipertext (which it does), +// I don't think that the Xbox games will care if the chipertext is not exactly the same as on the Xbox and should work fine offline. The only +// problem with this is that cxbxr will be unable to comunicate with a real Xbox on the network (des is used in console-to-console comunications) +// because the des key schedule generated on the console will be different and so it will fail to decrypt packets encrypted by cxbxr. + + +#include +#include +#include "EmuDes.h" + +/* +* 32-bit integer manipulation macros (big endian) +*/ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* +* Expanded DES S-boxes +*/ +static const uint32_t SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* +* PC1: left and right halves bit-swap +*/ +static const uint32_t LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* +* Initial Permutation macro +*/ +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* +* Final Permutation macro +*/ +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* +* DES round macro +*/ +#define DES_ROUND(X,Y,I) \ +{ \ + T = (SK[I] ^ X); \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = (SK[I+1] ^ ((X << 28) | (X >> 4))); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +// For each number between 0x0 and 0xF, this tells how many set bits there are +static const unsigned char DESParityTable[] = { 0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04 }; + + +// This function sets the parity on the DES key to be odd +// Test case: Halo, Tenchu, Dashboard, Splinter Cell 1 and 2, ... +void mbedtls_des_key_set_parity(unsigned char* Key, unsigned long KeyLenght) +{ + unsigned long i; + + for (i = 0; i < KeyLenght; i++) + { + if (!((DESParityTable[Key[i] >> 4] + DESParityTable[Key[i] & 0x0F]) % 2)) { + Key[i] = Key[i] ^ 0x01; + } + } +} + +void mbedtls_des_setkey(uint32_t SK[32], const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + uint32_t X, Y, T; + + GET_UINT32_BE(X, key, 0); + GET_UINT32_BE(Y, key, 4); + + /* + * Permuted Choice 1 + */ + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y) ^ X) & 0x10101010; X ^= T; Y ^= (T); + + X = (LHs[(X) & 0xF] << 3) | (LHs[(X >> 8) & 0xF] << 2) + | (LHs[(X >> 16) & 0xF] << 1) | (LHs[(X >> 24) & 0xF]) + | (LHs[(X >> 5) & 0xF] << 7) | (LHs[(X >> 13) & 0xF] << 6) + | (LHs[(X >> 21) & 0xF] << 5) | (LHs[(X >> 29) & 0xF] << 4); + + Y = (RHs[(Y >> 1) & 0xF] << 3) | (RHs[(Y >> 9) & 0xF] << 2) + | (RHs[(Y >> 17) & 0xF] << 1) | (RHs[(Y >> 25) & 0xF]) + | (RHs[(Y >> 4) & 0xF] << 7) | (RHs[(Y >> 12) & 0xF] << 6) + | (RHs[(Y >> 20) & 0xF] << 5) | (RHs[(Y >> 28) & 0xF] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* + * calculate subkeys + */ + for (i = 0; i < 16; i++) + { + if (i < 2 || i == 8 || i == 15) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* +* DES key schedule (56-bit, encryption) +*/ +void mbedtls_des_setkey_enc(mbedtls_des_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + mbedtls_des_setkey(ctx->sk, key); +} + +/* +* DES-ECB block encryption/decryption +*/ +void mbedtls_des_crypt_ecb(mbedtls_des_context* ctx, + const unsigned char input[8], + unsigned char output[8], + unsigned long encrypt) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE(X, input, 0); + GET_UINT32_BE(Y, input, 4); + + DES_IP(X, Y); + + if (encrypt == MBEDTLS_DES_ENCRYPT) { + for (i = 0; i < 32; i += 4) + { + DES_ROUND(Y, X, i + 0); + DES_ROUND(X, Y, i + 2); + } + } + else { + for (i = 30; i > 0; i -= 4) + { + DES_ROUND(Y, X, i - 0); + DES_ROUND(X, Y, i - 2); + } + } + + DES_FP(Y, X); + + PUT_UINT32_BE(Y, output, 0); + PUT_UINT32_BE(X, output, 4); +} + +/* +* DES-CBC buffer encryption/decryption +*/ +int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, + unsigned long mode, + unsigned long length, + unsigned char iv[8], + const unsigned char* input, + unsigned char* output) +{ + int i; + unsigned char temp[8]; + + // I'm undecided regarding this check. The original code of ReactOS correctly checks that the input lenght is a multiple of a + // des_block (8 bytes) but the kernel doesn't and will encrypt up to block (lenght + 7) / 8. However, if we do that, we'll run + // the risk of reading some random bytes after the buffer end and/or touching invalid memory and crash. + if (length % 8) { + return MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_DES_ENCRYPT) + { + while (length > 0) + { + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(input[i] ^ iv[i]); + } + + mbedtls_des_crypt_ecb(ctx, output, output, MBEDTLS_DES_ENCRYPT); + memcpy(iv, output, 8); + + input += 8; + output += 8; + length -= 8; + } + } + else /* MBEDTLS_DES_DECRYPT */ + { + while (length > 0) + { + memcpy(temp, input, 8); + mbedtls_des_crypt_ecb(ctx, input, output, MBEDTLS_DES_DECRYPT); + + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(output[i] ^ iv[i]); + } + + memcpy(iv, temp, 8); + + input += 8; + output += 8; + length -= 8; + } + } + + return 0; +} + +/* +* Triple-DES key schedule (168-bit, encryption) +*/ +void mbedtls_des3_set3key_enc(mbedtls_des3_context* ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]) +{ + mbedtls_des_setkey(ctx->sk, key); + mbedtls_des_setkey(ctx->sk + 32, key + 8); + mbedtls_des_setkey(ctx->sk + 64, key + 16); +} + +/* +* 3DES-ECB buffer encryption +*/ +void mbedtls_des3_encrypt_ecb(mbedtls_des3_context* ctx, + const unsigned char input[8], + unsigned char output[8]) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE(X, input, 0); + GET_UINT32_BE(Y, input, 4); + + DES_IP(X, Y); + + for (i = 0; i < 32; i += 4) + { + DES_ROUND(Y, X, i + 0); + DES_ROUND(X, Y, i + 2); + } + + for (i = 62; i > 32; i -= 4) + { + DES_ROUND(X, Y, i - 0); + DES_ROUND(Y, X, i - 2); + } + + for (i = 64; i < 96; i += 4) + { + DES_ROUND(Y, X, i + 0); + DES_ROUND(X, Y, i + 2); + } + + DES_FP(Y, X); + + PUT_UINT32_BE(Y, output, 0); + PUT_UINT32_BE(X, output, 4); +} + +/* +* 3DES-ECB buffer decryption +*/ +void mbedtls_des3_decrypt_ecb(mbedtls_des3_context* ctx, + const unsigned char input[8], + unsigned char output[8]) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE(X, input, 0); + GET_UINT32_BE(Y, input, 4); + + DES_IP(X, Y); + + for (i = 94; i > 64; i -= 4) + { + DES_ROUND(Y, X, i - 0); + DES_ROUND(X, Y, i - 2); + } + + for (i = 32; i < 64; i += 4) + { + DES_ROUND(X, Y, i + 0); + DES_ROUND(Y, X, i + 2); + } + + for (i = 30; i > 0; i -= 4) + { + DES_ROUND(Y, X, i - 0); + DES_ROUND(X, Y, i - 2); + } + + DES_FP(Y, X); + + PUT_UINT32_BE(Y, output, 0); + PUT_UINT32_BE(X, output, 4); +} + +void mbedtls_des3_crypt_ecb(mbedtls_des3_context* ctx, + const unsigned char input[8], + unsigned char output[8], + unsigned long encrypt) +{ + if (encrypt == MBEDTLS_DES_ENCRYPT) { + mbedtls_des3_encrypt_ecb(ctx, input, output); + } + else { + mbedtls_des3_decrypt_ecb(ctx, input, output); + } +} + +/* +* 3DES-CBC buffer encryption/decryption +*/ +int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, + unsigned long mode, + unsigned long length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output) +{ + int i; + unsigned char temp[8]; + + if (length % 8) { + return MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_DES_ENCRYPT) + { + while (length > 0) + { + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(input[i] ^ iv[i]); + } + + mbedtls_des3_encrypt_ecb(ctx, output, output); + memcpy(iv, output, 8); + + input += 8; + output += 8; + length -= 8; + } + } + else /* MBEDTLS_DES_DECRYPT */ + { + while (length > 0) + { + memcpy(temp, input, 8); + mbedtls_des3_decrypt_ecb(ctx, input, output); + + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(output[i] ^ iv[i]); + } + + memcpy(iv, temp, 8); + + input += 8; + output += 8; + length -= 8; + } + } + + return 0; +} diff --git a/src/common/crypto/EmuDes.h b/src/common/crypto/EmuDes.h new file mode 100644 index 000000000..4fa178b5a --- /dev/null +++ b/src/common/crypto/EmuDes.h @@ -0,0 +1,73 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * .,-::::: .,:: .::::::::. .,:: .: +// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;; +// * [[[ '[[,,[[' [[[__[[\. '[[,,[[' +// * $$$ Y$$$P $$""""Y$$ Y$$$P +// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo, +// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm, +// * +// * common->crypto->EmuDes.h +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them 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 recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018 ergo720 +// * +// * All rights reserved +// * +// ****************************************************************** + +#ifndef EMUDES_H +#define EMUDES_H + +#define MBEDTLS_DES_KEY_SIZE 8 + +#define MBEDTLS_DES_ENCRYPT 1 +#define MBEDTLS_DES_DECRYPT 0 + +#define MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH -0x0032 /**< The data input has an invalid length. */ + +/** +* \brief DES context structure +*/ +typedef struct +{ + uint32_t sk[32]; /*!< DES subkeys */ +} +mbedtls_des_context; + +/** +* \brief Triple-DES context structure +*/ +typedef struct +{ + uint32_t sk[96]; /*!< 3DES subkeys */ +} +mbedtls_des3_context; + +void mbedtls_des_key_set_parity(unsigned char* Key, unsigned long KeyLenght); +void mbedtls_des_setkey_enc(mbedtls_des_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]); +void mbedtls_des_crypt_ecb(mbedtls_des_context* ctx, const unsigned char input[8], unsigned char output[8], unsigned long encrypt); +int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, unsigned long mode, unsigned long length, unsigned char iv[8], const unsigned char* input, unsigned char* output); +void mbedtls_des3_set3key_enc(mbedtls_des3_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]); +void mbedtls_des3_crypt_ecb(mbedtls_des3_context* ctx, const unsigned char input[8], unsigned char output[8], unsigned long encrypt); +int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, unsigned long mode, unsigned long length, unsigned char iv[8], const unsigned char *input, unsigned char *output); + +#endif EMUDES_H diff --git a/src/core/kernel/exports/EmuKrnlXc.cpp b/src/core/kernel/exports/EmuKrnlXc.cpp index 58806eb43..52233a2cc 100644 --- a/src/core/kernel/exports/EmuKrnlXc.cpp +++ b/src/core/kernel/exports/EmuKrnlXc.cpp @@ -47,18 +47,15 @@ namespace xboxkrnl #include "Logging.h" // For LOG_FUNC() #include "EmuKrnlLogging.h" #include "common\crypto\EmuSha.h" // For A_SHAInit, etc. -#include "common\crypto\LibRc4.h" // For RC4 Functions +#include "common\crypto\LibRc4.h" // For RC4 Functions +#include "common\crypto\EmuDes.h" // For DES Functions // prevent name collisions namespace NtDll { #include "core\kernel\support\EmuNtDll.h" }; - - -// Used by JumpedDESKeyParity -static const xboxkrnl::BYTE DESParityTable[] = { 0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04 }; - + // The following are the default implementations of the crypto functions @@ -237,15 +234,7 @@ xboxkrnl::VOID NTAPI JumpedDESKeyParity xboxkrnl::ULONG dwKeyLength ) { - // This function sets the parity on the DES key to be odd - // Test case: Halo, Tenchu, dashboard, Splinter Cell 1 and 2, ... - - for (DWORD i = 0; i < dwKeyLength; i++) - { - if (!((DESParityTable[pbKey[i] >> 4] + DESParityTable[pbKey[i] & 0x0F]) % 2)) { - pbKey[i] = pbKey[i] ^ 0x01; - } - } + mbedtls_des_key_set_parity(pbKey, dwKeyLength); } xboxkrnl::VOID NTAPI JumpedKeyTable @@ -254,8 +243,13 @@ xboxkrnl::VOID NTAPI JumpedKeyTable xboxkrnl::PUCHAR pbKeyTable, xboxkrnl::PUCHAR pbKey ) -{ - LOG_UNIMPLEMENTED(); +{ + if (dwCipher) { + mbedtls_des3_set3key_enc((mbedtls_des3_context*)pbKeyTable, pbKey); + } + else { + mbedtls_des_setkey_enc((mbedtls_des_context*)pbKeyTable, pbKey); + } } xboxkrnl::VOID NTAPI JumpedBlockCrypt @@ -266,8 +260,13 @@ xboxkrnl::VOID NTAPI JumpedBlockCrypt xboxkrnl::PUCHAR pbKeyTable, xboxkrnl::ULONG dwOp ) -{ - LOG_UNIMPLEMENTED(); +{ + if (dwCipher) { + mbedtls_des3_crypt_ecb((mbedtls_des3_context*)pbKeyTable, pbInput, pbOutput, dwOp); + } + else { + mbedtls_des_crypt_ecb((mbedtls_des_context*)pbKeyTable, pbInput, pbOutput, dwOp); + } } xboxkrnl::VOID NTAPI JumpedBlockCryptCBC @@ -280,8 +279,19 @@ xboxkrnl::VOID NTAPI JumpedBlockCryptCBC xboxkrnl::ULONG dwOp, xboxkrnl::PUCHAR pbFeedback ) -{ - LOG_UNIMPLEMENTED(); +{ + int ret; + + if (dwCipher) { + ret = mbedtls_des3_crypt_cbc((mbedtls_des3_context*)pbKeyTable, dwOp, dwInputLength, pbFeedback, pbInput, pbOutput); + } + else { + ret = mbedtls_des_crypt_cbc((mbedtls_des_context*)pbKeyTable, dwOp, dwInputLength, pbFeedback, pbInput, pbOutput); + } + + if (ret == MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH) { + DBG_PRINTF("%s: dwInputLength was not a multiple of 8 (it was %lu)\n", __func__, dwInputLength); + } } xboxkrnl::ULONG NTAPI JumpedCryptService @@ -290,11 +300,9 @@ xboxkrnl::ULONG NTAPI JumpedCryptService xboxkrnl::PVOID pArgs ) { - ULONG ret = 0; + // This seems to be a dummy function. It just returns zero regardless of the input arguments, which are left unchanged. - LOG_UNIMPLEMENTED(); - - return ret; + return 0; } /* This struct contains the original crypto functions exposed by the kernel */ From 25d6fbc902c241b12e7193c95987dbf8f7703d77 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 18 Dec 2018 18:46:12 +0100 Subject: [PATCH 2/3] Allow buggy behaviour in cbc functions --- src/common/crypto/EmuDes.cpp | 51 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/common/crypto/EmuDes.cpp b/src/common/crypto/EmuDes.cpp index faa294628..280270192 100644 --- a/src/common/crypto/EmuDes.cpp +++ b/src/common/crypto/EmuDes.cpp @@ -41,13 +41,13 @@ // in reverse order during a decryption operation. This is necessary because XcKeyTable doesn't provide an "operation" argument, that is, it // always generates an encryption key schedule. -// NOTE: this des implementation doesn't produce exactly the same chipertext produced by the Xbox. I found that the implementation of Eric Young +// NOTE: this des implementation doesn't produce exactly the same ciphertext produced by the Xbox. I found that the implementation of Eric Young // used in OpenSSL does instead, but we can't use it since it's under the Apache 2.0 license, which is incompatible with GPLv2. For reference, // the DES-YOUN.zip package at https://www.schneier.com/books/applied_cryptography/source.html contains a previous version of the same code under -// the GPLv2 but again it doesn't produce the same chipertext, and modifying it to make it so would make it identical to the OpenSSL code, which -// is probably a violation of the license, so I won't do it. In practice, as long as the code correctly decrypts the chipertext (which it does), -// I don't think that the Xbox games will care if the chipertext is not exactly the same as on the Xbox and should work fine offline. The only -// problem with this is that cxbxr will be unable to comunicate with a real Xbox on the network (des is used in console-to-console comunications) +// the GPLv2 but again it doesn't produce the same ciphertext, and modifying it to make it so would make it identical to the OpenSSL code, which +// is probably a violation of the license, so I won't do it. In practice, as long as the code correctly decrypts the ciphertext (which it does), +// I don't think that the Xbox games will care if the ciphertext is not exactly the same as on the Xbox and should work fine offline. The only +// problem with this is that cxbxr will be unable to communicate with a real Xbox on the network (des is used in console-to-console communications) // because the des key schedule generated on the console will be different and so it will fail to decrypt packets encrypted by cxbxr. @@ -450,19 +450,23 @@ int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, const unsigned char* input, unsigned char* output) { - int i; + int i, ret, num_des_blocks; unsigned char temp[8]; - // I'm undecided regarding this check. The original code of ReactOS correctly checks that the input lenght is a multiple of a - // des_block (8 bytes) but the kernel doesn't and will encrypt up to block (lenght + 7) / 8. However, if we do that, we'll run - // the risk of reading some random bytes after the buffer end and/or touching invalid memory and crash. + ret = 0; + num_des_blocks = (length + 7) / 8; + + // The original code of ReactOS correctly checks that the input lenght is a multiple of a des_block (8 bytes) but the + // kernel doesn't and will encrypt up to block (lenght + 7) / 8. This means that we'll run the risk of reading some + // random bytes after the buffer end and/or touching invalid memory and crash. Because the real kernel does it, we'll + // allow this buggy behaviour for the sake of accuracy. if (length % 8) { - return MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + ret = MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; } if (mode == MBEDTLS_DES_ENCRYPT) { - while (length > 0) + while (num_des_blocks > 0) { for (i = 0; i < 8; i++) { output[i] = (unsigned char)(input[i] ^ iv[i]); @@ -473,12 +477,12 @@ int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, input += 8; output += 8; - length -= 8; + num_des_blocks--; } } else /* MBEDTLS_DES_DECRYPT */ { - while (length > 0) + while (num_des_blocks > 0) { memcpy(temp, input, 8); mbedtls_des_crypt_ecb(ctx, input, output, MBEDTLS_DES_DECRYPT); @@ -491,11 +495,11 @@ int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, input += 8; output += 8; - length -= 8; + num_des_blocks--; } } - return 0; + return ret; } /* @@ -614,16 +618,19 @@ int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, const unsigned char *input, unsigned char *output) { - int i; + int i, ret, num_des_blocks; unsigned char temp[8]; + ret = 0; + num_des_blocks = (length + 7) / 8; + if (length % 8) { - return MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + ret = MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; } if (mode == MBEDTLS_DES_ENCRYPT) { - while (length > 0) + while (num_des_blocks > 0) { for (i = 0; i < 8; i++) { output[i] = (unsigned char)(input[i] ^ iv[i]); @@ -634,12 +641,12 @@ int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, input += 8; output += 8; - length -= 8; + num_des_blocks--; } } else /* MBEDTLS_DES_DECRYPT */ { - while (length > 0) + while (num_des_blocks > 0) { memcpy(temp, input, 8); mbedtls_des3_decrypt_ecb(ctx, input, output); @@ -652,9 +659,9 @@ int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, input += 8; output += 8; - length -= 8; + num_des_blocks--; } } - return 0; + return ret; } From cb6881e4173088b64660dc6170b0c816ae6ef656 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 18 Dec 2018 21:26:23 +0100 Subject: [PATCH 3/3] Spelling... --- src/common/crypto/EmuDes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/crypto/EmuDes.cpp b/src/common/crypto/EmuDes.cpp index 280270192..8d9591ca0 100644 --- a/src/common/crypto/EmuDes.cpp +++ b/src/common/crypto/EmuDes.cpp @@ -456,7 +456,7 @@ int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, ret = 0; num_des_blocks = (length + 7) / 8; - // The original code of ReactOS correctly checks that the input lenght is a multiple of a des_block (8 bytes) but the + // The original code of ReactOS correctly checks that the input length is a multiple of a des_block (8 bytes) but the // kernel doesn't and will encrypt up to block (lenght + 7) / 8. This means that we'll run the risk of reading some // random bytes after the buffer end and/or touching invalid memory and crash. Because the real kernel does it, we'll // allow this buggy behaviour for the sake of accuracy.