diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a80b797 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,52 @@ +name: Build + +on: [push] + +jobs: + Linux: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Compile + run: make + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: linux + path: | + xenon-bltool + macOS: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Compile + run: make + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: macos + path: | + xenon-bltool + Windows: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: mingw64 + install: make mingw-w64-x86_64-gcc + - name: Compile + run: make CC=gcc + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: windows-mingw64-gcc + path: | + xenon-bltool.exe diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..934b4df --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# build artifacts +xenon-bltool.exe +xenon-bltool +# leftover files +out/ +*.bin +*.dec +# extra annoyances +*.dSYM +.DS_Store diff --git a/3rdparty/ExCrypt_LICENSE b/3rdparty/ExCrypt_LICENSE new file mode 100644 index 0000000..4c715f7 --- /dev/null +++ b/3rdparty/ExCrypt_LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, emoose +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/Xenia_LICENSE b/3rdparty/Xenia_LICENSE new file mode 100644 index 0000000..bc75986 --- /dev/null +++ b/3rdparty/Xenia_LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2015, Ben Vanik. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BEN VANIK BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/excrypt/excrypt.h b/3rdparty/excrypt/excrypt.h new file mode 100644 index 0000000..e8a3396 --- /dev/null +++ b/3rdparty/excrypt/excrypt.h @@ -0,0 +1,44 @@ +#pragma once +#ifdef __cplusplus +extern "C" { +#endif +#include + +#define U8V(data) ((uint8_t)(data) & 0xFF) +#define ROTL8(data, bits) (U8V((data) << (bits)) | ((data) >> (8 - (bits)))) + +#define U16V(data) ((uint16_t)(data) & 0xFFFF) +#define ROTL16(data, bits) (U16V((data) << (bits)) | ((data) >> (16 - (bits)))) + +#define U32V(data) ((uint32_t)(data) & 0xFFFFFFFF) +#define ROTL32(data, bits) (U32V((data) << (bits)) | ((data) >> (32 - (bits)))) + +#define ROTL64(data, bits) (((data) << (bits)) | ((data) >> (64 - (bits)))) + +#ifndef SHOULD_BE_BE +#define BE16(i) ((((i) & 0xFF) << 8 | ((i) >> 8) & 0xFF) & 0xFFFF) +#define BE(i) (((i) & 0xff) << 24 | ((i) & 0xff00) << 8 | ((i) & 0xff0000) >> 8 | ((i) >> 24) & 0xff) +#define BE64(i) (BE((i) & 0xFFFFFFFF) << 32 | BE(((i) >> 32) & 0xFFFFFFFF)) +#else +#define BE16(i) i +#define BE(i) i +#define BE64(i) i +#endif + +typedef int BOOL; + +#include "excrypt_aes.h" +#include "excrypt_bn.h" +#include "excrypt_des.h" +#include "excrypt_ecc.h" +#include "excrypt_md5.h" +#include "excrypt_mem.h" +#include "excrypt_parve.h" +#include "excrypt_rc4.h" +#include "excrypt_rotsum.h" +#include "excrypt_sha.h" +#include "excrypt_sha2.h" +#include "exkeys.h" +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/excrypt/excrypt_aes.c b/3rdparty/excrypt/excrypt_aes.c new file mode 100644 index 0000000..ef7fdbe --- /dev/null +++ b/3rdparty/excrypt/excrypt_aes.c @@ -0,0 +1,146 @@ +#include + +#include "excrypt.h" + +// reference rijndael implementation, from http://www.efgh.com/software/rijndael.htm +#include "rijndael.h" + +// function signature shared between reference impl. and our AESNI versions +typedef void(*rijndaelCrypt_fn)(const unsigned long*, int, const unsigned char*, unsigned char*); + +rijndaelCrypt_fn AesEnc = rijndaelEncrypt; +rijndaelCrypt_fn AesDec = rijndaelDecrypt; + +void ExCryptAesKey(EXCRYPT_AES_STATE* state, const uint8_t* key) +{ + rijndaelSetupEncrypt((unsigned long*)state->keytabenc, key, 128); + memcpy(state->keytabdec, state->keytabenc, sizeof(state->keytabdec)); + rijndaelSetupDecrypt((unsigned long*)state->keytabdec, key, 128); +} + +void ExCryptAesEcb(const EXCRYPT_AES_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt) +{ + if (encrypt) + { + AesEnc((unsigned long*)state->keytabenc, 10, input, output); + } + else + { + AesDec((unsigned long*)state->keytabdec, 10, input, output); + } +} + +void xorWithIv(const uint8_t* input, uint8_t* output, const uint8_t* iv) +{ + for (uint32_t i = 0; i < AES_BLOCKLEN; i++) + { + output[i] = input[i] ^ iv[i]; + } +} + +void rijndaelCbcEncrypt(const unsigned long* rk, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed) +{ + uint8_t* iv = feed; + for (uint32_t i = 0; i < input_size; i += AES_BLOCKLEN) + { + xorWithIv(input, output, iv); + AesEnc(rk, 10, output, output); + iv = output; + output += AES_BLOCKLEN; + input += AES_BLOCKLEN; + } + // store IV in feed param for next call + memcpy(feed, iv, AES_BLOCKLEN); +} + +void rijndaelCbcDecrypt(const unsigned long* rk, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed) +{ + const uint8_t* iv = feed; + for (uint32_t i = 0; i < input_size; i += AES_BLOCKLEN) + { + AesDec(rk, 10, input, output); + xorWithIv(output, output, iv); + iv = input; + output += AES_BLOCKLEN; + input += AES_BLOCKLEN; + } + // store IV in feed param for next call + memcpy(feed, iv, AES_BLOCKLEN); +} + +void ExCryptAesCbc(const EXCRYPT_AES_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt) +{ + if (encrypt) + { + rijndaelCbcEncrypt((unsigned long*)state->keytabenc, input, input_size, output, feed); + } + else + { + rijndaelCbcDecrypt((unsigned long*)state->keytabdec, input, input_size, output, feed); + } +} + +unsigned long* aesschedule_dectable(EXCRYPT_AES_SCHEDULE* state) +{ + return (unsigned long*)&state->keytab[state->num_rounds + 1]; // dec table starts at last entry of enc table +} + +void ExCryptAesCreateKeySchedule(const uint8_t* key, uint32_t key_size, EXCRYPT_AES_SCHEDULE* state) +{ + if (key_size != 0x10 && key_size != 0x18 && key_size != 0x20) + { + return; // invalid key size, must be 128/192/256 bits + } + + state->num_rounds = (key_size >> 2) + 5; // seems to be nr - 1 ? + + rijndaelSetupEncrypt((unsigned long*)state->keytab, key, key_size * 8); + memcpy(aesschedule_dectable(state), state->keytab, (state->num_rounds + 2) * 0x10); + rijndaelSetupDecrypt(aesschedule_dectable(state), key, key_size * 8); +} + +void ExCryptAesCbcEncrypt(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed) +{ + uint8_t* iv = feed; + for (uint32_t i = 0; i < input_size; i += AES_BLOCKLEN) + { + xorWithIv(input, output, iv); + rijndaelEncrypt((unsigned long*)state->keytab, state->num_rounds + 1, output, output); + iv = output; + output += AES_BLOCKLEN; + input += AES_BLOCKLEN; + } + // store IV in feed param for next call + memcpy(feed, iv, AES_BLOCKLEN); +} + +void ExCryptAesCbcDecrypt(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed) +{ + const uint8_t* iv = feed; + for (uint32_t i = 0; i < input_size; i += AES_BLOCKLEN) + { + rijndaelDecrypt(aesschedule_dectable(state), state->num_rounds + 1, input, output); + xorWithIv(output, output, iv); + iv = input; + output += AES_BLOCKLEN; + input += AES_BLOCKLEN; + } + // store IV in feed param for next call + memcpy(feed, iv, AES_BLOCKLEN); +} + +void ExCryptAesEncryptOne(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint8_t* output) +{ + uint8_t feed[0x10]; + memset(feed, 0, 0x10); + + ExCryptAesCbcEncrypt(state, input, 0x10, output, feed); +} + +void ExCryptAesDecryptOne(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint8_t* output) +{ + uint8_t feed[0x10]; + memset(feed, 0, 0x10); + + ExCryptAesCbcDecrypt(state, input, 0x10, output, feed); +} diff --git a/3rdparty/excrypt/excrypt_aes.h b/3rdparty/excrypt/excrypt_aes.h new file mode 100644 index 0000000..7c40e5f --- /dev/null +++ b/3rdparty/excrypt/excrypt_aes.h @@ -0,0 +1,33 @@ +#pragma once + +typedef struct _EXCRYPT_AES_STATE +{ + uint8_t keytabenc[11][4][4]; + uint8_t keytabdec[11][4][4]; +} EXCRYPT_AES_STATE; + +#define AES_BLOCKLEN 16 + +void ExCryptAesKey(EXCRYPT_AES_STATE * state, const uint8_t * key); +void ExCryptAesEcb(const EXCRYPT_AES_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt); +void ExCryptAesCbc(const EXCRYPT_AES_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt); + +// AES Schedule functions - allows using AES128/192/256 + +typedef struct _EXCRYPT_AES_SCHEDULE +{ + uint8_t keytab[29][4][4]; + uint32_t num_rounds; // actual nr = num_rounds + 1 +} EXCRYPT_AES_SCHEDULE; + +void ExCryptAesCreateKeySchedule(const uint8_t* key, uint32_t key_size, EXCRYPT_AES_SCHEDULE* state); + +void ExCryptAesCbcEncrypt(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed); +void ExCryptAesCbcDecrypt(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed); + +void ExCryptAesEncryptOne(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint8_t* output); +void ExCryptAesDecryptOne(EXCRYPT_AES_SCHEDULE* state, const uint8_t* input, uint8_t* output); + +//void ExCryptAesCtr(const EXCRYPT_AES_STATE* pAesState, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* counter); +//void ExCryptAesCbcMac(const uint8_t* key, const uint8_t* input, uint32_t input_size, uint8_t* output); +//void ExCryptAesDmMac(const uint8_t* key, const uint8_t* input, uint32_t input_size, uint8_t* output); diff --git a/3rdparty/excrypt/excrypt_bn.c b/3rdparty/excrypt/excrypt_bn.c new file mode 100644 index 0000000..3955541 --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn.c @@ -0,0 +1,113 @@ +#include +#include + +#include "excrypt.h" + +void ExCryptBnDw_Zero(uint32_t* data, uint32_t data_dwords) +{ + memset(data, 0, sizeof(uint32_t) * data_dwords); +} + +void ExCryptBnDw_Copy(const uint32_t* source, uint32_t* dest, uint32_t num_dwords) +{ + if (source != dest) + memcpy(dest, source, sizeof(uint32_t) * num_dwords); +} + +// Endian-swaps DWORDs +void ExCryptBnDw_SwapLeBe(const uint32_t* source, uint32_t* dest, uint32_t num_dwords) +{ + for (uint32_t i = 0; i < num_dwords; i++) + { + *dest = BE(*source); + dest++; + source++; + } +} + +void ExCryptBnQw_Zero(uint64_t* data, uint32_t num_qwords) +{ + memset(data, 0, sizeof(uint64_t) * num_qwords); +} + +void ExCryptBnQw_Copy(const uint64_t* source, uint64_t* dest, uint32_t num_qwords) +{ + if (source != dest) + memcpy(dest, source, sizeof(uint64_t) * num_qwords); +} + +// Endian-swaps QWORDs - seems to swap the two DWORDs inside it seperately? +// Maybe XeCryptBnQw_SwapDwQw is used afterward +void ExCryptBnQw_SwapLeBe(const uint64_t* source, uint64_t* dest, uint32_t num_qwords) +{ + ExCryptBnDw_SwapLeBe((const uint32_t*)source, (uint32_t*)dest, num_qwords * 2); +} + +// Swaps around the two DWORDs inside a QWORD +void ExCryptBnQw_SwapDwQw(const uint64_t* source, uint64_t* dest, uint32_t num_qwords) +{ + const uint32_t* source_dw = (const uint32_t*)source; + uint32_t* dest_dw = (uint32_t*)dest; + for (uint32_t i = 0; i < num_qwords; i++) + { + uint32_t first = source_dw[0]; + uint32_t second = source_dw[1]; + dest_dw[0] = second; + dest_dw[1] = first; + source_dw += 2; + dest_dw += 2; + } +} + +// Endian-swaps & swaps around the two DWORDs inside a QWORD +void ExCryptBnQw_SwapDwQwLeBe(const uint64_t* source, uint64_t* dest, uint32_t num_qwords) +{ +#ifdef OLD_CODE + const uint32_t* source_dw = (const uint32_t*)source; + uint32_t* dest_dw = (uint32_t*)dest; + for (uint32_t i = 0; i < num_qwords; i++) + { + uint32_t first = BE(source_dw[0]); + uint32_t second = BE(source_dw[1]); + dest_dw[0] = second; + dest_dw[1] = first; + source_dw += 2; + dest_dw += 2; + } +#else + for (uint32_t i = 0; i < num_qwords; i++) + { + dest[i] = BE64(source[i]); + } +#endif +} + +int32_t ExCryptBnQwNeCompare(const uint64_t* input1, const uint64_t* input2, uint32_t num_qwords) +{ + const uint32_t* input1_end = (uint32_t*)(input1 + num_qwords); + const uint32_t* input2_end = (uint32_t*)(input2 + num_qwords); + + if (!num_qwords) + return 0; + + while (1) + { + input1_end -= 2; + input2_end -= 2; + + uint32_t input1_dw = BE(*(input1_end + 1)); + uint32_t input2_dw = BE(*(input2_end + 1)); + + if (input1_dw != input2_dw) + break; + + if (!--num_qwords) + return 0; + } + + uint32_t input1_dw = BE(*(input1_end + 1)); + uint32_t input2_dw = BE(*(input2_end + 1)); + if (input1_dw <= input2_dw) + return -1; + return 1; +} diff --git a/3rdparty/excrypt/excrypt_bn.h b/3rdparty/excrypt/excrypt_bn.h new file mode 100644 index 0000000..94d9b71 --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn.h @@ -0,0 +1,86 @@ +#pragma once +// BigNum functions, used as part of public-key crypto + +void ExCryptBnDw_Zero(uint32_t* data, uint32_t data_dwords); +void ExCryptBnDw_Copy(const uint32_t* source, uint32_t* dest, uint32_t num_dwords); +void ExCryptBnDw_SwapLeBe(const uint32_t* source, uint32_t* dest, uint32_t num_dwords); + +void ExCryptBnQw_Zero(uint64_t* data, uint32_t num_qwords); +void ExCryptBnQw_Copy(const uint64_t* source, uint64_t* dest, uint32_t num_qwords); +void ExCryptBnQw_SwapLeBe(const uint64_t* source, uint64_t* dest, uint32_t num_qwords); +void ExCryptBnQw_SwapDwQw(const uint64_t* source, uint64_t* dest, uint32_t num_qwords); +void ExCryptBnQw_SwapDwQwLeBe(const uint64_t* source, uint64_t* dest, uint32_t num_qwords); + +int32_t ExCryptBnQwNeCompare(const uint64_t* input1, const uint64_t* input2, uint32_t num_qwords); + +// excrypt_bn_mod.cpp +void ExCryptBnQwNeModMul(const uint64_t* input_A, const uint64_t* input_B, uint64_t* output_C, uint64_t inverse, const uint64_t* modulus, uint32_t modulus_size); +uint64_t ExCryptBnQwNeModInv(uint64_t input); + +// Structure of RSA & RC4-decrypted signature +// (Signature is RC4-encrypted using hash field as key, and then RSA privkey-encrypted) +typedef struct _EXCRYPT_SIG +{ + uint64_t padding[28]; // zeroed + uint8_t one; // 0x01 + uint8_t salt[10]; + uint8_t hash[20]; // SHA1(sig[0:8] | salt | data-hash) + uint8_t end; // 0xBC +} EXCRYPT_SIG; + +// Base struct of all ExCrypt RSA keys +typedef struct _EXCRYPT_RSA +{ + uint32_t num_digits; + uint32_t pub_exponent; + uint64_t reserved; +} EXCRYPT_RSA; + +typedef struct _EXCRYPT_RSAPUB_1024 +{ + EXCRYPT_RSA rsa; + uint64_t modulus[16]; +} EXCRYPT_RSAPUB_1024; + +typedef struct _EXCRYPT_RSAPUB_2048 +{ + EXCRYPT_RSA rsa; + uint64_t modulus[32]; +} EXCRYPT_RSAPUB_2048; + +typedef struct _EXCRYPT_RSAPRV_1024 +{ + EXCRYPT_RSA rsa; + uint64_t modulus[16]; + uint64_t prime1[8]; + uint64_t prime2[8]; + uint64_t exponent1[8]; + uint64_t exponent2[8]; + uint64_t coefficient[8]; + uint64_t priv_exponent[16]; +} EXCRYPT_RSAPRV_1024; + +// excrypt_bn_sig.c +void ExCryptBnQwBeSigFormat(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt); +//BOOL ExCryptBnQwBeSigCreate(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt, const EXCRYPT_RSA* privkey); +BOOL ExCryptBnQwBeSigVerify(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt, const EXCRYPT_RSA* pubkey); +int32_t ExCryptBnQwBeSigDifference(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt, const EXCRYPT_RSA* pubkey); + +// excrypt_bn_key.cpp + +// (not from XeCrypt) +// Swaps an EXCRYPT_RSA key from Xbox360 format to a format usable with PC ExCrypt functions. +// TODO: remove this - our funcs should be handling conversions automatically! +void ExCryptBn_BeToLeKey(EXCRYPT_RSA* key, const uint8_t* input, uint32_t input_size); + +// excrypt_bn_rsa.cpp +// TODO: investigate PrivExp/'D' constant, XeCrypt struct doesn't provide it, but pretty much all RSA codebases need it +// We only have one for 1024-bit numbers atm, should be some way to use ModInv to calculate it though... +BOOL ExCryptBnQwNeRsaPrvCrypt(const uint64_t* input, uint64_t* output, const EXCRYPT_RSA* key); +BOOL ExCryptBnQwNeRsaPubCrypt(const uint64_t* input, uint64_t* output, const EXCRYPT_RSA* key); + +// excrypt_bn_pkcs1.cpp +void ExCryptBnDwLePkcs1Format(const uint8_t* hash, uint32_t format, uint8_t* output_sig, uint32_t output_sig_size); +BOOL ExCryptBnDwLePkcs1Verify(const uint8_t* hash, const uint8_t* input_sig, uint32_t input_sig_size); + +//BOOL ExCryptBnQwNeRsaKeyGen(uint32_t num_bits, uint32_t public_exponent, EXCRYPT_RSA* pub_key, EXCRYPT_RSA* priv_key); diff --git a/3rdparty/excrypt/excrypt_bn_key.c b/3rdparty/excrypt/excrypt_bn_key.c new file mode 100644 index 0000000..030a21b --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn_key.c @@ -0,0 +1,58 @@ +#include +#include + +#include "excrypt.h" + +void ExCryptBn_BeToLeKey(EXCRYPT_RSA* key, const uint8_t* input, uint32_t input_size) +{ + key->num_digits = BE(*(uint32_t*)input); + input += 4; + + int input_size_mul = key->num_digits / 0x10; + + key->pub_exponent = BE(*(uint32_t*)input); + input += 4; + + key->reserved = BE64(*(uint64_t*)input); + input += 8; + + if (input_size <= 0x10) + return; + + EXCRYPT_RSAPUB_1024* key_pub = (EXCRYPT_RSAPUB_1024*)key; + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input, key_pub->modulus, key->num_digits); + + uint32_t modulus_size = input_size_mul * 0x80; + input += modulus_size; + + if (input_size <= (modulus_size + 0x10)) + return; + + uint8_t* key_prv = ((uint8_t*)key_pub) + 0x10 + modulus_size; + int prv_size = input_size_mul * 0x40; + + // P + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input, (uint64_t*)key_prv, prv_size / 8); + input += prv_size; + key_prv += prv_size; + + // Q + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input, (uint64_t*)key_prv, prv_size / 8); + input += prv_size; + key_prv += prv_size; + + // DP + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input, (uint64_t*)key_prv, prv_size / 8); + input += prv_size; + key_prv += prv_size; + + // DQ + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input, (uint64_t*)key_prv, prv_size / 8); + input += prv_size; + key_prv += prv_size; + + // CR + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input, (uint64_t*)key_prv, prv_size / 8); + input += prv_size; + key_prv += prv_size; +} diff --git a/3rdparty/excrypt/excrypt_bn_mod.c b/3rdparty/excrypt/excrypt_bn_mod.c new file mode 100644 index 0000000..553988f --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn_mod.c @@ -0,0 +1,165 @@ +#include +#include + +#include "excrypt.h" + +#include "fp61.h" // CAT_MUL128 + +// Unfortunately this seems to be a XeCrypt custom function, OpenSSL etc doesn't seem to include it, so we have to implement it ourselves :/ +// Many thanks to Just1n for posting a C# impl. of it at https://www.se7ensins.com/forums/threads/c-verify-rsa-signature.173155 + +void ExCryptBnQwNeModMul(const uint64_t* input_A, const uint64_t* input_B, uint64_t* output_C, uint64_t inverse, const uint64_t* modulus, uint32_t modulus_size) +{ + uint8_t buffer[0x210]; + memset(buffer, 0, 0x210); + + uint64_t r10 = inverse * input_A[0]; + uint64_t r11 = 0; + uint64_t r12 = 0; + uint64_t r14 = 0; + uint64_t r15 = 0; + uint64_t r16 = 0; + uint64_t r17 = 0; + uint64_t r18 = 0; + uint64_t r19 = 0; + uint64_t r20 = 0; + + uint64_t throwaway = 0; + + int index = 8; // "0x58" ?? + + for (uint32_t a = 0; a < modulus_size; a++) + { + // reset index + index = 8; + r11 = input_B[a]; + r12 = r10 * r11; + r16 = *(uint64_t*)(buffer + index); + r17 = *(uint64_t*)(buffer + index + 0x108); + r16 = r16 - r17; + r16 = r16 * inverse; + r12 = r12 + r16; + + r14 = 0; + r15 = 0; + + for (uint32_t b = 0; b < modulus_size; b++) + { + r16 = input_A[b]; + CAT_MUL128(r17, throwaway, r11, r16); + r18 = r11 * r16; + r16 = *(uint64_t*)(buffer + index); + r18 = r18 + r16; + if (r18 < r16) + r17++; + + r18 = r18 + r14; + if (r18 < r14) + r17++; + + r14 = r17; + *(uint64_t*)(buffer + index - 8) = r18; + r16 = modulus[b]; + CAT_MUL128(r17, throwaway, r12, r16); + r18 = r12 * r16; + r16 = *(uint64_t*)(buffer + index + 0x108); + r18 = r18 + r16; + if (r18 < r16) + r17 = r17 + 0x01; + + r18 = r18 + r15; + if (r18 < r15) + r17 = r17 + 0x01; + + r15 = r17; + *(uint64_t*)(buffer + index + 0x100) = r18; + index += 8; + } + + *(uint64_t*)(buffer + index - 8) = r14; + *(uint64_t*)(buffer + index + 0x100) = r15; + } + + r14 = 0; + r15 = 0; + + // Loop that updates r16 & r17 for later use.. + uint64_t big_modulus_size = modulus_size; + index = ROTL64(big_modulus_size, 3) & 0xFFFFFFFFFFFFFFF8; + for (uint32_t c = 0; c < modulus_size; c++) + { + r16 = *(uint64_t*)(buffer + index); + r17 = *(uint64_t*)(buffer + index + 0x108); + if (r16 != r17) + break; + + index -= 8; + } + + index = 8; + + if (r16 > r17) + { + for (uint32_t c = 0; c < modulus_size; c++) + { + r16 = *(uint64_t*)(buffer + index); + r17 = *(uint64_t*)(buffer + index + 0x108); + r18 = r16 - r17; + r18 = r18 - r14; + output_C[c] = r18; + + r17 = r17 ^ r16; + r18 = r18 ^ r16; + r18 = r18 | r17; + r16 = r16 ^ r18; + r14 = ((r16 >> 63) & 1); + + index += 8; + } + } + else + { + for (uint32_t c = 0; c < modulus_size; c++) + { + r16 = *(uint64_t*)(buffer + index); + r17 = *(uint64_t*)(buffer + index + 0x108); + r18 = modulus[c]; + r19 = r16 + r18; + r19 = r19 + r14; + r20 = r19 - r17; + r20 = r20 - r15; + output_C[c] = r20; + + r18 = r18 ^ r19; + r16 = r16 ^ r19; + r16 = r16 | r18; + r16 = r16 ^ r19; + r14 = ((r16 >> 63) & 1); + r20 = r20 ^ r19; + r17 = r17 ^ r19; + r17 = r17 | r20; + r17 = r17 ^ r19; + r15 = ((r17 >> 63) & 1); + + index += 8; + } + } +} + +uint64_t ExCryptBnQwNeModInv(uint64_t input) +{ + // Compute the 2-adic of qw such that: val = -1 + input^2 + uint64_t val = (input * 3) ^ 2; + input = 1 - (val * input); + + // Raise it to another 32 such that: val = -1 + input^64 + for (uint32_t i = 5; i < 32; i <<= 1) + { + val = val * (input + 1); + input = input * input; + } + + // Done + val = val * (input + 1); + return val; +} diff --git a/3rdparty/excrypt/excrypt_bn_pkcs1.cpp b/3rdparty/excrypt/excrypt_bn_pkcs1.cpp new file mode 100644 index 0000000..d684513 --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn_pkcs1.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include "excrypt.h" + +uint64_t kPkcs1Format0_0 = 0xE03021A05000414; +uint64_t kPkcs1Format0_1 = 0x3021300906052B; + +uint64_t kPkcs1Format1_0 = 0x052B0E03021A0414; +uint32_t kPkcs1Format1_1 = 0x1F300706; +uint16_t kPkcs1Format1_2 = 0x30; + +extern "C" { + +void ExCryptBnDwLePkcs1Format(const uint8_t* hash, uint32_t format, uint8_t* output_sig, uint32_t output_sig_size) +{ + std::memset(output_sig, 0xFF, output_sig_size); + + if (output_sig_size - 39 > 473) + return; + + output_sig[output_sig_size - 1] = 0; + output_sig[output_sig_size - 2] = 1; + + // Copy reversed-hash into signature + std::reverse_copy(hash, hash + 0x14, output_sig); + + // Append different bytes depending on format + switch (format) + { + case 0: + *(uint64_t*)(output_sig + 0x14) = kPkcs1Format0_0; + *(uint64_t*)(output_sig + 0x1C) = kPkcs1Format0_1; + break; + case 1: + *(uint64_t*)(output_sig + 0x14) = kPkcs1Format1_0; + *(uint32_t*)(output_sig + 0x1C) = kPkcs1Format1_1; + *(uint16_t*)(output_sig + 0x20) = kPkcs1Format1_2; + break; + case 2: + output_sig[0x14] = 0; + } +} + +BOOL ExCryptBnDwLePkcs1Verify(const uint8_t* hash, const uint8_t* input_sig, uint32_t input_sig_size) +{ + if (input_sig_size - 39 > 473) + return false; + + // format = 0 if 0x16 == 0 + // format = 1 if 0x16 == 0x1A + // format = 2 if 0x16 != 0x1A + uint32_t format = 0; + if (input_sig[0x16] != 0) + format = (input_sig[0x16] != 0x1A) ? 2 : 1; + + auto test_sig = std::make_unique(input_sig_size); + ExCryptBnDwLePkcs1Format(hash, format, test_sig.get(), input_sig_size); + + return std::memcmp(test_sig.get(), input_sig, input_sig_size) == 0; +} + +}; diff --git a/3rdparty/excrypt/excrypt_bn_rsa.cpp b/3rdparty/excrypt/excrypt_bn_rsa.cpp new file mode 100644 index 0000000..2670f9b --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn_rsa.cpp @@ -0,0 +1,258 @@ +#include "excrypt.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#include +#include + +extern "C" { + +uint8_t kStaticPrivateExponent1024[] = { + 0x51, 0xEC, 0x1F, 0x9D, 0x56, 0x26, 0xC2, 0xFC, 0x10, 0xA6, 0x67, 0x64, 0xCB, 0x3A, 0x6D, 0x4D, + 0xA1, 0xE7, 0x4E, 0xA8, 0x42, 0xF0, 0xF4, 0xFD, 0xFA, 0x66, 0xEF, 0xC7, 0x8E, 0x10, 0x2F, 0xE4, + 0x1C, 0xA3, 0x1D, 0xD0, 0xCE, 0x39, 0x2E, 0xC3, 0x19, 0x2D, 0xD0, 0x58, 0x74, 0x79, 0xAC, 0x08, + 0xE7, 0x90, 0xC1, 0xAC, 0x2D, 0xC6, 0xEB, 0x47, 0xE8, 0x3D, 0xCF, 0x4C, 0x6D, 0xFF, 0x51, 0x65, + 0xD4, 0x6E, 0xBD, 0x0F, 0x15, 0x79, 0x37, 0x95, 0xC4, 0xAF, 0x90, 0x9E, 0x2B, 0x50, 0x8A, 0x0A, + 0x22, 0x4A, 0xB3, 0x41, 0xE5, 0x89, 0x80, 0x73, 0xCD, 0xFA, 0x21, 0x02, 0xF5, 0xDD, 0x30, 0xDD, + 0x07, 0x2A, 0x6F, 0x34, 0x07, 0x81, 0x97, 0x7E, 0xB2, 0xFB, 0x72, 0xE9, 0xEA, 0xC1, 0x88, 0x39, + 0xAC, 0x48, 0x2B, 0xA8, 0x4D, 0xFC, 0xD7, 0xED, 0x9B, 0xF9, 0xDE, 0xC2, 0x45, 0x93, 0x4C, 0x4C +}; + +BOOL ExCryptBnQwNeRsaPrvCrypt(const uint64_t* input, uint64_t* output, const EXCRYPT_RSA* key) +{ +#ifndef _WIN32 + return false; +#else + if (!input || !output || !key) + return false; + + uint32_t key_digits = BE(key->num_digits); + if (key_digits <= 0 || key_digits > 0x40) + return false; + + // TODO: currently only works with 1024-bit/16-digit keys, due to kStaticPrivateExponent1024 above + // Need to replace that with some way of calculating it from the rest of the privkey instead + if (key_digits != 16) + return false; + + // TODO: check this + //if (ExCryptBnQwNeCompare(input, xecrypt_modulus, key_digits) >= 0) + // return false; + + uint32_t modulus_size = key_digits * 8; // = 16 + uint32_t prime_count = key_digits / 2; + uint32_t prime_size = prime_count * 8; + + // Convert XECRYPT blob into BCrypt format + ULONG key_size = sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(uint32_t) + // exponent + modulus_size + // modulus + prime_size + // prime1 + prime_size + // prime2 + prime_size + // exponent1 + prime_size + // exponent2 + prime_size + // coefficient + modulus_size; // private exponent + auto key_buf = std::make_unique(key_size); + auto* key_header = reinterpret_cast(key_buf.get()); + + key_header->Magic = BCRYPT_RSAFULLPRIVATE_MAGIC; + key_header->BitLength = modulus_size * 8; + key_header->cbPublicExp = sizeof(uint32_t); + key_header->cbModulus = modulus_size; + key_header->cbPrime1 = key_header->cbPrime2 = prime_size; + + // Copy in exponent/modulus, luckily these are BE inside BCrypt blob + uint32_t* key_exponent = reinterpret_cast(&key_header[1]); + *key_exponent = key->pub_exponent; + + // ...except other fields need to be reversed in 64-bit chunks for BCrypt to + // make use of them properly for some reason + uint64_t* key_modulus = reinterpret_cast(&key_exponent[1]); + auto* xecrypt_modulus = reinterpret_cast(&key[1]); + std::reverse_copy(xecrypt_modulus, xecrypt_modulus + key_digits, key_modulus); + + uint64_t* key_prime1 = reinterpret_cast(&key_modulus[key_digits]); + auto* xecrypt_prime1 = + reinterpret_cast(&xecrypt_modulus[key_digits]); + + std::reverse_copy(xecrypt_prime1, xecrypt_prime1 + (prime_count), key_prime1); + + uint64_t* key_prime2 = reinterpret_cast(&key_prime1[prime_count]); + auto* xecrypt_prime2 = + reinterpret_cast(&xecrypt_prime1[prime_count]); + + std::reverse_copy(xecrypt_prime2, xecrypt_prime2 + prime_count, key_prime2); + + uint64_t* key_exponent1 = + reinterpret_cast(&key_prime2[prime_count]); + auto* xecrypt_exponent1 = + reinterpret_cast(&xecrypt_prime2[prime_count]); + + std::reverse_copy(xecrypt_exponent1, xecrypt_exponent1 + prime_count, + key_exponent1); + + uint64_t* key_exponent2 = + reinterpret_cast(&key_exponent1[prime_count]); + auto* xecrypt_exponent2 = + reinterpret_cast(&xecrypt_exponent1[prime_count]); + + std::reverse_copy(xecrypt_exponent2, xecrypt_exponent2 + prime_count, + key_exponent2); + + uint64_t* key_coefficient = + reinterpret_cast(&key_exponent2[prime_count]); + auto* xecrypt_coefficient = + reinterpret_cast(&xecrypt_exponent2[prime_count]); + + std::reverse_copy(xecrypt_coefficient, xecrypt_coefficient + prime_count, + key_coefficient); + + uint64_t* key_privexponent = + reinterpret_cast(&key_coefficient[prime_count]); + + // X360 uses a static private exponent / "D" value + std::memcpy(key_privexponent, kStaticPrivateExponent1024, 0x80); + + BCRYPT_ALG_HANDLE hAlgorithm = NULL; + NTSTATUS status = BCryptOpenAlgorithmProvider( + &hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); + + if (!BCRYPT_SUCCESS(status)) + return false; + + auto* buf = key_buf.get(); + BCRYPT_KEY_HANDLE hKey = NULL; + status = BCryptImportKeyPair(hAlgorithm, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, &hKey, + buf, key_size, 0); + + if (!BCRYPT_SUCCESS(status)) + { + if (hAlgorithm) + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + + return false; + } + + // Byteswap & reverse the input into output, as BCrypt wants MSB first + uint8_t* output_bytes = reinterpret_cast(output); + ExCryptBnQw_SwapDwQwLeBe(input, output, key_digits); + std::reverse(output_bytes, output_bytes + modulus_size); + + // BCryptDecrypt only works with private keys, fortunately BCryptEncrypt + // performs the right actions needed for us to decrypt the input + ULONG result_size = 0; + + status = BCryptDecrypt(hKey, output_bytes, modulus_size, nullptr, nullptr, 0, + output_bytes, modulus_size, &result_size, BCRYPT_PAD_NONE); + + //assert(result_size == modulus_size); + + if (BCRYPT_SUCCESS(status)) { + // Reverse data & byteswap again so data is as game expects + std::reverse(output_bytes, output_bytes + modulus_size); + ExCryptBnQw_SwapDwQwLeBe(output, output, key_digits); + } + + if (hKey) + BCryptDestroyKey(hKey); + + if (hAlgorithm) + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + + return BCRYPT_SUCCESS(status); +#endif +} + +BOOL ExCryptBnQwNeRsaPubCrypt(const uint64_t* input, uint64_t* output, const EXCRYPT_RSA* key) +{ +#ifndef _WIN32 + return false; +#else + if (!input || !output || !key) + return false; + + uint32_t key_digits = BE(key->num_digits); + if (key_digits <= 0 || key_digits > 0x40) + return false; + + auto* xecrypt_modulus = reinterpret_cast(&key[1]); + + // TODO: check this + //if (ExCryptBnQwNeCompare(input, xecrypt_modulus, key_digits) >= 0) + // return false; + + uint32_t modulus_size = key_digits * 8; + + // Convert XECRYPT blob into BCrypt format + ULONG key_size = sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(uint32_t) + modulus_size; + auto key_buf = std::make_unique(key_size); + + auto* key_header = reinterpret_cast(key_buf.get()); + key_header->Magic = BCRYPT_RSAPUBLIC_MAGIC; + key_header->BitLength = modulus_size * 8; + key_header->cbPublicExp = sizeof(uint32_t); + key_header->cbModulus = modulus_size; + key_header->cbPrime1 = key_header->cbPrime2 = 0; + + // Copy in exponent/modulus, luckily these are BE inside BCrypt blob + uint32_t* key_exponent = reinterpret_cast(&key_header[1]); + *key_exponent = key->pub_exponent; + + // ...except modulus needs to be reversed in 64-bit chunks for BCrypt to make + // use of it properly for some reason + auto* key_modulus = reinterpret_cast(&key_exponent[1]); + std::reverse_copy(xecrypt_modulus, xecrypt_modulus + key_digits, key_modulus); + + BCRYPT_ALG_HANDLE hAlgorithm = NULL; + NTSTATUS status = BCryptOpenAlgorithmProvider( + &hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); + + if (!BCRYPT_SUCCESS(status)) + return false; + + BCRYPT_KEY_HANDLE hKey = NULL; + status = BCryptImportKeyPair(hAlgorithm, NULL, BCRYPT_RSAPUBLIC_BLOB, &hKey, + key_buf.get(), key_size, 0); + + if (!BCRYPT_SUCCESS(status)) + { + if (hAlgorithm) + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + + return false; + } + + // Byteswap & reverse the input into output, as BCrypt wants MSB first + uint8_t* output_bytes = reinterpret_cast(output); + ExCryptBnQw_SwapDwQwLeBe(input, output, key_digits); + std::reverse(output_bytes, output_bytes + modulus_size); + + // BCryptDecrypt only works with private keys, fortunately BCryptEncrypt + // performs the right actions needed for us to decrypt the input + ULONG result_size = 0; + status = BCryptEncrypt(hKey, output_bytes, modulus_size, nullptr, nullptr, 0, + output_bytes, modulus_size, &result_size, BCRYPT_PAD_NONE); + + //assert(result_size == modulus_size); + + if (BCRYPT_SUCCESS(status)) { + // Reverse data & byteswap again so data is as game expects + std::reverse(output_bytes, output_bytes + modulus_size); + ExCryptBnQw_SwapDwQwLeBe(output, output, key_digits); + } + + if (hKey) + BCryptDestroyKey(hKey); + + if (hAlgorithm) + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + + return BCRYPT_SUCCESS(status); +#endif +} + +}; diff --git a/3rdparty/excrypt/excrypt_bn_sig.c b/3rdparty/excrypt/excrypt_bn_sig.c new file mode 100644 index 0000000..bb1316b --- /dev/null +++ b/3rdparty/excrypt/excrypt_bn_sig.c @@ -0,0 +1,74 @@ +#include +#include +#include + +#include "excrypt.h" + +// BnQwBeSig seems to be a custom XeCrypt signature format, so we have to implement code for it ourselves :( +// AFAIK console-signing (eg. thru XeKeysConsolePrivateKeySign) use PKCS1 instead, so hopefully we can use some existing codebase for those. + +void ExCryptBnQwBeSigFormat(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt) +{ + EXCRYPT_SIG output; + + memset(output.padding, 0, 28 * sizeof(uint64_t)); + output.one = 1; + memcpy(output.salt, salt, 10); + output.end = 0xBC; + + // Create hash value inside signature + ExCryptSha((uint8_t*)&output, 8, hash, 20, salt, 10, output.hash, 20); + + // RC4 encrypt signature contents + ExCryptRc4(output.hash, 20, (uint8_t*)&output, 0xEB); + + // Clear high bit of signature + *(uint8_t*)&output = (*(uint8_t*)&output) & 0x7F; + + // Swap signature, every 8 bytes + uint64_t* in64 = (uint64_t*)&output; + uint64_t* out64 = (uint64_t*)sig; + for (int c = 0; c < 0x20; c++) + out64[0x1F - c] = in64[c]; +} + +BOOL ExCryptBnQwBeSigVerify(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt, const EXCRYPT_RSA* pubkey) +{ + return ExCryptBnQwBeSigDifference(sig, hash, salt, pubkey) == 0; +} + +int32_t ExCryptBnQwBeSigDifference(EXCRYPT_SIG* sig, const uint8_t* hash, const uint8_t* salt, const EXCRYPT_RSA* pubkey) +{ + if (BE(pubkey->num_digits) != 0x20 || (BE(pubkey->pub_exponent) != 3 && BE(pubkey->pub_exponent) != 0x10001)) + return -1; + + EXCRYPT_RSAPUB_2048* key = (EXCRYPT_RSAPUB_2048*)pubkey; + + uint64_t modulus_swap[0x20]; + for (int i = 0; i < 0x20; i++) + modulus_swap[i] = BE64(key->modulus[i]); + + uint64_t* qwSig = (uint64_t*)sig; + + uint64_t inverse = ExCryptBnQwNeModInv(modulus_swap[0]); + + uint64_t sig_copy[0x20]; + ExCryptBnQw_SwapDwQwLeBe(qwSig, qwSig, 32); + ExCryptBnQw_Copy(qwSig, sig_copy, 32); + + uint32_t exp = BE(pubkey->pub_exponent); + while (1) + { + exp >>= 1; + if (!exp) + break; + ExCryptBnQwNeModMul(sig_copy, sig_copy, sig_copy, inverse, modulus_swap, 32); + exp = exp; + } + ExCryptBnQwNeModMul(sig_copy, qwSig, qwSig, inverse, modulus_swap, 32); + ExCryptBnQw_SwapDwQwLeBe(qwSig, qwSig, 32); + + ExCryptBnQwBeSigFormat((EXCRYPT_SIG*)&sig_copy, hash, salt); + + return ExCryptMemDiff((uint8_t*)qwSig, (uint8_t*)&sig_copy, 256); +} diff --git a/3rdparty/excrypt/excrypt_des.c b/3rdparty/excrypt/excrypt_des.c new file mode 100644 index 0000000..13fa785 --- /dev/null +++ b/3rdparty/excrypt/excrypt_des.c @@ -0,0 +1,208 @@ +#include "excrypt.h" +#include "excrypt_des_data.h" + +#define LODWORD(_qw) ((uint32_t)(_qw)) +#define HIDWORD(_qw) ((uint32_t)(((_qw) >> 32) & 0xffffffff)) + +// DES code based on https://github.com/fffaraz/cppDES + +void ExCryptDesParity(const uint8_t* input, uint32_t input_size, uint8_t* output) +{ + for (uint32_t i = 0; i < input_size; i++) + { + uint8_t parity = input[i]; + + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + output[i] = (input[i] & 0xFE) | (~parity & 1); + } +} + +void ExCryptDesKey(EXCRYPT_DES_STATE* state, const uint8_t* key) +{ + uint64_t qkey = *(const uint64_t*)key; + + // initial key schedule calculation + uint64_t permuted_choice_1 = 0; // 56 bits + for (int i = 0; i < 56; i++) + { + permuted_choice_1 <<= 1; + permuted_choice_1 |= (qkey >> (64 - PC1[i])) & LB64_MASK; + } + + // 28 bits + uint32_t C = (uint32_t)((permuted_choice_1 >> 28) & 0x000000000fffffff); + uint32_t D = (uint32_t)(permuted_choice_1 & 0x000000000fffffff); + + // Calculation of the 16 keys + for (int i = 0; i < 16; i++) + { + // key schedule, shifting Ci and Di + for (int j = 0; j < ITERATION_SHIFT[i]; j++) + { + C = (0x0fffffff & (C << 1)) | (0x00000001 & (C >> 27)); + D = (0x0fffffff & (D << 1)) | (0x00000001 & (D >> 27)); + } + + uint64_t permuted_choice_2 = (((uint64_t)C) << 28) | (uint64_t)D; + + uint64_t sub_key = 0; // 48 bits (2*24) + for (int j = 0; j < 48; j++) + { + sub_key <<= 1; + sub_key |= (permuted_choice_2 >> (56 - PC2[j])) & LB64_MASK; + } + state->keytab[i] = sub_key; + } +} + +uint32_t f(uint32_t R, uint64_t k) +{ + // applying expansion permutation and returning 48-bit data + uint64_t s_input = 0; + for (int i = 0; i < 48; i++) + { + s_input <<= 1; + s_input |= (uint64_t)((R >> (32 - EXPANSION[i])) & LB32_MASK); + } + + // XORing expanded Ri with Ki, the round key + s_input = s_input ^ k; + + // applying S-Boxes function and returning 32-bit data + uint32_t s_output = 0; + for (int i = 0; i < 8; i++) + { + // Outer bits + char row = (char)((s_input & (0x0000840000000000 >> 6 * i)) >> (42 - 6 * i)); + row = (row >> 4) | (row & 0x01); + + // Middle 4 bits of input + char column = (char)((s_input & (0x0000780000000000 >> 6 * i)) >> (43 - 6 * i)); + + s_output <<= 4; + s_output |= (uint32_t)(SBOX[i][16 * row + column] & 0x0f); + } + + // applying the round permutation + uint32_t f_result = 0; + for (int i = 0; i < 32; i++) + { + f_result <<= 1; + f_result |= (s_output >> (32 - PBOX[i])) & LB32_MASK; + } + + return f_result; +} + +void feistel(uint32_t* L, uint32_t* R, uint32_t F) +{ + uint32_t temp = *R; + *R = *L ^ F; + *L = temp; +} + +void ExCryptDesEcb(const EXCRYPT_DES_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt) +{ + uint64_t block = *(uint64_t*)input; + + // initial permutation + uint64_t result = 0; + for (int i = 0; i < 64; i++) + { + result <<= 1; + result |= (block >> (64 - IP[i])) & LB64_MASK; + } + + // dividing T' into two 32-bit parts + uint32_t L = HIDWORD(result); + uint32_t R = LODWORD(result); + + // 16 rounds + for (int i = 0; i < 16; i++) + { + uint32_t F = !encrypt ? f(R, state->keytab[15 - i]) : f(R, state->keytab[i]); + feistel(&L, &R, F); + } + + // swapping the two parts + block = (((uint64_t)R) << 32) | (uint64_t)L; + + // inverse initial permutation + result = 0; + for (int i = 0; i < 64; i++) + { + result <<= 1; + result |= (block >> (64 - FP[i])) & LB64_MASK; + } + *(uint64_t*)output = result; +} + +void ExCryptDesCbc(const EXCRYPT_DES_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt) +{ + uint64_t last_block = *(uint64_t*)feed; + for (uint32_t i = 0; i < input_size / 8; i++) + { + if (encrypt) { + *(uint64_t*)output = (*(uint64_t*)input) ^ last_block; + ExCryptDesEcb(state, output, output, encrypt); + last_block = *(uint64_t*)output; + } + else + { + ExCryptDesEcb(state, input, output, encrypt); + *(uint64_t*)output = (*(uint64_t*)output) ^ last_block; + last_block = *(uint64_t*)input; + } + input += 8; + output += 8; + } + *(uint64_t*)feed = last_block; +} + +void ExCryptDes3Key(EXCRYPT_DES3_STATE* state, const uint64_t* keys) +{ + ExCryptDesKey(&state->des_state[0], (const uint8_t*)& keys[0]); + ExCryptDesKey(&state->des_state[1], (const uint8_t*)& keys[1]); + ExCryptDesKey(&state->des_state[2], (const uint8_t*)& keys[2]); +} + +void ExCryptDes3Ecb(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt) +{ + if (encrypt) + { + ExCryptDesEcb(&state->des_state[0], input, output, encrypt); + ExCryptDesEcb(&state->des_state[1], output, output, !encrypt); + ExCryptDesEcb(&state->des_state[2], output, output, encrypt); + } + else + { + ExCryptDesEcb(&state->des_state[2], input, output, encrypt); + ExCryptDesEcb(&state->des_state[1], output, output, !encrypt); + ExCryptDesEcb(&state->des_state[0], output, output, encrypt); + } +} + +void ExCryptDes3Cbc(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt) +{ + uint64_t last_block = *(uint64_t*)feed; + for (uint32_t i = 0; i < input_size / 8; i++) + { + if (encrypt) { + *(uint64_t*)output = (*(uint64_t*)input) ^ last_block; + ExCryptDes3Ecb(state, output, output, encrypt); + last_block = *(uint64_t*)output; + } + else + { + ExCryptDes3Ecb(state, input, output, encrypt); + *(uint64_t*)output = (*(uint64_t*)output) ^ last_block; + last_block = *(uint64_t*)input; + } + input += 8; + output += 8; + } + *(uint64_t*)feed = last_block; +} diff --git a/3rdparty/excrypt/excrypt_des.h b/3rdparty/excrypt/excrypt_des.h new file mode 100644 index 0000000..231a2ae --- /dev/null +++ b/3rdparty/excrypt/excrypt_des.h @@ -0,0 +1,22 @@ +#pragma once +// DES & 3DES functions + +typedef struct _EXCRYPT_DES_STATE +{ + uint64_t keytab[16]; +} EXCRYPT_DES_STATE; + +void ExCryptDesParity(const uint8_t* input, uint32_t input_size, uint8_t* output); + +void ExCryptDesKey(EXCRYPT_DES_STATE* state, const uint8_t* key); +void ExCryptDesEcb(const EXCRYPT_DES_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt); +void ExCryptDesCbc(const EXCRYPT_DES_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt); + +typedef struct _EXCRYPT_DES3_STATE +{ + EXCRYPT_DES_STATE des_state[3]; +} EXCRYPT_DES3_STATE; + +void ExCryptDes3Key(EXCRYPT_DES3_STATE* state, const uint64_t* keys); +void ExCryptDes3Ecb(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint8_t* output, uint8_t encrypt); +void ExCryptDes3Cbc(const EXCRYPT_DES3_STATE* state, const uint8_t* input, uint32_t input_size, uint8_t* output, uint8_t* feed, uint8_t encrypt); diff --git a/3rdparty/excrypt/excrypt_des_data.h b/3rdparty/excrypt/excrypt_des_data.h new file mode 100644 index 0000000..f86c528 --- /dev/null +++ b/3rdparty/excrypt/excrypt_des_data.h @@ -0,0 +1,154 @@ +#pragma once +// Data needed by DES/3DES functions +// (only included by excrypt_des.c - no headers should include this!) + +#define LB32_MASK 0x00000001 +#define LB64_MASK 0x0000000000000001 +#define L64_MASK 0x00000000ffffffff + +// Initial Permutation Table [8*8] +static const char IP[] = +{ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +// Inverse Initial Permutation Table [8*8] +static const char FP[] = +{ + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +// Expansion table [6*8] +static const char EXPANSION[] = +{ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; + +// The S-Box tables [8*16*4] +static const char SBOX[8][64] = +{ + { + // S1 + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 +}, +{ + // S2 + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 +}, +{ + // S3 + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 +}, +{ + // S4 + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 +}, +{ + // S5 + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 +}, +{ + // S6 + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 +}, +{ + // S7 + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 +}, +{ + // S8 + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 +} +}; + +// Post S-Box permutation [4*8] +static const char PBOX[] = +{ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; + +// Permuted Choice 1 Table [7*8] +static const char PC1[] = +{ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +// Permuted Choice 2 Table [6*8] +static const char PC2[] = +{ + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +// Iteration Shift Array +static const char ITERATION_SHIFT[] = +{ + // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; diff --git a/3rdparty/excrypt/excrypt_ecc.c b/3rdparty/excrypt/excrypt_ecc.c new file mode 100644 index 0000000..c32d269 --- /dev/null +++ b/3rdparty/excrypt/excrypt_ecc.c @@ -0,0 +1,34 @@ +#include "excrypt.h" + +uint32_t curves[15] = { + 0x80000000, + 0x80000001, + 0x80000002, + 0x80000003, + 0x80000004, + 0x80000005, + 0x80000006, + 0x80000007, + 0x80000008, + 0x80000009, + 0x8000000A, + 0x8000000B, + 0x8000000C, + 0x8000000D, + 0x8000000E +}; + +uint32_t ExCryptEccGetCurveParameters(uint32_t curve_num, uint32_t** curve_param) +{ + if (!curve_param) + return 1; + + if (curve_num <= 0 || curve_num >= 15) + { + *curve_param = 0; + return 0; + } + + *curve_param = &curves[curve_num]; + return 4; +} diff --git a/3rdparty/excrypt/excrypt_ecc.h b/3rdparty/excrypt/excrypt_ecc.h new file mode 100644 index 0000000..f7c3503 --- /dev/null +++ b/3rdparty/excrypt/excrypt_ecc.h @@ -0,0 +1,3 @@ +#pragma once + +uint32_t ExCryptEccGetCurveParameters(uint32_t curve_num, uint32_t** curve_param); diff --git a/3rdparty/excrypt/excrypt_md5.c b/3rdparty/excrypt/excrypt_md5.c new file mode 100644 index 0000000..986f17d --- /dev/null +++ b/3rdparty/excrypt/excrypt_md5.c @@ -0,0 +1,268 @@ +#include +#include + +#include "excrypt.h" + +// MD5 code based on Brad Conte's md5.c + +#define F(x,y,z) ((x & y) | (~x & z)) +#define G(x,y,z) ((x & z) | (y & ~z)) +#define H(x,y,z) (x ^ y ^ z) +#define I(x,y,z) (y ^ (x | ~z)) + +#define FF(a,b,c,d,m,s,t) { a += F(b,c,d) + m + t; \ + a = b + ROTL32(a,s); } +#define GG(a,b,c,d,m,s,t) { a += G(b,c,d) + m + t; \ + a = b + ROTL32(a,s); } +#define HH(a,b,c,d,m,s,t) { a += H(b,c,d) + m + t; \ + a = b + ROTL32(a,s); } +#define II(a,b,c,d,m,s,t) { a += I(b,c,d) + m + t; \ + a = b + ROTL32(a,s); } + +void md5_process_block(EXCRYPT_MD5_STATE* state) +{ + uint32_t a, b, c, d, m[16], i, j; + + // MD5 specifies big endian byte order, but this implementation assumes a little + // endian byte order CPU. Reverse all the bytes upon input, and re-reverse them + // on output (in md5_final()). + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (state->buffer[j]) + (state->buffer[j + 1] << 8) + (state->buffer[j + 2] << 16) + (state->buffer[j + 3] << 24); + + a = state->state[0]; + b = state->state[1]; + c = state->state[2]; + d = state->state[3]; + + FF(a, b, c, d, m[0], 7, 0xd76aa478); + FF(d, a, b, c, m[1], 12, 0xe8c7b756); + FF(c, d, a, b, m[2], 17, 0x242070db); + FF(b, c, d, a, m[3], 22, 0xc1bdceee); + FF(a, b, c, d, m[4], 7, 0xf57c0faf); + FF(d, a, b, c, m[5], 12, 0x4787c62a); + FF(c, d, a, b, m[6], 17, 0xa8304613); + FF(b, c, d, a, m[7], 22, 0xfd469501); + FF(a, b, c, d, m[8], 7, 0x698098d8); + FF(d, a, b, c, m[9], 12, 0x8b44f7af); + FF(c, d, a, b, m[10], 17, 0xffff5bb1); + FF(b, c, d, a, m[11], 22, 0x895cd7be); + FF(a, b, c, d, m[12], 7, 0x6b901122); + FF(d, a, b, c, m[13], 12, 0xfd987193); + FF(c, d, a, b, m[14], 17, 0xa679438e); + FF(b, c, d, a, m[15], 22, 0x49b40821); + + GG(a, b, c, d, m[1], 5, 0xf61e2562); + GG(d, a, b, c, m[6], 9, 0xc040b340); + GG(c, d, a, b, m[11], 14, 0x265e5a51); + GG(b, c, d, a, m[0], 20, 0xe9b6c7aa); + GG(a, b, c, d, m[5], 5, 0xd62f105d); + GG(d, a, b, c, m[10], 9, 0x02441453); + GG(c, d, a, b, m[15], 14, 0xd8a1e681); + GG(b, c, d, a, m[4], 20, 0xe7d3fbc8); + GG(a, b, c, d, m[9], 5, 0x21e1cde6); + GG(d, a, b, c, m[14], 9, 0xc33707d6); + GG(c, d, a, b, m[3], 14, 0xf4d50d87); + GG(b, c, d, a, m[8], 20, 0x455a14ed); + GG(a, b, c, d, m[13], 5, 0xa9e3e905); + GG(d, a, b, c, m[2], 9, 0xfcefa3f8); + GG(c, d, a, b, m[7], 14, 0x676f02d9); + GG(b, c, d, a, m[12], 20, 0x8d2a4c8a); + + HH(a, b, c, d, m[5], 4, 0xfffa3942); + HH(d, a, b, c, m[8], 11, 0x8771f681); + HH(c, d, a, b, m[11], 16, 0x6d9d6122); + HH(b, c, d, a, m[14], 23, 0xfde5380c); + HH(a, b, c, d, m[1], 4, 0xa4beea44); + HH(d, a, b, c, m[4], 11, 0x4bdecfa9); + HH(c, d, a, b, m[7], 16, 0xf6bb4b60); + HH(b, c, d, a, m[10], 23, 0xbebfbc70); + HH(a, b, c, d, m[13], 4, 0x289b7ec6); + HH(d, a, b, c, m[0], 11, 0xeaa127fa); + HH(c, d, a, b, m[3], 16, 0xd4ef3085); + HH(b, c, d, a, m[6], 23, 0x04881d05); + HH(a, b, c, d, m[9], 4, 0xd9d4d039); + HH(d, a, b, c, m[12], 11, 0xe6db99e5); + HH(c, d, a, b, m[15], 16, 0x1fa27cf8); + HH(b, c, d, a, m[2], 23, 0xc4ac5665); + + II(a, b, c, d, m[0], 6, 0xf4292244); + II(d, a, b, c, m[7], 10, 0x432aff97); + II(c, d, a, b, m[14], 15, 0xab9423a7); + II(b, c, d, a, m[5], 21, 0xfc93a039); + II(a, b, c, d, m[12], 6, 0x655b59c3); + II(d, a, b, c, m[3], 10, 0x8f0ccc92); + II(c, d, a, b, m[10], 15, 0xffeff47d); + II(b, c, d, a, m[1], 21, 0x85845dd1); + II(a, b, c, d, m[8], 6, 0x6fa87e4f); + II(d, a, b, c, m[15], 10, 0xfe2ce6e0); + II(c, d, a, b, m[6], 15, 0xa3014314); + II(b, c, d, a, m[13], 21, 0x4e0811a1); + II(a, b, c, d, m[4], 6, 0xf7537e82); + II(d, a, b, c, m[11], 10, 0xbd3af235); + II(c, d, a, b, m[2], 15, 0x2ad7d2bb); + II(b, c, d, a, m[9], 21, 0xeb86d391); + + state->state[0] += a; + state->state[1] += b; + state->state[2] += c; + state->state[3] += d; +} + +void md5_process_byte(EXCRYPT_MD5_STATE* state, uint8_t octet) +{ + uint32_t offset = state->count++ & 0x3F; + state->buffer[offset] = octet; + if ((state->count & 0x3F) == 0) + { + md5_process_block(state); + } +} + +void ExCryptMd5Init(EXCRYPT_MD5_STATE* state) +{ + state->count = 0; + state->state[0] = 0x67452301; + state->state[1] = 0xEFCDAB89; + state->state[2] = 0x98BADCFE; + state->state[3] = 0x10325476; +} + +void ExCryptMd5Update(EXCRYPT_MD5_STATE* state, const uint8_t* input, uint32_t input_size) +{ + for (uint32_t i = 0; i < input_size; i++) + { + md5_process_byte(state, input[i]); + } +} + +void ExCryptMd5Final(EXCRYPT_MD5_STATE* state, uint8_t* output, uint32_t output_size) +{ + uint64_t bit_count = (uint64_t)state->count * 8; + + md5_process_byte(state, 0x80); + if ((state->count & 0x3F) < 56) + { + while ((state->count & 0x3F) < 56) + { + md5_process_byte(state, 0); + } + } + else if ((state->count & 0x3F) >= 56) + { + while ((state->count & 0x3F) != 0) + { + md5_process_byte(state, 0); + } + md5_process_block(state); + memset(state->buffer, 0, 56); + } + + state->buffer[56] = (uint8_t)(bit_count & 0xFF); + state->buffer[57] = (uint8_t)((bit_count >> 8) & 0xFF); + state->buffer[58] = (uint8_t)((bit_count >> 16) & 0xFF); + state->buffer[59] = (uint8_t)((bit_count >> 24) & 0xFF); + state->buffer[60] = (uint8_t)((bit_count >> 32) & 0xFF); + state->buffer[61] = (uint8_t)((bit_count >> 40) & 0xFF); + state->buffer[62] = (uint8_t)((bit_count >> 48) & 0xFF); + state->buffer[63] = (uint8_t)((bit_count >> 56) & 0xFF); + + md5_process_block(state); + + memcpy(output, state->state, output_size); +} + +void ExCryptMd5(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_MD5_STATE sha; + ExCryptMd5Init(&sha); + + if (input1) + { + ExCryptMd5Update(&sha, input1, input1_size); + } + if (input2) + { + ExCryptMd5Update(&sha, input2, input2_size); + } + if (input3) + { + ExCryptMd5Update(&sha, input3, input3_size); + } + + ExCryptMd5Final(&sha, output, output_size); +} + +void ExCryptHmacMd5Init(EXCRYPT_HMACMD5_STATE* state, const uint8_t* key, uint32_t key_size) +{ + ExCryptMd5Init(&state->Md5State[0]); + ExCryptMd5Init(&state->Md5State[1]); + + if (key_size > 64) + { + key_size = 64; + } + + uint32_t buf1[0x10]; + uint32_t buf2[0x10]; + memset(buf1, 0, 0x10 * 4); + memset(buf2, 0, 0x10 * 4); + + memcpy(buf1, key, key_size); + memcpy(buf2, key, key_size); + + for (int i = 0; i < 16; i++) + { + buf1[i] ^= 0x36363636; + buf2[i] ^= 0x5C5C5C5C; + } + + ExCryptMd5Update(&state->Md5State[0], (const uint8_t*)buf1, 0x40); + ExCryptMd5Update(&state->Md5State[1], (const uint8_t*)buf2, 0x40); +} + +void ExCryptHmacMd5Update(EXCRYPT_HMACMD5_STATE* state, const uint8_t* input, uint32_t input_size) +{ + ExCryptMd5Update(&state->Md5State[0], input, input_size); +} + +void ExCryptHmacMd5Final(EXCRYPT_HMACMD5_STATE* state, uint8_t* output, uint32_t output_size) +{ + ExCryptMd5Final(&state->Md5State[0], 0, 0); + + // updates second SHA1 state with result from first SHA1 + ExCryptMd5Update(&state->Md5State[1], (const uint8_t*)state->Md5State[0].state, 0x14); + + ExCryptMd5Final(&state->Md5State[1], output, output_size); +} + +void ExCryptHmacMd5(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_HMACMD5_STATE hmacsha; + ExCryptHmacMd5Init(&hmacsha, key, key_size); + + if (input1) + { + ExCryptHmacMd5Update(&hmacsha, input1, input1_size); + } + if (input2) + { + ExCryptHmacMd5Update(&hmacsha, input2, input2_size); + } + if (input3) + { + ExCryptHmacMd5Update(&hmacsha, input3, input3_size); + } + + ExCryptHmacMd5Final(&hmacsha, output, output_size); +} + +uint8_t ExCryptHmacMd5Verify(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, const uint8_t* compare_buf, uint32_t compare_buf_size) +{ + uint8_t output[0x14]; + ExCryptHmacMd5(key, key_size, input1, input1_size, input2, input2_size, input3, input3_size, output, 0x14); + + return compare_buf_size <= 0x14 && !memcmp(output, compare_buf, compare_buf_size); +} diff --git a/3rdparty/excrypt/excrypt_md5.h b/3rdparty/excrypt/excrypt_md5.h new file mode 100644 index 0000000..cf3fe2d --- /dev/null +++ b/3rdparty/excrypt/excrypt_md5.h @@ -0,0 +1,25 @@ +#pragma once +// MD5 hash & HMAC algorithm + +typedef struct _EXCRYPT_MD5_STATE +{ + uint32_t count; + uint32_t state[4]; + uint8_t buffer[64]; +} EXCRYPT_MD5_STATE; + +void ExCryptMd5Init(EXCRYPT_MD5_STATE* state); +void ExCryptMd5Update(EXCRYPT_MD5_STATE* state, const uint8_t* input, uint32_t input_size); +void ExCryptMd5Final(EXCRYPT_MD5_STATE* state, uint8_t* output, uint32_t output_size); +void ExCryptMd5(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); + +typedef struct _EXCRYPT_HMACMD5_STATE +{ + EXCRYPT_MD5_STATE Md5State[2]; +} EXCRYPT_HMACMD5_STATE; + +void ExCryptHmacMd5Init(EXCRYPT_HMACMD5_STATE* state, const uint8_t* key, uint32_t key_size); +void ExCryptHmacMd5Update(EXCRYPT_HMACMD5_STATE* state, const uint8_t* input, uint32_t input_size); +void ExCryptHmacMd5Final(EXCRYPT_HMACMD5_STATE* state, uint8_t* output, uint32_t output_size); +void ExCryptHmacMd5(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); diff --git a/3rdparty/excrypt/excrypt_mem.c b/3rdparty/excrypt/excrypt_mem.c new file mode 100644 index 0000000..4e4faf6 --- /dev/null +++ b/3rdparty/excrypt/excrypt_mem.c @@ -0,0 +1,16 @@ +#include "excrypt.h" + +int32_t ExCryptMemDiff(uint8_t* buf1, uint8_t* buf2, uint32_t size) +{ + uint8_t difference = 0; + if (!size) + return difference; + + do + { + difference |= *buf2++ ^ *buf1++; + size--; + } while (size > 0); + + return difference; +} diff --git a/3rdparty/excrypt/excrypt_mem.h b/3rdparty/excrypt/excrypt_mem.h new file mode 100644 index 0000000..eecd38b --- /dev/null +++ b/3rdparty/excrypt/excrypt_mem.h @@ -0,0 +1,3 @@ +#pragma once + +int32_t ExCryptMemDiff(uint8_t* buf1, uint8_t* buf2, uint32_t size); diff --git a/3rdparty/excrypt/excrypt_parve.c b/3rdparty/excrypt/excrypt_parve.c new file mode 100644 index 0000000..950e50f --- /dev/null +++ b/3rdparty/excrypt/excrypt_parve.c @@ -0,0 +1,75 @@ +#include +#include + +#include "excrypt.h" + +void ExCryptParveEcb(const uint8_t* key, const uint8_t* sbox, const uint8_t* input, uint8_t* output) +{ + uint8_t block[9]; + + memcpy(block, input, 8); + block[8] = block[0]; + + for (int i = 8; i > 0; i--) + { + for (int j = 0; j < 8; j++) + { + uint8_t x = key[j] + block[j] + i; + uint8_t y = sbox[x] + block[j + 1]; + block[j + 1] = ROTL8(y, 1); + } + + block[0] = block[8]; + } + + memcpy(output, block, 8); +} + +void ExCryptParveCbcMac(const uint8_t* key, const uint8_t* sbox, const uint8_t* iv, const uint8_t* input, uint32_t input_size, uint8_t* output) +{ + uint64_t block; + memcpy(&block, iv, 8); + + if (input_size >= 8) + { + for (uint32_t i = 0; i < input_size / 8; i++) + { + block ^= *(uint64_t*)&input[i * 8]; + ExCryptParveEcb(key, sbox, (uint8_t*)&block, (uint8_t*)&block); + } + } + + memcpy(output, &block, 8); +} + +void ExCryptChainAndSumMac(const uint32_t* cd, const uint32_t* ab, const uint32_t* input, uint32_t input_dwords, uint32_t* output) +{ + uint64_t out0 = 0; + uint64_t out1 = 0; + + uint32_t ab0 = ab[0] % 0x7FFFFFFF; + uint32_t ab1 = ab[1] % 0x7FFFFFFF; + uint32_t cd0 = cd[0] % 0x7FFFFFFF; + uint32_t cd1 = cd[1] % 0x7FFFFFFF; + + for (uint32_t i = 0; i < input_dwords / 2; i++) + { + out0 += (uint64_t)input[0] * 0xE79A9C1; + out0 = (out0 % 0x7FFFFFFF) * ab0; + out0 += ab1; + out0 = out0 % 0x7FFFFFFF; + + out1 += out0; + + out0 = (uint64_t)(input[1] + out0) * cd0; + out0 = (out0 % 0x7FFFFFFF) + cd1; + out0 = out0 % 0x7FFFFFFF; + + out1 += out0; + + input += 2; + } + + output[0] = (out0 + ab1) % 0x7FFFFFFF; + output[1] = (out1 + cd1) % 0x7FFFFFFF; +} diff --git a/3rdparty/excrypt/excrypt_parve.h b/3rdparty/excrypt/excrypt_parve.h new file mode 100644 index 0000000..98f7f76 --- /dev/null +++ b/3rdparty/excrypt/excrypt_parve.h @@ -0,0 +1,6 @@ +#pragma once +// "Parve" functions, seem to be used during controller auth + +void ExCryptParveEcb(const uint8_t* key, const uint8_t* sbox, const uint8_t* input, uint8_t* output); +void ExCryptParveCbcMac(const uint8_t* key, const uint8_t* sbox, const uint8_t* iv, const uint8_t* input, uint32_t input_size, uint8_t* output); +void ExCryptChainAndSumMac(const uint32_t* cd, const uint32_t* ab, const uint32_t* input, uint32_t input_dwords, uint32_t* output); diff --git a/3rdparty/excrypt/excrypt_rc4.c b/3rdparty/excrypt/excrypt_rc4.c new file mode 100644 index 0000000..edc2496 --- /dev/null +++ b/3rdparty/excrypt/excrypt_rc4.c @@ -0,0 +1,42 @@ +#include "excrypt.h" + +void ExCryptRc4Key(EXCRYPT_RC4_STATE* state, const uint8_t* key, uint32_t key_size) +{ + // Setup RC4 state + state->i = state->j = 0; + for (uint32_t x = 0; x < 0x100; x++) { + state->S[x] = (uint8_t)x; + } + + uint32_t idx = 0; + for (uint32_t x = 0; x < 0x100; x++) { + idx = (idx + state->S[x] + key[x % key_size]) % 0x100; + uint8_t temp = state->S[idx]; + state->S[idx] = state->S[x]; + state->S[x] = temp; + } +} + +void ExCryptRc4Ecb(EXCRYPT_RC4_STATE* state, uint8_t* buf, uint32_t buf_size) +{ + // Crypt data + for (uint32_t idx = 0; idx < buf_size; idx++) { + state->i = (state->i + 1) % 0x100; + state->j = (state->j + state->S[state->i]) % 0x100; + uint8_t temp = state->S[state->i]; + state->S[state->i] = state->S[state->j]; + state->S[state->j] = temp; + + uint8_t a = buf[idx]; + uint8_t b = + state->S[(state->S[state->i] + state->S[state->j]) % 0x100]; + buf[idx] = (uint8_t)(a ^ b); + } +} + +void ExCryptRc4(const uint8_t* key, uint32_t key_size, uint8_t* buf, uint32_t buf_size) +{ + EXCRYPT_RC4_STATE state; + ExCryptRc4Key(&state, key, key_size); + ExCryptRc4Ecb(&state, buf, buf_size); +} diff --git a/3rdparty/excrypt/excrypt_rc4.h b/3rdparty/excrypt/excrypt_rc4.h new file mode 100644 index 0000000..b36dabc --- /dev/null +++ b/3rdparty/excrypt/excrypt_rc4.h @@ -0,0 +1,12 @@ +#pragma once + +typedef struct _EXCRYPT_RC4_STATE +{ + uint8_t S[256]; + uint8_t i; + uint8_t j; +} EXCRYPT_RC4_STATE; + +void ExCryptRc4Key(EXCRYPT_RC4_STATE* state, const uint8_t* key, uint32_t key_size); +void ExCryptRc4Ecb(EXCRYPT_RC4_STATE* state, uint8_t* buf, uint32_t buf_size); +void ExCryptRc4(const uint8_t* key, uint32_t key_size, uint8_t* buf, uint32_t buf_size); diff --git a/3rdparty/excrypt/excrypt_rotsum.c b/3rdparty/excrypt/excrypt_rotsum.c new file mode 100644 index 0000000..8de3c99 --- /dev/null +++ b/3rdparty/excrypt/excrypt_rotsum.c @@ -0,0 +1,56 @@ +#include + +#include "excrypt.h" + +void ExCryptRotSum(EXCRYPT_ROTSUM_STATE* result, const uint64_t* input, uint32_t input_qwords) +{ + result->data[0] = BE64(result->data[0]); + result->data[1] = BE64(result->data[1]); + result->data[2] = BE64(result->data[2]); + result->data[3] = BE64(result->data[3]); + + for (uint32_t i = 0; i < input_qwords; i++) + { + uint64_t data = BE64(*input); + input++; + + result->data[1] += data; + result->data[3] -= data; + + if (result->data[1] < data) { + result->data[0]++; // adding must have overflowed, increment counter + } + if (result->data[3] > data) { + result->data[2]--; // subtracting must have underflowed, decrement counter + } + + result->data[1] = ROTL64(result->data[1], 29); + result->data[3] = ROTL64(result->data[3], 31); + } + + result->data[0] = BE64(result->data[0]); + result->data[1] = BE64(result->data[1]); + result->data[2] = BE64(result->data[2]); + result->data[3] = BE64(result->data[3]); +} + +void ExCryptRotSum4(EXCRYPT_ROTSUM4_STATE* result, uint32_t* input, uint32_t input_dwords) +{ + result->data[0] = BE64(result->data[0]); + result->data[1] = BE64(result->data[1]); + + for (uint32_t i = 0; i < input_dwords; i++) + { + uint32_t data = BE(*input); + input++; + + result->data[0] += data; + result->data[1] -= data; + + result->data[0] = ROTL64(result->data[0], 29); + result->data[1] = ROTL64(result->data[1], 31); + } + + result->data[0] = BE64(result->data[0]); + result->data[1] = BE64(result->data[1]); +} diff --git a/3rdparty/excrypt/excrypt_rotsum.h b/3rdparty/excrypt/excrypt_rotsum.h new file mode 100644 index 0000000..35b52fc --- /dev/null +++ b/3rdparty/excrypt/excrypt_rotsum.h @@ -0,0 +1,16 @@ +#pragma once +// RotSum functions +// these aren't exported by kernel, but do get used by some bootloaders, & inside XeCrypt internally + +typedef struct _EXCRYPT_ROTSUM_STATE +{ + uint64_t data[4]; +} EXCRYPT_ROTSUM_STATE; + +typedef struct _EXCRYPT_ROTSUM4_STATE +{ + uint64_t data[2]; +} EXCRYPT_ROTSUM4_STATE; + +void ExCryptRotSum(EXCRYPT_ROTSUM_STATE* result, const uint64_t* input, uint32_t input_qwords); +void ExCryptRotSum4(EXCRYPT_ROTSUM4_STATE* result, uint32_t* input, uint32_t input_dwords); diff --git a/3rdparty/excrypt/excrypt_sha.c b/3rdparty/excrypt/excrypt_sha.c new file mode 100644 index 0000000..8281a88 --- /dev/null +++ b/3rdparty/excrypt/excrypt_sha.c @@ -0,0 +1,262 @@ +#include +#include + +#include "excrypt.h" + +// SHA1 code based on https://github.com/mohaps/TinySHA1 + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +void sha1_process_block(EXCRYPT_SHA_STATE* state) +{ + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) { + w[i] = (state->buffer[i * 4 + 0] << 24); + w[i] |= (state->buffer[i * 4 + 1] << 16); + w[i] |= (state->buffer[i * 4 + 2] << 8); + w[i] |= (state->buffer[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) { + w[i] = ROTL32((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = state->state[0]; + uint32_t b = state->state[1]; + uint32_t c = state->state[2]; + uint32_t d = state->state[3]; + uint32_t e = state->state[4]; + + for (int i = 0; i < 80; ++i) { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } + else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = ROTL32(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = ROTL32(b, 30); + b = a; + a = temp; + } + + state->state[0] += a; + state->state[1] += b; + state->state[2] += c; + state->state[3] += d; + state->state[4] += e; +} + +void sha1_process_byte(EXCRYPT_SHA_STATE* state, uint8_t octet) +{ + uint32_t offset = state->count++ & 0x3F; + state->buffer[offset] = octet; + if ((state->count & 0x3F) == 0) + { + sha1_process_block(state); + } +} + +void ExCryptShaInit(EXCRYPT_SHA_STATE* state) +{ + state->count = 0; + state->state[0] = 0x67452301; + state->state[1] = 0xEFCDAB89; + state->state[2] = 0x98BADCFE; + state->state[3] = 0x10325476; + state->state[4] = 0xC3D2E1F0; +} + +void ExCryptShaUpdate(EXCRYPT_SHA_STATE* state, const uint8_t* input, uint32_t input_size) +{ + for (uint32_t i = 0; i < input_size; i++) + { + sha1_process_byte(state, input[i]); + } +} + +void ExCryptShaFinal(EXCRYPT_SHA_STATE* state, uint8_t* output, uint32_t output_size) +{ + uint64_t bit_count = (uint64_t)state->count * 8; + + sha1_process_byte(state, 0x80); + + if ((state->count & 0x3F) > 56) + { + while ((state->count & 0x3F) != 0) + { + sha1_process_byte(state, 0); + } + while ((state->count & 0x3F) < 56) + { + sha1_process_byte(state, 0); + } + } + else + { + while ((state->count & 0x3F) < 56) + { + sha1_process_byte(state, 0); + } + } + + sha1_process_byte(state, 0); + sha1_process_byte(state, 0); + sha1_process_byte(state, 0); + sha1_process_byte(state, 0); + + sha1_process_byte(state, (uint8_t)((bit_count >> 24) & 0xFF)); + sha1_process_byte(state, (uint8_t)((bit_count >> 16) & 0xFF)); + sha1_process_byte(state, (uint8_t)((bit_count >> 8) & 0xFF)); + sha1_process_byte(state, (uint8_t)((bit_count) & 0xFF)); + + //sha1_process_block(state); + uint32_t result[5]; + result[0] = BE(state->state[0]); + result[1] = BE(state->state[1]); + result[2] = BE(state->state[2]); + result[3] = BE(state->state[3]); + result[4] = BE(state->state[4]); + memcpy(output, result, MIN(output_size, 0x14)); +} + +void ExCryptSha(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_SHA_STATE state[1]; + ExCryptShaInit(state); + + if (input1 && input1_size) + { + ExCryptShaUpdate(state, input1, input1_size); + } + if (input2 && input2_size) + { + ExCryptShaUpdate(state, input2, input2_size); + } + if (input3 && input3_size) + { + ExCryptShaUpdate(state, input3, input3_size); + } + + ExCryptShaFinal(state, output, output_size); +} + +void ExCryptRotSumSha(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + uint8_t* output, uint32_t output_size) +{ + EXCRYPT_ROTSUM_STATE rotsum; + memset(&rotsum, 0, sizeof(EXCRYPT_ROTSUM_STATE)); + + ExCryptRotSum(&rotsum, (const uint64_t*)input1, input1_size / 8); + ExCryptRotSum(&rotsum, (const uint64_t*)input2, input2_size / 8); + + EXCRYPT_SHA_STATE sha; + ExCryptShaInit(&sha); + + ExCryptShaUpdate(&sha, (const uint8_t*)& rotsum, sizeof(EXCRYPT_ROTSUM_STATE)); + ExCryptShaUpdate(&sha, (const uint8_t*)& rotsum, sizeof(EXCRYPT_ROTSUM_STATE)); + + ExCryptShaUpdate(&sha, input1, input1_size); + ExCryptShaUpdate(&sha, input2, input2_size); + + rotsum.data[0] = ~rotsum.data[0]; + rotsum.data[1] = ~rotsum.data[1]; + rotsum.data[2] = ~rotsum.data[2]; + rotsum.data[3] = ~rotsum.data[3]; + + ExCryptShaUpdate(&sha, (const uint8_t*)& rotsum, sizeof(EXCRYPT_ROTSUM_STATE)); + ExCryptShaUpdate(&sha, (const uint8_t*)& rotsum, sizeof(EXCRYPT_ROTSUM_STATE)); + + ExCryptShaFinal(&sha, output, output_size); +} + +void ExCryptHmacShaInit(EXCRYPT_HMACSHA_STATE* state, const uint8_t* key, uint32_t key_size) +{ + ExCryptShaInit(&state->ShaState[0]); + ExCryptShaInit(&state->ShaState[1]); + + if (key_size > 64) + { + key_size = 64; + } + + uint32_t buf1[0x10]; + uint32_t buf2[0x10]; + memset(buf1, 0, 0x10 * 4); + memset(buf2, 0, 0x10 * 4); + + memcpy(buf1, key, key_size); + memcpy(buf2, key, key_size); + + for (int i = 0; i < 16; i++) + { + buf1[i] ^= 0x36363636; + buf2[i] ^= 0x5C5C5C5C; + } + + ExCryptShaUpdate(&state->ShaState[0], (const uint8_t*)buf1, 0x40); + ExCryptShaUpdate(&state->ShaState[1], (const uint8_t*)buf2, 0x40); +} + +void ExCryptHmacShaUpdate(EXCRYPT_HMACSHA_STATE* state, const uint8_t* input, uint32_t input_size) +{ + ExCryptShaUpdate(&state->ShaState[0], input, input_size); +} + +void ExCryptHmacShaFinal(EXCRYPT_HMACSHA_STATE* state, uint8_t* output, uint32_t output_size) +{ + uint8_t hash[0x14]; + ExCryptShaFinal(&state->ShaState[0], hash, 0x14); + + // updates second SHA1 state with result from first SHA1 + ExCryptShaUpdate(&state->ShaState[1], hash, 0x14); + + ExCryptShaFinal(&state->ShaState[1], output, output_size); +} + +void ExCryptHmacSha(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_HMACSHA_STATE hmacsha; + ExCryptHmacShaInit(&hmacsha, key, key_size); + + if (input1) + { + ExCryptHmacShaUpdate(&hmacsha, input1, input1_size); + } + if (input2) + { + ExCryptHmacShaUpdate(&hmacsha, input2, input2_size); + } + if (input3) + { + ExCryptHmacShaUpdate(&hmacsha, input3, input3_size); + } + + ExCryptHmacShaFinal(&hmacsha, output, output_size); +} + +uint8_t ExCryptHmacShaVerify(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, const uint8_t* compare_buf, uint32_t compare_buf_size) +{ + uint8_t output[0x14]; + ExCryptHmacSha(key, key_size, input1, input1_size, input2, input2_size, input3, input3_size, output, 0x14); + + return compare_buf_size <= 0x14 && !memcmp(output, compare_buf, compare_buf_size); +} diff --git a/3rdparty/excrypt/excrypt_sha.h b/3rdparty/excrypt/excrypt_sha.h new file mode 100644 index 0000000..8766695 --- /dev/null +++ b/3rdparty/excrypt/excrypt_sha.h @@ -0,0 +1,30 @@ +#pragma once +// SHA1 hash & HMAC algorithm + +typedef struct _EXCRYPT_SHA_STATE +{ + uint32_t count; + uint32_t state[5]; + uint8_t buffer[64]; +} EXCRYPT_SHA_STATE; + +void ExCryptShaInit(EXCRYPT_SHA_STATE* state); +void ExCryptShaUpdate(EXCRYPT_SHA_STATE* state, const uint8_t* input, uint32_t input_size); +void ExCryptShaFinal(EXCRYPT_SHA_STATE* state, uint8_t* output, uint32_t output_size); +void ExCryptSha(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); + +void ExCryptRotSumSha(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + uint8_t* output, uint32_t output_size); + +typedef struct _EXCRYPT_HMACSHA_STATE +{ + EXCRYPT_SHA_STATE ShaState[2]; +} EXCRYPT_HMACSHA_STATE; +void ExCryptHmacShaInit(EXCRYPT_HMACSHA_STATE* state, const uint8_t* key, uint32_t key_size); +void ExCryptHmacShaUpdate(EXCRYPT_HMACSHA_STATE* state, const uint8_t* input, uint32_t input_size); +void ExCryptHmacShaFinal(EXCRYPT_HMACSHA_STATE* state, uint8_t* output, uint32_t output_size); +void ExCryptHmacSha(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); +uint8_t ExCryptHmacShaVerify(const uint8_t* key, uint32_t key_size, const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, const uint8_t* compare_buf, uint32_t compare_buf_size); diff --git a/3rdparty/excrypt/excrypt_sha2.c b/3rdparty/excrypt/excrypt_sha2.c new file mode 100644 index 0000000..0f08f41 --- /dev/null +++ b/3rdparty/excrypt/excrypt_sha2.c @@ -0,0 +1,735 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + +source code distributions include the above copyright notice, this +list of conditions and the following disclaimer; + +binary distributions include the above copyright notice, this list +of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + +This code implements sha256, sha384 and sha512 but the latter two +functions rely on efficient 64-bit integer operations that may not be +very efficient on 32-bit machines + +The sha256 functions use a type 'sha256_ctx' to hold details of the +current hash state and uses the following three calls: + + void sha256_begin( sha256_ctx ctx[1] ) + void sha256_hash( const unsigned char data[], + unsigned long len, sha256_ctx ctx[1] ) + void sha_end1( unsigned char hval[], sha256_ctx ctx[1] ) + +The first subroutine initialises a hash computation by setting up the +context in the sha256_ctx context. The second subroutine hashes 8-bit +bytes from array data[] into the hash state withinh sha256_ctx context, +the number of bytes to be hashed being given by the the unsigned long +integer len. The third subroutine completes the hash calculation and +places the resulting digest value in the array of 8-bit bytes hval[]. + +The sha384 and sha512 functions are similar and use the interfaces: + + void sha384_begin( sha384_ctx ctx[1] ); + void sha384_hash( const unsigned char data[], + unsigned long len, sha384_ctx ctx[1] ); + void sha384_end( unsigned char hval[], sha384_ctx ctx[1] ); + + void sha512_begin( sha512_ctx ctx[1] ); + void sha512_hash( const unsigned char data[], + unsigned long len, sha512_ctx ctx[1] ); + void sha512_end( unsigned char hval[], sha512_ctx ctx[1] ); + +In addition there is a function sha2 that can be used to call all these +functions using a call with a hash length parameter as follows: + + int sha2_begin( unsigned long len, sha2_ctx ctx[1] ); + void sha2_hash( const unsigned char data[], + unsigned long len, sha2_ctx ctx[1] ); + void sha2_end( unsigned char hval[], sha2_ctx ctx[1] ); + +The data block length in any one call to any of these hash functions must +be no more than 2^32 - 1 bits or 2^29 - 1 bytes. + +My thanks to Erik Andersen for testing this code +on big-endian systems and for his assistance with corrections +*/ + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#if 1 +#define UNROLL_SHA2 /* for SHA2 loop unroll */ +#endif + +#include /* for memcpy() etc. */ +#include "excrypt.h" + +#if defined( _MSC_VER ) && ( _MSC_VER > 800 ) +#pragma intrinsic(memcpy) +#pragma intrinsic(memset) +#endif + +#if 0 && defined(_MSC_VER) +#define rotl32 _lrotl +#define rotr32 _lrotr +#else +#define rotl32(x,n) (((x) << n) | ((x) >> (32 - n))) +#define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) +#endif + +#if !defined(bswap_32) +#define bswap_32(x) ((rotr32((x), 24) & 0x00ff00ff) | (rotr32((x), 8) & 0xff00ff00)) +#endif + +#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) +#define SWAP_BYTES +#else +#undef SWAP_BYTES +#endif + +#if 0 + +#define ch(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#else /* Thanks to Rich Schroeppel and Colin Plumb for the following */ + +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y)))) + +#endif + +/* round transforms for SHA256 and SHA512 compression functions */ + +#define vf(n,i) v[(n - i) & 7] + +#define hf(i) (p[i & 15] += \ + g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) + +#define v_cycle(i,j) \ + vf(7,i) += (j ? hf(i) : p[i]) + k_0[i+j] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define SHA256_MASK (SHA256_BLOCK_SIZE - 1) + +#if defined(SWAP_BYTES) +#define bsw_32(p,n) \ + { int _i = (n); while(_i--) ((uint32_t*)p)[_i] = bswap_32(((uint32_t*)p)[_i]); } +#else +#define bsw_32(p,n) +#endif + +#define s_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) +#define s_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) +#define g_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) +#define g_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) +#define k_0 k256 + +/* rotated SHA256 round definition. Rather than swapping variables as in */ +/* FIPS-180, different variables are 'rotated' on each round, returning */ +/* to their starting positions every eight rounds */ + +#define q(n) v##n + +#define one_cycle(a,b,c,d,e,f,g,h,k,w) \ + q(h) += s_1(q(e)) + ch(q(e), q(f), q(g)) + k + w; \ + q(d) += q(h); q(h) += s_0(q(a)) + maj(q(a), q(b), q(c)) + +/* SHA256 mixing data */ + +const uint32_t k256[64] = +{ 0x428a2f98ul, 0x71374491ul, 0xb5c0fbcful, 0xe9b5dba5ul, + 0x3956c25bul, 0x59f111f1ul, 0x923f82a4ul, 0xab1c5ed5ul, + 0xd807aa98ul, 0x12835b01ul, 0x243185beul, 0x550c7dc3ul, + 0x72be5d74ul, 0x80deb1feul, 0x9bdc06a7ul, 0xc19bf174ul, + 0xe49b69c1ul, 0xefbe4786ul, 0x0fc19dc6ul, 0x240ca1ccul, + 0x2de92c6ful, 0x4a7484aaul, 0x5cb0a9dcul, 0x76f988daul, + 0x983e5152ul, 0xa831c66dul, 0xb00327c8ul, 0xbf597fc7ul, + 0xc6e00bf3ul, 0xd5a79147ul, 0x06ca6351ul, 0x14292967ul, + 0x27b70a85ul, 0x2e1b2138ul, 0x4d2c6dfcul, 0x53380d13ul, + 0x650a7354ul, 0x766a0abbul, 0x81c2c92eul, 0x92722c85ul, + 0xa2bfe8a1ul, 0xa81a664bul, 0xc24b8b70ul, 0xc76c51a3ul, + 0xd192e819ul, 0xd6990624ul, 0xf40e3585ul, 0x106aa070ul, + 0x19a4c116ul, 0x1e376c08ul, 0x2748774cul, 0x34b0bcb5ul, + 0x391c0cb3ul, 0x4ed8aa4aul, 0x5b9cca4ful, 0x682e6ff3ul, + 0x748f82eeul, 0x78a5636ful, 0x84c87814ul, 0x8cc70208ul, + 0x90befffaul, 0xa4506cebul, 0xbef9a3f7ul, 0xc67178f2ul, +}; + +/* Compile 64 bytes of hash data into SHA256 digest value */ +/* NOTE: this routine assumes that the byte order in the */ +/* ctx->wbuf[] at this point is such that low address bytes */ +/* in the ORIGINAL byte stream will go into the high end of */ +/* words on BOTH big and little endian systems */ + +void sha256_compile(EXCRYPT_SHA256_STATE ctx[1]) +{ +#if !defined(UNROLL_SHA2) + + uint32_t j, * p = ctx->wbuf, v[8]; + + memcpy(v, ctx->hash, sizeof(ctx->hash)); + + for (j = 0; j < 64; j += 16) + { + v_cycle(0, j); v_cycle(1, j); + v_cycle(2, j); v_cycle(3, j); + v_cycle(4, j); v_cycle(5, j); + v_cycle(6, j); v_cycle(7, j); + v_cycle(8, j); v_cycle(9, j); + v_cycle(10, j); v_cycle(11, j); + v_cycle(12, j); v_cycle(13, j); + v_cycle(14, j); v_cycle(15, j); + } + + ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; + ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; + ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; + ctx->hash[6] += v[6]; ctx->hash[7] += v[7]; + +#else + + uint32_t* p = ctx->wbuf, v0, v1, v2, v3, v4, v5, v6, v7; + + v0 = ctx->hash[0]; v1 = ctx->hash[1]; + v2 = ctx->hash[2]; v3 = ctx->hash[3]; + v4 = ctx->hash[4]; v5 = ctx->hash[5]; + v6 = ctx->hash[6]; v7 = ctx->hash[7]; + + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[0], p[0]); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[1], p[1]); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[2], p[2]); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[3], p[3]); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[4], p[4]); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[5], p[5]); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[6], p[6]); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[7], p[7]); + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[8], p[8]); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[9], p[9]); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[10], p[10]); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[11], p[11]); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[12], p[12]); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[13], p[13]); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[14], p[14]); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[15], p[15]); + + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[16], hf(0)); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[17], hf(1)); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[18], hf(2)); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[19], hf(3)); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[20], hf(4)); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[21], hf(5)); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[22], hf(6)); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[23], hf(7)); + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[24], hf(8)); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[25], hf(9)); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[26], hf(10)); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[27], hf(11)); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[28], hf(12)); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[29], hf(13)); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[30], hf(14)); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[31], hf(15)); + + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[32], hf(0)); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[33], hf(1)); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[34], hf(2)); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[35], hf(3)); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[36], hf(4)); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[37], hf(5)); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[38], hf(6)); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[39], hf(7)); + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[40], hf(8)); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[41], hf(9)); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[42], hf(10)); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[43], hf(11)); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[44], hf(12)); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[45], hf(13)); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[46], hf(14)); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[47], hf(15)); + + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[48], hf(0)); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[49], hf(1)); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[50], hf(2)); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[51], hf(3)); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[52], hf(4)); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[53], hf(5)); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[54], hf(6)); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[55], hf(7)); + one_cycle(0, 1, 2, 3, 4, 5, 6, 7, k256[56], hf(8)); + one_cycle(7, 0, 1, 2, 3, 4, 5, 6, k256[57], hf(9)); + one_cycle(6, 7, 0, 1, 2, 3, 4, 5, k256[58], hf(10)); + one_cycle(5, 6, 7, 0, 1, 2, 3, 4, k256[59], hf(11)); + one_cycle(4, 5, 6, 7, 0, 1, 2, 3, k256[60], hf(12)); + one_cycle(3, 4, 5, 6, 7, 0, 1, 2, k256[61], hf(13)); + one_cycle(2, 3, 4, 5, 6, 7, 0, 1, k256[62], hf(14)); + one_cycle(1, 2, 3, 4, 5, 6, 7, 0, k256[63], hf(15)); + + ctx->hash[0] += v0; ctx->hash[1] += v1; + ctx->hash[2] += v2; ctx->hash[3] += v3; + ctx->hash[4] += v4; ctx->hash[5] += v5; + ctx->hash[6] += v6; ctx->hash[7] += v7; +#endif +} + +/* SHA256 hash data in an array of bytes into hash buffer */ +/* and call the hash_compile function as required. */ + +void ExCryptSha256Update(EXCRYPT_SHA256_STATE* state, const uint8_t* input, uint32_t input_size) +{ + uint32_t pos = (uint32_t)((state->count >> 3) & SHA256_MASK); + const unsigned char* sp = input; + unsigned char* w = (unsigned char*)state->wbuf; +#if SHA2_BITS == 1 + uint32_t ofs = (state->count & 7); +#else + input_size <<= 3; +#endif + + state->count += input_size; + +#if SHA2_BITS == 1 + if (ofs) /* if not on a byte boundary */ + { + if (ofs + input_size < 8) /* if no added bytes are needed */ + { + w[pos] |= (*sp >> ofs); + } + else /* otherwise and add bytes */ + { + unsigned char part = w[pos]; + + while ((int)(ofs + (input_size -= 8)) >= 0) + { + w[pos++] = part | (*sp >> ofs); + part = *sp++ << (8 - ofs); + if (pos == SHA256_BLOCK_SIZE) + { + bsw_32(w, SHA256_BLOCK_SIZE >> 2); + sha256_compile(state); pos = 0; + } + } + + w[pos] = part; + } + } + else /* data is byte aligned */ +#endif + { + uint32_t space = SHA256_BLOCK_SIZE - pos; + + while (input_size >= (space << 3)) + { + memcpy(w + pos, sp, space); + bsw_32(w, SHA256_BLOCK_SIZE >> 2); + sha256_compile(state); + sp += space; input_size -= (space << 3); + space = SHA256_BLOCK_SIZE; pos = 0; + } + memcpy(w + pos, sp, (input_size + 7 * SHA2_BITS) >> 3); + } +} + +const uint32_t i256[8] = +{ + 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, + 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul +}; + +void ExCryptSha256Init(EXCRYPT_SHA256_STATE* state) +{ + memset(state, 0, sizeof(EXCRYPT_SHA256_STATE)); + memcpy(state->hash, i256, sizeof(state->hash)); +} + +/* SHA256 Final padding and digest calculation */ +void ExCryptSha256Final(EXCRYPT_SHA256_STATE* state, uint8_t* output, uint32_t output_size) +{ + uint32_t i = (uint32_t)((state->count >> 3) & SHA256_MASK), m1; + + /* put bytes in the buffer in an order in which references to */ + /* 32-bit words will put bytes with lower addresses into the */ + /* top of 32 bit words on BOTH big and little endian machines */ + bsw_32(state->wbuf, (i + 3 + SHA2_BITS) >> 2) + + /* we now need to mask valid bytes and add the padding which is */ + /* a single 1 bit and as many zero bits as necessary. Note that */ + /* we can always add the first padding byte here because the */ + /* buffer always has at least one empty slot */ + m1 = (unsigned char)0x80 >> (state->count & 7); + state->wbuf[i >> 2] &= ((0xffffff00 | (~m1 + 1)) << 8 * (~i & 3)); + state->wbuf[i >> 2] |= (m1 << 8 * (~i & 3)); + + /* we need 9 or more empty positions, one for the padding byte */ + /* (above) and eight for the length count. If there is not */ + /* enough space pad and empty the buffer */ + if (i > SHA256_BLOCK_SIZE - 9) + { + if (i < 60) state->wbuf[15] = 0; + sha256_compile(state); + i = 0; + } + else /* compute a word index for the empty buffer positions */ + i = (i >> 2) + 1; + + while (i < 15) /* and zero pad all but last two positions */ + state->wbuf[i++] = 0; + + /* the following 32-bit length fields are assembled in the */ + /* wrong byte order on little endian machines but this is */ + /* corrected later since they are only ever used as 32-bit */ + /* word values. */ + state->wbuf[15] = state->count; + sha256_compile(state); + + /* extract the hash value as bytes in case the hash buffer is */ + /* misaligned for 32-bit words */ + for (i = 0; i < MIN(output_size, 0x20); ++i) + output[i] = ((state->hash[i >> 2] >> (8 * (~i & 3))) & 0xff); +} + +void ExCryptSha256(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_SHA256_STATE state[1]; + ExCryptSha256Init(state); + + if (input1 && input1_size) + { + ExCryptSha256Update(state, input1, input1_size); + } + if (input2 && input2_size) + { + ExCryptSha256Update(state, input2, input2_size); + } + if (input3 && input3_size) + { + ExCryptSha256Update(state, input3, input3_size); + } + + ExCryptSha256Final(state, output, output_size); +} + +#define SHA512_MASK (SHA512_BLOCK_SIZE - 1) + +#define rotr64(x,n) (((x) >> n) | ((x) << (64 - n))) + +#if !defined(bswap_64) +#define bswap_64(x) (((uint64_t)(bswap_32((uint32_t)(x)))) << 32 | bswap_32((uint32_t)((x) >> 32))) +#endif + +#if defined(SWAP_BYTES) +#define bsw_64(p,n) \ + { int _i = (n); while(_i--) ((uint64_t*)p)[_i] = bswap_64(((uint64_t*)p)[_i]); } +#else +#define bsw_64(p,n) +#endif + +/* SHA512 mixing function definitions */ + +#ifdef s_0 +# undef s_0 +# undef s_1 +# undef g_0 +# undef g_1 +# undef k_0 +#endif + +#define s_0(x) (rotr64((x), 28) ^ rotr64((x), 34) ^ rotr64((x), 39)) +#define s_1(x) (rotr64((x), 14) ^ rotr64((x), 18) ^ rotr64((x), 41)) +#define g_0(x) (rotr64((x), 1) ^ rotr64((x), 8) ^ ((x) >> 7)) +#define g_1(x) (rotr64((x), 19) ^ rotr64((x), 61) ^ ((x) >> 6)) +#define k_0 k512 + +/* SHA384/SHA512 mixing data */ + +const uint64_t k512[80] = +{ + 0x428a2f98d728ae22, 0x7137449123ef65cd, + 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, + 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, + 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, + 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, + 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, + 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, + 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, + 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, + 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, + 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, + 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, + 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, + 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, + 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, + 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, + 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, + 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, + 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, + 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 +}; + +/* Compile 128 bytes of hash data into SHA384/512 digest */ +/* NOTE: this routine assumes that the byte order in the */ +/* ctx->wbuf[] at this point is such that low address bytes */ +/* in the ORIGINAL byte stream will go into the high end of */ +/* words on BOTH big and little endian systems */ + +void sha512_compile(EXCRYPT_SHA512_STATE ctx[1]) +{ + uint64_t v[8], * p = ctx->wbuf; + uint32_t j; + + memcpy(v, ctx->hash, sizeof(ctx->hash)); + + for (j = 0; j < 80; j += 16) + { + v_cycle(0, j); v_cycle(1, j); + v_cycle(2, j); v_cycle(3, j); + v_cycle(4, j); v_cycle(5, j); + v_cycle(6, j); v_cycle(7, j); + v_cycle(8, j); v_cycle(9, j); + v_cycle(10, j); v_cycle(11, j); + v_cycle(12, j); v_cycle(13, j); + v_cycle(14, j); v_cycle(15, j); + } + + ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; + ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; + ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; + ctx->hash[6] += v[6]; ctx->hash[7] += v[7]; +} + +/* Compile 128 bytes of hash data into SHA256 digest value */ +/* NOTE: this routine assumes that the byte order in the */ +/* ctx->wbuf[] at this point is in such an order that low */ +/* address bytes in the ORIGINAL byte stream placed in this */ +/* buffer will now go to the high end of words on BOTH big */ +/* and little endian systems */ + +void ExCryptSha512Update(EXCRYPT_SHA512_STATE* state, const uint8_t* input, uint32_t input_size) +{ + uint32_t pos = (uint32_t)(state->count >> 3) & SHA512_MASK; + const unsigned char* sp = input; + unsigned char* w = (unsigned char*)state->wbuf; +#if SHA2_BITS == 1 + uint32_t ofs = (state->count & 7); +#else + input_size <<= 3; +#endif + + state->count += input_size; + +#if SHA2_BITS == 1 + if (ofs) /* if not on a byte boundary */ + { + if (ofs + input_size < 8) /* if no added bytes are needed */ + { + w[pos] |= (*sp >> ofs); + } + else /* otherwise and add bytes */ + { + unsigned char part = w[pos]; + + while ((int)(ofs + (input_size -= 8)) >= 0) + { + w[pos++] = part | (*sp >> ofs); + part = *sp++ << (8 - ofs); + if (pos == SHA512_BLOCK_SIZE) + { + bsw_64(w, SHA512_BLOCK_SIZE >> 3); + sha512_compile(state); pos = 0; + } + } + + w[pos] = part; + } + } + else /* data is byte aligned */ +#endif + { + uint32_t space = SHA512_BLOCK_SIZE - pos; + + while (input_size >= (space << 3)) + { + memcpy(w + pos, sp, space); + bsw_64(w, SHA512_BLOCK_SIZE >> 3); + sha512_compile(state); + sp += space; input_size -= (space << 3); + space = SHA512_BLOCK_SIZE; pos = 0; + } + memcpy(w + pos, sp, (input_size + 7 * SHA2_BITS) >> 3); + } +} + +void ExCryptSha384Update(EXCRYPT_SHA384_STATE* state, const uint8_t* input, uint32_t input_size) +{ + ExCryptSha512Update(state, input, input_size); +} + +/* SHA384 initialisation data */ + +const uint64_t i384[80] = +{ + 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, + 0x9159015a3070dd17, 0x152fecd8f70e5939, + 0x67332667ffc00b31, 0x8eb44a8768581511, + 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4 +}; + +void ExCryptSha384Init(EXCRYPT_SHA384_STATE* state) +{ + memset(state, 0, sizeof(EXCRYPT_SHA384_STATE)); + memcpy(state->hash, i384, sizeof(state->hash)); +} + +void ExCryptSha384Final(EXCRYPT_SHA384_STATE* state, uint8_t* output, uint32_t output_size) +{ + ExCryptSha512Final(state, output, output_size); +} + +void ExCryptSha384(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_SHA384_STATE state[1]; + ExCryptSha384Init(state); + + if (input1 && input1_size) + { + ExCryptSha384Update(state, input1, input1_size); + } + if (input2 && input2_size) + { + ExCryptSha384Update(state, input2, input2_size); + } + if (input3 && input3_size) + { + ExCryptSha384Update(state, input3, input3_size); + } + + ExCryptSha512Final(state, output, output_size); +} + +/* SHA512 initialisation data */ + +static const uint64_t i512[SHA512_DIGEST_SIZE >> 3] = +{ + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 +}; + +void ExCryptSha512Init(EXCRYPT_SHA512_STATE* state) +{ + memset(state, 0, sizeof(EXCRYPT_SHA512_STATE)); + memcpy(state->hash, i512, sizeof(state->hash)); +} + +void ExCryptSha512Final(EXCRYPT_SHA512_STATE* state, uint8_t* output, uint32_t output_size) +{ + uint32_t i = (uint32_t)((state->count >> 3) & SHA512_MASK); + uint64_t m1; + + /* put bytes in the buffer in an order in which references to */ + /* 32-bit words will put bytes with lower addresses into the */ + /* top of 32 bit words on BOTH big and little endian machines */ + bsw_64(state->wbuf, (i + 7 + SHA2_BITS) >> 3); + + /* we now need to mask valid bytes and add the padding which is */ + /* a single 1 bit and as many zero bits as necessary. Note that */ + /* we can always add the first padding byte here because the */ + /* buffer always has at least one empty slot */ + m1 = (unsigned char)0x80 >> (state->count & 7); + state->wbuf[i >> 3] &= ((0xffffffffffffff00 | (~m1 + 1)) << 8 * (~i & 7)); + state->wbuf[i >> 3] |= (m1 << 8 * (~i & 7)); + + /* we need 17 or more empty byte positions, one for the padding */ + /* byte (above) and sixteen for the length count. If there is */ + /* not enough space pad and empty the buffer */ + if (i > SHA512_BLOCK_SIZE - 17) + { + if (i < 120) state->wbuf[15] = 0; + sha512_compile(state); + i = 0; + } + else + i = (i >> 3) + 1; + + while (i < 15) + state->wbuf[i++] = 0; + + /* the following 64-bit length fields are assembled in the */ + /* wrong byte order on little endian machines but this is */ + /* corrected later since they are only ever used as 64-bit */ + /* word values. */ + state->wbuf[15] = state->count; + sha512_compile(state); + + /* extract the hash value as bytes in case the hash buffer is */ + /* misaligned for 32-bit words */ + for (i = 0; i < MIN(output_size, 0x40); ++i) + output[i] = ((state->hash[i >> 3] >> (8 * (~i & 7))) & 0xff); +} + +void ExCryptSha512(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size) +{ + EXCRYPT_SHA512_STATE state[1]; + ExCryptSha512Init(state); + + if (input1 && input1_size) + { + ExCryptSha512Update(state, input1, input1_size); + } + if (input2 && input2_size) + { + ExCryptSha512Update(state, input2, input2_size); + } + if (input3 && input3_size) + { + ExCryptSha512Update(state, input3, input3_size); + } + + ExCryptSha512Final(state, output, output_size); +} + +const uint32_t i224[8] = +{ + 0xc1059ed8ul, 0x367cd507ul, 0x3070dd17ul, 0xf70e5939ul, + 0xffc00b31ul, 0x68581511ul, 0x64f98fa7ul, 0xbefa4fa4ul +}; + +void ExCryptSha224Init(EXCRYPT_SHA256_STATE* state) +{ + memset(state, 0, sizeof(EXCRYPT_SHA256_STATE)); + memcpy(state->hash, i224, sizeof(state->hash)); +} diff --git a/3rdparty/excrypt/excrypt_sha2.h b/3rdparty/excrypt/excrypt_sha2.h new file mode 100644 index 0000000..e4bdf5f --- /dev/null +++ b/3rdparty/excrypt/excrypt_sha2.h @@ -0,0 +1,100 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#ifndef _SHA2_H +#define _SHA2_H + +#include + +/* define for bit or byte oriented SHA */ +#if 1 +# define SHA2_BITS 0 /* byte oriented */ +#else +# define SHA2_BITS 1 /* bit oriented */ +#endif + +#if defined(__cplusplus) +extern "C" +{ +#endif + + /* Note that the following function prototypes are the same */ + /* for both the bit and byte oriented implementations. But */ + /* the length fields are in bytes or bits as is appropriate */ + /* for the version used. Bit sequences are arrays of bytes */ + /* in which bit sequence indexes increase from the most to */ + /* the least significant end of each byte. The value 'len' */ + /* in sha_hash for the byte oriented versions of SHA2 */ + /* is limited to 2^29 bytes, but multiple calls will handle */ + /* longer data blocks. */ + +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +#define SHA384_DIGEST_SIZE 48 +#define SHA384_BLOCK_SIZE 128 + +#define SHA512_DIGEST_SIZE 64 +#define SHA512_BLOCK_SIZE 128 + +/* type to hold the SHA256 (and SHA224) context */ + + typedef struct + { + uint32_t count; + uint32_t hash[SHA256_DIGEST_SIZE >> 2]; + uint32_t wbuf[SHA256_BLOCK_SIZE >> 2]; + } EXCRYPT_SHA256_STATE; + + /* type to hold the SHA384 (and SHA512) context */ + + typedef struct + { + uint64_t count; + uint64_t hash[SHA512_DIGEST_SIZE >> 3]; + uint64_t wbuf[SHA512_BLOCK_SIZE >> 3]; + } EXCRYPT_SHA512_STATE; + + typedef EXCRYPT_SHA512_STATE EXCRYPT_SHA384_STATE; + + void ExCryptSha256Init(EXCRYPT_SHA256_STATE* state); + void ExCryptSha256Update(EXCRYPT_SHA256_STATE* state, const uint8_t* input, uint32_t input_size); + void ExCryptSha256Final(EXCRYPT_SHA256_STATE* state, uint8_t* output, uint32_t output_size); + void ExCryptSha256(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); + + void ExCryptSha384Init(EXCRYPT_SHA384_STATE* state); + void ExCryptSha384Update(EXCRYPT_SHA384_STATE* state, const uint8_t* input, uint32_t input_size); + void ExCryptSha384Final(EXCRYPT_SHA384_STATE* state, uint8_t* output, uint32_t output_size); + void ExCryptSha384(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); + + void ExCryptSha512Init(EXCRYPT_SHA512_STATE* state); + void ExCryptSha512Update(EXCRYPT_SHA512_STATE* state, const uint8_t* input, uint32_t input_size); + void ExCryptSha512Final(EXCRYPT_SHA512_STATE* state, uint8_t* output, uint32_t output_size); + void ExCryptSha512(const uint8_t* input1, uint32_t input1_size, const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, uint8_t* output, uint32_t output_size); + + void ExCryptSha224Init(EXCRYPT_SHA256_STATE* state); +#if defined(__cplusplus) +} +#endif + +#endif \ No newline at end of file diff --git a/3rdparty/excrypt/exkeys.cpp b/3rdparty/excrypt/exkeys.cpp new file mode 100644 index 0000000..b7f5eb7 --- /dev/null +++ b/3rdparty/excrypt/exkeys.cpp @@ -0,0 +1,454 @@ +#include +#include +#include + +#include "excrypt.h" + +std::map> kExKeyProperties = { + { XEKEY_MANUFACTURING_MODE, { 0x8, 0x1 } }, + { XEKEY_ALTERNATE_KEY_VAULT, { 0x9, 0x1 } }, + { XEKEY_RESTRICTED_PRIVILEGES_FLAGS, { 0xA, 0x1 } }, + { XEKEY_RESERVED_BYTE3, { 0xB, 0x1 } }, + { XEKEY_ODD_FEATURES, { 0xC, 0x2 } }, + { XEKEY_ODD_AUTHTYPE, { 0xE, 0x2 } }, + { XEKEY_RESTRICTED_HVEXT_LOADER, { 0x10, 0x4 } }, + { XEKEY_POLICY_FLASH_SIZE, { 0x14, 0x4 } }, + { XEKEY_POLICY_BUILTIN_USBMU_SIZE, { 0x18, 0x4 } }, + { XEKEY_RESERVED_DWORD4, { 0x1C, 0x4 } }, + { XEKEY_RESTRICTED_PRIVILEGES, { 0x20, 0x8 } }, + { XEKEY_RESERVED_QWORD2, { 0x28, 0x8 } }, + { XEKEY_RESERVED_QWORD3, { 0x30, 0x8 } }, + { XEKEY_RESERVED_QWORD4, { 0x38, 0x8 } }, + { XEKEY_RESERVED_KEY1, { 0x40, 0x10 } }, + { XEKEY_RESERVED_KEY2, { 0x50, 0x10 } }, + { XEKEY_RESERVED_KEY3, { 0x60, 0x10 } }, + { XEKEY_RESERVED_KEY4, { 0x70, 0x10 } }, + { XEKEY_RESERVED_RANDOM_KEY1, { 0x80, 0x10 } }, + { XEKEY_RESERVED_RANDOM_KEY2, { 0x90, 0x10 } }, + { XEKEY_CONSOLE_SERIAL_NUMBER, { 0xA0, 0xC } }, + { XEKEY_MOBO_SERIAL_NUMBER, { 0xAC, 0xC } }, + { XEKEY_GAME_REGION, { 0xB8, 0x2 } }, + // 6 bytes padding + { XEKEY_CONSOLE_OBFUSCATION_KEY, { 0xC0, 0x10 } }, + { XEKEY_KEY_OBFUSCATION_KEY, { 0xD0, 0x10 } }, + { XEKEY_ROAMABLE_OBFUSCATION_KEY, { 0xE0, 0x10 } }, + { XEKEY_DVD_KEY, { 0xF0, 0x10 } }, + { XEKEY_PRIMARY_ACTIVATION_KEY, { 0x100, 0x18 } }, + { XEKEY_SECONDARY_ACTIVATION_KEY, { 0x118, 0x10 } }, + { XEKEY_GLOBAL_DEVICE_2DES_KEY1, { 0x128, 0x10 } }, + { XEKEY_GLOBAL_DEVICE_2DES_KEY2, { 0x138, 0x10 } }, + { XEKEY_WIRELESS_CONTROLLER_MS_2DES_KEY1, { 0x148, 0x10 } }, + { XEKEY_WIRELESS_CONTROLLER_MS_2DES_KEY2, { 0x158, 0x10 } }, + { XEKEY_WIRED_WEBCAM_MS_2DES_KEY1, { 0x168, 0x10 } }, + { XEKEY_WIRED_WEBCAM_MS_2DES_KEY2, { 0x178, 0x10 } }, + { XEKEY_WIRED_CONTROLLER_MS_2DES_KEY1, { 0x188, 0x10 } }, + { XEKEY_WIRED_CONTROLLER_MS_2DES_KEY2, { 0x198, 0x10 } }, + { XEKEY_MEMORY_UNIT_MS_2DES_KEY1, { 0x1A8, 0x10 } }, + { XEKEY_MEMORY_UNIT_MS_2DES_KEY2, { 0x1B8, 0x10 } }, + { XEKEY_OTHER_XSM3_DEVICE_MS_2DES_KEY1, { 0x1C8, 0x10 } }, + { XEKEY_OTHER_XSM3_DEVICE_MS_2DES_KEY2, { 0x1D8, 0x10 } }, + { XEKEY_WIRELESS_CONTROLLER_3P_2DES_KEY1, { 0x1E8, 0x10 } }, + { XEKEY_WIRELESS_CONTROLLER_3P_2DES_KEY2, { 0x1F8, 0x10 } }, + { XEKEY_WIRED_WEBCAM_3P_2DES_KEY1, { 0x208, 0x10 } }, + { XEKEY_WIRED_WEBCAM_3P_2DES_KEY2, { 0x218, 0x10 } }, + { XEKEY_WIRED_CONTROLLER_3P_2DES_KEY1, { 0x228, 0x10 } }, + { XEKEY_WIRED_CONTROLLER_3P_2DES_KEY2, { 0x238, 0x10 } }, + { XEKEY_MEMORY_UNIT_3P_2DES_KEY1, { 0x248, 0x10 } }, + { XEKEY_MEMORY_UNIT_3P_2DES_KEY2, { 0x258, 0x10 } }, + { XEKEY_OTHER_XSM3_DEVICE_3P_2DES_KEY1, { 0x268, 0x10 } }, + { XEKEY_OTHER_XSM3_DEVICE_3P_2DES_KEY2, { 0x278, 0x10 } }, + { XEKEY_CONSOLE_PRIVATE_KEY, { 0x288, 0x1D0 } }, + { XEKEY_XEIKA_PRIVATE_KEY, { 0x458, 0x390 } }, + { XEKEY_CARDEA_PRIVATE_KEY, { 0x7E8, 0x1D0 } }, + { XEKEY_CONSOLE_CERTIFICATE, { 0x9B8, 0x1A8 } }, + { XEKEY_XEIKA_CERTIFICATE, { 0xB60, 0x1288 } }, + { XEKEY_SPECIAL_KEY_VAULT_SIGNATURE, { 0x1DF8, 0x100 } }, + { XEKEY_CARDEA_CERTIFICATE, { 0x1EE8, 0x2108 } }, +}; + +std::map> ExImportedKeys; + +uint8_t kRoamableObfuscationKey_Retail[0x10] = +{ + 0xE1, 0xBC, 0x15, 0x9C, 0x73, 0xB1, 0xEA, 0xE9, 0xAB, 0x31, 0x70, 0xF3, 0xAD, 0x47, 0xEB, 0xF3 +}; +uint8_t kRoamableObfuscationKey_Devkit[0x10] = +{ + 0xDA, 0xB6, 0x9A, 0xD9, 0x8E, 0x28, 0x76, 0x4F, 0x97, 0x7E, 0xE2, 0x48, 0x7E, 0x4F, 0x3F, 0x68 +}; + +std::vector ExKeyVault; + +extern "C" +{ + +BOOL ExKeysKeyVaultLoaded() +{ + return !ExKeyVault.empty(); +} + +void ExKeysKeyVaultSetup() +{ + // ROAMABLE_OBFUSCATION_KEY doesn't seem to be stored in the keyvault, is generated at runtime/stored in HV? + auto* roamable_key = ExKeysGetKeyPtr(XEKEY_ROAMABLE_OBFUSCATION_KEY); + if (ExKeysGetConsoleType() == 2) + std::copy_n(kRoamableObfuscationKey_Retail, 0x10, roamable_key); + else + std::copy_n(kRoamableObfuscationKey_Devkit, 0x10, roamable_key); +} + +BOOL ExKeysLoadKeyVault(const uint8_t* decrypted_kv, uint32_t length) +{ + if (length < 0x3FF0) + return false; + + uint32_t offset = 0; + if (length >= 0x4000) + offset = 0x10; // skip over digest + + ExKeyVault.resize(length - offset); + + std::copy_n(decrypted_kv + offset, length - offset, ExKeyVault.data()); + + ExKeysKeyVaultSetup(); + + return true; +} + +BOOL ExKeysLoadKeyVaultFromPath(const char* filepath) +{ + FILE* file; + if (fopen_s(&file, filepath, "rb") != 0) + return false; + + fseek(file, 0, SEEK_END); + auto filesize = ftell(file); + fseek(file, 0, SEEK_SET); + + if (filesize < 0x3FF0) + { + fclose(file); + return false; + } + + if (filesize >= 0x4000) + { + // skip over digest + fseek(file, 0x10, SEEK_SET); + filesize -= 0x10; + } + + ExKeyVault.resize(filesize); + + fread(ExKeyVault.data(), 1, filesize, file); + fclose(file); + + ExKeysKeyVaultSetup(); + + return true; +} + +BOOL ExKeysIsKeySupported(uint32_t key_idx) +{ + return (ExKeysKeyVaultLoaded() && kExKeyProperties.count(key_idx) > 0) || ExImportedKeys.count(key_idx) > 0; +} + +BOOL ExKeysSetKey(uint32_t key_idx, const uint8_t* input, uint32_t size) +{ + std::vector key_vector; + key_vector.resize(size); + memcpy(key_vector.data(), input, size); + + ExImportedKeys[key_idx] = key_vector; + return true; +} + +BOOL ExKeysGetKey(uint32_t key_idx, uint8_t* output, uint32_t* output_size) +{ + if (output_size) + *output_size = 0; + + if (!ExKeysIsKeySupported(key_idx)) + return false; + + // Imported version of key has priority over keyvault version + if (ExImportedKeys.count(key_idx) > 0) + { + std::vector& imported_key = ExImportedKeys[key_idx]; + if (output_size) + *output_size = uint32_t(imported_key.size()); + if (output) + std::copy_n(imported_key.data(), imported_key.size(), output); + + return true; + } + + if (ExKeyVault.empty()) + return false; + + auto& key_info = kExKeyProperties.at(key_idx); + auto& key_offset = std::get<0>(key_info); + auto& key_size = std::get<1>(key_info); + + if (output_size) + *output_size = key_size; + + if (output) + std::copy_n(ExKeyVault.data() + key_offset, key_size, output); + + return true; +} + +uint8_t* ExKeysGetKeyPtr(uint32_t key_idx) +{ + if (!ExKeysIsKeySupported(key_idx)) + return nullptr; + + if (ExImportedKeys.count(key_idx) > 0) + return ExImportedKeys[key_idx].data(); + + auto& key_info = kExKeyProperties.at(key_idx); + auto& key_offset = std::get<0>(key_info); + + return ExKeyVault.data() + key_offset; +} + +// Returns size of given key, and IIRC can also return flags? +uint32_t ExKeysGetKeyProperties(uint32_t key_idx) +{ + if (!ExKeysIsKeySupported(key_idx)) + return 0; + + if (ExImportedKeys.count(key_idx)) + return uint32_t(ExImportedKeys[key_idx].size()); + + auto& key_info = kExKeyProperties.at(key_idx); + auto& key_size = std::get<1>(key_info); + return key_size; +} + +uint32_t ExKeysGetConsoleCertificate(uint8_t* output) +{ + uint32_t length = 0; + ExKeysGetKey(XEKEY_CONSOLE_CERTIFICATE, output, &length); + return 0; +} + +uint32_t ExKeysGetConsoleID(uint8_t* raw_bytes, char* hex_string) +{ + uint8_t* console_cert = ExKeysGetKeyPtr(XEKEY_CONSOLE_CERTIFICATE); + char string[0x10]; + + if (raw_bytes) { + std::copy_n(console_cert + 2, 5, raw_bytes); + } + if (hex_string) { + uint64_t counter = 0; + for (int i = 0; i < 5; i++) + counter = console_cert[2 + i] + counter * 0x100; + sprintf_s(string, 0x10, "%011llu%llx", counter >> 4, counter & 0xF); + memcpy(hex_string, string, 0xC); + } + return 0; +} + +uint32_t ExKeysGetConsoleType() +{ + uint8_t* console_cert = ExKeysGetKeyPtr(XEKEY_CONSOLE_CERTIFICATE); + + return BE(*(uint32_t*)(console_cert + 0x18)); +} + +uint32_t ExKeysGetConsolePrivateKey(EXCRYPT_RSAPRV_1024* output) +{ + uint32_t length = 0; + ExKeysGetKey(XEKEY_CONSOLE_PRIVATE_KEY, (uint8_t*)output, &length); + return 0; +} + +BOOL ExKeysQwNeRsaPrvCrypt(uint32_t key_idx, const uint64_t* input, uint64_t* output) +{ + if (key_idx != XEKEY_CONSOLE_PRIVATE_KEY && + key_idx != XEKEY_XEIKA_PRIVATE_KEY && + key_idx != XEKEY_CARDEA_PRIVATE_KEY) + return false; + + // Xeika key is larger than the others, and likely needs a different D/PrivExp value for it (see kStaticPrivateExponent1024), so disallow it for now + if (key_idx == XEKEY_XEIKA_PRIVATE_KEY) + return false; + + auto* key_ptr = ExKeysGetKeyPtr(key_idx); + + return ExCryptBnQwNeRsaPrvCrypt(input, output, (EXCRYPT_RSA*)key_ptr); +} + +// Signs the given hash with the loaded keyvaults private-key, and writes out console cert + signature to output_cert_sig +BOOL ExKeysConsolePrivateKeySign(const uint8_t* hash, uint8_t* output_cert_sig) +{ + uint64_t sig_buf[0x10]; + + ExCryptBnDwLePkcs1Format(hash, 0, (uint8_t*)sig_buf, 0x10 * 8); + ExCryptBnQw_SwapDwQwLeBe(sig_buf, sig_buf, 0x10); + + if (!ExKeysQwNeRsaPrvCrypt(XEKEY_CONSOLE_PRIVATE_KEY, sig_buf, sig_buf)) + return false; + + ExCryptBnQw_SwapDwQwLeBe(sig_buf, (uint64_t*)(output_cert_sig + 0x1A8), 0x10); + ExKeysGetConsoleCertificate(output_cert_sig); + return true; +} + +BOOL ExKeysPkcs1Verify(const uint8_t* hash, const uint8_t* input_sig, EXCRYPT_RSA* key) +{ + uint64_t temp_sig[0x20]; + + uint32_t key_digits = BE(key->num_digits); + uint32_t modulus_size = key_digits * 8; + if (modulus_size > 0x200) + return false; + + ExCryptBnQw_SwapDwQwLeBe((uint64_t*)input_sig, temp_sig, key_digits); + if (!ExCryptBnQwNeRsaPubCrypt(temp_sig, temp_sig, key)) + return false; + + ExCryptBnQw_SwapDwQwLeBe(temp_sig, temp_sig, key_digits); + return ExCryptBnDwLePkcs1Verify(hash, (uint8_t*)temp_sig, modulus_size); +} + +// Verifies that the given hash was signed by the given console certificate, and that the console certificate was signed by the master key. +BOOL ExKeysConsoleSignatureVerification(const uint8_t* hash, uint8_t* input_signature, int32_t *compare_result) +{ + uint32_t master_key_size = 0x110; + uint8_t our_console_cert[0x1A8]; + uint8_t master_key[0x110]; + EXCRYPT_RSAPUB_1024 console_public_key; + + ExKeysGetConsoleCertificate(our_console_cert); + + int32_t diff = ExCryptMemDiff(our_console_cert, input_signature, 0x1A8); + if (compare_result != NULL) + *compare_result = diff; + + if (!ExKeysGetKey(XEKEY_CONSTANT_MASTER_KEY, master_key, &master_key_size)) + memset(master_key, 0, 0x110); + + if (master_key_size == 0x110 && BE(*(uint32_t*)master_key) == 0x20) + { + // Validate the input console certificate against the master public key. + uint8_t cert_checksum[0x14]; + ExCryptSha(input_signature, 0xA8, NULL, 0, NULL, 0, cert_checksum, 0x14); + if (ExKeysPkcs1Verify(cert_checksum, input_signature + 0xA8, (EXCRYPT_RSA*)master_key)) + { + // The certificate doesn't have the public key size - the real function does this stuff, too. + console_public_key.rsa.num_digits = BE(0x10); + console_public_key.rsa.pub_exponent = *(uint32_t*)(input_signature + 0x24); + memcpy(console_public_key.modulus, input_signature + 0x28, sizeof(console_public_key.modulus)); + // Validate the input hash against the provided signatuer. + if (ExKeysPkcs1Verify(hash, input_signature + 0x1A8, &console_public_key.rsa)) + return true; + } + } + + return false; +} + +uint32_t ExKeysObscureKey(const uint8_t* input, uint8_t* output) +{ + EXCRYPT_AES_STATE aes; + ExCryptAesKey(&aes, ExKeysGetKeyPtr(XEKEY_KEY_OBFUSCATION_KEY)); + ExCryptAesEcb(&aes, input, output, 1); + + return 0; // TODO: X_STATUS_SUCCESS +} + +uint32_t ExKeysHmacShaUsingKey(const uint8_t* obscured_key, + const uint8_t* input1, uint32_t input1_size, + const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, + uint8_t* output, uint32_t output_size) +{ + if (!obscured_key) + return 1; // TODO: X_STATUS_INVALID_PARAMETER + + uint8_t key[0x10]; + + // Deobscure key + EXCRYPT_AES_STATE aes; + ExCryptAesKey(&aes, ExKeysGetKeyPtr(XEKEY_KEY_OBFUSCATION_KEY)); + ExCryptAesEcb(&aes, obscured_key, key, 0); + + ExCryptHmacSha(key, 0x10, input1, input1_size, input2, input2_size, input3, input3_size, output, output_size); + + return 0; // TODO: X_STATUS_SUCCESS +} + +uint32_t ExKeysHmacSha(uint32_t key_idx, + const uint8_t* input1, uint32_t input1_size, + const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, + uint8_t* output, uint32_t output_size) +{ + auto* key = ExKeysGetKeyPtr(key_idx); + if (!key) + return 1; // TODO: X_STATUS_INVALID_PARAMETER + + auto size = ExKeysGetKeyProperties(key_idx); + + ExCryptHmacSha(key, size, input1, input1_size, input2, input2_size, input3, input3_size, output, output_size); + + return 0; // TODO: X_STATUS_SUCCESS +} + +uint32_t ExKeysObfuscate(BOOL roaming, const uint8_t* input, uint32_t input_size, uint8_t* output, uint32_t* output_size) +{ + std::copy_n(input, input_size, output + 0x18); + *output_size = input_size + 0x18; + + //TODO: set random nonce/confounder + //ExCryptRandom(output + 0x10, 8); + std::memset(output + 0x10, 0xBB, 8); + + uint32_t key_idx = roaming ? uint32_t(XEKEY_ROAMABLE_OBFUSCATION_KEY) : uint32_t(XEKEY_CONSOLE_OBFUSCATION_KEY); + + auto result = ExKeysHmacSha(key_idx, output + 0x10, *output_size - 0x10, nullptr, 0, nullptr, 0, output, 0x10); + if (result < 0) + return result; + + uint8_t key[0x10]; + ExKeysHmacSha(key_idx, output, 0x10, 0, 0, 0, 0, key, 0x10); + + ExCryptRc4(key, 0x10, output + 0x10, *output_size - 0x10); + + return result; // TODO: X_STATUS_SUCCESS +} + +BOOL ExKeysUnObfuscate(BOOL roaming, const uint8_t* input, uint32_t input_size, uint8_t* output, uint32_t* output_size) +{ + if (input_size < 0x18) + return false; + + uint8_t buf1[0x18]; + std::copy_n(input, 0x18, buf1); + + *output_size = input_size - 0x18; + std::copy_n(input + 0x18, *output_size, output); + + uint32_t key_idx = roaming ? uint32_t(XEKEY_ROAMABLE_OBFUSCATION_KEY) : uint32_t(XEKEY_CONSOLE_OBFUSCATION_KEY); + + uint8_t key[0x10]; + auto result = ExKeysHmacSha(key_idx, buf1, 0x10, nullptr, 0, nullptr, 0, key, 0x10); + if (result < 0) + return false; + + EXCRYPT_RC4_STATE rc4; + ExCryptRc4Key(&rc4, key, 0x10); + ExCryptRc4Ecb(&rc4, buf1 + 0x10, 8); + ExCryptRc4Ecb(&rc4, output, *output_size); + + uint8_t hash[0x10]; + ExKeysHmacSha(key_idx, buf1 + 0x10, 8, output, *output_size, nullptr, 0, hash, 0x10); + + return std::memcmp(hash, buf1, 0x10) == 0; +} + +}; diff --git a/3rdparty/excrypt/exkeys.h b/3rdparty/excrypt/exkeys.h new file mode 100644 index 0000000..a3232fa --- /dev/null +++ b/3rdparty/excrypt/exkeys.h @@ -0,0 +1,127 @@ +#pragma once + +enum XeKey +{ + XEKEY_MANUFACTURING_MODE = 0x0, + XEKEY_ALTERNATE_KEY_VAULT = 0x1, + XEKEY_RESTRICTED_PRIVILEGES_FLAGS = 0x2, + XEKEY_RESERVED_BYTE3 = 0x3, + XEKEY_ODD_FEATURES = 0x4, + XEKEY_ODD_AUTHTYPE = 0x5, + XEKEY_RESTRICTED_HVEXT_LOADER = 0x6, + XEKEY_POLICY_FLASH_SIZE = 0x7, + XEKEY_POLICY_BUILTIN_USBMU_SIZE = 0x8, + XEKEY_RESERVED_DWORD4 = 0x9, + XEKEY_RESTRICTED_PRIVILEGES = 0xA, + XEKEY_RESERVED_QWORD2 = 0xB, + XEKEY_RESERVED_QWORD3 = 0xC, + XEKEY_RESERVED_QWORD4 = 0xD, + XEKEY_RESERVED_KEY1 = 0xE, + XEKEY_RESERVED_KEY2 = 0xF, + XEKEY_RESERVED_KEY3 = 0x10, + XEKEY_RESERVED_KEY4 = 0x11, + XEKEY_RESERVED_RANDOM_KEY1 = 0x12, + XEKEY_RESERVED_RANDOM_KEY2 = 0x13, + XEKEY_CONSOLE_SERIAL_NUMBER = 0x14, + XEKEY_MOBO_SERIAL_NUMBER = 0x15, + XEKEY_GAME_REGION = 0x16, + XEKEY_CONSOLE_OBFUSCATION_KEY = 0x17, + XEKEY_KEY_OBFUSCATION_KEY = 0x18, + XEKEY_ROAMABLE_OBFUSCATION_KEY = 0x19, + XEKEY_DVD_KEY = 0x1A, + XEKEY_PRIMARY_ACTIVATION_KEY = 0x1B, + XEKEY_SECONDARY_ACTIVATION_KEY = 0x1C, + XEKEY_GLOBAL_DEVICE_2DES_KEY1 = 0x1D, + XEKEY_GLOBAL_DEVICE_2DES_KEY2 = 0x1E, + XEKEY_WIRELESS_CONTROLLER_MS_2DES_KEY1 = 0x1F, + XEKEY_WIRELESS_CONTROLLER_MS_2DES_KEY2 = 0x20, + XEKEY_WIRED_WEBCAM_MS_2DES_KEY1 = 0x21, + XEKEY_WIRED_WEBCAM_MS_2DES_KEY2 = 0x22, + XEKEY_WIRED_CONTROLLER_MS_2DES_KEY1 = 0x23, + XEKEY_WIRED_CONTROLLER_MS_2DES_KEY2 = 0x24, + XEKEY_MEMORY_UNIT_MS_2DES_KEY1 = 0x25, + XEKEY_MEMORY_UNIT_MS_2DES_KEY2 = 0x26, + XEKEY_OTHER_XSM3_DEVICE_MS_2DES_KEY1 = 0x27, + XEKEY_OTHER_XSM3_DEVICE_MS_2DES_KEY2 = 0x28, + XEKEY_WIRELESS_CONTROLLER_3P_2DES_KEY1 = 0x29, + XEKEY_WIRELESS_CONTROLLER_3P_2DES_KEY2 = 0x2A, + XEKEY_WIRED_WEBCAM_3P_2DES_KEY1 = 0x2B, + XEKEY_WIRED_WEBCAM_3P_2DES_KEY2 = 0x2C, + XEKEY_WIRED_CONTROLLER_3P_2DES_KEY1 = 0x2D, + XEKEY_WIRED_CONTROLLER_3P_2DES_KEY2 = 0x2E, + XEKEY_MEMORY_UNIT_3P_2DES_KEY1 = 0x2F, + XEKEY_MEMORY_UNIT_3P_2DES_KEY2 = 0x30, + XEKEY_OTHER_XSM3_DEVICE_3P_2DES_KEY1 = 0x31, + XEKEY_OTHER_XSM3_DEVICE_3P_2DES_KEY2 = 0x32, + XEKEY_CONSOLE_PRIVATE_KEY = 0x33, + XEKEY_XEIKA_PRIVATE_KEY = 0x34, + XEKEY_CARDEA_PRIVATE_KEY = 0x35, + XEKEY_CONSOLE_CERTIFICATE = 0x36, + XEKEY_XEIKA_CERTIFICATE = 0x37, + XEKEY_CARDEA_CERTIFICATE = 0x38, + XEKEY_MAX_KEY_INDEX = 0x39, + + XEKEY_CONSTANT_PIRS_KEY = 0x39, + XEKEY_CONSTANT_ALT_MASTER_KEY = 0x3A, + XEKEY_CONSTANT_ALT_LIVE_KEY = 0x3B, + XEKEY_CONSTANT_MASTER_KEY = 0x3C, + XEKEY_CONSTANT_LIVE_KEY = 0x3D, + XEKEY_CONSTANT_XB1_GREEN_KEY = 0x3E, + XEKEY_CONSTANT_SATA_DISK_SECURITY_KEY = 0x3F, + XEKEY_CONSTANT_DEVICE_REVOCATION_KEY = 0x40, + XEKEY_CONSTANT_XMACS_KEY = 0x41, + XEKEY_CONSTANT_REVOCATION_LIST_NONCE = 0x42, + XEKEY_CONSTANT_CROSS_PLATFORM_SYSLINK_KEY = 0x43, + + XEKEY_SPECIAL_KEY_VAULT_SIGNATURE = 0x44, + XEKEY_SPECIAL_SECROM_DIGEST = 0x45, + XEKEY_SPECIAL_SECDATA = 0x46, + XEKEY_SPECIAL_DVD_FIRMWARE_KEY = 0x47, + XEKEY_SPECIAL_DEBUG_UNLOCK = 0x48, + XEKEY_SPECIAL_DEBUG_UNLOCK_STATE = 0x49, + XEKEY_MAX_CONSTANT_INDEX = 0x4A, + + XEKEY_TITLE_KEYS_BASE = 0xE0, + XEKEY_TITLE_KEYS_LIMIT = 0xE8, + XEKEY_TITLE_KEYS_RESET = 0xF0, + + XEKEY_SECURED_DATA_BASE = 0x1000, + XEKEY_SECURED_DATA_LIMIT = 0x2000, +}; + +BOOL ExKeysKeyVaultLoaded(); + +BOOL ExKeysLoadKeyVault(const uint8_t* decrypted_kv, uint32_t length); +BOOL ExKeysLoadKeyVaultFromPath(const char* filepath); + +BOOL ExKeysIsKeySupported(uint32_t key_idx); + +BOOL ExKeysSetKey(uint32_t key_idx, const uint8_t* input, uint32_t size); +BOOL ExKeysGetKey(uint32_t key_idx, uint8_t* output, uint32_t* output_size); +uint8_t* ExKeysGetKeyPtr(uint32_t key_idx); +uint32_t ExKeysGetKeyProperties(uint32_t key_idx); + +uint32_t ExKeysGetConsoleCertificate(uint8_t* output); +uint32_t ExKeysGetConsoleID(uint8_t* raw_bytes, char* hex_string); +uint32_t ExKeysGetConsoleType(); +uint32_t ExKeysGetConsolePrivateKey(EXCRYPT_RSAPRV_1024* output); + +BOOL ExKeysQwNeRsaPrvCrypt(uint32_t key_idx, const uint64_t* input, uint64_t* output); +BOOL ExKeysConsolePrivateKeySign(const uint8_t* hash, uint8_t* output_cert_sig); +BOOL ExKeysConsoleSignatureVerification(const uint8_t* hash, uint8_t* input_signature, int32_t* compare_result); +BOOL ExKeysPkcs1Verify(const uint8_t* hash, const uint8_t* input_sig, EXCRYPT_RSA* key); + +uint32_t ExKeysObscureKey(const uint8_t* input, uint8_t* output); +uint32_t ExKeysHmacShaUsingKey(const uint8_t* obscured_key, + const uint8_t* input1, uint32_t input1_size, + const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, + uint8_t* output, uint32_t output_size); +uint32_t ExKeysHmacSha(uint32_t key_idx, + const uint8_t* input1, uint32_t input1_size, + const uint8_t* input2, uint32_t input2_size, + const uint8_t* input3, uint32_t input3_size, + uint8_t* output, uint32_t output_size); + +uint32_t ExKeysObfuscate(BOOL roaming, const uint8_t* input, uint32_t input_size, uint8_t* output, uint32_t* output_size); +BOOL ExKeysUnObfuscate(BOOL roaming, const uint8_t* input, uint32_t input_size, uint8_t* output, uint32_t* output_size); diff --git a/3rdparty/excrypt/fp61.h b/3rdparty/excrypt/fp61.h new file mode 100644 index 0000000..a9859c1 --- /dev/null +++ b/3rdparty/excrypt/fp61.h @@ -0,0 +1,11 @@ +#pragma once + +// Code to extract high part of 64 bit integer multiplication +// from: https://github.com/catid/fp61/blob/master/fp61.h + +# define CAT_MUL128(r_hi, r_lo, x, y) \ + { \ + unsigned __int128 w = (unsigned __int128)x * y; \ + r_lo = (uint64_t)w; \ + r_hi = (uint64_t)(w >> 64); \ + } diff --git a/3rdparty/excrypt/rijndael.c b/3rdparty/excrypt/rijndael.c new file mode 100644 index 0000000..52055fb --- /dev/null +++ b/3rdparty/excrypt/rijndael.c @@ -0,0 +1,1208 @@ +#define FULL_UNROLL + +#include "rijndael.h" + +typedef unsigned long u32; +typedef unsigned char u8; + +static const u32 Te0[256] = +{ + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; + +static const u32 Te1[256] = +{ + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; + +static const u32 Te2[256] = +{ + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; + +static const u32 Te3[256] = +{ + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; + +static const u32 Te4[256] = +{ + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; + +static const u32 Td0[256] = +{ + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; + +static const u32 Td1[256] = +{ + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; + +static const u32 Td2[256] = +{ + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; + +static const u32 Td3[256] = +{ + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; + +static const u32 Td4[256] = +{ + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; + +static const u32 rcon[] = +{ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define GETU32(plaintext) (((u32)(plaintext)[0] << 24) ^ \ + ((u32)(plaintext)[1] << 16) ^ \ + ((u32)(plaintext)[2] << 8) ^ \ + ((u32)(plaintext)[3])) + +#define PUTU32(ciphertext, st) { (ciphertext)[0] = (u8)((st) >> 24); \ + (ciphertext)[1] = (u8)((st) >> 16); \ + (ciphertext)[2] = (u8)((st) >> 8); \ + (ciphertext)[3] = (u8)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelSetupEncrypt(u32* rk, const u8* key, int keybits) +{ + int i = 0; + u32 temp; + + rk[0] = GETU32(key); + rk[1] = GETU32(key + 4); + rk[2] = GETU32(key + 8); + rk[3] = GETU32(key + 12); + if (keybits == 128) + { + for (;;) + { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24)] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) + return 10; + rk += 4; + } + } + rk[4] = GETU32(key + 16); + rk[5] = GETU32(key + 20); + if (keybits == 192) + { + for (;;) + { + temp = rk[5]; + rk[6] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24)] & 0x000000ff) ^ + rcon[i]; + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (++i == 8) + return 12; + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(key + 24); + rk[7] = GETU32(key + 28); + if (keybits == 256) + { + for (;;) + { + temp = rk[7]; + rk[8] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24)] & 0x000000ff) ^ + rcon[i]; + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (++i == 7) + return 14; + temp = rk[11]; + rk[12] = rk[4] ^ + (Te4[(temp >> 24)] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp) & 0xff] & 0x000000ff); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelSetupDecrypt(u32* rk, const u8* key, int keybits) +{ + int nrounds, i, j; + u32 temp; + + /* expand the cipher key: */ + // emoose: we copy this in ExCryptAesKey + // nrounds = rijndaelSetupEncrypt(rk, key, keybits); + nrounds = ((keybits / 8) >> 2) + 6; + /* invert the order of the round keys: */ + for (i = 0, j = 4 * nrounds; i < j; i += 4, j -= 4) + { + temp = rk[i]; rk[i] = rk[j]; rk[j] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < nrounds; i++) + { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24)] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0]) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24)] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1]) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24)] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2]) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24)] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3]) & 0xff] & 0xff]; + } + return nrounds; +} + +void rijndaelEncrypt(const u32* rk, int nrounds, const u8 plaintext[16], + u8 ciphertext[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(plaintext) ^ rk[0]; + s1 = GETU32(plaintext + 4) ^ rk[1]; + s2 = GETU32(plaintext + 8) ^ rk[2]; + s3 = GETU32(plaintext + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (nrounds > 10) + { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (nrounds > 12) + { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += nrounds << 2; +#else /* !FULL_UNROLL */ + /* + * nrounds - 1 full rounds: + */ + r = nrounds >> 1; + for (;;) + { + t0 = + Te0[(s0 >> 24)] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24)] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24)] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24)] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2) & 0xff] ^ + rk[7]; + rk += 8; + if (--r == 0) + break; + s0 = + Te0[(t0 >> 24)] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24)] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24)] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24)] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24)] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ciphertext, s0); + s1 = + (Te4[(t1 >> 24)] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ciphertext + 4, s1); + s2 = + (Te4[(t2 >> 24)] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ciphertext + 8, s2); + s3 = + (Te4[(t3 >> 24)] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ciphertext + 12, s3); +} + +void rijndaelDecrypt(const u32* rk, int nrounds, const u8 ciphertext[16], + u8 plaintext[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ciphertext) ^ rk[0]; + s1 = GETU32(ciphertext + 4) ^ rk[1]; + s2 = GETU32(ciphertext + 8) ^ rk[2]; + s3 = GETU32(ciphertext + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (nrounds > 10) + { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (nrounds > 12) + { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += nrounds << 2; +#else /* !FULL_UNROLL */ + /* + * nrounds - 1 full rounds: + */ + r = nrounds >> 1; + for (;;) + { + t0 = + Td0[(s0 >> 24)] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24)] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24)] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24)] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0) & 0xff] ^ + rk[7]; + rk += 8; + if (--r == 0) + break; + s0 = + Td0[(t0 >> 24)] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24)] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24)] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24)] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24)] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(plaintext, s0); + s1 = + (Td4[(t1 >> 24)] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(plaintext + 4, s1); + s2 = + (Td4[(t2 >> 24)] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(plaintext + 8, s2); + s3 = + (Td4[(t3 >> 24)] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(plaintext + 12, s3); +} diff --git a/3rdparty/excrypt/rijndael.h b/3rdparty/excrypt/rijndael.h new file mode 100644 index 0000000..fda9ba7 --- /dev/null +++ b/3rdparty/excrypt/rijndael.h @@ -0,0 +1,17 @@ +#ifndef H__RIJNDAEL +#define H__RIJNDAEL + +int rijndaelSetupEncrypt(unsigned long* rk, const unsigned char* key, + int keybits); +int rijndaelSetupDecrypt(unsigned long* rk, const unsigned char* key, + int keybits); +void rijndaelEncrypt(const unsigned long* rk, int nrounds, + const unsigned char plaintext[16], unsigned char ciphertext[16]); +void rijndaelDecrypt(const unsigned long* rk, int nrounds, + const unsigned char ciphertext[16], unsigned char plaintext[16]); + +#define KEYLENGTH(keybits) ((keybits)/8) +#define RKLENGTH(keybits) ((keybits)/8+28) +#define NROUNDS(keybits) ((keybits)/32+6) + +#endif diff --git a/3rdparty/libmspack_LICENSE b/3rdparty/libmspack_LICENSE new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/3rdparty/libmspack_LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/3rdparty/mspack/config.h b/3rdparty/mspack/config.h new file mode 100644 index 0000000..544888d --- /dev/null +++ b/3rdparty/mspack/config.h @@ -0,0 +1,114 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Turn debugging mode on? */ +#undef DEBUG + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#undef HAVE_FSEEKO + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mkdir' function. */ +#undef HAVE_MKDIR + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the `towlower' function. */ +#undef HAVE_TOWLOWER + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `_mkdir' function. */ +#undef HAVE__MKDIR + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Define if mkdir takes only one argument. */ +#undef MKDIR_TAKES_ONE_ARG + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `off_t', as computed by sizeof. */ +#undef SIZEOF_OFF_T + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#undef _LARGEFILE_SOURCE + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `int' if does not define. */ +#undef mode_t + +/* Define to `long int' if does not define. */ +#define off_t long int + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/3rdparty/mspack/lzx.h b/3rdparty/mspack/lzx.h new file mode 100644 index 0000000..50b7321 --- /dev/null +++ b/3rdparty/mspack/lzx.h @@ -0,0 +1,221 @@ +/* This file is part of libmspack. + * (C) 2003-2013 Stuart Caie. + * + * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted + * by Microsoft Corporation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_LZX_H +#define MSPACK_LZX_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* LZX compression / decompression definitions */ + +/* some constants defined by the LZX specification */ +#define LZX_MIN_MATCH (2) +#define LZX_MAX_MATCH (257) +#define LZX_NUM_CHARS (256) +#define LZX_BLOCKTYPE_INVALID (0) /* also blocktypes 4-7 invalid */ +#define LZX_BLOCKTYPE_VERBATIM (1) +#define LZX_BLOCKTYPE_ALIGNED (2) +#define LZX_BLOCKTYPE_UNCOMPRESSED (3) +#define LZX_PRETREE_NUM_ELEMENTS (20) +#define LZX_ALIGNED_NUM_ELEMENTS (8) /* aligned offset tree #elements */ +#define LZX_NUM_PRIMARY_LENGTHS (7) /* this one missing from spec! */ +#define LZX_NUM_SECONDARY_LENGTHS (249) /* length tree #elements */ + +/* LZX huffman defines: tweak tablebits as desired */ +#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS) +#define LZX_PRETREE_TABLEBITS (6) +#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 290*8) +#define LZX_MAINTREE_TABLEBITS (12) +#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) +#define LZX_LENGTH_TABLEBITS (12) +#define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS) +#define LZX_ALIGNED_TABLEBITS (7) +#define LZX_LENTABLE_SAFETY (64) /* table decoding overruns are allowed */ + +#define LZX_FRAME_SIZE (32768) /* the size of a frame in LZX */ + +struct lzxd_stream { + struct mspack_system *sys; /* I/O routines */ + struct mspack_file *input; /* input file handle */ + struct mspack_file *output; /* output file handle */ + + long int offset; /* number of bytes actually output */ + long int length; /* overall decompressed length of stream */ + + unsigned char *window; /* decoding window */ + unsigned int window_size; /* window size */ + unsigned int ref_data_size; /* LZX DELTA reference data size */ + unsigned int num_offsets; /* number of match_offset entries in table */ + unsigned int window_posn; /* decompression offset within window */ + unsigned int frame_posn; /* current frame offset within in window */ + unsigned int frame; /* the number of 32kb frames processed */ + unsigned int reset_interval; /* which frame do we reset the compressor? */ + + unsigned int R0, R1, R2; /* for the LRU offset system */ + unsigned int block_length; /* uncompressed length of this LZX block */ + unsigned int block_remaining; /* uncompressed bytes still left to decode */ + + signed int intel_filesize; /* magic header value used for transform */ + signed int intel_curpos; /* current offset in transform space */ + + unsigned char intel_started; /* has intel E8 decoding started? */ + unsigned char block_type; /* type of the current block */ + unsigned char header_read; /* have we started decoding at all yet? */ + unsigned char input_end; /* have we reached the end of input? */ + unsigned char is_delta; /* does stream follow LZX DELTA spec? */ + + int error; + + /* I/O buffering */ + unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; + unsigned int bit_buffer, bits_left, inbuf_size; + + /* huffman code lengths */ + unsigned char PRETREE_len [LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + unsigned char LENGTH_len [LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + unsigned char ALIGNED_len [LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + /* huffman decoding tables */ + unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + + (LZX_PRETREE_MAXSYMBOLS * 2)]; + unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + + (LZX_MAINTREE_MAXSYMBOLS * 2)]; + unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + + (LZX_LENGTH_MAXSYMBOLS * 2)]; + unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + + (LZX_ALIGNED_MAXSYMBOLS * 2)]; + unsigned char LENGTH_empty; + + /* this is used purely for doing the intel E8 transform */ + unsigned char e8_buf[LZX_FRAME_SIZE]; +}; + +/** + * Allocates and initialises LZX decompression state for decoding an LZX + * stream. + * + * This routine uses system->alloc() to allocate memory. If memory + * allocation fails, or the parameters to this function are invalid, + * NULL is returned. + * + * @param system an mspack_system structure used to read from + * the input stream and write to the output + * stream, also to allocate and free memory. + * @param input an input stream with the LZX data. + * @param output an output stream to write the decoded data to. + * @param window_bits the size of the decoding window, which must be + * between 15 and 21 inclusive for regular LZX + * data, or between 17 and 25 inclusive for + * LZX DELTA data. + * @param reset_interval the interval at which the LZX bitstream is + * reset, in multiples of LZX frames (32678 + * bytes), e.g. a value of 2 indicates the input + * stream resets after every 65536 output bytes. + * A value of 0 indicates that the bitstream never + * resets, such as in CAB LZX streams. + * @param input_buffer_size the number of bytes to use as an input + * bitstream buffer. + * @param output_length the length in bytes of the entirely + * decompressed output stream, if known in + * advance. It is used to correctly perform the + * Intel E8 transformation, which must stop 6 + * bytes before the very end of the + * decompressed stream. It is not otherwise used + * or adhered to. If the full decompressed + * length is known in advance, set it here. + * If it is NOT known, use the value 0, and call + * lzxd_set_output_length() once it is + * known. If never set, 4 of the final 6 bytes + * of the output stream may be incorrect. + * @param is_delta should be zero for all regular LZX data, + * non-zero for LZX DELTA encoded data. + * @return a pointer to an initialised lzxd_stream structure, or NULL if + * there was not enough memory or parameters to the function were wrong. + */ +extern struct lzxd_stream *lzxd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int reset_interval, + int input_buffer_size, + long int output_length, + char is_delta); + +/* see description of output_length in lzxd_init() */ +extern void lzxd_set_output_length(struct lzxd_stream *lzx, + long int output_length); + +/** + * Reads LZX DELTA reference data into the window and allows + * lzxd_decompress() to reference it. + * + * Call this before the first call to lzxd_decompress(). + + * @param lzx the LZX stream to apply this reference data to + * @param system an mspack_system implementation to use with the + * input param. Only read() will be called. + * @param input an input file handle to read reference data using + * system->read(). + * @param length the length of the reference data. Cannot be longer + * than the LZX window size. + * @return an error code, or MSPACK_ERR_OK if successful + */ +extern int lzxd_set_reference_data(struct lzxd_stream *lzx, + struct mspack_system *system, + struct mspack_file *input, + unsigned int length); + +/** + * Decompresses entire or partial LZX streams. + * + * The number of bytes of data that should be decompressed is given as the + * out_bytes parameter. If more bytes are decoded than are needed, they + * will be kept over for a later invocation. + * + * The output bytes will be passed to the system->write() function given in + * lzxd_init(), using the output file handle given in lzxd_init(). More than + * one call may be made to system->write(). + + * Input bytes will be read in as necessary using the system->read() + * function given in lzxd_init(), using the input file handle given in + * lzxd_init(). This will continue until system->read() returns 0 bytes, + * or an error. Errors will be passed out of the function as + * MSPACK_ERR_READ errors. Input streams should convey an "end of input + * stream" by refusing to supply all the bytes that LZX asks for when they + * reach the end of the stream, rather than return an error code. + * + * If any error code other than MSPACK_ERR_OK is returned, the stream + * should be considered unusable and lzxd_decompress() should not be + * called again on this stream. + * + * @param lzx LZX decompression state, as allocated by lzxd_init(). + * @param out_bytes the number of bytes of data to decompress. + * @return an error code, or MSPACK_ERR_OK if successful + */ +extern int lzxd_decompress(struct lzxd_stream *lzx, long int out_bytes); + +/** + * Frees all state associated with an LZX data stream. This will call + * system->free() using the system pointer given in lzxd_init(). + * + * @param lzx LZX decompression state to free. + */ +void lzxd_free(struct lzxd_stream *lzx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rdparty/mspack/lzxd.c b/3rdparty/mspack/lzxd.c new file mode 100644 index 0000000..56464ad --- /dev/null +++ b/3rdparty/mspack/lzxd.c @@ -0,0 +1,909 @@ +/* This file is part of libmspack. + * (C) 2003-2013 Stuart Caie. + * + * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted + * by Microsoft Corporation. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* LZX decompression implementation */ + +#include +#include + +#undef D +#include +#define D(x) do { printf x; } while (0); + +/* Microsoft's LZX document (in cab-sdk.exe) and their implementation + * of the com.ms.util.cab Java package do not concur. + * + * In the LZX document, there is a table showing the correlation between + * window size and the number of position slots. It states that the 1MB + * window = 40 slots and the 2MB window = 42 slots. In the implementation, + * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the + * first slot whose position base is equal to or more than the required + * window size'. This would explain why other tables in the document refer + * to 50 slots rather than 42. + * + * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode + * is not defined in the specification. + * + * The LZX document does not state the uncompressed block has an + * uncompressed length field. Where does this length field come from, so + * we can know how large the block is? The implementation has it as the 24 + * bits following after the 3 blocktype bits, before the alignment + * padding. + * + * The LZX document states that aligned offset blocks have their aligned + * offset huffman tree AFTER the main and length trees. The implementation + * suggests that the aligned offset tree is BEFORE the main and length + * trees. + * + * The LZX document decoding algorithm states that, in an aligned offset + * block, if an extra_bits value is 1, 2 or 3, then that number of bits + * should be read and the result added to the match offset. This is + * correct for 1 and 2, but not 3, where just a huffman symbol (using the + * aligned tree) should be read. + * + * Regarding the E8 preprocessing, the LZX document states 'No translation + * may be performed on the last 6 bytes of the input block'. This is + * correct. However, the pseudocode provided checks for the *E8 leader* + * up to the last 6 bytes. If the leader appears between -10 and -7 bytes + * from the end, this would cause the next four bytes to be modified, at + * least one of which would be in the last 6 bytes, which is not allowed + * according to the spec. + * + * The specification states that the huffman trees must always contain at + * least one element. However, many CAB files contain blocks where the + * length tree is completely empty (because there are no matches), and + * this is expected to succeed. + * + * The errors in LZX documentation appear have been corrected in the + * new documentation for the LZX DELTA format. + * + * http://msdn.microsoft.com/en-us/library/cc483133.aspx + * + * However, this is a different format, an extension of regular LZX. + * I have noticed the following differences, there may be more: + * + * The maximum window size has increased from 2MB to 32MB. This also + * increases the maximum number of position slots, etc. + * + * If the match length is 257 (the maximum possible), this signals + * a further length decoding step, that allows for matches up to + * 33024 bytes long. + * + * The format now allows for "reference data", supplied by the caller. + * If match offsets go further back than the number of bytes + * decompressed so far, that is them accessing the reference data. + */ + +/* import bit-reading macros and code */ +#define BITS_TYPE struct lzxd_stream +#define BITS_VAR lzx +#define BITS_ORDER_MSB +#define READ_BYTES do { \ + unsigned char b0, b1; \ + READ_IF_NEEDED; b0 = *i_ptr++; \ + READ_IF_NEEDED; b1 = *i_ptr++; \ + INJECT_BITS((b1 << 8) | b0, 16); \ +} while (0) +#include + +/* import huffman-reading macros and code */ +#define TABLEBITS(tbl) LZX_##tbl##_TABLEBITS +#define MAXSYMBOLS(tbl) LZX_##tbl##_MAXSYMBOLS +#define HUFF_TABLE(tbl,idx) lzx->tbl##_table[idx] +#define HUFF_LEN(tbl,idx) lzx->tbl##_len[idx] +#define HUFF_ERROR return lzx->error = MSPACK_ERR_DECRUNCH +#include + +/* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ +#define BUILD_TABLE(tbl) \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + { \ + D(("failed to build %s table", #tbl)) \ + return lzx->error = MSPACK_ERR_DECRUNCH; \ + } + +#define BUILD_TABLE_MAYBE_EMPTY(tbl) do { \ + lzx->tbl##_empty = 0; \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + { \ + for (i = 0; i < MAXSYMBOLS(tbl); i++) { \ + if (HUFF_LEN(tbl, i) > 0) { \ + D(("failed to build %s table", #tbl)) \ + return lzx->error = MSPACK_ERR_DECRUNCH; \ + } \ + } \ + /* empty tree - allow it, but don't decode symbols with it */ \ + lzx->tbl##_empty = 1; \ + } \ +} while (0) + +/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols + * first to last in the given table. The code lengths are stored in their + * own special LZX way. + */ +#define READ_LENGTHS(tbl, first, last) do { \ + STORE_BITS; \ + if (lzxd_read_lens(lzx, &HUFF_LEN(tbl, 0), (first), \ + (unsigned int)(last))) return lzx->error; \ + RESTORE_BITS; \ +} while (0) + +static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, + unsigned int first, unsigned int last) +{ + /* bit buffer and huffman symbol decode variables */ + unsigned int bit_buffer; + int bits_left, i; + unsigned short sym; + unsigned char *i_ptr, *i_end; + + unsigned int x, y; + int z; + + RESTORE_BITS; + + /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */ + for (x = 0; x < 20; x++) { + READ_BITS(y, 4); + lzx->PRETREE_len[x] = y; + } + BUILD_TABLE(PRETREE); + + for (x = first; x < last; ) { + READ_HUFFSYM(PRETREE, z); + if (z == 17) { + /* code = 17, run of ([read 4 bits]+4) zeros */ + READ_BITS(y, 4); y += 4; + while (y--) lens[x++] = 0; + } + else if (z == 18) { + /* code = 18, run of ([read 5 bits]+20) zeros */ + READ_BITS(y, 5); y += 20; + while (y--) lens[x++] = 0; + } + else if (z == 19) { + /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */ + READ_BITS(y, 1); y += 4; + READ_HUFFSYM(PRETREE, z); + z = lens[x] - z; if (z < 0) z += 17; + while (y--) lens[x++] = z; + } + else { + /* code = 0 to 16, delta current length entry */ + z = lens[x] - z; if (z < 0) z += 17; + lens[x++] = z; + } + } + + STORE_BITS; + + return MSPACK_ERR_OK; +} + +/* LZX static data tables: + * + * LZX uses 'position slots' to represent match offsets. For every match, + * a small 'position slot' number and a small offset from that slot are + * encoded instead of one large offset. + * + * The number of slots is decided by how many are needed to encode the + * largest offset for a given window size. This is easy when the gap between + * slots is less than 128Kb, it's a linear relationship. But when extra_bits + * reaches its limit of 17 (because LZX can only ensure reading 17 bits of + * data at a time), we can only jump 128Kb at a time and have to start + * using more and more position slots as each window size doubles. + * + * position_base[] is an index to the position slot bases + * + * extra_bits[] states how many bits of offset-from-base data is needed. + * + * They are calculated as follows: + * extra_bits[i] = 0 where i < 4 + * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36 + * extra_bits[i] = 17 where i >= 36 + * position_base[0] = 0 + * position_base[i] = position_base[i-1] + (1 << extra_bits[i-1]) + */ +static const unsigned int position_slots[11] = { + 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 +}; +static const unsigned char extra_bits[36] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16 +}; +static const unsigned int position_base[290] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, + 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, + 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, + 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, + 1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512, + 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088, + 3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664, + 4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240, + 6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816, + 7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392, + 8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968, + 9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544, + 10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048, + 11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552, + 12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056, + 12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560, + 13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064, + 14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568, + 15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072, + 16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576, + 17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080, + 18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584, + 19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088, + 20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592, + 21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096, + 22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600, + 23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104, + 23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608, + 24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112, + 25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616, + 26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120, + 27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624, + 28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128, + 29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632, + 30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136, + 31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640, + 32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144, + 33161216, 33292288, 33423360 +}; + +static void lzxd_reset_state(struct lzxd_stream *lzx) { + int i; + + lzx->R0 = 1; + lzx->R1 = 1; + lzx->R2 = 1; + lzx->header_read = 0; + lzx->block_remaining = 0; + lzx->block_type = LZX_BLOCKTYPE_INVALID; + + /* initialise tables to 0 (because deltas will be applied to them) */ + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0; + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) lzx->LENGTH_len[i] = 0; +} + +/*-------- main LZX code --------*/ + +struct lzxd_stream *lzxd_init(struct mspack_system *system, + struct mspack_file *input, + struct mspack_file *output, + int window_bits, + int reset_interval, + int input_buffer_size, + long int output_length, + char is_delta) +{ + unsigned int window_size = 1 << window_bits; + struct lzxd_stream *lzx; + + if (!system) return NULL; + + /* LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB), + * regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB) + */ + if (is_delta) { + if (window_bits < 17 || window_bits > 25) return NULL; + } + else { + if (window_bits < 15 || window_bits > 21) return NULL; + } + + if (reset_interval < 0 || output_length < 0) { + D(("reset interval or output length < 0")) + return NULL; + } + + /* round up input buffer size to multiple of two */ + input_buffer_size = (input_buffer_size + 1) & -2; + if (input_buffer_size < 2) return NULL; + + /* allocate decompression state */ + if (!(lzx = (struct lzxd_stream *) system->alloc(system, sizeof(struct lzxd_stream)))) { + return NULL; + } + + /* allocate decompression window and input buffer */ + lzx->window = (unsigned char *) system->alloc(system, (size_t) window_size); + lzx->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); + if (!lzx->window || !lzx->inbuf) { + system->free(lzx->window); + system->free(lzx->inbuf); + system->free(lzx); + return NULL; + } + + /* initialise decompression state */ + lzx->sys = system; + lzx->input = input; + lzx->output = output; + lzx->offset = 0; + lzx->length = output_length; + + lzx->inbuf_size = input_buffer_size; + lzx->window_size = 1 << window_bits; + lzx->ref_data_size = 0; + lzx->window_posn = 0; + lzx->frame_posn = 0; + lzx->frame = 0; + lzx->reset_interval = reset_interval; + lzx->intel_filesize = 0; + lzx->intel_curpos = 0; + lzx->intel_started = 0; + lzx->error = MSPACK_ERR_OK; + lzx->num_offsets = position_slots[window_bits - 15] << 3; + lzx->is_delta = is_delta; + + lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; + lzxd_reset_state(lzx); + INIT_BITS; + return lzx; +} + +int lzxd_set_reference_data(struct lzxd_stream *lzx, + struct mspack_system *system, + struct mspack_file *input, + unsigned int length) +{ + if (!lzx) return MSPACK_ERR_ARGS; + + if (!lzx->is_delta) { + D(("only LZX DELTA streams support reference data")) + return MSPACK_ERR_ARGS; + } + if (lzx->offset) { + D(("too late to set reference data after decoding starts")) + return MSPACK_ERR_ARGS; + } + if (length > lzx->window_size) { + D(("reference length (%u) is longer than the window", length)) + return MSPACK_ERR_ARGS; + } + if (length > 0 && (!system || !input)) { + D(("length > 0 but no system or input")) + return MSPACK_ERR_ARGS; + } + + lzx->ref_data_size = length; + if (length > 0) { + /* copy reference data */ + unsigned char *pos = &lzx->window[lzx->window_size - length]; + int bytes = system->read(input, pos, length); + /* length can't be more than 2^25, so no signedness problem */ + if (bytes < (int)length) return MSPACK_ERR_READ; + } + lzx->ref_data_size = length; + return MSPACK_ERR_OK; +} + +void lzxd_set_output_length(struct lzxd_stream *lzx, long int out_bytes) { + if (lzx && out_bytes > 0) lzx->length = out_bytes; +} + +int lzxd_decompress(struct lzxd_stream *lzx, long int out_bytes) { + /* bitstream and huffman reading variables */ + unsigned int bit_buffer; + int bits_left, i=0; + unsigned char *i_ptr, *i_end; + unsigned short sym; + + int match_length, length_footer, extra, verbatim_bits, bytes_todo; + int this_run, main_element, aligned_bits, j, warned = 0; + unsigned char *window, *runsrc, *rundest, buf[12]; + unsigned int frame_size=0, end_frame, match_offset, window_posn; + unsigned int R0, R1, R2; + + /* easy answers */ + if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS; + if (lzx->error) return lzx->error; + + /* flush out any stored-up bytes before we begin */ + i = (int)(lzx->o_end - lzx->o_ptr); + if ((long int) i > out_bytes) i = (int) out_bytes; + if (i) { + if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { + return lzx->error = MSPACK_ERR_WRITE; + } + lzx->o_ptr += i; + lzx->offset += i; + out_bytes -= i; + } + if (out_bytes == 0) return MSPACK_ERR_OK; + + /* restore local state */ + RESTORE_BITS; + window = lzx->window; + window_posn = lzx->window_posn; + R0 = lzx->R0; + R1 = lzx->R1; + R2 = lzx->R2; + + end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1; + + while (lzx->frame < end_frame) { + /* have we reached the reset interval? (if there is one?) */ + if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { + if (lzx->block_remaining) { + /* this is a file format error, we can make a best effort to extract what we can */ + D(("%d bytes remaining at reset interval", lzx->block_remaining)) + if (!warned) { + lzx->sys->message(NULL, "WARNING; invalid reset interval detected during LZX decompression"); + warned++; + } + } + + /* re-read the intel header and reset the huffman lengths */ + lzxd_reset_state(lzx); + R0 = lzx->R0; + R1 = lzx->R1; + R2 = lzx->R2; + } + + /* LZX DELTA format has chunk_size, not present in LZX format */ + if (lzx->is_delta) { + ENSURE_BITS(16); + REMOVE_BITS(16); + } + + /* read header if necessary */ + if (!lzx->header_read) { + /* read 1 bit. if bit=0, intel filesize = 0. + * if bit=1, read intel filesize (32 bits) */ + j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); } + lzx->intel_filesize = (i << 16) | j; + lzx->header_read = 1; + } + + /* calculate size of frame: all frames are 32k except the final frame + * which is 32kb or less. this can only be calculated when lzx->length + * has been filled in. */ + frame_size = LZX_FRAME_SIZE; + if (lzx->length && (lzx->length - lzx->offset) < (long int)frame_size) { + frame_size = lzx->length - lzx->offset; + } + + /* decode until one more frame is available */ + bytes_todo = lzx->frame_posn + frame_size - window_posn; + while (bytes_todo > 0) { + /* initialise new block, if one is needed */ + if (lzx->block_remaining == 0) { + /* realign if previous block was an odd-sized UNCOMPRESSED block */ + if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && + (lzx->block_length & 1)) + { + READ_IF_NEEDED; + i_ptr++; + } + + /* read block type (3 bits) and block length (24 bits) */ + READ_BITS(lzx->block_type, 3); + READ_BITS(i, 16); READ_BITS(j, 8); + lzx->block_remaining = lzx->block_length = (i << 8) | j; + /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ + + /* read individual block headers */ + switch (lzx->block_type) { + case LZX_BLOCKTYPE_ALIGNED: + /* read lengths of and build aligned huffman decoding tree */ + for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } + BUILD_TABLE(ALIGNED); + /* rest of aligned header is same as verbatim */ /*@fallthrough@*/ + case LZX_BLOCKTYPE_VERBATIM: + /* read lengths of and build main huffman decoding tree */ + READ_LENGTHS(MAINTREE, 0, 256); + READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + lzx->num_offsets); + BUILD_TABLE(MAINTREE); + /* if the literal 0xE8 is anywhere in the block... */ + if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; + /* read lengths of and build lengths huffman decoding tree */ + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); + BUILD_TABLE_MAYBE_EMPTY(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + /* because we can't assume otherwise */ + lzx->intel_started = 1; + + /* read 1-16 (not 0-15) bits to align to bytes */ + if (bits_left == 0) ENSURE_BITS(16); + bits_left = 0; bit_buffer = 0; + + /* read 12 bytes of stored R0 / R1 / R2 values */ + for (rundest = &buf[0], i = 0; i < 12; i++) { + READ_IF_NEEDED; + *rundest++ = *i_ptr++; + } + R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); + R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); + break; + + default: + D(("bad block type")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + } + + /* decode more of the block: + * run = min(what's available, what's needed) */ + this_run = lzx->block_remaining; + if (this_run > bytes_todo) this_run = bytes_todo; + + /* assume we decode exactly this_run bytes, for now */ + bytes_todo -= this_run; + lzx->block_remaining -= this_run; + + /* decode at least this_run bytes */ + switch (lzx->block_type) { + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + /* get match length */ + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + if (lzx->LENGTH_empty) { + D(("LENGTH symbol needed but tree is empty")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + /* get match offset */ + switch ((match_offset = (main_element >> 3))) { + case 0: match_offset = R0; break; + case 1: match_offset = R1; R1=R0; R0 = match_offset; break; + case 2: match_offset = R2; R2=R0; R0 = match_offset; break; + case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; + default: + extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = position_base[match_offset] - 2 + verbatim_bits; + R2 = R1; R1 = R0; R0 = match_offset; + } + + /* LZX DELTA uses max match length to signal even longer match */ + if (match_length == LZX_MAX_MATCH && lzx->is_delta) { + int extra_len = 0; + ENSURE_BITS(3); /* 4 entry huffman tree */ + if (PEEK_BITS(1) == 0) { + REMOVE_BITS(1); /* '0' -> 8 extra length bits */ + READ_BITS(extra_len, 8); + } + else if (PEEK_BITS(2) == 2) { + REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */ + READ_BITS(extra_len, 10); + extra_len += 0x100; + } + else if (PEEK_BITS(3) == 6) { + REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */ + READ_BITS(extra_len, 12); + extra_len += 0x500; + } + else { + REMOVE_BITS(3); /* '111' -> 15 extra length bits */ + READ_BITS(extra_len, 15); + } + match_length += extra_len; + } + + if ((window_posn + match_length) > lzx->window_size) { + D(("match ran over window wrap")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* copy match */ + rundest = &window[window_posn]; + i = match_length; + /* does match offset wrap the window? */ + if (match_offset > window_posn) { + if ((long int)match_offset > lzx->offset && + (match_offset - window_posn) > lzx->ref_data_size) + { + D(("match offset beyond LZX stream")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) lzx->window_size) { + D(("match offset beyond window boundaries")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[lzx->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + + this_run -= match_length; + window_posn += match_length; + } + } /* while (this_run > 0) */ + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + /* get match length */ + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + if (lzx->LENGTH_empty) { + D(("LENGTH symbol needed but tree is empty")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + /* get match offset */ + switch ((match_offset = (main_element >> 3))) { + case 0: match_offset = R0; break; + case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; + case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; + default: + extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; + match_offset = position_base[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += verbatim_bits; + } + else /* extra == 0 */ { + /* ??? not defined in LZX specification! */ + match_offset = 1; + } + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + + /* LZX DELTA uses max match length to signal even longer match */ + if (match_length == LZX_MAX_MATCH && lzx->is_delta) { + int extra_len = 0; + ENSURE_BITS(3); /* 4 entry huffman tree */ + if (PEEK_BITS(1) == 0) { + REMOVE_BITS(1); /* '0' -> 8 extra length bits */ + READ_BITS(extra_len, 8); + } + else if (PEEK_BITS(2) == 2) { + REMOVE_BITS(2); /* '10' -> 10 extra length bits + 0x100 */ + READ_BITS(extra_len, 10); + extra_len += 0x100; + } + else if (PEEK_BITS(3) == 6) { + REMOVE_BITS(3); /* '110' -> 12 extra length bits + 0x500 */ + READ_BITS(extra_len, 12); + extra_len += 0x500; + } + else { + REMOVE_BITS(3); /* '111' -> 15 extra length bits */ + READ_BITS(extra_len, 15); + } + match_length += extra_len; + } + + if ((window_posn + match_length) > lzx->window_size) { + D(("match ran over window wrap")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* copy match */ + rundest = &window[window_posn]; + i = match_length; + /* does match offset wrap the window? */ + if (match_offset > window_posn) { + if ((long int)match_offset > lzx->offset && + (match_offset - window_posn) > lzx->ref_data_size) + { + D(("match offset beyond LZX stream")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + /* j = length from match offset to end of window */ + j = match_offset - window_posn; + if (j > (int) lzx->window_size) { + D(("match offset beyond window boundaries")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + runsrc = &window[lzx->window_size - j]; + if (j < i) { + /* if match goes over the window edge, do two copy runs */ + i -= j; while (j-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + while (i-- > 0) *rundest++ = *runsrc++; + } + else { + runsrc = rundest - match_offset; + while (i-- > 0) *rundest++ = *runsrc++; + } + + this_run -= match_length; + window_posn += match_length; + } + } /* while (this_run > 0) */ + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + /* as this_run is limited not to wrap a frame, this also means it + * won't wrap the window (as the window is a multiple of 32k) */ + rundest = &window[window_posn]; + window_posn += this_run; + while (this_run > 0) { + if ((i = (int)(i_end - i_ptr)) == 0) { + READ_IF_NEEDED; + } + else { + if (i > this_run) i = this_run; + lzx->sys->copy(i_ptr, rundest, (size_t) i); + rundest += i; + i_ptr += i; + this_run -= i; + } + } + break; + + default: + return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ + } + + /* did the final match overrun our desired this_run length? */ + if (this_run < 0) { + if ((unsigned int)(-this_run) > lzx->block_remaining) { + D(("overrun went past end of block by %d (%d remaining)", + -this_run, lzx->block_remaining )) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + lzx->block_remaining -= -this_run; + } + } /* while (bytes_todo > 0) */ + + /* streams don't extend over frame boundaries */ + if ((window_posn - lzx->frame_posn) != frame_size) { + D(("decode beyond output frame limits! %d != %d", + window_posn - lzx->frame_posn, frame_size)) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* re-align input bitstream */ + if (bits_left > 0) ENSURE_BITS(16); + if (bits_left & 15) REMOVE_BITS(bits_left & 15); + + /* check that we've used all of the previous frame first */ + if (lzx->o_ptr != lzx->o_end) { + D(("%ld avail bytes, new %d frame", + (long)(lzx->o_end - lzx->o_ptr), frame_size)) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* does this intel block _really_ need decoding? */ + if (lzx->intel_started && lzx->intel_filesize && + (lzx->frame <= 32768) && (frame_size > 10)) + { + unsigned char *data = &lzx->e8_buf[0]; + unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; + signed int curpos = lzx->intel_curpos; + signed int filesize = lzx->intel_filesize; + signed int abs_off, rel_off; + + /* copy e8 block to the e8 buffer and tweak if needed */ + lzx->o_ptr = data; + lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size); + + while (data < dataend) { + if (*data++ != 0xE8) { curpos++; continue; } + abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (unsigned char) rel_off; + data[1] = (unsigned char) (rel_off >> 8); + data[2] = (unsigned char) (rel_off >> 16); + data[3] = (unsigned char) (rel_off >> 24); + } + data += 4; + curpos += 5; + } + lzx->intel_curpos += frame_size; + } + else { + lzx->o_ptr = &lzx->window[lzx->frame_posn]; + if (lzx->intel_filesize) lzx->intel_curpos += frame_size; + } + lzx->o_end = &lzx->o_ptr[frame_size]; + + /* write a frame */ + i = (out_bytes < (long int)frame_size) ? (unsigned int)out_bytes : frame_size; + if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { + return lzx->error = MSPACK_ERR_WRITE; + } + lzx->o_ptr += i; + lzx->offset += i; + out_bytes -= i; + + /* advance frame start position */ + lzx->frame_posn += frame_size; + lzx->frame++; + + /* wrap window / frame position pointers */ + if (window_posn == lzx->window_size) window_posn = 0; + if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0; + + } /* while (lzx->frame < end_frame) */ + + if (out_bytes) { + D(("bytes left to output")) + return lzx->error = MSPACK_ERR_DECRUNCH; + } + + /* store local state */ + STORE_BITS; + lzx->window_posn = window_posn; + lzx->R0 = R0; + lzx->R1 = R1; + lzx->R2 = R2; + + return MSPACK_ERR_OK; +} + +void lzxd_free(struct lzxd_stream *lzx) { + struct mspack_system *sys; + if (lzx) { + sys = lzx->sys; + sys->free(lzx->inbuf); + sys->free(lzx->window); + sys->free(lzx); + } +} diff --git a/3rdparty/mspack/mspack.h b/3rdparty/mspack/mspack.h new file mode 100644 index 0000000..f9161f9 --- /dev/null +++ b/3rdparty/mspack/mspack.h @@ -0,0 +1,2362 @@ +/* libmspack -- a library for working with Microsoft compression formats. + * (C) 2003-2016 Stuart Caie + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** \mainpage + * + * \section intro Introduction + * + * libmspack is a library which provides compressors and decompressors, + * archivers and dearchivers for Microsoft compression formats. + * + * \section formats Formats supported + * + * The following file formats are supported: + * - SZDD files, which use LZSS compression + * - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression + * - .HLP (MS Help) files, which use LZSS compression + * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression + * - .CHM (HTML Help) files, which use LZX compression + * - .LIT (MS EBook) files, which use LZX compression and DES encryption + * - .LZX (Exchange Offline Addressbook) files, which use LZX compression + * + * To determine the capabilities of the library, and the binary + * compatibility version of any particular compressor or decompressor, use + * the mspack_version() function. The UNIX library interface version is + * defined as the highest-versioned library component. + * + * \section starting Getting started + * + * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can + * be used. In particular, it checks if the caller is using 32-bit file I/O + * when the library is compiled for 64-bit file I/O and vice versa. + * + * If compiled normally, the library includes basic file I/O and memory + * management functionality using the standard C library. This can be + * customised and replaced entirely by creating a mspack_system structure. + * + * A compressor or decompressor for the required format must be + * instantiated before it can be used. Each construction function takes + * one parameter, which is either a pointer to a custom mspack_system + * structure, or NULL to use the default. The instantiation returned, if + * not NULL, contains function pointers (methods) to work with the given + * file format. + * + * For compression: + * - mspack_create_cab_compressor() creates a mscab_compressor + * - mspack_create_chm_compressor() creates a mschm_compressor + * - mspack_create_lit_compressor() creates a mslit_compressor + * - mspack_create_hlp_compressor() creates a mshlp_compressor + * - mspack_create_szdd_compressor() creates a msszdd_compressor + * - mspack_create_kwaj_compressor() creates a mskwaj_compressor + * - mspack_create_oab_compressor() creates a msoab_compressor + * + * For decompression: + * - mspack_create_cab_decompressor() creates a mscab_decompressor + * - mspack_create_chm_decompressor() creates a mschm_decompressor + * - mspack_create_lit_decompressor() creates a mslit_decompressor + * - mspack_create_hlp_decompressor() creates a mshlp_decompressor + * - mspack_create_szdd_decompressor() creates a msszdd_decompressor + * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor + * - mspack_create_oab_decompressor() creates a msoab_decompressor + * + * Once finished working with a format, each kind of + * compressor/decompressor has its own specific destructor: + * - mspack_destroy_cab_compressor() + * - mspack_destroy_cab_decompressor() + * - mspack_destroy_chm_compressor() + * - mspack_destroy_chm_decompressor() + * - mspack_destroy_lit_compressor() + * - mspack_destroy_lit_decompressor() + * - mspack_destroy_hlp_compressor() + * - mspack_destroy_hlp_decompressor() + * - mspack_destroy_szdd_compressor() + * - mspack_destroy_szdd_decompressor() + * - mspack_destroy_kwaj_compressor() + * - mspack_destroy_kwaj_decompressor() + * - mspack_destroy_oab_compressor() + * - mspack_destroy_oab_decompressor() + * + * Destroying a compressor or decompressor does not destroy any objects, + * structures or handles that have been created using that compressor or + * decompressor. Ensure that everything created or opened is destroyed or + * closed before compressor/decompressor is itself destroyed. + * + * \section errors Error codes + * + * All compressors and decompressors use the same set of error codes. Most + * methods return an error code directly. For methods which do not + * return error codes directly, the error code can be obtained with the + * last_error() method. + * + * - #MSPACK_ERR_OK is used to indicate success. This error code is defined + * as zero, all other code are non-zero. + * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate + * arguments. + * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed. + * - #MSPACK_ERR_READ indicates that mspack_system::read() failed. + * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed. + * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed. + * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed. + * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not + * have the correct "signature". It is probably not a valid file for + * whatever format is being read. + * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read + * is corrupt. + * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed. + * - #MSPACK_ERR_CRUNCH indicates an error occured during compression. + * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression. + * + * \section threading Multi-threading + * + * libmspack methods are reentrant and multithreading-safe when each + * thread has its own compressor or decompressor. + + * You should not call multiple methods simultaneously on a single + * compressor or decompressor instance. + * + * If this may happen, you can either use one compressor or + * decompressor per thread, or you can use your preferred lock, + * semaphore or mutex library to ensure no more than one method on a + * compressor/decompressor is called simultaneously. libmspack will + * not do this locking for you. + * + * Example of incorrect behaviour: + * - thread 1 calls mspack_create_cab_decompressor() + * - thread 1 calls open() + * - thread 1 calls extract() for one file + * - thread 2 simultaneously calls extract() for another file + * + * Correct behaviour: + * - thread 1 calls mspack_create_cab_decompressor() + * - thread 2 calls mspack_create_cab_decompressor() + * - thread 1 calls its own open() / extract() + * - thread 2 simultaneously calls its own open() / extract() + * + * Also correct behaviour: + * - thread 1 calls mspack_create_cab_decompressor() + * - thread 1 locks a mutex for with the decompressor before + * calling any methods on it, and unlocks the mutex after each + * method returns. + * - thread 1 can share the results of open() with thread 2, and both + * can call extract(), provided they both guard against simultaneous + * use of extract(), and any other methods, with the mutex + */ + +#ifndef LIB_MSPACK_H +#define LIB_MSPACK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * System self-test function, to ensure both library and calling program + * can use one another. + * + * A result of MSPACK_ERR_OK means the library and caller are + * compatible. Any other result indicates that the library and caller are + * not compatible and should not be used. In particular, a value of + * MSPACK_ERR_SEEK means the library and caller use different off_t + * datatypes. + * + * It should be used like so: + * + * @code + * int selftest_result; + * MSPACK_SYS_SELFTEST(selftest_result); + * if (selftest_result != MSPACK_ERR_OK) { + * fprintf(stderr, "incompatible with this build of libmspack\n"); + * exit(0); + * } + * @endcode + * + * @param result an int variable to store the result of the self-test + */ +#define MSPACK_SYS_SELFTEST(result) do { \ + (result) = mspack_sys_selftest_internal(sizeof(off_t)); \ +} while (0) + +/** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */ +extern int mspack_sys_selftest_internal(int); + +/** + * Enquire about the binary compatibility version of a specific interface in + * the library. Currently, the following interfaces are defined: + * + * - #MSPACK_VER_LIBRARY: the overall library + * - #MSPACK_VER_SYSTEM: the mspack_system interface + * - #MSPACK_VER_MSCABD: the mscab_decompressor interface + * - #MSPACK_VER_MSCABC: the mscab_compressor interface + * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface + * - #MSPACK_VER_MSCHMC: the mschm_compressor interface + * - #MSPACK_VER_MSLITD: the mslit_decompressor interface + * - #MSPACK_VER_MSLITC: the mslit_compressor interface + * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface + * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface + * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface + * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface + * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface + * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface + * - #MSPACK_VER_MSOABD: the msoab_decompressor interface + * - #MSPACK_VER_MSOABC: the msoab_compressor interface + * + * The result of the function should be interpreted as follows: + * - -1: this interface is completely unknown to the library + * - 0: this interface is known, but non-functioning + * - 1: this interface has all basic functionality + * - 2, 3, ...: this interface has additional functionality, clearly marked + * in the documentation as "version 2", "version 3" and so on. + * + * @param entity the interface to request current version of + * @return the version of the requested interface + */ +extern int mspack_version(int entity); + +/** Pass to mspack_version() to get the overall library version */ +#define MSPACK_VER_LIBRARY (0) +/** Pass to mspack_version() to get the mspack_system version */ +#define MSPACK_VER_SYSTEM (1) +/** Pass to mspack_version() to get the mscab_decompressor version */ +#define MSPACK_VER_MSCABD (2) +/** Pass to mspack_version() to get the mscab_compressor version */ +#define MSPACK_VER_MSCABC (3) +/** Pass to mspack_version() to get the mschm_decompressor version */ +#define MSPACK_VER_MSCHMD (4) +/** Pass to mspack_version() to get the mschm_compressor version */ +#define MSPACK_VER_MSCHMC (5) +/** Pass to mspack_version() to get the mslit_decompressor version */ +#define MSPACK_VER_MSLITD (6) +/** Pass to mspack_version() to get the mslit_compressor version */ +#define MSPACK_VER_MSLITC (7) +/** Pass to mspack_version() to get the mshlp_decompressor version */ +#define MSPACK_VER_MSHLPD (8) +/** Pass to mspack_version() to get the mshlp_compressor version */ +#define MSPACK_VER_MSHLPC (9) +/** Pass to mspack_version() to get the msszdd_decompressor version */ +#define MSPACK_VER_MSSZDDD (10) +/** Pass to mspack_version() to get the msszdd_compressor version */ +#define MSPACK_VER_MSSZDDC (11) +/** Pass to mspack_version() to get the mskwaj_decompressor version */ +#define MSPACK_VER_MSKWAJD (12) +/** Pass to mspack_version() to get the mskwaj_compressor version */ +#define MSPACK_VER_MSKWAJC (13) +/** Pass to mspack_version() to get the msoab_decompressor version */ +#define MSPACK_VER_MSOABD (14) +/** Pass to mspack_version() to get the msoab_compressor version */ +#define MSPACK_VER_MSOABC (15) + +/* --- file I/O abstraction ------------------------------------------------ */ + +/** + * A structure which abstracts file I/O and memory management. + * + * The library always uses the mspack_system structure for interaction + * with the file system and to allocate, free and copy all memory. It also + * uses it to send literal messages to the library user. + * + * When the library is compiled normally, passing NULL to a compressor or + * decompressor constructor will result in a default mspack_system being + * used, where all methods are implemented with the standard C library. + * However, all constructors support being given a custom created + * mspack_system structure, with the library user's own methods. This + * allows for more abstract interaction, such as reading and writing files + * directly to memory, or from a network socket or pipe. + * + * Implementors of an mspack_system structure should read all + * documentation entries for every structure member, and write methods + * which conform to those standards. + */ +struct mspack_system { + /** + * Opens a file for reading, writing, appending or updating. + * + * @param self a self-referential pointer to the mspack_system + * structure whose open() method is being called. If + * this pointer is required by close(), read(), write(), + * seek() or tell(), it should be stored in the result + * structure at this time. + * @param filename the file to be opened. It is passed directly from the + * library caller without being modified, so it is up to + * the caller what this parameter actually represents. + * @param mode one of #MSPACK_SYS_OPEN_READ (open an existing file + * for reading), #MSPACK_SYS_OPEN_WRITE (open a new file + * for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing + * file for reading/writing from the start of the file) or + * #MSPACK_SYS_OPEN_APPEND (open an existing file for + * reading/writing from the end of the file) + * @return a pointer to a mspack_file structure. This structure officially + * contains no members, its true contents are up to the + * mspack_system implementor. It should contain whatever is needed + * for other mspack_system methods to operate. Returning the NULL + * pointer indicates an error condition. + * @see close(), read(), write(), seek(), tell(), message() + */ + struct mspack_file * (*open)(struct mspack_system *self, + const char *filename, + int mode); + + /** + * Closes a previously opened file. If any memory was allocated for this + * particular file handle, it should be freed at this time. + * + * @param file the file to close + * @see open() + */ + void (*close)(struct mspack_file *file); + + /** + * Reads a given number of bytes from an open file. + * + * @param file the file to read from + * @param buffer the location where the read bytes should be stored + * @param bytes the number of bytes to read from the file. + * @return the number of bytes successfully read (this can be less than + * the number requested), zero to mark the end of file, or less + * than zero to indicate an error. The library does not "retry" + * reads and assumes short reads are due to EOF, so you should + * avoid returning short reads because of transient errors. + * @see open(), write() + */ + int (*read)(struct mspack_file *file, + void *buffer, + int bytes); + + /** + * Writes a given number of bytes to an open file. + * + * @param file the file to write to + * @param buffer the location where the written bytes should be read from + * @param bytes the number of bytes to write to the file. + * @return the number of bytes successfully written, this can be less + * than the number requested. Zero or less can indicate an error + * where no bytes at all could be written. All cases where less + * bytes were written than requested are considered by the library + * to be an error. + * @see open(), read() + */ + int (*write)(struct mspack_file *file, + void *buffer, + int bytes); + + /** + * Seeks to a specific file offset within an open file. + * + * Sometimes the library needs to know the length of a file. It does + * this by seeking to the end of the file with seek(file, 0, + * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want + * to make a special case for this. + * + * Due to the potentially varying 32/64 bit datatype off_t on some + * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before + * using the library. If not, the error caused by the library passing an + * inappropriate stackframe to seek() is subtle and hard to trace. + * + * @param file the file to be seeked + * @param offset an offset to seek, measured in bytes + * @param mode one of #MSPACK_SYS_SEEK_START (the offset should be + * measured from the start of the file), #MSPACK_SYS_SEEK_CUR + * (the offset should be measured from the current file offset) + * or #MSPACK_SYS_SEEK_END (the offset should be measured from + * the end of the file) + * @return zero for success, non-zero for an error + * @see open(), tell() + */ + int (*seek)(struct mspack_file *file, + off_t offset, + int mode); + + /** + * Returns the current file position (in bytes) of the given file. + * + * @param file the file whose file position is wanted + * @return the current file position of the file + * @see open(), seek() + */ + off_t (*tell)(struct mspack_file *file); + + /** + * Used to send messages from the library to the user. + * + * Occasionally, the library generates warnings or other messages in + * plain english to inform the human user. These are informational only + * and can be ignored if not wanted. + * + * @param file may be a file handle returned from open() if this message + * pertains to a specific open file, or NULL if not related to + * a specific file. + * @param format a printf() style format string. It does NOT include a + * trailing newline. + * @see open() + */ + void (*message)(struct mspack_file *file, + const char *format, + ...); + + /** + * Allocates memory. + * + * @param self a self-referential pointer to the mspack_system + * structure whose alloc() method is being called. + * @param bytes the number of bytes to allocate + * @result a pointer to the requested number of bytes, or NULL if + * not enough memory is available + * @see free() + */ + void * (*alloc)(struct mspack_system *self, + size_t bytes); + + /** + * Frees memory. + * + * @param ptr the memory to be freed. NULL is accepted and ignored. + * @see alloc() + */ + void (*free)(void *ptr); + + /** + * Copies from one region of memory to another. + * + * The regions of memory are guaranteed not to overlap, are usually less + * than 256 bytes, and may not be aligned. Please note that the source + * parameter comes before the destination parameter, unlike the standard + * C function memcpy(). + * + * @param src the region of memory to copy from + * @param dest the region of memory to copy to + * @param bytes the size of the memory region, in bytes + */ + void (*copy)(void *src, + void *dest, + size_t bytes); + + /** + * A null pointer to mark the end of mspack_system. It must equal NULL. + * + * Should the mspack_system structure extend in the future, this NULL + * will be seen, rather than have an invalid method pointer called. + */ + void *null_ptr; +}; + +/** mspack_system::open() mode: open existing file for reading. */ +#define MSPACK_SYS_OPEN_READ (0) +/** mspack_system::open() mode: open new file for writing */ +#define MSPACK_SYS_OPEN_WRITE (1) +/** mspack_system::open() mode: open existing file for writing */ +#define MSPACK_SYS_OPEN_UPDATE (2) +/** mspack_system::open() mode: open existing file for writing */ +#define MSPACK_SYS_OPEN_APPEND (3) + +/** mspack_system::seek() mode: seek relative to start of file */ +#define MSPACK_SYS_SEEK_START (0) +/** mspack_system::seek() mode: seek relative to current offset */ +#define MSPACK_SYS_SEEK_CUR (1) +/** mspack_system::seek() mode: seek relative to end of file */ +#define MSPACK_SYS_SEEK_END (2) + +/** + * A structure which represents an open file handle. The contents of this + * structure are determined by the implementation of the + * mspack_system::open() method. + */ +struct mspack_file { + int dummy; +}; + +/* --- error codes --------------------------------------------------------- */ + +/** Error code: no error */ +#define MSPACK_ERR_OK (0) +/** Error code: bad arguments to method */ +#define MSPACK_ERR_ARGS (1) +/** Error code: error opening file */ +#define MSPACK_ERR_OPEN (2) +/** Error code: error reading file */ +#define MSPACK_ERR_READ (3) +/** Error code: error writing file */ +#define MSPACK_ERR_WRITE (4) +/** Error code: seek error */ +#define MSPACK_ERR_SEEK (5) +/** Error code: out of memory */ +#define MSPACK_ERR_NOMEMORY (6) +/** Error code: bad "magic id" in file */ +#define MSPACK_ERR_SIGNATURE (7) +/** Error code: bad or corrupt file format */ +#define MSPACK_ERR_DATAFORMAT (8) +/** Error code: bad checksum or CRC */ +#define MSPACK_ERR_CHECKSUM (9) +/** Error code: error during compression */ +#define MSPACK_ERR_CRUNCH (10) +/** Error code: error during decompression */ +#define MSPACK_ERR_DECRUNCH (11) + +/* --- functions available in library -------------------------------------- */ + +/** Creates a new CAB compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mscab_compressor or NULL + */ +extern struct mscab_compressor * + mspack_create_cab_compressor(struct mspack_system *sys); + +/** Creates a new CAB decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mscab_decompressor or NULL + */ +extern struct mscab_decompressor * + mspack_create_cab_decompressor(struct mspack_system *sys); + +/** Destroys an existing CAB compressor. + * @param self the #mscab_compressor to destroy + */ +extern void mspack_destroy_cab_compressor(struct mscab_compressor *self); + +/** Destroys an existing CAB decompressor. + * @param self the #mscab_decompressor to destroy + */ +extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *self); + + +/** Creates a new CHM compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mschm_compressor or NULL + */ +extern struct mschm_compressor * + mspack_create_chm_compressor(struct mspack_system *sys); + +/** Creates a new CHM decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mschm_decompressor or NULL + */ +extern struct mschm_decompressor * + mspack_create_chm_decompressor(struct mspack_system *sys); + +/** Destroys an existing CHM compressor. + * @param self the #mschm_compressor to destroy + */ +extern void mspack_destroy_chm_compressor(struct mschm_compressor *self); + +/** Destroys an existing CHM decompressor. + * @param self the #mschm_decompressor to destroy + */ +extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *self); + + +/** Creates a new LIT compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mslit_compressor or NULL + */ +extern struct mslit_compressor * + mspack_create_lit_compressor(struct mspack_system *sys); + +/** Creates a new LIT decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mslit_decompressor or NULL + */ +extern struct mslit_decompressor * + mspack_create_lit_decompressor(struct mspack_system *sys); + +/** Destroys an existing LIT compressor. + * @param self the #mslit_compressor to destroy + */ +extern void mspack_destroy_lit_compressor(struct mslit_compressor *self); + +/** Destroys an existing LIT decompressor. + * @param self the #mslit_decompressor to destroy + */ +extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *self); + + +/** Creates a new HLP compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mshlp_compressor or NULL + */ +extern struct mshlp_compressor * + mspack_create_hlp_compressor(struct mspack_system *sys); + +/** Creates a new HLP decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mshlp_decompressor or NULL + */ +extern struct mshlp_decompressor * + mspack_create_hlp_decompressor(struct mspack_system *sys); + +/** Destroys an existing hlp compressor. + * @param self the #mshlp_compressor to destroy + */ +extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *self); + +/** Destroys an existing hlp decompressor. + * @param self the #mshlp_decompressor to destroy + */ +extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *self); + + +/** Creates a new SZDD compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msszdd_compressor or NULL + */ +extern struct msszdd_compressor * + mspack_create_szdd_compressor(struct mspack_system *sys); + +/** Creates a new SZDD decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msszdd_decompressor or NULL + */ +extern struct msszdd_decompressor * + mspack_create_szdd_decompressor(struct mspack_system *sys); + +/** Destroys an existing SZDD compressor. + * @param self the #msszdd_compressor to destroy + */ +extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *self); + +/** Destroys an existing SZDD decompressor. + * @param self the #msszdd_decompressor to destroy + */ +extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *self); + + +/** Creates a new KWAJ compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mskwaj_compressor or NULL + */ +extern struct mskwaj_compressor * + mspack_create_kwaj_compressor(struct mspack_system *sys); + +/** Creates a new KWAJ decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #mskwaj_decompressor or NULL + */ +extern struct mskwaj_decompressor * + mspack_create_kwaj_decompressor(struct mspack_system *sys); + +/** Destroys an existing KWAJ compressor. + * @param self the #mskwaj_compressor to destroy + */ +extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *self); + +/** Destroys an existing KWAJ decompressor. + * @param self the #mskwaj_decompressor to destroy + */ +extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *self); + + +/** Creates a new OAB compressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msoab_compressor or NULL + */ +extern struct msoab_compressor * + mspack_create_oab_compressor(struct mspack_system *sys); + +/** Creates a new OAB decompressor. + * @param sys a custom mspack_system structure, or NULL to use the default + * @return a #msoab_decompressor or NULL + */ +extern struct msoab_decompressor * + mspack_create_oab_decompressor(struct mspack_system *sys); + +/** Destroys an existing OAB compressor. + * @param self the #msoab_compressor to destroy + */ +extern void mspack_destroy_oab_compressor(struct msoab_compressor *self); + +/** Destroys an existing OAB decompressor. + * @param self the #msoab_decompressor to destroy + */ +extern void mspack_destroy_oab_decompressor(struct msoab_decompressor *self); + + +/* --- support for .CAB (MS Cabinet) file format --------------------------- */ + +/** + * A structure which represents a single cabinet file. + * + * All fields are READ ONLY. + * + * If this cabinet is part of a merged cabinet set, the #files and #folders + * fields are common to all cabinets in the set, and will be identical. + * + * @see mscab_decompressor::open(), mscab_decompressor::close(), + * mscab_decompressor::search() + */ +struct mscabd_cabinet { + /** + * The next cabinet in a chained list, if this cabinet was opened with + * mscab_decompressor::search(). May be NULL to mark the end of the + * list. + */ + struct mscabd_cabinet *next; + + /** + * The filename of the cabinet. More correctly, the filename of the + * physical file that the cabinet resides in. This is given by the + * library user and may be in any format. + */ + const char *filename; + + /** The file offset of cabinet within the physical file it resides in. */ + off_t base_offset; + + /** The length of the cabinet file in bytes. */ + unsigned int length; + + /** The previous cabinet in a cabinet set, or NULL. */ + struct mscabd_cabinet *prevcab; + + /** The next cabinet in a cabinet set, or NULL. */ + struct mscabd_cabinet *nextcab; + + /** The filename of the previous cabinet in a cabinet set, or NULL. */ + char *prevname; + + /** The filename of the next cabinet in a cabinet set, or NULL. */ + char *nextname; + + /** The name of the disk containing the previous cabinet in a cabinet + * set, or NULL. + */ + char *previnfo; + + /** The name of the disk containing the next cabinet in a cabinet set, + * or NULL. + */ + char *nextinfo; + + /** A list of all files in the cabinet or cabinet set. */ + struct mscabd_file *files; + + /** A list of all folders in the cabinet or cabinet set. */ + struct mscabd_folder *folders; + + /** + * The set ID of the cabinet. All cabinets in the same set should have + * the same set ID. + */ + unsigned short set_id; + + /** + * The index number of the cabinet within the set. Numbering should + * start from 0 for the first cabinet in the set, and increment by 1 for + * each following cabinet. + */ + unsigned short set_index; + + /** + * The number of bytes reserved in the header area of the cabinet. + * + * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can + * be read by the calling application. It is of the given length, + * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the + * cabinet file. + * + * @see flags + */ + unsigned short header_resv; + + /** + * Header flags. + * + * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and + * has a predecessor cabinet. + * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and + * has a successor cabinet. + * - MSCAB_HDR_RESV indicates the cabinet has reserved header space. + * + * @see prevname, previnfo, nextname, nextinfo, header_resv + */ + int flags; +}; + +/** Offset from start of cabinet to the reserved header data (if present). */ +#define MSCAB_HDR_RESV_OFFSET (0x28) + +/** Cabinet header flag: cabinet has a predecessor */ +#define MSCAB_HDR_PREVCAB (0x01) +/** Cabinet header flag: cabinet has a successor */ +#define MSCAB_HDR_NEXTCAB (0x02) +/** Cabinet header flag: cabinet has reserved header space */ +#define MSCAB_HDR_RESV (0x04) + +/** + * A structure which represents a single folder in a cabinet or cabinet set. + * + * All fields are READ ONLY. + * + * A folder is a single compressed stream of data. When uncompressed, it + * holds the data of one or more files. A folder may be split across more + * than one cabinet. + */ +struct mscabd_folder { + /** + * A pointer to the next folder in this cabinet or cabinet set, or NULL + * if this is the final folder. + */ + struct mscabd_folder *next; + + /** + * The compression format used by this folder. + * + * The macro MSCABD_COMP_METHOD() should be used on this field to get + * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get + * the "compression level". + * + * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL() + */ + int comp_type; + + /** + * The total number of data blocks used by this folder. This includes + * data blocks present in other files, if this folder spans more than + * one cabinet. + */ + unsigned int num_blocks; +}; + +/** + * Returns the compression method used by a folder. + * + * @param comp_type a mscabd_folder::comp_type value + * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM + * or #MSCAB_COMP_LZX + */ +#define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F) +/** + * Returns the compression level used by a folder. + * + * @param comp_type a mscabd_folder::comp_type value + * @return the compression level. This is only defined by LZX and Quantum + * compression + */ +#define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F) + +/** Compression mode: no compression. */ +#define MSCAB_COMP_NONE (0) +/** Compression mode: MSZIP (deflate) compression. */ +#define MSCAB_COMP_MSZIP (1) +/** Compression mode: Quantum compression */ +#define MSCAB_COMP_QUANTUM (2) +/** Compression mode: LZX compression */ +#define MSCAB_COMP_LZX (3) + +/** + * A structure which represents a single file in a cabinet or cabinet set. + * + * All fields are READ ONLY. + */ +struct mscabd_file { + /** + * The next file in the cabinet or cabinet set, or NULL if this is the + * final file. + */ + struct mscabd_file *next; + + /** + * The filename of the file. + * + * A null terminated string of up to 255 bytes in length, it may be in + * either ISO-8859-1 or UTF8 format, depending on the file attributes. + * + * @see attribs + */ + char *filename; + + /** The uncompressed length of the file, in bytes. */ + unsigned int length; + + /** + * File attributes. + * + * The following attributes are defined: + * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected. + * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden. + * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file. + * - #MSCAB_ATTRIB_ARCH indicates the file is "archived". + * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program. + * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather + * than ISO-8859-1. + */ + int attribs; + + /** File's last modified time, hour field. */ + char time_h; + /** File's last modified time, minute field. */ + char time_m; + /** File's last modified time, second field. */ + char time_s; + + /** File's last modified date, day field. */ + char date_d; + /** File's last modified date, month field. */ + char date_m; + /** File's last modified date, year field. */ + int date_y; + + /** A pointer to the folder that contains this file. */ + struct mscabd_folder *folder; + + /** The uncompressed offset of this file in its folder. */ + unsigned int offset; +}; + +/** mscabd_file::attribs attribute: file is read-only. */ +#define MSCAB_ATTRIB_RDONLY (0x01) +/** mscabd_file::attribs attribute: file is hidden. */ +#define MSCAB_ATTRIB_HIDDEN (0x02) +/** mscabd_file::attribs attribute: file is an operating system file. */ +#define MSCAB_ATTRIB_SYSTEM (0x04) +/** mscabd_file::attribs attribute: file is "archived". */ +#define MSCAB_ATTRIB_ARCH (0x20) +/** mscabd_file::attribs attribute: file is an executable program. */ +#define MSCAB_ATTRIB_EXEC (0x40) +/** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */ +#define MSCAB_ATTRIB_UTF_NAME (0x80) + +/** mscab_decompressor::set_param() parameter: search buffer size. */ +#define MSCABD_PARAM_SEARCHBUF (0) +/** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */ +#define MSCABD_PARAM_FIXMSZIP (1) +/** mscab_decompressor::set_param() parameter: size of decompression buffer */ +#define MSCABD_PARAM_DECOMPBUF (2) +/** mscab_decompressor::set_param() parameter: salvage data from bad cabinets? + * If enabled, open() will skip file with bad folder indices or filenames + * rather than reject the whole cabinet, and extract() will limit rather than + * reject files with invalid offsets and lengths, and bad data block checksums + * will be ignored. Available only in CAB decoder version 2 and above. + */ +#define MSCABD_PARAM_SALVAGE (3) + +/** TODO */ +struct mscab_compressor { + int dummy; +}; + +/** + * A decompressor for .CAB (Microsoft Cabinet) files + * + * All fields are READ ONLY. + * + * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor() + */ +struct mscab_decompressor { + /** + * Opens a cabinet file and reads its contents. + * + * If the file opened is a valid cabinet file, all headers will be read + * and a mscabd_cabinet structure will be returned, with a full list of + * folders and files. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the cabinet. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param filename the filename of the cabinet file. This is passed + * directly to mspack_system::open(). + * @return a pointer to a mscabd_cabinet structure, or NULL on failure + * @see close(), search(), last_error() + */ + struct mscabd_cabinet * (*open) (struct mscab_decompressor *self, + const char *filename); + + /** + * Closes a previously opened cabinet or cabinet set. + * + * This closes a cabinet, all cabinets associated with it via the + * mscabd_cabinet::next, mscabd_cabinet::prevcab and + * mscabd_cabinet::nextcab pointers, and all folders and files. All + * memory used by these entities is freed. + * + * The cabinet pointer is now invalid and cannot be used again. All + * mscabd_folder and mscabd_file pointers from that cabinet or cabinet + * set are also now invalid, and cannot be used again. + * + * If the cabinet pointer given was created using search(), it MUST be + * the cabinet pointer returned by search() and not one of the later + * cabinet pointers further along the mscabd_cabinet::next chain. + + * If extra cabinets have been added using append() or prepend(), these + * will all be freed, even if the cabinet pointer given is not the first + * cabinet in the set. Do NOT close() more than one cabinet in the set. + * + * The mscabd_cabinet::filename is not freed by the library, as it is + * not allocated by the library. The caller should free this itself if + * necessary, before it is lost forever. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param cab the cabinet to close + * @see open(), search(), append(), prepend() + */ + void (*close)(struct mscab_decompressor *self, + struct mscabd_cabinet *cab); + + /** + * Searches a regular file for embedded cabinets. + * + * This opens a normal file with the given filename and will search the + * entire file for embedded cabinet files + * + * If any cabinets are found, the equivalent of open() is called on each + * potential cabinet file at the offset it was found. All successfully + * open()ed cabinets are kept in a list. + * + * The first cabinet found will be returned directly as the result of + * this method. Any further cabinets found will be chained in a list + * using the mscabd_cabinet::next field. + * + * In the case of an error occuring anywhere other than the simulated + * open(), NULL is returned and the error code is available from + * last_error(). + * + * If no error occurs, but no cabinets can be found in the file, NULL is + * returned and last_error() returns MSPACK_ERR_OK. + * + * The filename pointer should be considered in use until close() is + * called on the cabinet. + * + * close() should only be called on the result of search(), not on any + * subsequent cabinets in the mscabd_cabinet::next chain. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param filename the filename of the file to search for cabinets. This + * is passed directly to mspack_system::open(). + * @return a pointer to a mscabd_cabinet structure, or NULL + * @see close(), open(), last_error() + */ + struct mscabd_cabinet * (*search) (struct mscab_decompressor *self, + const char *filename); + + /** + * Appends one mscabd_cabinet to another, forming or extending a cabinet + * set. + * + * This will attempt to append one cabinet to another such that + * (cab->nextcab == nextcab) && (nextcab->prevcab == cab) and + * any folders split between the two cabinets are merged. + * + * The cabinets MUST be part of a cabinet set -- a cabinet set is a + * cabinet that spans more than one physical cabinet file on disk -- and + * must be appropriately matched. + * + * It can be determined if a cabinet has further parts to load by + * examining the mscabd_cabinet::flags field: + * + * - if (flags & MSCAB_HDR_PREVCAB) is non-zero, there is a + * predecessor cabinet to open() and prepend(). Its MS-DOS + * case-insensitive filename is mscabd_cabinet::prevname + * - if (flags & MSCAB_HDR_NEXTCAB) is non-zero, there is a + * successor cabinet to open() and append(). Its MS-DOS case-insensitive + * filename is mscabd_cabinet::nextname + * + * If the cabinets do not match, an error code will be returned. Neither + * cabinet has been altered, and both should be closed seperately. + * + * Files and folders in a cabinet set are a single entity. All cabinets + * in a set use the same file list, which is updated as cabinets in the + * set are added. All pointers to mscabd_folder and mscabd_file + * structures in either cabinet must be discarded and re-obtained after + * merging. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param cab the cabinet which will be appended to, + * predecessor of nextcab + * @param nextcab the cabinet which will be appended, + * successor of cab + * @return an error code, or MSPACK_ERR_OK if successful + * @see prepend(), open(), close() + */ + int (*append) (struct mscab_decompressor *self, + struct mscabd_cabinet *cab, + struct mscabd_cabinet *nextcab); + + /** + * Prepends one mscabd_cabinet to another, forming or extending a + * cabinet set. + * + * This will attempt to prepend one cabinet to another, such that + * (cab->prevcab == prevcab) && (prevcab->nextcab == cab). In + * all other respects, it is identical to append(). See append() for the + * full documentation. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param cab the cabinet which will be prepended to, + * successor of prevcab + * @param prevcab the cabinet which will be prepended, + * predecessor of cab + * @return an error code, or MSPACK_ERR_OK if successful + * @see append(), open(), close() + */ + int (*prepend) (struct mscab_decompressor *self, + struct mscabd_cabinet *cab, + struct mscabd_cabinet *prevcab); + + /** + * Extracts a file from a cabinet or cabinet set. + * + * This extracts a compressed file in a cabinet and writes it to the given + * filename. + * + * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED + * by extract(). The caller must examine this MS-DOS filename, copy and + * change it as necessary, create directories as necessary, and provide + * the correct filename as a parameter, which will be passed unchanged + * to the decompressor's mspack_system::open() + * + * If the file belongs to a split folder in a multi-part cabinet set, + * and not enough parts of the cabinet set have been loaded and appended + * or prepended, an error will be returned immediately. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param file the file to be decompressed + * @param filename the filename of the file being written to + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct mscab_decompressor *self, + struct mscabd_file *file, + const char *filename); + + /** + * Sets a CAB decompression engine parameter. + * + * The following parameters are defined: + * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a + * buffer when using search()? The minimum value is 4. The default + * value is 32768. + * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad + * checksums and recover from decompression errors in MS-ZIP + * compressed folders. The default value is 0 (don't recover). + * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input + * bit buffer by decompressors? The minimum value is 4. The default + * value is 4096. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see search(), extract() + */ + int (*set_param)(struct mscab_decompressor *self, + int param, + int value); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() and search(), which do not return an error + * code directly. + * + * @param self a self-referential pointer to the mscab_decompressor + * instance being called + * @return the most recent error code + * @see open(), search() + */ + int (*last_error)(struct mscab_decompressor *self); +}; + +/* --- support for .CHM (HTMLHelp) file format ----------------------------- */ + +/** + * A structure which represents a file to be placed in a CHM helpfile. + * + * A contiguous array of these structures should be passed to + * mschm_compressor::generate(). The array list is terminated with an + * entry whose mschmc_file::section field is set to #MSCHMC_ENDLIST, the + * other fields in this entry are ignored. + */ +struct mschmc_file { + /** One of #MSCHMC_ENDLIST, #MSCHMC_UNCOMP or #MSCHMC_MSCOMP. */ + int section; + + /** The filename of the source file that will be added to the CHM. This + * is passed directly to mspack_system::open(). */ + const char *filename; + + /** The full path and filename of the file within the CHM helpfile, a + * UTF-1 encoded null-terminated string. */ + char *chm_filename; + + /** The length of the file, in bytes. This will be adhered to strictly + * and a read error will be issued if this many bytes cannot be read + * from the real file at CHM generation time. */ + off_t length; +}; + +/** + * A structure which represents a section of a CHM helpfile. + * + * All fields are READ ONLY. + * + * Not used directly, but used as a generic base type for + * mschmd_sec_uncompressed and mschmd_sec_mscompressed. + */ +struct mschmd_section { + /** A pointer to the CHM helpfile that contains this section. */ + struct mschmd_header *chm; + + /** + * The section ID. Either 0 for the uncompressed section + * mschmd_sec_uncompressed, or 1 for the LZX compressed section + * mschmd_sec_mscompressed. No other section IDs are known. + */ + unsigned int id; +}; + +/** + * A structure which represents the uncompressed section of a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_sec_uncompressed { + /** Generic section data. */ + struct mschmd_section base; + + /** The file offset of where this section begins in the CHM helpfile. */ + off_t offset; +}; + +/** + * A structure which represents the LZX compressed section of a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_sec_mscompressed { + /** Generic section data. */ + struct mschmd_section base; + + /** A pointer to the meta-file which represents all LZX compressed data. */ + struct mschmd_file *content; + + /** A pointer to the file which contains the LZX control data. */ + struct mschmd_file *control; + + /** A pointer to the file which contains the LZX reset table. */ + struct mschmd_file *rtable; + + /** A pointer to the file which contains the LZX span information. + * Available only in CHM decoder version 2 and above. + */ + struct mschmd_file *spaninfo; +}; + +/** + * A structure which represents a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_header { + /** The version of the CHM file format used in this file. */ + unsigned int version; + + /** + * The "timestamp" of the CHM helpfile. + * + * It is the lower 32 bits of a 64-bit value representing the number of + * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful + * as a timestamp, but it is useful as a semi-unique ID. + */ + unsigned int timestamp; + + /** + * The default Language and Country ID (LCID) of the user who ran the + * HTMLHelp Compiler. This is not the language of the CHM file itself. + */ + unsigned int language; + + /** + * The filename of the CHM helpfile. This is given by the library user + * and may be in any format. + */ + const char *filename; + + /** The length of the CHM helpfile, in bytes. */ + off_t length; + + /** A list of all non-system files in the CHM helpfile. */ + struct mschmd_file *files; + + /** + * A list of all system files in the CHM helpfile. + * + * System files are files which begin with "::". They are meta-files + * generated by the CHM creation process. + */ + struct mschmd_file *sysfiles; + + /** The section 0 (uncompressed) data in this CHM helpfile. */ + struct mschmd_sec_uncompressed sec0; + + /** The section 1 (MSCompressed) data in this CHM helpfile. */ + struct mschmd_sec_mscompressed sec1; + + /** The file offset of the first PMGL/PMGI directory chunk. */ + off_t dir_offset; + + /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */ + unsigned int num_chunks; + + /** The size of each PMGL/PMGI chunk, in bytes. */ + unsigned int chunk_size; + + /** The "density" of the quick-reference section in PMGL/PMGI chunks. */ + unsigned int density; + + /** The depth of the index tree. + * + * - if 1, there are no PMGI chunks, only PMGL chunks. + * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks. + * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in + * turn point to PMGL chunks. + * - and so on... + */ + unsigned int depth; + + /** + * The number of the root PMGI chunk. + * + * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF. + */ + unsigned int index_root; + + /** + * The number of the first PMGL chunk. Usually zero. + * Available only in CHM decoder version 2 and above. + */ + unsigned int first_pmgl; + + /** + * The number of the last PMGL chunk. Usually num_chunks-1. + * Available only in CHM decoder version 2 and above. + */ + unsigned int last_pmgl; + + /** + * A cache of loaded chunks, filled in by mschm_decoder::fast_find(). + * Available only in CHM decoder version 2 and above. + */ + unsigned char **chunk_cache; +}; + +/** + * A structure which represents a file stored in a CHM helpfile. + * + * All fields are READ ONLY. + */ +struct mschmd_file { + /** + * A pointer to the next file in the list, or NULL if this is the final + * file. + */ + struct mschmd_file *next; + + /** + * A pointer to the section that this file is located in. Indirectly, + * it also points to the CHM helpfile the file is located in. + */ + struct mschmd_section *section; + + /** The offset within the section data that this file is located at. */ + off_t offset; + + /** The length of this file, in bytes */ + off_t length; + + /** The filename of this file -- a null terminated string in UTF-8. */ + char *filename; +}; + +/** mschmc_file::section value: end of CHM file list */ +#define MSCHMC_ENDLIST (0) +/** mschmc_file::section value: this file is in the Uncompressed section */ +#define MSCHMC_UNCOMP (1) +/** mschmc_file::section value: this file is in the MSCompressed section */ +#define MSCHMC_MSCOMP (2) + +/** mschm_compressor::set_param() parameter: "timestamp" header */ +#define MSCHMC_PARAM_TIMESTAMP (0) +/** mschm_compressor::set_param() parameter: "language" header */ +#define MSCHMC_PARAM_LANGUAGE (1) +/** mschm_compressor::set_param() parameter: LZX window size */ +#define MSCHMC_PARAM_LZXWINDOW (2) +/** mschm_compressor::set_param() parameter: intra-chunk quickref density */ +#define MSCHMC_PARAM_DENSITY (3) +/** mschm_compressor::set_param() parameter: whether to create indices */ +#define MSCHMC_PARAM_INDEX (4) + +/** + * A compressor for .CHM (Microsoft HTMLHelp) files. + * + * All fields are READ ONLY. + * + * @see mspack_create_chm_compressor(), mspack_destroy_chm_compressor() + */ +struct mschm_compressor { + /** + * Generates a CHM help file. + * + * The help file will contain up to two sections, an Uncompressed + * section and potentially an MSCompressed (LZX compressed) + * section. + * + * While the contents listing of a CHM file is always in lexical order, + * the file list passed in will be taken as the correct order for files + * within the sections. It is in your interest to place similar files + * together for better compression. + * + * There are two modes of generation, to use a temporary file or not to + * use one. See use_temporary_file() for the behaviour of generate() in + * these two different modes. + * + * @param self a self-referential pointer to the mschm_compressor + * instance being called + * @param file_list an array of mschmc_file structures, terminated + * with an entry whose mschmc_file::section field is + * #MSCHMC_ENDLIST. The order of the list is + * preserved within each section. The length of any + * mschmc_file::chm_filename string cannot exceed + * roughly 4096 bytes. Each source file must be able + * to supply as many bytes as given in the + * mschmc_file::length field. + * @param output_file the file to write the generated CHM helpfile to. + * This is passed directly to mspack_system::open() + * @return an error code, or MSPACK_ERR_OK if successful + * @see use_temporary_file() set_param() + */ + int (*generate)(struct mschm_compressor *self, + struct mschmc_file file_list[], + const char *output_file); + + /** + * Specifies whether a temporary file is used during CHM generation. + * + * The CHM file format includes data about the compressed section (such + * as its overall size) that is stored in the output CHM file prior to + * the compressed section itself. This unavoidably requires that the + * compressed section has to be generated, before these details can be + * set. There are several ways this can be handled. Firstly, the + * compressed section could be generated entirely in memory before + * writing any of the output CHM file. This approach is not used in + * libmspack, as the compressed section can exceed the addressable + * memory space on most architectures. + * + * libmspack has two options, either to write these unknowable sections + * with blank data, generate the compressed section, then re-open the + * output file for update once the compressed section has been + * completed, or to write the compressed section to a temporary file, + * then write the entire output file at once, performing a simple + * file-to-file copy for the compressed section. + * + * The simple solution of buffering the entire compressed section in + * memory can still be used, if desired. As the temporary file's + * filename is passed directly to mspack_system::open(), it is possible + * for a custom mspack_system implementation to hold this file in memory, + * without writing to a disk. + * + * If a temporary file is set, generate() performs the following + * sequence of events: the temporary file is opened for writing, the + * compression algorithm writes to the temporary file, the temporary + * file is closed. Then the output file is opened for writing and the + * temporary file is re-opened for reading. The output file is written + * and the temporary file is read from. Both files are then closed. The + * temporary file itself is not deleted. If that is desired, the + * temporary file should be deleted after the completion of generate(), + * if it exists. + * + * If a temporary file is set not to be used, generate() performs the + * following sequence of events: the output file is opened for writing, + * then it is written and closed. The output file is then re-opened for + * update, the appropriate sections are seek()ed to and re-written, then + * the output file is closed. + * + * @param self a self-referential pointer to the + * mschm_compressor instance being called + * @param use_temp_file non-zero if the temporary file should be used, + * zero if the temporary file should not be used. + * @param temp_file a file to temporarily write compressed data to, + * before opening it for reading and copying the + * contents to the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + * @see generate() + */ + int (*use_temporary_file)(struct mschm_compressor *self, + int use_temp_file, + const char *temp_file); + /** + * Sets a CHM compression engine parameter. + * + * The following parameters are defined: + + * - #MSCHMC_PARAM_TIMESTAMP: Sets the "timestamp" of the CHM file + * generated. This is not a timestamp, see mschmd_header::timestamp + * for a description. If this timestamp is 0, generate() will use its + * own algorithm for making a unique ID, based on the lengths and + * names of files in the CHM itself. Defaults to 0, any value between + * 0 and (2^32)-1 is valid. + * - #MSCHMC_PARAM_LANGUAGE: Sets the "language" of the CHM file + * generated. This is not the language used in the CHM file, but the + * language setting of the user who ran the HTMLHelp compiler. It + * defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F. + * - #MSCHMC_PARAM_LZXWINDOW: Sets the size of the LZX history window, + * which is also the interval at which the compressed data stream can be + * randomly accessed. The value is not a size in bytes, but a power of + * two. The default value is 16 (which makes the window 2^16 bytes, or + * 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2 + * megabytes). + * - #MSCHMC_PARAM_DENSITY: Sets the "density" of quick reference + * entries stored at the end of directory listing chunk. Each chunk is + * 4096 bytes in size, and contains as many file entries as there is + * room for. At the other end of the chunk, a list of "quick reference" + * pointers is included. The offset of every 'N'th file entry is given a + * quick reference, where N = (2^density) + 1. The default density is + * 2. The smallest density is 0 (N=2), the maximum is 10 (N=1025). As + * each file entry requires at least 5 bytes, the maximum number of + * entries in a single chunk is roughly 800, so the maximum value 10 + * can be used to indicate there are no quickrefs at all. + * - #MSCHMC_PARAM_INDEX: Sets whether or not to include quick lookup + * index chunk(s), in addition to normal directory listing chunks. A + * value of zero means no index chunks will be created, a non-zero value + * means index chunks will be created. The default is zero, "don't + * create an index". + * + * @param self a self-referential pointer to the mschm_compressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see generate() + */ + int (*set_param)(struct mschm_compressor *self, + int param, + unsigned int value); + + /** + * Returns the error code set by the most recently called method. + * + * @param self a self-referential pointer to the mschm_compressor + * instance being called + * @return the most recent error code + * @see set_param(), generate() + */ + int (*last_error)(struct mschm_compressor *self); +}; + +/** + * A decompressor for .CHM (Microsoft HTMLHelp) files + * + * All fields are READ ONLY. + * + * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor() + */ +struct mschm_decompressor { + /** + * Opens a CHM helpfile and reads its contents. + * + * If the file opened is a valid CHM helpfile, all headers will be read + * and a mschmd_header structure will be returned, with a full list of + * files. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the CHM helpfile. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param filename the filename of the CHM helpfile. This is passed + * directly to mspack_system::open(). + * @return a pointer to a mschmd_header structure, or NULL on failure + * @see close() + */ + struct mschmd_header *(*open)(struct mschm_decompressor *self, + const char *filename); + + /** + * Closes a previously opened CHM helpfile. + * + * This closes a CHM helpfile, frees the mschmd_header and all + * mschmd_file structures associated with it (if any). This works on + * both helpfiles opened with open() and helpfiles opened with + * fast_open(). + * + * The CHM header pointer is now invalid and cannot be used again. All + * mschmd_file pointers referencing that CHM are also now invalid, and + * cannot be used again. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param chm the CHM helpfile to close + * @see open(), fast_open() + */ + void (*close)(struct mschm_decompressor *self, + struct mschmd_header *chm); + + /** + * Extracts a file from a CHM helpfile. + * + * This extracts a file from a CHM helpfile and writes it to the given + * filename. The filename of the file, mscabd_file::filename, is not + * used by extract(), but can be used by the caller as a guide for + * constructing an appropriate filename. + * + * This method works both with files found in the mschmd_header::files + * and mschmd_header::sysfiles list and mschmd_file structures generated + * on the fly by fast_find(). + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param file the file to be decompressed + * @param filename the filename of the file being written to + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct mschm_decompressor *self, + struct mschmd_file *file, + const char *filename); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() and fast_open(), which do not return an + * error code directly. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @return the most recent error code + * @see open(), extract() + */ + int (*last_error)(struct mschm_decompressor *self); + + /** + * Opens a CHM helpfile quickly. + * + * If the file opened is a valid CHM helpfile, only essential headers + * will be read. A mschmd_header structure will be still be returned, as + * with open(), but the mschmd_header::files field will be NULL. No + * files details will be automatically read. The fast_find() method + * must be used to obtain file details. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the CHM helpfile. + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param filename the filename of the CHM helpfile. This is passed + * directly to mspack_system::open(). + * @return a pointer to a mschmd_header structure, or NULL on failure + * @see open(), close(), fast_find(), extract() + */ + struct mschmd_header *(*fast_open)(struct mschm_decompressor *self, + const char *filename); + + /** + * Finds file details quickly. + * + * Instead of reading all CHM helpfile headers and building a list of + * files, fast_open() and fast_find() are intended for finding file + * details only when they are needed. The CHM file format includes an + * on-disk file index to allow this. + * + * Given a case-sensitive filename, fast_find() will search the on-disk + * index for that file. + * + * If the file was found, the caller-provided mschmd_file structure will + * be filled out like so: + * - section: the correct value for the found file + * - offset: the correct value for the found file + * - length: the correct value for the found file + * - all other structure elements: NULL or 0 + * + * If the file was not found, MSPACK_ERR_OK will still be returned as the + * result, but the caller-provided structure will be filled out like so: + * - section: NULL + * - offset: 0 + * - length: 0 + * - all other structure elements: NULL or 0 + * + * This method is intended to be used in conjunction with CHM helpfiles + * opened with fast_open(), but it also works with helpfiles opened + * using the regular open(). + * + * @param self a self-referential pointer to the mschm_decompressor + * instance being called + * @param chm the CHM helpfile to search for the file + * @param filename the filename of the file to search for + * @param f_ptr a pointer to a caller-provded mschmd_file structure + * @param f_size sizeof(struct mschmd_file) + * @return an error code, or MSPACK_ERR_OK if successful + * @see open(), close(), fast_find(), extract() + */ + int (*fast_find)(struct mschm_decompressor *self, + struct mschmd_header *chm, + const char *filename, + struct mschmd_file *f_ptr, + int f_size); +}; + +/* --- support for .LIT (EBook) file format -------------------------------- */ + +/** TODO */ +struct mslit_compressor { + int dummy; +}; + +/** TODO */ +struct mslit_decompressor { + int dummy; +}; + + +/* --- support for .HLP (MS Help) file format ------------------------------ */ + +/** TODO */ +struct mshlp_compressor { + int dummy; +}; + +/** TODO */ +struct mshlp_decompressor { + int dummy; +}; + + +/* --- support for SZDD file format ---------------------------------------- */ + +/** msszdd_compressor::set_param() parameter: the missing character */ +#define MSSZDDC_PARAM_MISSINGCHAR (0) + +/** msszddd_header::format value - a regular SZDD file */ +#define MSSZDD_FMT_NORMAL (0) + +/** msszddd_header::format value - a special QBasic SZDD file */ +#define MSSZDD_FMT_QBASIC (1) + +/** + * A structure which represents an SZDD compressed file. + * + * All fields are READ ONLY. + */ +struct msszddd_header { + /** The file format; either #MSSZDD_FMT_NORMAL or #MSSZDD_FMT_QBASIC */ + int format; + + /** The amount of data in the SZDD file once uncompressed. */ + off_t length; + + /** + * The last character in the filename, traditionally replaced with an + * underscore to show the file is compressed. The null character is used + * to show that this character has not been stored (e.g. because the + * filename is not known). Generally, only characters that may appear in + * an MS-DOS filename (except ".") are valid. + */ + char missing_char; +}; + +/** + * A compressor for the SZDD file format. + * + * All fields are READ ONLY. + * + * @see mspack_create_szdd_compressor(), mspack_destroy_szdd_compressor() + */ +struct msszdd_compressor { + /** + * Reads an input file and creates a compressed output file in the + * SZDD compressed file format. The SZDD compression format is quick + * but gives poor compression. It is possible for the compressed output + * file to be larger than the input file. + * + * Conventionally, SZDD compressed files have the final character in + * their filename replaced with an underscore, to show they are + * compressed. The missing character is stored in the compressed file + * itself. This is due to the restricted filename conventions of MS-DOS, + * most operating systems, such as UNIX, simply append another file + * extension to the existing filename. As mspack does not deal with + * filenames, this is left up to you. If you wish to set the missing + * character stored in the file header, use set_param() with the + * #MSSZDDC_PARAM_MISSINGCHAR parameter. + * + * "Stream" compression (where the length of the input data is not + * known) is not possible. The length of the input data is stored in the + * header of the SZDD file and must therefore be known before any data + * is compressed. Due to technical limitations of the file format, the + * maximum size of uncompressed file that will be accepted is 2147483647 + * bytes. + * + * @param self a self-referential pointer to the msszdd_compressor + * instance being called + * @param input the name of the file to compressed. This is passed + * passed directly to mspack_system::open() + * @param output the name of the file to write compressed data to. + * This is passed directly to mspack_system::open(). + * @param length the length of the uncompressed file, or -1 to indicate + * that this should be determined automatically by using + * mspack_system::seek() on the input file. + * @return an error code, or MSPACK_ERR_OK if successful + * @see set_param() + */ + int (*compress)(struct msszdd_compressor *self, + const char *input, + const char *output, + off_t length); + + /** + * Sets an SZDD compression engine parameter. + * + * The following parameters are defined: + + * - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character + * in the uncompressed file's filename, which is traditionally replaced + * with an underscore to show the file is compressed. Traditionally, + * this can only be a character that is a valid part of an MS-DOS, + * filename, but libmspack permits any character between 0x00 and 0xFF + * to be stored. 0x00 is the default, and it represents "no character + * stored". + * + * @param self a self-referential pointer to the msszdd_compressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see compress() + */ + int (*set_param)(struct msszdd_compressor *self, + int param, + unsigned int value); + + /** + * Returns the error code set by the most recently called method. + * + * @param self a self-referential pointer to the msszdd_compressor + * instance being called + * @return the most recent error code + * @see compress() + */ + int (*last_error)(struct mschm_decompressor *self); +}; + +/** + * A decompressor for SZDD compressed files. + * + * All fields are READ ONLY. + * + * @see mspack_create_szdd_decompressor(), mspack_destroy_szdd_decompressor() + */ +struct msszdd_decompressor { + /** + * Opens a SZDD file and reads the header. + * + * If the file opened is a valid SZDD file, all headers will be read and + * a msszddd_header structure will be returned. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the SZDD file. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param filename the filename of the SZDD compressed file. This is + * passed directly to mspack_system::open(). + * @return a pointer to a msszddd_header structure, or NULL on failure + * @see close() + */ + struct msszddd_header *(*open)(struct msszdd_decompressor *self, + const char *filename); + + /** + * Closes a previously opened SZDD file. + * + * This closes a SZDD file and frees the msszddd_header associated with + * it. + * + * The SZDD header pointer is now invalid and cannot be used again. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param szdd the SZDD file to close + * @see open() + */ + void (*close)(struct msszdd_decompressor *self, + struct msszddd_header *szdd); + + /** + * Extracts the compressed data from a SZDD file. + * + * This decompresses the compressed SZDD data stream and writes it to + * an output file. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param szdd the SZDD file to extract data from + * @param filename the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct msszdd_decompressor *self, + struct msszddd_header *szdd, + const char *filename); + + /** + * Decompresses an SZDD file to an output file in one step. + * + * This opens an SZDD file as input, reads the header, then decompresses + * the compressed data immediately to an output file, finally closing + * both the input and output file. It is more convenient to use than + * open() then extract() then close(), if you do not need to know the + * SZDD output size or missing character. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @param input the filename of the input SZDD file. This is passed + * directly to mspack_system::open(). + * @param output the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress)(struct msszdd_decompressor *self, + const char *input, + const char *output); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() which does not return an + * error code directly. + * + * @param self a self-referential pointer to the msszdd_decompressor + * instance being called + * @return the most recent error code + * @see open(), extract(), decompress() + */ + int (*last_error)(struct msszdd_decompressor *self); +}; + +/* --- support for KWAJ file format ---------------------------------------- */ + +/** mskwaj_compressor::set_param() parameter: compression type */ +#define MSKWAJC_PARAM_COMP_TYPE (0) + +/** mskwaj_compressor::set_param() parameter: include the length of the + * uncompressed file in the header? + */ +#define MSKWAJC_PARAM_INCLUDE_LENGTH (1) + +/** KWAJ compression type: no compression. */ +#define MSKWAJ_COMP_NONE (0) +/** KWAJ compression type: no compression, 0xFF XOR "encryption". */ +#define MSKWAJ_COMP_XOR (1) +/** KWAJ compression type: LZSS (same method as SZDD) */ +#define MSKWAJ_COMP_SZDD (2) +/** KWAJ compression type: LZ+Huffman compression */ +#define MSKWAJ_COMP_LZH (3) +/** KWAJ compression type: MSZIP */ +#define MSKWAJ_COMP_MSZIP (4) + +/** KWAJ optional header flag: decompressed file length is included */ +#define MSKWAJ_HDR_HASLENGTH (0x01) + +/** KWAJ optional header flag: unknown 2-byte structure is included */ +#define MSKWAJ_HDR_HASUNKNOWN1 (0x02) + +/** KWAJ optional header flag: unknown multi-sized structure is included */ +#define MSKWAJ_HDR_HASUNKNOWN2 (0x04) + +/** KWAJ optional header flag: file name (no extension) is included */ +#define MSKWAJ_HDR_HASFILENAME (0x08) + +/** KWAJ optional header flag: file extension is included */ +#define MSKWAJ_HDR_HASFILEEXT (0x10) + +/** KWAJ optional header flag: extra text is included */ +#define MSKWAJ_HDR_HASEXTRATEXT (0x20) + +/** + * A structure which represents an KWAJ compressed file. + * + * All fields are READ ONLY. + */ +struct mskwajd_header { + /** The compression type; should be one of #MSKWAJ_COMP_NONE, + * #MSKWAJ_COMP_XOR, #MSKWAJ_COMP_SZDD or #MSKWAJ_COMP_LZH + */ + unsigned short comp_type; + + /** The offset in the file where the compressed data stream begins */ + off_t data_offset; + + /** Flags indicating which optional headers were included. */ + int headers; + + /** The amount of uncompressed data in the file, or 0 if not present. */ + off_t length; + + /** output filename, or NULL if not present */ + char *filename; + + /** extra uncompressed data (usually text) in the header. + * This data can contain nulls so use extra_length to get the size. + */ + char *extra; + + /** length of extra uncompressed data in the header */ + unsigned short extra_length; +}; + +/** + * A compressor for the KWAJ file format. + * + * All fields are READ ONLY. + * + * @see mspack_create_kwaj_compressor(), mspack_destroy_kwaj_compressor() + */ +struct mskwaj_compressor { + /** + * Reads an input file and creates a compressed output file in the + * KWAJ compressed file format. The KWAJ compression format is quick + * but gives poor compression. It is possible for the compressed output + * file to be larger than the input file. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param input the name of the file to compressed. This is passed + * passed directly to mspack_system::open() + * @param output the name of the file to write compressed data to. + * This is passed directly to mspack_system::open(). + * @param length the length of the uncompressed file, or -1 to indicate + * that this should be determined automatically by using + * mspack_system::seek() on the input file. + * @return an error code, or MSPACK_ERR_OK if successful + * @see set_param() + */ + int (*compress)(struct mskwaj_compressor *self, + const char *input, + const char *output, + off_t length); + + /** + * Sets an KWAJ compression engine parameter. + * + * The following parameters are defined: + * + * - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must + * be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD + * or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH. + * + * - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed + * output file should include the uncompressed length of the input + * file in the header? This adds 4 bytes to the size of the output + * file. A value of zero says "no", non-zero says "yes". The default + * is "no". + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param param the parameter to set + * @param value the value to set the parameter to + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there + * is a problem with either parameter or value. + * @see generate() + */ + int (*set_param)(struct mskwaj_compressor *self, + int param, + unsigned int value); + + + /** + * Sets the original filename of the file before compression, + * which will be stored in the header of the output file. + * + * The filename should be a null-terminated string, it must be an + * MS-DOS "8.3" type filename (up to 8 bytes for the filename, then + * optionally a "." and up to 3 bytes for a filename extension). + * + * If NULL is passed as the filename, no filename is included in the + * header. This is the default. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param filename the original filename to use + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the + * filename is too long + */ + int (*set_filename)(struct mskwaj_compressor *self, + const char *filename); + + /** + * Sets arbitrary data that will be stored in the header of the + * output file, uncompressed. It can be up to roughly 64 kilobytes, + * as the overall size of the header must not exceed 65535 bytes. + * The data can contain null bytes if desired. + * + * If NULL is passed as the data pointer, or zero is passed as the + * length, no extra data is included in the header. This is the + * default. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @param data a pointer to the data to be stored in the header + * @param bytes the length of the data in bytes + * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data + * is too long + */ + int (*set_extra_data)(struct mskwaj_compressor *self, + void *data, + size_t bytes); + + /** + * Returns the error code set by the most recently called method. + * + * @param self a self-referential pointer to the mskwaj_compressor + * instance being called + * @return the most recent error code + * @see compress() + */ + int (*last_error)(struct mschm_decompressor *self); +}; + +/** + * A decompressor for KWAJ compressed files. + * + * All fields are READ ONLY. + * + * @see mspack_create_kwaj_decompressor(), mspack_destroy_kwaj_decompressor() + */ +struct mskwaj_decompressor { + /** + * Opens a KWAJ file and reads the header. + * + * If the file opened is a valid KWAJ file, all headers will be read and + * a mskwajd_header structure will be returned. + * + * In the case of an error occuring, NULL is returned and the error code + * is available from last_error(). + * + * The filename pointer should be considered "in use" until close() is + * called on the KWAJ file. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param filename the filename of the KWAJ compressed file. This is + * passed directly to mspack_system::open(). + * @return a pointer to a mskwajd_header structure, or NULL on failure + * @see close() + */ + struct mskwajd_header *(*open)(struct mskwaj_decompressor *self, + const char *filename); + + /** + * Closes a previously opened KWAJ file. + * + * This closes a KWAJ file and frees the mskwajd_header associated + * with it. The KWAJ header pointer is now invalid and cannot be + * used again. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param kwaj the KWAJ file to close + * @see open() + */ + void (*close)(struct mskwaj_decompressor *self, + struct mskwajd_header *kwaj); + + /** + * Extracts the compressed data from a KWAJ file. + * + * This decompresses the compressed KWAJ data stream and writes it to + * an output file. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param kwaj the KWAJ file to extract data from + * @param filename the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*extract)(struct mskwaj_decompressor *self, + struct mskwajd_header *kwaj, + const char *filename); + + /** + * Decompresses an KWAJ file to an output file in one step. + * + * This opens an KWAJ file as input, reads the header, then decompresses + * the compressed data immediately to an output file, finally closing + * both the input and output file. It is more convenient to use than + * open() then extract() then close(), if you do not need to know the + * KWAJ output size or output filename. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @param input the filename of the input KWAJ file. This is passed + * directly to mspack_system::open(). + * @param output the filename to write the decompressed data to. This + * is passed directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress)(struct mskwaj_decompressor *self, + const char *input, + const char *output); + + /** + * Returns the error code set by the most recently called method. + * + * This is useful for open() which does not return an + * error code directly. + * + * @param self a self-referential pointer to the mskwaj_decompressor + * instance being called + * @return the most recent error code + * @see open(), search() + */ + int (*last_error)(struct mskwaj_decompressor *self); +}; + +/* --- support for .LZX (Offline Address Book) file format ----------------- */ + +/** + * A compressor for the Offline Address Book (OAB) format. + * + * All fields are READ ONLY. + * + * @see mspack_create_oab_compressor(), mspack_destroy_oab_compressor() + */ +struct msoab_compressor { + /** + * Compress a full OAB file. + * + * The input file will be read and the compressed contents written to the + * output file. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param input the filename of the input file. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*compress) (struct msoab_compressor *self, + const char *input, + const char *output); + + /** + * Generate a compressed incremental OAB patch file. + * + * The two uncompressed files "input" and "base" will be read, and an + * incremental patch to generate "input" from "base" will be written to + * the output file. + * + * @param self a self-referential pointer to the msoab_compressor + * instance being called + * @param input the filename of the input file containing the new + * version of its contents. This is passed directly + * to mspack_system::open(). + * @param base the filename of the original base file containing + * the old version of its contents, against which the + * incremental patch shall generated. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*compress_incremental) (struct msoab_compressor *self, + const char *input, + const char *base, + const char *output); +}; + +/** + * A decompressor for .LZX (Offline Address Book) files + * + * All fields are READ ONLY. + * + * @see mspack_create_oab_decompressor(), mspack_destroy_oab_decompressor() + */ +struct msoab_decompressor { + /** + * Decompresses a full Offline Address Book file. + * + * If the input file is a valid compressed Offline Address Book file, + * it will be read and the decompressed contents will be written to + * the output file. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param input the filename of the input file. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress) (struct msoab_decompressor *self, + const char *input, + const char *output); + + /** + * Decompresses an Offline Address Book with an incremental patch file. + * + * This requires both a full UNCOMPRESSED Offline Address Book file to + * act as the "base", and a compressed incremental patch file as input. + * If the input file is valid, it will be decompressed with reference to + * the base file, and the decompressed contents will be written to the + * output file. + * + * There is no way to tell what the right base file is for the given + * incremental patch, but if you get it wrong, this will usually result + * in incorrect data being decompressed, which will then fail a checksum + * test. + * + * @param self a self-referential pointer to the msoab_decompressor + * instance being called + * @param input the filename of the input file. This is passed + * directly to mspack_system::open(). + * @param base the filename of the base file to which the + * incremental patch shall be applied. This is passed + * directly to mspack_system::open(). + * @param output the filename of the output file. This is passed + * directly to mspack_system::open(). + * @return an error code, or MSPACK_ERR_OK if successful + */ + int (*decompress_incremental) (struct msoab_decompressor *self, + const char *input, + const char *base, + const char *output); +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rdparty/mspack/readbits.h b/3rdparty/mspack/readbits.h new file mode 100644 index 0000000..9b237a3 --- /dev/null +++ b/3rdparty/mspack/readbits.h @@ -0,0 +1,207 @@ +/* This file is part of libmspack. + * (C) 2003-2010 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_READBITS_H +#define MSPACK_READBITS_H 1 + +/* this header defines macros that read data streams by + * the individual bits + * + * INIT_BITS initialises bitstream state in state structure + * STORE_BITS stores bitstream state in state structure + * RESTORE_BITS restores bitstream state from state structure + * ENSURE_BITS(n) ensure there are at least N bits in the bit buffer + * READ_BITS(var,n) takes N bits from the buffer and puts them in var + * PEEK_BITS(n) extracts without removing N bits from the bit buffer + * REMOVE_BITS(n) removes N bits from the bit buffer + * + * READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS, + * which means it's limited to reading the number of bits you can + * ensure at any one time. It also fails if asked to read zero bits. + * If you need to read zero bits, or more bits than can be ensured in + * one go, use READ_MANY_BITS instead. + * + * These macros have variable names baked into them, so to use them + * you have to define some macros: + * - BITS_TYPE: the type name of your state structure + * - BITS_VAR: the variable that points to your state structure + * - define BITS_ORDER_MSB if bits are read from the MSB, or + * define BITS_ORDER_LSB if bits are read from the LSB + * - READ_BYTES: some code that reads more data into the bit buffer, + * it should use READ_IF_NEEDED (calls read_input if the byte buffer + * is empty), then INJECT_BITS(data,n) to put data from the byte + * buffer into the bit buffer. + * + * You also need to define some variables and structure members: + * - unsigned char *i_ptr; // current position in the byte buffer + * - unsigned char *i_end; // end of the byte buffer + * - unsigned int bit_buffer; // the bit buffer itself + * - unsigned int bits_left; // number of bits remaining + * + * If you use read_input() and READ_IF_NEEDED, they also expect these + * structure members: + * - struct mspack_system *sys; // to access sys->read() + * - unsigned int error; // to record/return read errors + * - unsigned char input_end; // to mark reaching the EOF + * - unsigned char *inbuf; // the input byte buffer + * - unsigned int inbuf_size; // the size of the input byte buffer + * + * Your READ_BYTES implementation should read data from *i_ptr and + * put them in the bit buffer. READ_IF_NEEDED will call read_input() + * if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to + * the start of inbuf and i_end to the end of inbuf. + * + * If you're reading in MSB order, the routines work by using the area + * beyond the MSB and the LSB of the bit buffer as a free source of + * zeroes when shifting. This avoids having to mask any bits. So we + * have to know the bit width of the bit buffer variable. We use + * and CHAR_BIT to find the size of the bit buffer in bits. + * + * If you are reading in LSB order, bits need to be masked. Normally + * this is done by computing the mask: N bits are masked by the value + * (1< +#endif +#ifndef CHAR_BIT +# define CHAR_BIT (8) +#endif +#define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT) + +#define INIT_BITS do { \ + BITS_VAR->i_ptr = &BITS_VAR->inbuf[0]; \ + BITS_VAR->i_end = &BITS_VAR->inbuf[0]; \ + BITS_VAR->bit_buffer = 0; \ + BITS_VAR->bits_left = 0; \ + BITS_VAR->input_end = 0; \ +} while (0) + +#define STORE_BITS do { \ + BITS_VAR->i_ptr = i_ptr; \ + BITS_VAR->i_end = i_end; \ + BITS_VAR->bit_buffer = bit_buffer; \ + BITS_VAR->bits_left = bits_left; \ +} while (0) + +#define RESTORE_BITS do { \ + i_ptr = BITS_VAR->i_ptr; \ + i_end = BITS_VAR->i_end; \ + bit_buffer = BITS_VAR->bit_buffer; \ + bits_left = BITS_VAR->bits_left; \ +} while (0) + +#define ENSURE_BITS(nbits) do { \ + while (bits_left < (nbits)) READ_BYTES; \ +} while (0) + +#define READ_BITS(val, nbits) do { \ + ENSURE_BITS(nbits); \ + (val) = PEEK_BITS(nbits); \ + REMOVE_BITS(nbits); \ +} while (0) + +#define READ_MANY_BITS(val, bits) do { \ + unsigned char needed = (bits), bitrun; \ + (val) = 0; \ + while (needed > 0) { \ + if (bits_left <= (BITBUF_WIDTH - 16)) READ_BYTES; \ + bitrun = (bits_left < needed) ? bits_left : needed; \ + (val) = ((val) << bitrun) | PEEK_BITS(bitrun); \ + REMOVE_BITS(bitrun); \ + needed -= bitrun; \ + } \ +} while (0) + +#ifdef BITS_ORDER_MSB +# define PEEK_BITS(nbits) (bit_buffer >> (BITBUF_WIDTH - (nbits))) +# define REMOVE_BITS(nbits) ((bit_buffer <<= (nbits)), (bits_left -= (nbits))) +# define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \ + (bitdata) << (BITBUF_WIDTH - (nbits) - bits_left)), (bits_left += (nbits))) +#else /* BITS_ORDER_LSB */ +# define PEEK_BITS(nbits) (bit_buffer & ((1 << (nbits))-1)) +# define REMOVE_BITS(nbits) ((bit_buffer >>= (nbits)), (bits_left -= (nbits))) +# define INJECT_BITS(bitdata,nbits) ((bit_buffer |= \ + (bitdata) << bits_left), (bits_left += (nbits))) +#endif + +#ifdef BITS_LSB_TABLE +/* lsb_bit_mask[n] = (1 << n) - 1 */ +static const unsigned short lsb_bit_mask[17] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; +# define PEEK_BITS_T(nbits) (bit_buffer & lsb_bit_mask[(nbits)]) +# define READ_BITS_T(val, nbits) do { \ + ENSURE_BITS(nbits); \ + (val) = PEEK_BITS_T(nbits); \ + REMOVE_BITS(nbits); \ +} while (0) +#endif + +#ifndef BITS_NO_READ_INPUT +# define READ_IF_NEEDED do { \ + if (i_ptr >= i_end) { \ + if (read_input(BITS_VAR)) \ + return BITS_VAR->error; \ + i_ptr = BITS_VAR->i_ptr; \ + i_end = BITS_VAR->i_end; \ + } \ +} while (0) + +static int read_input(BITS_TYPE *p) { + int read = p->sys->read(p->input, &p->inbuf[0], (int)p->inbuf_size); + if (read < 0) return p->error = MSPACK_ERR_READ; + + /* we might overrun the input stream by asking for bits we don't use, + * so fake 2 more bytes at the end of input */ + if (read == 0) { + if (p->input_end) { + D(("out of input bytes")) + return p->error = MSPACK_ERR_READ; + } + else { + read = 2; + p->inbuf[0] = p->inbuf[1] = 0; + p->input_end = 1; + } + } + + /* update i_ptr and i_end */ + p->i_ptr = &p->inbuf[0]; + p->i_end = &p->inbuf[read]; + return MSPACK_ERR_OK; +} +#endif +#endif diff --git a/3rdparty/mspack/readhuff.h b/3rdparty/mspack/readhuff.h new file mode 100644 index 0000000..4d94225 --- /dev/null +++ b/3rdparty/mspack/readhuff.h @@ -0,0 +1,172 @@ +/* This file is part of libmspack. + * (C) 2003-2014 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_READHUFF_H +#define MSPACK_READHUFF_H 1 + +/* This implements a fast Huffman tree decoding system. */ + +#if !(defined(BITS_ORDER_MSB) || defined(BITS_ORDER_LSB)) +# error "readhuff.h is used in conjunction with readbits.h, include that first" +#endif +#if !(defined(TABLEBITS) && defined(MAXSYMBOLS)) +# error "define TABLEBITS(tbl) and MAXSYMBOLS(tbl) before using readhuff.h" +#endif +#if !(defined(HUFF_TABLE) && defined(HUFF_LEN)) +# error "define HUFF_TABLE(tbl) and HUFF_LEN(tbl) before using readhuff.h" +#endif +#ifndef HUFF_ERROR +# error "define HUFF_ERROR before using readhuff.h" +#endif +#ifndef HUFF_MAXBITS +# define HUFF_MAXBITS 16 +#endif + +/* Decodes the next huffman symbol from the input bitstream into var. + * Do not use this macro on a table unless build_decode_table() succeeded. + */ +#define READ_HUFFSYM(tbl, var) do { \ + ENSURE_BITS(HUFF_MAXBITS); \ + sym = HUFF_TABLE(tbl, PEEK_BITS(TABLEBITS(tbl))); \ + if (sym >= MAXSYMBOLS(tbl)) HUFF_TRAVERSE(tbl); \ + (var) = sym; \ + i = HUFF_LEN(tbl, sym); \ + REMOVE_BITS(i); \ +} while (0) + +#ifdef BITS_ORDER_LSB +# define HUFF_TRAVERSE(tbl) do { \ + i = TABLEBITS(tbl) - 1; \ + do { \ + if (i++ > HUFF_MAXBITS) HUFF_ERROR; \ + sym = HUFF_TABLE(tbl, \ + (sym << 1) | ((bit_buffer >> i) & 1)); \ + } while (sym >= MAXSYMBOLS(tbl)); \ +} while (0) +#else +#define HUFF_TRAVERSE(tbl) do { \ + i = 1 << (BITBUF_WIDTH - TABLEBITS(tbl)); \ + do { \ + if ((i >>= 1) == 0) HUFF_ERROR; \ + sym = HUFF_TABLE(tbl, \ + (sym << 1) | ((bit_buffer & i) ? 1 : 0)); \ + } while (sym >= MAXSYMBOLS(tbl)); \ +} while (0) +#endif + +/* make_decode_table(nsyms, nbits, length[], table[]) + * + * This function was originally coded by David Tritscher. + * It builds a fast huffman decoding table from + * a canonical huffman code lengths table. + * + * nsyms = total number of symbols in this huffman tree. + * nbits = any symbols with a code length of nbits or less can be decoded + * in one lookup of the table. + * length = A table to get code lengths from [0 to nsyms-1] + * table = The table to fill up with decoded symbols and pointers. + * Should be ((1<> 1; /* don't do 0 length codes */ + + /* fill entries for codes short enough for a direct mapping */ + for (bit_num = 1; bit_num <= nbits; bit_num++) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) continue; +#ifdef BITS_ORDER_MSB + leaf = pos; +#else + /* reverse the significant bits */ + fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; + do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); +#endif + + if((pos += bit_mask) > table_mask) return 1; /* table overrun */ + + /* fill all possible lookups of this symbol with the symbol itself */ +#ifdef BITS_ORDER_MSB + for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; +#else + fill = bit_mask; next_symbol = 1 << bit_num; + do { table[leaf] = sym; leaf += next_symbol; } while (--fill); +#endif + } + bit_mask >>= 1; + } + + /* exit with success if table is now complete */ + if (pos == table_mask) return 0; + + /* mark all remaining table entries as unused */ + for (sym = pos; sym < table_mask; sym++) { +#ifdef BITS_ORDER_MSB + table[sym] = 0xFFFF; +#else + reverse = sym; leaf = 0; fill = nbits; + do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); + table[leaf] = 0xFFFF; +#endif + } + + /* next_symbol = base of allocation for long codes */ + next_symbol = ((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1); + + /* give ourselves room for codes to grow by up to 16 more bits. + * codes now start at bit nbits+16 and end at (nbits+16-codelength) */ + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + for (bit_num = nbits+1; bit_num <= HUFF_MAXBITS; bit_num++) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) continue; + if (pos >= table_mask) return 1; /* table overflow */ + +#ifdef BITS_ORDER_MSB + leaf = pos >> 16; +#else + /* leaf = the first nbits of the code, reversed */ + reverse = pos >> 16; leaf = 0; fill = nbits; + do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); +#endif + for (fill = 0; fill < (bit_num - nbits); fill++) { + /* if this path hasn't been taken yet, 'allocate' two entries */ + if (table[leaf] == 0xFFFF) { + table[(next_symbol << 1) ] = 0xFFFF; + table[(next_symbol << 1) + 1 ] = 0xFFFF; + table[leaf] = next_symbol++; + } + + /* follow the path and select either left or right for next bit */ + leaf = table[leaf] << 1; + if ((pos >> (15-fill)) & 1) leaf++; + } + table[leaf] = sym; + pos += bit_mask; + } + bit_mask >>= 1; + } + + /* full table? */ + return (pos == table_mask) ? 0 : 1; +} +#endif diff --git a/3rdparty/mspack/system.c b/3rdparty/mspack/system.c new file mode 100644 index 0000000..cbc214f --- /dev/null +++ b/3rdparty/mspack/system.c @@ -0,0 +1,242 @@ +/* This file is part of libmspack. + * (C) 2003-2004 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#if !LARGEFILE_SUPPORT +const char *largefile_msg = "library not compiled to support large files."; +#endif + + +int mspack_version(int entity) { + switch (entity) { + /* CHM decoder version 1 -> 2 changes: + * - added mschmd_sec_mscompressed::spaninfo + * - added mschmd_header::first_pmgl + * - added mschmd_header::last_pmgl + * - added mschmd_header::chunk_cache; + */ + case MSPACK_VER_MSCHMD: + /* CAB decoder version 1 -> 2 changes: + * - added MSCABD_PARAM_SALVAGE + */ + case MSPACK_VER_MSCABD: + return 2; + case MSPACK_VER_LIBRARY: + case MSPACK_VER_SYSTEM: + case MSPACK_VER_MSSZDDD: + case MSPACK_VER_MSKWAJD: + case MSPACK_VER_MSOABD: + return 1; + case MSPACK_VER_MSCABC: + case MSPACK_VER_MSCHMC: + case MSPACK_VER_MSLITD: + case MSPACK_VER_MSLITC: + case MSPACK_VER_MSHLPD: + case MSPACK_VER_MSHLPC: + case MSPACK_VER_MSSZDDC: + case MSPACK_VER_MSKWAJC: + case MSPACK_VER_MSOABC: + return 0; + } + return -1; +} + +int mspack_sys_selftest_internal(int offt_size) { + return (sizeof(long int) == offt_size) ? MSPACK_ERR_OK : MSPACK_ERR_SEEK; +} + +/* validates a system structure */ +int mspack_valid_system(struct mspack_system *sys) { + return (sys != NULL) && (sys->open != NULL) && (sys->close != NULL) && + (sys->read != NULL) && (sys->write != NULL) && (sys->seek != NULL) && + (sys->tell != NULL) && (sys->message != NULL) && (sys->alloc != NULL) && + (sys->free != NULL) && (sys->copy != NULL) && (sys->null_ptr == NULL); +} + +/* returns the length of a file opened for reading */ +int mspack_sys_filelen(struct mspack_system *system, + struct mspack_file *file, long int *length) +{ + long int current; + + if (!system || !file || !length) return MSPACK_ERR_OPEN; + + /* get current offset */ + current = system->tell(file); + + /* seek to end of file */ + if (system->seek(file, (long int) 0, MSPACK_SYS_SEEK_END)) { + return MSPACK_ERR_SEEK; + } + + /* get offset of end of file */ + *length = system->tell(file); + + /* seek back to original offset */ + if (system->seek(file, current, MSPACK_SYS_SEEK_START)) { + return MSPACK_ERR_SEEK; + } + + return MSPACK_ERR_OK; +} + + + +/* definition of mspack_default_system -- if the library is compiled with + * MSPACK_NO_DEFAULT_SYSTEM, no default system will be provided. Otherwise, + * an appropriate default system (e.g. the standard C library, or some native + * API calls) + */ + +#ifdef MSPACK_NO_DEFAULT_SYSTEM +struct mspack_system *mspack_default_system = NULL; +#else + +/* implementation of mspack_default_system for standard C library */ + +#include +#include +#include +#include + +struct mspack_file_p { + FILE *fh; + const char *name; +}; + +static struct mspack_file *msp_open(struct mspack_system *self, + const char *filename, int mode) +{ + struct mspack_file_p *fh; + const char *fmode; + + switch (mode) { + case MSPACK_SYS_OPEN_READ: fmode = "rb"; break; + case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break; + case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break; + case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break; + default: return NULL; + } + + if ((fh = (struct mspack_file_p *) malloc(sizeof(struct mspack_file_p)))) { + fh->name = filename; + if ((fh->fh = fopen(filename, fmode))) return (struct mspack_file *) fh; + free(fh); + } + return NULL; +} + +static void msp_close(struct mspack_file *file) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self) { + fclose(self->fh); + free(self); + } +} + +static int msp_read(struct mspack_file *file, void *buffer, int bytes) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self && buffer && bytes >= 0) { + size_t count = fread(buffer, 1, (size_t) bytes, self->fh); + if (!ferror(self->fh)) return (int) count; + } + return -1; +} + +static int msp_write(struct mspack_file *file, void *buffer, int bytes) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self && buffer && bytes >= 0) { + size_t count = fwrite(buffer, 1, (size_t) bytes, self->fh); + if (!ferror(self->fh)) return (int) count; + } + return -1; +} + +static int msp_seek(struct mspack_file *file, long int offset, int mode) { + struct mspack_file_p *self = (struct mspack_file_p *) file; + if (self) { + switch (mode) { + case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break; + case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break; + case MSPACK_SYS_SEEK_END: mode = SEEK_END; break; + default: return -1; + } +#if HAVE_FSEEKO + return fseeko(self->fh, offset, mode); +#else + return fseek(self->fh, offset, mode); +#endif + } + return -1; +} + +static long int msp_tell(struct mspack_file *file) { + struct mspack_file_p *self = (struct mspack_file_p *) file; +#if HAVE_FSEEKO + return (self) ? (long int) ftello(self->fh) : 0; +#else + return (self) ? (long int) ftell(self->fh) : 0; +#endif +} + +static void msp_msg(struct mspack_file *file, const char *format, ...) { + va_list ap; + if (file) fprintf(stderr, "%s: ", ((struct mspack_file_p *) file)->name); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fputc((int) '\n', stderr); + fflush(stderr); +} + +static void *msp_alloc(struct mspack_system *self, size_t bytes) { +#if DEBUG + /* make uninitialised data obvious */ + char *buf = malloc(bytes + 8); + if (buf) memset(buf, 0xDC, bytes); + *((size_t *)buf) = bytes; + return &buf[8]; +#else + return malloc(bytes); +#endif +} + +static void msp_free(void *buffer) { +#if DEBUG + char *buf = buffer; + size_t bytes; + if (buf) { + buf -= 8; + bytes = *((size_t *)buf); + /* make freed data obvious */ + memset(buf, 0xED, bytes); + free(buf); + } +#else + free(buffer); +#endif +} + +static void msp_copy(void *src, void *dest, size_t bytes) { + memcpy(dest, src, bytes); +} + +static struct mspack_system msp_system = { + &msp_open, &msp_close, &msp_read, &msp_write, &msp_seek, + &msp_tell, &msp_msg, &msp_alloc, &msp_free, &msp_copy, NULL +}; + +struct mspack_system *mspack_default_system = &msp_system; + +#endif diff --git a/3rdparty/mspack/system.h b/3rdparty/mspack/system.h new file mode 100644 index 0000000..826e89f --- /dev/null +++ b/3rdparty/mspack/system.h @@ -0,0 +1,113 @@ +/* This file is part of libmspack. + * (C) 2003-2018 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +#ifndef MSPACK_SYSTEM_H +#define MSPACK_SYSTEM_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* ensure config.h is read before mspack.h */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* assume exists */ +#include + +/* fix for problem with GCC 4 and glibc (thanks to Ville Skytta) + * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=150429 + */ +#ifdef read +# undef read +#endif + +/* Old GCCs don't have __func__, but __FUNCTION__: + * http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html + */ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + +#if DEBUG +# include +# define D(x) do { printf("%s:%d (%s) ",__FILE__, __LINE__, __func__); \ + printf x ; fputc('\n', stdout); fflush(stdout);} while (0); +#else +# define D(x) +#endif + +/* CAB supports searching through files over 4GB in size, and the CHM file + * format actively uses 64-bit offsets. These can only be fully supported + * if the system the code runs on supports large files. If not, the library + * will work as normal using only 32-bit arithmetic, but if an offset + * greater than 2GB is detected, an error message indicating the library + * can't support the file should be printed. + */ +#if HAVE_INTTYPES_H +# include +#else +# define PRId64 "lld" +# define PRIu64 "llu" +# define PRId32 "ld" +# define PRIu32 "lu" +#endif + +#include +#if ((defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS >= 64) || \ + (defined(FILESIZEBITS) && FILESIZEBITS >= 64) || \ + defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE) || \ + SIZEOF_OFF_T >= 8) +# define LARGEFILE_SUPPORT 1 +# define LD PRId64 +# define LU PRIu64 +#else +extern const char *largefile_msg; +# define LD PRId32 +# define LU PRIu32 +#endif + +/* endian-neutral reading of little-endian data */ +#define __egi32(a,n) ( ((((unsigned char *) a)[n+3]) << 24) | \ + ((((unsigned char *) a)[n+2]) << 16) | \ + ((((unsigned char *) a)[n+1]) << 8) | \ + ((((unsigned char *) a)[n+0]))) +#define EndGetI64(a) ((((unsigned long long int) __egi32(a,4)) << 32) | \ + ((unsigned int) __egi32(a,0))) +#define EndGetI32(a) __egi32(a,0) +#define EndGetI16(a) ((((a)[1])<<8)|((a)[0])) + +/* endian-neutral reading of big-endian data */ +#define EndGetM32(a) (((((unsigned char *) a)[0]) << 24) | \ + ((((unsigned char *) a)[1]) << 16) | \ + ((((unsigned char *) a)[2]) << 8) | \ + ((((unsigned char *) a)[3]))) +#define EndGetM16(a) ((((a)[0])<<8)|((a)[1])) + +extern struct mspack_system *mspack_default_system; + +/* returns the length of a file opened for reading */ +extern int mspack_sys_filelen(struct mspack_system *system, + struct mspack_file *file, off_t *length); + +/* validates a system structure */ +extern int mspack_valid_system(struct mspack_system *sys); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 + + 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) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7687258 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +TARGET := xenon-bltool +ifeq ($(OS),Windows_NT) + TARGET := $(addsuffix .exe,$(TARGET)) +endif + +SRC_DIR := source +EXCRYPT_DIR := 3rdparty/excrypt +MSPACK_DIR := 3rdparty/mspack +SOURCES := $(wildcard $(SRC_DIR)/*.c) $(wildcard $(EXCRYPT_DIR)/*.c) $(wildcard $(MSPACK_DIR)/*.c) + +INCLUDES := include $(EXCRYPT_DIR) $(MSPACK_DIR) + +default: all + +$(TARGET): $(SOURCES) + $(CC) $(CFLAGS) $(patsubst %,-I %,$(INCLUDES)) -o $@ $^ + +.PHONY: all +all: $(TARGET) + +.PHONY: clean +clean: + @-rm -f $(TARGET) diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1533e0 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# xenon-bltool + +Work-in-progress utility for working with Xbox 360 bootloader stages, and soon more. + +Licensed to you under the GNU General Public License version 2. See LICENSE for more information. + +## Features so far + +* Extracting and updating the kernel stages. (CE/SE + CG/SE) + +## Usage + +``` +xenon-bltool - https://github.com/InvoxiPlayGames/xenon-bltool + +This program is free software licensed under version 2 of the GNU General +Public License. It comes with absolutely NO WARRANTY of any kind. + +usage: xenon-bltool [verb] [arguments] + +available verbs: + decompress - Decompresses a CE/SE (5BL) bootloader. + xenon-bltool decompress [path to CE] [output path] + xboxupd - Applies an xboxupd.bin (CF+CG) patch to a base kernel or CE. + xenon-bltool xboxupd [path to xboxupd.bin] [path to CE/base] [output_path] +``` + +## Credits + +Code has been used from the following libraries: + +* [ExCrypt](https://github.com/emoose/ExCrypt) + * Reimplementation of Xbox 360 cryptography functions. + * Licensed under BSD-3-Clause license. (See 3rdparty/ExCrypt_LICENSE) +* [libmspack](https://github.com/kyz/libmspack) + * Implementation of Microsoft's LZX compression scheme. + * Licensed under GNU Lesser General Public License version 2.1. (see 3rdparty/libspack_LICENSE) +* [Xenia](https://github.com/xenia-project/xenia) + * Implementation of LZX delta compression for Xbox 360 binaries. + * Licensed under BSD license. (See 3rdparty/Xenia_LICENSE) + +... and some obligatory shoutouts to some other open source Xbox 360 hacking projects: + +* [libxenon](https://github.com/Free60Project/libxenon) and [xell-reloaded](https://github.com/Free60Project/xell-reloaded) +* [Xbox_360_Crypto](https://github.com/GoobyCorp/Xbox_360_Crypto) +* [J-Runner with Extras](https://github.com/Octal450/J-Runner-with-Extras) +* [Xbox-Reversing](https://github.com/TEIR1plus2/Xbox-Reversing) +* [RGLoader](https://github.com/RGLoader/RGLoader-Patches) + +... and everyone involved in modding the 360. I can't name everyone here, but if you've worked on freeing this box, you've done a great job. <3 diff --git a/include/1bl-keys.h b/include/1bl-keys.h new file mode 100644 index 0000000..6ef8df6 --- /dev/null +++ b/include/1bl-keys.h @@ -0,0 +1,4 @@ +#include + +extern uint8_t key_1bl[]; +extern uint8_t rsa_1bl[]; diff --git a/include/ce-handler.h b/include/ce-handler.h new file mode 100644 index 0000000..b1cad47 --- /dev/null +++ b/include/ce-handler.h @@ -0,0 +1,9 @@ +#include +#include + +bool ce_is_decrypted(uint8_t *ce_data); +void ce_print_info(uint8_t *ce_data); +void ce_decrypt(uint8_t *ce_data, uint8_t *cd_key); +void ce_calculate_rotsum(uint8_t *ce_data, uint8_t *sha_out); + +bool ce_decompress(uint8_t *ce_data, uint8_t *output_buf); diff --git a/include/cf-handler.h b/include/cf-handler.h new file mode 100644 index 0000000..a4640dd --- /dev/null +++ b/include/cf-handler.h @@ -0,0 +1,8 @@ +#include +#include + +bool cf_is_decrypted(uint8_t *cf_data); +void cf_print_info(uint8_t *cf_data); +void cf_decrypt(uint8_t *cf_data, uint8_t *cpu_or_1bl_key); +void cf_calculate_rotsum(uint8_t *cf_data, uint8_t *sha_out); +bool cf_verify_signature(uint8_t *cf_data); diff --git a/include/cg-handler.h b/include/cg-handler.h new file mode 100644 index 0000000..8b22531 --- /dev/null +++ b/include/cg-handler.h @@ -0,0 +1,9 @@ +#include +#include + +bool cg_is_decrypted(uint8_t *cf_data); +void cg_print_info(uint8_t *cf_data); +void cg_decrypt(uint8_t *cf_data, uint8_t *cpu_or_1bl_key); +void cg_calculate_rotsum(uint8_t *cf_data, uint8_t *sha_out); + +bool cg_apply_patch(uint8_t *cg_data, uint8_t *base_data, uint8_t *output_buf); diff --git a/include/compression-handler.h b/include/compression-handler.h new file mode 100644 index 0000000..7c50dc6 --- /dev/null +++ b/include/compression-handler.h @@ -0,0 +1,4 @@ +#include +#include + +bool get_full_compressed_buffer(uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t decompressed_size, uint32_t *compressed_size); diff --git a/include/lzx-delta.h b/include/lzx-delta.h new file mode 100644 index 0000000..7531d13 --- /dev/null +++ b/include/lzx-delta.h @@ -0,0 +1,23 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_CPU_LZX_H_ +#define XENIA_CPU_LZX_H_ + +#include +#include "xenon-bootloader.h" + +int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest, + size_t dest_len, uint32_t window_size, void* window_data, + size_t window_data_len); + +int lzxdelta_apply_patch(bootloader_delta_block* patch, size_t patch_len, + uint32_t window_size, void* dest); + +#endif // XENIA_CPU_LZX_H_ diff --git a/include/nand.h b/include/nand.h new file mode 100644 index 0000000..3a0545e --- /dev/null +++ b/include/nand.h @@ -0,0 +1,17 @@ +#include +#include "xenon-bootloader.h" + +typedef struct _xenon_nand_header { + bootloader_header header; // entrypoint leads to CB + char copyright[0x40]; + uint8_t unused[0x10]; + uint32_t kv_size; + uint32_t cf_offset; + int16_t patch_slots; + uint16_t kv_version; + uint32_t kv_addr; + uint32_t patch_size; + uint32_t smc_config_offset; + uint32_t smc_boot_size; + uint32_t smc_boot_offset; +} xenon_nand_header; diff --git a/include/utility.h b/include/utility.h new file mode 100644 index 0000000..5008d0e --- /dev/null +++ b/include/utility.h @@ -0,0 +1,13 @@ +#include + +#ifndef SHOULD_BE_BE +#define BE16(i) ((((i) & 0xFF) << 8 | ((i) >> 8) & 0xFF) & 0xFFFF) +#define BE(i) (((i) & 0xff) << 24 | ((i) & 0xff00) << 8 | ((i) & 0xff0000) >> 8 | ((i) >> 24) & 0xff) +#define BE64(i) (BE((i) & 0xFFFFFFFF) << 32 | BE(((i) >> 32) & 0xFFFFFFFF)) +#else +#define BE16(i) i +#define BE(i) i +#define BE64(i) i +#endif + +void hexdump(uint8_t *data, uint32_t size); diff --git a/include/xenon-bootloader.h b/include/xenon-bootloader.h new file mode 100644 index 0000000..c9859ce --- /dev/null +++ b/include/xenon-bootloader.h @@ -0,0 +1,148 @@ +#ifndef XENON_BOOTLOADER_H_ +#define XENON_BOOTLOADER_H_ + +#include +#include "excrypt.h" + +typedef enum _xenon_bl_type { + XENON_BOOTLOADER_1BL, + XENON_BOOTLOADER_CB, + XENON_BOOTLOADER_SC, + XENON_BOOTLOADER_CD, + XENON_BOOTLOADER_CE, + XENON_BOOTLOADER_CF, + XENON_BOOTLOADER_CG, + + // not bootloaders but same structure + XENON_BOOTLOADER_HV = 0x10, + XENON_BOOTLOADER_XKE, + XENON_BOOTLOADER_BLUPD, + + XENON_BOOTLOADER_INVALID = -1 +} xenon_bl_type; + +typedef struct _onebl_globals { + EXCRYPT_RSAPUB_2048 ms_pubkey; + uint64_t addr_post; + uint64_t addr_61008; // what periph is this + uint64_t addr_pcie; + uint64_t addr_e1000000; // what is this too + uint64_t addr_nand; + uint64_t addr_base; + uint64_t addr_sram; + uint8_t onebl_key[0x10]; + char cb_salt[10]; +} onebl_globals; + +typedef struct _bootloader_header { + uint16_t magic; + uint16_t version; + uint16_t pairing; + uint16_t flags; + uint32_t entrypoint; + uint32_t size; +} bootloader_header; + +typedef struct _bootloader_1bl_header { + bootloader_header header; + char copyright[0x80]; + uint8_t padding[0x6C]; + uint32_t globals_addr; +} bootloader_1bl_header; + +// used by SC/3BL, XKE, etc +typedef struct _bootloader_generic_header { + bootloader_header header; + uint8_t key[0x10]; + EXCRYPT_SIG signature; +} bootloader_generic_header; + +typedef struct _bootloader_cb_header { + bootloader_header header; + uint8_t key[0x10]; + uint64_t padding_or_args[4]; + EXCRYPT_SIG signature; + uint8_t globals[0x25C]; // find out whats in here... + uint8_t cd_cbb_hash[0x14]; // CB_A has CB_B hash, CB_B has CD hash + uint8_t more_globals[0x10]; // and here as well... +} bootloader_cb_header; + +typedef struct _bootloader_cd_header { + bootloader_header header; + uint8_t key[0x10]; + uint8_t idk_yet[0x220]; // possibly unused? + char cf_salt[10]; + uint16_t unused; + uint8_t ce_hash[0x14]; +} bootloader_cd_header; + +typedef struct _bootloader_ce_header { + bootloader_header header; + uint8_t key[0x10]; + uint64_t target_address; + uint32_t uncompressed_size; + uint32_t unknown; // think this is unused +} bootloader_ce_header; + +typedef struct _bootloader_cf_header { + bootloader_header header; + uint16_t base_ver; + uint16_t base_flags; // unsure + uint16_t target_ver; + uint16_t target_flags; // unsure + uint32_t unknown; // think this is unused + uint32_t cg_size; + uint8_t key[0x10]; + uint8_t pairing[0x200]; + EXCRYPT_SIG signature; + uint8_t cg_hmac[0x10]; + uint8_t cg_hash[0x14]; +} bootloader_cf_header; + +typedef struct _bootloader_cg_header { + bootloader_header header; + uint8_t key[0x10]; + uint32_t original_size; + uint8_t original_hash[0x14]; + uint32_t new_size; + uint8_t new_hash[0x14]; +} bootloader_cg_header; + +typedef struct _bootloader_update_header { + bootloader_header header; + uint32_t update_format; + uint32_t part2_size; + uint32_t part1_size; + uint32_t part2_decompressed_size; + uint8_t key[0x10]; + EXCRYPT_SIG signature; +} bootloader_update_header; + +typedef struct _bootloader_update_inner { + uint8_t aes_key[0x10]; + uint32_t some_flags; // unsure? + uint8_t part2_sha_compressed[0x14]; + uint8_t part1_sha[0x14]; + uint8_t part2_sha[0x14]; +} bootloader_update_inner; + +typedef struct _bootloader_compression_header { + uint32_t window_size; + uint32_t block_size; + uint32_t compressed_size; + uint32_t decompressed_size; +} bootloader_compression_header; + +typedef struct _bootloader_compression_block { + uint16_t compressed_size; + uint16_t decompressed_size; +} bootloader_compression_block; + +typedef struct _bootloader_delta_block { + uint32_t old_addr; + uint32_t new_addr; + uint16_t decompressed_size; + uint16_t compressed_size; +} bootloader_delta_block; + +#endif diff --git a/source/1bl-keys.c b/source/1bl-keys.c new file mode 100644 index 0000000..e30acd8 --- /dev/null +++ b/source/1bl-keys.c @@ -0,0 +1,35 @@ +/* + 1bl-keys.c - Encryption and signature verification keys from the Xbox 360's 1BL. + Everypony should know these numbers. +*/ + +#include + +// RC4/AES key used for decrypting bootloader stages. +uint8_t key_1bl[0x10] = { + 0xDD, 0x88, 0xAD, 0x0C, 0x9E, 0xD6, 0x69, 0xE7, 0xB5, 0x67, 0x94, 0xFB, 0x68, 0x56, 0x3E, 0xFA +}; + +// The public key used to verify integrity of bootloader stages. +uint8_t rsa_1bl[0x110] = { + 0x00, 0x00, 0x00, 0x20, // key size + 0x00, 0x01, 0x00, 0x01, // exponent + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved + // public key in big endian + 0xE9, 0x8D, 0xB5, 0xDC, 0xAF, 0x38, 0x8E, 0xF1, 0x38, 0x9E, 0x28, 0xCB, 0x4A, 0x11, 0xC8, 0x22, + 0x52, 0xE1, 0x1F, 0x53, 0x45, 0x56, 0x60, 0xA2, 0x52, 0xD4, 0xD1, 0x68, 0x4E, 0xCC, 0x80, 0x99, + 0xD7, 0x5C, 0x40, 0xC5, 0xAF, 0x73, 0x0C, 0xCF, 0x44, 0x06, 0xB0, 0x6D, 0x16, 0x91, 0x08, 0x38, + 0xB3, 0x00, 0x2D, 0xBC, 0xEB, 0x1D, 0x0C, 0x1D, 0xC5, 0xC6, 0x68, 0x0B, 0x80, 0x4C, 0x62, 0x0B, + 0x7E, 0xE8, 0x72, 0x0C, 0xCF, 0x1D, 0xB4, 0xBD, 0xEE, 0x4B, 0x11, 0x36, 0xD1, 0xC9, 0x92, 0x1F, + 0xE9, 0xAE, 0xC0, 0x51, 0x52, 0x51, 0xF7, 0x23, 0xD6, 0xBC, 0xF4, 0xE9, 0x58, 0x87, 0x40, 0xB1, + 0x02, 0x66, 0x5A, 0x43, 0xEB, 0x67, 0x5F, 0x50, 0x94, 0x32, 0x34, 0x7A, 0xA7, 0x50, 0xD9, 0xB4, + 0x14, 0x4E, 0xB0, 0x02, 0x31, 0x8B, 0xA7, 0x00, 0x9A, 0x12, 0xC8, 0x3B, 0x8F, 0x76, 0xE4, 0x8F, + 0x33, 0xB5, 0xCD, 0x0C, 0x24, 0x6D, 0x2A, 0xE5, 0x57, 0xA0, 0x44, 0x76, 0x78, 0x41, 0xF4, 0x8F, + 0xCB, 0x3A, 0xB5, 0x0E, 0xA1, 0xA2, 0x56, 0x6D, 0x17, 0xDB, 0x32, 0xCC, 0xB8, 0x5A, 0x5F, 0xAE, + 0xED, 0x9A, 0x62, 0x31, 0x5D, 0x88, 0x7F, 0x6D, 0x9A, 0x53, 0x80, 0xB0, 0x34, 0xC7, 0x42, 0x51, + 0x2D, 0x94, 0x4D, 0x86, 0x09, 0x32, 0x8F, 0x71, 0xA7, 0xBA, 0x16, 0x6C, 0xE6, 0xDC, 0x6B, 0x64, + 0x61, 0x7D, 0x16, 0xB5, 0x20, 0x51, 0xD0, 0xB1, 0x1F, 0xFE, 0x1E, 0x35, 0x56, 0x9A, 0x76, 0x4D, + 0x62, 0x7F, 0x5D, 0xF4, 0xB8, 0x7D, 0xC4, 0x18, 0x2C, 0x81, 0xB7, 0xAF, 0xE4, 0x7D, 0x13, 0x5D, + 0xF4, 0x0F, 0x63, 0x05, 0x3F, 0x1A, 0xED, 0xED, 0x4B, 0xEE, 0xFD, 0x6D, 0x74, 0xE6, 0xA5, 0x92, + 0xA7, 0x99, 0x81, 0x73, 0x95, 0xD8, 0xC7, 0xA5, 0xA1, 0xC7, 0x7B, 0x09, 0x05, 0x85, 0x41, 0x04 +}; diff --git a/source/bltool-cli.c b/source/bltool-cli.c new file mode 100644 index 0000000..162bd5c --- /dev/null +++ b/source/bltool-cli.c @@ -0,0 +1,357 @@ +/* + bltool-cli.c - The xenon-bltool command line interface. + Copyright 2024 Emma https://ipg.gay/ + + This file is part of xenon-bltool. + + xenon-bltool 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, version 2 of + the License. + + xenon-bltool 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 xenon-bltool. + If not, see . +*/ + +#include +#include +#include +#include +#include "1bl-keys.h" +#include "ce-handler.h" +#include "cg-handler.h" +#include "cf-handler.h" +#include "xenon-bootloader.h" + +void hexdump(uint8_t *data, uint32_t size) { + for (uint32_t i = 0; i < size; i++) { + printf("%02x ", data[i]); + } + printf("\n"); +} + +// shitty hack to get a file size +// idk how to do it properly +size_t get_file_size(FILE *fp) { + fseek(fp, 0, SEEK_END); + size_t fs = ftell(fp); + fseek(fp, 0, SEEK_SET); + return fs; +} + +int do_decompress_command(int argc, char **argv) { + if (argc < 2) { + printf("not enough arguments!\n"); + return -1; + } + + char *base_path = argv[1]; + char *output_path = argv[2]; + + // open, read and close our base file + FILE *base_file = fopen(base_path, "rb"); + if (base_file == NULL) { + printf("couldn't open the input file!\n"); + return -1; + } + size_t base_size = get_file_size(base_file); + void *base_buf = malloc(base_size); + if (base_buf == NULL) { + printf("failed to allocate buffer for input file.\n"); + return -1; + } + fread(base_buf, 1, base_size, base_file); + fclose(base_file); + + // detect the type of the base kernel file + uint8_t *base_kernel = (uint8_t *)base_buf; + uint32_t base_kernel_size = base_size; + bootloader_header *hdr = (bootloader_header *)base_buf; + if ((BE16(hdr->magic) & 0xFFF) != 0x345) { + printf("input file isn't CE/5BL.\n"); + free(base_buf); + return -1; + } + + bootloader_ce_header *ce_hdr = (bootloader_ce_header *)hdr; + ce_print_info(base_buf); + if (!ce_is_decrypted(base_buf)) { + printf("input CE/5BL file is encrypted.\n"); + free(base_buf); + return -1; + } + + // we have to decompress the base kernel + uint8_t *decompressed_kernel_buf = malloc(BE(ce_hdr->uncompressed_size)); + if (decompressed_kernel_buf == NULL) { + printf("failed to allocate buffer for decompressed kernel.\n"); + free(base_buf); + return -1; + } + + if (!ce_decompress(base_buf, decompressed_kernel_buf)) { + printf("decompression failed.\n"); + free(base_buf); + free (decompressed_kernel_buf); + return -1; + } + + base_kernel_size = BE(ce_hdr->uncompressed_size); + base_kernel = decompressed_kernel_buf; + hdr = (bootloader_header *)decompressed_kernel_buf; + + // free the original basefile buffer, we don't need the CE anymore + free(base_buf); + + // save the file + printf("writing decompressed kernel to '%s'...\n", output_path); + FILE *out_file = fopen(output_path, "wb+"); + if (out_file == NULL) { + printf("failed to open output file!\n"); + free(decompressed_kernel_buf); + return -1; + } + fwrite(decompressed_kernel_buf, 1, BE(ce_hdr->uncompressed_size), out_file); + fclose(out_file); + free(decompressed_kernel_buf); + + return 0; +} + +int do_xboxupd_command(int argc, char **argv) { + if (argc < 3) { + printf("not enough arguments!\n"); + return -1; + } + + char *xboxupd_path = argv[1]; + char *base_path = argv[2]; + char *output_path = argv[3]; + + // open, read and close our base file + FILE *base_file = fopen(base_path, "rb"); + if (base_file == NULL) { + printf("couldn't open the base file!\n"); + return -1; + } + size_t base_size = get_file_size(base_file); + void *base_buf = malloc(base_size); + if (base_buf == NULL) { + printf("failed to allocate buffer for basefile.\n"); + return -1; + } + fread(base_buf, 1, base_size, base_file); + fclose(base_file); + + // detect the type of the base kernel file + uint8_t *base_kernel = (uint8_t *)base_buf; + uint32_t base_kernel_size = base_size; + bootloader_header *hdr = (bootloader_header *)base_buf; + if ((BE16(hdr->magic) & 0xFFF) == 0xE4E) { + printf("base file is raw HV %i with size 0x%x\n", BE16(hdr->version), base_kernel_size); + } else if ((BE16(hdr->magic) & 0xFFF) == 0x345) { + bootloader_ce_header *ce_hdr = (bootloader_ce_header *)hdr; + ce_print_info(base_buf); + if (!ce_is_decrypted(base_buf)) { + printf("base CE/5BL file is encrypted.\n"); + free(base_buf); + return -1; + } + + // we have to decompress the base kernel + uint8_t *decompressed_kernel_buf = malloc(BE(ce_hdr->uncompressed_size)); + if (decompressed_kernel_buf == NULL) { + printf("failed to allocate buffer for decompressed kernel.\n"); + free(base_buf); + return -1; + } + + if (!ce_decompress(base_buf, decompressed_kernel_buf)) { + printf("decompression failed.\n"); + free(base_buf); + free (decompressed_kernel_buf); + return -1; + } + + base_kernel_size = BE(ce_hdr->uncompressed_size); + base_kernel = decompressed_kernel_buf; + hdr = (bootloader_header *)decompressed_kernel_buf; + + // free the original basefile buffer, we don't need the CE anymore + free(base_buf); + + printf("base file is decompressed HV %i\n", BE16(hdr->version)); + } else { + printf("unknown base file format. (%04x)\n", BE16(hdr->magic)); + free(base_buf); + return -1; + } + + // load the xboxupd.bin file + FILE *xboxupd_file = fopen(xboxupd_path, "rb"); + if (xboxupd_file == NULL) { + printf("couldn't open the xboxupd.bin file!\n"); + free(base_kernel); + return -1; + } + size_t xboxupd_size = get_file_size(xboxupd_file); + uint8_t *xboxupd_buf = malloc(xboxupd_size); + if (xboxupd_buf == NULL) { + printf("failed to allocate buffer for xboxupd.bin.\n"); + free(base_kernel); + return -1; + } + fread(xboxupd_buf, 1, xboxupd_size, xboxupd_file); + fclose(xboxupd_file); + + // TODO(Emma): allow just a decrypted CG (or CG with key) without CF + + // make sure we have both the CF and CG stages in the file + bootloader_cf_header *cf_hdr = (bootloader_cf_header *)xboxupd_buf; + if ((BE16(cf_hdr->header.magic) & 0xFFF) != 0x346) { + printf("CF header not found. invalid xboxupd.bin?\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + // some basic size sanity checking + if (xboxupd_size < ( BE(cf_hdr->header.size) + BE(cf_hdr->cg_size) )) { + printf("input xboxupd.bin file too small for CF+CG. invalid file?\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + // and try to decrypt the CF + if (!cf_is_decrypted((uint8_t *)cf_hdr)) { + cf_decrypt((uint8_t *)cf_hdr, key_1bl); + } + if (!cf_is_decrypted((uint8_t *)cf_hdr)) { + printf("failed to decrypt CF\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + cf_print_info((uint8_t *)cf_hdr); + + if (BE16(cf_hdr->base_ver) != BE16(hdr->version)) { + printf("mismatching base kernel version %i (CF expects %i)\n", BE16(hdr->version), BE16(cf_hdr->base_ver)); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + + // load the CG as well + bootloader_cg_header *cg_hdr = (bootloader_cg_header *)(xboxupd_buf + BE(cf_hdr->header.size)); + if ((BE16(cg_hdr->header.magic) & 0xFFF) != 0x347) { + printf("CG header not found. invalid xboxupd.bin?\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + // and try to decrypt that, too + // maybe cg_is_decrypted should be replaced with a rotsum check against CF? + if (!cg_is_decrypted((uint8_t *)cg_hdr)) { + cg_decrypt((uint8_t *)cg_hdr, cf_hdr->cg_hmac); + } + if (!cg_is_decrypted((uint8_t *)cg_hdr)) { + printf("failed to decrypt CG\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + + uint8_t cg_rotsum[0x14]; + cg_calculate_rotsum((uint8_t *)cg_hdr, cg_rotsum); + if (memcmp(cg_rotsum, cf_hdr->cg_hash, 0x14) != 0) { + printf("CG checksum does not match CF header\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + cg_print_info((uint8_t *)cg_hdr); + + // patch the kernel + uint8_t *patched_kernel = malloc(BE(cg_hdr->new_size)); + if (patched_kernel == NULL) { + printf("failed to allocate memory for new kernel.\n"); + free(base_kernel); + free(xboxupd_buf); + return -1; + } + if (!cg_apply_patch((uint8_t *)cg_hdr, base_kernel, patched_kernel)) { + printf("failed to apply the kernel patch.\n"); + free(base_kernel); + free(xboxupd_buf); + free(patched_kernel); + return -1; + } + + // free these now since we won't need them later + free(base_kernel); + free(xboxupd_buf); + + // save the file + printf("writing patched kernel to '%s'...\n", output_path); + FILE *out_file = fopen(output_path, "wb+"); + if (out_file == NULL) { + printf("failed to open output file!\n"); + free(patched_kernel); + return -1; + } + fwrite(patched_kernel, 1, BE(cg_hdr->new_size), out_file); + fclose(out_file); + free(patched_kernel); + + return 0; +} + +typedef int(*command_handler_t)(int argc, char **argv); +typedef struct _command_verb { + char *verb; + char *description; + int required_args; + char *argument_example; + command_handler_t handler; +} command_verb; + +static const command_verb verbs[] = { + { "decompress", "Decompresses a CE/SE (5BL) bootloader.", 2, "[path to CE] [output path]", do_decompress_command }, + { "xboxupd", "Applies an xboxupd.bin (CF+CG) patch to a base kernel or CE.", 3, "[path to xboxupd.bin] [path to CE/base] [output_path]", do_xboxupd_command }, +}; +static const int total_verbs = 2; + +int main(int argc, char **argv) { + printf("xenon-bltool - https://github.com/InvoxiPlayGames/xenon-bltool\n\n"); + + if (argc < 2) { + printf("This program is free software licensed under version 2 of the GNU General\nPublic License. It comes with absolutely NO WARRANTY of any kind.\n\n"); +print_usage: + printf("usage: %s [verb] [arguments]\n\n", argv[0]); + printf("available verbs:\n"); + for (int i = 0; i < total_verbs; i++) { + command_verb *verb = &verbs[i]; + printf(" %s - %s\n", verb->verb, verb->description); + printf(" %s %s %s\n", argv[0], verb->verb, verb->argument_example); + } + return -1; + } + + command_verb *chosen_verb = NULL; + for (int i = 0; i < total_verbs; i++) { + if (strcasecmp(verbs[i].verb, argv[1]) == 0) + chosen_verb = &verbs[i]; + } + + if (chosen_verb == NULL) { + printf("invalid command!\n"); + goto print_usage; + } + + // "push along" the argc/argv, so the handlers only get their verb name + argv++; + argc--; + return chosen_verb->handler(argc - 1, argv); +} diff --git a/source/cb-handler.c b/source/cb-handler.c new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/source/cb-handler.c @@ -0,0 +1 @@ +// TODO diff --git a/source/cd-handler.c b/source/cd-handler.c new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/source/cd-handler.c @@ -0,0 +1 @@ +// TODO diff --git a/source/ce-handler.c b/source/ce-handler.c new file mode 100644 index 0000000..eda9fb2 --- /dev/null +++ b/source/ce-handler.c @@ -0,0 +1,104 @@ +/* + ce-handler.c - Handling for Xbox 360 CE/5BL bootloader stages. + Copyright 2024 Emma https://ipg.gay/ + + This file is part of xenon-bltool. + + xenon-bltool 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, version 2 of + the License. + + xenon-bltool 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 xenon-bltool. + If not, see . +*/ + +#include +#include +#include +#include +#include "utility.h" +#include "excrypt.h" +#include "xenon-bootloader.h" +#include "lzx-delta.h" +#include "compression-handler.h" + +bool ce_is_decrypted(uint8_t *ce_data) { + bootloader_ce_header *hdr = (bootloader_ce_header *)ce_data; + // this might be a naive assumption, but i've never seen this unknown value be anything but 0. + return (hdr->unknown == 0x00000000); +} + +void ce_print_info(uint8_t *ce_data) { + bootloader_ce_header *hdr = (bootloader_ce_header *)ce_data; + char *indicator = (hdr->header.magic & 0xF000 == 0x5000) ? "SE" : "CE"; + printf("%s version: %i\n", indicator, BE16(hdr->header.version)); + printf("%s size: 0x%x\n", indicator, BE(hdr->header.size)); + if (ce_is_decrypted(ce_data)) { + printf("%s decompressed size: 0x%x\n", indicator, BE(hdr->uncompressed_size)); + printf("%s load address: 0x%llx\n", indicator, BE64(hdr->target_address)); + } else { + printf("%s is encrypted\n", indicator); + } +} + +void ce_decrypt(uint8_t *ce_data, uint8_t *cd_key) { + bootloader_ce_header *hdr = (bootloader_ce_header *)ce_data; + uint8_t ce_key[0x10]; + EXCRYPT_RC4_STATE rc4 = {0}; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + // the key for CE is calculated using the key used to encrypt CD + ExCryptHmacSha(cd_key, 0x10, hdr->key, sizeof(hdr->key), NULL, 0, NULL, 0, ce_key, sizeof(ce_key)); + ExCryptRc4Key(&rc4, ce_key, sizeof(ce_key)); + + // 0x20 = sizeof(bootloader_header), sizeof(hdr->key) - all content after &target_address is encrypted + ExCryptRc4Ecb(&rc4, (uint8_t *)&hdr->target_address, size_aligned - 0x20); + + // set the key to all 00s so we know it's not encrypted + memset(hdr->key, 0, sizeof(hdr->key)); +} + +void ce_calculate_rotsum(uint8_t *ce_data, uint8_t *sha_out) { + bootloader_ce_header *hdr = (bootloader_ce_header *)ce_data; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + ExCryptRotSumSha((uint8_t *)hdr, 0x10, (uint8_t *)&hdr->target_address, size_aligned - 0x20, sha_out, 0x14); +} + +bool ce_decompress(uint8_t *ce_data, uint8_t *output_buf) { + bootloader_ce_header *hdr = (bootloader_ce_header *)ce_data; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + // allocate a buffer to store the compressed output + uint8_t *compressed_out = malloc(size_aligned); + memset(compressed_out, 0, size_aligned); + if (compressed_out == NULL) { + printf("! error ! failed to allocate compressed output buffer!\n"); + return false; + } + + uint32_t compressed_size = 0; + if (!get_full_compressed_buffer((uint8_t *)(hdr + 1), size_aligned, compressed_out, + BE(hdr->uncompressed_size), &compressed_size)) { + free(compressed_out); + return false; + } + + // decompress the combined compressed input + int r = lzx_decompress(compressed_out, compressed_size, output_buf, BE(hdr->uncompressed_size), 0x20000, NULL, 0); + + if (r != 0) { + // newline here since the lzx library doesn't newline + printf("\n! error ! lzx_decompress returned error code %i\n", r); + free(compressed_out); + return false; + } + + free(compressed_out); + + return true; +} diff --git a/source/cf-handler.c b/source/cf-handler.c new file mode 100644 index 0000000..5210833 --- /dev/null +++ b/source/cf-handler.c @@ -0,0 +1,85 @@ +/* + cf-handler.c - Handling for Xbox 360 CF/6BL bootloader stages. + Copyright 2024 Emma https://ipg.gay/ + + This file is part of xenon-bltool. + + xenon-bltool 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, version 2 of + the License. + + xenon-bltool 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 xenon-bltool. + If not, see . +*/ + +#include +#include +#include +#include +#include "1bl-keys.h" +#include "utility.h" +#include "excrypt.h" +#include "xenon-bootloader.h" + +bool cf_is_decrypted(uint8_t *cf_data) { + bootloader_cf_header *hdr = (bootloader_cf_header *)cf_data; + // this might be a naive assumption, but i think this is always all zeros. + return (hdr->pairing[0] == 0x00); +} + +void cf_calculate_rotsum(uint8_t *cf_data, uint8_t *sha_out) { + bootloader_cf_header *hdr = (bootloader_cf_header *)cf_data; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + ExCryptRotSumSha((uint8_t *)hdr, 0x20, (uint8_t *)&hdr->cg_hmac, size_aligned - 0x330, sha_out, 0x14); +} + +bool cf_verify_signature(uint8_t *cf_data) { + bootloader_cf_header *hdr = (bootloader_cf_header *)cf_data; + uint8_t cf_hash[0x14]; + cf_calculate_rotsum(cf_data, cf_hash); + + BOOL result = ExCryptBnQwBeSigVerify(&hdr->signature, cf_hash, "XBOX_ROM_6", (EXCRYPT_RSA *)rsa_1bl); + return (result == 1); +} + +void cf_print_info(uint8_t *cf_data) { + bootloader_cf_header *hdr = (bootloader_cf_header *)cf_data; + char *indicator = (hdr->header.magic & 0xF000 == 0x5000) ? "SF" : "CF"; + printf("%s version: %i\n", indicator, BE16(hdr->header.version)); + printf("%s size: 0x%x\n", indicator, BE(hdr->header.size)); + printf("%s entrypoint: 0x%x\n", indicator, BE(hdr->header.entrypoint)); + printf("%s base version: %i\n", indicator, BE16(hdr->base_ver)); + printf("%s target version: %i\n", indicator, BE16(hdr->target_ver)); + printf("%s-G size: 0x%x\n", indicator, BE(hdr->cg_size)); + if (cf_is_decrypted(cf_data)) { + printf("%s-G key: ", indicator); + hexdump(hdr->cg_hmac, sizeof(hdr->cg_hmac)); + printf("%s-G checksum: ", indicator); + hexdump(hdr->cg_hash, sizeof(hdr->cg_hash)); + printf("%s signature: %s\n", indicator, cf_verify_signature(cf_data) ? "signed" : "invalid"); + } else { + printf("%s is encrypted\n", indicator); + } +} + +void cf_decrypt(uint8_t *cf_data, uint8_t *cpu_or_1bl_key) { + bootloader_cf_header *hdr = (bootloader_cf_header *)cf_data; + uint8_t cf_key[0x10]; + EXCRYPT_RC4_STATE rc4 = {0}; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + // the key is the CPU key for decrypting from CD or 1bl key for decrypting from HV + ExCryptHmacSha(cpu_or_1bl_key, 0x10, hdr->key, sizeof(hdr->key), NULL, 0, NULL, 0, cf_key, sizeof(cf_key)); + ExCryptRc4Key(&rc4, cf_key, sizeof(cf_key)); + + // 0x30 = sizeof(bootloader_header), sizeof(hdr->key), and the version info - all content after &pairing is encrypted + ExCryptRc4Ecb(&rc4, (uint8_t *)&hdr->pairing, size_aligned - 0x30); + + // set the key to all 00s so we know it's not encrypted + memset(hdr->key, 0, sizeof(hdr->key)); +} diff --git a/source/cg-handler.c b/source/cg-handler.c new file mode 100644 index 0000000..26847df --- /dev/null +++ b/source/cg-handler.c @@ -0,0 +1,106 @@ +/* + cg-handler.c - Handling for Xbox 360 CG/7BL bootloader stages. + Copyright 2024 Emma https://ipg.gay/ + + This file is part of xenon-bltool. + + xenon-bltool 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, version 2 of + the License. + + xenon-bltool 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 xenon-bltool. + If not, see . +*/ + +#include +#include +#include +#include +#include "utility.h" +#include "excrypt.h" +#include "xenon-bootloader.h" +#include "lzx-delta.h" + +bool cg_is_decrypted(uint8_t *cg_data) { + bootloader_cg_header *hdr = (bootloader_cg_header *)cg_data; + // we make sure the size is page aligned. + return ((BE(hdr->original_size) & 0xFFF) == 0x000); +} + +void cg_print_info(uint8_t *cg_data) { + bootloader_cg_header *hdr = (bootloader_cg_header *)cg_data; + char *indicator = (hdr->header.magic & 0xF000 == 0x5000) ? "SG" : "CG"; + printf("%s version: %i\n", indicator, BE16(hdr->header.version)); + printf("%s size: 0x%x\n", indicator, BE(hdr->header.size)); + if (cg_is_decrypted(cg_data)) { + printf("%s base size: 0x%x\n", indicator, BE(hdr->original_size)); + printf("%s base hash: ", indicator); + hexdump(hdr->original_hash, sizeof(hdr->original_hash)); + printf("%s target size: 0x%x\n", indicator, BE(hdr->new_size)); + printf("%s target hash: ", indicator); + hexdump(hdr->new_hash, sizeof(hdr->new_hash)); + } else { + printf("%s is encrypted\n", indicator); + } +} + +void cg_decrypt(uint8_t *cg_data, uint8_t *cg_hmac) { + bootloader_cg_header *hdr = (bootloader_cg_header *)cg_data; + uint8_t cg_key[0x10]; + EXCRYPT_RC4_STATE rc4 = {0}; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + // key to decrypt is contained in the CF + ExCryptHmacSha(cg_hmac, 0x10, hdr->key, sizeof(hdr->key), NULL, 0, NULL, 0, cg_key, sizeof(cg_key)); + ExCryptRc4Key(&rc4, cg_key, sizeof(cg_key)); + + // 0x20 = sizeof(bootloader_header), sizeof(hdr->key) - all content after &original_size is encrypted + ExCryptRc4Ecb(&rc4, (uint8_t *)&hdr->original_size, size_aligned - 0x20); + + // set the key to all 00s so we know it's not encrypted + memset(hdr->key, 0, sizeof(hdr->key)); +} + +void cg_calculate_rotsum(uint8_t *cg_data, uint8_t *sha_out) { + bootloader_cg_header *hdr = (bootloader_cg_header *)cg_data; + uint32_t size_aligned = BE(hdr->header.size) + 0xF & 0xFFFFFFF0; + + ExCryptRotSumSha((uint8_t *)hdr, 0x10, (uint8_t *)&hdr->original_size, size_aligned - 0x20, sha_out, 0x14); +} + +bool cg_apply_patch(uint8_t *cg_data, uint8_t *base_data, uint8_t *output_buf) { + bootloader_cg_header *hdr = (bootloader_cg_header *)cg_data; + uint32_t size_of_compressed = BE(hdr->header.size) - sizeof(bootloader_cg_header); + + // validate the integrity of the base kernel + uint8_t base_kernel_hash[0x14]; + ExCryptSha(base_data, BE(hdr->original_size), NULL, 0, NULL, 0, base_kernel_hash, sizeof(base_kernel_hash)); + if (memcmp(base_kernel_hash, hdr->original_hash, 0x14) != 0) { + printf("! error ! base kernel hash did not match expected.\n"); + return false; + } + + // copy the base kernel's data to the output buffer and then decompress into it + memcpy(output_buf, base_data, BE(hdr->original_size)); + int r = lzxdelta_apply_patch((bootloader_delta_block *)(hdr + 1), size_of_compressed, 0x8000, output_buf); + + if (r != 0) { + // newline here since the lzx library doesn't newline + printf("\n! error ! lzxdelta_apply_patch returned error code %i\n", r); + return false; + } + + // verify the hash of the resulting kernel + uint8_t updated_kernel_hash[0x14]; + ExCryptSha(output_buf, BE(hdr->new_size), NULL, 0, NULL, 0, updated_kernel_hash, sizeof(updated_kernel_hash)); + if (memcmp(updated_kernel_hash, hdr->new_hash, 0x14) != 0) { + printf("! error ! updated kernel hash did not match expected.\n"); + return false; + } + + return true; +} diff --git a/source/compression-handler.c b/source/compression-handler.c new file mode 100644 index 0000000..7dc4370 --- /dev/null +++ b/source/compression-handler.c @@ -0,0 +1,66 @@ +/* + compression-handler.c - Utility file for working with compressed Xbox 360 bootloaders. + Copyright 2024 Emma https://ipg.gay/ + + This file is part of xenon-bltool. + + xenon-bltool 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, version 2 of + the License. + + xenon-bltool 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 xenon-bltool. + If not, see . +*/ + +#include +#include +#include +#include +#include "xenon-bootloader.h" + +/* + Our lzx_decompress from memory function (taken from Xenia) requires a + contiguous buffer of compressed data. CE/SE and the extender bootloader blob + are both compressed in a way that defines the compressed block size and + decompressed block size (almost always 0x8000). + This function takes those compressed blocks and puts them all together. +*/ +bool get_full_compressed_buffer(uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t decompressed_size, uint32_t *compressed_size) { + uint32_t decompressed_size_parsed = 0; + uint32_t compressed_size_parsed = 0; + + // loop until we've parsed enough blocks to match the compressed size + while (decompressed_size_parsed < decompressed_size) { + bootloader_compression_block *block = (bootloader_compression_block *)in_buf; + + // update the amount of data we've parsed + decompressed_size_parsed += BE16(block->decompressed_size); + compressed_size_parsed += BE16(block->compressed_size); + + // some sanity check to make sure we aren't going to overflow or anything + if (compressed_size_parsed >= in_size) { + printf("! error ! compressed size larger than output size\n"); + return false; + } + memcpy(out_buf, (uint8_t *)(block + 1), BE16(block->compressed_size)); + + // progress the input and output buffers + in_buf += sizeof(bootloader_compression_block) + BE16(block->compressed_size); + out_buf += BE16(block->compressed_size); + } + + // make sure we've got the expected amount of decompressed data + if (decompressed_size_parsed > decompressed_size) { + printf("! error ! decompressed size larger than expected\n"); + return false; + } + + if (compressed_size != NULL) + *compressed_size = compressed_size_parsed; + + return true; +} \ No newline at end of file diff --git a/source/lzx-delta.c b/source/lzx-delta.c new file mode 100644 index 0000000..161784c --- /dev/null +++ b/source/lzx-delta.c @@ -0,0 +1,202 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "lzx.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "utility.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#include "lzx-delta.h" +#include "xenon-bootloader.h" + +typedef struct mspack_memory_file_t { + struct mspack_system sys; + void* buffer; + uint32_t buffer_size; + uint32_t offset; +} mspack_memory_file; + +mspack_memory_file* mspack_memory_open(struct mspack_system* sys, void* buffer, + const size_t buffer_size) { + if (buffer_size >= INT_MAX) { + return NULL; + } + mspack_memory_file* memfile = + (mspack_memory_file*)calloc(1, sizeof(mspack_memory_file)); + if (!memfile) { + return NULL; + } + memfile->buffer = buffer; + memfile->buffer_size = (uint32_t)buffer_size; + memfile->offset = 0; + return memfile; +} + +void mspack_memory_close(mspack_memory_file* file) { + mspack_memory_file* memfile = (mspack_memory_file*)file; + free(memfile); +} + +int mspack_memory_read(struct mspack_file* file, void* buffer, int chars) { + mspack_memory_file* memfile = (mspack_memory_file*)file; + const uint32_t remaining = memfile->buffer_size - memfile->offset; + const uint32_t total = MIN(chars, remaining); + memcpy(buffer, (uint8_t*)memfile->buffer + memfile->offset, total); + memfile->offset += total; + return (int)total; +} + +int mspack_memory_write(struct mspack_file* file, void* buffer, int chars) { + mspack_memory_file* memfile = (mspack_memory_file*)file; + const uint32_t remaining = memfile->buffer_size - memfile->offset; + const uint32_t total = MIN(chars, remaining); + memcpy((uint8_t*)memfile->buffer + memfile->offset, buffer, total); + memfile->offset += total; + return (int)total; +} + +void* mspack_memory_alloc(struct mspack_system* sys, size_t chars) { + return calloc(chars, 1); +} + +void mspack_memory_free(void* ptr) { free(ptr); } + +void mspack_memory_copy(void* src, void* dest, size_t chars) { + memcpy(dest, src, chars); +} + +struct mspack_system* mspack_memory_sys_create() { + struct mspack_system* sys = (struct mspack_system*)calloc(1, sizeof(struct mspack_system)); + if (!sys) { + return NULL; + } + sys->read = mspack_memory_read; + sys->write = mspack_memory_write; + sys->alloc = mspack_memory_alloc; + sys->free = mspack_memory_free; + sys->copy = mspack_memory_copy; + return sys; +} + +void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); } + +bool bit_scan_forward(uint32_t v, uint32_t* out_first_set_index) { + int i = __builtin_ffs(v); + *out_first_set_index = i - 1; + return i != 0; +} + +int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest, + size_t dest_len, uint32_t window_size, void* window_data, + size_t window_data_len) { + int result_code = 1; + + uint32_t window_bits; + if (!bit_scan_forward(window_size, &window_bits)) { + return result_code; + } + + struct mspack_system* sys = mspack_memory_sys_create(); + mspack_memory_file* lzxsrc = + mspack_memory_open(sys, (void*)lzx_data, lzx_len); + mspack_memory_file* lzxdst = mspack_memory_open(sys, dest, dest_len); + struct lzxd_stream* lzxd = lzxd_init(sys, (struct mspack_file*)lzxsrc, (struct mspack_file*)lzxdst, + window_bits, 0, 0x8000, (uint32_t)dest_len, 0); + + if (lzxd) { + if (window_data) { + // zero the window and then copy window_data to the end of it + int padding_len = window_size - window_data_len; + memset(&lzxd->window[0], 0, padding_len); + memcpy(&lzxd->window[padding_len], window_data, window_data_len); + // TODO(gibbed): should this be set regardless if source window data is + // available or not? + lzxd->ref_data_size = window_size; + } + + result_code = lzxd_decompress(lzxd, (uint32_t)dest_len); + + lzxd_free(lzxd); + lzxd = NULL; + } + + if (lzxsrc) { + mspack_memory_close(lzxsrc); + lzxsrc = NULL; + } + + if (lzxdst) { + mspack_memory_close(lzxdst); + lzxdst = NULL; + } + + if (sys) { + mspack_memory_sys_destroy(sys); + sys = NULL; + } + + return result_code; +} + +int lzxdelta_apply_patch(bootloader_delta_block* patch, size_t patch_len, + uint32_t window_size, void* dest) { + uint8_t* patch_end = (char*)patch + patch_len; + bootloader_delta_block* cur_patch = patch; + + while (patch_end > (uint8_t*)cur_patch) { + int patch_sz = 0; // 0 byte patches need us to remove 4 byte from next + // patch addr because of patch_data field + if (BE16(cur_patch->compressed_size) == 0 && BE16(cur_patch->decompressed_size) == 0 && + BE(cur_patch->new_addr) == 0 && BE(cur_patch->old_addr) == 0) + break; + switch (BE16(cur_patch->compressed_size)) { + case 0: // fill with 0 + memset((char*)dest + BE(cur_patch->new_addr), 0, + BE16(cur_patch->decompressed_size)); + break; + case 1: // copy from old -> new + memcpy((char*)dest + BE(cur_patch->new_addr), + (char*)dest + BE(cur_patch->old_addr), + BE16(cur_patch->decompressed_size)); + break; + default: // delta patch + patch_sz = + BE16(cur_patch->compressed_size); // -4 because of patch_data field + + int result = lzx_decompress( + (char*)(cur_patch + 1), BE16(cur_patch->compressed_size), + (char*)dest + BE(cur_patch->new_addr), BE16(cur_patch->decompressed_size), + window_size, (char*)dest + BE(cur_patch->old_addr), + BE16(cur_patch->decompressed_size)); + + if (result) { + return result; + } + break; + } + + cur_patch++; + cur_patch = (bootloader_delta_block*)(((char*)cur_patch) + + patch_sz); // TODO: make this less ugly + } + + return 0; +}