From de31f9cb2b1db470f398fa1c31bfc8d7870dd2c0 Mon Sep 17 00:00:00 2001 From: ApacheThunder Date: Wed, 27 Nov 2024 21:50:32 -0600 Subject: [PATCH] 1.3 Refactor * Added DSOnei kernel to included nds files for Stage2 menu. * Added N-Card rom dump to included nds files for Stage2 menu. * Added CycloDS, and DSTWo bootloader dumps to included nds files for Stage2 menu. * DSTwo now boots correctly from cart launcher. * R4 SDHC Gold and other similar DEMON time bomb DSTTi clones now boot correctly from cart launcher. * Added back option for enabling/disabling TWL ram. * Added fixes to allow DS only carts to run with TWL ram enabled. * Initial modcrypt code added for TWL carts. Currently works in emulation however TWL carts will fail to boot on hardware (when twl mode, ram, etc is enabled). * If TWL mode and ram is enabled, cart loader will now load the DSi extended binaries into ram. Currently however they will only boot in emulation. Have not resolved why it's not working on hardware yet. * Stage2 menu now allowed to load dsi extended binaries of SRLs if TWL mode and TWL ram is enabled. Booting rom dumps as a method of booting into TWL carts is confirmed working. At least for System Flaw it does. :D * Despite the improvents Acekard 2i still appears to require using the stage2 menu to boot into. * Fixes that allowed Demon timebomb carts to boot from cart launcher/autoboot may allow other non working carts to work. Further testing needed. --- .gitignore | 2 + BootLoader/Makefile | 7 +- BootLoader/load.ld | 4 +- BootLoader/source/clear_cache.arm9.s | 23 + BootLoader/source/common.h | 203 +- BootLoader/source/encryption.c | 10 +- BootLoader/source/encryption.h | 2 +- BootLoader/source/key1.h | 264 ++ BootLoader/source/key2.h | 350 +++ BootLoader/source/launch_ds_crt0.s | 2 +- BootLoader/source/main.arm7.c | 296 +- BootLoader/source/main.arm9.c | 225 +- BootLoader/source/miniConsole.h | 50 +- BootLoader/source/modcrypt/aes.c | 1165 ++++++++ BootLoader/source/modcrypt/aes.h | 139 + BootLoader/source/modcrypt/bignum.c | 2452 +++++++++++++++++ BootLoader/source/modcrypt/bignum.h | 761 +++++ BootLoader/source/modcrypt/bn_mul.h | 887 ++++++ BootLoader/source/modcrypt/config.h | 5 + BootLoader/source/modcrypt/crypto.c | 125 + BootLoader/source/modcrypt/crypto.h | 70 + BootLoader/source/modcrypt/dsi.c | 346 +++ BootLoader/source/modcrypt/dsi.h | 105 + BootLoader/source/modcrypt/f_xy.c | 50 + BootLoader/source/modcrypt/f_xy.h | 36 + BootLoader/source/modcrypt/padlock.h | 98 + BootLoader/source/modcrypt/u128_math.c | 106 + BootLoader/source/modcrypt/u128_math.h | 38 + BootLoader/source/read_bios.h | 21 - BootLoader/source/read_bios.s | 54 - BootLoader/source/read_card.c | 258 +- BootLoader/source/read_card.h | 6 +- BootLoader/source/reset.arm7.s | 2 +- BootLoader/source/reset.arm9.s | 4 +- CartFiles/NTR_Launcher/CycloDS.nds | Bin 0 -> 131072 bytes CartFiles/NTR_Launcher/DSONEi.nds | Bin 0 -> 1412160 bytes CartFiles/NTR_Launcher/DSTwo.nds | Bin 32848 -> 131072 bytes CartFiles/NTR_Launcher/NCARD.nds | Bin 0 -> 131072 bytes CartFiles/NTR_Launcher/NTR_Launcher.ini | 5 +- CartFiles/NTR_Launcher/R4iGold_Launcher.nds | Bin 71248 -> 71248 bytes CartFiles/NTR_Launcher/R4i_SDHC_AVRJ.nds | Bin 35424 -> 38976 bytes Makefile | 7 +- arm7/Makefile | 4 +- arm7/source/main.c | 2 +- arm9/Makefile | 2 +- arm9/common/{audio.cpp => audio.twl.cpp} | 0 arm9/common/{crc.c => crc.twl.c} | 0 arm9/common/defaultBanners.s | 3 +- .../common/{encryption.c => encryption.twl.c} | 0 arm9/common/{inifile.cpp => inifile.twl.cpp} | 0 arm9/common/launcherData.h | 28 +- arm9/common/{nds_card.c => nds_card.twl.c} | 10 +- arm9/common/ndsheaderbanner.h | 1 + arm9/common/{nitrofs.c => nitrofs.twl.c} | 0 arm9/common/read_card.h | 6 +- arm9/common/{read_card.c => read_card.twl.c} | 117 +- arm9/common/tonccpy.itcm.c | 126 + arm9/hbmenu/{args.cpp => args.twl.cpp} | 0 arm9/hbmenu/file_browse.h | 3 +- .../{file_browse.cpp => file_browse.twl.cpp} | 24 +- arm9/hbmenu/{hbmenu.cpp => hbmenu.twl.cpp} | 111 +- .../{iconTitle.cpp => iconTitle.twl.cpp} | 60 +- arm9/hbmenu/nds_loader_arm9.c | 72 +- arm9/include/dsCardInvalid.bin | Bin 0 -> 2112 bytes .../{bootsplash.cpp => bootsplash.twl.cpp} | 0 .../{debugConsole.c => debugConsole.twl.c} | 0 arm9/source/launch_engine.c | 39 +- arm9/source/main.cpp | 46 +- ndsbootloader/source/bios.s | 13 - ndsbootloader/source/biosCalls.s | 233 ++ ndsbootloader/source/boot.c | 440 ++- ndsbootloader/source/boot.h | 133 +- .../common => ndsbootloader/source}/tonccpy.c | 72 +- ndsbootloader/source/tonccpy.h | 43 + 74 files changed, 8985 insertions(+), 781 deletions(-) create mode 100644 BootLoader/source/key1.h create mode 100644 BootLoader/source/key2.h create mode 100644 BootLoader/source/modcrypt/aes.c create mode 100644 BootLoader/source/modcrypt/aes.h create mode 100644 BootLoader/source/modcrypt/bignum.c create mode 100644 BootLoader/source/modcrypt/bignum.h create mode 100644 BootLoader/source/modcrypt/bn_mul.h create mode 100644 BootLoader/source/modcrypt/config.h create mode 100644 BootLoader/source/modcrypt/crypto.c create mode 100644 BootLoader/source/modcrypt/crypto.h create mode 100644 BootLoader/source/modcrypt/dsi.c create mode 100644 BootLoader/source/modcrypt/dsi.h create mode 100644 BootLoader/source/modcrypt/f_xy.c create mode 100644 BootLoader/source/modcrypt/f_xy.h create mode 100644 BootLoader/source/modcrypt/padlock.h create mode 100644 BootLoader/source/modcrypt/u128_math.c create mode 100644 BootLoader/source/modcrypt/u128_math.h delete mode 100755 BootLoader/source/read_bios.h delete mode 100755 BootLoader/source/read_bios.s create mode 100644 CartFiles/NTR_Launcher/CycloDS.nds create mode 100644 CartFiles/NTR_Launcher/DSONEi.nds create mode 100644 CartFiles/NTR_Launcher/NCARD.nds rename arm9/common/{audio.cpp => audio.twl.cpp} (100%) rename arm9/common/{crc.c => crc.twl.c} (100%) rename arm9/common/{encryption.c => encryption.twl.c} (100%) rename arm9/common/{inifile.cpp => inifile.twl.cpp} (100%) rename arm9/common/{nds_card.c => nds_card.twl.c} (87%) rename arm9/common/{nitrofs.c => nitrofs.twl.c} (100%) rename arm9/common/{read_card.c => read_card.twl.c} (87%) create mode 100644 arm9/common/tonccpy.itcm.c rename arm9/hbmenu/{args.cpp => args.twl.cpp} (100%) rename arm9/hbmenu/{file_browse.cpp => file_browse.twl.cpp} (92%) rename arm9/hbmenu/{hbmenu.cpp => hbmenu.twl.cpp} (64%) rename arm9/hbmenu/{iconTitle.cpp => iconTitle.twl.cpp} (86%) create mode 100644 arm9/include/dsCardInvalid.bin rename arm9/source/{bootsplash.cpp => bootsplash.twl.cpp} (100%) rename arm9/source/{debugConsole.c => debugConsole.twl.c} (100%) delete mode 100644 ndsbootloader/source/bios.s create mode 100644 ndsbootloader/source/biosCalls.s rename {arm9/common => ndsbootloader/source}/tonccpy.c (66%) create mode 100644 ndsbootloader/source/tonccpy.h diff --git a/.gitignore b/.gitignore index c6e1d50..8605dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.app *.exe *.cia +*.rar +*.zip /*.nds *.app *.tmd diff --git a/BootLoader/Makefile b/BootLoader/Makefile index f8f4061..e55d40a 100755 --- a/BootLoader/Makefile +++ b/BootLoader/Makefile @@ -14,8 +14,8 @@ endif #--------------------------------------------------------------------------------- TARGET := load BUILD := build -SOURCES := source source/patches -INCLUDES := build ../include +SOURCES := source source/patches source/modcrypt +INCLUDES := build ../include source/modcrypt DATA := ../data SPECS := specs @@ -24,10 +24,9 @@ SPECS := specs #--------------------------------------------------------------------------------- ARCH := -mthumb-interwork -march=armv4t -mtune=arm7tdmi -CFLAGS := -g -Wall -O2 \ +CFLAGS := -g -Wall -Wextra -Werror -O2 \ -fomit-frame-pointer \ -ffast-math \ - -Wall -Wextra -Werror \ $(ARCH) CFLAGS += $(INCLUDE) -DARM7 diff --git a/BootLoader/load.ld b/BootLoader/load.ld index 1c8efa1..07333db 100755 --- a/BootLoader/load.ld +++ b/BootLoader/load.ld @@ -3,10 +3,8 @@ OUTPUT_ARCH(arm) ENTRY(_start) MEMORY { - vram : ORIGIN = 0x06020000, LENGTH = 128K - /* arm9ram : ORIGIN = 0x026FD800, LENGTH = 4K Used for the ARM9's functions */ - arm9ram : ORIGIN = 0x027FC800, LENGTH = 4K /* Used for the ARM9's functions */ + arm9ram : ORIGIN = 0x02FFD000, LENGTH = 4K /* Used for the ARM9's functions */ } __vram_start = ORIGIN(vram); diff --git a/BootLoader/source/clear_cache.arm9.s b/BootLoader/source/clear_cache.arm9.s index 9d37fe9..49ec8bb 100644 --- a/BootLoader/source/clear_cache.arm9.s +++ b/BootLoader/source/clear_cache.arm9.s @@ -41,5 +41,28 @@ BEGIN_ASM_FUNC arm9_clearCache mcr p15, 0, r3, c7, c6, 0 @ Flush DCache mcr p15, 0, r3, c7, c10, 4 @ empty write buffer + mcr p15, 0, r3, c3, c0, 0 @ disable write buffer (def = 0) + + mcr p15, 0, r3, c2, c0, 0 @ disable DTCM and protection unit + + mcr p15, 0, r3, c6, c0, 0 @ disable protection unit 0 (def = 0) + mcr p15, 0, r3, c6, c1, 0 @ disable protection unit 1 (def = 0) + mcr p15, 0, r3, c6, c2, 0 @ disable protection unit 2 (def = 0) + mcr p15, 0, r3, c6, c3, 0 @ disable protection unit 3 (def = 0) + mcr p15, 0, r3, c6, c4, 0 @ disable protection unit 4 (def = ?) + mcr p15, 0, r3, c6, c5, 0 @ disable protection unit 5 (def = ?) + mcr p15, 0, r3, c6, c6, 0 @ disable protection unit 6 (def = ?) + mcr p15, 0, r3, c6, c7, 0 @ disable protection unit 7 (def = ?) + + mcr p15, 0, r3, c5, c0, 3 @ IAccess + mcr p15, 0, r3, c5, c0, 2 @ DAccess + + mov r3, #0x00800000 + add r3, r3, #0x00A + mcr p15, 0, r3, c9, c1, 0 @ DTCM base (def = 0x0080000A) ??? + + mov r3, #0x0000000C + mcr p15, 0, r3, c9, c1, 1 @ ITCM base (def = 0x0000000C) ??? + bx lr diff --git a/BootLoader/source/common.h b/BootLoader/source/common.h index 7de1577..a678eed 100755 --- a/BootLoader/source/common.h +++ b/BootLoader/source/common.h @@ -25,46 +25,38 @@ #define resetCpu() \ __asm volatile("swi 0x000000") - -typedef struct sLauncherSettings { - u8 language; - u8 scfgUnlock; - u8 twlMode; - u8 twlCLK; - u8 twlVRAM; - u8 debugMode; - u8 fastBoot; - u8 unused2; -} tLauncherSettings; - -extern volatile tLauncherSettings* launchData; -extern volatile int language; -extern volatile bool scfgUnlock; -extern volatile bool twlMode; -extern volatile bool twlCLK; -extern volatile bool debugMode; - -#define CartHeaderCopy 0x02000000 -#define CartChipIDCopy 0x02000180 - -enum ERROR_CODES { - ERR_NONE = 0x00, - ERR_STS_CLR_MEM = 0x01, - ERR_STS_LOAD_BIN = 0x02, - ERR_STS_HOOK_BIN = 0x03, - ERR_STS_START = 0x04, + +#include "../../arm9/common/launcherData.h" + +ALIGN(4) extern volatile u16 arm9_errorCode; +ALIGN(4) extern volatile tLauncherSettings* launchData; +ALIGN(4) extern volatile u16 language; +ALIGN(4) extern volatile u16 scfgUnlock; +ALIGN(4) extern volatile u16 twlMode; +// ALIGN(4) extern volatile u16 twlVRAM; +ALIGN(4) extern volatile u16 twlRAM; +ALIGN(4) extern volatile u16 twlCLK; +ALIGN(4) extern volatile u16 isTWLSRL; +ALIGN(4) extern volatile u16 debugMode; + +ALIGN(4) enum ERROR_CODES { + ERR_NONE = (u16)0x00, + ERR_STS_STARTBIN = (u16)0x01, + ERR_STS_CLR_MEM = (u16)0x02, + ERR_STS_LOAD_BIN = (u16)0x03, + ERR_STS_START = (u16)0x04, // initCard error codes: - ERR_LOAD_NORM = 0x11, - ERR_LOAD_OTHR = 0x12, - ERR_SEC_NORM = 0x13, - ERR_SEC_OTHR = 0x14, - ERR_LOGO_CRC = 0x15, - ERR_HEAD_CRC = 0x16, - ERR_STS_STARTBIN = 0x21, + ERR_LOAD_NORM = (u16)0x05, + ERR_LOAD_OTHR = (u16)0x06, + ERR_SEC_NORM = (u16)0x07, + ERR_SEC_OTHR = (u16)0x08, + ERR_HEAD_CRC = (u16)0x09, + ERR_UNKNOWN = (u16)0x10, + ERR_UNKNOWN2 = (u16)0x11 }; // Values fixed so they can be shared with ASM code -enum ARM9_STATE { +ALIGN(4) enum ARM9_STATE { ARM9_BOOT = 0, ARM9_START = 1, ARM9_RESET = 2, @@ -72,7 +64,7 @@ enum ARM9_STATE { ARM9_MEMCLR = 4 }; -enum ARM7_STATE { +ALIGN(4) enum ARM7_STATE { ARM7_BOOT = 0, ARM7_START = 1, ARM7_RESET = 2, @@ -84,27 +76,152 @@ enum ARM7_STATE { ARM7_ERR = 8 }; -extern volatile u32 arm9_errorCode; - -static inline void dmaFill(const void* src, void* dest, uint32 size) { +ALIGN(4) static inline void dmaFill(const void* src, void* dest, uint32 size) { DMA_SRC(3) = (uint32)src; DMA_DEST(3) = (uint32)dest; DMA_CR(3) = DMA_COPY_WORDS | DMA_SRC_FIX | (size>>2); while(DMA_CR(3) & DMA_BUSY); } -static inline void copyLoop (u32* dest, const u32* src, size_t size) { +ALIGN(4) static inline void copyLoop (u32* dest, const u32* src, size_t size) { do { *dest++ = *src++; } while (size -= 4); } -static inline void ipcSendState(uint8_t state) { +ALIGN(4) static inline void ipcSendState(uint8_t state) { REG_IPC_SYNC = (state & 0x0f) << 8; } -static inline uint8_t ipcRecvState(void) { +ALIGN(4) static inline uint8_t ipcRecvState(void) { return (uint8_t)(REG_IPC_SYNC & 0x0f); } +typedef struct sTWLHeader { + char gameTitle[12]; //!< 12 characters for the game title. + char gameCode[4]; //!< 4 characters for the game code. + char makercode[2]; //!< identifies the (commercial) developer. + u8 unitCode; //!< identifies the required hardware. + u8 deviceType; //!< type of device in the game card + u8 deviceSize; //!< capacity of the device (1 << n Mbit) + u8 reserved1[7]; + u8 twlHeaderSettings; + u8 jumpSettings; + u8 romversion; //!< version of the ROM. + u8 flags; //!< bit 2: auto-boot flag. + + u32 arm9romOffset; //!< offset of the arm9 binary in the nds file. + void *arm9executeAddress; //!< adress that should be executed after the binary has been copied. + void *arm9destination; //!< destination address to where the arm9 binary should be copied. + u32 arm9binarySize; //!< size of the arm9 binary. + + u32 arm7romOffset; //!< offset of the arm7 binary in the nds file. + void *arm7executeAddress; //!< adress that should be executed after the binary has been copied. + void *arm7destination; //!< destination address to where the arm7 binary should be copied. + u32 arm7binarySize; //!< size of the arm7 binary. + + u32 filenameOffset; //!< File Name Table (FNT) offset. + u32 filenameSize; //!< File Name Table (FNT) size. + u32 fatOffset; //!< File Allocation Table (FAT) offset. + u32 fatSize; //!< File Allocation Table (FAT) size. + + u32 arm9overlaySource; //!< File arm9 overlay offset. + u32 arm9overlaySize; //!< File arm9 overlay size. + u32 arm7overlaySource; //!< File arm7 overlay offset. + u32 arm7overlaySize; //!< File arm7 overlay size. + + u32 cardControl13; //!< Port 40001A4h setting for normal commands (used in modes 1 and 3) + u32 cardControlBF; //!< Port 40001A4h setting for KEY1 commands (used in mode 2) + u32 bannerOffset; //!< offset to the banner with icon and titles etc. + + u16 secureCRC16; //!< Secure Area Checksum, CRC-16. + + u16 readTimeout; //!< Secure Area Loading Timeout. + + u32 unknownRAM1; //!< ARM9 Auto Load List RAM Address (?) + u32 unknownRAM2; //!< ARM7 Auto Load List RAM Address (?) + + u32 bfPrime1; //!< Secure Area Disable part 1. + u32 bfPrime2; //!< Secure Area Disable part 2. + u32 romSize; //!< total size of the ROM. + + u32 headerSize; //!< ROM header size. + u32 zeros88[14]; + u8 gbaLogo[156]; //!< Nintendo logo needed for booting the game. + u16 logoCRC16; //!< Nintendo Logo Checksum, CRC-16. + u16 headerCRC16; //!< header checksum, CRC-16. + + u32 debugRomSource; //!< debug ROM offset. + u32 debugRomSize; //!< debug size. + u32 debugRomDestination; //!< debug RAM destination. + u32 offset_0x16C; //reserved? + + u8 zero[0x10]; + + u32 arm9MBK1; + u32 arm9MBK2; + u32 arm9MBK3; + u32 arm9MBK4; + u32 arm9MBK5; + u32 arm9MBK6; + u32 arm9MBK7; + u32 arm9MBK8; + u32 arm7MBK6; + u32 arm7MBK7; + u32 arm7MBK8; + u32 arm9MBKMaster; + + u32 region; + u32 accessControl; + u32 arm7SCFGSettings; + u16 dsi_unk1; + u8 dsi_unk2; + u8 dsi_flags; + + u32 arm9iromOffset; //!< offset of the arm9 binary in the nds file. + u32 arm9iexecuteAddress; + u32 arm9idestination; //!< destination address to where the arm9 binary should be copied. + u32 arm9ibinarySize; //!< size of the arm9 binary. + + u32 arm7iromOffset; //!< offset of the arm7 binary in the nds file. + u32 deviceListDestination; + u32 arm7idestination; //!< destination address to where the arm7 binary should be copied. + u32 arm7ibinarySize; //!< size of the arm7 binary. + + u8 zero2[0x20]; + + // 0x200 + // TODO: More DSi-specific fields. + u32 dsi1[0x10/4]; + u32 twlRomSize; + u32 dsi_unk3; + u32 dsi_unk4; + u32 dsi_unk5; + + u32 modCrypt1Offset; + u32 modcrypt1Size; + u32 modcrypt2Offset; + u32 modcrypt2Size; + + u32 dsi_tid; + u32 dsi_tid2; + u32 pubSavSize; + u32 prvSavSize; + + u8 reserved3[176]; + u8 age_ratings[0x10]; + + unsigned char hmac_arm9[16]; + unsigned char hmac_arm7[16]; + u8 hmac_digest_master[0x14]; + u8 hmac_icon_title[0x14]; + u8 hmac_arm9i[0x14]; + u8 hmac_arm7i[0x14]; + u8 reserved4[0x28]; + u8 hmac_arm9_no_secure[0x14]; + u8 reserved5[0xA4C]; + u8 debug_args[0x180]; + u8 rsa_signature[0x80]; +} tTWLHeader; + #endif // _COMMON_H diff --git a/BootLoader/source/encryption.c b/BootLoader/source/encryption.c index 37523ac..0634891 100755 --- a/BootLoader/source/encryption.c +++ b/BootLoader/source/encryption.c @@ -18,7 +18,9 @@ #include #include "encryption.h" -#include "read_bios.h" +#include "key1.h" +#include "key2.h" +#include "tonccpy.h" #define KEYSIZE 0x1048 @@ -84,7 +86,7 @@ void apply_keycode (u32 modulo) { crypt_64bit_up (&keycode[1]); crypt_64bit_up (&keycode[0]); - memset (scratch, 0, 8); + toncset (scratch, 0, 8); for (i = 0; i < 0x12; i+=1) { keybuf[i] = keybuf[i] ^ bswap_32bit (keycode[i % modulo]); @@ -96,8 +98,8 @@ void apply_keycode (u32 modulo) { } } -void init_keycode (u32 idcode, u32 level, u32 modulo) { - readBios ((u8*)keybuf, 0x30, KEYSIZE); +void init_keycode (u32 idcode, u32 level, u32 modulo, int iCardDevice) { + tonccpy ((u8*)keybuf, (iCardDevice ? gEncrDataTwl : gEncrData), KEYSIZE); keycode[0] = idcode; keycode[1] = idcode/2; keycode[2] = idcode*2; diff --git a/BootLoader/source/encryption.h b/BootLoader/source/encryption.h index 6eb27b4..0b947b3 100755 --- a/BootLoader/source/encryption.h +++ b/BootLoader/source/encryption.h @@ -20,7 +20,7 @@ #define ENCRYPTION_H #include -void init_keycode (u32 idcode, u32 level, u32 modulo); +void init_keycode (u32 idcode, u32 level, u32 modulo, int iCardDevice); void crypt_64bit_down (u32* ptr); void crypt_64bit_up (u32* ptr); diff --git a/BootLoader/source/key1.h b/BootLoader/source/key1.h new file mode 100644 index 0000000..5980e77 --- /dev/null +++ b/BootLoader/source/key1.h @@ -0,0 +1,264 @@ +const unsigned char gEncrData[] = +{ + 0x99,0xD5,0x20,0x5F,0x57,0x44,0xF5,0xB9,0x6E,0x19,0xA4,0xD9,0x9E,0x6A,0x5A,0x94, + 0xD8,0xAE,0xF1,0xEB,0x41,0x75,0xE2,0x3A,0x93,0x82,0xD0,0x32,0x33,0xEE,0x31,0xD5, + 0xCC,0x57,0x61,0x9A,0x37,0x06,0xA2,0x1B,0x79,0x39,0x72,0xF5,0x55,0xAE,0xF6,0xBE, + 0x5F,0x1B,0x69,0xFB,0xE5,0x9D,0xF1,0xE9,0xCE,0x2C,0xD9,0xA1,0x5E,0x32,0x05,0xE6, + 0xFE,0xD3,0xFE,0xCF,0xD4,0x62,0x04,0x0D,0x8B,0xF5,0xEC,0xB7,0x2B,0x60,0x79,0xBB, + 0x12,0x95,0x31,0x0D,0x6E,0x3F,0xDA,0x2B,0x88,0x84,0xF0,0xF1,0x3D,0x12,0x7E,0x25, + 0x45,0x22,0xF1,0xBB,0x24,0x06,0x1A,0x06,0x11,0xAD,0xDF,0x28,0x8B,0x64,0x81,0x34, + 0x2B,0xEB,0x33,0x29,0x99,0xAA,0xF2,0xBD,0x9C,0x14,0x95,0x9D,0x9F,0xF7,0xF5,0x8C, + 0x72,0x97,0xA1,0x29,0x9D,0xD1,0x5F,0xCF,0x66,0x4D,0x07,0x1A,0xDE,0xD3,0x4A,0x4B, + 0x85,0xC9,0xA7,0xA3,0x17,0x95,0x05,0x3A,0x3D,0x49,0x0A,0xBF,0x0A,0x89,0x8B,0xA2, + 0x4A,0x82,0x49,0xDD,0x27,0x90,0xF1,0x0B,0xE9,0xEB,0x1C,0x6A,0x83,0x76,0x45,0x05, + 0xBA,0x81,0x70,0x61,0x17,0x3F,0x4B,0xDE,0xAE,0xCF,0xAB,0x39,0x57,0xF2,0x3A,0x56, + 0x48,0x11,0xAD,0x8A,0x40,0xE1,0x45,0x3F,0xFA,0x9B,0x02,0x54,0xCA,0xA6,0x93,0xFB, + 0xEF,0x4D,0xFE,0x6F,0xA3,0xD8,0x87,0x9C,0x08,0xBA,0xD5,0x48,0x6A,0x8D,0x2D,0xFD, + 0x6E,0x15,0xF8,0x74,0xBD,0xBE,0x52,0x8B,0x18,0x22,0x8A,0x9E,0xFB,0x74,0x37,0x07, + 0x1B,0x36,0x6C,0x4A,0x19,0xBA,0x42,0x62,0xB9,0x79,0x91,0x10,0x7B,0x67,0x65,0x96, + 0xFE,0x02,0x23,0xE8,0xEE,0x99,0x8C,0x77,0x3E,0x5C,0x86,0x64,0x4D,0x6D,0x78,0x86, + 0xA5,0x4F,0x65,0xE2,0x1E,0xB2,0xDF,0x5A,0x0A,0xD0,0x7E,0x08,0x14,0xB0,0x71,0xAC, + 0xBD,0xDB,0x83,0x1C,0xB9,0xD7,0xA1,0x62,0xCD,0xC6,0x63,0x7C,0x52,0x69,0xC3,0xE6, + 0xBF,0x75,0xCE,0x12,0x44,0x5D,0x21,0x04,0xFA,0xFB,0xD3,0x3C,0x38,0x11,0x63,0xD4, + 0x95,0x85,0x41,0x49,0x46,0x09,0xF2,0x08,0x43,0x11,0xDC,0x1F,0x76,0xC0,0x15,0x6D, + 0x1F,0x3C,0x63,0x70,0xEA,0x87,0x80,0x6C,0xC3,0xBD,0x63,0x8B,0xC2,0x37,0x21,0x37, + 0xDC,0xEE,0x09,0x23,0x2E,0x37,0x6A,0x4D,0x73,0x90,0xF7,0x50,0x30,0xAC,0x1C,0x92, + 0x04,0x10,0x23,0x91,0x4F,0xD2,0x07,0xAA,0x68,0x3E,0x4F,0x9A,0xC9,0x64,0x60,0x6A, + 0xC8,0x14,0x21,0xF3,0xD6,0x22,0x41,0x12,0x44,0x24,0xCF,0xE6,0x8A,0x56,0xDD,0x0D, + 0x53,0x4D,0xE1,0x85,0x1E,0x8C,0x52,0x5A,0x9C,0x19,0x84,0xC2,0x03,0x57,0xF1,0x6F, + 0xE3,0x00,0xBE,0x58,0xF6,0x4C,0xED,0xD5,0x21,0x64,0x9C,0x1F,0xBE,0x55,0x03,0x3C, + 0x4A,0xDC,0xFF,0xAA,0xC9,0xDA,0xE0,0x5D,0x5E,0xBF,0xE6,0xDE,0xF5,0xD8,0xB1,0xF8, + 0xFF,0x36,0xB3,0xB9,0x62,0x67,0x95,0xDB,0x31,0x5F,0x37,0xED,0x4C,0x70,0x67,0x99, + 0x90,0xB5,0x18,0x31,0x6C,0x3D,0x99,0x99,0xE4,0x42,0xDA,0xD3,0x25,0x42,0x13,0xA0, + 0xAE,0xD7,0x70,0x6C,0xB1,0x55,0xCF,0xC7,0xD7,0x46,0xD5,0x43,0x61,0x17,0x3D,0x44, + 0x28,0xE9,0x33,0x85,0xD5,0xD0,0xA2,0x93,0xAA,0x25,0x12,0x1F,0xFB,0xC5,0x0B,0x46, + 0xF5,0x97,0x76,0x56,0x45,0xA6,0xBE,0x87,0xB1,0x94,0x6B,0xE8,0xB1,0xFE,0x33,0x99, + 0xAE,0x1F,0x3E,0x6C,0x39,0x71,0x1D,0x09,0x00,0x90,0x37,0xE4,0x10,0x3E,0x75,0x74, + 0xFF,0x8C,0x83,0x3B,0xB0,0xF1,0xB0,0xF9,0x01,0x05,0x47,0x42,0x95,0xF1,0xD6,0xAC, + 0x7E,0x38,0xE6,0x9E,0x95,0x74,0x26,0x3F,0xB4,0x68,0x50,0x18,0xD0,0x43,0x30,0xB4, + 0x4C,0x4B,0xE3,0x68,0xBF,0xE5,0x4D,0xB6,0x95,0x8B,0x0A,0xA0,0x74,0x25,0x32,0x77, + 0xCF,0xA1,0xF7,0x2C,0xD8,0x71,0x13,0x5A,0xAB,0xEA,0xC9,0x51,0xE8,0x0D,0xEE,0xEF, + 0xE9,0x93,0x7E,0x19,0xA7,0x1E,0x43,0x38,0x81,0x16,0x2C,0xA1,0x48,0xE3,0x73,0xCC, + 0x29,0x21,0x6C,0xD3,0x5D,0xCE,0xA0,0xD9,0x61,0x71,0x43,0xA0,0x15,0x13,0xB5,0x64, + 0x92,0xCF,0x2A,0x19,0xDC,0xAD,0xB7,0xA5,0x9F,0x86,0x65,0xF8,0x1A,0x9F,0xE7,0xFB, + 0xF7,0xFD,0xB8,0x13,0x6C,0x27,0xDB,0x6F,0xDF,0x35,0x1C,0xF7,0x8D,0x2C,0x5B,0x9B, + 0x12,0xAB,0x38,0x64,0x06,0xCC,0xDE,0x31,0xE8,0x4E,0x75,0x11,0x64,0xE3,0xFA,0xEA, + 0xEB,0x34,0x54,0xC2,0xAD,0x3F,0x34,0xEB,0x93,0x2C,0x7D,0x26,0x36,0x9D,0x56,0xF3, + 0x5A,0xE1,0xF6,0xB3,0x98,0x63,0x4A,0x9E,0x32,0x83,0xE4,0x9A,0x84,0x60,0x7D,0x90, + 0x2E,0x13,0x0E,0xEE,0x93,0x4B,0x36,0xA2,0x85,0xEC,0x16,0x38,0xE8,0x88,0x06,0x02, + 0xBF,0xF0,0xA0,0x3A,0xED,0xD7,0x6A,0x9A,0x73,0xE1,0x57,0xCF,0xF8,0x44,0xB8,0xDC, + 0x2E,0x23,0x59,0xD1,0xDF,0x95,0x52,0x71,0x99,0x61,0xA0,0x4B,0xD5,0x7F,0x6E,0x78, + 0xBA,0xA9,0xC5,0x30,0xD3,0x40,0x86,0x32,0x9D,0x32,0x0C,0x9C,0x37,0xB7,0x02,0x2F, + 0xBA,0x54,0x98,0xA9,0xC4,0x13,0x04,0xC9,0x8D,0xBE,0xC8,0xE7,0x5D,0x97,0x50,0x2E, + 0x93,0xD6,0x22,0x59,0x0C,0x27,0xBC,0x22,0x92,0xE0,0xA7,0x20,0x0F,0x93,0x6F,0x7F, + 0x4C,0x9F,0xD3,0xB5,0xA6,0x2A,0x0B,0x74,0x67,0x49,0x7D,0x10,0x26,0xCB,0xD1,0xC5, + 0x86,0x71,0xE7,0x8C,0xA0,0x9C,0xE9,0x5B,0xB2,0x1A,0xF6,0x01,0xEE,0x8C,0x9E,0x5E, + 0x83,0xF2,0x1A,0xDB,0xE6,0xE5,0xEA,0x84,0x59,0x76,0xD2,0x7C,0xF6,0x8D,0xA5,0x49, + 0x36,0x48,0xC2,0x16,0x52,0xBB,0x83,0xA3,0x74,0xB9,0x07,0x0C,0x3B,0xFF,0x61,0x28, + 0xE1,0x61,0xE9,0xE4,0xEF,0x6E,0x15,0xAA,0x4E,0xBA,0xE8,0x5D,0x05,0x96,0xBB,0x32, + 0x56,0xB0,0xFB,0x72,0x52,0x0F,0x0E,0xC8,0x42,0x25,0x65,0x76,0x89,0xAF,0xF2,0xDE, + 0x10,0x27,0xF0,0x01,0x4B,0x74,0xA7,0x97,0x07,0xD5,0x26,0x54,0x54,0x09,0x1F,0x82, + 0x0A,0x86,0x7D,0x30,0x39,0x0E,0xB3,0x26,0x9B,0x0B,0x57,0xBB,0x36,0x06,0x31,0xAF, + 0xFD,0x79,0xFC,0xD9,0x30,0x10,0x2B,0x0C,0xB3,0xE1,0x9B,0xD7,0x7B,0xDC,0x5F,0xEF, + 0xD2,0xF8,0x13,0x45,0x4D,0x47,0x75,0xBD,0x46,0x96,0x3C,0x7E,0x75,0xF3,0x3E,0xB5, + 0x67,0xC5,0x9A,0x3B,0xB0,0x5B,0x29,0x6B,0xDE,0x80,0x5B,0xC8,0x15,0x05,0xB1,0x31, + 0xB6,0xCE,0x49,0xDD,0xAD,0x84,0xB5,0xAE,0x60,0xDC,0x67,0x31,0x34,0x30,0xFE,0x4E, + 0xBD,0x80,0x2F,0xA6,0xBF,0x63,0x39,0x21,0x86,0xD9,0x35,0x7F,0x16,0x68,0x22,0x05, + 0x54,0xE9,0x90,0x26,0x8C,0x07,0x6C,0x51,0xA4,0x31,0x55,0xD7,0x09,0x07,0xA8,0x3E, + 0x2E,0x53,0x66,0xC1,0xF8,0xF2,0x7B,0xC4,0xF2,0x58,0xCF,0xF1,0x87,0xC5,0xA2,0xE7, + 0x27,0x8F,0x30,0x87,0x58,0xA0,0x64,0x62,0x23,0x18,0xB9,0x88,0x7C,0xFA,0xCE,0xC4, + 0x98,0xAE,0xAD,0x17,0xCC,0x4A,0x5B,0xF3,0xE9,0x48,0xD5,0x56,0xD3,0x0D,0xF2,0xC8, + 0x92,0x73,0x8C,0xDB,0xD7,0x2F,0x56,0xAC,0x81,0xF9,0x92,0x69,0x4D,0xC6,0x32,0xF6, + 0xE6,0xC0,0x8D,0x21,0xE2,0x76,0x80,0x61,0x11,0xBC,0xDC,0x6C,0x93,0xAF,0x19,0x69, + 0x9B,0xD0,0xBF,0xB9,0x31,0x9F,0x02,0x67,0xA3,0x51,0xEE,0x83,0x06,0x22,0x7B,0x0C, + 0xAB,0x49,0x42,0x40,0xB8,0xD5,0x01,0x7D,0xCE,0x5E,0xF7,0x55,0x53,0x39,0xC5,0x99, + 0x46,0xD8,0x87,0x9F,0xBA,0xF7,0x64,0xB4,0xE3,0x9A,0xFA,0xA1,0x6D,0x90,0x68,0x10, + 0x30,0xCA,0x8A,0x54,0xA7,0x9F,0x60,0xC3,0x19,0xF5,0x6B,0x0D,0x7A,0x51,0x98,0xE6, + 0x98,0x43,0x51,0xB4,0xD6,0x35,0xE9,0x4F,0xC3,0xDF,0x0F,0x7B,0xD6,0x2F,0x5C,0xBD, + 0x3A,0x15,0x61,0x19,0xF1,0x4B,0xCB,0xAA,0xDC,0x6D,0x64,0xC9,0xD3,0xC6,0x1E,0x56, + 0xEF,0x38,0x4C,0x50,0x71,0x86,0x75,0xCC,0x0D,0x0D,0x4E,0xE9,0x28,0xF6,0x06,0x5D, + 0x70,0x1B,0xAA,0xD3,0x45,0xCF,0xA8,0x39,0xAC,0x95,0xA6,0x2E,0xB4,0xE4,0x22,0xD4, + 0x74,0xA8,0x37,0x5F,0x48,0x7A,0x04,0xCC,0xA5,0x4C,0x40,0xD8,0x28,0xB4,0x28,0x08, + 0x0D,0x1C,0x72,0x52,0x41,0xF0,0x7D,0x47,0x19,0x3A,0x53,0x4E,0x58,0x84,0x62,0x6B, + 0x93,0xB5,0x8A,0x81,0x21,0x4E,0x0D,0xDC,0xB4,0x3F,0xA2,0xC6,0xFC,0xC9,0x2B,0x40, + 0xDA,0x38,0x04,0xE9,0x5E,0x5A,0x86,0x6B,0x0C,0x22,0x25,0x85,0x68,0x11,0x8D,0x7C, + 0x92,0x1D,0x95,0x55,0x4D,0xAB,0x8E,0xBB,0xDA,0xA6,0xE6,0xB7,0x51,0xB6,0x32,0x5A, + 0x05,0x41,0xDD,0x05,0x2A,0x0A,0x56,0x50,0x91,0x17,0x47,0xCC,0xC9,0xE6,0x7E,0xB5, + 0x61,0x4A,0xDB,0x73,0x67,0x51,0xC8,0x33,0xF5,0xDA,0x6E,0x74,0x2E,0x54,0xC3,0x37, + 0x0D,0x6D,0xAF,0x08,0xE8,0x15,0x8A,0x5F,0xE2,0x59,0x21,0xCD,0xA8,0xDE,0x0C,0x06, + 0x5A,0x77,0x6B,0x5F,0xDB,0x18,0x65,0x3E,0xC8,0x50,0xDE,0x78,0xE0,0xB8,0x82,0xB3, + 0x5D,0x4E,0x72,0x32,0x07,0x4F,0xC1,0x34,0x23,0xBA,0x96,0xB7,0x67,0x4E,0xA4,0x28, + 0x1E,0x34,0x62,0xEB,0x2D,0x6A,0x70,0xE9,0x2F,0x42,0xC4,0x70,0x4E,0x5A,0x31,0x9C, + 0xF9,0x5B,0x47,0x28,0xAA,0xDA,0x71,0x6F,0x38,0x1F,0xB3,0x78,0xC4,0x92,0x6B,0x1C, + 0x9E,0xF6,0x35,0x9A,0xB7,0x4D,0x0E,0xBF,0xCC,0x18,0x29,0x41,0x03,0x48,0x35,0x5D, + 0x55,0xD0,0x2B,0xC6,0x29,0xAF,0x5C,0x60,0x74,0x69,0x8E,0x5E,0x9B,0x7C,0xD4,0xBD, + 0x7B,0x44,0x64,0x7D,0x3F,0x92,0x5D,0x69,0xB6,0x1F,0x00,0x4B,0xD4,0x83,0x35,0xCF, + 0x7E,0x64,0x4E,0x17,0xAE,0x8D,0xD5,0x2E,0x9A,0x28,0x12,0x4E,0x2E,0x2B,0x49,0x08, + 0x5C,0xAE,0xC6,0x46,0x85,0xAE,0x41,0x61,0x1E,0x6F,0x82,0xD2,0x51,0x37,0x16,0x1F, + 0x0B,0xF6,0x59,0xA4,0x9A,0xCA,0x5A,0xAF,0x0D,0xD4,0x33,0x8B,0x20,0x63,0xF1,0x84, + 0x80,0x5C,0xCB,0xCF,0x08,0xB4,0xB9,0xD3,0x16,0x05,0xBD,0x62,0x83,0x31,0x9B,0x56, + 0x51,0x98,0x9F,0xBA,0xB2,0x5B,0xAA,0xB2,0x22,0x6B,0x2C,0xB5,0xD4,0x48,0xFA,0x63, + 0x2B,0x5F,0x58,0xFA,0x61,0xFA,0x64,0x09,0xBB,0x38,0xE0,0xB8,0x9D,0x92,0x60,0xA8, + 0x0D,0x67,0x6F,0x0E,0x37,0xF5,0x0D,0x01,0x9F,0xC2,0x77,0xD4,0xFE,0xEC,0xF1,0x73, + 0x30,0x39,0xE0,0x7D,0xF5,0x61,0x98,0xE4,0x2C,0x28,0x55,0x04,0x56,0x55,0xDB,0x2F, + 0x6B,0xEC,0xE5,0x58,0x06,0xB6,0x64,0x80,0x6A,0x2A,0x1A,0x4E,0x5B,0x0F,0xD8,0xC4, + 0x0A,0x2E,0x52,0x19,0xD9,0x62,0xF5,0x30,0x48,0xBE,0x8C,0x7B,0x4F,0x38,0x9B,0xA2, + 0xC3,0xAF,0xC9,0xD3,0xC7,0xC1,0x62,0x41,0x86,0xB9,0x61,0x21,0x57,0x6F,0x99,0x4F, + 0xC1,0xBA,0xCE,0x7B,0xB5,0x3B,0x4D,0x5E,0x8A,0x8B,0x44,0x57,0x5F,0x13,0x5F,0x70, + 0x6D,0x5B,0x29,0x47,0xDC,0x38,0xE2,0xEC,0x04,0x55,0x65,0x12,0x2A,0xE8,0x17,0x43, + 0xE1,0x8E,0xDD,0x2A,0xB3,0xE2,0x94,0xF7,0x09,0x6E,0x5C,0xE6,0xEB,0x8A,0xF8,0x6D, + 0x89,0x49,0x54,0x48,0xF5,0x2F,0xAD,0xBF,0xEA,0x94,0x4B,0xCA,0xFC,0x39,0x87,0x82, + 0x5F,0x8A,0x01,0xF2,0x75,0xF2,0xE6,0x71,0xD6,0xD8,0x42,0xDE,0xF1,0x2D,0x1D,0x28, + 0xA6,0x88,0x7E,0xA3,0xA0,0x47,0x1D,0x30,0xD9,0xA3,0x71,0xDF,0x49,0x1C,0xCB,0x01, + 0xF8,0x36,0xB1,0xF2,0xF0,0x22,0x58,0x5D,0x45,0x6B,0xBD,0xA0,0xBB,0xB2,0x88,0x42, + 0xC7,0x8C,0x28,0xCE,0x93,0xE8,0x90,0x63,0x08,0x90,0x7C,0x89,0x3C,0xF5,0x7D,0xB7, + 0x04,0x2D,0x4F,0x55,0x51,0x16,0xFD,0x7E,0x79,0xE8,0xBE,0xC1,0xF2,0x12,0xD4,0xF8, + 0xB4,0x84,0x05,0x23,0xA0,0xCC,0xD2,0x2B,0xFD,0xE1,0xAB,0xAD,0x0D,0xD1,0x55,0x6C, + 0x23,0x41,0x94,0x4D,0x77,0x37,0x4F,0x05,0x28,0x0C,0xBF,0x17,0xB3,0x12,0x67,0x6C, + 0x8C,0xC3,0x5A,0xF7,0x41,0x84,0x2A,0x6D,0xD0,0x94,0x12,0x27,0x2C,0xB4,0xED,0x9C, + 0x4D,0xEC,0x47,0x82,0x97,0xD5,0x67,0xB9,0x1B,0x9D,0xC0,0x55,0x07,0x7E,0xE5,0x8E, + 0xE2,0xA8,0xE7,0x3E,0x12,0xE4,0x0E,0x3A,0x2A,0x45,0x55,0x34,0xA2,0xF9,0x2D,0x5A, + 0x1B,0xAB,0x52,0x7C,0x83,0x10,0x5F,0x55,0xD2,0xF1,0x5A,0x43,0x2B,0xC6,0xA7,0xA4, + 0x89,0x15,0x95,0xE8,0xB4,0x4B,0x9D,0xF8,0x75,0xE3,0x9F,0x60,0x78,0x5B,0xD6,0xE6, + 0x0D,0x44,0xE6,0x21,0x06,0xBD,0x47,0x22,0x53,0xA4,0x00,0xAD,0x8D,0x43,0x13,0x85, + 0x39,0xF7,0xAA,0xFC,0x38,0xAF,0x7B,0xED,0xFC,0xE4,0x2B,0x54,0x50,0x98,0x4C,0xFC, + 0x85,0x80,0xF7,0xDF,0x3C,0x80,0x22,0xE1,0x94,0xDA,0xDE,0x24,0xC6,0xB0,0x7A,0x39, + 0x38,0xDC,0x0F,0xA1,0xA7,0xF4,0xF9,0x6F,0x63,0x18,0x57,0x8B,0x84,0x41,0x2A,0x2E, + 0xD4,0x53,0xF2,0xD9,0x00,0x0F,0xD0,0xDD,0x99,0x6E,0x19,0xA6,0x0A,0xD0,0xEC,0x5B, + 0x58,0x24,0xAB,0xC0,0xCB,0x06,0x65,0xEC,0x1A,0x13,0x38,0x94,0x0A,0x67,0x03,0x2F, + 0x3F,0xF7,0xE3,0x77,0x44,0x77,0x33,0xC6,0x14,0x39,0xD0,0xE3,0xC0,0xA2,0x08,0x79, + 0xBB,0x40,0x99,0x57,0x41,0x0B,0x01,0x90,0xCD,0xE1,0xCC,0x48,0x67,0xDB,0xB3,0xAF, + 0x88,0x74,0xF3,0x4C,0x82,0x8F,0x72,0xB1,0xB5,0x23,0x29,0xC4,0x12,0x6C,0x19,0xFC, + 0x8E,0x46,0xA4,0x9C,0xC4,0x25,0x65,0x87,0xD3,0x6D,0xBE,0x8A,0x93,0x11,0x03,0x38, + 0xED,0x83,0x2B,0xF3,0x46,0xA4,0x93,0xEA,0x3B,0x53,0x85,0x1D,0xCE,0xD4,0xF1,0x08, + 0x83,0x27,0xED,0xFC,0x9B,0x1A,0x18,0xBC,0xF9,0x8B,0xAE,0xDC,0x24,0xAB,0x50,0x38, + 0xE9,0x72,0x4B,0x10,0x22,0x17,0x7B,0x46,0x5D,0xAB,0x59,0x64,0xF3,0x40,0xAE,0xF8, + 0xBB,0xE5,0xC8,0xF9,0x26,0x03,0x4E,0x55,0x7D,0xEB,0xEB,0xFE,0xF7,0x39,0xE6,0xE0, + 0x0A,0x11,0xBE,0x2E,0x28,0xFF,0x98,0xED,0xC0,0xC9,0x42,0x56,0x42,0xC3,0xFD,0x00, + 0xF6,0xAF,0x87,0xA2,0x5B,0x01,0x3F,0x32,0x92,0x47,0x95,0x9A,0x72,0xA5,0x32,0x3D, + 0xAE,0x6B,0xD0,0x9B,0x07,0xD2,0x49,0x92,0xE3,0x78,0x4A,0xFA,0xA1,0x06,0x7D,0xF2, + 0x41,0xCF,0x77,0x74,0x04,0x14,0xB2,0x0C,0x86,0x84,0x64,0x16,0xD5,0xBB,0x51,0xA1, + 0xE5,0x6F,0xF1,0xD1,0xF2,0xE2,0xF7,0x5F,0x58,0x20,0x4D,0xB8,0x57,0xC7,0xCF,0xDD, + 0xC5,0xD8,0xBE,0x76,0x3D,0xF6,0x5F,0x7E,0xE7,0x2A,0x8B,0x88,0x24,0x1B,0x38,0x3F, + 0x0E,0x41,0x23,0x77,0xF5,0xF0,0x4B,0xD4,0x0C,0x1F,0xFA,0xA4,0x0B,0x80,0x5F,0xCF, + 0x45,0xF6,0xE0,0xDA,0x2F,0x34,0x59,0x53,0xFB,0x20,0x3C,0x52,0x62,0x5E,0x35,0xB5, + 0x62,0xFE,0x8B,0x60,0x63,0xE3,0x86,0x5A,0x15,0x1A,0x6E,0xD1,0x47,0x45,0xBC,0x32, + 0xB4,0xEB,0x67,0x38,0xAB,0xE4,0x6E,0x33,0x3A,0xB5,0xED,0xA3,0xAD,0x67,0xE0,0x4E, + 0x41,0x95,0xEE,0x62,0x62,0x71,0x26,0x1D,0x31,0xEF,0x62,0x30,0xAF,0xD7,0x82,0xAC, + 0xC2,0xDC,0x05,0x04,0xF5,0x97,0x07,0xBF,0x11,0x59,0x23,0x07,0xC0,0x64,0x02,0xE8, + 0x97,0xE5,0x3E,0xAF,0x18,0xAC,0x59,0xA6,0x8B,0x4A,0x33,0x90,0x1C,0x6E,0x7C,0x9C, + 0x20,0x7E,0x4C,0x3C,0x3E,0x61,0x64,0xBB,0xC5,0x6B,0x7C,0x7E,0x3E,0x9F,0xC5,0x4C, + 0x9F,0xEA,0x73,0xF5,0xD7,0x89,0xC0,0x4C,0xF4,0xFB,0xF4,0x2D,0xEC,0x14,0x1B,0x51, + 0xD5,0xC1,0x12,0xC8,0x10,0xDF,0x0B,0x4A,0x8B,0x9C,0xBC,0x93,0x45,0x6A,0x3E,0x3E, + 0x7D,0xC1,0xA9,0xBA,0xCD,0xC1,0xB4,0x07,0xE4,0xE1,0x68,0x86,0x43,0xB2,0x6D,0x38, + 0xF3,0xFB,0x0C,0x5C,0x66,0x37,0x71,0xDE,0x56,0xEF,0x6E,0xA0,0x10,0x40,0x65,0xA7, + 0x98,0xF7,0xD0,0xBE,0x0E,0xC8,0x37,0x36,0xEC,0x10,0xCA,0x7C,0x9C,0xAB,0x84,0x1E, + 0x05,0x17,0x76,0x02,0x1C,0x4F,0x52,0xAA,0x5F,0xC1,0xC6,0xA0,0x56,0xB9,0xD8,0x04, + 0x84,0x44,0x4D,0xA7,0x59,0xD8,0xDE,0x60,0xE6,0x38,0x0E,0x05,0x8F,0x03,0xE1,0x3B, + 0x6D,0x81,0x04,0x33,0x6F,0x30,0x0B,0xCE,0x69,0x05,0x21,0x33,0xFB,0x26,0xBB,0x89, + 0x7D,0xB6,0xAE,0x87,0x7E,0x51,0x07,0xE0,0xAC,0xF7,0x96,0x0A,0x6B,0xF9,0xC4,0x5C, + 0x1D,0xE4,0x44,0x47,0xB8,0x5E,0xFA,0xE3,0x78,0x84,0x55,0x42,0x4B,0x48,0x5E,0xF7, + 0x7D,0x47,0x35,0x86,0x1D,0x2B,0x43,0x05,0x03,0xEC,0x8A,0xB8,0x1E,0x06,0x3C,0x76, + 0x0C,0x48,0x1A,0x43,0xA7,0xB7,0x8A,0xED,0x1E,0x13,0xC6,0x43,0xEE,0x10,0xEF,0xDB, + 0xEC,0xFB,0x3C,0x83,0xB2,0x95,0x44,0xEF,0xD8,0x54,0x51,0x4E,0x2D,0x11,0x44,0x1D, + 0xFB,0x36,0x59,0x1E,0x7A,0x34,0xC1,0xC3,0xCA,0x57,0x00,0x61,0xEA,0x67,0xA5,0x16, + 0x9B,0x55,0xD0,0x55,0xE1,0x7F,0xD9,0x36,0xD2,0x40,0x76,0xAE,0xDC,0x01,0xCE,0xB0, + 0x7A,0x83,0xD5,0xCB,0x20,0x98,0xEC,0x6B,0xC1,0x72,0x92,0x34,0xF3,0x82,0x57,0x37, + 0x62,0x8A,0x32,0x36,0x0C,0x90,0x43,0xAE,0xAE,0x5C,0x9B,0x78,0x8E,0x13,0x65,0x02, + 0xFD,0x68,0x71,0xC1,0xFE,0xB0,0x31,0xA0,0x24,0x82,0xB0,0xC3,0xB1,0x79,0x69,0xA7, + 0xF5,0xD2,0xEB,0xD0,0x82,0xC0,0x32,0xDC,0x9E,0xC7,0x26,0x3C,0x6D,0x8D,0x98,0xC1, + 0xBB,0x22,0xD4,0xD0,0x0F,0x33,0xEC,0x3E,0xB9,0xCC,0xE1,0xDC,0x6A,0x4C,0x77,0x36, + 0x14,0x1C,0xF9,0xBF,0x81,0x9F,0x28,0x5F,0x71,0x85,0x32,0x29,0x90,0x75,0x48,0xC4, + 0xB3,0x4A,0xCE,0xD8,0x44,0x8F,0x14,0x2F,0xFD,0x40,0x57,0xEF,0xAA,0x08,0x75,0xD9, + 0x46,0xD1,0xD6,0x6E,0x32,0x55,0x1F,0xC3,0x18,0xFE,0x84,0x1F,0xFC,0x84,0xD5,0xFF, + 0x71,0x5E,0x1B,0x48,0xC3,0x86,0x95,0x0E,0x28,0x08,0x27,0xD3,0x38,0x83,0x71,0x7B, + 0x4C,0x80,0x63,0x54,0x9A,0x56,0xB0,0xAC,0xCF,0x80,0xCA,0x31,0x09,0xEF,0xFE,0xF3, + 0xBE,0xAF,0x24,0x7E,0xA6,0xFE,0x53,0x3F,0xC2,0x8D,0x4A,0x33,0x68,0xD1,0x22,0xA6, + 0x66,0xAD,0x7B,0xEA,0xDE,0xB6,0x43,0xB0,0xA1,0x25,0x95,0x00,0xA3,0x3F,0x75,0x46, + 0x14,0x11,0x44,0xEC,0xD7,0x95,0xBC,0x92,0xF0,0x4F,0xA9,0x16,0x53,0x62,0x97,0x60, + 0x2A,0x0F,0x41,0xF1,0x71,0x24,0xBE,0xEE,0x94,0x7F,0x08,0xCD,0x60,0x93,0xB3,0x85, + 0x5B,0x07,0x00,0x3F,0xD8,0x0F,0x28,0x83,0x9A,0xD1,0x69,0x9F,0xD1,0xDA,0x2E,0xC3, + 0x90,0x01,0xA2,0xB9,0x6B,0x4E,0x2A,0x66,0x9D,0xDA,0xAE,0xA6,0xEA,0x2A,0xD3,0x68, + 0x2F,0x0C,0x0C,0x9C,0xD2,0x8C,0x4A,0xED,0xE2,0x9E,0x57,0x65,0x9D,0x09,0x87,0xA3, + 0xB4,0xC4,0x32,0x5D,0xC9,0xD4,0x32,0x2B,0xB1,0xE0,0x71,0x1E,0x64,0x4D,0xE6,0x90, + 0x71,0xE3,0x1E,0x40,0xED,0x7D,0xF3,0x84,0x0E,0xED,0xC8,0x78,0x76,0xAE,0xC0,0x71, + 0x27,0x72,0xBB,0x05,0xEA,0x02,0x64,0xFB,0xF3,0x48,0x6B,0xB5,0x42,0x93,0x3F,0xED, + 0x9F,0x13,0x53,0xD2,0xF7,0xFE,0x2A,0xEC,0x1D,0x47,0x25,0xDB,0x3C,0x91,0x86,0xC6, + 0x8E,0xF0,0x11,0xFD,0x23,0x74,0x36,0xF7,0xA4,0xF5,0x9E,0x7A,0x7E,0x53,0x50,0x44, + 0xD4,0x47,0xCA,0xD3,0xEB,0x38,0x6D,0xE6,0xD9,0x71,0x94,0x7F,0x4A,0xC6,0x69,0x4B, + 0x11,0xF4,0x52,0xEA,0x22,0xFE,0x8A,0xB0,0x36,0x67,0x8B,0x59,0xE8,0xE6,0x80,0x2A, + 0xEB,0x65,0x04,0x13,0xEE,0xEC,0xDC,0x9E,0x5F,0xB1,0xEC,0x05,0x6A,0x59,0xE6,0x9F, + 0x5E,0x59,0x6B,0x89,0xBF,0xF7,0x1A,0xCA,0x44,0xF9,0x5B,0x6A,0x71,0x85,0x03,0xE4, + 0x29,0x62,0xE0,0x70,0x6F,0x41,0xC4,0xCF,0xB2,0xB1,0xCC,0xE3,0x7E,0xA6,0x07,0xA8, + 0x87,0xE7,0x7F,0x84,0x93,0xDB,0x52,0x4B,0x6C,0xEC,0x7E,0xDD,0xD4,0x24,0x48,0x10, + 0x69,0x9F,0x04,0x60,0x74,0xE6,0x48,0x18,0xF3,0xE4,0x2C,0xB9,0x4F,0x2E,0x50,0x7A, + 0xDF,0xD4,0x54,0x69,0x2B,0x8B,0xA7,0xF3,0xCE,0xFF,0x1F,0xF3,0x3E,0x26,0x01,0x39, + 0x17,0x95,0x84,0x89,0xB0,0xF0,0x4C,0x4B,0x82,0x91,0x9F,0xC4,0x4B,0xAC,0x9D,0xA5, + 0x74,0xAF,0x17,0x25,0xC9,0xCA,0x32,0xD3,0xBC,0x89,0x8A,0x84,0x89,0xCC,0x0D,0xAE, + 0x7C,0xA2,0xDB,0x9C,0x6A,0x78,0x91,0xEE,0xEA,0x76,0x5D,0x4E,0x87,0x60,0xF5,0x69, + 0x15,0x67,0xD4,0x02,0xCF,0xAF,0x48,0x36,0x07,0xEA,0xBF,0x6F,0x66,0x2D,0x06,0x8F, + 0xC4,0x9A,0xFE,0xF9,0xF6,0x90,0x87,0x75,0xB8,0xF7,0xAD,0x0F,0x76,0x10,0x5A,0x3D, + 0x59,0xB0,0x2E,0xB3,0xC7,0x35,0x2C,0xCC,0x70,0x56,0x2B,0xCB,0xE3,0x37,0x96,0xC5, + 0x2F,0x46,0x1B,0x8A,0x22,0x46,0xC7,0x88,0xA7,0x26,0x32,0x98,0x61,0xDF,0x86,0x22, + 0x8A,0xF4,0x1C,0x2F,0x87,0xA1,0x09,0xAA,0xCC,0xA9,0xAE,0xD3,0xBD,0x00,0x45,0x1C, + 0x9A,0x54,0x87,0x86,0x52,0x87,0xEF,0xFF,0x1E,0x8F,0xA1,0x8F,0xC1,0x89,0x5C,0x35, + 0x1B,0xDA,0x2D,0x3A,0x2C,0x16,0xB2,0xC2,0xF1,0x56,0xE2,0x78,0xC1,0x6B,0x63,0x97, + 0xC5,0x56,0x8F,0xC9,0x32,0x7F,0x2C,0xAA,0xAF,0xA6,0xA8,0xAC,0x20,0x91,0x22,0x88, + 0xDE,0xE4,0x60,0x8B,0xF9,0x4B,0x42,0x25,0x1A,0xE3,0x7F,0x9C,0x2C,0x19,0x89,0x3A, + 0x7E,0x05,0xD4,0x36,0xCC,0x69,0x58,0xC2,0xC1,0x32,0x8B,0x2F,0x90,0x85,0xEB,0x7A, + 0x39,0x50,0xA5,0xA1,0x27,0x92,0xC5,0x66,0xB0,0x20,0x4F,0x58,0x7E,0x55,0x83,0x43, + 0x2B,0x45,0xE2,0x9C,0xE4,0xD8,0x12,0x90,0x2C,0x16,0x83,0x56,0x16,0x79,0x03,0xB3, + 0xAD,0x2D,0x61,0x18,0x1A,0x13,0x1F,0x37,0xE2,0xE1,0x9C,0x73,0x7B,0x80,0xD5,0xFD, + 0x2D,0x51,0x87,0xFC,0x7B,0xAA,0xD7,0x1F,0x2C,0x7A,0x8E,0xAF,0xF4,0x8D,0xBB,0xCD, + 0x95,0x11,0x7C,0x72,0x0B,0xEE,0x6F,0xE2,0xB9,0xAF,0xDE,0x37,0x83,0xDE,0x8C,0x8D, + 0x62,0x05,0x67,0xB7,0x96,0xC6,0x8D,0x56,0xB6,0x0D,0xD7,0x62,0xBA,0xD6,0x46,0x36, + 0xBD,0x8E,0xC8,0xE6,0xEA,0x2A,0x6C,0x10,0x14,0xFF,0x6B,0x5B,0xFA,0x82,0x3C,0x46, + 0xB1,0x30,0x43,0x46,0x51,0x8A,0x7D,0x9B,0x92,0x3E,0x83,0x79,0x5B,0x55,0x5D,0xB2, + 0x6C,0x5E,0xCE,0x90,0x62,0x8E,0x53,0x98,0xC9,0x0D,0x6D,0xE5,0x2D,0x57,0xCD,0xC5, + 0x81,0x57,0xBA,0xE1,0xE8,0xB8,0x8F,0x72,0xE5,0x4F,0x13,0xDC,0xEA,0x9D,0x71,0x15, + 0x10,0xB2,0x11,0x88,0xD5,0x09,0xD4,0x7F,0x5B,0x65,0x7F,0x2C,0x3B,0x38,0x4C,0x11, + 0x68,0x50,0x8D,0xFB,0x9E,0xB0,0x59,0xBF,0x94,0x80,0x89,0x4A,0xC5,0x1A,0x18,0x12, + 0x89,0x53,0xD1,0x4A,0x10,0x29,0xE8,0x8C,0x1C,0xEC,0xB6,0xEA,0x46,0xC7,0x17,0x8B, + 0x25,0x15,0x31,0xA8,0xA2,0x6B,0x43,0xB1,0x9D,0xE2,0xDB,0x0B,0x87,0x9B,0xB0,0x11, + 0x04,0x0E,0x71,0xD2,0x29,0x77,0x89,0x82,0x0A,0x66,0x41,0x7F,0x1D,0x0B,0x48,0xFF, + 0x72,0xBB,0x24,0xFD,0xC2,0x48,0xA1,0x9B,0xFE,0x7B,0x7F,0xCE,0x88,0xDB,0x86,0xD9, + 0x85,0x3B,0x1C,0xB0,0xDC,0xA8,0x33,0x07,0xBF,0x51,0x2E,0xE3,0x0E,0x9A,0x00,0x97, + 0x1E,0x06,0xC0,0x97,0x43,0x9D,0xD8,0xB6,0x45,0xC4,0x86,0x67,0x5F,0x00,0xF8,0x88, + 0x9A,0xA4,0x52,0x9E,0xC7,0xAA,0x8A,0x83,0x75,0xEC,0xC5,0x18,0xAE,0xCE,0xC3,0x2F, + 0x1A,0x2B,0xF9,0x18,0xFF,0xAE,0x1A,0xF5,0x53,0x0B,0xB5,0x33,0x51,0xA7,0xFD,0xE8, + 0xA8,0xE1,0xA2,0x64,0xB6,0x22,0x17,0x43,0x80,0xCC,0x0A,0xD8,0xAE,0x3B,0xBA,0x40, + 0xD7,0xD9,0x92,0x4A,0x89,0xDF,0x04,0x10,0xEE,0x9B,0x18,0x2B,0x6A,0x77,0x69,0x8A, + 0x68,0xF4,0xF9,0xB9,0xA2,0x21,0x15,0x6E,0xE6,0x1E,0x3B,0x03,0x62,0x30,0x9B,0x60, + 0x41,0x7E,0x25,0x9B,0x9E,0x8F,0xC5,0x52,0x10,0x08,0xF8,0xC2,0x69,0xA1,0x21,0x11, + 0x88,0x37,0x5E,0x79,0x35,0x66,0xFF,0x10,0x42,0x18,0x6E,0xED,0x97,0xB6,0x6B,0x1C, + 0x4E,0x36,0xE5,0x6D,0x7D,0xB4,0xE4,0xBF,0x20,0xB9,0xE0,0x05,0x3A,0x69,0xD5,0xB8, + 0xE3,0xD5,0xDC,0xE0,0xB9,0xAC,0x53,0x3E,0x07,0xA4,0x57,0xAD,0x77,0xFF,0x48,0x18, + 0x76,0x2A,0xAC,0x49,0x2A,0x8E,0x47,0x75,0x6D,0x9F,0x67,0x63,0x30,0x35,0x8C,0x39, + 0x05,0x39,0xD5,0x6F,0x64,0x3A,0x5B,0xAD,0xCA,0x0B,0xBB,0x82,0x52,0x99,0x45,0xB1, + 0x93,0x36,0x36,0x99,0xAF,0x13,0x20,0x44,0x36,0xD8,0x02,0x44,0x09,0x39,0x92,0x85, + 0xFF,0x4A,0x4A,0x97,0x87,0xA6,0x63,0xD7,0xC7,0xB5,0xB5,0x24,0xED,0x0F,0xB4,0x6F, + 0x0C,0x58,0x52,0x14,0xD9,0xA6,0x7B,0xD3,0x79,0xBC,0x38,0x58,0xA1,0xBD,0x3B,0x84, + 0x06,0xD8,0x1A,0x06,0xFD,0x6B,0xA8,0xEA,0x4B,0x69,0x28,0x04,0x37,0xAD,0x82,0x99, + 0xFB,0x0E,0x1B,0x85,0xBD,0xA8,0x5D,0x73,0xCD,0xDC,0x58,0x75,0x0A,0xBE,0x63,0x6C, + 0x48,0xE7,0x4C,0xE4,0x30,0x2B,0x04,0x60,0xB9,0x15,0xD8,0xDA,0x86,0x81,0x75,0x8F, + 0x96,0xD4,0x8D,0x1C,0x5D,0x70,0x85,0x7C,0x1C,0x67,0x7B,0xD5,0x08,0x67,0xA6,0xCE, + 0x4B,0x0A,0x66,0x70,0xB7,0xE5,0x63,0xD4,0x5B,0x8A,0x82,0xEA,0x10,0x67,0xCA,0xE2, + 0xF4,0xEF,0x17,0x85,0x2F,0x2A,0x5F,0x8A,0x97,0x82,0xF8,0x6A,0xD6,0x34,0x10,0xEA, + 0xEB,0xC9,0x5C,0x3C,0xE1,0x49,0xF8,0x46,0xEB,0xDE,0xBD,0xF6,0xA9,0x92,0xF1,0xAA, + 0xA6,0xA0,0x18,0xB0,0x3A,0xD3,0x0F,0x1F,0xF3,0x6F,0xFF,0x31,0x45,0x43,0x44,0xD3, + 0x50,0x9A,0xF7,0x88,0x09,0x96,0xC1,0xCE,0x76,0xCC,0xF2,0x2C,0x2C,0xBA,0xAD,0x82, + 0x77,0x8F,0x18,0x84,0xC0,0xD2,0x07,0x9C,0x36,0x90,0x83,0x4E,0x0B,0xA5,0x4F,0x43, + 0x3E,0x04,0xAB,0x78,0x4F,0xD6,0xFB,0x09,0x01,0x24,0x90,0xDA,0x6F,0x3C,0x3A,0x61, + 0x0D,0x7F,0x69,0x4A,0xEB,0x2B,0x30,0x02,0xB4,0xDB,0xE0,0x84,0xA9,0xEC,0xD7,0x35, + 0xBF,0x37,0x7D,0x85,0x58,0xCE,0xA9,0x4E,0xE4,0x80,0xC7,0xA8,0xD3,0x30,0x67,0x48, + 0xEB,0x29,0xAF,0x2F,0x74,0x6A,0xB4,0xA7,0x3F,0x0F,0x3F,0x92,0xAF,0xF3,0xCA,0xAC, + 0xAF,0x4B,0xD9,0x94,0xC0,0x43,0xCA,0x81,0x0D,0x2F,0x48,0xA1,0xB0,0x27,0xD5,0xD2, + 0xEF,0x4B,0x05,0x85,0xA3,0xDE,0x4D,0x93,0x30,0x3C,0xF0,0xBB,0x4A,0x8F,0x30,0x27, + 0x4C,0xEB,0xE3,0x3E,0x64,0xED,0x9A,0x2F,0x3B,0xF1,0x82,0xF0,0xBA,0xF4,0xCF,0x7F, + 0x40,0xCB,0xB0,0xE1,0x7F,0xBC,0xAA,0x57,0xD3,0xC9,0x74,0xF2,0xFA,0x43,0x0D,0x22, + 0xD0,0xF4,0x77,0x4E,0x93,0xD7,0x85,0x70,0x1F,0x99,0xBF,0xB6,0xDE,0x35,0xF1,0x30, + 0xA7,0x5E,0x71,0xF0,0x6B,0x01,0x2D,0x7B,0x64,0xF0,0x33,0x53,0x0A,0x39,0x88,0xF3, + 0x6B,0x3A,0xA6,0x6B,0x35,0xD2,0x2F,0x43,0xCD,0x02,0xFD,0xB5,0xE9,0xBC,0x5B,0xAA, + 0xD8,0xA4,0x19,0x7E,0x0E,0x5D,0x94,0x81,0x9E,0x6F,0x77,0xAD,0xD6,0x0E,0x74,0x93, + 0x96,0xE7,0xC4,0x18,0x5F,0xAD,0xF5,0x19 +}; diff --git a/BootLoader/source/key2.h b/BootLoader/source/key2.h new file mode 100644 index 0000000..72b8ef2 --- /dev/null +++ b/BootLoader/source/key2.h @@ -0,0 +1,350 @@ +const unsigned char gEncrDataTwl[] = { + 0x59, 0xaa, 0x56, 0x8e, 0x90, 0xd7, 0x11, 0x55, 0x4d, 0xea, 0xbf, 0xfe, + 0xbd, 0x0d, 0x75, 0x91, 0xf7, 0x85, 0x39, 0x98, 0xd0, 0x9c, 0xc3, 0x58, + 0xc4, 0x15, 0x6f, 0xf1, 0x90, 0xf9, 0xe4, 0xc3, 0x8e, 0xc0, 0x9b, 0x0e, + 0x5d, 0xe1, 0x87, 0x94, 0xb9, 0x07, 0x2c, 0xba, 0xa6, 0x4f, 0x75, 0x74, + 0xc1, 0xe3, 0x1c, 0x86, 0xe6, 0xed, 0xf8, 0x09, 0x3b, 0xbb, 0x37, 0x7a, + 0x4e, 0xf0, 0xf0, 0x92, 0xf6, 0x55, 0xfa, 0x47, 0xfb, 0x1b, 0xc5, 0x16, + 0x06, 0x74, 0x4e, 0x56, 0x20, 0xdd, 0xb6, 0xd1, 0x42, 0xcf, 0xcf, 0xf1, + 0x55, 0x7e, 0x17, 0x18, 0xa1, 0x93, 0xff, 0x09, 0xda, 0x36, 0xa6, 0x9a, + 0x43, 0x3d, 0xf4, 0x65, 0xed, 0x40, 0x97, 0x6c, 0xd5, 0xa6, 0xdd, 0x6d, + 0x6c, 0x23, 0xbf, 0x94, 0xe7, 0x51, 0xa6, 0x68, 0x3c, 0xe8, 0xe6, 0x65, + 0xd6, 0xbc, 0x9e, 0x92, 0x78, 0x26, 0x46, 0xa1, 0x73, 0xdc, 0xe5, 0x36, + 0x8e, 0xcd, 0xec, 0xa1, 0xf1, 0xee, 0x8b, 0x68, 0xf4, 0xac, 0xc1, 0xdc, + 0xc8, 0x84, 0x95, 0x31, 0xe8, 0xed, 0xc7, 0x5e, 0xe4, 0x5a, 0x37, 0xca, + 0xec, 0x55, 0xbe, 0x2a, 0xfc, 0xf6, 0x45, 0x67, 0xa9, 0xb4, 0x7d, 0x7d, + 0x9b, 0x6e, 0xe9, 0x2c, 0xff, 0x3f, 0xeb, 0x69, 0xb7, 0x2e, 0x68, 0xa8, + 0x94, 0xef, 0x7b, 0xbd, 0x88, 0x93, 0x15, 0x66, 0x3a, 0xb7, 0x7f, 0xfe, + 0x1d, 0xc3, 0x89, 0x08, 0xd7, 0x74, 0x59, 0xfa, 0xaf, 0x91, 0x41, 0x9e, + 0x57, 0xd5, 0x67, 0x84, 0xba, 0x00, 0xe9, 0x63, 0x58, 0x07, 0x4d, 0xec, + 0xdf, 0xc6, 0xda, 0x1e, 0x62, 0x52, 0xd9, 0x14, 0xbc, 0x03, 0xc3, 0xb0, + 0xa5, 0xfd, 0xb7, 0x27, 0xde, 0xb1, 0x6f, 0x1b, 0x7c, 0x72, 0x4a, 0xcd, + 0x09, 0xe5, 0x82, 0x70, 0xd3, 0x9f, 0xb6, 0xd6, 0xa4, 0x6a, 0x2f, 0xc2, + 0x32, 0xbd, 0xb5, 0x39, 0xe4, 0xea, 0xb9, 0x71, 0x1c, 0x70, 0x67, 0x21, + 0x92, 0x21, 0xac, 0xf4, 0x9e, 0x63, 0xe8, 0x5e, 0x83, 0x02, 0xcc, 0x0c, + 0xf8, 0xf8, 0x9e, 0x87, 0x89, 0xfc, 0x03, 0x85, 0xfa, 0xcc, 0x77, 0x07, + 0x44, 0x5f, 0x4d, 0xe5, 0x19, 0xd3, 0x12, 0xee, 0xca, 0xe1, 0xe0, 0xbf, + 0x1e, 0xbe, 0xe7, 0x12, 0x1f, 0x6a, 0x93, 0x1e, 0x38, 0x4b, 0xa7, 0x9f, + 0x81, 0xa9, 0x77, 0x85, 0x0c, 0xc6, 0x39, 0x02, 0x55, 0xd2, 0x62, 0x56, + 0x19, 0x85, 0xa6, 0x38, 0x85, 0xe1, 0x2d, 0x3c, 0x38, 0x3b, 0x5b, 0xa0, + 0x24, 0x18, 0xe9, 0x29, 0x6c, 0x9f, 0xe4, 0x4d, 0x4e, 0x23, 0x5f, 0xb1, + 0xe2, 0xa0, 0x6f, 0x97, 0xb2, 0x41, 0xd1, 0xea, 0xdb, 0xa7, 0x37, 0x81, + 0xeb, 0x06, 0x8d, 0x77, 0xc2, 0x68, 0xfc, 0x5a, 0x65, 0x97, 0x33, 0x58, + 0xa1, 0xb8, 0x35, 0x0f, 0xf4, 0x25, 0xbc, 0x3b, 0x4f, 0x18, 0x0f, 0x0e, + 0x60, 0x25, 0x3d, 0xd8, 0x77, 0x1a, 0xd0, 0x8a, 0xb0, 0x61, 0x57, 0x16, + 0x0b, 0x55, 0xf2, 0x58, 0xb9, 0x91, 0x52, 0x30, 0x33, 0xab, 0x29, 0x9b, + 0x03, 0x62, 0xe5, 0xcc, 0xdf, 0x6e, 0x62, 0x86, 0x9d, 0x76, 0xe5, 0xdd, + 0x6f, 0xca, 0x3e, 0x75, 0xd8, 0x88, 0x58, 0x06, 0x8d, 0xa4, 0x58, 0xf5, + 0xaa, 0x7c, 0xce, 0x17, 0xdd, 0xde, 0xca, 0x0a, 0x72, 0x87, 0x6f, 0x29, + 0x6c, 0x0c, 0xe9, 0xc0, 0x3d, 0x32, 0x2e, 0x55, 0xf3, 0xa7, 0x27, 0xda, + 0xfc, 0x86, 0x0c, 0x9e, 0x33, 0x83, 0xb5, 0x47, 0xeb, 0xe8, 0xf6, 0xc9, + 0xf4, 0x24, 0x72, 0xee, 0xaf, 0xf8, 0xb5, 0x59, 0x70, 0x06, 0x85, 0x71, + 0xbb, 0x3c, 0xbe, 0xbb, 0x2c, 0x24, 0xad, 0x67, 0xba, 0x42, 0xb9, 0xee, + 0x68, 0xec, 0x0b, 0xe6, 0x5b, 0x26, 0x0f, 0x2b, 0xb6, 0x3a, 0x93, 0x4f, + 0x9f, 0xe6, 0x9f, 0xb9, 0x1a, 0xa0, 0xb9, 0x51, 0x1c, 0x8d, 0x66, 0x37, + 0xd2, 0x50, 0xcc, 0xae, 0x10, 0x30, 0x16, 0x60, 0x56, 0x3b, 0x99, 0x0e, + 0x90, 0x7b, 0x28, 0xde, 0x93, 0xf4, 0x16, 0x87, 0x1f, 0xd0, 0x9b, 0xc2, + 0x33, 0x42, 0x2c, 0x2c, 0xf1, 0x36, 0xc3, 0x39, 0xf8, 0x4f, 0xa4, 0x1e, + 0x00, 0x43, 0xb1, 0xac, 0xdf, 0x08, 0xbb, 0xfe, 0x5e, 0x2a, 0xdc, 0x2a, + 0x10, 0xf3, 0x7b, 0xc5, 0x2f, 0x96, 0xc9, 0x1d, 0x51, 0x4f, 0xc0, 0xde, + 0x6e, 0x93, 0x9a, 0x35, 0x19, 0xb8, 0x58, 0xb5, 0x19, 0xba, 0xaf, 0x2a, + 0xb1, 0xb5, 0xb2, 0xff, 0xc1, 0x89, 0xbc, 0x3f, 0xd8, 0x8f, 0x34, 0x07, + 0x63, 0x60, 0xa5, 0xed, 0xdb, 0xff, 0x9e, 0xf5, 0x5b, 0x23, 0xc0, 0x1b, + 0x13, 0x96, 0xd4, 0x2f, 0x07, 0x51, 0x1b, 0xac, 0x90, 0x72, 0x71, 0x28, + 0x65, 0x98, 0xe1, 0xff, 0x6a, 0x9d, 0xe7, 0x30, 0x6d, 0xb1, 0x2c, 0x21, + 0xfa, 0xcb, 0xbc, 0x6a, 0x3c, 0x25, 0xe8, 0x50, 0x5c, 0x53, 0xd8, 0xd5, + 0xcb, 0xa2, 0x53, 0x53, 0xa5, 0x64, 0x3f, 0x78, 0x61, 0x77, 0x1d, 0x8d, + 0x16, 0xe4, 0xe4, 0xa1, 0x32, 0x9c, 0x00, 0x52, 0x5f, 0x2a, 0xd7, 0xf5, + 0x3c, 0xfd, 0x09, 0xd7, 0x1b, 0x3b, 0x99, 0x01, 0x4d, 0xdc, 0x91, 0xe4, + 0x6d, 0xe8, 0x9e, 0xa3, 0x18, 0xad, 0x43, 0x27, 0xba, 0xc1, 0x5f, 0x37, + 0xa6, 0x12, 0x61, 0xf5, 0x1c, 0x63, 0x0c, 0x25, 0x2d, 0x90, 0xf8, 0x52, + 0xcb, 0x2c, 0x37, 0x4b, 0xde, 0x1e, 0x6c, 0x36, 0x1d, 0x47, 0xf5, 0xdf, + 0x87, 0xca, 0x79, 0x98, 0x80, 0x09, 0x59, 0xd7, 0x14, 0xfd, 0xf7, 0xf9, + 0xf4, 0xce, 0x69, 0x23, 0xd2, 0xf8, 0xc4, 0xee, 0xa0, 0x7e, 0xf8, 0x36, + 0x8e, 0x35, 0x93, 0x45, 0x82, 0xae, 0x0d, 0xfc, 0x65, 0xbc, 0xaa, 0xf5, + 0x58, 0xa9, 0x65, 0xba, 0xc6, 0x08, 0x4b, 0x63, 0xc3, 0x3f, 0xa6, 0x8a, + 0xf4, 0xc1, 0x9b, 0x8f, 0x02, 0x45, 0x1b, 0x13, 0x78, 0x28, 0x9f, 0xd6, + 0x53, 0xb1, 0xc2, 0x7e, 0x4e, 0x71, 0x17, 0xe7, 0x55, 0x09, 0x62, 0xc7, + 0xad, 0xd5, 0x91, 0x1a, 0xc0, 0xfa, 0x49, 0x4a, 0xef, 0x00, 0xd6, 0xf6, + 0xf1, 0xd0, 0xc9, 0x40, 0x1b, 0xb1, 0xfd, 0x0e, 0xd3, 0x95, 0xf1, 0xcd, + 0x95, 0x60, 0x08, 0x73, 0xd2, 0xe0, 0x56, 0xfa, 0xd0, 0x65, 0x51, 0xfd, + 0xc4, 0x48, 0xd1, 0xaa, 0x5a, 0xba, 0xcb, 0x8f, 0x76, 0x22, 0xe3, 0x60, + 0x6f, 0x4a, 0x3c, 0x86, 0x35, 0xee, 0xe9, 0x88, 0x9a, 0x4a, 0x36, 0x34, + 0x74, 0xe3, 0x6d, 0x3f, 0xe4, 0x2a, 0x93, 0x0b, 0xe2, 0xc6, 0x47, 0x4d, + 0xf2, 0xb6, 0x8e, 0x78, 0x14, 0x91, 0x61, 0xcf, 0xfa, 0xb6, 0x1b, 0x39, + 0xca, 0x88, 0x0c, 0x04, 0x65, 0xd3, 0x3b, 0xd1, 0xc6, 0xda, 0xe5, 0xf4, + 0xe9, 0x1a, 0x38, 0x0f, 0xa5, 0xca, 0x32, 0x29, 0x78, 0x6c, 0x91, 0x9d, + 0xd8, 0xc1, 0x8c, 0x3d, 0x6e, 0x82, 0x49, 0x10, 0x38, 0x4c, 0x95, 0xe3, + 0xf1, 0x69, 0x30, 0x2e, 0x3e, 0xbf, 0xaf, 0x7d, 0x5e, 0x51, 0x3c, 0x6a, + 0x15, 0x04, 0xbd, 0x8f, 0xcf, 0xeb, 0x3f, 0xe0, 0xe0, 0xa7, 0xb3, 0x3e, + 0xf3, 0xf7, 0xd8, 0x1d, 0x15, 0x74, 0xef, 0x4e, 0x5b, 0xa0, 0x1e, 0x3a, + 0x45, 0xec, 0x98, 0x8b, 0xe4, 0x0c, 0xfb, 0x77, 0xfd, 0xcf, 0xde, 0x88, + 0x4d, 0x42, 0x18, 0x81, 0x14, 0x0d, 0xe2, 0x20, 0x4e, 0xcf, 0x0d, 0x3b, + 0xc8, 0x41, 0x36, 0x9d, 0x99, 0xab, 0x47, 0xcb, 0x55, 0xf0, 0x79, 0x77, + 0x32, 0x85, 0xa4, 0xe4, 0x11, 0x14, 0x42, 0x8d, 0x03, 0x8c, 0x76, 0xba, + 0x05, 0xcf, 0xe8, 0x40, 0x47, 0xcf, 0xbd, 0xe7, 0x22, 0xe6, 0x72, 0xce, + 0xa0, 0x13, 0xe4, 0x59, 0x5e, 0x68, 0xc2, 0x53, 0x7a, 0x4d, 0x4b, 0x4c, + 0xcd, 0xbf, 0xe2, 0xb0, 0xa3, 0x63, 0x77, 0xf2, 0x1e, 0xc3, 0x21, 0xca, + 0xd2, 0xb6, 0x7b, 0x01, 0x79, 0x02, 0x43, 0xec, 0x6d, 0x98, 0x97, 0x86, + 0x27, 0x41, 0x67, 0xe7, 0x04, 0xcf, 0x71, 0x0e, 0xfc, 0xc8, 0x3d, 0x32, + 0x99, 0x35, 0x4d, 0x2c, 0x94, 0xd7, 0x82, 0xb5, 0x2e, 0x20, 0x73, 0xd8, + 0xa4, 0xf9, 0xae, 0x6c, 0xd6, 0x12, 0x57, 0xe9, 0x44, 0x86, 0x6a, 0x9e, + 0xe0, 0x72, 0x84, 0x97, 0xb3, 0x8d, 0x56, 0x28, 0x66, 0xdb, 0xec, 0x25, + 0xbf, 0x01, 0x11, 0x76, 0x9b, 0xe1, 0x43, 0x8d, 0x6d, 0x0b, 0xfa, 0x3d, + 0x45, 0x15, 0x4a, 0xb4, 0xac, 0x76, 0x2a, 0x4a, 0xfb, 0x8d, 0xa5, 0x03, + 0xe4, 0x36, 0xe6, 0xd9, 0xfd, 0xc1, 0x20, 0x63, 0xe3, 0x5c, 0x9a, 0x0e, + 0x0f, 0x99, 0x49, 0xc6, 0x10, 0x9a, 0x08, 0x47, 0xff, 0x3d, 0xaa, 0x0c, + 0x9f, 0x46, 0x57, 0x5a, 0xe5, 0xc5, 0x24, 0xc5, 0xf1, 0xca, 0x1a, 0xa2, + 0xb0, 0x29, 0x78, 0xdd, 0x7a, 0x72, 0x49, 0x54, 0xac, 0xc4, 0x22, 0x04, + 0x97, 0xa2, 0xa1, 0x1a, 0x2f, 0x57, 0xfd, 0x9b, 0xaf, 0xc9, 0x30, 0x10, + 0x4a, 0xf4, 0x5e, 0x52, 0xf8, 0x25, 0x32, 0x48, 0xcb, 0x02, 0x6c, 0x3b, + 0xa7, 0xe3, 0xbd, 0xe9, 0x54, 0xd5, 0xbe, 0x46, 0x6b, 0xea, 0x0b, 0x43, + 0x13, 0x1d, 0x6f, 0x9c, 0xf5, 0xeb, 0x0e, 0xba, 0x28, 0x4f, 0x98, 0x84, + 0xb2, 0x19, 0x9c, 0xfe, 0xa0, 0x4a, 0xf6, 0x07, 0xcc, 0x0c, 0x8f, 0x75, + 0x6a, 0x16, 0xa1, 0x1c, 0x4e, 0x42, 0x51, 0xdc, 0x17, 0xb0, 0xa4, 0x2c, + 0x86, 0x87, 0x55, 0xf5, 0x7a, 0x5a, 0xd0, 0x0d, 0x4b, 0x9f, 0xb9, 0xcb, + 0xf3, 0x23, 0x5b, 0xaa, 0x81, 0x0e, 0x74, 0x56, 0x96, 0xbb, 0x65, 0x14, + 0x3e, 0xb2, 0x17, 0x53, 0x7e, 0x71, 0xf1, 0x9b, 0xfd, 0x1c, 0x5c, 0xfe, + 0xee, 0x6b, 0x58, 0xc7, 0xb5, 0x82, 0xed, 0x14, 0x47, 0xb0, 0x62, 0xe8, + 0xad, 0x34, 0x9c, 0xe6, 0x12, 0x29, 0x3b, 0x91, 0x2b, 0x83, 0xe6, 0x5c, + 0xd4, 0xf1, 0x5b, 0x7f, 0xe0, 0x58, 0xc8, 0x29, 0xa4, 0x17, 0x76, 0xa0, + 0x95, 0x9d, 0xb1, 0xad, 0xa1, 0x01, 0xa2, 0xce, 0xd0, 0xa3, 0x14, 0x1e, + 0xb7, 0x22, 0x98, 0x9d, 0xcd, 0x7f, 0x8c, 0xb8, 0x0f, 0x5b, 0x5b, 0x36, + 0x3f, 0xce, 0xca, 0xce, 0x5b, 0x54, 0x8b, 0xbd, 0xde, 0x82, 0x7e, 0xf1, + 0xf9, 0xa0, 0x30, 0xfe, 0xbd, 0xe7, 0x35, 0x84, 0x29, 0x1e, 0x41, 0x8e, + 0x55, 0x3f, 0xf7, 0x40, 0x23, 0xaa, 0x2d, 0x5a, 0xe5, 0xc4, 0x32, 0x9e, + 0xbf, 0x22, 0xb0, 0xc1, 0xe7, 0x8c, 0x7d, 0x5d, 0x0b, 0x28, 0xb4, 0x57, + 0x8e, 0xe7, 0x56, 0x3d, 0x1f, 0x35, 0x1e, 0x98, 0xa9, 0x0d, 0xd7, 0xb7, + 0x20, 0xe2, 0x89, 0x90, 0x04, 0xa7, 0x56, 0xea, 0x84, 0x16, 0x6f, 0xff, + 0xa9, 0x38, 0x5e, 0xa0, 0xaf, 0x2d, 0xc1, 0xb6, 0xc1, 0x77, 0x72, 0xe1, + 0x21, 0xc7, 0x2f, 0x3f, 0x85, 0x51, 0x4b, 0x83, 0xca, 0x33, 0x50, 0xb1, + 0x4c, 0x58, 0x0c, 0x54, 0x7c, 0x70, 0xfe, 0x23, 0xef, 0xc7, 0xc7, 0xaf, + 0xaf, 0xbf, 0xe5, 0x7b, 0x05, 0x90, 0x6c, 0x7a, 0x9f, 0x66, 0xb9, 0xc6, + 0x44, 0xd5, 0x99, 0x6c, 0xd5, 0xac, 0x74, 0xce, 0x00, 0x49, 0x4b, 0xcf, + 0x51, 0x01, 0xda, 0x24, 0xc5, 0x42, 0xba, 0x6f, 0x8a, 0x73, 0x20, 0x11, + 0xbc, 0x4a, 0x4f, 0xdb, 0xa6, 0x40, 0x27, 0xbc, 0x93, 0xa3, 0x30, 0xb2, + 0xcc, 0x6e, 0x78, 0xa0, 0x28, 0x7d, 0xe7, 0x34, 0x11, 0x4c, 0x00, 0x8b, + 0x04, 0x3d, 0x93, 0x7f, 0x2a, 0x3c, 0x67, 0x56, 0xad, 0xc5, 0xdd, 0x2a, + 0x75, 0xe1, 0x96, 0x02, 0x8d, 0x66, 0x0e, 0xd8, 0xc1, 0x83, 0xdf, 0x27, + 0x42, 0xc4, 0x47, 0x18, 0x24, 0xac, 0x99, 0x8b, 0x22, 0x28, 0x68, 0x74, + 0xb2, 0x7e, 0x58, 0x19, 0x19, 0xda, 0xd4, 0x96, 0x36, 0x26, 0xc7, 0x53, + 0x37, 0xdb, 0x53, 0xa5, 0xd3, 0x98, 0xb4, 0x65, 0x80, 0xde, 0x73, 0xcb, + 0x97, 0x7e, 0x59, 0x80, 0xf6, 0x25, 0x60, 0x6f, 0x77, 0x20, 0x4c, 0xc7, + 0x35, 0xc6, 0x80, 0xe3, 0x56, 0x2c, 0xba, 0x62, 0xf7, 0x56, 0xf9, 0x63, + 0x3e, 0xf9, 0x91, 0x7b, 0x9c, 0x35, 0x02, 0x04, 0xd8, 0x3d, 0x35, 0xfd, + 0xb7, 0x85, 0xba, 0x04, 0x19, 0x7f, 0xb9, 0xe6, 0x6a, 0x65, 0x51, 0x9e, + 0xde, 0x21, 0xec, 0xf0, 0x6b, 0xfd, 0x41, 0x90, 0xdc, 0x32, 0x08, 0x4d, + 0x9b, 0x43, 0x2a, 0x61, 0x5b, 0x35, 0x61, 0xc1, 0xfd, 0xa2, 0xde, 0x30, + 0xd3, 0x93, 0xc6, 0x0d, 0xad, 0x76, 0xac, 0xfb, 0xb0, 0xee, 0x85, 0x5f, + 0xde, 0x4e, 0x2b, 0xe8, 0x8f, 0x67, 0xa0, 0x12, 0x00, 0x3f, 0xcf, 0x04, + 0xe4, 0xb1, 0x2b, 0xa0, 0xda, 0xbb, 0x33, 0x5a, 0x58, 0x9b, 0x7c, 0x05, + 0xea, 0x2b, 0x7b, 0x40, 0x9c, 0xc3, 0xe0, 0x99, 0x9e, 0xe0, 0x91, 0x67, + 0xa5, 0x63, 0x6b, 0x9f, 0x15, 0xb6, 0x3c, 0xda, 0x17, 0x90, 0x8f, 0x05, + 0x7e, 0x61, 0x7c, 0xc7, 0x25, 0xdf, 0xbb, 0xd6, 0x96, 0xba, 0x45, 0xa8, + 0x84, 0xa0, 0x7d, 0x0f, 0x41, 0xdd, 0xba, 0xe5, 0x5a, 0x09, 0x3d, 0xe7, + 0x20, 0x22, 0xc6, 0x8e, 0x0d, 0xd5, 0xc5, 0x75, 0x38, 0x8c, 0x6e, 0x4f, + 0xa0, 0x42, 0xf7, 0x5e, 0xb1, 0x35, 0xe5, 0xfc, 0x93, 0x13, 0x58, 0x2b, + 0xa7, 0xe0, 0xfe, 0xff, 0x1a, 0xdd, 0x30, 0x27, 0x9e, 0x69, 0xdd, 0x05, + 0x18, 0xf7, 0x23, 0x5d, 0x9c, 0x64, 0xbe, 0x47, 0xf0, 0xa8, 0xe1, 0xf5, + 0xde, 0x67, 0x8a, 0xcc, 0x18, 0xed, 0x4a, 0x76, 0xa0, 0x23, 0x96, 0x55, + 0xd0, 0x84, 0x22, 0xce, 0xe1, 0xe2, 0x11, 0x80, 0x95, 0x61, 0x0d, 0x75, + 0x12, 0x86, 0xb9, 0x3c, 0x10, 0x9d, 0x4d, 0x39, 0x93, 0x42, 0x7d, 0x83, + 0xa5, 0xf4, 0xe4, 0xaa, 0x9b, 0x59, 0x22, 0x5e, 0xd3, 0xfd, 0xad, 0xf9, + 0xa0, 0xf2, 0xb2, 0x70, 0x86, 0x29, 0xcd, 0x71, 0x61, 0x98, 0xb8, 0x21, + 0x15, 0x5d, 0xf5, 0xde, 0x4d, 0x65, 0x27, 0x09, 0x8c, 0xed, 0xd0, 0xc8, + 0xe7, 0xed, 0x0b, 0x0c, 0x13, 0x9e, 0x78, 0xea, 0xf8, 0x3c, 0x10, 0xda, + 0xcd, 0xfc, 0xaf, 0x33, 0x96, 0x62, 0x31, 0x9c, 0xb6, 0x9d, 0xc8, 0x7a, + 0x35, 0xe6, 0xff, 0x75, 0xa8, 0x30, 0x98, 0xd4, 0xaa, 0xcf, 0x9c, 0xef, + 0xda, 0xb9, 0x64, 0xe8, 0x3b, 0xa6, 0x2f, 0xc1, 0xbd, 0x7e, 0x6b, 0xfc, + 0x1a, 0xef, 0x62, 0xad, 0x90, 0x5e, 0x7d, 0x29, 0x12, 0x4d, 0x76, 0x86, + 0x5c, 0x29, 0x7c, 0x61, 0x1d, 0x1e, 0x63, 0x97, 0x21, 0xcd, 0x77, 0xbd, + 0xc2, 0x32, 0x45, 0xca, 0x7a, 0xdc, 0x0b, 0x16, 0xa4, 0x10, 0xac, 0x37, + 0xba, 0xf5, 0xf6, 0xbc, 0x26, 0x66, 0x67, 0x2b, 0xb8, 0x2e, 0x22, 0xc0, + 0xea, 0x90, 0x78, 0xf0, 0x0d, 0x0f, 0x80, 0x69, 0x60, 0xd2, 0x89, 0xa5, + 0x1a, 0xb0, 0xcf, 0x5e, 0x57, 0x6f, 0x79, 0xdc, 0xd8, 0x2c, 0x51, 0x92, + 0xd6, 0x62, 0x41, 0xf9, 0xf7, 0x26, 0xf0, 0x59, 0x93, 0xe2, 0x76, 0x82, + 0x21, 0xf6, 0xab, 0x7a, 0xd2, 0x7b, 0x81, 0xcb, 0x8c, 0xe8, 0x87, 0x77, + 0x76, 0xce, 0xf2, 0xaa, 0x00, 0xdc, 0xec, 0xd1, 0xc1, 0x8d, 0xf8, 0x42, + 0x41, 0x8c, 0x35, 0xd1, 0x70, 0x97, 0xf4, 0x82, 0x2f, 0x3a, 0x2f, 0x4a, + 0x18, 0x8f, 0xac, 0x41, 0xfa, 0x29, 0xc2, 0x9d, 0x0a, 0xfa, 0x0c, 0x44, + 0xdd, 0xea, 0xc6, 0x2b, 0xd3, 0x2e, 0x28, 0xee, 0xca, 0x6e, 0x84, 0x90, + 0xec, 0xaf, 0xf4, 0x8f, 0xbd, 0xc7, 0xd1, 0x2d, 0xf6, 0x9a, 0xd2, 0x00, + 0xaa, 0x5c, 0x38, 0xc5, 0x11, 0x43, 0x7c, 0xf4, 0x0d, 0xbd, 0x57, 0x6d, + 0x42, 0x62, 0xa5, 0xd8, 0x05, 0xa7, 0xe9, 0x30, 0xc0, 0x81, 0x9b, 0xfc, + 0x30, 0xda, 0x16, 0x2f, 0x54, 0x61, 0x08, 0xaa, 0xf7, 0xc0, 0x1e, 0x4d, + 0xf2, 0xd4, 0xed, 0x5c, 0x96, 0x30, 0xad, 0x9f, 0xc5, 0xe3, 0xf0, 0x91, + 0xff, 0xf0, 0xb1, 0xe4, 0x93, 0x7b, 0x67, 0x11, 0xba, 0xef, 0xb7, 0xf4, + 0x29, 0x93, 0x6d, 0x32, 0x1f, 0x88, 0xd1, 0x6c, 0x7c, 0x5a, 0x7e, 0x0a, + 0xef, 0x6a, 0xe9, 0x23, 0x2c, 0xde, 0x4c, 0x68, 0x36, 0xcb, 0xaa, 0x1f, + 0xd3, 0x71, 0xce, 0x31, 0x8b, 0x2b, 0x51, 0x16, 0xe6, 0x65, 0xd1, 0x30, + 0xaf, 0xb8, 0xbe, 0x02, 0x21, 0x61, 0x36, 0xbc, 0x19, 0x7c, 0x0e, 0x9d, + 0x9c, 0xd6, 0xa9, 0xc7, 0x5c, 0x2f, 0xb6, 0x23, 0x4b, 0x64, 0x3b, 0x99, + 0x74, 0x83, 0x51, 0xda, 0x3e, 0xf8, 0xcf, 0x0f, 0xa3, 0x7a, 0xfb, 0xaa, + 0xd1, 0xe2, 0x09, 0x05, 0x3a, 0xf5, 0xa8, 0x61, 0x51, 0x59, 0xf6, 0xb3, + 0x3d, 0xe9, 0xa3, 0xc7, 0x3a, 0xe6, 0xff, 0x2d, 0x96, 0xaf, 0xe4, 0x41, + 0xb8, 0x7d, 0xca, 0xdf, 0x42, 0x16, 0x5c, 0xee, 0xd0, 0x9d, 0xa3, 0x74, + 0xa9, 0xae, 0xfd, 0x6d, 0x3b, 0x15, 0xb9, 0x89, 0x19, 0xa8, 0xf8, 0x48, + 0xfe, 0x3a, 0xf6, 0xd7, 0x44, 0x4b, 0x96, 0x07, 0x37, 0x4b, 0xf9, 0x33, + 0x62, 0x4f, 0x08, 0x38, 0xfc, 0x02, 0xfc, 0x8d, 0x3d, 0x65, 0x83, 0x02, + 0xed, 0xd7, 0x48, 0x40, 0x51, 0x99, 0x0a, 0x20, 0xb2, 0xda, 0x9d, 0xca, + 0xbf, 0xb7, 0xcf, 0xa8, 0x32, 0x67, 0x2f, 0x31, 0xa3, 0x00, 0xe3, 0xcb, + 0x09, 0x7e, 0x0a, 0xb0, 0x7a, 0x34, 0x7b, 0xfc, 0x1d, 0x97, 0x8c, 0xa6, + 0x17, 0xcb, 0x62, 0xc7, 0x28, 0xf4, 0xb8, 0x21, 0xdb, 0x51, 0xc9, 0xef, + 0x69, 0xb6, 0xac, 0x36, 0x90, 0x74, 0x90, 0xb7, 0xdb, 0xcb, 0xfd, 0xdb, + 0x17, 0x81, 0xed, 0x94, 0x4d, 0xe5, 0x4e, 0xe5, 0xf6, 0x01, 0x4a, 0x99, + 0x9f, 0x5e, 0xe0, 0x45, 0x70, 0x41, 0x45, 0xa2, 0x2b, 0x4e, 0xd6, 0xab, + 0xdc, 0x06, 0x15, 0x2d, 0x48, 0x88, 0x17, 0x43, 0x39, 0x94, 0xb4, 0x3a, + 0x23, 0xce, 0xbb, 0xda, 0x0e, 0xb0, 0x5c, 0x1e, 0x0d, 0x0b, 0x31, 0x8e, + 0x9b, 0x04, 0x80, 0x78, 0x75, 0x1c, 0x9b, 0x97, 0xac, 0xc7, 0xad, 0xde, + 0x2b, 0x7f, 0x48, 0xb2, 0x29, 0xae, 0x76, 0x59, 0x27, 0xee, 0x79, 0xb8, + 0x8e, 0x30, 0xe7, 0xf2, 0x84, 0x44, 0x40, 0x79, 0x25, 0xce, 0x13, 0x87, + 0x8e, 0xfa, 0x08, 0x18, 0x8d, 0x71, 0xac, 0xeb, 0xf2, 0x7c, 0xa6, 0x69, + 0x29, 0x1b, 0xd8, 0x02, 0xea, 0x64, 0x40, 0x7d, 0xa1, 0xb2, 0x05, 0xd3, + 0x2b, 0x9d, 0x98, 0xa4, 0x2c, 0xee, 0xc9, 0x2c, 0x52, 0x5a, 0xd9, 0x3e, + 0xd4, 0xcc, 0x6b, 0xf5, 0x11, 0x4a, 0x0a, 0x84, 0x4b, 0x6e, 0xa4, 0xab, + 0x16, 0x46, 0x31, 0xb2, 0x84, 0x32, 0x43, 0x43, 0xe3, 0x21, 0x09, 0x33, + 0x53, 0x9d, 0x93, 0x60, 0xd4, 0x18, 0xef, 0x71, 0xc8, 0xd1, 0x97, 0x2b, + 0x2d, 0xa0, 0xe3, 0xc3, 0xb7, 0x54, 0x5b, 0xa2, 0xbf, 0x92, 0xa0, 0x48, + 0x15, 0xef, 0x8e, 0x25, 0x02, 0x49, 0x35, 0x20, 0x9e, 0x1b, 0x52, 0xfa, + 0xf9, 0x33, 0x99, 0x31, 0x2c, 0x1b, 0x04, 0x92, 0x8d, 0x19, 0xdb, 0x7d, + 0xad, 0x61, 0x29, 0xe3, 0x5c, 0xb7, 0x94, 0xa6, 0x8b, 0x2e, 0xc5, 0x2e, + 0xbd, 0xe0, 0x60, 0xae, 0xea, 0x93, 0x08, 0x64, 0x98, 0x9e, 0x8e, 0xa1, + 0x2e, 0xf1, 0xe0, 0x31, 0x57, 0x87, 0xd4, 0x77, 0x81, 0x6d, 0xf5, 0xa6, + 0x4c, 0x9b, 0x89, 0x8d, 0x08, 0x96, 0xc5, 0x96, 0xbe, 0x59, 0xcc, 0xbd, + 0x58, 0x7b, 0x21, 0x08, 0x19, 0xc0, 0x55, 0x33, 0x80, 0x44, 0x0d, 0x8e, + 0x59, 0xf9, 0xe8, 0x00, 0x50, 0x98, 0xa2, 0x30, 0xa6, 0xfd, 0xa8, 0x46, + 0xc5, 0x05, 0x65, 0x59, 0xe7, 0x25, 0x1a, 0x17, 0x32, 0x8a, 0xc0, 0x2a, + 0x15, 0x7e, 0x69, 0x11, 0xe9, 0x6d, 0xff, 0x96, 0x52, 0x98, 0xa3, 0xfa, + 0x43, 0x7b, 0x33, 0x79, 0x56, 0xc4, 0xe3, 0x27, 0x40, 0xd6, 0x33, 0xea, + 0xac, 0x87, 0x4e, 0x74, 0xbb, 0xe0, 0x52, 0xab, 0x56, 0x8a, 0xed, 0x3e, + 0xd2, 0x25, 0xb2, 0xbe, 0x58, 0x4c, 0xdf, 0x0e, 0x8f, 0xca, 0x57, 0xdc, + 0x00, 0xfa, 0xb0, 0xc0, 0xf3, 0x7b, 0x6e, 0x41, 0x17, 0x07, 0xcb, 0x08, + 0xe3, 0xd8, 0xa5, 0x04, 0xdb, 0x42, 0x99, 0x67, 0x73, 0xd5, 0xd7, 0x1f, + 0x22, 0x9e, 0xea, 0x66, 0x5f, 0x44, 0x7d, 0xf4, 0xbf, 0x50, 0xb2, 0x3e, + 0x2f, 0x9f, 0x7a, 0xca, 0x80, 0x95, 0x59, 0x83, 0x69, 0x05, 0xec, 0x70, + 0x71, 0x12, 0x97, 0xaf, 0xdb, 0xfd, 0xe8, 0x11, 0x44, 0x8a, 0x6e, 0x09, + 0x90, 0xd5, 0x59, 0x8c, 0x6a, 0x65, 0xf9, 0xa9, 0x3d, 0x3c, 0x0c, 0xf3, + 0x2f, 0xa0, 0xb1, 0x8e, 0xf0, 0x3f, 0x16, 0x63, 0xf6, 0xe8, 0x80, 0x27, + 0x64, 0x56, 0x99, 0x94, 0x93, 0xc8, 0x36, 0x00, 0x21, 0xeb, 0x7c, 0x41, + 0x86, 0xae, 0xb2, 0x4b, 0x7d, 0xac, 0xac, 0x90, 0x8b, 0x99, 0x18, 0x25, + 0xa4, 0x0d, 0x9c, 0x96, 0x53, 0x0c, 0xfa, 0x7e, 0x61, 0xba, 0x7f, 0xca, + 0x61, 0x7b, 0xba, 0x2f, 0x96, 0x2f, 0x75, 0x29, 0x84, 0xb7, 0x32, 0xca, + 0x3b, 0x1f, 0xe6, 0x57, 0x34, 0xf3, 0xf1, 0x58, 0x61, 0xc9, 0x04, 0xa2, + 0x20, 0xea, 0x77, 0xa7, 0x83, 0xed, 0x3e, 0x14, 0x87, 0x8f, 0x82, 0x86, + 0x88, 0xc3, 0xf9, 0x10, 0x7e, 0x03, 0xa4, 0x33, 0xe0, 0x4e, 0x97, 0xef, + 0x66, 0x91, 0x9f, 0xce, 0x85, 0xc8, 0xca, 0x04, 0x3a, 0x8b, 0xf6, 0xc7, + 0xdf, 0xeb, 0x75, 0x31, 0xf4, 0x1a, 0x9a, 0x67, 0xc7, 0xb1, 0xd0, 0x33, + 0x97, 0xea, 0xd2, 0x52, 0xc3, 0x81, 0xdb, 0x63, 0x64, 0x31, 0x0f, 0x9e, + 0x75, 0x5f, 0xde, 0xe7, 0x46, 0x01, 0x19, 0x03, 0xe5, 0x0b, 0xf8, 0x9f, + 0xab, 0x4f, 0x1a, 0x1f, 0xe0, 0xb0, 0x75, 0x96, 0xf2, 0x15, 0x49, 0x63, + 0xa7, 0xae, 0x26, 0xe5, 0x41, 0x82, 0x1b, 0x1e, 0xd0, 0x8b, 0x2e, 0xcc, + 0xf7, 0x30, 0xb3, 0xb5, 0x1b, 0xd9, 0xe7, 0x65, 0x1e, 0x60, 0x3b, 0x74, + 0xfa, 0x52, 0x03, 0xe9, 0x0f, 0x45, 0x87, 0x8c, 0x1a, 0x4d, 0x0d, 0xb9, + 0x90, 0xec, 0xa3, 0x59, 0xad, 0xa2, 0x33, 0xfb, 0xd3, 0xac, 0xf0, 0x58, + 0x0c, 0x6f, 0x27, 0xa9, 0x1b, 0x18, 0xcb, 0x5d, 0x70, 0xcd, 0x14, 0xd9, + 0xe3, 0x9f, 0x42, 0xf7, 0x82, 0x3e, 0x47, 0x39, 0xe4, 0xa2, 0x9e, 0x03, + 0x7c, 0x2f, 0x5d, 0x18, 0x55, 0x20, 0x63, 0x68, 0x38, 0xc8, 0x0b, 0x3d, + 0xf3, 0xc7, 0x91, 0x72, 0x70, 0x4c, 0x8a, 0x0d, 0xc0, 0x80, 0x7d, 0xde, + 0xa3, 0x7f, 0x0a, 0x54, 0xb5, 0xd0, 0x49, 0x42, 0xc3, 0x27, 0x69, 0x19, + 0x1c, 0xbc, 0xf9, 0x5e, 0x16, 0x19, 0xab, 0xdb, 0x99, 0x1e, 0x90, 0x8c, + 0x6e, 0xb9, 0x17, 0x95, 0xad, 0xde, 0x70, 0x93, 0x7f, 0x5f, 0x26, 0xb0, + 0x9f, 0x86, 0xb3, 0x82, 0x9d, 0x30, 0x24, 0x19, 0x83, 0x7d, 0x20, 0x70, + 0x29, 0xe1, 0xb3, 0x22, 0x99, 0xb5, 0xef, 0x8f, 0x7a, 0x66, 0xbc, 0x86, + 0x18, 0x85, 0x39, 0x56, 0x9b, 0x5f, 0xf6, 0x63, 0xc8, 0x24, 0xbc, 0x54, + 0xc3, 0xc3, 0xa3, 0x27, 0xad, 0x80, 0xa9, 0x8f, 0x3b, 0x4b, 0xa0, 0x08, + 0xcc, 0xf9, 0xde, 0x0b, 0x92, 0xa3, 0x26, 0x79, 0x42, 0xa2, 0xd0, 0x7c, + 0xd4, 0x2e, 0x8f, 0x63, 0x4b, 0xaf, 0x6b, 0x7a, 0x58, 0x99, 0xb2, 0xb4, + 0x01, 0x55, 0x75, 0x4a, 0xca, 0x9d, 0xd7, 0x85, 0xa8, 0x28, 0x97, 0x3d, + 0xa6, 0x3c, 0xd1, 0xf6, 0x97, 0xd4, 0x15, 0x45, 0xc4, 0x25, 0x48, 0xd5, + 0xa6, 0x10, 0x00, 0xa4, 0x3e, 0xc4, 0xb4, 0x1d, 0x08, 0xf0, 0xf7, 0x6d, + 0x91, 0xc9, 0xc4, 0x4e, 0xb5, 0xd5, 0x45, 0x07, 0x3e, 0x05, 0x34, 0x44, + 0x14, 0xe1, 0x11, 0x1e, 0xa2, 0xba, 0x37, 0x18, 0xea, 0x16, 0x78, 0x58, + 0x83, 0xfd, 0x98, 0x42, 0xd3, 0x83, 0xac, 0x77, 0x65, 0xc9, 0x5b, 0x36, + 0x8b, 0x9d, 0x76, 0x25, 0x5d, 0xfc, 0x80, 0x21, 0x0e, 0xd8, 0xcb, 0xc4, + 0xcc, 0x59, 0x4e, 0x85, 0x51, 0x3b, 0xc0, 0xa8, 0x0b, 0x5d, 0x60, 0xa2, + 0xc3, 0x2f, 0x2e, 0x8a, 0x18, 0x3c, 0x9d, 0x18, 0x0d, 0xd5, 0xa7, 0x32, + 0x72, 0xf0, 0xef, 0xdf, 0xa4, 0x35, 0x09, 0x75, 0x2f, 0xb0, 0xe0, 0xec, + 0xb2, 0x2d, 0x54, 0xeb, 0x22, 0x01, 0x67, 0x73, 0x61, 0x2d, 0x00, 0x8e, + 0x2a, 0x59, 0xc5, 0xa0, 0xf1, 0xb4, 0x20, 0x15, 0xeb, 0xe5, 0x0b, 0x7d, + 0x6b, 0x70, 0x45, 0x64, 0x3a, 0xc6, 0xbf, 0x34, 0x6a, 0x33, 0x35, 0xf2, + 0x88, 0x47, 0x4d, 0x95, 0x3d, 0x76, 0x7c, 0x56, 0x7a, 0x6a, 0x72, 0x48, + 0xa9, 0x28, 0x32, 0xf6, 0x25, 0xf5, 0x2a, 0x52, 0x40, 0x70, 0x46, 0x93, + 0x4e, 0x86, 0x58, 0xde, 0x11, 0x66, 0x2f, 0xa6, 0x75, 0xbd, 0x24, 0x05, + 0x3c, 0x5e, 0xf4, 0xbc, 0x88, 0xda, 0x69, 0xd0, 0x9d, 0x7f, 0xfa, 0x6b, + 0xf4, 0x50, 0x51, 0x03, 0x26, 0xf6, 0xaa, 0x11, 0xa6, 0x3d, 0x2a, 0xa3, + 0x18, 0x0e, 0xb1, 0x0b, 0x8c, 0x5a, 0x3a, 0xc4, 0x14, 0xd3, 0x9b, 0xea, + 0x2f, 0xf9, 0x5f, 0xbc, 0x9a, 0x94, 0x92, 0x2b, 0xaa, 0xf7, 0x62, 0x0c, + 0xf0, 0xf9, 0xcc, 0x20, 0x1b, 0x5b, 0x56, 0xed, 0xe4, 0x5f, 0xef, 0xa0, + 0x5d, 0xe2, 0xe7, 0x50, 0x0d, 0x13, 0x92, 0x7f, 0x70, 0x68, 0x81, 0x3c, + 0x5d, 0x71, 0x12, 0x14, 0xba, 0xe9, 0xf1, 0x24, 0x70, 0xc3, 0xea, 0x3a, + 0x8e, 0x19, 0xab, 0x4f, 0x5f, 0x20, 0x38, 0xcb, 0xd8, 0x91, 0x3d, 0x47, + 0x8a, 0xb8, 0xe0, 0x81, 0x73, 0x57, 0x19, 0xc4, 0xb6, 0xd7, 0x6e, 0x01, + 0xad, 0xb7, 0x80, 0xb2, 0x44, 0xe7, 0x77, 0x3a, 0x55, 0x9f, 0x36, 0x77, + 0x4c, 0x88, 0x69, 0x6b, 0xc6, 0x67, 0x2f, 0xbd, 0x37, 0x0c, 0xf2, 0x9f, + 0x88, 0x6e, 0xa6, 0xa0, 0xd7, 0x5a, 0x53, 0xf3, 0xc0, 0xaa, 0x7b, 0xca, + 0xc3, 0x07, 0xf4, 0x08, 0xc9, 0x84, 0x0c, 0x36, 0x49, 0x15, 0x71, 0x8c, + 0x60, 0x6f, 0xd5, 0x91, 0xc5, 0x3a, 0x33, 0x3b, 0xde, 0x8c, 0xe0, 0xf4, + 0x08, 0x42, 0x6d, 0xf7, 0x3b, 0xae, 0xd0, 0x06, 0xe5, 0x1b, 0x60, 0x24, + 0xc4, 0xaa, 0xcf, 0x54, 0x0e, 0x78, 0xa6, 0xf9, 0x40, 0x3b, 0xca, 0xed, + 0x5f, 0xa4, 0xd3, 0x13, 0x6e, 0x0a, 0x59, 0x3c, 0xda, 0xdb, 0xc6, 0x6f, + 0x8f, 0x89, 0x31, 0x8c, 0x87, 0x1e, 0xfe, 0x01, 0x16, 0x5b, 0xac, 0x1a, + 0x62, 0x85, 0x39, 0x61, 0xe1, 0x4c, 0xae, 0xae, 0x85, 0xd9, 0x5d, 0x9c, + 0x2a, 0x6e, 0x6b, 0xec, 0xef, 0x2b, 0xc6, 0x32, 0xd6, 0x62, 0x7e, 0x46, + 0x67, 0xe8, 0x6f, 0x4a, 0x50, 0x05, 0x75, 0xda, 0xe0, 0x6b, 0x47, 0xb6, + 0x2a, 0x48, 0x56, 0x3e, 0x22, 0x18, 0xfb, 0xf5, 0x66, 0xf1, 0x42, 0xd6, + 0xf3, 0x3d, 0x26, 0xc8, 0x44, 0xea, 0xa7, 0x9f, 0x90, 0xd8, 0x8f, 0xeb, + 0x45, 0x50, 0x99, 0xf8, 0x86, 0x5f, 0x70, 0x03, 0x89, 0x06, 0x08, 0x92, + 0x02, 0x6a, 0x93, 0x90, 0xce, 0xf2, 0xb5, 0x06, 0x78, 0x1e, 0x96, 0x1d, + 0xa2, 0x20, 0x27, 0x90, 0xfc, 0x07, 0x50, 0x84, 0xf8, 0x19, 0xd0, 0xb1, + 0x0c, 0x75, 0xb6, 0x3b, 0xb2, 0xaa, 0x73, 0x34, 0x49, 0xc9, 0xb2, 0xc2, + 0x58, 0x7e, 0x40, 0x19, 0xa6, 0x08, 0x6b, 0x9e, 0x87, 0xce, 0x7a, 0x27, + 0x3d, 0x7e, 0xe3, 0xe4, 0x12, 0x7c, 0x39, 0x24, 0x16, 0x80, 0x97, 0xbb, + 0x94, 0x9e, 0xa6, 0x0f, 0x5f, 0x42, 0xa1, 0xca, 0x37, 0xa0, 0xbe, 0x1c, + 0x4f, 0x62, 0x68, 0x6a, 0x50, 0x1e, 0x77, 0xe2, 0xb6, 0xdf, 0xa8, 0x89, + 0xdf, 0x98, 0x9b, 0x80, 0xc2, 0x00, 0x21, 0x6a, 0xf6, 0x82, 0xb3, 0x5f, + 0x8b, 0x98, 0x68, 0xe1, 0x76, 0xef, 0x06, 0x8c, 0x24, 0x07, 0xd2, 0xe5, + 0x86, 0xc1, 0xc0, 0x26, 0x94, 0x2d, 0x96, 0xb1, 0xa4, 0x77, 0x11, 0xcb, + 0xa9, 0xf9, 0x46, 0xe8, 0xfd, 0x91, 0x61, 0x9d, 0xce, 0xd1, 0x24, 0x27, + 0xbb, 0xae, 0x68, 0x04, 0x44, 0xc9, 0x44, 0x69, 0x52, 0x05, 0x10, 0x4d, + 0x96, 0x56, 0x98, 0x69, 0x46, 0x49, 0xc2, 0x75, 0xb9, 0xd8, 0x1c, 0x46, + 0xdc, 0x71, 0x86, 0x71, 0x07, 0xea, 0x18, 0x5d, 0x7c, 0x3f, 0x81, 0x97, + 0xfc, 0xc8, 0x88, 0xe4, 0xe7, 0x5e, 0xb2, 0xf8, 0x04, 0x17, 0x14, 0xcf, + 0x24, 0x4c, 0x35, 0x1e, 0x0d, 0xba, 0xf6, 0x0d, 0x29, 0x1c, 0x3c, 0x5a, + 0x0f, 0xd0, 0xf8, 0xf8, 0xdb, 0xa6, 0xa5, 0xf2, 0x83, 0x3d, 0xa0, 0x16, + 0x5d, 0xf4, 0xb7, 0xc0, 0xc5, 0xf1, 0x0f, 0x09, 0x7d, 0x6a, 0x24, 0xf0, + 0x94, 0x03, 0x7d, 0xc0, 0x96, 0x61, 0xa7, 0x44, 0xe2, 0xa3, 0x40, 0xa5, + 0x00, 0x06, 0x13, 0x45, 0xba, 0x60, 0xc3, 0x15, 0x75, 0x6a, 0xe6, 0xd9, + 0x21, 0x37, 0x15, 0x80, 0xfd, 0x68, 0xdf, 0xab, 0x9f, 0xde, 0xcf, 0x4a, + 0x98, 0xf8, 0xa9, 0x27, 0xd3, 0x2d, 0x3d, 0x5c, 0xdc, 0xff, 0x8c, 0x61, + 0x4d, 0x25, 0xc8, 0xdf, 0x66, 0xf6, 0xde, 0xf0, 0x21, 0x5a, 0x27, 0x36, + 0x38, 0x1f, 0xfe, 0x89, 0x33, 0x52, 0x4e, 0x99, 0x55, 0x9b, 0xd0, 0x4f, + 0x15, 0xba, 0x91, 0xdd, 0x95, 0xeb, 0x57, 0x18, 0xc4, 0x21, 0x86, 0x24, + 0x67, 0x38, 0xf0, 0x2f, 0xd2, 0x66, 0x0b, 0x92, 0xb9, 0xc3, 0xcb, 0x8e, + 0x7f, 0x6c, 0x44, 0xbc, 0xcb, 0x45, 0x71, 0x51, 0x61, 0x35, 0xf2, 0x9f, + 0xbf, 0xf2, 0x8f, 0x5a, 0xdb, 0xaa, 0x54, 0x20, 0x1d, 0x52, 0xce, 0x89, + 0xd4, 0xe7, 0x79, 0x75, 0xa7, 0xff, 0x1f, 0x6a, 0xfd, 0xb9, 0x56, 0xee, + 0xca, 0x2c, 0x9f, 0xf3, 0x6d, 0xdf, 0xce, 0x96, 0x0c, 0xaf, 0x2c, 0x2c, + 0x7d, 0x35, 0x79, 0x5d, 0x43, 0xbb, 0x27, 0xaf, 0x07, 0x17, 0x9a, 0x67, + 0xde, 0x3b, 0x35, 0xf1, 0xd9, 0x98, 0x92, 0xf8, 0x3d, 0x35, 0xf1, 0x25, + 0xf8, 0x15, 0xcc, 0x6f, 0x49, 0x9e, 0xba, 0x52, 0xd8, 0x76, 0x71, 0x8d, + 0x90, 0x2c, 0x73, 0x3f, 0xef, 0xa2, 0xef, 0x43, 0x19, 0x2d, 0x83, 0xb9, + 0xde, 0xc6, 0x33, 0x00, 0xd9, 0x9d, 0x50, 0x58, 0xa7, 0xaa, 0x07, 0xa6, + 0x81, 0x8c, 0x1b, 0xe7, 0x9a, 0x3f, 0x11, 0x4c, 0xef, 0x96, 0x07, 0xb3, + 0x48, 0x3e, 0x95, 0x2e, 0xb1, 0x27, 0x2d, 0xf9, 0x50, 0x49, 0x91, 0x7e, + 0x91, 0xd2, 0xcc, 0xf0, 0x36, 0xe8, 0xcd, 0x69, 0x05, 0x68, 0x50, 0xe6, + 0x4d, 0xbe, 0xd7, 0x55, 0xd5, 0xd2, 0xe4, 0x96, 0xd0, 0xa8, 0x1b, 0x4b, + 0x28, 0x54, 0xcb, 0x95, 0x4c, 0xb5, 0xe7, 0x1c, 0x95, 0x2e, 0x11, 0xda, + 0x30, 0x0e, 0x87, 0xfe, 0x5e, 0x34, 0x80, 0x59, 0x9d, 0x70, 0x09, 0x48, + 0x9e, 0xe8, 0x31, 0x12, 0x3f, 0xe2, 0x07, 0x35, 0x74, 0x79, 0x43, 0x68, + 0x42, 0x85, 0x09, 0x85, 0x42, 0x99, 0x5a, 0x92, 0x8c, 0xe9, 0x14, 0x6c, + 0xf3, 0x06, 0x50, 0x2a, 0x6d, 0xd8, 0xd5, 0x06, 0xdc, 0x8c, 0x4d, 0x60, + 0x84, 0x98, 0x6a, 0xf6, 0x30, 0xb9, 0x06, 0xe3, 0xcd, 0x75, 0xd7, 0xaa, + 0xbc, 0x56, 0x8d, 0x2f, 0x6b, 0x0d, 0x2e, 0x26, 0x40, 0x86, 0x08, 0xf5, + 0xc6, 0xee, 0x12, 0xd6, 0x06, 0x59, 0x7f, 0xa6, 0xac, 0x3f, 0xef, 0x15, + 0xd6, 0x20, 0x55, 0x21, 0xa9, 0x29, 0xdb, 0xf4, 0x1f, 0xa6, 0x79, 0x3c, + 0x13, 0xf0, 0x32, 0x7f, 0x25, 0x27, 0x7a, 0x64, 0xe1, 0x64, 0x3b, 0x86, + 0x14, 0x4e, 0xfd, 0x29, 0xbc, 0x6e, 0x9f, 0x1b, 0xaa, 0xdf, 0xe3, 0x77, + 0xd7, 0xb8, 0x7f, 0x61, 0x2f, 0xbc, 0xea, 0xfe, 0x18, 0xc6, 0x54, 0x91, + 0x85, 0xaa, 0x55, 0xa7, 0xca, 0x00, 0x8f, 0x56, 0xaf, 0xa8, 0x49, 0x02, + 0xcb, 0xbe, 0x20, 0x5a +}; diff --git a/BootLoader/source/launch_ds_crt0.s b/BootLoader/source/launch_ds_crt0.s index 51e7896..d8923a4 100755 --- a/BootLoader/source/launch_ds_crt0.s +++ b/BootLoader/source/launch_ds_crt0.s @@ -36,7 +36,7 @@ _start: bl CopyMem @ Start ARM9 binary - ldr r0, =0x027FFE24 + ldr r0, =0x02FFFE24 ldr r1, =_arm9_start str r1, [r0] diff --git a/BootLoader/source/main.arm7.c b/BootLoader/source/main.arm7.c index 5af0327..3de179c 100755 --- a/BootLoader/source/main.arm7.c +++ b/BootLoader/source/main.arm7.c @@ -25,7 +25,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - #ifndef ARM7 # define ARM7 #endif @@ -44,31 +43,44 @@ #define NULL 0 #endif + #include "common.h" +#include "../../arm9/common/launcherData.h" + #include "read_card.h" #include "tonccpy.h" + +#include "crypto.h" +#include "f_xy.h" +#include "dsi.h" +#include "u128_math.h" + /*------------------------------------------------------------------------- External functions --------------------------------------------------------------------------*/ extern void arm7_clearmem (void* loc, size_t len); extern void arm7_reset (void); -static bool useTwlCfg = false; -static int twlCfgLang = 0; -// static bool useShortInit = false; +volatile u16 useTwlCfg = 0; +volatile int twlCfgLang = 0; +// volatile bool useShortInit = false; + +// ALIGN(4) char modcrypt_shared_key[8] = "Nintendo"; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Important things -#define NDS_HEADER 0x027FFE00 -#define NDS_HEADER_POKEMON 0x027FF000 -#define TWL_HEADER 0x027FE000 -#define TMP_HEADER 0x027FC000 +#define NDS_HEADER 0x02FFFE00 +#define NDS_HEADER_POKEMON 0x02FFF000 +#define TWL_HEADER 0x02FFE000 +#define TMP_HEADER 0x02FFC000 tNDSHeader* ndsHeader; -tNDSHeader* tmpHeader; +tTWLHeader* twlHeader; #define REG_GPIO_WIFI *(vu16*)0x4004C04 -static u32 chipID; +// ALIGN(4) u32 chipID = 0; + +// static void memset_addrs_arm7(u32 start, u32 end) { toncset((u32*)start, 0, ((int)end - (int)start)); } static tNDSHeader* loadHeader(tDSiHeader* twlHeaderTemp) { tNDSHeader* ntrHeader = (tNDSHeader*)NDS_HEADER; @@ -80,9 +92,22 @@ static tNDSHeader* loadHeader(tDSiHeader* twlHeaderTemp) { return ntrHeader; } +static void decrypt_modcrypt_area(dsi_context* ctx, u8 *buffer, unsigned int size) { + uint32_t len = size / 0x10; + u8 block[0x10]; + + while (len>0) { + toncset(block, 0, 0x10); + dsi_crypt_ctr_block(ctx, buffer, block); + tonccpy(buffer, block, 0x10); + buffer+=0x10; + len--; + } +} + static void NDSTouchscreenMode(void) { - bool specialSetting = false; + u16 specialSetting = 0; u8 volLevel; static const char list[][4] = { @@ -146,12 +171,12 @@ static void NDSTouchscreenMode(void) { for (unsigned int i = 0; i < sizeof(list) / sizeof(list[0]); i++) { if (memcmp(ndsHeader->gameCode, list[i], 3) == 0) { // Found a match. - specialSetting = true; // Special setting (when found special gamecode) + specialSetting = 0xFFFF; // Special setting (when found special gamecode) break; } } - if (specialSetting) { + if (specialSetting > 0) { // special setting (when found special gamecode) volLevel = 0xAC; } else { @@ -318,9 +343,9 @@ const char* getRomTid(const tNDSHeader* ndsHeader) { return romTid; } -static void errorOutput (u32 code, bool isError) { +ALIGN(4) static void errorOutput (u16 code, u16 isError) { arm9_errorCode = code; - if (isError) { + if (isError > 0) { ipcSendState(ARM7_ERR); while(1); // Stop } @@ -369,16 +394,16 @@ static void arm7_readFirmware(tNDSHeader* ndsHeader) { tonccpy(PersonalData, currentSettings, sizeof(PERSONAL_DATA)); - if (useTwlCfg && (language == 0xFF)) { language = twlCfgLang; } + if ((useTwlCfg > 0) && (language == 0xFF)) { language = twlCfgLang; } - if (language >= 0 && language <= 7) { + if ((language != 0) && (language <= 7)) { // Change language - personalData->language = language; //*(u8*)((u32)ndsHeader - 0x11C) = language; + personalData->language = (unsigned int)language; //*(u8*)((u32)ndsHeader - 0x11C) = language; } - if (personalData->language != 6 && ndsHeader->reserved1[8] == 0x80) { + if ((unsigned int)personalData->language != 6 && ndsHeader->reserved1[8] == 0x80) { ndsHeader->reserved1[8] = 0; // Patch iQue game to be region-free - ndsHeader->headerCRC16 = swiCRC16(0xFFFF, ndsHeader, 0x15E); // Fix CRC + ndsHeader->headerCRC16 = swiCRC16(0xFFFF, ndsHeader, 0x015E); // Fix CRC } } @@ -393,6 +418,7 @@ static void arm7_resetMemory (void) { SCHANNEL_SOURCE(i) = 0; SCHANNEL_LENGTH(i) = 0; } + REG_SOUNDCNT = 0; // Clear out ARM7 DMA channels and timers @@ -411,7 +437,7 @@ static void arm7_resetMemory (void) { REG_IPC_FIFO_CR = 0; // clear IWRAM - 037F:8000 to 0380:FFFF, total 96KiB - arm7_clearmem ((void*)0x037F8000, 96*1024); + // arm7_clearmem ((void*)0x037F8000, 96*1024); // clear most of EXRAM - except after 0x022FD800, which has the ARM9 code // Skip 0x0200000 region if fastBoot enabled. (cart header copy stored here) @@ -420,24 +446,100 @@ static void arm7_resetMemory (void) { } else { arm7_clearmem ((void*)0x02000000, 0x002FD800); }*/ - arm7_clearmem ((void*)0x02000000, 0x002FD800); + + // arm7_clearmem ((void*)0x02000000, 0x002FD000); + // clear last part of EXRAM, skipping the ARM9's section - arm7_clearmem ((void*)0x023FE000, 0x2000); + // arm7_clearmem ((void*)0x023FF800, 0x800); // Clear tmp header region previously used by custom struct - arm7_clearmem ((void*)TMP_HEADER, 0x160); + // arm7_clearmem ((void*)TMP_HEADER, 0x160); + + /*if ((REG_SNDEXTCNT != 0) && (REG_SCFG_EXT & BIT(31)) && (REG_SCFG_ROM != 0x703)) { + memset_addrs_arm7(0x03000000, 0x0380FFC0); + memset_addrs_arm7(0x0380FFD0, 0x03800000 + 0x10000); + } else { + memset_addrs_arm7(0x03800000 - 0x8000, 0x03800000 + 0x10000); + }*/ + // memset_addrs_arm7(0x03000000, 0x0380FFC0); + // memset_addrs_arm7(0x0380FFD0, 0x03800000 + 0x10000); + arm7_clearmem((void*)0x03000000, 0x80FFC0); + arm7_clearmem((void*)0x0380FFD0, 0x30); + + // clear most of EXRAM + arm7_clearmem((void*)0x02000000, 0x3FD000); // clear more of EXRAM, skipping the arm9 temp area used by bootloader + // memset_addrs_arm7(0x02000000, 0x023FD000); // clear more of EXRAM, skipping the arm9 temp area used by bootloader + // memset_addrs_arm7(0x023FD000, 0x023FD7BC); // Leave eMMC CID Mirror intact + // memset_addrs_arm7(0x023FD7CC, 0x02400000); + + arm7_clearmem((void*)0x023FE000, 0x2000); + + if (twlRAM > 0) { + arm7_clearmem((void*)0x02FFE000, 0x2000); + // memset_addrs_arm7(0x02400000, 0x02800000); // Clear the rest of EXRAM + // clear last part of EXRAM + // memset_addrs_arm7(0x02800000, 0x02FFD000); // Leave arm9 temp area intact (used by bootloader) + // memset_addrs_arm7(0x02FFD000, 0x02FFD7BC); // Leave eMMC CID intact + // memset_addrs_arm7(0x02FFD7CC, 0x03000000); + // memset_addrs_arm7(0x02FFE000, 0x03000000); + } REG_IE = 0; REG_IF = ~0; REG_AUXIE = 0; REG_AUXIF = ~0; + REG_POWERCNT = 1; //turn off power to stuffs (*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version (*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version } -static void setMemoryAddress(const tNDSHeader* ndsHeader) { - if (ndsHeader->unitCode > 0) { +static void setMemoryAddress(const tNDSHeader* ndsHeader, u32 ChipID) { + if (ndsHeader->unitCode & BIT(1)) { + copyLoop((u32*)0x02FFFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header + + *(u32*)(0x02FFA680) = 0x02FD4D80; + *(u32*)(0x02FFA684) = 0x00000000; + *(u32*)(0x02FFA688) = 0x00001980; + + *(u32*)(0x02FFF00C) = 0x0000007F; + *(u32*)(0x02FFF010) = 0x550E25B8; + *(u32*)(0x02FFF014) = 0x02FF4000; + + // Set region flag + if (strncmp(getRomTid(ndsHeader)+3, "J", 1) == 0) { + *(u8*)(0x02FFFD70) = 0; + } else if (strncmp(getRomTid(ndsHeader)+3, "E", 1) == 0) { + *(u8*)(0x02FFFD70) = 1; + } else if (strncmp(getRomTid(ndsHeader)+3, "P", 1) == 0) { + *(u8*)(0x02FFFD70) = 2; + } else if (strncmp(getRomTid(ndsHeader)+3, "U", 1) == 0) { + *(u8*)(0x02FFFD70) = 3; + } else if (strncmp(getRomTid(ndsHeader)+3, "C", 1) == 0) { + *(u8*)(0x02FFFD70) = 4; + } else if (strncmp(getRomTid(ndsHeader)+3, "K", 1) == 0) { + *(u8*)(0x02FFFD70) = 5; + } + } + + // Set memory values expected by loaded NDS + // from NitroHax, thanks to Chism + *((u32*)0x02FFF800) = ChipID; // CurrentCardID + // *((u32*)0x02FFF804) = ChipID; // Command10CardID // This does not result in correct v + *((u16*)0x02FFF808) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] + *((u16*)0x02FFF80A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] + *((u16*)0x02FFF850) = 0x5835; + // Copies of above + *((u32*)0x02FFFC00) = ChipID; // CurrentCardID + // *((u32*)0x02FFFC04) = ChipID; // Command10CardID + *((u16*)0x02FFFC08) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] + *((u16*)0x02FFFC0A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] + *((u16*)0x02FFFC10) = 0x5835; + *((u16*)0x02FFFC40) = 0x01; // Boot Indicator -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr +} + +static void setMemoryAddressTWL(const tNDSHeader* ndsHeader, u32 ChipID) { + if (ndsHeader->unitCode & BIT(1)) { copyLoop((u32*)0x027FFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header *(u32*)(0x027FA680) = 0x02FD4D80; @@ -466,23 +568,90 @@ static void setMemoryAddress(const tNDSHeader* ndsHeader) { // Set memory values expected by loaded NDS // from NitroHax, thanks to Chism - *((u32*)0x027FF800) = chipID; // CurrentCardID - *((u32*)0x027FF804) = chipID; // Command10CardID + *((u32*)0x027FF800) = ChipID; // CurrentCardID + // *((u32*)0x027FF804) = ChipID; // Command10CardID *((u16*)0x027FF808) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] *((u16*)0x027FF80A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] *((u16*)0x027FF850) = 0x5835; // Copies of above - *((u32*)0x027FFC00) = chipID; // CurrentCardID - *((u32*)0x027FFC04) = chipID; // Command10CardID + *((u32*)0x027FFC00) = ChipID; // CurrentCardID + // *((u32*)0x027FFC04) = ChipID; // Command10CardID *((u16*)0x027FFC08) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] *((u16*)0x027FFC0A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] *((u16*)0x027FFC10) = 0x5835; *((u16*)0x027FFC40) = 0x01; // Boot Indicator -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr + + tonccpy((u32*)0x027FC000, (u32*)TMP_HEADER, 0x1000); + tonccpy((u32*)0x027FF000, (u32*)NDS_HEADER_POKEMON, 0x170); + tonccpy((u32*)0x027FFE00, (u32*)NDS_HEADER, 0x160); + tonccpy((u32*)0x027FE000, (u32*)TWL_HEADER, 0x1000); + + tonccpy((u32*)0x027FF830, (u32*)0x02FFF830, 0x20); + tonccpy((u32*)0x027FFC80, (u32*)0x02FFFC80, 0x70); + tonccpy((u32*)0x027FFD80, (u32*)0x02FFFD80, 0x70); } -static u32 arm7_loadBinary (void) { - u32 errorCode; +static void arm7_loadExtendedBinary(sNDSHeaderExt* ndsHeader) { + twlHeader = (tTWLHeader*)TWL_HEADER; + + *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; + *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; + *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; + *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; + *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; + REG_MBK6 = *(u32*)0x02FFE1A0; + REG_MBK7 = *(u32*)0x02FFE1A4; + REG_MBK8 = *(u32*)0x02FFE1A8; + REG_MBK9 = *(u32*)0x02FFE1AC; + /*REG_MBK9 = twlHeader->arm9MBKMaster; + REG_MBK6 = twlHeader->arm7MBK6; + REG_MBK7 = twlHeader->arm7MBK7; + REG_MBK8 = twlHeader->arm7MBK8;*/ + + cardRead(ndsHeader, twlHeader->arm9iromOffset, (u32*)twlHeader->arm9idestination, twlHeader->arm9ibinarySize); + cardRead(ndsHeader, twlHeader->arm7iromOffset, (u32*)twlHeader->arm7idestination, twlHeader->arm7ibinarySize); + + if (twlHeader->twlHeaderSettings & BIT(1)) { + char modcrypt_shared_key[9] = { 'N', 'i', 'n', 't', 'e', 'n', 'd', 'o', '\0' }; + int i; + u8 key[16]; + u8 keyp[16]; + + for (i = 0; i < 16; i++) { + key[i] = 0; + keyp[i] = 0; + } + + if (twlHeader->twlHeaderSettings & BIT(3)) { + // Debug Key + tonccpy(key, (u8*)twlHeader, 16); + } else { + u8 *target = (u8*)twlHeader; + tonccpy(keyp, modcrypt_shared_key, 8); + for (int i=0;i<4;i++) { keyp[8+i] = target[0x0c+i]; keyp[15-i] = target[0x0c+i]; } + tonccpy(key, target+0x350, 16); + u128_xor(key, keyp); + u128_add(key, DSi_KEY_MAGIC); + u128_lrot(key, 42); + } + + u32 rk[4]; + tonccpy(rk, key, 16); + dsi_context ctx; + dsi_set_key(&ctx, key); + dsi_set_ctr(&ctx, twlHeader->hmac_arm9); + if (twlHeader->modcrypt1Size != 0) { decrypt_modcrypt_area(&ctx, (u8*)twlHeader->arm9idestination, twlHeader->modcrypt1Size); } + + dsi_set_key(&ctx, key); + dsi_set_ctr(&ctx, twlHeader->hmac_arm7); + if (twlHeader->modcrypt2Size != 0) { decrypt_modcrypt_area(&ctx, (u8*)twlHeader->arm7idestination, twlHeader->modcrypt2Size); } + } +} + +static u16 arm7_loadBinary (void) { + u16 errorCode; + tDSiHeader* twlHeaderTemp = (tDSiHeader*)TMP_HEADER; // Use same region cheat engine goes. Cheat engine will replace this later when it's not needed. // Init card @@ -493,14 +662,16 @@ static u32 arm7_loadBinary (void) { errorCode = cardInit((sNDSHeaderExt*)twlHeaderTemp, &chipID); }*/ - errorCode = cardInit((sNDSHeaderExt*)twlHeaderTemp, &chipID); + errorCode = cardInit((sNDSHeaderExt*)twlHeaderTemp, (u32*)InitialCartChipID); if (errorCode)return errorCode; ndsHeader = loadHeader(twlHeaderTemp); // copy twlHeaderTemp to ndsHeader location - cardRead(ndsHeader->arm9romOffset, (u32*)ndsHeader->arm9destination, ndsHeader->arm9binarySize); - cardRead(ndsHeader->arm7romOffset, (u32*)ndsHeader->arm7destination, ndsHeader->arm7binarySize); + cardRead((sNDSHeaderExt*)twlHeaderTemp, ndsHeader->arm9romOffset, (u32*)ndsHeader->arm9destination, ndsHeader->arm9binarySize); + cardRead((sNDSHeaderExt*)twlHeaderTemp, ndsHeader->arm7romOffset, (u32*)ndsHeader->arm7destination, ndsHeader->arm7binarySize); + + if ((ndsHeader->unitCode & BIT(1)) && (twlMode > 0) && (twlRAM > 0))arm7_loadExtendedBinary((sNDSHeaderExt*)twlHeaderTemp); // Fix Pokemon games needing header data. copyLoop((u32*)NDS_HEADER_POKEMON, (u32*)NDS_HEADER, 0x170); @@ -524,7 +695,7 @@ static u32 arm7_loadBinary (void) { // Main function void arm7_main (void) { - u32 errorCode; + u16 errorCode; // Synchronise start while (ipcRecvState() != ARM9_START); @@ -533,54 +704,44 @@ void arm7_main (void) { // Wait until ARM9 is ready while (ipcRecvState() != ARM9_READY); - if (language != 0xFF)language = (int)launchData->language; - if (launchData->scfgUnlock > 0x00)scfgUnlock = true; - if (launchData->twlMode > 0x00)twlMode = true; - if (launchData->twlCLK > 0x00)twlCLK = true; - if (launchData->debugMode > 0x00)debugMode = true; - // if (launchData->fastBoot > 0x00)useShortInit = true; - - - if (twlMode) { - REG_MBK9=0x0300000F; - REG_MBK6=0x080037C0; - REG_MBK7=0x07C03740; - REG_MBK8=0x07403700; - } else { + if ((twlMode == 0) || (twlRAM == 0) || (isTWLSRL == 0)) { REG_MBK9=0xFCFFFF0F; REG_MBK6=0x09403900; REG_MBK7=0x09803940; REG_MBK8=0x09C03980; } - errorOutput(ERR_STS_CLR_MEM, false); + errorOutput(ERR_STS_CLR_MEM, 0); ipcSendState(ARM7_MEMCLR); // Get ARM7 to clear RAM arm7_resetMemory(); - tmpHeader = (tNDSHeader*)TMP_HEADER; - - if (!twlMode)REG_SCFG_ROM = 0x703; + if (twlMode == 0)REG_SCFG_ROM = 0x703; - errorOutput(ERR_STS_LOAD_BIN, false); + errorOutput(ERR_STS_LOAD_BIN, 0); ipcSendState(ARM7_LOADBIN); // Load the NDS file errorCode = arm7_loadBinary(); - if (errorCode)errorOutput(errorCode, true); + if (errorCode)errorOutput(errorCode, 0xFFFF); - errorOutput(ERR_STS_STARTBIN, false); + errorOutput(ERR_STS_STARTBIN, 0); - tonccpy((u32*)0x023FF000, (u32*)0x027FF000, 0x1000); + if (twlRAM == 0)tonccpy((u32*)0x023FF000, (u32*)0x02FFF000, 0x1000); arm7_readFirmware(ndsHeader); // Header has to be loaded first - if (twlMode) { - REG_SCFG_EXT = 0x92FBFB06; + if (twlMode > 0) { + REG_SCFG_CLK = 0x187; + // REG_SCFG_ROM = 0x501; + // REG_SCFG_EXT = 0x92FBFB06; + // REG_SCFG_EXT = 0x9307F100; + REG_SCFG_EXT = 0x93FFFB06; } else { + REG_SCFG_CLK = 0x107; if (cdcReadReg(CDC_SOUND, 0x22) == 0xF0) { // Switch touch mode to NTR *(u16*)0x4004700 = 0x800F; @@ -588,19 +749,18 @@ void arm7_main (void) { *(u16*)0x4000500 = 0x807F; } REG_GPIO_WIFI |= BIT(8); // Old NDS-Wifi mode - REG_SCFG_EXT = 0x92A00000; + REG_SCFG_EXT = 0x92A40000; } - if (twlCLK) { REG_SCFG_CLK = 0x0187; } else { REG_SCFG_CLK = 0x0101; } - if (scfgUnlock) { REG_SCFG_EXT |= BIT(18); } else { REG_SCFG_EXT &= ~(1UL << 31); } + // if (twlCLK > 0) { REG_SCFG_CLK = 0x187; } else { REG_SCFG_CLK = 0x107; } + if (scfgUnlock == 0)REG_SCFG_EXT &= ~(1UL << 31); /// REG_SCFG_EXT |= BIT(18); + + setMemoryAddress(ndsHeader, *(u32*)InitialCartChipID); - setMemoryAddress(ndsHeader); + if ((twlRAM > 0) && ((twlMode == 0) || (isTWLSRL == 0)))setMemoryAddressTWL(ndsHeader, *(u32*)InitialCartChipID); ipcSendState(ARM7_BOOTBIN); - - // Moved here to prevent interfering with arm9main's new console - REG_POWERCNT = 1; //turn off power to stuffs - + arm7_reset(); } diff --git a/BootLoader/source/main.arm9.c b/BootLoader/source/main.arm9.c index ca1efce..34687ea 100755 --- a/BootLoader/source/main.arm9.c +++ b/BootLoader/source/main.arm9.c @@ -40,39 +40,41 @@ #include "common.h" #include "miniconsole.h" -#define LAUNCH_DATA 0x020007F0 - -volatile int arm9_stateFlag = ARM9_BOOT; -volatile u32 arm9_errorCode = 0xFFFFFFFF; -volatile bool arm9_errorClearBG = false; -volatile bool consoleDebugMode = false; -volatile u32 arm9_BLANK_RAM = 0; -volatile u32 defaultFontPalSlot = 0; +#include "../../arm9/common/launcherData.h" volatile tLauncherSettings* launchData = (tLauncherSettings*)LAUNCH_DATA; -volatile int language = -1; -volatile bool scfgUnlock = false; -volatile bool twlMode = false; -volatile bool twlCLK = false; -volatile bool TWLVRAM = false; -volatile bool debugMode = false; -volatile bool consoleInit = false; - -static char TXT_STATUS[] = "STATUS: "; -static char TXT_ERROR[] = "ERROR: "; -static char ERRTXT_NONE[] = "NONE"; -static char ERRTXT_STS_CLRMEM[] = "CLEAR MEMORY"; -static char ERRTXT_STS_LOAD_BIN[] = "LOAD CART"; -static char ERRTXT_STS_STARTBIN[] = "START BINARY"; -static char ERRTXT_STS_START[] = "BOOTLOADER STARTUP"; -static char ERRTXT_LOAD_NORM[] = "LOAD NORMAL"; -static char ERRTXT_LOAD_OTHR[] = "LOAD OTHER"; -static char ERRTXT_SEC_NORM[] = "SECURE NORMAL"; -static char ERRTXT_SEC_OTHR[] = "SECURE OTHER"; -static char ERRTXT_LOGO_CRC[] = "LOGO CRC"; -static char ERRTXT_HEAD_CRC[] = "HEADER CRC"; -static char NEW_LINE[] = "\n"; +ALIGN(4) volatile int arm9_stateFlag = ARM9_BOOT; +ALIGN(4) volatile u16 arm9_errorCode = 0xFFFF; +ALIGN(4) volatile u16 consoleDebugMode = 0; +ALIGN(4) volatile u32 arm9_BLANK_RAM = 0; +ALIGN(4) volatile u32 defaultFontPalSlot = 0; +ALIGN(4) volatile u32 arm9_cachedChipID = 0xFFFFFFFF; + +ALIGN(4) volatile u16 language = 0xFFFF; +ALIGN(4) volatile u16 scfgUnlock = 0; +ALIGN(4) volatile u16 twlMode = 0; +ALIGN(4) volatile u16 twlCLK = 0; +ALIGN(4) volatile u16 isTWLSRL = 0; +// ALIGN(4) volatile u16 twlVRAM = 0; +ALIGN(4) volatile u16 twlRAM = 0; +ALIGN(4) volatile u16 debugMode = 0; +ALIGN(4) volatile u16 consoleInit = 0; + + +ALIGN(4) const u16 redFont = 0x801B; +ALIGN(4) const u16 greenFont = 0x8360; + +ALIGN(4) char ERRTXT_NONE[14] = "\nSTATUS: NONE"; +ALIGN(4) char ERRTXT_STS_START[22] = "\nSTATUS: LOADER START"; +ALIGN(4) char ERRTXT_STS_CLRMEM[22] = "\nSTATUS: CLEAR MEMORY"; +ALIGN(4) char ERRTXT_STS_LOAD_BIN[19] = "\nSTATUS: LOAD CART"; +ALIGN(4) char ERRTXT_STS_STARTBIN[21] = "\nSTATUS: START BINARY"; +ALIGN(4) char ERRTXT_LOAD_NORM[20] = "\nERROR: LOAD NORMAL"; +ALIGN(4) char ERRTXT_LOAD_OTHR[19] = "\nERROR: LOAD OTHER"; +ALIGN(4) char ERRTXT_SEC_NORM[22] = "\nERROR: SECURE NORMAL"; +ALIGN(4) char ERRTXT_SEC_OTHR[21] = "\nERROR: SECURE OTHER"; +ALIGN(4) char ERRTXT_HEAD_CRC[19] = "\nERROR: HEADER CRC"; /*------------------------------------------------------------------------- External functions @@ -81,72 +83,58 @@ extern void arm9_clearCache (void); extern void arm9_reset (void); extern void Print(char *str); +static void waitForVBlank() { while (REG_VCOUNT!=191); while (REG_VCOUNT==191); } + /*------------------------------------------------------------------------- arm9_errorOutput -Displays an error code on screen. - -Each box is 2 bits, and left-to-right is most-significant bits to least. -Red = 00, Yellow = 01, Green = 10, Blue = 11 - -Written by Chishm +Displays an error text on screen. +Rewritten by Apache Thunder. --------------------------------------------------------------------------*/ -static void arm9_errorOutput (u32 code) { - Print(NEW_LINE); +static void arm9_errorOutput (u16 code) { switch (code) { - case (ERR_NONE) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x8360; - Print(TXT_STATUS); - Print(ERRTXT_NONE); + case ERR_NONE: { + BG_PALETTE_SUB[defaultFontPalSlot] = greenFont; + Print((char*)ERRTXT_NONE); } break; - case (ERR_STS_CLR_MEM) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x8360; - Print(TXT_STATUS); - Print(ERRTXT_STS_CLRMEM); + case ERR_STS_START: { + BG_PALETTE_SUB[defaultFontPalSlot] = greenFont; + Print((char*)ERRTXT_STS_START); } break; - case (ERR_STS_LOAD_BIN) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x8360; - Print(TXT_STATUS); - Print(ERRTXT_STS_LOAD_BIN); + case ERR_STS_CLR_MEM: { + BG_PALETTE_SUB[defaultFontPalSlot] = greenFont; + Print((char*)ERRTXT_STS_CLRMEM); } break; - case (ERR_STS_STARTBIN) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x8360; - Print(TXT_STATUS); - Print(ERRTXT_STS_STARTBIN); + case ERR_STS_LOAD_BIN: { + BG_PALETTE_SUB[defaultFontPalSlot] = greenFont; + Print((char*)ERRTXT_STS_LOAD_BIN); } break; - case (ERR_STS_START) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x8360; - Print(TXT_STATUS); - Print(ERRTXT_STS_START); + case ERR_STS_STARTBIN: { + BG_PALETTE_SUB[defaultFontPalSlot] = greenFont; + Print((char*)ERRTXT_STS_STARTBIN); } break; - case (ERR_LOAD_NORM) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x801B; - Print(TXT_ERROR); - Print(ERRTXT_LOAD_NORM); + case ERR_LOAD_NORM: { + BG_PALETTE_SUB[defaultFontPalSlot] = redFont; + Print((char*)ERRTXT_LOAD_NORM); } break; - case (ERR_LOAD_OTHR) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x801B; - Print(TXT_ERROR); - Print(ERRTXT_LOAD_OTHR); + case ERR_LOAD_OTHR: { + BG_PALETTE_SUB[defaultFontPalSlot] = redFont; + Print((char*)ERRTXT_LOAD_OTHR); } break; - case (ERR_SEC_NORM) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x801B; - Print(TXT_ERROR); - Print(ERRTXT_SEC_NORM); + case ERR_SEC_NORM: { + BG_PALETTE_SUB[defaultFontPalSlot] = redFont; + Print((char*)ERRTXT_SEC_NORM); } break; - case (ERR_SEC_OTHR) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x801B; - Print(TXT_ERROR); - Print(ERRTXT_SEC_OTHR); + case ERR_SEC_OTHR: { + BG_PALETTE_SUB[defaultFontPalSlot] = redFont; + Print((char*)ERRTXT_SEC_OTHR); } break; - case (ERR_LOGO_CRC) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x801B; - Print(TXT_ERROR); - Print(ERRTXT_LOGO_CRC); + case ERR_HEAD_CRC: { + BG_PALETTE_SUB[defaultFontPalSlot] = redFont; + Print((char*)ERRTXT_HEAD_CRC); } break; - case (ERR_HEAD_CRC) : { - BG_PALETTE_SUB[defaultFontPalSlot] = 0x801B; - Print(TXT_ERROR); - Print(ERRTXT_HEAD_CRC); + default: { + BG_PALETTE_SUB[defaultFontPalSlot] = greenFont; + Print((char*)ERR_NONE); } break; } } @@ -159,26 +147,40 @@ Jumps to the ARM9 NDS binary in sync with the ARM7 Written by Darkain, modified by Chishm --------------------------------------------------------------------------*/ void arm9_main (void) { - register int i; + language = 0xFFFF; + scfgUnlock = 0; + twlMode = 0; + twlCLK = 0; + // twlVRAM = 0; + twlRAM = 0; + isTWLSRL = 0; + debugMode = 0; + consoleInit = 0; + + if (launchData->language != 0xFF)language = (u16)launchData->language; + if (launchData->scfgUnlock > 0)scfgUnlock = 0xFFFF; + if (launchData->twlMode > 0)twlMode = 0xFFFF; + // if (launchData->twlVRAM > 0)twlVRAM = 0xFFFF; + if (launchData->twlRAM > 0)twlRAM = 0xFFFF; + if (launchData->twlCLK > 0)twlCLK = 0xFFFF; + if (launchData->isTWLSRL > 0)isTWLSRL = 0xFFFF; + if (launchData->debugMode > 0)debugMode = 0xFFFF; + arm9_cachedChipID = launchData->cachedChipID; - if (launchData->language != 0xFF)language = (u8)launchData->language; - if (launchData->scfgUnlock == 0x01)scfgUnlock = true; - if (launchData->twlMode == 0x01)twlMode = true; - if (launchData->twlVRAM == 0x01)TWLVRAM = true; - if (launchData->twlCLK == 0x01)twlCLK = true; - if (launchData->debugMode == 0x01)debugMode = true; - - if (twlMode) { - *((vu32*)REG_MBK1)=0x8D898581; - *((vu32*)REG_MBK2)=0x8C888480; - *((vu32*)REG_MBK3)=0x9C989490; - *((vu32*)REG_MBK4)=0x8C888480; - *((vu32*)REG_MBK5)=0x9C989490; - REG_MBK6=0x00000000; - REG_MBK7=0x07C03740; - REG_MBK8=0x07403700; + if ((isTWLSRL > 0) && (twlMode > 0) && (twlRAM > 0)) { + *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; + *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; + *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; + *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; + *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; + REG_MBK6 = *(u32*)0x02FFE194; + REG_MBK7 = *(u32*)0x02FFE198; + REG_MBK8 = *(u32*)0x02FFE19C; + REG_MBK9 = *(u32*)0x02FFE1AC; + WRAM_CR = *(u8*)0x02FFE1AF; + scfgUnlock = 0x01; } else { // MBK settings for NTR mode games *((vu32*)REG_MBK1)=0x8D898581; @@ -200,7 +202,7 @@ void arm9_main (void) { REG_IE = 0; REG_IF = ~0; - if (debugMode)arm9_errorCode = ERR_STS_START; + if (debugMode > 0)arm9_errorCode = ERR_STS_START; // Synchronise start ipcSendState(ARM9_START); @@ -276,8 +278,8 @@ void arm9_main (void) { } arm9_errorOutput (arm9_errorCode); // Halt after displaying error code - while(1); - } else if ((arm9_errorCode != ERR_NONE) && debugMode) { + while(1)waitForVBlank(); + } else if ((arm9_errorCode != ERR_NONE) && debugMode > 0) { if (!consoleInit) { BG_PALETTE_SUB[0] = RGB15(31,31,31); BG_PALETTE_SUB[255] = RGB15(0,0,0); @@ -285,13 +287,12 @@ void arm9_main (void) { miniconsoleSetWindow(5, 11, 24, 1); // Set console position for debug text if/when needed. consoleInit = true; } - while(REG_VCOUNT!=191); // Add vblank delay. Arm7 can somtimes go through the status codes pretty quick. - while(REG_VCOUNT==191); + waitForVBlank(); arm9_errorOutput (arm9_errorCode); arm9_errorCode = ERR_NONE; } } - + VRAM_C_CR = 0x80; // BG_PALETTE_SUB[0] = 0xFFFF; dmaFill((void*)&arm9_BLANK_RAM, BG_PALETTE+1, (2*1024)-2); @@ -306,14 +307,16 @@ void arm9_main (void) { videoSetModeSub(0); REG_POWERCNT = 0x820F; - if (!twlCLK)REG_SCFG_CLK = 0x80; - if (twlMode) { - REG_SCFG_EXT = 0x82073100; - REG_SCFG_RST = 1; + /*if ((twlRAM > 0) && ((twlMode == 0) || (isTWLSRL == 0))) { + *((u32*)0x027FF800) = arm9_cachedChipID; + *((u32*)0x027FFC00) = arm9_cachedChipID; } - if (!TWLVRAM)REG_SCFG_EXT &= ~(1UL << 13); - if (!scfgUnlock)REG_SCFG_EXT &= ~(1UL << 31); + *((u32*)0x02FFF800) = arm9_cachedChipID; + *((u32*)0x02FFFC00) = arm9_cachedChipID;*/ + if (twlCLK == 0) { REG_SCFG_CLK = 0x80; } else { REG_SCFG_CLK = 0x87; }; + if (scfgUnlock == 0)REG_SCFG_EXT &= ~(1UL << 31); + arm9_reset(); } diff --git a/BootLoader/source/miniConsole.h b/BootLoader/source/miniConsole.h index 505bf29..a836c9b 100644 --- a/BootLoader/source/miniConsole.h +++ b/BootLoader/source/miniConsole.h @@ -7,27 +7,18 @@ #include #include -static int windowX = 0; -static int windowY = 0; -static int windowWidth = 32; -static int windowHeight = 24; -static int consoleWidth = 32; -static int tabSize = 3; -static int cursorX = 0; -static int cursorY = 0; -static u16 fontCurPal = 1; -static u16* fontBgMap = BG_MAP_RAM_SUB(4); - -static void miniconsoleSetWindow(int x, int y, int width, int height) { - windowX = x; - windowY = y; - windowWidth = width; - windowHeight = height; - cursorX = 0; - cursorY = 0; -} - -void miniNewRow() { +int windowX = 0; +int windowY = 0; +int windowWidth = 32; +int windowHeight = 24; +int consoleWidth = 32; +int tabSize = 3; +int cursorX = 0; +int cursorY = 0; +u16 fontCurPal = 1; +u16* fontBgMap = BG_MAP_RAM_SUB(4); + +static void miniNewRow() { cursorY ++; if(cursorY >= windowHeight) { int rowCount; @@ -44,7 +35,7 @@ void miniNewRow() { } } -void miniconsolePrintChar(char c) { +static void miniconsolePrintChar(char c) { if (c==0) return; if(fontBgMap == 0) return; if(cursorX >= windowWidth) { cursorX = 0; miniNewRow(); } @@ -76,7 +67,7 @@ void miniconsolePrintChar(char c) { } } -void miniconsoleCls(char mode) { +/*static void miniconsoleCls(char mode) { int i = 0; int colTemp,rowTemp; @@ -122,13 +113,22 @@ void miniconsoleCls(char mode) { break; } } +}*/ + + +void miniconsoleSetWindow(int x, int y, int width, int height) { + windowX = x; + windowY = y; + windowWidth = width; + windowHeight = height; + cursorX = 0; + cursorY = 0; } -static void Print(char *str) { +void Print(char *str) { if (str == 0)return; while(*str)miniconsolePrintChar(*(str++)); } - #endif // MINICONSOLE_ARM9_H diff --git a/BootLoader/source/modcrypt/aes.c b/BootLoader/source/modcrypt/aes.c new file mode 100644 index 0000000..6966e93 --- /dev/null +++ b/BootLoader/source/modcrypt/aes.c @@ -0,0 +1,1165 @@ +/* + * FIPS-197 compliant AES implementation + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#include "config.h" + +#if defined(POLARSSL_AES_C) + +#include "aes.h" +#include "padlock.h" + +#include + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +#if defined(POLARSSL_AES_ROM_TABLES) +/* + * Forward S-box + */ +static const unsigned char FSb[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +/* + * Forward tables + */ +#define FT \ +\ + V(A5,63,63,C6), V(84,7C,7C,F8), V(99,77,77,EE), V(8D,7B,7B,F6), \ + V(0D,F2,F2,FF), V(BD,6B,6B,D6), V(B1,6F,6F,DE), V(54,C5,C5,91), \ + V(50,30,30,60), V(03,01,01,02), V(A9,67,67,CE), V(7D,2B,2B,56), \ + V(19,FE,FE,E7), V(62,D7,D7,B5), V(E6,AB,AB,4D), V(9A,76,76,EC), \ + V(45,CA,CA,8F), V(9D,82,82,1F), V(40,C9,C9,89), V(87,7D,7D,FA), \ + V(15,FA,FA,EF), V(EB,59,59,B2), V(C9,47,47,8E), V(0B,F0,F0,FB), \ + V(EC,AD,AD,41), V(67,D4,D4,B3), V(FD,A2,A2,5F), V(EA,AF,AF,45), \ + V(BF,9C,9C,23), V(F7,A4,A4,53), V(96,72,72,E4), V(5B,C0,C0,9B), \ + V(C2,B7,B7,75), V(1C,FD,FD,E1), V(AE,93,93,3D), V(6A,26,26,4C), \ + V(5A,36,36,6C), V(41,3F,3F,7E), V(02,F7,F7,F5), V(4F,CC,CC,83), \ + V(5C,34,34,68), V(F4,A5,A5,51), V(34,E5,E5,D1), V(08,F1,F1,F9), \ + V(93,71,71,E2), V(73,D8,D8,AB), V(53,31,31,62), V(3F,15,15,2A), \ + V(0C,04,04,08), V(52,C7,C7,95), V(65,23,23,46), V(5E,C3,C3,9D), \ + V(28,18,18,30), V(A1,96,96,37), V(0F,05,05,0A), V(B5,9A,9A,2F), \ + V(09,07,07,0E), V(36,12,12,24), V(9B,80,80,1B), V(3D,E2,E2,DF), \ + V(26,EB,EB,CD), V(69,27,27,4E), V(CD,B2,B2,7F), V(9F,75,75,EA), \ + V(1B,09,09,12), V(9E,83,83,1D), V(74,2C,2C,58), V(2E,1A,1A,34), \ + V(2D,1B,1B,36), V(B2,6E,6E,DC), V(EE,5A,5A,B4), V(FB,A0,A0,5B), \ + V(F6,52,52,A4), V(4D,3B,3B,76), V(61,D6,D6,B7), V(CE,B3,B3,7D), \ + V(7B,29,29,52), V(3E,E3,E3,DD), V(71,2F,2F,5E), V(97,84,84,13), \ + V(F5,53,53,A6), V(68,D1,D1,B9), V(00,00,00,00), V(2C,ED,ED,C1), \ + V(60,20,20,40), V(1F,FC,FC,E3), V(C8,B1,B1,79), V(ED,5B,5B,B6), \ + V(BE,6A,6A,D4), V(46,CB,CB,8D), V(D9,BE,BE,67), V(4B,39,39,72), \ + V(DE,4A,4A,94), V(D4,4C,4C,98), V(E8,58,58,B0), V(4A,CF,CF,85), \ + V(6B,D0,D0,BB), V(2A,EF,EF,C5), V(E5,AA,AA,4F), V(16,FB,FB,ED), \ + V(C5,43,43,86), V(D7,4D,4D,9A), V(55,33,33,66), V(94,85,85,11), \ + V(CF,45,45,8A), V(10,F9,F9,E9), V(06,02,02,04), V(81,7F,7F,FE), \ + V(F0,50,50,A0), V(44,3C,3C,78), V(BA,9F,9F,25), V(E3,A8,A8,4B), \ + V(F3,51,51,A2), V(FE,A3,A3,5D), V(C0,40,40,80), V(8A,8F,8F,05), \ + V(AD,92,92,3F), V(BC,9D,9D,21), V(48,38,38,70), V(04,F5,F5,F1), \ + V(DF,BC,BC,63), V(C1,B6,B6,77), V(75,DA,DA,AF), V(63,21,21,42), \ + V(30,10,10,20), V(1A,FF,FF,E5), V(0E,F3,F3,FD), V(6D,D2,D2,BF), \ + V(4C,CD,CD,81), V(14,0C,0C,18), V(35,13,13,26), V(2F,EC,EC,C3), \ + V(E1,5F,5F,BE), V(A2,97,97,35), V(CC,44,44,88), V(39,17,17,2E), \ + V(57,C4,C4,93), V(F2,A7,A7,55), V(82,7E,7E,FC), V(47,3D,3D,7A), \ + V(AC,64,64,C8), V(E7,5D,5D,BA), V(2B,19,19,32), V(95,73,73,E6), \ + V(A0,60,60,C0), V(98,81,81,19), V(D1,4F,4F,9E), V(7F,DC,DC,A3), \ + V(66,22,22,44), V(7E,2A,2A,54), V(AB,90,90,3B), V(83,88,88,0B), \ + V(CA,46,46,8C), V(29,EE,EE,C7), V(D3,B8,B8,6B), V(3C,14,14,28), \ + V(79,DE,DE,A7), V(E2,5E,5E,BC), V(1D,0B,0B,16), V(76,DB,DB,AD), \ + V(3B,E0,E0,DB), V(56,32,32,64), V(4E,3A,3A,74), V(1E,0A,0A,14), \ + V(DB,49,49,92), V(0A,06,06,0C), V(6C,24,24,48), V(E4,5C,5C,B8), \ + V(5D,C2,C2,9F), V(6E,D3,D3,BD), V(EF,AC,AC,43), V(A6,62,62,C4), \ + V(A8,91,91,39), V(A4,95,95,31), V(37,E4,E4,D3), V(8B,79,79,F2), \ + V(32,E7,E7,D5), V(43,C8,C8,8B), V(59,37,37,6E), V(B7,6D,6D,DA), \ + V(8C,8D,8D,01), V(64,D5,D5,B1), V(D2,4E,4E,9C), V(E0,A9,A9,49), \ + V(B4,6C,6C,D8), V(FA,56,56,AC), V(07,F4,F4,F3), V(25,EA,EA,CF), \ + V(AF,65,65,CA), V(8E,7A,7A,F4), V(E9,AE,AE,47), V(18,08,08,10), \ + V(D5,BA,BA,6F), V(88,78,78,F0), V(6F,25,25,4A), V(72,2E,2E,5C), \ + V(24,1C,1C,38), V(F1,A6,A6,57), V(C7,B4,B4,73), V(51,C6,C6,97), \ + V(23,E8,E8,CB), V(7C,DD,DD,A1), V(9C,74,74,E8), V(21,1F,1F,3E), \ + V(DD,4B,4B,96), V(DC,BD,BD,61), V(86,8B,8B,0D), V(85,8A,8A,0F), \ + V(90,70,70,E0), V(42,3E,3E,7C), V(C4,B5,B5,71), V(AA,66,66,CC), \ + V(D8,48,48,90), V(05,03,03,06), V(01,F6,F6,F7), V(12,0E,0E,1C), \ + V(A3,61,61,C2), V(5F,35,35,6A), V(F9,57,57,AE), V(D0,B9,B9,69), \ + V(91,86,86,17), V(58,C1,C1,99), V(27,1D,1D,3A), V(B9,9E,9E,27), \ + V(38,E1,E1,D9), V(13,F8,F8,EB), V(B3,98,98,2B), V(33,11,11,22), \ + V(BB,69,69,D2), V(70,D9,D9,A9), V(89,8E,8E,07), V(A7,94,94,33), \ + V(B6,9B,9B,2D), V(22,1E,1E,3C), V(92,87,87,15), V(20,E9,E9,C9), \ + V(49,CE,CE,87), V(FF,55,55,AA), V(78,28,28,50), V(7A,DF,DF,A5), \ + V(8F,8C,8C,03), V(F8,A1,A1,59), V(80,89,89,09), V(17,0D,0D,1A), \ + V(DA,BF,BF,65), V(31,E6,E6,D7), V(C6,42,42,84), V(B8,68,68,D0), \ + V(C3,41,41,82), V(B0,99,99,29), V(77,2D,2D,5A), V(11,0F,0F,1E), \ + V(CB,B0,B0,7B), V(FC,54,54,A8), V(D6,BB,BB,6D), V(3A,16,16,2C) + +#define V(a,b,c,d) 0x##a##b##c##d +static const unsigned long FT0[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const unsigned long FT1[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const unsigned long FT2[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const unsigned long FT3[256] = { FT }; +#undef V + +#undef FT + +/* + * Reverse S-box + */ +static const unsigned char RSb[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/* + * Reverse tables + */ +#define RT \ +\ + V(50,A7,F4,51), V(53,65,41,7E), V(C3,A4,17,1A), V(96,5E,27,3A), \ + V(CB,6B,AB,3B), V(F1,45,9D,1F), V(AB,58,FA,AC), V(93,03,E3,4B), \ + V(55,FA,30,20), V(F6,6D,76,AD), V(91,76,CC,88), V(25,4C,02,F5), \ + V(FC,D7,E5,4F), V(D7,CB,2A,C5), V(80,44,35,26), V(8F,A3,62,B5), \ + V(49,5A,B1,DE), V(67,1B,BA,25), V(98,0E,EA,45), V(E1,C0,FE,5D), \ + V(02,75,2F,C3), V(12,F0,4C,81), V(A3,97,46,8D), V(C6,F9,D3,6B), \ + V(E7,5F,8F,03), V(95,9C,92,15), V(EB,7A,6D,BF), V(DA,59,52,95), \ + V(2D,83,BE,D4), V(D3,21,74,58), V(29,69,E0,49), V(44,C8,C9,8E), \ + V(6A,89,C2,75), V(78,79,8E,F4), V(6B,3E,58,99), V(DD,71,B9,27), \ + V(B6,4F,E1,BE), V(17,AD,88,F0), V(66,AC,20,C9), V(B4,3A,CE,7D), \ + V(18,4A,DF,63), V(82,31,1A,E5), V(60,33,51,97), V(45,7F,53,62), \ + V(E0,77,64,B1), V(84,AE,6B,BB), V(1C,A0,81,FE), V(94,2B,08,F9), \ + V(58,68,48,70), V(19,FD,45,8F), V(87,6C,DE,94), V(B7,F8,7B,52), \ + V(23,D3,73,AB), V(E2,02,4B,72), V(57,8F,1F,E3), V(2A,AB,55,66), \ + V(07,28,EB,B2), V(03,C2,B5,2F), V(9A,7B,C5,86), V(A5,08,37,D3), \ + V(F2,87,28,30), V(B2,A5,BF,23), V(BA,6A,03,02), V(5C,82,16,ED), \ + V(2B,1C,CF,8A), V(92,B4,79,A7), V(F0,F2,07,F3), V(A1,E2,69,4E), \ + V(CD,F4,DA,65), V(D5,BE,05,06), V(1F,62,34,D1), V(8A,FE,A6,C4), \ + V(9D,53,2E,34), V(A0,55,F3,A2), V(32,E1,8A,05), V(75,EB,F6,A4), \ + V(39,EC,83,0B), V(AA,EF,60,40), V(06,9F,71,5E), V(51,10,6E,BD), \ + V(F9,8A,21,3E), V(3D,06,DD,96), V(AE,05,3E,DD), V(46,BD,E6,4D), \ + V(B5,8D,54,91), V(05,5D,C4,71), V(6F,D4,06,04), V(FF,15,50,60), \ + V(24,FB,98,19), V(97,E9,BD,D6), V(CC,43,40,89), V(77,9E,D9,67), \ + V(BD,42,E8,B0), V(88,8B,89,07), V(38,5B,19,E7), V(DB,EE,C8,79), \ + V(47,0A,7C,A1), V(E9,0F,42,7C), V(C9,1E,84,F8), V(00,00,00,00), \ + V(83,86,80,09), V(48,ED,2B,32), V(AC,70,11,1E), V(4E,72,5A,6C), \ + V(FB,FF,0E,FD), V(56,38,85,0F), V(1E,D5,AE,3D), V(27,39,2D,36), \ + V(64,D9,0F,0A), V(21,A6,5C,68), V(D1,54,5B,9B), V(3A,2E,36,24), \ + V(B1,67,0A,0C), V(0F,E7,57,93), V(D2,96,EE,B4), V(9E,91,9B,1B), \ + V(4F,C5,C0,80), V(A2,20,DC,61), V(69,4B,77,5A), V(16,1A,12,1C), \ + V(0A,BA,93,E2), V(E5,2A,A0,C0), V(43,E0,22,3C), V(1D,17,1B,12), \ + V(0B,0D,09,0E), V(AD,C7,8B,F2), V(B9,A8,B6,2D), V(C8,A9,1E,14), \ + V(85,19,F1,57), V(4C,07,75,AF), V(BB,DD,99,EE), V(FD,60,7F,A3), \ + V(9F,26,01,F7), V(BC,F5,72,5C), V(C5,3B,66,44), V(34,7E,FB,5B), \ + V(76,29,43,8B), V(DC,C6,23,CB), V(68,FC,ED,B6), V(63,F1,E4,B8), \ + V(CA,DC,31,D7), V(10,85,63,42), V(40,22,97,13), V(20,11,C6,84), \ + V(7D,24,4A,85), V(F8,3D,BB,D2), V(11,32,F9,AE), V(6D,A1,29,C7), \ + V(4B,2F,9E,1D), V(F3,30,B2,DC), V(EC,52,86,0D), V(D0,E3,C1,77), \ + V(6C,16,B3,2B), V(99,B9,70,A9), V(FA,48,94,11), V(22,64,E9,47), \ + V(C4,8C,FC,A8), V(1A,3F,F0,A0), V(D8,2C,7D,56), V(EF,90,33,22), \ + V(C7,4E,49,87), V(C1,D1,38,D9), V(FE,A2,CA,8C), V(36,0B,D4,98), \ + V(CF,81,F5,A6), V(28,DE,7A,A5), V(26,8E,B7,DA), V(A4,BF,AD,3F), \ + V(E4,9D,3A,2C), V(0D,92,78,50), V(9B,CC,5F,6A), V(62,46,7E,54), \ + V(C2,13,8D,F6), V(E8,B8,D8,90), V(5E,F7,39,2E), V(F5,AF,C3,82), \ + V(BE,80,5D,9F), V(7C,93,D0,69), V(A9,2D,D5,6F), V(B3,12,25,CF), \ + V(3B,99,AC,C8), V(A7,7D,18,10), V(6E,63,9C,E8), V(7B,BB,3B,DB), \ + V(09,78,26,CD), V(F4,18,59,6E), V(01,B7,9A,EC), V(A8,9A,4F,83), \ + V(65,6E,95,E6), V(7E,E6,FF,AA), V(08,CF,BC,21), V(E6,E8,15,EF), \ + V(D9,9B,E7,BA), V(CE,36,6F,4A), V(D4,09,9F,EA), V(D6,7C,B0,29), \ + V(AF,B2,A4,31), V(31,23,3F,2A), V(30,94,A5,C6), V(C0,66,A2,35), \ + V(37,BC,4E,74), V(A6,CA,82,FC), V(B0,D0,90,E0), V(15,D8,A7,33), \ + V(4A,98,04,F1), V(F7,DA,EC,41), V(0E,50,CD,7F), V(2F,F6,91,17), \ + V(8D,D6,4D,76), V(4D,B0,EF,43), V(54,4D,AA,CC), V(DF,04,96,E4), \ + V(E3,B5,D1,9E), V(1B,88,6A,4C), V(B8,1F,2C,C1), V(7F,51,65,46), \ + V(04,EA,5E,9D), V(5D,35,8C,01), V(73,74,87,FA), V(2E,41,0B,FB), \ + V(5A,1D,67,B3), V(52,D2,DB,92), V(33,56,10,E9), V(13,47,D6,6D), \ + V(8C,61,D7,9A), V(7A,0C,A1,37), V(8E,14,F8,59), V(89,3C,13,EB), \ + V(EE,27,A9,CE), V(35,C9,61,B7), V(ED,E5,1C,E1), V(3C,B1,47,7A), \ + V(59,DF,D2,9C), V(3F,73,F2,55), V(79,CE,14,18), V(BF,37,C7,73), \ + V(EA,CD,F7,53), V(5B,AA,FD,5F), V(14,6F,3D,DF), V(86,DB,44,78), \ + V(81,F3,AF,CA), V(3E,C4,68,B9), V(2C,34,24,38), V(5F,40,A3,C2), \ + V(72,C3,1D,16), V(0C,25,E2,BC), V(8B,49,3C,28), V(41,95,0D,FF), \ + V(71,01,A8,39), V(DE,B3,0C,08), V(9C,E4,B4,D8), V(90,C1,56,64), \ + V(61,84,CB,7B), V(70,B6,32,D5), V(74,5C,6C,48), V(42,57,B8,D0) + +#define V(a,b,c,d) 0x##a##b##c##d +static const unsigned long RT0[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const unsigned long RT1[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const unsigned long RT2[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const unsigned long RT3[256] = { RT }; +#undef V + +#undef RT + +/* + * Round constants + */ +static const unsigned long RCON[10] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x0000001B, 0x00000036 +}; + +#else + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static unsigned long FT0[256]; +static unsigned long FT1[256]; +static unsigned long FT2[256]; +static unsigned long FT3[256]; + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static unsigned long RT0[256]; +static unsigned long RT1[256]; +static unsigned long RT2[256]; +static unsigned long RT3[256]; + +/* + * Round constants + */ +static unsigned long RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) +#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) +#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) + +static int aes_init_done = 0; + +static void aes_gen_tables( void ) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for ( i = 0, x = 1; i < 256; i++ ) + { + pow[i] = x; + log[x] = i; + x = ( x ^ XTIME( x ) ) & 0xFF; + } + + /* + * calculate the round constants + */ + for ( i = 0, x = 1; i < 10; i++ ) + { + RCON[i] = (unsigned long) x; + x = XTIME( x ) & 0xFF; + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for ( i = 1; i < 256; i++ ) + { + x = pow[255 - log[i]]; + + y = x; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for ( i = 0; i < 256; i++ ) + { + x = FSb[i]; + y = XTIME( x ) & 0xFF; + z = ( y ^ x ) & 0xFF; + + FT0[i] = ( (unsigned long) y ) ^ + ( (unsigned long) x << 8 ) ^ + ( (unsigned long) x << 16 ) ^ + ( (unsigned long) z << 24 ); + + FT1[i] = ROTL8( FT0[i] ); + FT2[i] = ROTL8( FT1[i] ); + FT3[i] = ROTL8( FT2[i] ); + + x = RSb[i]; + + RT0[i] = ( (unsigned long) MUL( 0x0E, x ) ) ^ + ( (unsigned long) MUL( 0x09, x ) << 8 ) ^ + ( (unsigned long) MUL( 0x0D, x ) << 16 ) ^ + ( (unsigned long) MUL( 0x0B, x ) << 24 ); + + RT1[i] = ROTL8( RT0[i] ); + RT2[i] = ROTL8( RT1[i] ); + RT3[i] = ROTL8( RT2[i] ); + } +} + +#endif + +/* + * AES key schedule (encryption) + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ) +{ + int i; + unsigned long *RK; + +#if !defined(POLARSSL_AES_ROM_TABLES) + if ( aes_init_done == 0 ) + { + aes_gen_tables(); + aes_init_done = 1; + } +#endif + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(PADLOCK_ALIGN16) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); +#else + ctx->rk = RK = ctx->buf; +#endif + + for ( i = 0; i < (keysize >> 5); i++ ) + { + GET_ULONG_LE( RK[i], key, i << 2 ); + } + + switch( ctx->nr ) + { + case 10: + + for ( i = 0; i < 10; i++, RK += 4 ) + { + RK[4] = RK[0] ^ RCON[i] ^ + ( (unsigned long) FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[3] ) & 0xFF ] << 24 ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for ( i = 0; i < 8; i++, RK += 6 ) + { + RK[6] = RK[0] ^ RCON[i] ^ + ( (unsigned long) FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[5] ) & 0xFF ] << 24 ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for ( i = 0; i < 7; i++, RK += 8 ) + { + RK[8] = RK[0] ^ RCON[i] ^ + ( (unsigned long) FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[7] ) & 0xFF ] << 24 ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ( (unsigned long) FSb[ ( RK[11] ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + + default: + + break; + } + + return( 0 ); +} + +/* + * AES key schedule (decryption) + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize ) +{ + int i, j; + aes_context cty; + unsigned long *RK; + unsigned long *SK; + int ret; + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(PADLOCK_ALIGN16) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); +#else + ctx->rk = RK = ctx->buf; +#endif + + ret = aes_setkey_enc( &cty, key, keysize ); + if ( ret != 0 ) + return( ret ); + + SK = cty.rk + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for ( i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8 ) + { + for ( j = 0; j < 4; j++, SK++ ) + { + *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ + RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ + RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ + RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + memset( &cty, 0, sizeof( aes_context ) ); + + return( 0 ); +} + +#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ + FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ + FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y0 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ + FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ + FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y2 >> 24 ) & 0xFF ]; \ +} + +#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ + RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ + RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y2 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ + RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ + RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y0 >> 24 ) & 0xFF ]; \ +} + +/* + * AES-ECB block encryption/decryption + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int i; + unsigned long *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if ( padlock_supports( PADLOCK_ACE ) ) + { + if ( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + RK = ctx->rk; + + GET_ULONG_LE( X0, input, 0 ); X0 ^= *RK++; + GET_ULONG_LE( X1, input, 4 ); X1 ^= *RK++; + GET_ULONG_LE( X2, input, 8 ); X2 ^= *RK++; + GET_ULONG_LE( X3, input, 12 ); X3 ^= *RK++; + + if ( mode == AES_DECRYPT ) + { + for ( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y0 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y1 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y2 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y3 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + } + else /* AES_ENCRYPT */ + { + for ( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y0 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y1 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y2 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y3 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + } + + PUT_ULONG_LE( X0, output, 0 ); + PUT_ULONG_LE( X1, output, 4 ); + PUT_ULONG_LE( X2, output, 8 ); + PUT_ULONG_LE( X3, output, 12 ); + + return( 0 ); +} + +/* + * AES-CBC buffer encryption/decryption + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int i; + unsigned char temp[16]; + + if ( length % 16 ) + return( POLARSSL_ERR_AES_INVALID_INPUT_LENGTH ); + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if ( padlock_supports( PADLOCK_ACE ) ) + { + if ( padlock_xcryptcbc( ctx, mode, length, iv, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + if ( mode == AES_DECRYPT ) + { + while ( length > 0 ) + { + memcpy( temp, input, 16 ); + aes_crypt_ecb( ctx, mode, input, output ); + + for ( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while ( length > 0 ) + { + for ( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + aes_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + + return( 0 ); +} + +/* + * AES-CFB128 buffer encryption/decryption + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + int length, + int *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, n = *iv_off; + + if ( mode == AES_DECRYPT ) + { + while ( length-- ) + { + if ( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } + else + { + while ( length-- ) + { + if ( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + + return( 0 ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * AES test vectors from: + * + * http://csrc.nist.gov/archive/aes/rijndael/rijndael-vals.zip + */ +static const unsigned char aes_test_ecb_dec[3][16] = +{ + { 0x44, 0x41, 0x6A, 0xC2, 0xD1, 0xF5, 0x3C, 0x58, + 0x33, 0x03, 0x91, 0x7E, 0x6B, 0xE9, 0xEB, 0xE0 }, + { 0x48, 0xE3, 0x1E, 0x9E, 0x25, 0x67, 0x18, 0xF2, + 0x92, 0x29, 0x31, 0x9C, 0x19, 0xF1, 0x5B, 0xA4 }, + { 0x05, 0x8C, 0xCF, 0xFD, 0xBB, 0xCB, 0x38, 0x2D, + 0x1F, 0x6F, 0x56, 0x58, 0x5D, 0x8A, 0x4A, 0xDE } +}; + +static const unsigned char aes_test_ecb_enc[3][16] = +{ + { 0xC3, 0x4C, 0x05, 0x2C, 0xC0, 0xDA, 0x8D, 0x73, + 0x45, 0x1A, 0xFE, 0x5F, 0x03, 0xBE, 0x29, 0x7F }, + { 0xF3, 0xF6, 0x75, 0x2A, 0xE8, 0xD7, 0x83, 0x11, + 0x38, 0xF0, 0x41, 0x56, 0x06, 0x31, 0xB1, 0x14 }, + { 0x8B, 0x79, 0xEE, 0xCC, 0x93, 0xA0, 0xEE, 0x5D, + 0xFF, 0x30, 0xB4, 0xEA, 0x21, 0x63, 0x6D, 0xA4 } +}; + +static const unsigned char aes_test_cbc_dec[3][16] = +{ + { 0xFA, 0xCA, 0x37, 0xE0, 0xB0, 0xC8, 0x53, 0x73, + 0xDF, 0x70, 0x6E, 0x73, 0xF7, 0xC9, 0xAF, 0x86 }, + { 0x5D, 0xF6, 0x78, 0xDD, 0x17, 0xBA, 0x4E, 0x75, + 0xB6, 0x17, 0x68, 0xC6, 0xAD, 0xEF, 0x7C, 0x7B }, + { 0x48, 0x04, 0xE1, 0x81, 0x8F, 0xE6, 0x29, 0x75, + 0x19, 0xA3, 0xE8, 0x8C, 0x57, 0x31, 0x04, 0x13 } +}; + +static const unsigned char aes_test_cbc_enc[3][16] = +{ + { 0x8A, 0x05, 0xFC, 0x5E, 0x09, 0x5A, 0xF4, 0x84, + 0x8A, 0x08, 0xD3, 0x28, 0xD3, 0x68, 0x8E, 0x3D }, + { 0x7B, 0xD9, 0x66, 0xD5, 0x3A, 0xD8, 0xC1, 0xBB, + 0x85, 0xD2, 0xAD, 0xFA, 0xE8, 0x7B, 0xB1, 0x04 }, + { 0xFE, 0x3C, 0x53, 0x65, 0x3E, 0x2F, 0x45, 0xB5, + 0x6F, 0xCD, 0x88, 0xB2, 0xCC, 0x89, 0x8F, 0xF0 } +}; + +/* + * AES-CFB128 test vectors from: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static const unsigned char aes_test_cfb128_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_cfb128_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_cfb128_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_cfb128_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, + 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B, + 0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, + 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF, + 0xC0, 0x4B, 0x05, 0x35, 0x7C, 0x5D, 0x1C, 0x0E, + 0xEA, 0xC4, 0xC6, 0x6F, 0x9F, 0xF7, 0xF2, 0xE6 }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0x67, 0xCE, 0x7F, 0x7F, 0x81, 0x17, 0x36, 0x21, + 0x96, 0x1A, 0x2B, 0x70, 0x17, 0x1D, 0x3D, 0x7A, + 0x2E, 0x1E, 0x8A, 0x1D, 0xD5, 0x9B, 0x88, 0xB1, + 0xC8, 0xE6, 0x0F, 0xED, 0x1E, 0xFA, 0xC4, 0xC9, + 0xC0, 0x5F, 0x9F, 0x9C, 0xA9, 0x83, 0x4F, 0xA0, + 0x42, 0xAE, 0x8F, 0xBA, 0x58, 0x4B, 0x09, 0xFF }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x39, 0xFF, 0xED, 0x14, 0x3B, 0x28, 0xB1, 0xC8, + 0x32, 0x11, 0x3C, 0x63, 0x31, 0xE5, 0x40, 0x7B, + 0xDF, 0x10, 0x13, 0x24, 0x15, 0xE5, 0x4B, 0x92, + 0xA1, 0x3E, 0xD0, 0xA8, 0x26, 0x7A, 0xE2, 0xF9, + 0x75, 0xA3, 0x85, 0x74, 0x1A, 0xB9, 0xCE, 0xF8, + 0x20, 0x31, 0x62, 0x3D, 0x55, 0xB1, 0xE4, 0x71 } +}; + +/* + * Checkup routine + */ +int aes_self_test( int verbose ) +{ + int i, j, u, v, offset; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char prv[16]; + unsigned char iv[16]; + aes_context ctx; + + memset( key, 0, 32 ); + + /* + * ECB mode + */ + for ( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if ( verbose != 0 ) + printf( " AES-ECB-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( buf, 0, 16 ); + + if ( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for ( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if ( memcmp( buf, aes_test_ecb_dec[u], 16 ) != 0 ) + { + if ( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for ( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if ( memcmp( buf, aes_test_ecb_enc[u], 16 ) != 0 ) + { + if ( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if ( verbose != 0 ) + printf( "passed\n" ); + } + + if ( verbose != 0 ) + printf( "\n" ); + + /* + * CBC mode + */ + for ( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if ( verbose != 0 ) + printf( " AES-CBC-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( iv , 0, 16 ); + memset( prv, 0, 16 ); + memset( buf, 0, 16 ); + + if ( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for ( j = 0; j < 10000; j++ ) + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + if ( memcmp( buf, aes_test_cbc_dec[u], 16 ) != 0 ) + { + if ( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for ( j = 0; j < 10000; j++ ) + { + unsigned char tmp[16]; + + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + memcpy( tmp, prv, 16 ); + memcpy( prv, buf, 16 ); + memcpy( buf, tmp, 16 ); + } + + if ( memcmp( prv, aes_test_cbc_enc[u], 16 ) != 0 ) + { + if ( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if ( verbose != 0 ) + printf( "passed\n" ); + } + + if ( verbose != 0 ) + printf( "\n" ); + + /* + * CFB128 mode + */ + for ( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if ( verbose != 0 ) + printf( " AES-CFB128-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, aes_test_cfb128_iv, 16 ); + memcpy( key, aes_test_cfb128_key[u], 16 + u * 8 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + if ( v == AES_DECRYPT ) + { + memcpy( buf, aes_test_cfb128_ct[u], 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if ( memcmp( buf, aes_test_cfb128_pt, 64 ) != 0 ) + { + if ( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + memcpy( buf, aes_test_cfb128_pt, 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if ( memcmp( buf, aes_test_cfb128_ct[u], 64 ) != 0 ) + { + if ( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if ( verbose != 0 ) + printf( "passed\n" ); + } + + + if ( verbose != 0 ) + printf( "\n" ); + + return( 0 ); +} + +#endif + +#endif + diff --git a/BootLoader/source/modcrypt/aes.h b/BootLoader/source/modcrypt/aes.h new file mode 100644 index 0000000..5576680 --- /dev/null +++ b/BootLoader/source/modcrypt/aes.h @@ -0,0 +1,139 @@ +/** + * \file aes.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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. + */ +#ifndef POLARSSL_AES_H +#define POLARSSL_AES_H + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0800 +#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0810 + +/** + * \brief AES context structure + */ +typedef struct +{ + int nr; /*!< number of rounds */ + unsigned long *rk; /*!< AES round keys */ + unsigned long buf[68]; /*!< unaligned data */ +} +aes_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief AES key schedule (encryption) + * + * \param ctx AES context to be initialized + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ); + +/** + * \brief AES key schedule (decryption) + * + * \param ctx AES context to be initialized + * \param key decryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize ); + +/** + * \brief AES-ECB block encryption/decryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if successful + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +/** + * \brief AES-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (16 bytes) + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief AES-CFB128 buffer encryption/decryption. + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + int length, + int *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* aes.h */ diff --git a/BootLoader/source/modcrypt/bignum.c b/BootLoader/source/modcrypt/bignum.c new file mode 100644 index 0000000..b6ce9ee --- /dev/null +++ b/BootLoader/source/modcrypt/bignum.c @@ -0,0 +1,2452 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * The following sources were referenced in the design of this Multi-precision + * Integer library: + * + * [1] Handbook of Applied Cryptography - 1997 + * Menezes, van Oorschot and Vanstone + * + * [2] Multi-Precision Math + * Tom St Denis + * https://github.com/libtom/libtommath/blob/develop/tommath.pdf + * + * [3] GNU Multi-Precision Arithmetic Library + * https://gmplib.org/manual/index.html + * + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_BIGNUM_C) + +#include "bignum.h" +#include "bn_mul.h" + +#include + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +// #include +#include +// #define mbedtls_printf printf +#define mbedtls_calloc calloc +#define mbedtls_free free +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void mbedtls_mpi_zeroize( mbedtls_mpi_uint *v, size_t n ) { + volatile mbedtls_mpi_uint *p = v; while ( n-- ) *p++ = 0; +} + +#define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +#define MPI_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */ + +/* + * Convert between bits/chars and number of limbs + * Divide first in order to avoid potential overflows + */ +#define BITS_TO_LIMBS(i) ( (i) / biL + ( (i) % biL != 0 ) ) +#define CHARS_TO_LIMBS(i) ( (i) / ciL + ( (i) % ciL != 0 ) ) + +/* + * Initialize one MPI + */ +void mbedtls_mpi_init( mbedtls_mpi *X ) +{ + if ( X == NULL ) + return; + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mbedtls_mpi_free( mbedtls_mpi *X ) +{ + if ( X == NULL ) + return; + + if ( X->p != NULL ) + { + mbedtls_mpi_zeroize( X->p, X->n ); + mbedtls_free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs ) +{ + mbedtls_mpi_uint *p; + + if ( nblimbs > MBEDTLS_MPI_MAX_LIMBS ) + return( MBEDTLS_ERR_MPI_ALLOC_FAILED ); + + if ( X->n < nblimbs ) + { + if ( ( p = (mbedtls_mpi_uint*)mbedtls_calloc( nblimbs, ciL ) ) == NULL ) + return( MBEDTLS_ERR_MPI_ALLOC_FAILED ); + + if ( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + mbedtls_mpi_zeroize( X->p, X->n ); + mbedtls_free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Resize down as much as possible, + * while keeping at least the specified number of limbs + */ +int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs ) +{ + mbedtls_mpi_uint *p; + size_t i; + + /* Actually resize up in this case */ + if ( X->n <= nblimbs ) + return( mbedtls_mpi_grow( X, nblimbs ) ); + + for ( i = X->n - 1; i > 0; i-- ) + if ( X->p[i] != 0 ) + break; + i++; + + if ( i < nblimbs ) + i = nblimbs; + + if ( ( p = (mbedtls_mpi_uint*)mbedtls_calloc( i, ciL ) ) == NULL ) + return( MBEDTLS_ERR_MPI_ALLOC_FAILED ); + + if ( X->p != NULL ) + { + memcpy( p, X->p, i * ciL ); + mbedtls_mpi_zeroize( X->p, X->n ); + mbedtls_free( X->p ); + } + + X->n = i; + X->p = p; + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y ) +{ + int ret; + size_t i; + + if ( X == Y ) + return( 0 ); + + if ( Y->p == NULL ) + { + mbedtls_mpi_free( X ); + return( 0 ); + } + + for ( i = Y->n - 1; i > 0; i-- ) + if ( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y ) +{ + mbedtls_mpi T; + + memcpy( &T, X, sizeof( mbedtls_mpi ) ); + memcpy( X, Y, sizeof( mbedtls_mpi ) ); + memcpy( Y, &T, sizeof( mbedtls_mpi ) ); +} + +/* + * Conditionally assign X = Y, without leaking information + * about whether the assignment was made or not. + * (Leaking information about the respective sizes of X and Y is ok however.) + */ +int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign ) +{ + int ret = 0; + size_t i; + + /* make sure assign is 0 or 1 in a time-constant manner */ + assign = (assign | (unsigned char)-assign) >> 7; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) ); + + X->s = X->s * ( 1 - assign ) + Y->s * assign; + + for ( i = 0; i < Y->n; i++ ) + X->p[i] = X->p[i] * ( 1 - assign ) + Y->p[i] * assign; + + for ( ; i < X->n; i++ ) + X->p[i] *= ( 1 - assign ); + +cleanup: + return( ret ); +} + +/* + * Conditionally swap X and Y, without leaking information + * about whether the swap was made or not. + * Here it is not ok to simply swap the pointers, which whould lead to + * different memory access patterns when X and Y are used afterwards. + */ +int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char swap ) +{ + int ret, s; + size_t i; + mbedtls_mpi_uint tmp; + + if ( X == Y ) + return( 0 ); + + /* make sure swap is 0 or 1 in a time-constant manner */ + swap = (swap | (unsigned char)-swap) >> 7; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( Y, X->n ) ); + + s = X->s; + X->s = X->s * ( 1 - swap ) + Y->s * swap; + Y->s = Y->s * ( 1 - swap ) + s * swap; + + + for ( i = 0; i < X->n; i++ ) + { + tmp = X->p[i]; + X->p[i] = X->p[i] * ( 1 - swap ) + Y->p[i] * swap; + Y->p[i] = Y->p[i] * ( 1 - swap ) + tmp * swap; + } + +cleanup: + return( ret ); +} + +/* + * Set value from integer + */ +int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z ) +{ + int ret; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Get a specific bit + */ +int mbedtls_mpi_get_bit( const mbedtls_mpi *X, size_t pos ) +{ + if ( X->n * biL <= pos ) + return( 0 ); + + return( ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01 ); +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mbedtls_mpi_set_bit( mbedtls_mpi *X, size_t pos, unsigned char val ) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + + if ( val != 0 && val != 1 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + if ( X->n * biL <= pos ) + { + if ( val == 0 ) + return( 0 ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, off + 1 ) ); + } + + X->p[off] &= ~( (mbedtls_mpi_uint) 0x01 << idx ); + X->p[off] |= (mbedtls_mpi_uint) val << idx; + +cleanup: + + return( ret ); +} + +/* + * Return the number of less significant zero-bits + */ +size_t mbedtls_mpi_lsb( const mbedtls_mpi *X ) +{ + size_t i, j, count = 0; + + for ( i = 0; i < X->n; i++ ) + for ( j = 0; j < biL; j++, count++ ) + if ( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +/* + * Count leading zero bits in a given integer + */ +static size_t mbedtls_clz( const mbedtls_mpi_uint x ) +{ + size_t j; + mbedtls_mpi_uint mask = (mbedtls_mpi_uint) 1 << (biL - 1); + + for ( j = 0; j < biL; j++ ) + { + if ( x & mask ) break; + + mask >>= 1; + } + + return j; +} + +/* + * Return the number of bits + */ +size_t mbedtls_mpi_bitlen( const mbedtls_mpi *X ) +{ + size_t i, j; + + if ( X->n == 0 ) + return( 0 ); + + for ( i = X->n - 1; i > 0; i-- ) + if ( X->p[i] != 0 ) + break; + + j = biL - mbedtls_clz( X->p[i] ); + + return( ( i * biL ) + j ); +} + +/* + * Return the total size in bytes + */ +size_t mbedtls_mpi_size( const mbedtls_mpi *X ) +{ + return( ( mbedtls_mpi_bitlen( X ) + 7 ) >> 3 ); +} + +#if 0 // not used +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( mbedtls_mpi_uint *d, int radix, char c ) +{ + *d = 255; + + if ( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if ( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if ( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if ( *d >= (mbedtls_mpi_uint) radix ) + return( MBEDTLS_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mbedtls_mpi_read_string( mbedtls_mpi *X, int radix, const char *s ) +{ + int ret; + size_t i, j, slen, n; + mbedtls_mpi_uint d; + mbedtls_mpi T; + + if ( radix < 2 || radix > 16 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + mbedtls_mpi_init( &T ); + + slen = strlen( s ); + + if ( radix == 16 ) + { + if ( slen > MPI_SIZE_T_MAX >> 2 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + n = BITS_TO_LIMBS( slen << 2 ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, n ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); + + for ( i = slen, j = 0; i > 0; i--, j++ ) + { + if ( i == 1 && s[i - 1] == '-' ) + { + X->s = -1; + break; + } + + MBEDTLS_MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) ); + X->p[j / ( 2 * ciL )] |= d << ( ( j % ( 2 * ciL ) ) << 2 ); + } + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); + + for ( i = 0; i < slen; i++ ) + { + if ( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MBEDTLS_MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_int( &T, X, radix ) ); + + if ( X->s == 1 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, &T, d ) ); + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mbedtls_mpi_free( &T ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mbedtls_mpi *X, int radix, char **p ) +{ + int ret; + mbedtls_mpi_uint r; + + if ( radix < 2 || radix > 16 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, X, radix ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_div_int( X, NULL, X, radix ) ); + + if ( mbedtls_mpi_cmp_int( X, 0 ) != 0 ) + MBEDTLS_MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if ( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, + char *buf, size_t buflen, size_t *olen ) +{ + int ret = 0; + size_t n; + char *p; + mbedtls_mpi T; + + if ( radix < 2 || radix > 16 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + n = mbedtls_mpi_bitlen( X ); + if ( radix >= 4 ) n >>= 1; + if ( radix >= 16 ) n >>= 1; + /* + * Round up the buffer length to an even value to ensure that there is + * enough room for hexadecimal values that can be represented in an odd + * number of digits. + */ + n += 3 + ( ( n + 1 ) & 1 ); + + if ( buflen < n ) + { + *olen = n; + return( MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = buf; + mbedtls_mpi_init( &T ); + + if ( X->s == -1 ) + *p++ = '-'; + + if ( radix == 16 ) + { + int c; + size_t i, j, k; + + for ( i = X->n, k = 0; i > 0; i-- ) + { + for ( j = ciL; j > 0; j-- ) + { + c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF; + + if ( c == 0 && k == 0 && ( i + j ) != 2 ) + continue; + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &T, X ) ); + + if ( T.s == -1 ) + T.s = 1; + + MBEDTLS_MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *olen = p - buf; + +cleanup: + + mbedtls_mpi_free( &T ); + + return( ret ); +} +#endif // not used + +#if defined(MBEDTLS_FS_IO) +/* + * Read X from an opened file + */ +int mbedtls_mpi_read_file( mbedtls_mpi *X, int radix, FILE *fin ) +{ + mbedtls_mpi_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ MBEDTLS_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + if ( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( MBEDTLS_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if ( slen == sizeof( s ) - 2 ) + return( MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL ); + + if ( slen > 0 && s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if ( slen > 0 && s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while ( p-- > s ) + if ( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mbedtls_mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mbedtls_mpi_write_file( const char *p, const mbedtls_mpi *X, int radix, FILE *fout ) +{ + int ret; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ MBEDTLS_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_write_string( X, radix, s, sizeof( s ) - 2, &n ) ); + + if ( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if ( fout != NULL ) + { + if ( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( MBEDTLS_ERR_MPI_FILE_IO_ERROR ); + } + else + mbedtls_printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} +#endif /* MBEDTLS_FS_IO */ + +/* + * Import X from unsigned binary data, big endian + */ +int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t i, j, n; + + for ( n = 0; n < buflen; n++ ) + if ( buf[n] != 0 ) + break; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); + + for ( i = buflen, j = 0; i > n; i--, j++ ) + X->p[j / ciL] |= ((mbedtls_mpi_uint) buf[i - 1]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mbedtls_mpi_write_binary( const mbedtls_mpi *X, unsigned char *buf, size_t buflen ) +{ + size_t i, j, n; + + n = mbedtls_mpi_size( X ); + + if ( buflen < n ) + return( MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for ( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mbedtls_mpi_shift_l( mbedtls_mpi *X, size_t count ) +{ + int ret; + size_t i, v0, t1; + mbedtls_mpi_uint r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mbedtls_mpi_bitlen( X ) + count; + + if ( X->n * biL < i ) + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if ( v0 > 0 ) + { + for ( i = X->n; i > v0; i-- ) + X->p[i - 1] = X->p[i - v0 - 1]; + + for ( ; i > 0; i-- ) + X->p[i - 1] = 0; + } + + /* + * shift by count % limb_size + */ + if ( t1 > 0 ) + { + for ( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mbedtls_mpi_shift_r( mbedtls_mpi *X, size_t count ) +{ + size_t i, v0, v1; + mbedtls_mpi_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if ( v0 > X->n || ( v0 == X->n && v1 > 0 ) ) + return mbedtls_mpi_lset( X, 0 ); + + /* + * shift by count / limb_size + */ + if ( v0 > 0 ) + { + for ( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for ( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if ( v1 > 0 ) + { + for ( i = X->n; i > 0; i-- ) + { + r1 = X->p[i - 1] << (biL - v1); + X->p[i - 1] >>= v1; + X->p[i - 1] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y ) +{ + size_t i, j; + + for ( i = X->n; i > 0; i-- ) + if ( X->p[i - 1] != 0 ) + break; + + for ( j = Y->n; j > 0; j-- ) + if ( Y->p[j - 1] != 0 ) + break; + + if ( i == 0 && j == 0 ) + return( 0 ); + + if ( i > j ) return( 1 ); + if ( j > i ) return( -1 ); + + for ( ; i > 0; i-- ) + { + if ( X->p[i - 1] > Y->p[i - 1] ) return( 1 ); + if ( X->p[i - 1] < Y->p[i - 1] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y ) +{ + size_t i, j; + + for ( i = X->n; i > 0; i-- ) + if ( X->p[i - 1] != 0 ) + break; + + for ( j = Y->n; j > 0; j-- ) + if ( Y->p[j - 1] != 0 ) + break; + + if ( i == 0 && j == 0 ) + return( 0 ); + + if ( i > j ) return( X->s ); + if ( j > i ) return( -Y->s ); + + if ( X->s > 0 && Y->s < 0 ) return( 1 ); + if ( Y->s > 0 && X->s < 0 ) return( -1 ); + + for ( ; i > 0; i-- ) + { + if ( X->p[i - 1] > Y->p[i - 1] ) return( X->s ); + if ( X->p[i - 1] < Y->p[i - 1] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z ) +{ + mbedtls_mpi Y; + mbedtls_mpi_uint p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mbedtls_mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret; + size_t i, j; + mbedtls_mpi_uint *o, *p, c, tmp; + + if ( X == B ) + { + const mbedtls_mpi *T = A; A = X; B = T; + } + + if ( X != A ) + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for ( j = B->n; j > 0; j-- ) + if ( B->p[j - 1] != 0 ) + break; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) ); + + o = B->p; p = X->p; c = 0; + + /* + * tmp is used because it might happen that p == o + */ + for ( i = 0; i < j; i++, o++, p++ ) + { + tmp= *o; + *p += c; c = ( *p < c ); + *p += tmp; c += ( *p < tmp ); + } + + while ( c != 0 ) + { + if ( i >= X->n ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; p++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mbedtls_mpi subtraction + */ +static void mpi_sub_hlp( size_t n, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d ) +{ + size_t i; + mbedtls_mpi_uint c, z; + + for ( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + while ( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; i++; d++; + } +} + +/* + * Unsigned subtraction: X = |A| - |B| (HAC 14.9) + */ +int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + mbedtls_mpi TB; + int ret; + size_t n; + + if ( mbedtls_mpi_cmp_abs( A, B ) < 0 ) + return( MBEDTLS_ERR_MPI_NEGATIVE_VALUE ); + + mbedtls_mpi_init( &TB ); + + if ( X == B ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); + B = &TB; + } + + if ( X != A ) + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned subtractions. + */ + X->s = 1; + + ret = 0; + + for ( n = B->n; n > 0; n-- ) + if ( B->p[n - 1] != 0 ) + break; + + mpi_sub_hlp( n, B->p, X->p ); + +cleanup: + + mbedtls_mpi_free( &TB ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret, s = A->s; + + if ( A->s * B->s < 0 ) + { + if ( mbedtls_mpi_cmp_abs( A, B ) >= 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed subtraction: X = A - B + */ +int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret, s = A->s; + + if ( A->s * B->s > 0 ) + { + if ( mbedtls_mpi_cmp_abs( A, B ) >= 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b ) +{ + mbedtls_mpi _B; + mbedtls_mpi_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mbedtls_mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed subtraction: X = A - b + */ +int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b ) +{ + mbedtls_mpi _B; + mbedtls_mpi_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mbedtls_mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mbedtls_mpi multiplication + */ +static +#if defined(__APPLE__) && defined(__arm__) +/* + * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) + * appears to need this to prevent bad ARM code generation at -O3. + */ +__attribute__ ((noinline)) +#endif +void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b ) +{ + mbedtls_mpi_uint c = 0, t = 0; + +#if defined(MULADDC_HUIT) + for ( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for ( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else /* MULADDC_HUIT */ + for ( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for ( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for ( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif /* MULADDC_HUIT */ + + t++; + + do { + *d += c; c = ( *d < c ); d++; + } + while ( c != 0 ); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret; + size_t i, j; + mbedtls_mpi TA, TB; + + mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TB ); + + if ( X == A ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); A = &TA; } + if ( X == B ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); B = &TB; } + + for ( i = A->n; i > 0; i-- ) + if ( A->p[i - 1] != 0 ) + break; + + for ( j = B->n; j > 0; j-- ) + if ( B->p[j - 1] != 0 ) + break; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i + j ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); + + for ( i++; j > 0; j-- ) + mpi_mul_hlp( i - 1, A->p, X->p + j - 1, B->p[j - 1] ); + + X->s = A->s * B->s; + +cleanup: + + mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b ) +{ + mbedtls_mpi _B; + mbedtls_mpi_uint p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mbedtls_mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Unsigned integer divide - double mbedtls_mpi_uint dividend, u1/u0, and + * mbedtls_mpi_uint divisor, d + */ +static mbedtls_mpi_uint mbedtls_int_div_int( mbedtls_mpi_uint u1, + mbedtls_mpi_uint u0, mbedtls_mpi_uint d, mbedtls_mpi_uint *r ) +{ +#if defined(MBEDTLS_HAVE_UDBL) + mbedtls_t_udbl dividend, quotient; +#else + const mbedtls_mpi_uint radix = (mbedtls_mpi_uint) 1 << biH; + const mbedtls_mpi_uint uint_halfword_mask = ( (mbedtls_mpi_uint) 1 << biH ) - 1; + mbedtls_mpi_uint d0, d1, q0, q1, rAX, r0, quotient; + mbedtls_mpi_uint u0_msw, u0_lsw; + size_t s; +#endif + + /* + * Check for overflow + */ + if ( 0 == d || u1 >= d ) + { + if (r != NULL) *r = ~0; + + return ( ~0 ); + } + +#if defined(MBEDTLS_HAVE_UDBL) + dividend = (mbedtls_t_udbl) u1 << biL; + dividend |= (mbedtls_t_udbl) u0; + quotient = dividend / d; + if ( quotient > ( (mbedtls_t_udbl) 1 << biL ) - 1 ) + quotient = ( (mbedtls_t_udbl) 1 << biL ) - 1; + + if ( r != NULL ) + *r = (mbedtls_mpi_uint)( dividend - (quotient * d ) ); + + return (mbedtls_mpi_uint) quotient; +#else + + /* + * Algorithm D, Section 4.3.1 - The Art of Computer Programming + * Vol. 2 - Seminumerical Algorithms, Knuth + */ + + /* + * Normalize the divisor, d, and dividend, u0, u1 + */ + s = mbedtls_clz( d ); + d = d << s; + + u1 = u1 << s; + u1 |= ( u0 >> ( biL - s ) ) & ( -(mbedtls_mpi_sint)s >> ( biL - 1 ) ); + u0 = u0 << s; + + d1 = d >> biH; + d0 = d & uint_halfword_mask; + + u0_msw = u0 >> biH; + u0_lsw = u0 & uint_halfword_mask; + + /* + * Find the first quotient and remainder + */ + q1 = u1 / d1; + r0 = u1 - d1 * q1; + + while ( q1 >= radix || ( q1 * d0 > radix * r0 + u0_msw ) ) + { + q1 -= 1; + r0 += d1; + + if ( r0 >= radix ) break; + } + + rAX = ( u1 * radix ) + ( u0_msw - q1 * d ); + q0 = rAX / d1; + r0 = rAX - q0 * d1; + + while ( q0 >= radix || ( q0 * d0 > radix * r0 + u0_lsw ) ) + { + q0 -= 1; + r0 += d1; + + if ( r0 >= radix ) break; + } + + if (r != NULL) + *r = ( rAX * radix + u0_lsw - q0 * d ) >> s; + + quotient = q1 * radix + q0; + + return quotient; +#endif +} + +/* + * Division by mbedtls_mpi: A = Q * B + R (HAC 14.20) + */ +int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret; + size_t i, n, t, k; + mbedtls_mpi X, Y, Z, T1, T2; + + if ( mbedtls_mpi_cmp_int( B, 0 ) == 0 ) + return( MBEDTLS_ERR_MPI_DIVISION_BY_ZERO ); + + mbedtls_mpi_init( &X ); mbedtls_mpi_init( &Y ); mbedtls_mpi_init( &Z ); + mbedtls_mpi_init( &T1 ); mbedtls_mpi_init( &T2 ); + + if ( mbedtls_mpi_cmp_abs( A, B ) < 0 ) + { + if ( Q != NULL ) MBEDTLS_MPI_CHK( mbedtls_mpi_lset( Q, 0 ) ); + if ( R != NULL ) MBEDTLS_MPI_CHK( mbedtls_mpi_copy( R, A ) ); + return( 0 ); + } + + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &X, A ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &Z, A->n + 2 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &Z, 0 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T1, 2 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T2, 3 ) ); + + k = mbedtls_mpi_bitlen( &Y ) % biL; + if ( k < biL - 1 ) + { + k = biL - 1 - k; + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &X, k ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &Y, biL * ( n - t ) ) ); + + while ( mbedtls_mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &X, &X, &Y ) ); + } + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &Y, biL * ( n - t ) ) ); + + for ( i = n; i > t ; i-- ) + { + if ( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0; + else + { + Z.p[i - t - 1] = mbedtls_int_div_int( X.p[i], X.p[i - 1], + Y.p[t], NULL); + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &T1, 0 ) ); + T1.p[0] = ( t < 1 ) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &T2, 0 ) ); + T2.p[0] = ( i < 2 ) ? 0 : X.p[i - 2]; + T2.p[1] = ( i < 1 ) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while ( mbedtls_mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &T1, biL * ( i - t - 1 ) ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &X, &X, &T1 ) ); + + if ( mbedtls_mpi_cmp_int( &X, 0 ) < 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &T1, &Y ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &T1, biL * ( i - t - 1 ) ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if ( Q != NULL ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( Q, &Z ) ); + Q->s = A->s * B->s; + } + + if ( R != NULL ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &X, k ) ); + X.s = A->s; + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( R, &X ) ); + + if ( mbedtls_mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mbedtls_mpi_free( &X ); mbedtls_mpi_free( &Y ); mbedtls_mpi_free( &Z ); + mbedtls_mpi_free( &T1 ); mbedtls_mpi_free( &T2 ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + */ +int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, mbedtls_mpi_sint b ) +{ + mbedtls_mpi _B; + mbedtls_mpi_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mbedtls_mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mbedtls_mpi_mod_mpi( mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret; + + if ( mbedtls_mpi_cmp_int( B, 0 ) < 0 ) + return( MBEDTLS_ERR_MPI_NEGATIVE_VALUE ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_div_mpi( NULL, R, A, B ) ); + + while ( mbedtls_mpi_cmp_int( R, 0 ) < 0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( R, R, B ) ); + + while ( mbedtls_mpi_cmp_mpi( R, B ) >= 0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_sint b ) +{ + size_t i; + mbedtls_mpi_uint x, y, z; + + if ( b == 0 ) + return( MBEDTLS_ERR_MPI_DIVISION_BY_ZERO ); + + if ( b < 0 ) + return( MBEDTLS_ERR_MPI_NEGATIVE_VALUE ); + + /* + * handle trivial cases + */ + if ( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if ( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for ( i = A->n, y = 0; i > 0; i-- ) + { + x = A->p[i - 1]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if ( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( mbedtls_mpi_uint *mm, const mbedtls_mpi *N ) +{ + mbedtls_mpi_uint x, m0 = N->p[0]; + unsigned int i; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + + for ( i = biL; i >= 8; i /= 2 ) + x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + */ +static int mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *N, mbedtls_mpi_uint mm, + const mbedtls_mpi *T ) +{ + size_t i, n, m; + mbedtls_mpi_uint u0, u1, *d; + + if ( T->n < N->n + 1 || T->p == NULL ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + memset( T->p, 0, T->n * ciL ); + + d = T->p; + n = N->n; + m = ( B->n < n ) ? B->n : n; + + for ( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = A->p[i]; + u1 = ( d[0] + u0 * B->p[0] ) * mm; + + mpi_mul_hlp( m, B->p, d, u0 ); + mpi_mul_hlp( n, N->p, d, u1 ); + + *d++ = u0; d[n + 1] = 0; + } + + memcpy( A->p, d, ( n + 1 ) * ciL ); + + if ( mbedtls_mpi_cmp_abs( A, N ) >= 0 ) + mpi_sub_hlp( n, N->p, A->p ); + else + /* prevent timing attacks */ + mpi_sub_hlp( n, A->p, T->p ); + + return( 0 ); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + */ +static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint mm, const mbedtls_mpi *T ) +{ + mbedtls_mpi_uint z = 1; + mbedtls_mpi U; + + U.n = U.s = (int) z; + U.p = &z; + + return( mpi_montmul( A, &U, N, mm, T ) ); +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *E, const mbedtls_mpi *N, mbedtls_mpi *_RR ) +{ + int ret; + size_t wbits, wsize, one = 1; + size_t i, j, nblimbs; + size_t bufsize, nbits; + mbedtls_mpi_uint ei, mm, state; + mbedtls_mpi RR, T, W[ 2 << MBEDTLS_MPI_WINDOW_SIZE ], Apos; + int neg; + + if ( mbedtls_mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + if ( mbedtls_mpi_cmp_int( E, 0 ) < 0 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + mbedtls_mpi_init( &RR ); mbedtls_mpi_init( &T ); + mbedtls_mpi_init( &Apos ); + memset( W, 0, sizeof( W ) ); + + i = mbedtls_mpi_bitlen( E ); + + wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + + if ( wsize > MBEDTLS_MPI_WINDOW_SIZE ) + wsize = MBEDTLS_MPI_WINDOW_SIZE; + + j = N->n + 1; + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[1], j ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T, j * 2 ) ); + + /* + * Compensate for negative A (and correct at the end) + */ + neg = ( A->s == -1 ); + if ( neg ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Apos, A ) ); + Apos.s = 1; + A = &Apos; + } + + /* + * If 1st call, pre-compute R^2 mod N + */ + if ( _RR == NULL || _RR->p == NULL ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &RR, 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &RR, N->n * 2 * biL ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &RR, &RR, N ) ); + + if ( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mbedtls_mpi ) ); + } + else + memcpy( &RR, _RR, sizeof( mbedtls_mpi ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if ( mbedtls_mpi_cmp_mpi( A, N ) >= 0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &W[1], A, N ) ); + else + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[1], A ) ); + + MBEDTLS_MPI_CHK( mpi_montmul( &W[1], &RR, N, mm, &T ) ); + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &RR ) ); + MBEDTLS_MPI_CHK( mpi_montred( X, N, mm, &T ) ); + + if ( wsize > 1 ) + { + /* + * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + */ + j = one << ( wsize - 1 ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[j], N->n + 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[j], &W[1] ) ); + + for ( i = 0; i < wsize - 1; i++ ) + MBEDTLS_MPI_CHK( mpi_montmul( &W[j], &W[j], N, mm, &T ) ); + + /* + * W[i] = W[i - 1] * W[1] + */ + for ( i = j + 1; i < ( one << wsize ); i++ ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[i], N->n + 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[i], &W[i - 1] ) ); + + MBEDTLS_MPI_CHK( mpi_montmul( &W[i], &W[1], N, mm, &T ) ); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while ( 1 ) + { + if ( bufsize == 0 ) + { + if ( nblimbs == 0 ) + break; + + nblimbs--; + + bufsize = sizeof( mbedtls_mpi_uint ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if ( ei == 0 && state == 0 ) + continue; + + if ( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + MBEDTLS_MPI_CHK( mpi_montmul( X, X, N, mm, &T ) ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= ( ei << ( wsize - nbits ) ); + + if ( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for ( i = 0; i < wsize; i++ ) + MBEDTLS_MPI_CHK( mpi_montmul( X, X, N, mm, &T ) ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + MBEDTLS_MPI_CHK( mpi_montmul( X, &W[wbits], N, mm, &T ) ); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for ( i = 0; i < nbits; i++ ) + { + MBEDTLS_MPI_CHK( mpi_montmul( X, X, N, mm, &T ) ); + + wbits <<= 1; + + if ( ( wbits & ( one << wsize ) ) != 0 ) + MBEDTLS_MPI_CHK( mpi_montmul( X, &W[1], N, mm, &T ) ); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + MBEDTLS_MPI_CHK( mpi_montred( X, N, mm, &T ) ); + + if ( neg && E->n != 0 && ( E->p[0] & 1 ) != 0 ) + { + X->s = -1; + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( X, N, X ) ); + } + +cleanup: + + for ( i = ( one << ( wsize - 1 ) ); i < ( one << wsize ); i++ ) + mbedtls_mpi_free( &W[i] ); + + mbedtls_mpi_free( &W[1] ); mbedtls_mpi_free( &T ); mbedtls_mpi_free( &Apos ); + + if ( _RR == NULL || _RR->p == NULL ) + mbedtls_mpi_free( &RR ); + + return( ret ); +} + +#if 0 // not used +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *B ) +{ + int ret; + size_t lz, lzt; + mbedtls_mpi TG, TA, TB; + + mbedtls_mpi_init( &TG ); mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TB ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); + + lz = mbedtls_mpi_lsb( &TA ); + lzt = mbedtls_mpi_lsb( &TB ); + + if ( lzt < lz ) + lz = lzt; + + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TA, lz ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while ( mbedtls_mpi_cmp_int( &TA, 0 ) != 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TA, mbedtls_mpi_lsb( &TA ) ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TB, mbedtls_mpi_lsb( &TB ) ) ); + + if ( mbedtls_mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( &TA, &TA, &TB ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TA, 1 ) ); + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( &TB, &TB, &TA ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TB, 1 ) ); + } + } + + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &TB, lz ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( G, &TB ) ); + +cleanup: + + mbedtls_mpi_free( &TG ); mbedtls_mpi_free( &TA ); mbedtls_mpi_free( &TB ); + + return( ret ); +} + +/* + * Fill X with size bytes of random. + * + * Use a temporary bytes representation to make sure the result is the same + * regardless of the platform endianness (useful when f_rng is actually + * deterministic, eg for tests). + */ +int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE]; + + if ( size > MBEDTLS_MPI_MAX_SIZE ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + MBEDTLS_MPI_CHK( f_rng( p_rng, buf, size ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( X, buf, size ) ); + +cleanup: + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mbedtls_mpi_inv_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N ) +{ + int ret; + mbedtls_mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if ( mbedtls_mpi_cmp_int( N, 1 ) <= 0 ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TU ); mbedtls_mpi_init( &U1 ); mbedtls_mpi_init( &U2 ); + mbedtls_mpi_init( &G ); mbedtls_mpi_init( &TB ); mbedtls_mpi_init( &TV ); + mbedtls_mpi_init( &V1 ); mbedtls_mpi_init( &V2 ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_gcd( &G, A, N ) ); + + if ( mbedtls_mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &TA, A, N ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TU, &TA ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, N ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TV, N ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &U1, 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &U2, 0 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &V1, 0 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &V2, 1 ) ); + + do + { + while ( ( TU.p[0] & 1 ) == 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TU, 1 ) ); + + if ( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &U1, &U1, &TB ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &U1, 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &U2, 1 ) ); + } + + while ( ( TV.p[0] & 1 ) == 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TV, 1 ) ); + + if ( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &V1, &V1, &TB ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &V1, 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &V2, 1 ) ); + } + + if ( mbedtls_mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &TU, &TU, &TV ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &U1, &U1, &V1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &TV, &TV, &TU ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &V1, &V1, &U1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while ( mbedtls_mpi_cmp_int( &TU, 0 ) != 0 ); + + while ( mbedtls_mpi_cmp_int( &V1, 0 ) < 0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &V1, &V1, N ) ); + + while ( mbedtls_mpi_cmp_mpi( &V1, N ) >= 0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &V1, &V1, N ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &V1 ) ); + +cleanup: + + mbedtls_mpi_free( &TA ); mbedtls_mpi_free( &TU ); mbedtls_mpi_free( &U1 ); mbedtls_mpi_free( &U2 ); + mbedtls_mpi_free( &G ); mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TV ); + mbedtls_mpi_free( &V1 ); mbedtls_mpi_free( &V2 ); + + return( ret ); +} + +#if defined(MBEDTLS_GENPRIME) + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Small divisors test (X must be positive) + * + * Return values: + * 0: no small factor (possible prime, more tests needed) + * 1: certain prime + * MBEDTLS_ERR_MPI_NOT_ACCEPTABLE: certain non-prime + * other negative: error + */ +static int mpi_check_small_factors( const mbedtls_mpi *X ) +{ + int ret = 0; + size_t i; + mbedtls_mpi_uint r; + + if ( ( X->p[0] & 1 ) == 0 ) + return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ); + + for ( i = 0; small_prime[i] > 0; i++ ) + { + if ( mbedtls_mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 1 ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, X, small_prime[i] ) ); + + if ( r == 0 ) + return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ); + } + +cleanup: + return( ret ); +} + +/* + * Miller-Rabin pseudo-primality test (HAC 4.24) + */ +static int mpi_miller_rabin( const mbedtls_mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret, count; + size_t i, j, k, n, s; + mbedtls_mpi W, R, T, A, RR; + + mbedtls_mpi_init( &W ); mbedtls_mpi_init( &R ); mbedtls_mpi_init( &T ); mbedtls_mpi_init( &A ); + mbedtls_mpi_init( &RR ); + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &W, X, 1 ) ); + s = mbedtls_mpi_lsb( &W ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &R, &W ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &R, s ) ); + + i = mbedtls_mpi_bitlen( X ); + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for ( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &A, X->n * ciL, f_rng, p_rng ) ); + + if ( mbedtls_mpi_cmp_mpi( &A, &W ) >= 0 ) + { + j = mbedtls_mpi_bitlen( &A ) - mbedtls_mpi_bitlen( &W ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &A, j + 1 ) ); + } + A.p[0] |= 3; + + count = 0; + do { + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &A, X->n * ciL, f_rng, p_rng ) ); + + j = mbedtls_mpi_bitlen( &A ); + k = mbedtls_mpi_bitlen( &W ); + if (j > k) { + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &A, j - k ) ); + } + + if (count++ > 30) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + + } while ( mbedtls_mpi_cmp_mpi( &A, &W ) >= 0 || + mbedtls_mpi_cmp_int( &A, 1 ) <= 0 ); + + /* + * A = A^R mod |X| + */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if ( mbedtls_mpi_cmp_mpi( &A, &W ) == 0 || + mbedtls_mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while ( j < s && mbedtls_mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T, &A, &A ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &A, &T, X ) ); + + if ( mbedtls_mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if ( mbedtls_mpi_cmp_mpi( &A, &W ) != 0 || + mbedtls_mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + mbedtls_mpi_free( &W ); mbedtls_mpi_free( &R ); mbedtls_mpi_free( &T ); mbedtls_mpi_free( &A ); + mbedtls_mpi_free( &RR ); + + return( ret ); +} + +/* + * Pseudo-primality test: small factors, then Miller-Rabin + */ +int mbedtls_mpi_is_prime( const mbedtls_mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + mbedtls_mpi XX; + + XX.s = 1; + XX.n = X->n; + XX.p = X->p; + + if ( mbedtls_mpi_cmp_int( &XX, 0 ) == 0 || + mbedtls_mpi_cmp_int( &XX, 1 ) == 0 ) + return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ); + + if ( mbedtls_mpi_cmp_int( &XX, 2 ) == 0 ) + return( 0 ); + + if ( ( ret = mpi_check_small_factors( &XX ) ) != 0 ) + { + if ( ret == 1 ) + return( 0 ); + + return( ret ); + } + + return( mpi_miller_rabin( &XX, f_rng, p_rng ) ); +} + +/* + * Prime number generation + */ +int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t k, n; + mbedtls_mpi_uint r; + mbedtls_mpi Y; + + if ( nbits < 3 || nbits > MBEDTLS_MPI_MAX_BITS ) + return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + + mbedtls_mpi_init( &Y ); + + n = BITS_TO_LIMBS( nbits ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( X, n * ciL, f_rng, p_rng ) ); + + k = mbedtls_mpi_bitlen( X ); + if ( k > nbits ) MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, k - nbits + 1 ) ); + + mbedtls_mpi_set_bit( X, nbits-1, 1 ); + + X->p[0] |= 1; + + if ( dh_flag == 0 ) + { + while ( ( ret = mbedtls_mpi_is_prime( X, f_rng, p_rng ) ) != 0 ) + { + if ( ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 2 ) ); + } + } + else + { + /* + * An necessary condition for Y and X = 2Y + 1 to be prime + * is X = 2 mod 3 (which is equivalent to Y = 2 mod 3). + * Make sure it is satisfied, while keeping X = 3 mod 4 + */ + + X->p[0] |= 2; + + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, X, 3 ) ); + if ( r == 0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 8 ) ); + else if ( r == 1 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 4 ) ); + + /* Set Y = (X-1) / 2, which is X / 2 because X is odd */ + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Y, X ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &Y, 1 ) ); + + while ( 1 ) + { + /* + * First, check small factors for X and Y + * before doing Miller-Rabin on any of them + */ + if ( ( ret = mpi_check_small_factors( X ) ) == 0 && + ( ret = mpi_check_small_factors( &Y ) ) == 0 && + ( ret = mpi_miller_rabin( X, f_rng, p_rng ) ) == 0 && + ( ret = mpi_miller_rabin( &Y, f_rng, p_rng ) ) == 0 ) + { + break; + } + + if ( ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + /* + * Next candidates. We want to preserve Y = (X-1) / 2 and + * Y = 1 mod 2 and Y = 2 mod 3 (eq X = 3 mod 4 and X = 2 mod 3) + * so up Y by 6 and X by 12. + */ + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, X, 12 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( &Y, &Y, 6 ) ); + } + } + +cleanup: + + mbedtls_mpi_free( &Y ); + + return( ret ); +} + +#endif /* MBEDTLS_GENPRIME */ + +#if defined(MBEDTLS_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mbedtls_mpi_self_test( int verbose ) +{ + int ret, i; + mbedtls_mpi A, E, N, X, Y, U, V; + + mbedtls_mpi_init( &A ); mbedtls_mpi_init( &E ); mbedtls_mpi_init( &N ); mbedtls_mpi_init( &X ); + mbedtls_mpi_init( &Y ); mbedtls_mpi_init( &U ); mbedtls_mpi_init( &V ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &X, &A, &N ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if ( verbose != 0 ) + mbedtls_printf( " MPI test #1 (mul_mpi): " ); + + if ( mbedtls_mpi_cmp_mpi( &X, &U ) != 0 ) + { + if ( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if ( verbose != 0 ) + mbedtls_printf( "passed\n" ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_div_mpi( &X, &Y, &A, &N ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if ( verbose != 0 ) + mbedtls_printf( " MPI test #2 (div_mpi): " ); + + if ( mbedtls_mpi_cmp_mpi( &X, &U ) != 0 || + mbedtls_mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if ( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if ( verbose != 0 ) + mbedtls_printf( "passed\n" ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if ( verbose != 0 ) + mbedtls_printf( " MPI test #3 (exp_mod): " ); + + if ( mbedtls_mpi_cmp_mpi( &X, &U ) != 0 ) + { + if ( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if ( verbose != 0 ) + mbedtls_printf( "passed\n" ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &X, &A, &N ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if ( verbose != 0 ) + mbedtls_printf( " MPI test #4 (inv_mod): " ); + + if ( mbedtls_mpi_cmp_mpi( &X, &U ) != 0 ) + { + if ( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if ( verbose != 0 ) + mbedtls_printf( "passed\n" ); + + if ( verbose != 0 ) + mbedtls_printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++ ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &X, gcd_pairs[i][0] ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_gcd( &A, &X, &Y ) ); + + if ( mbedtls_mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if ( verbose != 0 ) + mbedtls_printf( "failed at %d\n", i ); + + ret = 1; + goto cleanup; + } + } + + if ( verbose != 0 ) + mbedtls_printf( "passed\n" ); + +cleanup: + + if ( ret != 0 && verbose != 0 ) + mbedtls_printf( "Unexpected error, return code = %08X\n", ret ); + + mbedtls_mpi_free( &A ); mbedtls_mpi_free( &E ); mbedtls_mpi_free( &N ); mbedtls_mpi_free( &X ); + mbedtls_mpi_free( &Y ); mbedtls_mpi_free( &U ); mbedtls_mpi_free( &V ); + + if ( verbose != 0 ) + mbedtls_printf( "\n" ); + + return( ret ); +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif // not used + +#endif /* MBEDTLS_BIGNUM_C */ diff --git a/BootLoader/source/modcrypt/bignum.h b/BootLoader/source/modcrypt/bignum.h new file mode 100644 index 0000000..7bf9a73 --- /dev/null +++ b/BootLoader/source/modcrypt/bignum.h @@ -0,0 +1,761 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#ifndef MBEDTLS_BIGNUM_H +#define MBEDTLS_BIGNUM_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +#define MBEDTLS_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define MBEDTLS_ERR_MPI_ALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MBEDTLS_MPI_CHK(f) do { if ( ( ret = f ) != 0 ) goto cleanup; } while ( 0 ) + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define MBEDTLS_MPI_MAX_LIMBS 10000 + +#if !defined(MBEDTLS_MPI_WINDOW_SIZE) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 << MBEDTLS_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +#endif /* !MBEDTLS_MPI_WINDOW_SIZE */ + +#if !defined(MBEDTLS_MPI_MAX_SIZE) +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can results temporarily in larger MPIs. So the number + * of limbs required (MBEDTLS_MPI_MAX_LIMBS) is higher. + */ +#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ +#endif /* !MBEDTLS_MPI_MAX_SIZE */ + +#define MBEDTLS_MPI_MAX_BITS ( 8 * MBEDTLS_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mbedtls_mpi_read_file() and writing to files with + * mbedtls_mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of MBEDTLS_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * MBEDTLS_MPI_RW_BUFFER_SIZE = ceil(MBEDTLS_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define MBEDTLS_MPI_MAX_BITS_SCALE100 ( 100 * MBEDTLS_MPI_MAX_BITS ) +#define MBEDTLS_LN_2_DIV_LN_10_SCALE100 332 +#define MBEDTLS_MPI_RW_BUFFER_SIZE ( ((MBEDTLS_MPI_MAX_BITS_SCALE100 + MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise. + * + * 32 or 64-bit integer types can be forced regardless of the underlying + * architecture by defining MBEDTLS_HAVE_INT32 or MBEDTLS_HAVE_INT64 + * respectively and undefining MBEDTLS_HAVE_ASM. + * + * Double-width integers (e.g. 128-bit in 64-bit architectures) can be + * disabled by defining MBEDTLS_NO_UDBL_DIVISION. + */ +#if !defined(MBEDTLS_HAVE_INT32) + #if defined(_MSC_VER) && defined(_M_AMD64) + /* Always choose 64-bit when using MSC */ + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* !MBEDTLS_HAVE_INT64 */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #elif defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + ( defined(__sparc__) && defined(__arch64__) ) || \ + defined(__s390x__) || defined(__mips64) ) + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* MBEDTLS_HAVE_INT64 */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) + /* mbedtls_t_udbl defined as 128-bit unsigned int */ + typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI))); + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ + #elif defined(__ARMCC_VERSION) && defined(__aarch64__) + /* + * __ARMCC_VERSION is defined for both armcc and armclang and + * __aarch64__ is only defined by armclang when compiling 64-bit code + */ + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* !MBEDTLS_HAVE_INT64 */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) + /* mbedtls_t_udbl defined as 128-bit unsigned int */ + typedef __uint128_t mbedtls_t_udbl; + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ + #elif defined(MBEDTLS_HAVE_INT64) + /* Force 64-bit integers with unknown compiler */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #endif +#endif /* !MBEDTLS_HAVE_INT32 */ + +#if !defined(MBEDTLS_HAVE_INT64) + /* Default to 32-bit compilation */ + #if !defined(MBEDTLS_HAVE_INT32) + #define MBEDTLS_HAVE_INT32 + #endif /* !MBEDTLS_HAVE_INT32 */ + typedef int32_t mbedtls_mpi_sint; + typedef uint32_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) + typedef uint64_t mbedtls_t_udbl; + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ +#endif /* !MBEDTLS_HAVE_INT64 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + size_t n; /*!< total # of limbs */ + mbedtls_mpi_uint *p; /*!< pointer to limbs */ +} +mbedtls_mpi; + +/** + * \brief Initialize one MPI (make internal references valid) + * This just makes it ready to be set or freed, + * but does not define a value for the MPI. + * + * \param X One MPI to initialize. + */ +void mbedtls_mpi_init( mbedtls_mpi *X ); + +/** + * \brief Unallocate one MPI + * + * \param X One MPI to unallocate. + */ +void mbedtls_mpi_free( mbedtls_mpi *X ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs ); + +/** + * \brief Resize down, keeping at least the specified number of limbs + * + * \param X MPI to shrink + * \param nblimbs The minimum number of limbs to keep + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y ); + +/** + * \brief Safe conditional assignement X = Y if assign is 1 + * + * \param X MPI to conditionally assign to + * \param Y Value to be assigned + * \param assign 1: perform the assignment, 0: keep X's original value + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * + * \note This function is equivalent to + * if ( assign ) mbedtls_mpi_copy( X, Y ); + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + */ +int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign ); + +/** + * \brief Safe conditional swap X <-> Y if swap is 1 + * + * \param X First mbedtls_mpi value + * \param Y Second mbedtls_mpi value + * \param assign 1: perform the swap, 0: keep X and Y's original values + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * + * \note This function is equivalent to + * if ( assign ) mbedtls_mpi_swap( X, Y ); + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + */ +int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char assign ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z ); + +/** + * \brief Get a specific bit from X + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * + * \return Either a 0 or a 1 + */ +int mbedtls_mpi_get_bit( const mbedtls_mpi *X, size_t pos ); + +/** + * \brief Set a bit of X to a specific value of 0 or 1 + * + * \note Will grow X if necessary to set a bit to 1 in a not yet + * existing limb. Will not grow if bit should be set to 0 + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * \param val The value to set the bit to (0 or 1) + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1 + */ +int mbedtls_mpi_set_bit( mbedtls_mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of zero-bits before the least significant + * '1' bit + * + * Note: Thus also the zero-based index of the least significant '1' bit + * + * \param X MPI to use + */ +size_t mbedtls_mpi_lsb( const mbedtls_mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant '1' bit' + * + * Note: Thus also the one-based index of the most significant '1' bit + * + * \param X MPI to use + */ +size_t mbedtls_mpi_bitlen( const mbedtls_mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +size_t mbedtls_mpi_size( const mbedtls_mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code + */ +int mbedtls_mpi_read_string( mbedtls_mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param buf Buffer to write the string to + * \param buflen Length of buf + * \param olen Length of the string written, including final NUL byte + * + * \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code. + * *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with buflen = 0 to obtain the + * minimum required buffer size in *olen. + */ +int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, + char *buf, size_t buflen, size_t *olen ); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Read MPI from a line in an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if + * the file read buffer is too small or a + * MBEDTLS_ERR_MPI_XXX error code + * + * \note On success, this function advances the file stream + * to the end of the current line or to EOF. + * + * The function returns 0 on an empty line. + * + * Leading whitespaces are ignored, as is a + * '0x' prefix for radix 16. + * + */ +int mbedtls_mpi_read_file( mbedtls_mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mbedtls_mpi_write_file( const char *p, const mbedtls_mpi *X, int radix, FILE *fout ); +#endif /* MBEDTLS_FS_IO */ + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian. + * Always fills the whole buffer, which will start with zeros + * if the number is smaller. + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mbedtls_mpi_write_binary( const mbedtls_mpi *X, unsigned char *buf, size_t buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_shift_l( mbedtls_mpi *X, size_t count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_shift_r( mbedtls_mpi *X, size_t count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Unsigned subtraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Signed subtraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b ); + +/** + * \brief Signed subtraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The unsigned integer value to multiply with + * + * \note b is unsigned + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b ); + +/** + * \brief Division by mbedtls_mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, mbedtls_mpi_sint b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * MBEDTLS_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mbedtls_mpi_mod_mpi( mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination mbedtls_mpi_uint + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * MBEDTLS_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_sint b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_BAD_INPUT_DATA if N is negative or even or + * if E is negative + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *E, const mbedtls_mpi *N, mbedtls_mpi *_RR ); + +/** + * \brief Fill an MPI X with size bytes of random + * + * \param X Destination MPI + * \param size Size in bytes + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + */ +int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_BAD_INPUT_DATA if N is <= 1, + MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N. + */ +int mbedtls_mpi_inv_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N ); + +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mbedtls_mpi_is_prime( const mbedtls_mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits + * ( 3 <= nbits <= MBEDTLS_MPI_MAX_BITS ) + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed, + * MBEDTLS_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/BootLoader/source/modcrypt/bn_mul.h b/BootLoader/source/modcrypt/bn_mul.h new file mode 100644 index 0000000..9210dd1 --- /dev/null +++ b/BootLoader/source/modcrypt/bn_mul.h @@ -0,0 +1,887 @@ +/** + * \file bn_mul.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef MBEDTLS_BN_MUL_H +#define MBEDTLS_BN_MUL_H + +#include "bignum.h" + +#if defined(MBEDTLS_HAVE_ASM) + +#ifndef asm +#define asm __asm +#endif + +/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */ +#if defined(__GNUC__) && \ + ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 ) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( \ + "movl %%ebx, %0 \n\t" \ + "movl %5, %%esi \n\t" \ + "movl %6, %%edi \n\t" \ + "movl %7, %%ecx \n\t" \ + "movl %8, %%ebx \n\t" + +#define MULADDC_CORE \ + "lodsl \n\t" \ + "mull %%ebx \n\t" \ + "addl %%ecx, %%eax \n\t" \ + "adcl $0, %%edx \n\t" \ + "addl (%%edi), %%eax \n\t" \ + "adcl $0, %%edx \n\t" \ + "movl %%edx, %%ecx \n\t" \ + "stosl \n\t" + +#if defined(MBEDTLS_HAVE_SSE2) + +#define MULADDC_HUIT \ + "movd %%ecx, %%mm1 \n\t" \ + "movd %%ebx, %%mm0 \n\t" \ + "movd (%%edi), %%mm3 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd (%%esi), %%mm2 \n\t" \ + "pmuludq %%mm0, %%mm2 \n\t" \ + "movd 4(%%esi), %%mm4 \n\t" \ + "pmuludq %%mm0, %%mm4 \n\t" \ + "movd 8(%%esi), %%mm6 \n\t" \ + "pmuludq %%mm0, %%mm6 \n\t" \ + "movd 12(%%esi), %%mm7 \n\t" \ + "pmuludq %%mm0, %%mm7 \n\t" \ + "paddq %%mm2, %%mm1 \n\t" \ + "movd 4(%%edi), %%mm3 \n\t" \ + "paddq %%mm4, %%mm3 \n\t" \ + "movd 8(%%edi), %%mm5 \n\t" \ + "paddq %%mm6, %%mm5 \n\t" \ + "movd 12(%%edi), %%mm4 \n\t" \ + "paddq %%mm4, %%mm7 \n\t" \ + "movd %%mm1, (%%edi) \n\t" \ + "movd 16(%%esi), %%mm2 \n\t" \ + "pmuludq %%mm0, %%mm2 \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd 20(%%esi), %%mm4 \n\t" \ + "pmuludq %%mm0, %%mm4 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd 24(%%esi), %%mm6 \n\t" \ + "pmuludq %%mm0, %%mm6 \n\t" \ + "movd %%mm1, 4(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd 28(%%esi), %%mm3 \n\t" \ + "pmuludq %%mm0, %%mm3 \n\t" \ + "paddq %%mm5, %%mm1 \n\t" \ + "movd 16(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm2 \n\t" \ + "movd %%mm1, 8(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm7, %%mm1 \n\t" \ + "movd 20(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm4 \n\t" \ + "movd %%mm1, 12(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm2, %%mm1 \n\t" \ + "movd 24(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm6 \n\t" \ + "movd %%mm1, 16(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm4, %%mm1 \n\t" \ + "movd 28(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm3 \n\t" \ + "movd %%mm1, 20(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm6, %%mm1 \n\t" \ + "movd %%mm1, 24(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd %%mm1, 28(%%edi) \n\t" \ + "addl $32, %%edi \n\t" \ + "addl $32, %%esi \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd %%mm1, %%ecx \n\t" + +#define MULADDC_STOP \ + "emms \n\t" \ + "movl %4, %%ebx \n\t" \ + "movl %%ecx, %1 \n\t" \ + "movl %%edi, %2 \n\t" \ + "movl %%esi, %3 \n\t" \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + "movl %4, %%ebx \n\t" \ + "movl %%ecx, %1 \n\t" \ + "movl %%edi, %2 \n\t" \ + "movl %%esi, %3 \n\t" \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( \ + "xorq %%r8, %%r8 \n\t" + +#define MULADDC_CORE \ + "movq (%%rsi), %%rax \n\t" \ + "mulq %%rbx \n\t" \ + "addq $8, %%rsi \n\t" \ + "addq %%rcx, %%rax \n\t" \ + "movq %%r8, %%rcx \n\t" \ + "adcq $0, %%rdx \n\t" \ + "nop \n\t" \ + "addq %%rax, (%%rdi) \n\t" \ + "adcq %%rdx, %%rcx \n\t" \ + "addq $8, %%rdi \n\t" + +#define MULADDC_STOP \ + : "+c" (c), "+D" (d), "+S" (s) \ + : "b" (b) \ + : "rax", "rdx", "r8" \ + ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( \ + "movl %3, %%a2 \n\t" \ + "movl %4, %%a3 \n\t" \ + "movl %5, %%d3 \n\t" \ + "movl %6, %%d2 \n\t" \ + "moveq #0, %%d0 \n\t" + +#define MULADDC_CORE \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "moveq #0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "addxl %%d4, %%d3 \n\t" + +#define MULADDC_STOP \ + "movl %%d3, %0 \n\t" \ + "movl %%a3, %1 \n\t" \ + "movl %%a2, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "d0", "d1", "d2", "d3", "d4", "a2", "a3" \ + ); + +#define MULADDC_HUIT \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "addxl %%d0, %%d3 \n\t" + +#endif /* MC68000 */ + +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( \ + "ld r3, %3 \n\t" \ + "ld r4, %4 \n\t" \ + "ld r5, %5 \n\t" \ + "ld r6, %6 \n\t" \ + "addi r3, r3, -8 \n\t" \ + "addi r4, r4, -8 \n\t" \ + "addic r5, r5, 0 \n\t" + +#define MULADDC_CORE \ + "ldu r7, 8(r3) \n\t" \ + "mulld r8, r7, r6 \n\t" \ + "mulhdu r9, r7, r6 \n\t" \ + "adde r8, r8, r5 \n\t" \ + "ld r7, 8(r4) \n\t" \ + "addze r5, r9 \n\t" \ + "addc r8, r8, r7 \n\t" \ + "stdu r8, 8(r4) \n\t" + +#define MULADDC_STOP \ + "addze r5, r5 \n\t" \ + "addi r4, r4, 8 \n\t" \ + "addi r3, r3, 8 \n\t" \ + "std r5, %0 \n\t" \ + "std r4, %1 \n\t" \ + "std r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + + +#else /* __MACH__ && __APPLE__ */ + +#define MULADDC_INIT \ + asm( \ + "ld %%r3, %3 \n\t" \ + "ld %%r4, %4 \n\t" \ + "ld %%r5, %5 \n\t" \ + "ld %%r6, %6 \n\t" \ + "addi %%r3, %%r3, -8 \n\t" \ + "addi %%r4, %%r4, -8 \n\t" \ + "addic %%r5, %%r5, 0 \n\t" + +#define MULADDC_CORE \ + "ldu %%r7, 8(%%r3) \n\t" \ + "mulld %%r8, %%r7, %%r6 \n\t" \ + "mulhdu %%r9, %%r7, %%r6 \n\t" \ + "adde %%r8, %%r8, %%r5 \n\t" \ + "ld %%r7, 8(%%r4) \n\t" \ + "addze %%r5, %%r9 \n\t" \ + "addc %%r8, %%r8, %%r7 \n\t" \ + "stdu %%r8, 8(%%r4) \n\t" + +#define MULADDC_STOP \ + "addze %%r5, %%r5 \n\t" \ + "addi %%r4, %%r4, 8 \n\t" \ + "addi %%r3, %%r3, 8 \n\t" \ + "std %%r5, %0 \n\t" \ + "std %%r4, %1 \n\t" \ + "std %%r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#endif /* __MACH__ && __APPLE__ */ + +#elif defined(__powerpc__) || defined(__ppc__) /* end PPC64/begin PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( \ + "lwz r3, %3 \n\t" \ + "lwz r4, %4 \n\t" \ + "lwz r5, %5 \n\t" \ + "lwz r6, %6 \n\t" \ + "addi r3, r3, -4 \n\t" \ + "addi r4, r4, -4 \n\t" \ + "addic r5, r5, 0 \n\t" + +#define MULADDC_CORE \ + "lwzu r7, 4(r3) \n\t" \ + "mullw r8, r7, r6 \n\t" \ + "mulhwu r9, r7, r6 \n\t" \ + "adde r8, r8, r5 \n\t" \ + "lwz r7, 4(r4) \n\t" \ + "addze r5, r9 \n\t" \ + "addc r8, r8, r7 \n\t" \ + "stwu r8, 4(r4) \n\t" + +#define MULADDC_STOP \ + "addze r5, r5 \n\t" \ + "addi r4, r4, 4 \n\t" \ + "addi r3, r3, 4 \n\t" \ + "stw r5, %0 \n\t" \ + "stw r4, %1 \n\t" \ + "stw r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#else /* __MACH__ && __APPLE__ */ + +#define MULADDC_INIT \ + asm( \ + "lwz %%r3, %3 \n\t" \ + "lwz %%r4, %4 \n\t" \ + "lwz %%r5, %5 \n\t" \ + "lwz %%r6, %6 \n\t" \ + "addi %%r3, %%r3, -4 \n\t" \ + "addi %%r4, %%r4, -4 \n\t" \ + "addic %%r5, %%r5, 0 \n\t" + +#define MULADDC_CORE \ + "lwzu %%r7, 4(%%r3) \n\t" \ + "mullw %%r8, %%r7, %%r6 \n\t" \ + "mulhwu %%r9, %%r7, %%r6 \n\t" \ + "adde %%r8, %%r8, %%r5 \n\t" \ + "lwz %%r7, 4(%%r4) \n\t" \ + "addze %%r5, %%r9 \n\t" \ + "addc %%r8, %%r8, %%r7 \n\t" \ + "stwu %%r8, 4(%%r4) \n\t" + +#define MULADDC_STOP \ + "addze %%r5, %%r5 \n\t" \ + "addi %%r4, %%r4, 4 \n\t" \ + "addi %%r3, %%r3, 4 \n\t" \ + "stw %%r5, %0 \n\t" \ + "stw %%r4, %1 \n\t" \ + "stw %%r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#endif /* __MACH__ && __APPLE__ */ + +#endif /* PPC32 */ + +/* + * The Sparc(64) assembly is reported to be broken. + * Disable it for now, until we're able to fix it. + */ +#if 0 && defined(__sparc__) +#if defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + "ldx %3, %%o0 \n\t" \ + "ldx %4, %%o1 \n\t" \ + "ld %5, %%o2 \n\t" \ + "ld %6, %%o3 \n\t" + +#define MULADDC_CORE \ + "ld [%%o0], %%o4 \n\t" \ + "inc 4, %%o0 \n\t" \ + "ld [%%o1], %%o5 \n\t" \ + "umul %%o3, %%o4, %%o4 \n\t" \ + "addcc %%o4, %%o2, %%o4 \n\t" \ + "rd %%y, %%g1 \n\t" \ + "addx %%g1, 0, %%g1 \n\t" \ + "addcc %%o4, %%o5, %%o4 \n\t" \ + "st %%o4, [%%o1] \n\t" \ + "addx %%g1, 0, %%o2 \n\t" \ + "inc 4, %%o1 \n\t" + + #define MULADDC_STOP \ + "st %%o2, %0 \n\t" \ + "stx %%o1, %1 \n\t" \ + "stx %%o0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#else /* __sparc64__ */ + +#define MULADDC_INIT \ + asm( \ + "ld %3, %%o0 \n\t" \ + "ld %4, %%o1 \n\t" \ + "ld %5, %%o2 \n\t" \ + "ld %6, %%o3 \n\t" + +#define MULADDC_CORE \ + "ld [%%o0], %%o4 \n\t" \ + "inc 4, %%o0 \n\t" \ + "ld [%%o1], %%o5 \n\t" \ + "umul %%o3, %%o4, %%o4 \n\t" \ + "addcc %%o4, %%o2, %%o4 \n\t" \ + "rd %%y, %%g1 \n\t" \ + "addx %%g1, 0, %%g1 \n\t" \ + "addcc %%o4, %%o5, %%o4 \n\t" \ + "st %%o4, [%%o1] \n\t" \ + "addx %%g1, 0, %%o2 \n\t" \ + "inc 4, %%o1 \n\t" + +#define MULADDC_STOP \ + "st %%o2, %0 \n\t" \ + "st %%o1, %1 \n\t" \ + "st %%o0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#endif /* __sparc64__ */ +#endif /* __sparc__ */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( \ + "lwi r3, %3 \n\t" \ + "lwi r4, %4 \n\t" \ + "lwi r5, %5 \n\t" \ + "lwi r6, %6 \n\t" \ + "andi r7, r6, 0xffff \n\t" \ + "bsrli r6, r6, 16 \n\t" + +#define MULADDC_CORE \ + "lhui r8, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "lhui r9, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "mul r10, r9, r6 \n\t" \ + "mul r11, r8, r7 \n\t" \ + "mul r12, r9, r7 \n\t" \ + "mul r13, r8, r6 \n\t" \ + "bsrli r8, r10, 16 \n\t" \ + "bsrli r9, r11, 16 \n\t" \ + "add r13, r13, r8 \n\t" \ + "add r13, r13, r9 \n\t" \ + "bslli r10, r10, 16 \n\t" \ + "bslli r11, r11, 16 \n\t" \ + "add r12, r12, r10 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "add r12, r12, r11 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "lwi r10, r4, 0 \n\t" \ + "add r12, r12, r10 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "add r12, r12, r5 \n\t" \ + "addc r5, r13, r0 \n\t" \ + "swi r12, r4, 0 \n\t" \ + "addi r4, r4, 4 \n\t" + +#define MULADDC_STOP \ + "swi r5, %0 \n\t" \ + "swi r4, %1 \n\t" \ + "swi r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4" "r5", "r6", "r7", "r8", \ + "r9", "r10", "r11", "r12", "r13" \ + ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( \ + "ld.a %%a2, %3 \n\t" \ + "ld.a %%a3, %4 \n\t" \ + "ld.w %%d4, %5 \n\t" \ + "ld.w %%d1, %6 \n\t" \ + "xor %%d5, %%d5 \n\t" + +#define MULADDC_CORE \ + "ld.w %%d0, [%%a2+] \n\t" \ + "madd.u %%e2, %%e4, %%d0, %%d1 \n\t" \ + "ld.w %%d0, [%%a3] \n\t" \ + "addx %%d2, %%d2, %%d0 \n\t" \ + "addc %%d3, %%d3, 0 \n\t" \ + "mov %%d4, %%d3 \n\t" \ + "st.w [%%a3+], %%d2 \n\t" + +#define MULADDC_STOP \ + "st.w %0, %%d4 \n\t" \ + "st.a %1, %%a3 \n\t" \ + "st.a %2, %%a2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "d0", "d1", "e2", "d4", "a2", "a3" \ + ); + +#endif /* TriCore */ + +/* + * gcc -O0 by default uses r7 for the frame pointer, so it complains about our + * use of r7 below, unless -fomit-frame-pointer is passed. Unfortunately, + * passing that option is not easy when building with yotta. + * + * On the other hand, -fomit-frame-pointer is implied by any -Ox options with + * x !=0, which we can detect using __OPTIMIZE__ (which is also defined by + * clang and armcc5 under the same conditions). + * + * So, only use the optimized assembly below for optimized build, which avoids + * the build error and is pretty reasonable anyway. + */ +#if defined(__GNUC__) && !defined(__OPTIMIZE__) +#define MULADDC_CANNOT_USE_R7 +#endif + +#if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7) + +#if defined(__thumb__) && !defined(__thumb2__) + +#pragma message "using ARM THUMB MULADDC" + +#define MULADDC_INIT \ + asm( \ + "ldr r0, %3 \n\t" \ + "ldr r1, %4 \n\t" \ + "ldr r2, %5 \n\t" \ + "ldr r3, %6 \n\t" \ + "lsr r7, r3, #16 \n\t" \ + "mov r9, r7 \n\t" \ + "lsl r7, r3, #16 \n\t" \ + "lsr r7, r7, #16 \n\t" \ + "mov r8, r7 \n\t" + +#define MULADDC_CORE \ + "ldmia r0!, {r6} \n\t" \ + "lsr r7, r6, #16 \n\t" \ + "lsl r6, r6, #16 \n\t" \ + "lsr r6, r6, #16 \n\t" \ + "mov r4, r8 \n\t" \ + "mul r4, r6 \n\t" \ + "mov r3, r9 \n\t" \ + "mul r6, r3 \n\t" \ + "mov r5, r9 \n\t" \ + "mul r5, r7 \n\t" \ + "mov r3, r8 \n\t" \ + "mul r7, r3 \n\t" \ + "lsr r3, r6, #16 \n\t" \ + "add r5, r5, r3 \n\t" \ + "lsr r3, r7, #16 \n\t" \ + "add r5, r5, r3 \n\t" \ + "add r4, r4, r2 \n\t" \ + "mov r2, #0 \n\t" \ + "adc r5, r2 \n\t" \ + "lsl r3, r6, #16 \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r5, r2 \n\t" \ + "lsl r3, r7, #16 \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r5, r2 \n\t" \ + "ldr r3, [r1] \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r2, r5 \n\t" \ + "stmia r1!, {r4} \n\t" + +#define MULADDC_STOP \ + "str r2, %0 \n\t" \ + "str r1, %1 \n\t" \ + "str r0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "r8", "r9", "cc" \ + ); + +#else + +#define MULADDC_INIT \ + asm( \ + "ldr r0, %3 \n\t" \ + "ldr r1, %4 \n\t" \ + "ldr r2, %5 \n\t" \ + "ldr r3, %6 \n\t" + +#define MULADDC_CORE \ + "ldr r4, [r0], #4 \n\t" \ + "mov r5, #0 \n\t" \ + "ldr r6, [r1] \n\t" \ + "umlal r2, r5, r3, r4 \n\t" \ + "adds r7, r6, r2 \n\t" \ + "adc r2, r5, #0 \n\t" \ + "str r7, [r1], #4 \n\t" + +#define MULADDC_STOP \ + "str r2, %0 \n\t" \ + "str r1, %1 \n\t" \ + "str r0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "cc" \ + ); + +#endif /* Thumb */ + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( \ + "ldq $1, %3 \n\t" \ + "ldq $2, %4 \n\t" \ + "ldq $3, %5 \n\t" \ + "ldq $4, %6 \n\t" + +#define MULADDC_CORE \ + "ldq $6, 0($1) \n\t" \ + "addq $1, 8, $1 \n\t" \ + "mulq $6, $4, $7 \n\t" \ + "umulh $6, $4, $6 \n\t" \ + "addq $7, $3, $7 \n\t" \ + "cmpult $7, $3, $3 \n\t" \ + "ldq $5, 0($2) \n\t" \ + "addq $7, $5, $7 \n\t" \ + "cmpult $7, $5, $5 \n\t" \ + "stq $7, 0($2) \n\t" \ + "addq $2, 8, $2 \n\t" \ + "addq $6, $3, $3 \n\t" \ + "addq $5, $3, $3 \n\t" + +#define MULADDC_STOP \ + "stq $3, %0 \n\t" \ + "stq $2, %1 \n\t" \ + "stq $1, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "$1", "$2", "$3", "$4", "$5", "$6", "$7" \ + ); +#endif /* Alpha */ + +#if defined(__mips__) && !defined(__mips64) + +#define MULADDC_INIT \ + asm( \ + "lw $10, %3 \n\t" \ + "lw $11, %4 \n\t" \ + "lw $12, %5 \n\t" \ + "lw $13, %6 \n\t" + +#define MULADDC_CORE \ + "lw $14, 0($10) \n\t" \ + "multu $13, $14 \n\t" \ + "addi $10, $10, 4 \n\t" \ + "mflo $14 \n\t" \ + "mfhi $9 \n\t" \ + "addu $14, $12, $14 \n\t" \ + "lw $15, 0($11) \n\t" \ + "sltu $12, $14, $12 \n\t" \ + "addu $15, $14, $15 \n\t" \ + "sltu $14, $15, $14 \n\t" \ + "addu $12, $12, $9 \n\t" \ + "sw $15, 0($11) \n\t" \ + "addu $12, $12, $14 \n\t" \ + "addi $11, $11, 4 \n\t" + +#define MULADDC_STOP \ + "sw $12, %0 \n\t" \ + "sw $11, %1 \n\t" \ + "sw $10, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "$9", "$10", "$11", "$12", "$13", "$14", "$15" \ + ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(MBEDTLS_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* MBEDTLS_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(MBEDTLS_HAVE_UDBL) + +#define MULADDC_INIT \ +{ \ + mbedtls_t_udbl r; \ + mbedtls_mpi_uint r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (mbedtls_t_udbl) b; \ + r0 = (mbedtls_mpi_uint) r; \ + r1 = (mbedtls_mpi_uint)( r >> biL ); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + mbedtls_mpi_uint s0, s1, b0, b1; \ + mbedtls_mpi_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/BootLoader/source/modcrypt/config.h b/BootLoader/source/modcrypt/config.h new file mode 100644 index 0000000..fb70eb2 --- /dev/null +++ b/BootLoader/source/modcrypt/config.h @@ -0,0 +1,5 @@ + +#define MBEDTLS_BIGNUM_C +#define POLARSSL_AES_C + +#define MBEDTLS_HAVE_ASM diff --git a/BootLoader/source/modcrypt/crypto.c b/BootLoader/source/modcrypt/crypto.c new file mode 100644 index 0000000..db69c75 --- /dev/null +++ b/BootLoader/source/modcrypt/crypto.c @@ -0,0 +1,125 @@ +#include +#include +#include "crypto.h" +#include "u128_math.h" +#include "f_xy.h" +#include "dsi.h" + +// more info: +// https://github.com/Jimmy-Z/TWLbf/blob/master/dsi.c +// https://github.com/Jimmy-Z/bfCL/blob/master/dsi.h +// ported back to 32 bit for ARM9 + +static dsi_context nand_ctx ; +static dsi_context boot2_ctx ; +static dsi_context es_ctx ; + +static uint8_t nand_ctr_iv[16]; +static uint8_t boot2_ctr[16]; + +static void generate_key(uint8_t *generated_key, const uint32_t *console_id, const key_mode_t mode) +{ + uint32_t key[4]; + switch (mode) + { + case NAND: + key[0] = console_id[0]; + key[1] = console_id[0] ^ KEYSEED_DSI_NAND_0; + key[2] = console_id[1] ^ KEYSEED_DSI_NAND_1; + key[3] = console_id[1]; + break; + case NAND_3DS: + key[0] = (console_id[0] ^ KEYSEED_3DS_NAND_0) | 0x80000000; + key[1] = KEYSEED_3DS_NAND_1; + key[2] = KEYSEED_3DS_NAND_2; + key[3] = console_id[1] ^ KEYSEED_3DS_NAND_3; + break; + case ES: + key[0] = KEYSEED_ES_0; + key[1] = KEYSEED_ES_1; + key[2] = console_id[1] ^ KEYSEED_ES_2; + key[3] = console_id[0]; + break; + default: + break; + } + u128_xor((uint8_t *)key, mode == ES ? DSi_ES_KEY_Y : DSi_NAND_KEY_Y); + u128_add((uint8_t *)key, DSi_KEY_MAGIC); + u128_lrot((uint8_t *)key, 42) ; + memcpy(generated_key, key, 16) ; +} + +int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len) +{ + uint8_t digest[SHA1_LEN]; + swiSHA1Calc(digest, data, len); + return memcmp(digest, digest_verify, SHA1_LEN); +} + +void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int is3DS) +{ + uint32_t console_id[2]; + GET_UINT32_BE(console_id[0], console_id_be, 4); + GET_UINT32_BE(console_id[1], console_id_be, 0); + + uint8_t key[16]; + generate_key(key, console_id, is3DS ? NAND_3DS : NAND); + dsi_set_key(&nand_ctx, key) ; + + generate_key(key, console_id, ES); + dsi_set_key(&es_ctx, key) ; + + dsi_set_key(&boot2_ctx, DSi_BOOT2_KEY) ; + + swiSHA1Calc(nand_ctr_iv, emmc_cid, 16); + +} + +// crypt one block, in/out must be aligned to 32 bit(restriction induced by xor_128) +// offset as block offset, block as AES block +void dsi_nand_crypt_1(uint8_t* out, const uint8_t* in, uint32_t offset) +{ + uint8_t ctr[16] ; + memcpy(ctr, nand_ctr_iv, sizeof(nand_ctr_iv)) ; + u128_add32(ctr, offset); + dsi_set_ctr(&nand_ctx, ctr) ; + dsi_crypt_ctr(&nand_ctx, in, out, 16) ; +} + +void dsi_nand_crypt(uint8_t* out, const uint8_t* in, uint32_t offset, unsigned count) +{ + uint8_t ctr[16] ; + memcpy(ctr, nand_ctr_iv, sizeof(nand_ctr_iv)) ; + u128_add32(ctr, offset); + for (unsigned i = 0; i < count; ++i) + { + dsi_set_ctr(&nand_ctx, ctr) ; + dsi_crypt_ctr(&nand_ctx, in, out, 16) ; + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + u128_add32(ctr, 1); + } +} + +void dsi_boot2_crypt_set_ctr(uint32_t size_r) +{ + for (int i=0;i<4;i++) + { + boot2_ctr[i] = (size_r) >> (8*i) ; + boot2_ctr[i+4] = (-size_r) >> (8*i) ; + boot2_ctr[i+8] = (~size_r) >> (8*i) ; + boot2_ctr[i+12] = 0 ; + } +} + +void dsi_boot2_crypt(uint8_t* out, const uint8_t* in, unsigned count) +{ + for (unsigned i = 0; i < count; ++i) + { + dsi_set_ctr(&boot2_ctx, boot2_ctr) ; + dsi_crypt_ctr(&boot2_ctx, in, out, 16) ; + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + u128_add32(boot2_ctr, 1); + } +} diff --git a/BootLoader/source/modcrypt/crypto.h b/BootLoader/source/modcrypt/crypto.h new file mode 100644 index 0000000..aafd9e2 --- /dev/null +++ b/BootLoader/source/modcrypt/crypto.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/************************ Constants / Defines *********************************/ + +#define AES_BLOCK_SIZE 16 +#define SHA1_LEN 20 + +#define KEYSEED_DSI_NAND_0 0x24ee6906 +#define KEYSEED_DSI_NAND_1 0xe65b601d + +#define KEYSEED_3DS_NAND_0 0xb358a6af +#define KEYSEED_3DS_NAND_1 0x544e494e +#define KEYSEED_3DS_NAND_2 0x4f444e45 +#define KEYSEED_3DS_NAND_3 0x08c267b7 + +#define KEYSEED_ES_0 0x4e00004a +#define KEYSEED_ES_1 0x4a00004e +#define KEYSEED_ES_2 0xc80c4b72 + +#define GET_UINT32_BE(n, b, i) \ + ((uint8_t*)&(n))[0] = (b)[i + 3]; \ + ((uint8_t*)&(n))[1] = (b)[i + 2]; \ + ((uint8_t*)&(n))[2] = (b)[i + 1]; \ + ((uint8_t*)&(n))[3] = (b)[i + 0] + +#define PUT_UINT32_BE(n, b, i) \ + (b)[i + 0] = ((uint8_t*)&(n))[3]; \ + (b)[i + 1] = ((uint8_t*)&(n))[2]; \ + (b)[i + 2] = ((uint8_t*)&(n))[1]; \ + (b)[i + 3] = ((uint8_t*)&(n))[0] + + +typedef enum { + ENCRYPT, + DECRYPT +} crypt_mode_t; + +typedef enum { + NAND, + NAND_3DS, + ES +} key_mode_t; + + +// don't want to include nds.h just for this +void swiSHA1Calc(void *digest, const void *buf, size_t len); + +int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len); + +void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int is3DS); + +void dsi_nand_crypt_1(uint8_t *out, const uint8_t* in, u32 offset); + +void dsi_nand_crypt(uint8_t *out, const uint8_t* in, u32 offset, unsigned count); + +int dsi_es_block_crypt(uint8_t *buf, unsigned buf_len, crypt_mode_t mode); + +void dsi_boot2_crypt_set_ctr(uint32_t size_r); + +void dsi_boot2_crypt(uint8_t* out, const uint8_t* in, unsigned count); + +#ifdef __cplusplus +} +#endif diff --git a/BootLoader/source/modcrypt/dsi.c b/BootLoader/source/modcrypt/dsi.c new file mode 100644 index 0000000..cca4b48 --- /dev/null +++ b/BootLoader/source/modcrypt/dsi.c @@ -0,0 +1,346 @@ +#include "dsi.h" +#include +#include +#include +#include +#include "u128_math.h" + +void dsi_set_key( dsi_context* ctx, + const unsigned char key[16] ) +{ + unsigned char keyswap[16]; + u128_swap(keyswap, key) ; + + aes_setkey_enc(&ctx->aes, keyswap, 128); +} + +void dsi_add_ctr( dsi_context* ctx, + unsigned int carry) +{ + unsigned int counter[4]; + unsigned char *outctr = (unsigned char*)ctx->ctr; + unsigned int sum; + signed int i; + + for (i = 0; i < 4; i++) + counter[i] = (outctr[i * 4 + 0] << 24) | (outctr[i * 4 + 1] << 16) | + (outctr[i * 4 + 2] << 8) | (outctr[i * 4 + 3] << 0); + + for (i = 3; i >= 0; i--) { + sum = counter[i] + carry; + carry = (uint)(sum < counter[i]); + + counter[i] = sum; + } + + for (i = 0; i < 4; i++) { + outctr[i * 4 + 0] = counter[i] >> 24; + outctr[i * 4 + 1] = counter[i] >> 16; + outctr[i * 4 + 2] = counter[i] >> 8; + outctr[i * 4 + 3] = counter[i] >> 0; + } +} + +void dsi_set_ctr( dsi_context* ctx, const unsigned char ctr[16] ) { + for (int i = 0; i < 16; i++)ctx->ctr[i] = ctr[15-i]; +} + +void dsi_init_ctr( dsi_context* ctx, const unsigned char key[16], const unsigned char ctr[16]) { +// void dsi_init_ctr( dsi_context* ctx, const unsigned char key[16], const unsigned char ctr[12]) { + dsi_set_key(ctx, key); + dsi_set_ctr(ctx, ctr); +} + +void dsi_crypt_ctr( dsi_context* ctx, + const void* in, + void* out, + unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i += 0x10) { + dsi_crypt_ctr_block(ctx, in+i, out+i); + } +} + +void dsi_crypt_ctr_block( dsi_context* ctx, + const unsigned char input[16], + unsigned char output[16] ) +{ + int i; + unsigned char stream[16]; + + + aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->ctr, stream); + + + if (input) { + for (i=0; i<16; i++) { + output[i] = stream[15-i] ^ input[i]; + } + } else { + for (i=0; i<16; i++) + output[i] = stream[15-i]; + } + + dsi_add_ctr(ctx, 1); +} + + +void dsi_init_ccm( dsi_context* ctx, + unsigned char key[16], + unsigned int maclength, + unsigned int payloadlength, + unsigned int assoclength, + unsigned char nonce[12] ) +{ + int i; + + dsi_set_key(ctx, key); + + ctx->maclen = maclength; + + maclength = (maclength-2)/2; + + payloadlength = (payloadlength+15) & ~15; + + // CCM B0 block: + // [1-byte flags] [12-byte nonce] [3-byte size] + ctx->mac[0] = (maclength<<3) | 2; + if (assoclength) + ctx->mac[0] |= (1<<6); + for (i=0; i<12; i++) + ctx->mac[1+i] = nonce[11-i]; + ctx->mac[13] = payloadlength>>16; + ctx->mac[14] = payloadlength>>8; + ctx->mac[15] = payloadlength>>0; + + aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac); + + // CCM CTR: + // [1-byte flags] [12-byte nonce] [3-byte ctr] + ctx->ctr[0] = 2; + for (i=0; i<12; i++) + ctx->ctr[1+i] = nonce[11-i]; + ctx->ctr[13] = 0; + ctx->ctr[14] = 0; + ctx->ctr[15] = 0; + + dsi_crypt_ctr_block(ctx, 0, ctx->S0); +} + +void dsi_encrypt_ccm_block( dsi_context* ctx, + unsigned char input[16], + unsigned char output[16], + unsigned char* mac ) +{ + int i; + + for (i=0; i<16; i++) + ctx->mac[i] ^= input[15-i]; + + aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac); + + if (mac) { + for (i=0; i<16; i++) + mac[i] = ctx->mac[15-i] ^ ctx->S0[i]; + } + + if (output) + dsi_crypt_ctr_block(ctx, input, output); +} + + +void dsi_decrypt_ccm_block( dsi_context* ctx, + unsigned char input[16], + unsigned char output[16], + unsigned char* mac ) +{ + int i; + + + if (output) { + dsi_crypt_ctr_block(ctx, input, output); + + + for (i=0; i<16; i++) + ctx->mac[i] ^= output[15-i]; + } else { + for (i=0; i<16; i++) + ctx->mac[i] ^= input[15-i]; + } + + aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac); + + + if (mac) { + for (i=0; i<16; i++) + mac[i] = ctx->mac[15-i] ^ ctx->S0[i]; + } +} + + +void dsi_decrypt_ccm( dsi_context* ctx, + unsigned char* input, + unsigned char* output, + unsigned int size, + unsigned char* mac ) +{ + unsigned char block[16]; + unsigned char ctr[16]; + + while (size > 16) { + dsi_decrypt_ccm_block(ctx, input, output, mac); + + if (input) + input += 16; + if (output) + output += 16; + + size -= 16; + } + + memcpy(ctr, ctx->ctr, 16); + memset(block, 0, 16); + dsi_crypt_ctr_block(ctx, block, block); + memcpy(ctx->ctr, ctr, 16); + memcpy(block, input, size); + + + dsi_decrypt_ccm_block(ctx, block, block, mac); + memcpy(output, block, size); +} + + +void dsi_encrypt_ccm( dsi_context* ctx, + unsigned char* input, + unsigned char* output, + unsigned int size, + unsigned char* mac ) +{ + unsigned char block[16]; + + while (size > 16) { + dsi_encrypt_ccm_block(ctx, input, output, mac); + + if (input) + input += 16; + if (output) + output += 16; + + size -= 16; + } + + memset(block, 0, 16); + memcpy(block, input, size); + dsi_encrypt_ccm_block(ctx, block, block, mac); + memcpy(output, block, size); +} + +void dsi_es_init( dsi_es_context* ctx, + unsigned char key[16] ) +{ + memcpy(ctx->key, key, 16); + ctx->randomnonce = 1; +} + +void dsi_es_set_nonce( dsi_es_context* ctx, + unsigned char nonce[12] ) +{ + memcpy(ctx->nonce, nonce, 12); + ctx->randomnonce = 0; +} + +void dsi_es_set_random_nonce( dsi_es_context* ctx ) +{ + ctx->randomnonce = 1; +} + +int dsi_es_decrypt( dsi_es_context* ctx, + unsigned char* buffer, + unsigned char metablock[32], + unsigned int size ) +{ + unsigned char ctr[16]; + unsigned char nonce[12]; + unsigned char scratchpad[16]; + unsigned char chkmac[16]; + unsigned char genmac[16]; + dsi_context cryptoctx; + unsigned int chksize; + + + memcpy(chkmac, metablock, 16); + + memcpy(ctr, metablock + 16, 16); + ctr[0] = 0; + ctr[13] = 0; + ctr[14] = 0; + ctr[15] = 0; + + dsi_init_ctr(&cryptoctx, ctx->key, ctr); + dsi_crypt_ctr_block(&cryptoctx, metablock+16, scratchpad); + + chksize = (scratchpad[13]<<16) | (scratchpad[14]<<8) | (scratchpad[15]<<0); + + if (scratchpad[0] != 0x3A) { + return -1; + } + + if (chksize != size) { + return -2; + } + + memcpy(nonce, metablock + 17, 12); + + dsi_init_ccm(&cryptoctx, ctx->key, 16, size, 0, nonce); + dsi_decrypt_ccm(&cryptoctx, buffer, buffer, size, genmac); + + if (memcmp(genmac, chkmac, 16) != 0) { + return -3; + } + + return 0; +} + + +void dsi_es_encrypt( dsi_es_context* ctx, + unsigned char* buffer, + unsigned char metablock[32], + unsigned int size ) +{ + int i; + unsigned char nonce[12]; + unsigned char mac[16]; + unsigned char ctr[16]; + unsigned char scratchpad[16]; + dsi_context cryptoctx; + + if (ctx->randomnonce) { + srand( (unsigned int)time(0) ); + + for (i=0; i<12; i++) + nonce[i] = rand(); + } else { + memcpy(nonce, ctx->nonce, 12); + } + + dsi_init_ccm(&cryptoctx, ctx->key, 16, size, 0, nonce); + dsi_encrypt_ccm(&cryptoctx, buffer, buffer, size, mac); + + memset(scratchpad, 0, 16); + scratchpad[0] = 0x3A; + scratchpad[13] = size >> 16; + scratchpad[14] = size >> 8; + scratchpad[15] = size >> 0; + + memset(ctr, 0, 16); + memcpy(ctr+1, nonce, 12); + + dsi_init_ctr(&cryptoctx, ctx->key, ctr); + dsi_crypt_ctr_block(&cryptoctx, scratchpad, metablock+16); + memcpy(metablock+17, nonce, 12); + + memcpy(metablock, mac, 16); + +} + diff --git a/BootLoader/source/modcrypt/dsi.h b/BootLoader/source/modcrypt/dsi.h new file mode 100644 index 0000000..d7acfc0 --- /dev/null +++ b/BootLoader/source/modcrypt/dsi.h @@ -0,0 +1,105 @@ +#ifndef _DSI_H_ +#define _DSI_H_ + +#include "aes.h" + +typedef struct +{ + unsigned char ctr[16]; + unsigned char mac[16]; + unsigned char S0[16]; + unsigned int maclen; + + aes_context aes; +} +dsi_context; + +typedef struct +{ + unsigned char key[16]; + unsigned char nonce[12]; + int randomnonce; +} dsi_es_context; + + + +#ifdef __cplusplus +extern "C" { +#endif + +void dsi_set_key( dsi_context* ctx, + const unsigned char key[16] ); + +void dsi_add_ctr( dsi_context* ctx, + unsigned int carry ); + +void dsi_set_ctr( dsi_context* ctx, + const unsigned char ctr[16] ); + +void dsi_init_ctr( dsi_context* ctx, + const unsigned char key[16], + const unsigned char ctr[16]); + +void dsi_crypt_ctr( dsi_context* ctx, + const void* in, + void* out, + unsigned int len); + +void dsi_crypt_ctr_block( dsi_context* ctx, + const unsigned char input[16], + unsigned char output[16] ); + +void dsi_init_ccm( dsi_context* ctx, + unsigned char key[16], + unsigned int maclength, + unsigned int payloadlength, + unsigned int assoclength, + unsigned char nonce[12] ); + +void dsi_encrypt_ccm_block( dsi_context* ctx, + unsigned char input[16], + unsigned char output[16], + unsigned char* mac ); + +void dsi_decrypt_ccm_block( dsi_context* ctx, + unsigned char input[16], + unsigned char output[16], + unsigned char* mac ); + + +void dsi_decrypt_ccm( dsi_context* ctx, + unsigned char* input, + unsigned char* output, + unsigned int size, + unsigned char* mac ); + +void dsi_encrypt_ccm( dsi_context* ctx, + unsigned char* input, + unsigned char* output, + unsigned int size, + unsigned char* mac ); + +void dsi_es_init( dsi_es_context* ctx, + unsigned char key[16] ); + +void dsi_es_set_nonce( dsi_es_context* ctx, + unsigned char nonce[12] ); + +void dsi_es_set_random_nonce( dsi_es_context* ctx ); + +int dsi_es_decrypt( dsi_es_context* ctx, + unsigned char* buffer, + unsigned char metablock[32], + unsigned int size ); + +void dsi_es_encrypt( dsi_es_context* ctx, + unsigned char* buffer, + unsigned char metablock[32], + unsigned int size ); + +#ifdef __cplusplus +} +#endif + +#endif // _DSI_H_ + diff --git a/BootLoader/source/modcrypt/f_xy.c b/BootLoader/source/modcrypt/f_xy.c new file mode 100644 index 0000000..374924c --- /dev/null +++ b/BootLoader/source/modcrypt/f_xy.c @@ -0,0 +1,50 @@ +/* f_xy.c + * + * This file was imported from godmode9i, but it is liely not the + * original source. twltool uses the same file. + * + * If you happen to know whom to credit I'd love to add the name + * + * Refactored to reduce the pointer casts and remove the dependency + * from tonccpy. + */ + +#include +#include +#include "u128_math.h" + + +/************************ Constants / Defines *********************************/ + +const uint8_t DSi_KEY_MAGIC[16] = { 0x79, 0x3e, 0x4f, 0x1a, 0x5f, 0x0f, 0x68, 0x2a, + 0x58, 0x02, 0x59, 0x29, 0x4e, 0xfb, 0xfe, 0xff }; +const uint8_t DSi_NAND_KEY_Y[16] = { 0x76, 0xdc, 0xb9, 0x0a, 0xd3, 0xc4, 0x4d, 0xbd, + 0x1d, 0xdd, 0x2d, 0x20, 0x05, 0x00, 0xa0, 0xe1 }; +const uint8_t DSi_ES_KEY_Y[16] = { 0xe5, 0xcc, 0x5a, 0x8b, 0x56, 0xd0, 0xc9,0x72, + 0x9c, 0x17, 0xe8, 0xdc, 0x39, 0x12, 0x36, 0xa9 }; +const uint8_t DSi_BOOT2_KEY[16] = { 0x98, 0xee, 0x80, 0x80, 0x00, 0x6c, 0xb4, 0xf6, + 0x3a, 0xc2, 0x6e, 0x62, 0xf9, 0xec, 0x34, 0xad }; + +/************************ Functions *******************************************/ + +void F_XY(uint8_t *key, const uint8_t *key_x, const uint8_t *key_y) +{ + uint8_t key_xy[16]; + + for (int i=0; i<16; i++) + key_xy[i] = key_x[i] ^ key_y[i]; + + memcpy(key, DSi_KEY_MAGIC, sizeof(DSi_KEY_MAGIC)); + + u128_add(key, key_xy); + u128_lrot(key, 42); +} + +//F_XY_reverse does the reverse of F(X^Y): takes (normal)key, and does F in reverse to generate the original X^Y key_xy. +void F_XY_reverse(const uint8_t *key, uint8_t *key_xy) +{ + memcpy(key_xy, key, 16); + u128_rrot(key_xy, 42); + u128_sub(key_xy, DSi_KEY_MAGIC); +} + diff --git a/BootLoader/source/modcrypt/f_xy.h b/BootLoader/source/modcrypt/f_xy.h new file mode 100644 index 0000000..383868e --- /dev/null +++ b/BootLoader/source/modcrypt/f_xy.h @@ -0,0 +1,36 @@ +/* f_xy.h + * + * This file was imported from godmode9i, but it is liely not the + * original source. twltool uses the same file. + * + * If you happen to know whom to credit I'd love to add the name + * + * Refactored to reduce the pointer casts and remove the dependency + * from tonccpy. + */ + +#ifndef _H_F_XY +#define _H_F_XY + +#ifdef __cplusplus +extern "C" { +#endif + +/************************ Constants / Defines *********************************/ + +extern const uint8_t DSi_KEY_MAGIC[16] ; +extern const uint8_t DSi_NAND_KEY_Y[16] ; +extern const uint8_t DSi_ES_KEY_Y[16] ; +extern const uint8_t DSi_BOOT2_KEY[16] ; + +/************************ Function Protoypes **********************************/ + +void F_XY(uint8_t *key, const uint8_t *key_x, const uint8_t *key_y); +void F_XY_reverse(const uint8_t *key, uint8_t *key_xy); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/BootLoader/source/modcrypt/padlock.h b/BootLoader/source/modcrypt/padlock.h new file mode 100644 index 0000000..86f019e --- /dev/null +++ b/BootLoader/source/modcrypt/padlock.h @@ -0,0 +1,98 @@ +/** + * \file padlock.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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. + */ +#ifndef POLARSSL_PADLOCK_H +#define POLARSSL_PADLOCK_H + +#include "aes.h" + +#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && defined(__i386__) + +#ifndef POLARSSL_HAVE_X86 +#define POLARSSL_HAVE_X86 +#endif + +#define PADLOCK_RNG 0x000C +#define PADLOCK_ACE 0x00C0 +#define PADLOCK_PHE 0x0C00 +#define PADLOCK_PMM 0x3000 + +#define PADLOCK_ALIGN16(x) (unsigned long *) (16 + ((long) x & ~15)) + +#define POLARSSL_ERR_PADLOCK_DATA_MISALIGNED -0x08E0 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief PadLock detection routine + * + * \param The feature to detect + * + * \return 1 if CPU has support for the feature, 0 otherwise + */ +int padlock_supports( int feature ); + +/** + * \brief PadLock AES-ECB block en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if success, 1 if operation failed + */ +int padlock_xcryptecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +/** + * \brief PadLock AES-CBC buffer en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if success, 1 if operation failed + */ +int padlock_xcryptcbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_X86 */ + +#endif /* padlock.h */ diff --git a/BootLoader/source/modcrypt/u128_math.c b/BootLoader/source/modcrypt/u128_math.c new file mode 100644 index 0000000..e6b2abe --- /dev/null +++ b/BootLoader/source/modcrypt/u128_math.c @@ -0,0 +1,106 @@ +// u128_math + +#include "u128_math.h" +#include + +// rotate a 128bit, little endian by shift bits in direction of increasing significance. +void u128_lrot(uint8_t *num, uint32_t shift) +{ + uint8_t tmp[16]; + for (int i=0;i<16;i++) + { + // rot: rotate to more significant. + // LSB is tmp[0], MSB is tmp[15] + const uint32_t byteshift = shift / 8 ; + const uint32_t bitshift = shift % 8; + tmp[(i+byteshift) % 16] = (num[i] << bitshift) + | ((num[(i+16-1) % 16] >> (8-bitshift)) & 0xff); + } + memcpy(num, tmp, 16) ; +} + +// rotate a 128bit, little endian by shift bits in direction of decreasing significance. +void u128_rrot(uint8_t *num, uint32_t shift) +{ + uint8_t tmp[16]; + for (int i=0;i<16;i++) + { + // rot: rotate to less significant. + // LSB is tmp[0], MSB is tmp[15] + const uint32_t byteshift = shift / 8 ; + const uint32_t bitshift = shift % 8; + tmp[i] = (num[(i+byteshift) % 16] >> bitshift) + | ((num[(i+byteshift+1) % 16] << (8-bitshift)) & 0xff); + } + memcpy(num, tmp, 16) ; +} + +// xor two 128bit, little endian values and store the result into the first +void u128_xor(uint8_t *a, const uint8_t *b) +{ + for (int i=0;i<16;i++) + { + a[i] = a[i] ^ b[i] ; + } +} + +// or two 128bit, little endian values and store the result into the first +void u128_or(uint8_t *a, const uint8_t *b) +{ + for (int i=0;i<16;i++) + { + a[i] = a[i] | b[i] ; + } +} + +// and two 128bit, little endian values and store the result into the first +void u128_and(uint8_t *a, const uint8_t *b) +{ + for (int i=0;i<16;i++) + { + a[i] = a[i] & b[i] ; + } +} + + +// add two 128bit, little endian values and store the result into the first +void u128_add(uint8_t *a, const uint8_t *b) +{ + uint8_t carry = 0 ; + for (int i=0;i<16;i++) + { + uint16_t sum = a[i] + b[i] + carry ; + a[i] = sum & 0xff ; + carry = sum >> 8 ; + } +} + +// add two 128bit, little endian values and store the result into the first +void u128_add32(uint8_t *a, const uint32_t b) +{ + uint8_t _b[16]; + memset(_b, 0, sizeof(_b)) ; + for (int i=0;i<4;i++) + _b[i] = b >> (i*8) ; + u128_add(a, _b); +} + +// sub two 128bit, little endian values and store the result into the first +void u128_sub(uint8_t *a, const uint8_t *b) +{ + uint8_t carry = 0 ; + for (int i=0;i<16;i++) + { + uint16_t sub = a[i] - b[i] - (carry & 1); + a[i] = sub & 0xff ; + carry = sub >> 8 ; + } +} + +void u128_swap(uint8_t *out, const uint8_t *in) +{ + for (int i=0;i<16;i++) + { + out[15-i] = in[i]; + } +} \ No newline at end of file diff --git a/BootLoader/source/modcrypt/u128_math.h b/BootLoader/source/modcrypt/u128_math.h new file mode 100644 index 0000000..114b500 --- /dev/null +++ b/BootLoader/source/modcrypt/u128_math.h @@ -0,0 +1,38 @@ +// u128_math + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// left rotate by (shift % 128) bits +void u128_lrot(uint8_t *num, uint32_t shift) ; + +// right rotate by (shift % 128) bits +void u128_rrot(uint8_t *num, uint32_t shift) ; + +// bitwise xor strored to a +void u128_xor(uint8_t *a, const uint8_t *b) ; + +// bitwise or strored to a +void u128_or(uint8_t *a, const uint8_t *b) ; + +// bitwise and strored to a +void u128_and(uint8_t *a, const uint8_t *b) ; + +// add b to a and store in a +void u128_add(uint8_t *a, const uint8_t *b) ; + +// add 32 bit value b to a and store in a +void u128_add32(uint8_t *a, const uint32_t b) ; + +// substract b from a and store in a +void u128_sub(uint8_t *a, const uint8_t *b) ; + +// swap byte order +void u128_swap(uint8_t *out, const uint8_t *in) ; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/BootLoader/source/read_bios.h b/BootLoader/source/read_bios.h deleted file mode 100755 index 4efa97f..0000000 --- a/BootLoader/source/read_bios.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - NitroHax -- Cheat tool for the Nintendo DS - Copyright (C) 2008 Michael "Chishm" Chisholm - - 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 3 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, see . -*/ - #include - -void readBios (u8* dest, u32 src, u32 size); - diff --git a/BootLoader/source/read_bios.s b/BootLoader/source/read_bios.s deleted file mode 100755 index 57474ef..0000000 --- a/BootLoader/source/read_bios.s +++ /dev/null @@ -1,54 +0,0 @@ -#include - -@This code comes from a post by CaitSith2 at gbadev.org - THANKS!! -@ -@Code to dump the complete Nintendo DS ARM7 bios, including the -@first 0x1204 bytes residing in the secure area. -@ -@The ARM7 bios has read protection where 0x(Word)[FFFF(Half word)[FF(Byte)[FF]]] -@is returned, if any reads are attempted while PC is outside the arm7 bios range. -@ -@Additionally, if the PC is outside the 0x0000 - 0x1204 range, that range of the bios -@is completely locked out from reading. - - -@ void readBios (u8* dest, u32 src, u32 size) - - .arm - -BEGIN_ASM_FUNC readBios - adr r3,bios_dump+1 - bx r3 - .thumb - -bios_dump: - push {r4-r7,lr} @Even though we don't use R7, the code we are jumping to is going - @trash R7, therefore, we must save it. - mov r5, r1 @ src - sub r1, r2, #1 @ size - mov r2, r0 @ dest - ldr r0,=0x5ED @The code that will be made to read the full bios resides here. - -loop: - mov r6,#0x12 @We Subtract 12 from the location we wish to read - sub r3,r1,r6 @because the code at 0x5EC is LDRB R3, [R3,#0x12] - add r3, r3, r5 - adr r6,ret - push {r2-r6} @The line of code at 0x5EE is POP {R2,R4,R6,R7,PC} - bx r0 - .align - -ret: - strb r3,[r2,r1] @Store the read byte contained in r3, to SRAM. - sub r1,#1 @Subtract 1 - bpl loop @And branch as long as R1 doesn't roll into -1 (0xFFFFFFFF). - - pop {r4-r7} @Restore the saved registers - pop {r3} @and return. - bx r3 - - .END - -@The exact code that resides at 0x5EC (secure area range) of the arm7 bios. -@ROM:000005EC 9B 7C LDRB R3, [R3,#0x12] -@ROM:000005EE D4 BD POP {R2,R4,R6,R7,PC} diff --git a/BootLoader/source/read_card.c b/BootLoader/source/read_card.c index fef797c..66cd640 100755 --- a/BootLoader/source/read_card.c +++ b/BootLoader/source/read_card.c @@ -20,9 +20,6 @@ #include #include -#include -#include -#include #include #include @@ -30,25 +27,72 @@ #include "common.h" #include "tonccpy.h" +#define BASE_DELAY (100) + +/*#define ARM7OWNSCARD BIT(11) +#define ARM7OWNSROM BIT(7)*/ + typedef union { char title[4]; u32 key; } GameCode; +/*void SetCardOwner(bool arm9) { + REG_EXMEMSTAT = (REG_EXMEMSTAT & ~ARM7OWNSCARD) | (arm9 ? 0 : ARM7OWNSCARD); +}*/ + +static u32 twlBlowfish = 0; +static u32 normalChip = 0; static u32 portFlags = 0; static u32 secureAreaData[CARD_SECURE_AREA_SIZE/sizeof(u32)]; +static ALIGN(4) u32 twlSecureAreaData[CARD_SECURE_AREA_SIZE/sizeof(u32)]; // static bool shortInit = false; +// static u32 iCardId; + +// u32 cardGetId() { return iCardId; } + +static const u8 cardSeedBytes[] = { 0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5 }; + +void EnableSlot1() { + int oldIME = enterCriticalSection(); + + while((REG_SCFG_MC & 0x0c) == 0x0c) swiDelay(1 * BASE_DELAY); + + if(!(REG_SCFG_MC & 0x0c)) { + REG_SCFG_MC = (REG_SCFG_MC & ~0x0c) | 4; + swiDelay(20 * BASE_DELAY); + REG_SCFG_MC = (REG_SCFG_MC & ~0x0c) | 8; + swiDelay(20 * BASE_DELAY); + } + + /*REG_ROMCTRL = 0x20000000; // wait 27ms, then set ROMCTRL=20000000h + while((REG_ROMCTRL & 0x8000000))swiDelay(1 * BASE_DELAY);*/ + + leaveCriticalSection(oldIME); +} + +void DisableSlot1() { + int oldIME = enterCriticalSection(); + + while((REG_SCFG_MC & 0x0c) == 0x0c) swiDelay(1 * BASE_DELAY); + + if((REG_SCFG_MC & 0x0c) == 8) { + REG_SCFG_MC = (REG_SCFG_MC & ~0x0c) | 0x0c; + while((REG_SCFG_MC & 0x0c) != 0) swiDelay(1 * BASE_DELAY); + } + + leaveCriticalSection(oldIME); +} -static const u8 cardSeedBytes[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5}; // chosen by fair dice roll. // guaranteed to be random. // static u32 getRandomNumber(void) { return 4; } -static u32 getRandomNumber(void) { return rand(); } // make this actually random lol. :P +// static u32 getRandomNumber(void) { return rand(); } // make this actually random lol. :P +static u32 getRandomNumber(void) { return 0xDDAC86F5; } -static void decryptSecureArea (u32 gameCode, u32* secureArea) { - int i; - init_keycode (gameCode, 2, 8); +static void decryptSecureArea (u32 gameCode, u32* secureArea, int iCardDevice) { + init_keycode (gameCode, 2, 8, iCardDevice); crypt_64bit_down (secureArea); - init_keycode (gameCode, 3, 8); - for (i = 0; i < 0x200; i+= 2) { crypt_64bit_down (secureArea + i); } + init_keycode (gameCode, 3, 8, iCardDevice); + for (int i = 0; i < 0x200; i+= 2)crypt_64bit_down (secureArea + i); } static struct { @@ -61,7 +105,7 @@ static struct { } key1data; -static void initKey1Encryption (u8* cmdData) { +static void initKey1Encryption (u8* cmdData, int iCardDevice) { key1data.iii = getRandomNumber() & 0x00000fff; key1data.jjj = getRandomNumber() & 0x00000fff; key1data.kkkkk = getRandomNumber() & 0x000fffff; @@ -69,7 +113,11 @@ static void initKey1Encryption (u8* cmdData) { key1data.mmm = getRandomNumber() & 0x00000fff; key1data.nnn = getRandomNumber() & 0x00000fff; - cmdData[7] = CARD_CMD_ACTIVATE_BF; + if (iCardDevice) //DSi + cmdData[7]=0x3D; // CARD_CMD_ACTIVATE_BF2 + else + cmdData[7]=CARD_CMD_ACTIVATE_BF; + cmdData[6] = (u8) (key1data.iii >> 4); cmdData[5] = (u8) ((key1data.iii << 4) | (key1data.jjj >> 8)); cmdData[4] = (u8) key1data.jjj; @@ -122,10 +170,118 @@ static void cardDelay (u16 readTimeout) { TIMER_DATA(0) = 0; } +static void switchToTwlBlowfish(sNDSHeaderExt* ndsHeader) { + if ((twlBlowfish == 0) || ndsHeader->unitCode == 0) return; + + // Used for dumping the DSi arm9i/7i binaries -u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { u32 portFlagsKey1, portFlagsSecRead; - bool normalChip; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000 + int secureBlockNumber; + int i; + u8 cmdData[8] __attribute__ ((aligned)); + GameCode* gameCode; + + if (REG_SCFG_EXT & BIT(31)) { + // int oldIME = 0; + // Reset card slot + DisableSlot1(); + // oldIME = enterCriticalSection(); + swiDelay(40 * BASE_DELAY); + // leaveCriticalSection(oldIME); + EnableSlot1(); + // oldIME = enterCriticalSection(); + swiDelay(30 * BASE_DELAY); + // leaveCriticalSection(oldIME); + } + + // Dummy command sent after card reset + cardParamCommand (CARD_CMD_DUMMY, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), NULL, 0); + + //int iCardDevice = 1; + + // Initialise blowfish encryption for KEY1 commands and decrypting the secure area + gameCode = (GameCode*)ndsHeader->gameCode; + init_keycode (gameCode->key, 1, 8, 1); + + // Port 40001A4h setting for normal reads (command B7) + portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7); + // Port 40001A4h setting for KEY1 commands (usually 001808F8h) + portFlagsKey1 = CARD_ACTIVATE | CARD_nRESET | (ndsHeader->cardControl13 & (CARD_WR|CARD_CLK_SLOW)) | + ((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16)); + + // Adjust card transfer method depending on the most significant bit of the chip ID + if (normalChip == 0)portFlagsKey1 |= CARD_SEC_LARGE; + + // 3Ciiijjj xkkkkkxx - Activate KEY1 Encryption Mode + initKey1Encryption(cmdData, 1); + cardPolledTransfer((ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE, NULL, 0, cmdData); + + // 4llllmmm nnnkkkkk - Activate KEY2 Encryption Mode + createEncryptedCommand (CARD_CMD_ACTIVATE_SEC, cmdData, 0); + + if (normalChip > 0) { + cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); + cardDelay(ndsHeader->readTimeout); + } + cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); + + // Set the KEY2 encryption registers + REG_ROMCTRL = 0; + REG_CARD_1B0 = cardSeedBytes[ndsHeader->deviceType & 0x07] | (key1data.nnn << 15) | (key1data.mmm << 27) | 0x6000; + REG_CARD_1B4 = 0x879b9b05; + REG_CARD_1B8 = key1data.mmm >> 5; + REG_CARD_1BA = 0x5c; + REG_ROMCTRL = CARD_nRESET | CARD_SEC_SEED | CARD_SEC_EN | CARD_SEC_DAT; + + // Update the DS card flags to suit KEY2 encryption + portFlagsKey1 |= CARD_SEC_EN | CARD_SEC_DAT; + + // 1lllliii jjjkkkkk - 2nd Get ROM Chip ID / Get KEY2 Stream + createEncryptedCommand (CARD_CMD_SECURE_CHIPID, cmdData, 0); + + if (normalChip > 0) { + cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); + cardDelay(ndsHeader->readTimeout); + } + cardPolledTransfer(portFlagsKey1 | CARD_BLK_SIZE(7), NULL, 0, cmdData); + + // 2bbbbiii jjjkkkkk - Get Secure Area Block + portFlagsSecRead = (ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF)|CARD_DELAY2(0x3F))) + | CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT; + + int secureAreaOffset = 0; + for (secureBlockNumber = 4; secureBlockNumber < 8; secureBlockNumber++) { + createEncryptedCommand (CARD_CMD_SECURE_READ, cmdData, secureBlockNumber); + + if (normalChip > 0) { + cardPolledTransfer(portFlagsSecRead, NULL, 0, cmdData); + cardDelay(ndsHeader->readTimeout); + for (i = 8; i > 0; i--) { + cardPolledTransfer(portFlagsSecRead | CARD_BLK_SIZE(1), twlSecureAreaData + secureAreaOffset, 0x200, cmdData); + secureAreaOffset += 0x200/sizeof(u32); + } + } else { + cardPolledTransfer(portFlagsSecRead | CARD_BLK_SIZE(4) | CARD_SEC_LARGE, twlSecureAreaData + secureAreaOffset, 0x1000, cmdData); + secureAreaOffset += 0x1000/sizeof(u32); + } + } + + // Alllliii jjjkkkkk - Enter Main Data Mode + createEncryptedCommand (CARD_CMD_DATA_MODE, cmdData, 0); + + if (normalChip > 0) { + cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); + cardDelay(ndsHeader->readTimeout); + } + cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); + + twlBlowfish = 0x01; +} + + +u16 cardInit (sNDSHeaderExt* ndsHeader, u32 *chipID) { + u32 portFlagsKey1, portFlagsSecRead; + normalChip = 0; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000 u32* secureArea; int secureBlockNumber; int i; @@ -133,6 +289,8 @@ u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { GameCode* gameCode; // shortInit = false; + // SetCardOwner(false); + // Dummy command sent after card reset cardParamCommand (CARD_CMD_DUMMY, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), NULL, 0); @@ -140,36 +298,43 @@ u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { static_assert(sizeof(tNDSHeader) == 0x160, "tNDSHeader not packed properly"); // Read the header - cardParamCommand (CARD_CMD_HEADER_READ, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), (u32*)ndsHeader, sizeof(tNDSHeader)); - + cardReadHeader((u8*)ndsHeader); + // cardParamCommand (CARD_CMD_HEADER_READ, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), (u32*)ndsHeader, sizeof(tNDSHeader)); + // while (REG_ROMCTRL & CARD_BUSY); + // Check header CRC if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E))return ERR_HEAD_CRC; - + + // 1st Get ROM Chip ID + cardParamCommand (CARD_CMD_HEADER_CHIPID, 0, (ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE | CARD_BLK_SIZE(7), chipID, sizeof(u32)); + // iCardId = cardReadID((ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE | CARD_BLK_SIZE(7)); + // iCardId = cardReadID(CARD_CLK_SLOW); + // chipID = cardReadID(CARD_CLK_SLOW); + while (REG_ROMCTRL & CARD_BUSY); + + // *chipID = iCardId; + // Initialise blowfish encryption for KEY1 commands and decrypting the secure area gameCode = (GameCode*)ndsHeader->gameCode; - init_keycode (gameCode->key, 2, 8); + init_keycode (gameCode->key, 2, 8, 0); // Port 40001A4h setting for normal reads (command B7) portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7); // Port 40001A4h setting for KEY1 commands (usually 001808F8h) portFlagsKey1 = CARD_ACTIVATE | CARD_nRESET | (ndsHeader->cardControl13 & (CARD_WR|CARD_CLK_SLOW)) | ((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16)); - - // 1st Get ROM Chip ID - cardParamCommand (CARD_CMD_HEADER_CHIPID, 0, (ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE | CARD_BLK_SIZE(7), chipID, sizeof(u32)); - while (REG_ROMCTRL & CARD_BUSY); // Adjust card transfer method depending on the most significant bit of the chip ID - normalChip = ((*chipID) & 0x80000000) != 0; // ROM chip ID MSB - if (!normalChip)portFlagsKey1 |= CARD_SEC_LARGE; + if(((*chipID) & 0x80000000) != 0)normalChip = 0xFFFF; // ROM chip ID MSB + if (normalChip == 0)portFlagsKey1 |= CARD_SEC_LARGE; // 3Ciiijjj xkkkkkxx - Activate KEY1 Encryption Mode - initKey1Encryption (cmdData); + initKey1Encryption (cmdData, 0); cardPolledTransfer((ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE, NULL, 0, cmdData); // 4llllmmm nnnkkkkk - Activate KEY2 Encryption Mode createEncryptedCommand (CARD_CMD_ACTIVATE_SEC, cmdData, 0); - if (normalChip) { + if (normalChip > 0) { cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); cardDelay(ndsHeader->readTimeout); cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); @@ -191,7 +356,7 @@ u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { // 1lllliii jjjkkkkk - 2nd Get ROM Chip ID / Get KEY2 Stream createEncryptedCommand (CARD_CMD_SECURE_CHIPID, cmdData, 0); - if (normalChip) { + if (normalChip > 0) { cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); cardDelay(ndsHeader->readTimeout); cardPolledTransfer(portFlagsKey1 | CARD_BLK_SIZE(7), NULL, 0, cmdData); @@ -205,7 +370,7 @@ u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { for (secureBlockNumber = 4; secureBlockNumber < 8; secureBlockNumber++) { createEncryptedCommand (CARD_CMD_SECURE_READ, cmdData, secureBlockNumber); - if (normalChip) { + if (normalChip > 0) { cardPolledTransfer(portFlagsSecRead, NULL, 0, cmdData); cardDelay(ndsHeader->readTimeout); for (i = 8; i > 0; i--) { @@ -221,7 +386,7 @@ u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { // Alllliii jjjkkkkk - Enter Main Data Mode createEncryptedCommand (CARD_CMD_DATA_MODE, cmdData, 0); - if (normalChip) { + if (normalChip > 0) { cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); cardDelay(ndsHeader->readTimeout); cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); @@ -230,47 +395,52 @@ u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID) { } // Now deal with secure area decryption and verification - decryptSecureArea (gameCode->key, secureAreaData); + decryptSecureArea (gameCode->key, secureAreaData, 0); secureArea = secureAreaData; if (secureArea[0] == 0x72636e65 /*'encr'*/ && secureArea[1] == 0x6a624f79 /*'yObj'*/) { // Secure area exists, so just clear the tag secureArea[0] = 0xe7ffdeff; secureArea[1] = 0xe7ffdeff; - }/* else { - // Secure area tag is not there, so destroy the entire secure area - for (i = 0; i < 0x200; i ++) { - *secureArea++ = 0xe7ffdeff; - } - return normalChip ? ERR_SEC_NORM : ERR_SEC_OTHR; - }*/ + } return ERR_NONE; } -void cardRead (u32 src, u32* dest, size_t size) { +void cardRead(sNDSHeaderExt* ndsHeader, u32 src, u32* dest, size_t size) { size_t readSize; - if ((src < CARD_SECURE_AREA_OFFSET) /*&& !shortInit*/) { + if ((twlBlowfish == 0) && (ndsHeader->unitCode & BIT(1)) && (src > ndsHeader->romSize))switchToTwlBlowfish(ndsHeader); + + if (src < CARD_SECURE_AREA_OFFSET) { return; } else if (src < CARD_DATA_OFFSET) { // Read data from secure area readSize = src + size < CARD_DATA_OFFSET ? size : CARD_DATA_OFFSET - src; - memcpy (dest, (u8*)secureAreaData + src - CARD_SECURE_AREA_OFFSET, readSize); + tonccpy (dest, (u8*)secureAreaData + src - CARD_SECURE_AREA_OFFSET, readSize); src += readSize; dest += readSize/sizeof(*dest); size -= readSize; + } else if ((ndsHeader->unitCode & BIT(1)) && (twlBlowfish > 0) && (src >= ndsHeader->arm9iromOffset) && (src < ndsHeader->arm9iromOffset+CARD_SECURE_AREA_SIZE)) { + // Read data from secure area + readSize = src + size < (ndsHeader->arm9iromOffset+CARD_SECURE_AREA_SIZE) ? size : (ndsHeader->arm9iromOffset+CARD_SECURE_AREA_SIZE) - src; + tonccpy (dest, (u8*)twlSecureAreaData + src - ndsHeader->arm9iromOffset, readSize); + src += readSize; + dest += readSize/sizeof(*dest); + size -= readSize; + if (size <= 0)return; } - + while (size > 0) { readSize = size < CARD_DATA_BLOCK_SIZE ? size : CARD_DATA_BLOCK_SIZE; - cardParamCommand (CARD_CMD_DATA_READ, src, (portFlags &~CARD_BLK_SIZE(7)) | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), dest, readSize); + cardParamCommand (CARD_CMD_DATA_READ, src, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), dest, readSize); src += readSize; dest += readSize/sizeof(*dest); size -= readSize; } } + // If booted from DSi System Menu short cart init with no card reads or pokes to rom ctrl registers can be done. // System Menu is nice enough to do this for you. :P // (also is the case for booting from DS Download Play. ;) ) @@ -291,7 +461,7 @@ void cardRead (u32 src, u32* dest, size_t size) { // Initialise blowfish encryption for KEY1 commands and decrypting the secure area gameCode = (GameCode*)ndsHeader->gameCode; - init_keycode (gameCode->key, 2, 8); + init_keycode (gameCode->key, 2, 8, 0); // Port 40001A4h setting for normal reads (command B7) portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7); @@ -314,7 +484,7 @@ void cardRead (u32 src, u32* dest, size_t size) { cardRead (ndsHeader->arm9romOffset, secureArea, secureBlockSize); // Now deal with secure area decryption and verification - decryptSecureArea (gameCode->key, secureAreaData); + decryptSecureArea (gameCode->key, secureAreaData, 0); secureArea = secureAreaData; if (secureArea[0] == 0x72636e65 && secureArea[1] == 0x6a624f79) { diff --git a/BootLoader/source/read_card.h b/BootLoader/source/read_card.h index 2750fe1..3c668f0 100755 --- a/BootLoader/source/read_card.h +++ b/BootLoader/source/read_card.h @@ -20,7 +20,6 @@ #define READ_CARD_H #include -#include #include #define CARD_NDS_HEADER_SIZE (0x200) @@ -121,8 +120,9 @@ typedef struct { u8 dsi3[0x174]; } sNDSHeaderExt; -u32 cardInit (sNDSHeaderExt* ndsHeader, u32* chipID); -void cardRead (u32 src, u32* dest, size_t size); +// u32 cardGetId(); +u16 cardInit (sNDSHeaderExt* ndsHeader, u32 *chipID); +void cardRead (sNDSHeaderExt* ndsHeader, u32 src, u32* dest, size_t size); // u32 cardInitShort (sNDSHeaderExt* ndsHeader, u32* chipID); #endif // READ_CARD_H diff --git a/BootLoader/source/reset.arm7.s b/BootLoader/source/reset.arm7.s index 34f3ac3..344ee8a 100644 --- a/BootLoader/source/reset.arm7.s +++ b/BootLoader/source/reset.arm7.s @@ -64,7 +64,7 @@ arm7_reset: @ ipcSendState(ARM7_BOOT) strh r0, [r12] - ldr r0,=0x27FFE34 + ldr r0,=0x2FFFE34 ldr r0,[r0] bx r0 diff --git a/BootLoader/source/reset.arm9.s b/BootLoader/source/reset.arm9.s index d36d8a1..5d2a256 100644 --- a/BootLoader/source/reset.arm9.s +++ b/BootLoader/source/reset.arm9.s @@ -100,7 +100,7 @@ arm9_reset: @ while (ipcRecvState() != ARM7_BOOT); bl waitsync - ldr r10, =0x27FFE24 + ldr r10, =0x2FFFE24 ldr r2, [r10] @ Switch MPU to startup default @@ -130,6 +130,6 @@ mpu_initial_data: .word 0x08000035 @ p15,0,c6,c3,0,r6 ;PU Protection Unit Data/Unified Region 3 .word 0x0300001b @ p15,0,c6,c4,0,r7 ;PU Protection Unit Data/Unified Region 4 .word 0xffff001d @ p15,0,c6,c6,0,r8 ;PU Protection Unit Data/Unified Region 6 - .word 0x027ff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB + .word 0x02fff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB .word 0x0300000a @ p15,0,c9,c1,0,r10 ;TCM Data TCM Base and Virtual Size itcm_reset_code_end: \ No newline at end of file diff --git a/CartFiles/NTR_Launcher/CycloDS.nds b/CartFiles/NTR_Launcher/CycloDS.nds new file mode 100644 index 0000000000000000000000000000000000000000..149fb3c8bf5d27ed6d2597581ae5bbbac48914a3 GIT binary patch literal 131072 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≪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ⅆ|~+*j+1EpL{+-nxSnK)&3fl-+X$G%y*tU_OepsmzAbrX18O39 zgERMpy=sq)VrKB@-}J4!D*P}hr-X6MX!!-h@F1O%KLR1E?nrKT$7b;SbP4sCcy2@M z!sPFp_`oxVZ(H~_w{yzxhB*-TL)(S4Hdn*fz7mC*X`RUz8`_%NP@aet#xtLVO^kTK zqx4mlTHKHSu}m6flh0Gw9aIZTk7YI@0+N+P0&| zzN(a;1T;^UI7Q4fsRip7bWVm$?|EBT%z28cf0O(@{X4`dX7lr3jz~Yc3R@a*u~GIp zjvwE&#XHB-dM^5F{iulx1BYnwBgUD}{Ss`*Bw8X%GrRNf<+7vvQ%w9XX8{v!FPW+0 zW)dwkxt9>8N5hV#VaOe_eX#QnTXGm=Odgp7wLT%78!o0CB~CL>8?c)>!kaOTt|tzM ziBrMsi&Au5E<2aj+6NrMC{C5o`LqOI>y&qmhq}hE?to$D@fO@mTOI|HI6-pGqU41g z^aHwqeI1g5ZTG3p(Iss#vsIoN6;{ITOdir+8)(^zr+Olsr|qBE&y;-f7QH>0iz>G7 z=Zl!O5AH7{Qk%oRxPu=aB%+$bW~DYGdoRd#4;q`om|q@Xa=h41woVZ;#>9}-%VGI^ z5A*OS*%|Ox4W2fS`wVnSEM`ERF1%dJBuv zJ0^^0^92IT%_r8&N6T|3QW+E8u)s|y>B_Qb4)Quu>CVISLaHIt$jtsv{#20(!i$gb zvJMCEqn6zTL8|HXm;qked-|b4cKViIJvf)CQHYm?*hC?Xv?y7QF1bJ~8Z@2=pE6`> z06AUgNxt}+8U@YnLbCXW%hx;=fu79>wv$oD?cA}Q%%^*EM*7=NlPz1BP4!&khicW^ zjh<;<6D{GXfcpy}0vkuMMHIc+8A07KnoR6z+;6lGHr6g30OYuxB%gYWK|f?} zeaV4imX*h6Ce;JioA%}UQ65AMPW%`G+soKe$C`1SMTps?^J~D7GB^>p-MSURNCb?8 zFId3L4>%*`(Hnxel4#7apd4Gth?LwoB>c?>r242UQd{-2J2=68@_h}FTBO#8MGq#2 zcftc&s%0`C@VblU&A&^957*!U0)&;C6z##cVzz=PV0icy8~M;u<6dz%vuQZ zK!NVGj)ppntJTpnTnp$$YVRt?cVXoIDQa1?Vk`=pe%x~U=yEc}&iZOenr!%{E!s^Z zHuYEY@S_QLKZ7xi#q@p%rwd_0EG-z018t`^74#ZDrGB!fgH=07B~`zG!;MVLPc-aobYzF< zb*stxIb2UE3+u6T{nos5zIUAIES`+PB}(XyG6yv}qMxv;YGN-&W14VvPTKy6YHaLG)qz{@YJ$#6i044{+#Z#^h1 zZVI@s?&PBqU;{rX`84nyC813mfXFAg0l3z7m)PlEkiri|X418bVv~5XnVAmNO z>~xEqnaX_WVZUHL8?k@gk^pZa0ctA*c1S&cJz>HSCr&n`z|I1jR3Md8UYk%O;q{Ro zQ>hH4H8jMGwaE9JH+1fRz)QYZ=R53Mu1MJYOZQ8olpSpq-g>D+XQIKy(`ja!Y+oeX z?6)f2{!!0|A6hTE{r2d5b&yG6g%^%#B+bWEb{Ug+kq3Hk&?P46mNVMmWXQwmh>9Mo z&dqX+h$}i}-WT2ek~5V_eTl|Ugm8J&PR_3neLNaxC)tTrZitxQSPl`H?h%k&-mL&< zx*eNEaT)60xcAAfDfgTcxKTaifC-U$^1MF}ISERUdnV{~Q=KOJEhf$Hi1%>k)g7W~ z)IdE2!{5o$SVPjSXL;xSq=)O-$#ZrX0iKHuY{42dLN-!j`0Q2`kT#Y+^`YK9$S z=?_WdW@D`y*f}G%A+#;O$<*0nXDB)j|MrDHnTt)gP<83i%^$-Y{t-Q@#n>AA(v}I8 zUi(+}kg}<1ZJD-VPhenf|j)1Adn*#__6Rj_s<_fCbe4p1+D>t99e0ILuU1xG0;YS;Na_N>+ z`8&>1&=32dHWq8=a_Sx5VRA(blLaX%gNxbeq>a#wT=hDk_2cu7N!6!Dh5 z{xjwb2fOoOckqfqzPw6(VEb5XFZ?I5-IvTF4*EPF;wrTq?BW*?F)Qa658W<7Px2CJ zDk%q_Z-;G5AHYoHj~!h$}@^f)zALaRxgbD}X?+0QPJ~EM=NQ`7o<(bXCN+ zW7Nobbeg^?p%peok4y*~Y84lDiN&0lpFHkh#g6A;j6|B049SL?5i8h&IY(#KP5Q;a*%dSx3!S0>{PTE>y*# z4h!TQ!&c^p!A+YJl8~TdgeknhM>ZrCSWs!3TcL?}gH8sW+>XR^;CiRheunu5w%6cS zmJ5vRH_S%Zrqj%`OuRp2R3xqyH}5@9CTC_0HcvnpkHOgu)$R*QV(WG634HWSq%GVZ z0J>Otgpx%kyLIQ?-GVr`?g8`J;!Xu~MJhk{x_fz8h?=j)Hu;YP&j)XlgK2b^AkBU_ zLuY^zhK3_1GCIBc$NAKW)bscCQy07x=*G=Wr9b?@i~OJ|7Zg7Yo@o7hpy>_zx#e61 zobz08Y@jJ7=cp7h>$e_x08C9va%!#@Yr}?|3i}2aHdlKiE=u`KRFl@wnH`sr_FXg< z%f@m1>{krK#4;~RYhvVMcE!1QI|0cJCgL_bkq;$2S^fq-#pHTv)w}b2O>!*WSyzJW zvLK%foET{tI_#91mEjOr(|~N@5Ug}D$kRvNo>{Vzz?Rt+Y?kJYvgkH#!Bxj|$;~wC_ye%0{f~MnvtTwOG?H9Pq6K&+vtF_&} zkerb;U`WiMY+KB^(e@hh(PV1mafBya)DtpRMAs*!$+g#9y3`ocVa{M0;YL;Gh}AQ9dy>a9`9kIZy!%9a)O(Jk6E~6d>S?Ulea$T znocKC?f3PG-Soj|_FJv#bW#3?!`gzDfpL!(LSnQrn4sHX5l{bLsWCS|4L!S$$VKmBjO+WV$0f?Tm$hGaBFy(vsx>FlF~wW<>&b2-Gc{H(x1TG6f^ad}a?}z6fE@YR zCNCr8B>!9@;CfuW`u0f0zpHNZ$v6jA4p#1m&4e7{Ar(^?N%86wnHVQ6?p+^QLRO1|NH&7kbk@d?t10m1WGlp2bd*L}J)L;KWq5? z$UuR`P3(#;F`EZ&MYqn;(hy%*x#|<=s{LA3T}VX3r}Oy7Gv`v%dMfJWI)i)1a?(YR zL+goZjrUAUR6k7C$1tcMnmb-yf1m{UoIPLkrZj_ew_tjw(cNR_AMJJpv*rOcxs-}J z_HYZL4lpVKHOiN!s&loJ`Dd7_^^bptT^eXt@NcM9lt$*iA8k5Crr6H-v`zs%-wC5q zP<>x)s;q}gp!4n5?IeZ$Ox^9?liC>V`hs5HO4c@zOaplxiy-OOsQTBa_NCO6CT(t6 z?7~2%LuHb~2(K4|m|yWy^=mkVMm}{HJBsQ!wIN3j&QH*HF?rOMH*O0I%|8%1p~kjd zu}*_sS#%RMg8}aBd3nNh3+xRGS`guoqlWhNCGrr>F|0VCxr@S+PP;Hil-7blQm4r=d4r{ILr|gJ z5JFAvd^EX40wpCvj=4TV$Kk)|MB6(Lk8mK3lb{XE0!#R=5c&9*ExP3@YQVxm4R8k= zw#G5&9Cg7FPC1!O?(IIM0A4@kd@8I9-k)C`OfRU`cO4e;u8`C~hyKon zz|Z!+JCIZrJy2O9IZKa>?>sR8MtB7yE(uDbLlM)!UC786-bObAEVUUB?E)v_5Fklg zz>4V{VrofsPIQ_|s0#AL)9UFA?*XeD=83LVPlkDow+9#H-Ht%32)VX%VO#ZL>jqqd(+5RmHQ}t!_vE&z~FoWgW z0uVj$+N#|HJZ+}!xU-CMzed~nu#IO=P+hvg!m*i@B57w@8L8z^)1o(W$c&&sX0zP=0#tkA8V|Hj?iq?g)7wf+ANXq?u|Ox1gR4;m!dHODD0cgp9;JttukA zIWdWH##j^sPuXZjC1MnS5zF+C26+`nG`cxPw8it9mpV}-Rz?5rMu394`y(NZAIquR&o zT}k=lzpMPCRfxzBc&Q_2fog@mi7Q#sV^5{?;**ls+5`d^O zcDP9xUOQD?d>BUCDuTBS?%OQ}TmRFUMV0gXt_rf=GXH1kw=mMxkWm_)Xw9Kees-~*%wsth%2$w!maW-UZi zZU1Kp|09&BtcSE_z8=i4Zb!A5+WCP4|8!xMfbaWAzRx2W34Rj?DpkSLw3AfmH0?Z< zg8!_B`#=FOFLT%{=RUVLa-(n384m0yi>@+Y-bxOstT&X#hza`twD&GxO`K`p_%oBq zfslk52nQj+KX0d=)IOkeU6yxT%?!R4=bROp zTM}Kq!PT}`ZkuG}<2e))?hFpe2`^qjDT9A4e878_#PfJGPip76dg5FcTw4{^?QPr3 zH?J+x^pNM3`h*Up<-AI)dR%E+Pbr$@Apr@G7r)d=Ewe^qw7mRaorK`;huA(u-QH0C zsZOdAYNR%=a`0)}n;F%77-|dTdd=Ay2U?`+xy8-tH1h48#wgpIW?SG4^&46#x$|fT z6rDM4`I&*Ik`zf_w~m~ZJNC#eKRQ~8dxjfIk9h;E2Vt{0*zwq?sHnU0?TNt)<${2Y zW6h3Z(hzv@xU=MionC&E#NIWja2XV~i*#_WdaI0n%r<+KqdfA>n4Jzx^9B+ykGFAZ z1cAEqRw&4m$Zn#TTqQ%tmYRghdx=LIQjYOBDITA z$POnC;&RLUasr}(EcL{>4t9i9#Sh`z@EM>n@F>X}YAY4gPT2Bbls7}6k0@LXDPOCFj0X>i4@l2PbDe&o`zzC&_ zR>iCjK&q}pMFqVC>zafW*Xu~^R7)@x_I=K&2k>$!I}`t0=(Vt>yY@j(Cg}9loqI2| z4TxSF4A%~r_qE^WV3hk(?Zf3OwmZY*C+F^oC=bW2$;F>!(t~A*C%AU;lCP{EG;tR> z1g_8;q8igC1+MY%t+B2}uEoAsg)Jvg{msJV17Xm};mZcXa&2Mf_eviv+3=V*PkvfH z5NBI3+xoffGtBnDM-DD#*m`)RxETv~ZdM%KQ+cTT$U<>LjtH|2+^sk^&&OO+P9$QY zmz5{7$>2I3l}p1mLtW)B3!9=Mw?^&Xy-dDvxKchrUu!C;bNsEm=!y+H?GSG@Uz^>v ztmm540}lQ}_fhKLNbxup)b13kT(b;r!&GHbdy1i0<#uO@*5TWoTisJnm zlB~HpZ?pt^UADX=nBF_mGC3oWy?W<+^4i55JV1myAFvYg z!C6gJed3u*tr`cv<~P)MS1GI;?zKiK`U{$dUoFO(X~nm>i$*Q-7)K8+Z!eUeOpdSz z=LuWA3ccN$5R=ea>EK#t6bEu(a@yz=TgeXbsIqG|2Fq-?@n>)fQ*QmLk5bqiI{Aq- ztNJv3Nhz7UG+h4bsJ!b9rA}Whusa@huBqkBCR;1MZy8m#opn8`6wPtKE257RVtk(T zxYJehL++JDsG?U4D+ep(`qvaR-e09XVGt>!la49J)0_Cg4!*+om@|tGie^4C${435 zB;*pg7k7r)1R+i9&@to~#o$F@sZuWor;a}yuJa0k{@&+&yi;7*%N?#L-# zzvqu?-#qDjzJ0i7mLs^yl@n&tcETqKj|YWr_i(J&T-^U+_E{CMOpy#SCVsc|OBkx0nED=a%i%hM$)F91C@1vZ zL^~<0OSJB_MGObmg*yo8_C$MlbE1=@s7Se%tLphXMcw3zF#HkjoO=(qb-5D9e_t}| zZk|zPIgfGd4|qsycz#NJxs0do**xAIy!;-E+UK;v?kfcKJ982xiqgmeYYdpZ`d0_q zAB*$V+f-3;z6Pwt2j8O%6<$%d8*I#Q+e_vA1Y4+8Z7FCkP$aD*AGusDuWL zowFz=%5%iea>TJMi+OXGGdsEEJy^w4LV=UxOB}g&e`{V=37yf^IT`5auZR{%eLKbl^g!#d{eG}hZEe3JJwX6#aw5pVP$Ar zFx4BO?a7OCR$5`@Vx|15EA(6Za#T*gRN<_2MpwKow7}r~%xm6pm%;Yiulidf&*eIe z!7q-_QHz(nAs2u7NA*BSsJtoet@p93w@!~;{rsYUc`JVJa#^_;Cd^(+xVnT)9jffz zc6@NGVq1ZyljEp|GC-c^E}KtD=n~Z1hRPA9^39jNnSsfBs$xeE*$u(_y6ta@c{y_3 zA2CmvSMS&x%t)H>a`*x`G$+gT`|6iP344PjD&KB-)|N}Q!}F?hFIN2Zt#U_^P0VRh z|4q3syV;}EJzidm1!QF@)5<)+W({3QfsNApUexyEVL9PVA4P|rn-S(IP*}}0>}Xh>5mRza)O$hnXj5%( zuspI&_Zd>X}BC86}~Yr6FxJqOEY5}{0( z^&RK#^0vy*4Uy-)@D-WLw_M@KmtVawe2OBAm8W+%mtRD(O1`4HEzr4-Z~t(N%Y1ZM zz=TA-_>^A0ck$d1EOJ5dq7Mn>8%yc?M#_Veo8Bd1{an-2OgYCEdah8>o?NjfQlzfP z!gJ*lnq2WDp+58V6IWcPR8I3&Z1EM>Y3YG*%=O3eAY$7Ax^SfY_mNI!-UBd?*0D%# zi%*kR>$x*Lr3znK#e1KK5=ye4jD5s$N##%v;UN_`M`%rr>&oUHp=pG}8ZKwFG(lt2V+Fy8~n1-yN+g+jTUnVq9be5 zP}aiotc9P$h5W%Rg*91Pm&@^tdM{6Xy%(!Hjqz-j*ru5%jEwzeW&y+ji`7^2iHBw` z9G+pD$xo=^cm2gSv-bp?R`W4PkJrxZZNpuWF6~ySpcBF|I^GOyuR9hhtn9=j8$297 zX~QdtBKcKC$Y1y6I7aijpF@MmE|V)<(f+rd7v-Pn_7?_i+y9_f>$uA$a#^a^c6X$I z5FgF|eN%c4DB9_Ok!@CmcsNKBGa6(of8BK|;dH>*(99zc_K4=}2C$`)^X4vXsCyE>g3+0a@i+iy}x3NC+THOid@%97fg9iBK}T*V&Yj$%%Fom^BP zkNwVU-&qqhDl&9EJoKuk^dQElQbMn^j*Y9~^a! z#qCwm&S1GrHG_Vt={2obOnaE7GT1nMI zSw;3frEh(^#PQ>JP!si;dZJaN?uv6DWrc!ZRC{N=>&mi`BkJ)Ig*b7WdSJtQO?`Ip zssVM4^7lWS#&79slrkheRo|9m)aO5*AaPo9)Z_PuHs{84?p5`?fVFyCu<#diBS}tk zu2#YGzC;GH}Mw!yl zUY*%slQU{Z5^STo45!hfvAvZp6^3#=FNK}T+vYQqk0zYnvqM>|buy}nhP%)L^;E+S z@0>xRT4+_dynA9{v-bDPrsYQ@Jx6?)^v!OUP|9%B;>4HyNX zoja=`vZ#wk29k52;A^`O9pL0;cko}OwR-Gznx}_g5T_p(E1mDkAF`vCN1Je$_1B8x z176E)4U&tOigtJC*~dLk4<>0I@hl5fAZ;W?`41>;Wm0Jdy6 zaG-!GuWae(IkOefpOb3c0-UOwJ;G0b6aJICFaPLZg1>wBy*9(98ml!Ab??iaoh{p7`m^5=%gw_B7gXc)Wze(V((m3m!Cu@{~Q`oyF}g+?z&=V z%50U3tS&w*W`Rx2+xEL6;`*5;X^xY$cH;P(k6ae1Mcvg02!-xH)NPA9oc5EkAABKq zZRrmv=<^5Ew8K;%r6c<5DED9$a=2CF~X#&L~Dt?}B9}A7E4`cBZ1WrKFdR&6R5=pZ-I zCFv{b!Fpnz*2$%j)WQXs4lQ!2v5h6Ktu*w{&}MUAUqd6Fg{ysk#$c`PI9F?qq`z?q ztL}frK3JdHPZ+E!4L`JD?jU{D%uS7xzP3_Zg|zQv1F8ETkI{6XPuLm5_ZA}G~B zzP_e>qLzNYFt#@Jl9U-sHf_p2yi~Lzy^pJq-c1jjxocxddLO}z2b6KLEIh^{1l%{3 z((kqvFz*lzbIsC>XV9k7YyvZVK@8T??+gYR<01!ZGgpUBH3EIv>~um-zv-C8#2F4R zwQNO$_1S%7=5-$Z28fg$G*O>zP#sxjAwuc5!;%2%PYp2}4lT+YmonD7G>@Uz*N8$2 zlNwD99{pCkbh7jC($ta^wtbZTPU*?bfe$u3$pNpAZv6s7uZ<~=$B&(jx00B5EJ#8Cue}u za0((j9F6I1chT>fx!{+B^_EE?I49w^5d-Y<&ToZ5BrqVGk{cJY(&?2TgOOw44O!<}h3|KUMt8lPh(^_rnn=#4x zbUo}T`K*O&-n8P-BJFq<69*<4zsR^^HC+Zq+iO0!e(;Kh%44M%dDH$>y6 zQpVi$3h~-XmRi)6NSN;e^u1~6S3BPy}h}eRVZsDF0Y%HOl z-3moScTzdoXz0r}ls&8xeHdp>@o~?5xUqx*S>NwEyfnT%u~D>&e!eJavhgJS+RBrP zjU|&m4AyEbTt(3~`n;;~1-L~(JON;Su6>jU)B zgLl=?N0v#9K$i#Qcq~KZ^e6_s@h?BwsM@&!N?xNhML!!~i!*S~^wSM-RkbH;HWM`z&P zJ6~IZZCE&z{8%%n{yx)Ms_mJh4Z|K;B1T&hkIuQV;*6x{K0={^-qFmekQ+*Sv9^!z zvS_QY2A-FgJSg;qO^Vak6I{dRguOW3X#Jrq(H7Vh1ZphNtX9J(;@%E}@=0o9)720v`58*?N9~iT_A5>^Sqvx*%+*50? zaG%j0S*GfVCj;jYu!>VNPQ0>U!@_Z;_6)IMAsv#jV^RONOBY~8k{7D~ssoN0*ZUI*TyEh9kkXTUgJ^Sj?LTeuCsRp}Hx z&*}<1+Jhpeuv??4;%<^oZOar9@Szpg8DLUD$4xmNptZ_qv4uN1ue(B&N>lLUhMt~H zK^qnVf7g$ili7zB=`7{YDryb}eX%WlJ+y{h{d6E?egO(;cLjWMswv>6pAHI7uE^}J z(D?|O+Y{6k-$3{hu8WQzW6lIYo0*yg?UhZu<>0xoiyN^?qfYCLp@S^k>VPfAeN7t{ z5(e!cZ%mt+9SQ#bNY8zc%jNh-mP}rfnzAErtk6A%h8n*SZ82s?e!4!sdU2zHAk#(B z;1nAcc2`&~DFE}xN0uUA>FajoTy>fZT1@#$Hx{JbqvjB+-;%I%pwUg-}X1 zEMy#1Poyb330%mM1OQ<}jrYz^wIpc@P_`whJppeVtUt0$qnlr&8LW+a#|HHdVEv=^ zLu$i99%oCf)|uLsriEoCm$<8ACbxmps=DJfeOT4Po;^%^SQYEWHDG6&3wK1S>}>AT zHcfx#b==&Oq4B-1y0loOnw<*gH*hFEeelkzX??fyI`c(X`g-j`xOQ^whJ~hnrKX4s zUvs^>iW_&_V){JlktOLQNg5k|Uxjp{979=-57&&vXeS$N#Qz#l9Rmne@lyIX?2$%Y&;mD_x6q*wdia~QSSAlad(WSIaj>4|ls!ui z@gQG1;AFs&W%N`wcd*vdcUDIr&2?B{MIwQHCdus$%dR52o8ksH`w;y#0N*gTY&I#s zc8_`NBK^08vC#DqU+2=_vCW3A`>g8duMaKaJ^A_?RhSRDGR|1~S=V7Yp6#8{SZji6 z^}a)kNJRXWNiU04$u3@nj*7Nx2J{%sNxHIFMU`FQ+@b0_QpIt?Ah4>L`A~Xzsmj+8 z+&h!Jyq}>l#|)-`a3$9K_Ryl^A0Ce%N7$xPQ1okaQkN?1)DoI=Mlw;aJ2|?sghcqB zUmaN%cj?sztG3Ae`WlFv-bjXfAg~1fUTyb}ii0m0{V&iP|rOfYby<#lFDY&-&wdMzixq{pBQ_t<~itIbGw7+Pt8f0zCS1&Nx{ss z=Heb;W!MC=jHV`SD$Tq!Uvc$KsH)nxFm1&3g(0_IYSij5`s36jXnU5Yiv7H zp@lB(rBDY~?RZo%_+Hlo^yk+T3=6rNO34`?ozV6nd3ym~yQy@l(bP9ee_6&=^d*oK zv)k}~=%?$+&~tYBav8Vb&$gyU(f+LZ`#Ja`i&6X|y1X{*MLRdUF|<{=r#6CzNqXq^%pZ zutz%mHJT4MP=tk>%3k2*yhh8}6V>L6;7f2~xH-fQU4G;u$qX}WDO;RLr-o1B)Bnqf>c=H{m4no?6tX$9s&b4GzFBQ4)#EX>QuC`wKhn=)HcwoYd6+Z8R*;i0{K%s5j$_=%p1@y}08>$Q&xpqukFBatD{90OMy(et7kY7z- zBJK^a+Hrhc%^EuX8KGg7qzp#y=!_^cNmGW(u?;q~FfeW1>ibvm)?JYvEOw2?SQl1C9miBJ`SeE~Vuh+2-wSJ4dA(~YmiS7R8J zgSQcT5!p&K@P|dH9dqHyE_7VD3U-1M77$LX4LADW>2X{q=U|CCY#31<)EA`n;jhl< zA|H-H=mf&{QyL#?5ZB?|^lmPtS&J$q9f({95lHz6mQ`q+SRe9*IOBVyPUIACX4+6Y z?^Y~2ZfrF>AV9?`8at=xP?E5wiwDy8U_*)6AJtTw&}rUHpad8c1PrD%b6-rvwB=l~ zBM9q33H!Jt#t_Jd56-cplQOW z{?1EJ#f!u}{I*5#^6R~G2|nZt{Dz?PF6c_@J)8tFkJJw!Dy<2EZ|>1g;K*p<(;N$m zcky)D*`SMmB8W;b@L^{;3&*sL1F|NGBJ|Bz zqCFt7Iu^w(r^&9P?IAtUeQ%mJV-vC1x%*C2*vVHZQa`~({hQ*ylTnE-q?_cBn9(KD zPw-I21VNq-BK!Z?6=$B{V+J2Kes@xn7;}D=i`WVVdy3K@Lcfd)$wtsI?Bp!WqQ$Vj~}{BLebJ1HWuWFH!QQ|F7oNHCu+K|<;U zYF7XhfC({{;KREl2A?qgE-sbg3(_&6i82z#4muGTzZIh}i;IeH#kxj&YP(S4HZU|Y z9n~XbmIWEM@+gvt0n;0N2)f4F{oF2hx5Ri8ALMJqB$?<-)z_#fGD)nb^aQxLAk`NH zc#X%aMOp(K`6h#yLX3~2J*;mE$Kt<lf^^~6|wT#ylw=`KlJlN_Z^l8Bn+qIv?&V3(dCqYLSM0r5_JFv=H_ z>cf*{(S~gZLujUtliD6+auFt9)c8xOE|l!TwG#@c6w*yDG?*6W%=CR=5M^}59aCWx z33DXv*p_gF^$!nVNOA}vqYL7K#ALdHliJY!T89 zVi`UJBV4^l3p+m=Die(^VrG9*wuDDPX~Z-~F^ZaHHNrE7ekJ%!whx;LFY&2Nm!NsC zS;V{0NKt8ZLG2v#1iTY`CDlbt;MA;1Xg5-P0<#NC@?lWz=?8hKK9t}J$nxPd?&Hn0 zic+`oDJt0)phd76WI}+le4sS0cLZengxM~!p$Vt55Hk4)=-FqAkOgKtu^ueJW$F9A z|2yCJaS;6v8~dUAODB=&v(5Z56C=Cd((I%Wmgp+cx{|f<|NhdpF|{kf@LsaPCBL5! zvLNjN93_;`LJ4hg?@y6PH>lH(>ylk%apDY2-Bo=vdHa``UX*2s3@5QTk21+uIVhfo zuvZaAAqtsYS%*K>w<%LacAYblks<9?2?=#~thP;U9KR>AO`Tk`6d|2UlQ0LTIc;>} zgcddo#YyAU*(7D|v3wGQ-~)y$*3^E=(1!E{P_!1u>$Oy(GcM8U)c*}3uYj8Ai8fS{ zY_%F)SIZs!NuOY1z$GBBV05m zKYtl)Aim66RJ?3K@m)&tVLaKun2}Yim}uKD!#i|eIf8%7;UDaHR#R2KdU-?UX9!Kj zweKgEs;g!~mu(2GZbG?3c-|1ocOiUbZ4JuL5s4Y2DcPKonwFlie8tMDRjZvGjTLcn z^YRM{i)!!{8x`001=5>V*KJr=)37>|Ua`EY7}c+`au8Qjv(f2HKvVhp1!YD0bd;|z zz5A|G9tK8G0j{s&A;Og7xvOIELroI%}IthobJ^ zz545~?;q!0T2B=F<0|} zd)=pLZp*j|U)*lOTOTOKX+i{%9{;8>xKWweUGOV|d+a@hyY+Dm{&f3Xw>3qKzZxjT z)AdGtY%}S$FD`alF6;2IYqa|#r53l})Vn(r7WdOh5$?VGAh+kyOt<~jQ1_FcrMs`} z48m2;Xw4ZsUV#ZaZ1*mdO|3b~4(nxoC6SuNmA~(jxbGFKlx9Fwb3}6}z*#Lfz&?dVKM@ z2;BZqEN-tY!Ucae;Oj0@?kkIO+-jxBExIoQ7kn+l6Oh$y3*@_95h7d_6OG$MdiRwF zX1T{0%5BcecV9Ua8_j9;stv&ZsQ*? zhZubthx?p&Lk8A-HV22)}6x#Vzm7$FD53;>HIlci#mC?%fdTe$kiVmdbUw zbS%lOeyhwqelf}Y@!$1s>D&}Npe57&Xt2oboo{qET*$+BJ!^4m@ErI4s6zZ>!s0d` zQse3AGJI@toO@R*j`uvF!)4NYaGNaA-J#8Nuls|@P2Qc1+dq=IJ64$7L%%C?+k5qH z@rq1$-(HnlcCQ4ty%p--wcO$^h?!X)V-i5xq zb^3>Sl7MrI#QX+)mEaLk4pKM&em4H){sOn=op^Wpm2`aYxlla)G=mRr2*%BSn&aLz z!{S~SrEx!LkhpIynTOjKhr2B?mi8B1pG~%2N0PsnL5=z)0IC~6 zn+H(y0NOQx_79-L1E_NV{dNGI8bGfOpxy!W-T*o`fIb>Po&j`a09_kEH{VBya{>tl z5IKM%2L5|8ApW~E;Qn`K!22nKzko4t0X{FM%E|fjyx) z^7MZBjeHMRf<+1>{E?e8&Q3~$MHC*0PG1o$Ly~KqVi8#bn~&O<$RNCP_=c634z&C^ zeRedKc8}uM5{9L?EhtA6jU&%PBUI%1$sAEodvJ9y1K*=EZ;d|8w2Z=*uH%6iy0&h3 zYv9dU${A|tZru4R#|hjB>CW%}laBw^G+-*J;t}tEVi_wsIpzVy@9nMxW)us8j*T&%k= zKzqkVV97hYSxd3}Z$g{|um`aB|NY;B0LFpp1aI~Rqs~zTKC%DYF>lE2^IdQrd;7c@ z&YPiTX(pj|fj9@yl6<|HIp)>E5wSF6=cp5Y$H8wLc#`0I9J+H9R%Ci`^89ejR1$Rk zJ4XrBd4(Y7jrbro1za1(nF0@oeV&006q$?DbQpT^3WlD#LNKKxf4@!FIkk<`ISFY3 zk__Rk(XiMv3gM~{1a%3NjoV4w3x52${!8MY%XzM_`TWD4y~n&V`2A=kJa_xtHshb? zABX+(d~f(a&mWC|{GeRvazKCP5|qQzQ5Aj6%a&J!=8wwIF>es;!5uu6L}3i8PoI~B zvd8Al%FE_|5RQaL@67?6*yo9JwvlvrE*&sspACU$8TgHu{nPg~KnD-Pta9n-o%_VO zy(5IDW9*JjEYIVR4&TlL;u?%1bF-I#R0r~6m7T-TeIMo+n=fNb%Lpba9c3{^y`7_? z90<|v=^W<4PA`Z&dYJ>k3*b{Y&r}X)gn%dqIJ5sU*4s9Mn0=SI$T3WK_Km=Ezs3S9 zz^=`guK}OB+d5v$g*vBs{s;noSRL4-Y`>Q%*zc7h^a-l$9L1O&BU5(H2p5BUy+9DM!owG#c89Ok?idCq$=`n(tKJ@4gM0E@DH!=m(-VUB&rFiAI$h!E_AUAf=O z=~Z~7p8ejZQVK@|(CUhMd7cFs`6HrUgQq4df5Z$nCgHxQ*X6_Er<{9tb z>5(vT9zNv3VMHEY&Ur6kXU`2Dt_Skfbq*66_pqdF$MBctmJuBC9R|Kq$YW1-{)h(n z2!S75_kc{ERZH4DBH$M|&5!F50>1#@!+~pp%AfcJB)5!A^Bmhbygj33gsj}}#Xb9m z-^ywkQF-=yc{%$$QQ#vS*fC^m_6|=F@ZvH$kErgvR{}i5)BFhV5%@S3uK+_z{fSLpGoH1_MtDcybsC@KaCo;{!il z)vjSJ%dc|h@GO>J?@rG=;PVq^`v5bQM^*_K0%qaBPYS%kfLG`=FV!@k*?`v_UJsJk^c{bpf zvlHZQ8IiKQZsWyb6+6v~@W_By4DgDc=JgX^6F-xy=(b#sf?Oo1%)_++(2{ZGj zF)9Ju5x>AFiRH%-9v$#9053i8y5m!OeumMnA#aw~lTeOWd{}?FgAa=hV)=sqBm+Mq z@Gt={W*OqyxWcOn}cb zcjRf^HGDs7|K?r8`5+IGv&*v&cFGbso;~J;OXFM4rj+ z9ObVCd((txKKLfDa}S*BJk@ES57eC!&mIq3=GgM~lbyL>BbDa{;>z0O9w^s3kl~Tc{1MjASex?h*z}8kmG?uCHv(ix z0(o<%<=yUyzGE|1UfSR}kOut=z+oQT$KiJfdmk_$m<})3-r`~F#$U>t3Ya|!JinO6 zeUE4R|3ubIpp${DfnfXm+p@Ct_q6~0Le>WW0|ChT0ATPL)D0roJdXEsS$~G@_kh7e zpuZb)S-au3qX4me2vAG^|ybAYaw7I0$G=UUZsEqi>sc^pY?-Z z$oeeMjso46pg$LAM7`j@Y&rOa52!#NBghawla+J(bldgsr5!gZ5<9z?8GRoEw z4*-vQLH--8{7|1y+mE&Lbo-Ls$N^-I}+ZU)G|9cVJ) z{vA919IFMuLkDs-Lcf7GZAbS{EvV9D)2_WATsFTD%%l3UT{ta%If$jjv76&?T?Yp7x0eb#Iwtc4jk#fM5 z$l2p*0lHtxWCYn&AbTR@wQyRdot{;<{ff0Em+?TGv~RkN%K};*$e#du?gl+Y^mec{ z=n4JMmC^g)oTc49-G;HYqJaJb$eT;=^sHlLW9bsNL%qtB4TmUagujb}CROaR+H z1Nvh7cSBnMdBbsC#||imEZuHMYR^MJM|(QF$xx>L2=xx0vk&;N`m}mhe!cr=x+i$1 zeJVA7!~p&&$bk=er}b&|jLzBT37F>D3Oo&-9l%pVgD-8wEL%?YfV8r@i0F=iUS-wEKqP*=ko~%wfPc-EH4)~x1_9+Qw{T`0LdLDKt zqdmjFHg}FnK@XhXGt32DV4~gg_AJ&9_j%a*90z?yuk87mK3Y#P$jSO)Bl{;T3|NUS|6zIc|^-^kQY5!?uyE3@oNP&%6}2Pu1bg z0KOs^3%m&Q7|4+X^it@9vwkI^;ki(^hlKvKh}r28^uqXw`B1Tbb{$21}svyeH> zUNejVua*4kxaV3SjKA#jN7ykXg7Fsy<33n~@(DYZG`B%_) zX>miqwH=6A0_U9}0=N%j$Cuc#COc~w4CC5ausb^sc<{*1;T+hQosT+)Ux9C@+3Ptl z7K1seKe0Qw6F?Jz9vBg#W)Dltr8|e;$I;j9SoK=rPjW`&G{gN?(4}+uUAV{cB7pWO z;K#MEg)z!b59;h39fmorXZRty}4U&|dGswJ}K0SHjQ$jA;gd&wv-p=^Pe9-j~Q@UN&!-;)b!^fS2nT zfWGa3mtQ&HJpr*CLB3J;z8GYCcKY`rFQWTk9C^sAG_&JWIHwPJ6&e^5!u3GruVBAE z7*`&GeBgMSj(htWOUHxjK|sf6=LCRG0KduGbOE=ov2;SX9s+cMa7_wyBKQr_HKy|w z-@eAuN#J@o&;`LY(a$ip-kx7z=lj^X77WTS7ScI-O1|C8`b9S!Url_AotGItm40D3 zh~bRjU~hIlfc?!$2mBxfS>75|SXxF;rJo%Zz};ptBvfO5X0hMItsrd*T8REdgrJO{GX*|Wxg|qkq%??8&E!$LwP?1^FMcF zz$}~*))$Y!^ASMvD9|A0k?FjCE(1c%xd_=4jG!~e_&@T&K}VL)4&ZZ#W(8ZffZT{~ zna1yqEbQD2>sJKY>-`G+aMRzXzWy^De|{a2d%Z*a&e0)$&PW01m89$(H7N5&mM6JK zwpl!*KR}s2#he}f6+G*N#LnkDEzB8V&)K<&KS4dj&P&XKeh&6$uoLXwz+!@fzlmt@ zODG#BAy3Q>(>~3=c3?mYze&O$y2_E@^cMKOtS_;8plbvnxQ*dv&{G?qH^S4qN9y3Y^;EfR zT)_@f;LYO1>IL)eU-^EV`nm@0{Rqb;aAfZXfX{qCHT8)A>?aP)8G$`FziwxrHzUOI zzV;#L5lWPwi&r;=Eve?N5#OI^>4mIw@Ap2w6+^9~oVJU)Y%>nKrq z4yjRGgth86|F&DJZ=XGIOSu_SutB~7|qIJLHjpTc97D>-#qxQ+q+`l-at96*MdMjRpL<(5;`Mb08_ z%5o*&oLHxjVM-0qPW`ZOO0l_D8JQ&RUK`9A_#LuhM0gmowXafN$# z$XYg{wdZm#)Ym<1osAGdLfm%rvS_a8?isT8jEIh`TwEzgelYLH^8t4UKw#^Dxk3nR z&0@<2xyBTc%`7^wUK5^=hIwk=R5R>6jBu8Z6p~k* zbR7bCE8Q8p8T|S>e=vzoXKq6ljSOs@uN+e&JCvOgc(C#xJv6?8Ge?ku-R#sMK_IXwh z@?}g#1Ii@e_OD2a{sqbW+t||o#CC4vT0B3p62fQS=KfD?-{KPxWL)$Q`5~hAKeT~p z-yd-2@^9bc{R|EEc~%crA673`#;KqEq0j4Pb086rxRCge1dsxzlR#ECThtx!IfTou zoKaafI}omlAc-MKAO%4pAyJTmA%#GaLJEb%gA@rV3X%*`4jubBE{lb;-lBj0_y7BM zj$m)rhY@HcfJ8!yfJ8&mKw==JL&||P2a+AqVn~&c)ABKJR!^W$=1$`da5*q?m|I|O-4>GXtQtE&g+gAO5g%7&@&8taJNdUk-ggo!OAx$MV%sfv-!hDY+&DsyLm1Ry4hk6!n0_ZzG0J6+Y(MXI zL{xa=KO%v4pN;!|r%!{J16~M)3;l!xklLUhdjL|KmxPdOA~+916{8~dH@SU;gm*pv zF*UV^1J}NontGf78iF}6#{MQCWGe9807FjseHhxyo@Ds@w>RM;tPO~U+h;BJ zt(N|bW;>iGQq=H#ijqTM$ln~G4un+CTP3B^ilV923m*LRxkaXeqFukeza9ufsO_Ib zQ4ik>r_+Bi6tz92?9i5VYZjEXJ@?#m)bQ}4aEdBhbmV?%)#`$sMJcO_sPNfE z)Pv3i)Cp=O^>x`ns$kV|YW3=0Lwf07v-&UmI={rcilR>`upwg_kVx+|G%~V``J*~f9glTkANQmKLUOP{0R6F z@FUk0*0Y3tM1pEm25%446N5GGO9|1oCegymo_!00U;77oZfFA)r0)7Pi2>223 zBj88CkANQmKLUOP{0R6F@FUk0*0Y3tM1pEm25%446N5GGO9|1oCegymo_!00U z;77oZfFA)r0)7Pi2>223Bj88CkANQmKLUOP{0R6F@FUk0*0Y3tM1pEm25%446 zN5GGO9|1oCegymo_!00U;77oZfFA)r0)7Pi2>223Bj88CkANQmKLUOP{0R6F@FUk0*0Y3tM1pEm25%446N5GGO9|1oCegymo_!00U;77oZfFA)r0)7Pi2>223Bj88C ikANQmKLUOP{0R6F@FUk0*0Y3tM1pc>0;Qs*^RFYi) literal 0 HcmV?d00001 diff --git a/CartFiles/NTR_Launcher/DSONEi.nds b/CartFiles/NTR_Launcher/DSONEi.nds new file mode 100644 index 0000000000000000000000000000000000000000..772bba2824d7f7acda3fb8db12a8f4a2f0be295f GIT binary patch literal 1412160 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 P@~eOC-}(QC+W-9@CsJz| literal 0 HcmV?d00001 diff --git a/CartFiles/NTR_Launcher/DSTwo.nds b/CartFiles/NTR_Launcher/DSTwo.nds index 847de316a42def42d1010f2f4eec8c22d949042e..4e79567974b5ac6a054b4b303bd42d8ba3a39fac 100644 GIT binary patch delta 5219 zcmcc6z|_#dF(FfS0s}*IU>$G7EC!|sMhhkeC~%koWH?MtWVD)iU>=jN_hd(=fXy2i z1ME2d-~0a@gf}xX|6pI_VX#?%*@0uSLxaHN8T`SM7w~%zGpx9&0_eXDj4YF<@`nvG zzYVvz8uicc%YUPBIvS^=X<#%BjHZFnG%%V5M$^D(8W>FjqiJ9?4UDFN(KIlc1_o~$ GumAw=h~jnt delta 72 zcmZo@;JDDhG$B*Af`P$%Y8`LHEC!|sMhhkeDDZ3mG8`r+GFnYMFpnv5*JMYgfXy2i W1MC(HFmo*OFxWJ~fn%~mg8%@wDiizw diff --git a/CartFiles/NTR_Launcher/NCARD.nds b/CartFiles/NTR_Launcher/NCARD.nds new file mode 100644 index 0000000000000000000000000000000000000000..c404045cc48b106f238d050d48903ec471637926 GIT binary patch literal 131072 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 GDDYoBi=UzZ literal 0 HcmV?d00001 diff --git a/CartFiles/NTR_Launcher/NTR_Launcher.ini b/CartFiles/NTR_Launcher/NTR_Launcher.ini index 453fc2c..af17373 100644 --- a/CartFiles/NTR_Launcher/NTR_Launcher.ini +++ b/CartFiles/NTR_Launcher/NTR_Launcher.ini @@ -1,7 +1,8 @@ [NTRLAUNCHER] -TWLCLOCK = 0 -TWLVRAM = 0 TWLMODE = 0 +TWLRAM = 0 +TWLVRAM = 0 +TWLCLOCK = 0 SCFGUNLOCK = 0 ANIMATEDSPLASH = 0 NTRSPLASH = 0 diff --git a/CartFiles/NTR_Launcher/R4iGold_Launcher.nds b/CartFiles/NTR_Launcher/R4iGold_Launcher.nds index aecc3f6dcb90b020b661f77ffa1574205d09914b..688bef131aee141c4b396be235b99cfb7c4afe76 100644 GIT binary patch delta 553 zcmZWm%MHRX3~f=la6n3q2noe¨4kg%vUdmmb-JZUoQIc0X#`B+m1T{p?KRFpb0O z)93Ty{%!4A6Go9Nx~paTb`=oJYrF!$7=^g_Im0lX`4N_9;KqZoj)QBJ1Piubi`a#| z$B`s3zDe7%_LTU!Oae7mpJuIDnhG^KP`0#I2sNUUsha4 zbQtLj@DA1lWu6!psnQ6`3X~!#bb(hEQu*KuYV4RZ%M*Kt;t5X>mT%9l`_v7 Gy7YgN@;Y_^ delta 467 zcmY*VI}XAy40T(C#DW82L8{cNpzaLJRGb8fJ+>44{yHHadR9gpSuuVCg}(?T diff --git a/CartFiles/NTR_Launcher/R4i_SDHC_AVRJ.nds b/CartFiles/NTR_Launcher/R4i_SDHC_AVRJ.nds index c30e768b28a50affe7b9eed806ce67c309d6473b..3660b51c91d967105fbf905441b8bb164a2d67b6 100644 GIT binary patch delta 707 zcmcgqI}XAy40S6e7HB0FgapYMxB!BI87{yI6GKO&tc;D|02~3q#KuwJ1|YG+vz_qW zNt?uZezBk3p4RDco0ik0^s`%fY^=`3W|vNu`*YDw_Lrjmy{zU*uaeMV=Y7j%7{=z7 zceZr@TrCLdRh|K$3?sM==NyLe)Q_-o2Ch69?{RR=2|*5xMx{( z2@yK9fTy5Xh~rw2gav*~!3;3Cr9<2$nS2&1LbFR=vDjh3;c%FEa>jYY4kMicp@S}i zvXB@TMWqs6D^L+BLSOKkg;Zh47v$J+K3O4g=urHGRS3)53!=O8zF8Ylm!{OAF%4)) Kef&z-e_#*H&uVM{ delta 24 gcmX@Gf$6~%rU{vh3=^}yF!csbeps?#6H`J50EaOO)&Kwi diff --git a/Makefile b/Makefile index 29948de..9519810 100755 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ export TARGET := NTR_Launcher export TOPDIR := $(CURDIR) export VERSION_MAJOR := 3 -export VERSION_MINOR := 1 +export VERSION_MINOR := 2 export VERSTRING := $(VERSION_MAJOR).$(VERSION_MINOR) # specify a directory which contains the nitro filesystem @@ -24,6 +24,7 @@ NITRO_FILES := CartFiles #--------------------------------------------------------------------------------- # main targets +# -g KKGP 01 "NTR Launcher" -z 80040000 -u 00030004 -a 00000138 -p 0001 \ #--------------------------------------------------------------------------------- all: bootloader ndsbootloader $(TARGET).nds @@ -34,8 +35,8 @@ dist: all $(TARGET).nds: $(TARGET).arm7 $(TARGET).arm9 ndstool -c $(TARGET).nds -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf \ - -b $(CURDIR)/icon.bmp "NTR Launcher;Slot-1 Launcher;Apache Thunder & RocketRobz" \ - -g KKGP 01 "NTR Launcher" -z 80040000 -u 00030004 -a 00000138 -p 0001 \ + -b $(CURDIR)/icon.bmp "NTR Launcher v$(VERSTRING);Slot-1 Launcher;Apache Thunder & RocketRobz" \ + -g KKGP 01 "NTR Launcher" -z 80040407 -u 00030004 -a 00000138 -p 0001 \ -d $(NITRO_FILES) @cp $(TARGET).nds 00000000.app diff --git a/arm7/Makefile b/arm7/Makefile index 8871d23..c842a15 100755 --- a/arm7/Makefile +++ b/arm7/Makefile @@ -24,8 +24,8 @@ DATA := #--------------------------------------------------------------------------------- ARCH := -mthumb-interwork -march=armv4t -mtune=arm7tdmi -CFLAGS := -g -Wall -O2\ - -fomit-frame-pointer\ +CFLAGS := -g -Wall -O2 \ + -fomit-frame-pointer \ -ffast-math \ $(ARCH) diff --git a/arm7/source/main.c b/arm7/source/main.c index 906f756..a8e51c7 100755 --- a/arm7/source/main.c +++ b/arm7/source/main.c @@ -218,7 +218,7 @@ int main(void) { irqSet(IRQ_VCOUNT, VcountHandler); irqSet(IRQ_VBLANK, VblankHandler); - irqEnable( IRQ_VBLANK | IRQ_VCOUNT); + irqEnable(IRQ_VBLANK | IRQ_VCOUNT); i2cWriteRegister(0x4A, 0x12, 0x00); // Press power-button for auto-reset i2cWriteRegister(0x4A, 0x70, 0x01); // Bootflag = Warmboot/SkipHealthSafety diff --git a/arm9/Makefile b/arm9/Makefile index 174a96d..bdca3fb 100755 --- a/arm9/Makefile +++ b/arm9/Makefile @@ -27,7 +27,7 @@ MUSIC := audio ARCH := -march=armv5te -mtune=arm946e-s -mthumb -mthumb-interwork CFLAGS := -g -Wall -O2 \ - -fomit-frame-pointer\ + -fomit-frame-pointer \ -ffast-math \ $(ARCH) diff --git a/arm9/common/audio.cpp b/arm9/common/audio.twl.cpp similarity index 100% rename from arm9/common/audio.cpp rename to arm9/common/audio.twl.cpp diff --git a/arm9/common/crc.c b/arm9/common/crc.twl.c similarity index 100% rename from arm9/common/crc.c rename to arm9/common/crc.twl.c diff --git a/arm9/common/defaultBanners.s b/arm9/common/defaultBanners.s index a413bdb..96d66c1 100644 --- a/arm9/common/defaultBanners.s +++ b/arm9/common/defaultBanners.s @@ -1,6 +1,7 @@ .arm - .global dsCardDefault_bin, hbNoIcon_bin + .global dsCardDefault_bin, dsCardInvalid_bin, hbNoIcon_bin dsCardDefault_bin: .incbin "../include/dsCardDefault.bin" +dsCardInvalid_bin: .incbin "../include/dsCardInvalid.bin" hbNoIcon_bin: .incbin "../include/hbNoIcon.bin" diff --git a/arm9/common/encryption.c b/arm9/common/encryption.twl.c similarity index 100% rename from arm9/common/encryption.c rename to arm9/common/encryption.twl.c diff --git a/arm9/common/inifile.cpp b/arm9/common/inifile.twl.cpp similarity index 100% rename from arm9/common/inifile.cpp rename to arm9/common/inifile.twl.cpp diff --git a/arm9/common/launcherData.h b/arm9/common/launcherData.h index da1c8b4..e4d9d6b 100644 --- a/arm9/common/launcherData.h +++ b/arm9/common/launcherData.h @@ -1,21 +1,3 @@ -/* - NitroHax -- Cheat tool for the Nintendo DS - Copyright (C) 2008 Michael "Chishm" Chisholm - - 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 3 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, see . -*/ - #ifndef LAUNCHERDATA_H #define LAUNCHERDATA_H @@ -23,13 +5,12 @@ extern "C" { #endif -#define LAUNCH_DATA 0x020007F0 #define CartHeaderCopy 0x02000000 #define CartChipIDCopy 0x02000180 -#define CartBannerBuffer 0x02500000 -// #define InitialCartBannerOffset 0x02FFC068 +#define LAUNCH_DATA 0x020007F0 +// #define InitialCartBannerOffset 0x02FFC068 #define InitialCartHeaderTWL 0x02FFC000 // System Menu keeps cart's header here (if cart is present) on initial boot of any DSiWare! #define InitialCartHeader 0x02FFFA80 // System Menu keeps cart's header here (if cart is present) on initial boot of any DSiWare! #define InitialCartChipID 0x02FFFC00 // System Menu keeps cart's chip id here (if cart is present) on initial boot of any DSiWare! @@ -40,10 +21,11 @@ typedef struct sLauncherSettings { u8 scfgUnlock; u8 twlMode; u8 twlCLK; + u8 twlRAM; u8 twlVRAM; u8 debugMode; - u8 fastBoot; - u8 unused2; + u8 isTWLSRL; + u32 cachedChipID; } tLauncherSettings; diff --git a/arm9/common/nds_card.c b/arm9/common/nds_card.twl.c similarity index 87% rename from arm9/common/nds_card.c rename to arm9/common/nds_card.twl.c index 7e9f46d..bd0c3e3 100644 --- a/arm9/common/nds_card.c +++ b/arm9/common/nds_card.twl.c @@ -19,16 +19,18 @@ #include #include #include "nds_card.h" +#include "read_card.h" void CardReset(bool properReset) { int i; sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart if (isDSiMode()) { // Reset card slot - disableSlot1(); + /*disableSlot1(); for (i = 0; i < 25; i++) { swiWaitForVBlank(); } enableSlot1(); - for (i = 0; i < 15; i++) { swiWaitForVBlank(); } + for (i = 0; i < 15; i++) { swiWaitForVBlank(); }*/ + ResetSlot(); if (!properReset) { // Dummy command sent after card reset @@ -43,9 +45,9 @@ void CardReset(bool properReset) { for (i = 0; i < 10; i++) { swiWaitForVBlank(); } REG_AUXSPICNT=CARD_CR1_ENABLE|CARD_CR1_IRQ; REG_ROMCTRL=CARD_nRESET|CARD_SEC_SEED; - while (REG_ROMCTRL&CARD_BUSY); + while (REG_ROMCTRL&CARD_BUSY)swiWaitForVBlank(); cardReset(); - while (REG_ROMCTRL&CARD_BUSY); + while (REG_ROMCTRL&CARD_BUSY)swiWaitForVBlank(); } } diff --git a/arm9/common/ndsheaderbanner.h b/arm9/common/ndsheaderbanner.h index 133b2e3..68d9e34 100644 --- a/arm9/common/ndsheaderbanner.h +++ b/arm9/common/ndsheaderbanner.h @@ -139,3 +139,4 @@ typedef struct { //static_assert(sizeof(sNDSHeaderExt) == 0x3B4, "sizeof(sNDSHeaderExt) is not 0x3B4 bytes"); #endif // NDS_HEADER2 + diff --git a/arm9/common/nitrofs.c b/arm9/common/nitrofs.twl.c similarity index 100% rename from arm9/common/nitrofs.c rename to arm9/common/nitrofs.twl.c diff --git a/arm9/common/read_card.h b/arm9/common/read_card.h index f8821bc..20b6c91 100644 --- a/arm9/common/read_card.h +++ b/arm9/common/read_card.h @@ -39,8 +39,10 @@ extern "C" { extern u32 cardNandRomEnd; extern u32 cardNandRwStart; -int cardInit (sNDSHeaderExt* ndsHeader); -int cardInitShort(sNDSHeaderExt* ndsHeader); +void ResetSlot(); + +u32 cardInit (sNDSHeaderExt* ndsHeader); +u32 cardInitShort(sNDSHeaderExt* ndsHeader); void cardRead (u32 src, void* dest, bool nandSave); void cardReadAlt (u32 src, void* dest, size_t size); diff --git a/arm9/common/read_card.c b/arm9/common/read_card.twl.c similarity index 87% rename from arm9/common/read_card.c rename to arm9/common/read_card.twl.c index bbb00eb..03516ad 100644 --- a/arm9/common/read_card.c +++ b/arm9/common/read_card.twl.c @@ -72,7 +72,18 @@ u32 cardNandRwStart = 0; static const u8 cardSeedBytes[] = { 0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5 }; -static u32 getRandomNumber(void) { return rand(); } +static u32 getRandomNumber() { return 0xDDAC86F5; } + +void ResetSlot() { + // Reset card slot + if((REG_SCFG_MC != 0x11) && (REG_SCFG_MC != 0x10)) { + disableSlot1(); + for(int i = 0; i < 15; i++)swiWaitForVBlank(); + } + enableSlot1(); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); + for(int i = 0; i < 15; i++)swiWaitForVBlank(); +} //--------------------------------------------------------------------------------- // https://github.com/devkitPro/libnds/blob/105d4943dbac8f2bd99a47b22cd3ed48f96af083/source/common/card.c#L47-L62 @@ -302,8 +313,7 @@ static void switchToTwlBlowfish(sNDSHeaderExt* ndsHeader) { twlBlowfish = true; } - -int cardInit (sNDSHeaderExt* ndsHeader) { +u32 cardInit (sNDSHeaderExt* ndsHeader) { u32 portFlagsKey1, portFlagsSecRead; normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000 nandChip = false; @@ -316,41 +326,37 @@ int cardInit (sNDSHeaderExt* ndsHeader) { twlBlowfish = false; sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart + if (isDSiMode()) { - // Reset card slot - if(REG_SCFG_MC != 0x11){ - disableSlot1(); - for(i = 0; i < 15; i++)swiWaitForVBlank(); - } - enableSlot1(); - for(i = 0; i < 10; i++)swiWaitForVBlank(); + ResetSlot(); // Dummy command sent after card reset cardParamCommand (CARD_CMD_DUMMY, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), NULL, 0); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); } - REG_ROMCTRL=0; - REG_AUXSPICNT=0; + /*REG_ROMCTRL = 0; + REG_AUXSPICNT = 0; //ioDelay2(167550); for(i = 0; i < 25; i++) { swiWaitForVBlank(); } - REG_AUXSPICNT=CARD_CR1_ENABLE|CARD_CR1_IRQ; - REG_ROMCTRL=CARD_nRESET|CARD_SEC_SEED; - while(REG_ROMCTRL&CARD_BUSY) ; + REG_AUXSPICNT = (CARD_CR1_ENABLE | CARD_CR1_IRQ); + REG_ROMCTRL = (CARD_nRESET | CARD_SEC_SEED); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); cardReset(); - while(REG_ROMCTRL&CARD_BUSY) ; + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank();*/ toncset(headerData, 0, 0x1000); // Read the header - cardParamCommand (CARD_CMD_HEADER_READ, 0, - CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), - (void*)headerData, 0x200/sizeof(u32)); - while(REG_ROMCTRL&CARD_BUSY); + cardReadHeader((u8*)headerData); + /*cardParamCommand (CARD_CMD_HEADER_READ, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), (void*)headerData, 0x200/sizeof(u32)); + while(REG_ROMCTRL & CARD_BUSY);*/ tonccpy(ndsHeader, headerData, 0x200); - iCardId=cardReadID(CARD_CLK_SLOW); - while(REG_ROMCTRL & CARD_BUSY); - + iCardId = cardReadID(CARD_CLK_SLOW); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); + *(u32*)InitialCartChipID = iCardId; + normalChip = (iCardId & BIT(31)) != 0; // ROM chip ID MSB nandChip = (iCardId & BIT(27)) != 0; // Card has a NAND chip @@ -377,35 +383,23 @@ int cardInit (sNDSHeaderExt* ndsHeader) { } // Check header CRC - if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E)) { - return ERR_HEAD_CRC; - } - - /* - // Check logo CRC - if (ndsHeader->logoCRC16 != 0xCF56) { - return ERR_LOGO_CRC; - } - */ + if (ndsHeader->headerCRC16 != swiCRC16(0xFFFF, (void*)ndsHeader, 0x15E))return ERR_HEAD_CRC; // Initialise blowfish encryption for KEY1 commands and decrypting the secure area gameCode = (GameCode*)ndsHeader->gameCode; init_keycode (gameCode->key, 2, 8, 0); // Port 40001A4h setting for normal reads (command B7) - portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7); + portFlags = (ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7)); // Port 40001A4h setting for KEY1 commands (usually 001808F8h) - portFlagsKey1 = CARD_ACTIVATE | CARD_nRESET | (ndsHeader->cardControl13 & (CARD_WR|CARD_CLK_SLOW)) | - ((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16)); + portFlagsKey1 = (CARD_ACTIVATE | CARD_nRESET | (ndsHeader->cardControl13 & (CARD_WR|CARD_CLK_SLOW)) | ((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16))); // Adjust card transfer method depending on the most significant bit of the chip ID - if (!normalChip) { - portFlagsKey1 |= CARD_SEC_LARGE; - } + if (!normalChip)portFlagsKey1 |= CARD_SEC_LARGE; // 3Ciiijjj xkkkkkxx - Activate KEY1 Encryption Mode initKey1Encryption (cmdData, 0); - cardPolledTransfer((ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE, NULL, 0, cmdData); + cardPolledTransfer (((ndsHeader->cardControl13 & (CARD_WR|CARD_nRESET|CARD_CLK_SLOW)) | CARD_ACTIVATE), NULL, 0, cmdData); // 4llllmmm nnnkkkkk - Activate KEY2 Encryption Mode createEncryptedCommand (CARD_CMD_ACTIVATE_SEC, cmdData, 0); @@ -418,11 +412,11 @@ int cardInit (sNDSHeaderExt* ndsHeader) { // Set the KEY2 encryption registers REG_ROMCTRL = 0; - REG_CARD_1B0 = cardSeedBytes[ndsHeader->deviceType & 0x07] | (key1data.nnn << 15) | (key1data.mmm << 27) | 0x6000; - REG_CARD_1B4 = 0x879b9b05; - REG_CARD_1B8 = key1data.mmm >> 5; + REG_CARD_1B0 = (cardSeedBytes[ndsHeader->deviceType & 0x07] | (key1data.nnn << 15) | (key1data.mmm << 27) | 0x6000); + REG_CARD_1B4 = 0x879B9B05; + REG_CARD_1B8 = (key1data.mmm >> 5); REG_CARD_1BA = 0x5c; - REG_ROMCTRL = CARD_nRESET | CARD_SEC_SEED | CARD_SEC_EN | CARD_SEC_DAT; + REG_ROMCTRL = (CARD_nRESET | CARD_SEC_SEED | CARD_SEC_EN | CARD_SEC_DAT); // Update the DS card flags to suit KEY2 encryption portFlagsKey1 |= CARD_SEC_EN | CARD_SEC_DAT; @@ -434,11 +428,10 @@ int cardInit (sNDSHeaderExt* ndsHeader) { cardPolledTransfer(portFlagsKey1, NULL, 0, cmdData); cardDelay(ndsHeader->readTimeout); } - cardPolledTransfer(portFlagsKey1 | CARD_BLK_SIZE(7), NULL, 0, cmdData); + cardPolledTransfer((portFlagsKey1 | CARD_BLK_SIZE(7)), NULL, 0, cmdData); // 2bbbbiii jjjkkkkk - Get Secure Area Block - portFlagsSecRead = (ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF)|CARD_DELAY2(0x3F))) - | CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT; + portFlagsSecRead = ((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF)|CARD_DELAY2(0x3F))) | CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT); int secureAreaOffset = 0; for (secureBlockNumber = 4; secureBlockNumber < 8; secureBlockNumber++) { @@ -448,11 +441,11 @@ int cardInit (sNDSHeaderExt* ndsHeader) { cardPolledTransfer(portFlagsSecRead, NULL, 0, cmdData); cardDelay(ndsHeader->readTimeout); for (i = 8; i > 0; i--) { - cardPolledTransfer(portFlagsSecRead | CARD_BLK_SIZE(1), secureArea + secureAreaOffset, 0x200, cmdData); + cardPolledTransfer((portFlagsSecRead | CARD_BLK_SIZE(1)), (secureArea + secureAreaOffset), 0x200, cmdData); secureAreaOffset += 0x200/sizeof(u32); } } else { - cardPolledTransfer(portFlagsSecRead | CARD_BLK_SIZE(4) | CARD_SEC_LARGE, secureArea + secureAreaOffset, 0x1000, cmdData); + cardPolledTransfer((portFlagsSecRead | CARD_BLK_SIZE(4) | CARD_SEC_LARGE), (secureArea + secureAreaOffset), 0x1000, cmdData); secureAreaOffset += 0x1000/sizeof(u32); } } @@ -469,12 +462,10 @@ int cardInit (sNDSHeaderExt* ndsHeader) { //CycloDS doesn't like the dsi secure area being decrypted if((ndsHeader->arm9romOffset != 0x4000) || secureArea[0] || secureArea[1])decryptSecureArea (gameCode->key, secureArea, 0); - if (secureArea[0] == 0x72636e65 /*'encr'*/ && secureArea[1] == 0x6a624f79 /*'yObj'*/) { + if (secureArea[0] == 0x72636E65 /*'encr'*/ && secureArea[1] == 0x6A624F79 /*'yObj'*/) { // Secure area exists, so just clear the tag - secureArea[0] = 0xe7ffdeff; - secureArea[1] = 0xe7ffdeff; - } else { - //return normalChip ? ERR_SEC_NORM : ERR_SEC_OTHR; + secureArea[0] = 0xE7FFDEFF; + secureArea[1] = 0xE7FFDEFF; } // Set NAND card section location variables @@ -496,7 +487,7 @@ int cardInit (sNDSHeaderExt* ndsHeader) { // If booted from DSi System Menu short cart init with no card reads or pokes to rom ctrl registers can be done. // System Menu is nice enough to do this for you. :P // (also is the case for booting from DS Download Play. ;) ) -int cardInitShort(sNDSHeaderExt* ndsHeader) { +u32 cardInitShort(sNDSHeaderExt* ndsHeader) { normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000 nandChip = false; nandSection = -1; @@ -515,8 +506,8 @@ int cardInitShort(sNDSHeaderExt* ndsHeader) { iCardId = *(u32*)CartChipIDCopy; // This location contains cart's chipID when booting DSiWare that has Slot-1 access. // iCardId = *(u32*)0x027FF800; // This location contains cart's chipID for DS Download Play users - normalChip = (iCardId & BIT(31)) != 0; // ROM chip ID MSB - nandChip = (iCardId & BIT(27)) != 0; // Card has a NAND chip + normalChip = ((iCardId & BIT(31)) != 0); // ROM chip ID MSB + nandChip = ((iCardId & BIT(27)) != 0); // Card has a NAND chip tonccpy(ndsHeader, headerData, 0x200); @@ -528,20 +519,20 @@ int cardInitShort(sNDSHeaderExt* ndsHeader) { init_keycode (gameCode->key, 2, 8, 0); // Port 40001A4h setting for normal reads (command B7) - portFlags = ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7); + portFlags = (ndsHeader->cardControl13 & ~CARD_BLK_SIZE(7)); - if (secureArea[0] == 0x72636e65 /*'encr'*/ && secureArea[1] == 0x6a624f79 /*'yObj'*/) { + if (secureArea[0] == 0x72636E65 /*'encr'*/ && secureArea[1] == 0x6A624F79 /*'yObj'*/) { // Secure area exists, so just clear the tag - secureArea[0] = 0xe7ffdeff; - secureArea[1] = 0xe7ffdeff; + secureArea[0] = 0xE7FFDEFF; + secureArea[1] = 0xE7FFDEFF; } // Set NAND card section location variables if (nandChip) { if(ndsHeader->nandRomEnd != 0) { // TWL cards (Face Training) multiply by 0x80000 instead of 0x20000 - cardNandRomEnd = ndsHeader->nandRomEnd * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000); - cardNandRwStart = ndsHeader->nandRwStart * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000); + cardNandRomEnd = (ndsHeader->nandRomEnd * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000)); + cardNandRwStart = (ndsHeader->nandRwStart * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000)); } else { // Jam with the Band (J) (大合奏!バンドブラザーズ) doesn't have the RW section in the header cardNandRomEnd = 0x7200000; diff --git a/arm9/common/tonccpy.itcm.c b/arm9/common/tonccpy.itcm.c new file mode 100644 index 0000000..09f80ec --- /dev/null +++ b/arm9/common/tonccpy.itcm.c @@ -0,0 +1,126 @@ +#include +#include "tonccpy.h" +//# tonccpy.c + +//! VRAM-safe cpy. +/*! This version mimics memcpy in functionality, with + the benefit of working for VRAM as well. It is also + slightly faster than the original memcpy, but faster + implementations can be made. + \param dst Destination pointer. + \param src Source pointer. + \param size Fill-length in bytes. + \note The pointers and size need not be word-aligned. +*/ +void tonccpy(void *dst, const void *src, uint size) { + + if(size==0 || dst==NULL || src==NULL) { return; } + + uint count; + u16 *dst16; // hword destination + u8 *src8; // byte source + + // Ideal case: copy by 4x words. Leaves tail for later. + if( ((u32)src|(u32)dst)%4==0 && size>=4) { + u32 *src32= (u32*)src, *dst32= (u32*)dst; + + count= size/4; + uint tmp= count&3; + count /= 4; + + // Duff's Device, good friend! + // Added fall through attribute to silance the compiler about this. ;) + switch(tmp) { + do { *dst32++ = *src32++; // fallthrough + case 3: *dst32++ = *src32++; // fallthrough + case 2: *dst32++ = *src32++; // fallthrough + case 1: *dst32++ = *src32++; // fallthrough + case 0: ;} while(count--); // fallthrough + } + + // Check for tail + size &= 3; + if(size == 0) { return; } + src8= (u8*)src32; + dst16= (u16*)dst32; + } else { + // Unaligned. + uint dstOfs= (u32)dst&1; + src8= (u8*)src; + dst16= (u16*)(dst-dstOfs); + + // Head: 1 byte. + if(dstOfs != 0) { + *dst16= (*dst16 & 0xFF) | *src8++<<8; + dst16++; + if(--size==0) { return; } + } + } + + // Unaligned main: copy by 2x byte. + count= size/2; + while(count--) { + *dst16++ = src8[0] | src8[1]<<8; + src8 += 2; + } + + // Tail: 1 byte. + if(size&1) { *dst16= (*dst16 &~ 0xFF) | *src8; } +} +//# toncset.c + +//! VRAM-safe memset, internal routine. +/*! This version mimics memset in functionality, with + the benefit of working for VRAM as well. It is also + slightly faster than the original memset. + \param dst Destination pointer. + \param fill Word to fill with. + \param size Fill-length in bytes. + \note The \a dst pointer and \a size need not be + word-aligned. In the case of unaligned fills, \a fill + will be masked off to match the situation. +*/ +void __toncset(void *dst, u32 fill, uint size) { + if(size==0 || dst==NULL) { return; } + + uint left= (u32)dst&3; + u32 *dst32= (u32*)(dst-left); + u32 count, mask; + + // Unaligned head. + if(left != 0) { + // Adjust for very small stint. + if(left+size<4) { + mask= BIT_MASK(size*8)<<(left*8); + *dst32= (*dst32 &~ mask) | (fill & mask); + return; + } + + mask= BIT_MASK(left*8); + *dst32= (*dst32 & mask) | (fill&~mask); + dst32++; + size -= 4-left; + } + + // Main stint. + count= size/4; + uint tmp= count&3; + count /= 4; + + // Added fall through attribute to silance the compiler about this. ;) + switch(tmp) { + do { *dst32++ = fill; // fallthrough + case 3: *dst32++ = fill; // fallthrough + case 2: *dst32++ = fill; // fallthrough + case 1: *dst32++ = fill; // fallthrough + case 0: ;} while(count--); // fallthrough + } + + // Tail + size &= 3; + if(size) { + mask= BIT_MASK(size*8); + *dst32= (*dst32 &~ mask) | (fill & mask); + } +} + diff --git a/arm9/hbmenu/args.cpp b/arm9/hbmenu/args.twl.cpp similarity index 100% rename from arm9/hbmenu/args.cpp rename to arm9/hbmenu/args.twl.cpp diff --git a/arm9/hbmenu/file_browse.h b/arm9/hbmenu/file_browse.h index f091667..0533f02 100644 --- a/arm9/hbmenu/file_browse.h +++ b/arm9/hbmenu/file_browse.h @@ -25,12 +25,13 @@ #include #include #include "read_card.h" +#include "launcherData.h" extern bool cartInsertedOnBoot; extern sNDSHeaderExt ntrHeader; -std::string browseForFile (const std::vector& extensionList); +std::string browseForFile (const std::vector& extensionList, tLauncherSettings launchdata); #endif //FILE_BROWSE_H diff --git a/arm9/hbmenu/file_browse.cpp b/arm9/hbmenu/file_browse.twl.cpp similarity index 92% rename from arm9/hbmenu/file_browse.cpp rename to arm9/hbmenu/file_browse.twl.cpp index 59b5347..5197b63 100644 --- a/arm9/hbmenu/file_browse.cpp +++ b/arm9/hbmenu/file_browse.twl.cpp @@ -35,6 +35,8 @@ #include "iconTitle.h" #include "read_card.h" #include "audio.h" +#include "tonccpy.h" +#include "launcherData.h" #define SCREEN_COLS 30 #define ENTRIES_PER_SCREEN 20 @@ -60,18 +62,24 @@ extern bool cartSelected; bool cartInsertedOnBoot = false; -static void cartCheck() { +static void cartCheck(tLauncherSettings launchdata) { switch (REG_SCFG_MC) { case 0x10: { if (!cardInserted)cardInserted = true; initialLoad = false; - if (cartInsertedOnBoot)cartInsertedOnBoot = false; + if (cartInsertedOnBoot) { + cartInsertedOnBoot = false; + launchdata.cachedChipID = 0x00000000; + } }break; case 0x11: { cardInserted = false; initialLoad = false; if (cartSelected)cartSelected = false; - if (cartInsertedOnBoot)cartInsertedOnBoot = false; + if (cartInsertedOnBoot) { + cartInsertedOnBoot = false; + launchdata.cachedChipID = 0x00000000; + } }break; case 0x18: { cardInserted = true; @@ -85,10 +93,12 @@ static void cartCheck() { } if (cartInsertedOnBoot)cartInsertedOnBoot = false; if (cartSelected)cartSelected = false; + launchdata.cachedChipID = 0x00000000; ToggleBackground(true); return; } if (cardInserted && initialLoad) { + launchdata.cachedChipID = *(u32*)0x02FFFC00; cardInitShort(&ntrHeader); cartIconUpdate(0, initialLoad); initialLoad = false; @@ -97,7 +107,9 @@ static void cartCheck() { ToggleBackground(false); } else if (cardInserted && !cardLoaded){ cardInit(&ntrHeader); - for (int i = 0; i < 25; i++)swiWaitForVBlank(); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); + launchdata.cachedChipID = cardGetId(); + for (int i = 0; i < 30; i++)swiWaitForVBlank(); cartIconUpdate(ntrHeader.bannerOffset, false); ToggleBackground(false); cardLoaded = true; @@ -202,7 +214,7 @@ void showDirectoryContents (const vector& dirContents, int startRow) { } } -string browseForFile (const vector& extensionList) { +string browseForFile (const vector& extensionList, tLauncherSettings launchdata) { int pressed = 0; int screenOffset = 0; int fileOffset = 0; @@ -223,7 +235,7 @@ string browseForFile (const vector& extensionList) { // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do do { - cartCheck(); + cartCheck(launchdata); scanKeys(); pressed = keysDownRepeat(); swiWaitForVBlank(); diff --git a/arm9/hbmenu/hbmenu.cpp b/arm9/hbmenu/hbmenu.twl.cpp similarity index 64% rename from arm9/hbmenu/hbmenu.cpp rename to arm9/hbmenu/hbmenu.twl.cpp index bf67e6a..f4e2bd4 100644 --- a/arm9/hbmenu/hbmenu.cpp +++ b/arm9/hbmenu/hbmenu.twl.cpp @@ -42,53 +42,61 @@ using namespace std; -// ALIGN(4) sNDSHeaderExt NTRHeader; - static char gameTitle[13] = {0}; -// static char gameCode[5] = {0}; - -static u8* fileBuffer; - -static const char* NitroSourceFileList[9] = { - "nitro:/NTR_Launcher/Acekard2i.nds", - "nitro:/NTR_Launcher/ActionReplayDS.nds", - "nitro:/NTR_Launcher/DSTwo.nds", - "nitro:/NTR_Launcher/EZFlashV.nds", - "nitro:/NTR_Launcher/R4DS_Ultra.nds", - "nitro:/NTR_Launcher/R4i_SDHC_AVRJ.nds", - "nitro:/NTR_Launcher/R4iGold_Launcher.nds", - "nitro:/NTR_Launcher/R4iSDHC_Demon.nds", - "nitro:/NTR_Launcher/TTDS.nds" +static ALIGN(4) u8* fileBuffer; + +static const int FileListSize = 13; +static const u32 FileMaxSize = 0x500000; + +static const char* NitroSourceFileList[FileListSize] = { + "nitro:/NTR_Launcher/Acekard2i.nds", // 0 + "nitro:/NTR_Launcher/ActionReplayDS.nds", // 1 + "nitro:/NTR_Launcher/CycloDS.nds", // 2 + "nitro:/NTR_Launcher/DSONEi.nds", // 3 + "nitro:/NTR_Launcher/DSTwo.nds", // 4 + "nitro:/NTR_Launcher/EZFlashV.nds", // 5 + "nitro:/NTR_Launcher/NCARD.nds", // 6 + "nitro:/NTR_Launcher/R4DS.nds", // 7 + "nitro:/NTR_Launcher/R4DS_Ultra.nds", // 8 + "nitro:/NTR_Launcher/R4i_SDHC_AVRJ.nds", // 9 + "nitro:/NTR_Launcher/R4iGold_Launcher.nds", // 10 + "nitro:/NTR_Launcher/R4iSDHC_Demon.nds", // 11 + "nitro:/NTR_Launcher/TTDS.nds" // 12 }; -static const char* NitroDestFileList[9] = { - "sd:/NTR_Launcher/Acekard2i.nds", - "sd:/NTR_Launcher/ActionReplayDS.nds", - "sd:/NTR_Launcher/DSTwo.nds", - "sd:/NTR_Launcher/EZFlashV.nds", - "sd:/NTR_Launcher/R4DS_Ultra.nds", - "sd:/NTR_Launcher/R4i_SDHC_AVRJ.nds", - "sd:/NTR_Launcher/R4iGold_Launcher.nds", - "sd:/NTR_Launcher/R4iSDHC_Demon.nds", - "sd:/NTR_Launcher/TTDS.nds" +static const char* NitroDestFileList[FileListSize] = { + "sd:/NTR_Launcher/Acekard2i.nds", // 0 + "sd:/NTR_Launcher/ActionReplayDS.nds", // 1 + "sd:/NTR_Launcher/CycloDS.nds", // 2 + "sd:/NTR_Launcher/DSONEi.nds", // 3 + "sd:/NTR_Launcher/DSTwo.nds", // 4 + "sd:/NTR_Launcher/EZFlashV.nds", // 5 + "sd:/NTR_Launcher/NCARD.nds", // 6 + "sd:/NTR_Launcher/R4DS.nds", // 7 + "sd:/NTR_Launcher/R4DS_Ultra.nds", // 8 + "sd:/NTR_Launcher/R4i_SDHC_AVRJ.nds", // 9 + "sd:/NTR_Launcher/R4iGold_Launcher.nds", // 10 + "sd:/NTR_Launcher/R4iSDHC_Demon.nds", // 11 + "sd:/NTR_Launcher/TTDS.nds" // 12 }; static void DoWait(int waitTime = 30) { for (int i = 0; i < waitTime; i++)swiWaitForVBlank(); }; static void DoCardInit() { + sysSetCardOwner(BUS_OWNER_ARM9); switch (REG_SCFG_MC) { case 0x10: { enableSlot1(); DoWait(15); }break; case 0x11: { enableSlot1(); DoWait(15); }break; } // Do cart init stuff to wake cart up. DLDI init may fail otherwise! - CardReset(true); - cardReadHeader((u8*)&ntrHeader); + cardInit(&ntrHeader); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); + // CardReset(true); + // cardReadHeader((u8*)&ntrHeader); CardReset(false); - // cardInit(&ntrHeader); tonccpy(gameTitle, ntrHeader.gameTitle, 12); - // tonccpy(gameCode, ntrHeader.gameCode, 4); - // DoWait(25); + while(REG_ROMCTRL & CARD_BUSY)swiWaitForVBlank(); } @@ -121,30 +129,28 @@ static int stop(void) { } static void CheckFolder() { - // if (sizeof(NitroSourceFileList) != sizeof(NitroDestFileList))return; bool copyNeeded = false; - int listSize = 8; - for (int i = 0; i < listSize; i++) { + int i = 0; + for (i = 0; i < FileListSize; i++) { if (access(NitroDestFileList[i], F_OK) != 0) { copyNeeded = true; break; } } if (!copyNeeded)return; + printf("\n\n\n\n\n\n\n\n\n Setting up Stage2 folder"); printf("\n\n Please Wait...\n"); - u32 BufferSize = 0x40000; - fileBuffer = (u8*)malloc(BufferSize); - for (int i = 0; i < listSize; i++) { + fileBuffer = (u8*)malloc(FileMaxSize); + for (i = 0; i < FileListSize; i++) { if (access(NitroDestFileList[i], F_OK) != 0) { FILE *src = fopen(NitroSourceFileList[i], "rb"); if (src) { fseek(src, 0, SEEK_END); u32 fSize = ftell(src); - // toncset((u8*)fileBuffer, 0xFF, fSize); fseek(src, 0, SEEK_SET); - if (fSize <= BufferSize) { + if (fSize <= FileMaxSize) { FILE *dest = fopen(NitroDestFileList[i], "wb"); if (dest) { fread((u8*)fileBuffer, 1, fSize, src); @@ -164,7 +170,7 @@ static int BrowserUI(tLauncherSettings launchdata) { vector extensionList = argsGetExtensionList(); chdir("sd:/NTR_Launcher"); while(1) { - string filename = browseForFile(extensionList); + string filename = browseForFile(extensionList, launchdata); if (cartSelected)break; // Construct a command line vector argarray; @@ -176,6 +182,16 @@ static int BrowserUI(tLauncherSettings launchdata) { vector c_args; for (const auto& arg: argarray) { c_args.push_back(arg.c_str()); } // Try to run the NDS file with the given arguments + if (access(c_args[0], F_OK) == 0) { + FILE *src = fopen(c_args[0], "rb"); + if (src) { + fread((u8*)0x02FFE000, 1, 0x200, src); + if (((tNDSHeader*)0x02FFE000)->unitCode & BIT(1))launchdata.isTWLSRL = 0xFF; + fclose(src); + } + } + // tonccpy((void*)0x02FFDFF0, (void*)LAUNCH_DATA, 0x10); + launchdata.cachedChipID = *(u32*)InitialCartChipID; int err = runNdsFile(c_args[0], c_args.size(), &c_args[0], launchdata); iprintf("Start failed. Error %i\n", err); break; @@ -185,17 +201,22 @@ static int BrowserUI(tLauncherSettings launchdata) { if (cartSelected) { // DS-Xtreme does not like running in TWL clock speeds. // (write function likely goes too fast and semi-bricks hidden sector region randomly when using official launcher) - if (!memcmp(gameTitle, "D!S!XTREME", 9)) { + if (!memcmp(gameTitle, "D!S!XTREME", 10)) { launchdata.scfgUnlock = 0x00; launchdata.twlMode = 0x00; launchdata.twlCLK = 0x00; launchdata.twlVRAM = 0x00; - cardInit(&ntrHeader); + launchdata.twlRAM = 0x00; + launchdata.isTWLSRL = 0x00; + // cardInit(&ntrHeader); + } + if (cartInsertedOnBoot) { + DoCardInit(); // Currently required for bootlaoder to succeed with card init. } else { - if (cartInsertedOnBoot)DoCardInit(); // Currently required for bootlaoder to succeed with card init. - // Give launch soundfx time to finish if card Init already occured. - if (!cartInsertedOnBoot)DoWait(29); + DoWait(29); // Give launch soundfx time to finish if card Init already occured. } + if (ntrHeader.unitCode & BIT(1))launchdata.isTWLSRL = 0x01; + launchdata.cachedChipID = *(u32*)InitialCartChipID; runLaunchEngine(launchdata); } return stop(); diff --git a/arm9/hbmenu/iconTitle.cpp b/arm9/hbmenu/iconTitle.twl.cpp similarity index 86% rename from arm9/hbmenu/iconTitle.cpp rename to arm9/hbmenu/iconTitle.twl.cpp index b24c11c..4420d87 100644 --- a/arm9/hbmenu/iconTitle.cpp +++ b/arm9/hbmenu/iconTitle.twl.cpp @@ -31,6 +31,7 @@ #include "hbmenu_banner_cartSelected.h" #include "hbmenu_banner_noCart.h" #include "font6x8.h" +#include "tonccpy.h" #include "read_card.h" #include "launcherData.h" @@ -45,19 +46,29 @@ #define ICON2_POS_X 26 #define ICON2_POS_Y 132 - #define TEXT_WIDTH ((22-4)*8/6) +typedef struct sNDSBannerTest { + u16 version; //!< version of the banner. + u16 crc; //!< 16 bit crc/checksum of the banner. + u8 reserved[28]; + u8 iconData[2080]; +} tNDSBannerTest; + + static int bg2, bg3; static u16 *sprite; static u16 *sprite2; extern tNDSBanner dsCardDefault_bin; +extern tNDSBanner dsCardInvalid_bin; extern tNDSBanner hbNoIcon_bin; static tNDSBanner banner; static tNDSBanner* cartBanner; +static u32 CartBannerBuffer[2304]; + bool cartSelected = false; static inline void writecharRS (int row, int col, u16 car) { @@ -115,6 +126,10 @@ void clearCartIcon(bool clearBannerText) { dmaFillHalfWords(0, sprite2, sizeof(banner.icon)); } +static bool checkBannerCRC(u8* banner) { + return (((tNDSBannerTest*)banner)->crc == swiCRC16(0xFFFF, ((tNDSBannerTest*)banner)->iconData, 0x820)); +} + void iconTitleInit (void) { // initialize video mode @@ -162,6 +177,8 @@ void iconTitleInit (void) { // oam can only be updated during vblank swiWaitForVBlank(); oamUpdate(&oamMain); + + toncset32(CartBannerBuffer, 0, 0x900); cartBanner = (tNDSBanner*)CartBannerBuffer; @@ -253,6 +270,18 @@ void iconTitleUpdate (int isdir, const std::string& name) { return; } + if (!checkBannerCRC((u8*)&banner)) { + // text + writeRow (2,"(invalid icon/title!)", false); + // icon + clearIcon(); + DC_FlushAll(); + dmaCopy(hbNoIcon_bin.icon, sprite, sizeof(hbNoIcon_bin.icon)); + dmaCopy(hbNoIcon_bin.palette, SPRITE_PALETTE, sizeof(hbNoIcon_bin.palette)); + fclose (fp); + return; + } + // close file! fclose (fp); @@ -287,28 +316,37 @@ void iconTitleUpdate (int isdir, const std::string& name) { void cartIconUpdate (u32 BannerOffset, bool readExistingBanner) { + toncset32(CartBannerBuffer, 0, 0x900); if(readExistingBanner) { - cardReadAlt(*(u32*)InitialCartBannerOffset, (u32*)CartBannerBuffer, 0x1000); + cardReadAlt(*(u32*)InitialCartBannerOffset, (u32*)CartBannerBuffer, 0x2400); } else { - cardReadAlt(BannerOffset, (u32*)CartBannerBuffer, 0x1000); + cardReadAlt(BannerOffset, (u32*)CartBannerBuffer, 0x2400); } switch (cartBanner->crc) { case 0x0000: { clearCartIcon(false); writeRow (1,"(invalid icon/title!)", true); DC_FlushAll(); - dmaCopy(dsCardDefault_bin.icon, sprite2, 512); - dmaCopy(dsCardDefault_bin.palette, (u16*)((u32)SPRITE_PALETTE + 0x20), 0x20); + dmaCopy(dsCardInvalid_bin.icon, sprite2, 512); + dmaCopy(dsCardInvalid_bin.palette, (u16*)((u32)SPRITE_PALETTE + 0x20), 0x20); return; - }break; + } break; case 0xFFFF: { clearCartIcon(false); - dmaCopy(dsCardDefault_bin.icon, sprite2, 512); - dmaCopy(dsCardDefault_bin.palette, (u16*)((u32)SPRITE_PALETTE + 0x20), 0x20); + dmaCopy(dsCardInvalid_bin.icon, sprite2, 512); + dmaCopy(dsCardInvalid_bin.palette, (u16*)((u32)SPRITE_PALETTE + 0x20), 0x20); writeRow (1,"(invalid icon/title!)", true); return; }break; default: { + if (!checkBannerCRC((u8*)cartBanner)) { + clearCartIcon(false); + dmaCopy(dsCardInvalid_bin.icon, sprite2, 512); + dmaCopy(dsCardInvalid_bin.palette, (u16*)((u32)SPRITE_PALETTE + 0x20), 0x20); + writeRow (1,"(invalid icon/title!)", true); + return; + } + clearCartIcon(false); // turn unicode into ascii (kind of) // and convert 0x0A into 0x00 @@ -326,11 +364,7 @@ void cartIconUpdate (u32 BannerOffset, bool readExistingBanner) { // Recenter text to center row if less then 2 rows of text. // Default offset 0 instead of 1 since no NDS file name for this to account for. - if (lineReturns < 2 && lineReturns != 1) { - rowOffset = 1; - } else if (lineReturns == 1) { - rowOffset = 0; - } + if (lineReturns < 2 && lineReturns != 1) { rowOffset = 1; } else if (lineReturns == 1) { rowOffset = 0; } // text for (size_t i = 0; i < 3; ++i) { diff --git a/arm9/hbmenu/nds_loader_arm9.c b/arm9/hbmenu/nds_loader_arm9.c index 0fb6b7c..664cab3 100644 --- a/arm9/hbmenu/nds_loader_arm9.c +++ b/arm9/hbmenu/nds_loader_arm9.c @@ -214,9 +214,8 @@ static bool dldiPatchLoader (data_t *binData, u32 binSize, bool clearBSS) { return true; }*/ -ITCM_CODE static void SetSCFG(u8 scfgUnlock, u8 twlMode, u8 twlCLK, u8 twlVRAM) { - - if (twlMode > 0) { +ITCM_CODE static void SetSCFG(tLauncherSettings launchData) { + /*if (launchData->twlMode > 0) { *((vu32*)REG_MBK1)=0x8D898581; *((vu32*)REG_MBK2)=0x8C888480; *((vu32*)REG_MBK3)=0x9C989490; @@ -225,7 +224,7 @@ ITCM_CODE static void SetSCFG(u8 scfgUnlock, u8 twlMode, u8 twlCLK, u8 twlVRAM) REG_MBK6=0x00000000; REG_MBK7=0x07C03740; REG_MBK8=0x07403700; - } else { + } else if ((launchData->twlMode == 0) && (launchData->twlRAM == 0)) { // MBK settings for NTR mode games *((vu32*)REG_MBK1)=0x8D898581; *((vu32*)REG_MBK2)=0x91898581; @@ -235,18 +234,47 @@ ITCM_CODE static void SetSCFG(u8 scfgUnlock, u8 twlMode, u8 twlCLK, u8 twlVRAM) REG_MBK6 = 0x00003000; REG_MBK7 = 0x00003000; REG_MBK8 = 0x00003000; - } + }*/ + + if (launchData.twlCLK == 0) { REG_SCFG_CLK = 0x80; } else { REG_SCFG_CLK = 0x87; }; - if (twlCLK == 0)REG_SCFG_CLK = 0x80; - if (twlMode > 0) { - REG_SCFG_EXT = 0x82073100; + if (launchData.twlMode > 0) { + REG_SCFG_EXT = 0x8307F100; REG_SCFG_RST = 1; } else { - REG_SCFG_EXT=0x83002000; + REG_SCFG_EXT = 0x8300E000; } - if (twlVRAM == 0)REG_SCFG_EXT &= ~(1UL << 13); - if (scfgUnlock == 0x00)REG_SCFG_EXT &= ~(1UL << 31); - for (int i = 0; i < 10; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); } + + if (launchData.twlVRAM == 0)REG_SCFG_EXT &= ~(1UL << 13); + if (launchData.twlRAM == 0) { + REG_SCFG_EXT &= ~(1UL << 14); + REG_SCFG_EXT &= ~(1UL << 15); + } + + if ((launchData.isTWLSRL > 0) && (launchData.twlRAM > 0) && (launchData.twlMode > 0)) { + *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; + *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; + *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; + *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; + *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; + REG_MBK6 = *(u32*)0x02FFE194; + REG_MBK7 = *(u32*)0x02FFE198; + REG_MBK8 = *(u32*)0x02FFE19C; + REG_MBK9 = *(u32*)0x02FFE1AC; + WRAM_CR = *(u8*)0x02FFE1AF; + } else { + *((vu32*)REG_MBK1)=0x8D898581; + *((vu32*)REG_MBK2)=0x91898581; + *((vu32*)REG_MBK3)=0x91999591; + *((vu32*)REG_MBK4)=0x91898581; + *((vu32*)REG_MBK5)=0x91999591; + REG_MBK6 = 0x00003000; + REG_MBK7 = 0x00003000; + REG_MBK8 = 0x00003000; + if (launchData.scfgUnlock == 0)REG_SCFG_EXT &= ~(1UL << 31); + } + + for (int i = 0; i < 10; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); } } @@ -256,28 +284,18 @@ eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool ini u16 argTempVal = 0; int argSize; const char* argChar; - - bool scfgUnlock = false; - bool twlMode = false; - bool twlCLK = false; - bool twlVRAM = false; - if (launchdata.scfgUnlock > 0x00)scfgUnlock = true; - if (launchdata.twlMode > 0x00)twlMode = true; - if (launchdata.twlCLK > 0x00)twlCLK = true; - if (launchdata.twlVRAM > 0x00)twlVRAM = true; - - if (!twlMode) { + if (launchdata.twlMode == 0) { fifoSendValue32(FIFO_USER_01, 1); fifoWaitValue32(FIFO_USER_02); } irqDisable(IRQ_ALL); - SetSCFG(scfgUnlock, twlMode, twlCLK, twlVRAM); + SetSCFG(launchdata); // Direct CPU access to VRAM bank C - VRAM_D_CR = VRAM_ENABLE | VRAM_D_LCD; + VRAM_D_CR = (VRAM_ENABLE | VRAM_D_LCD); // Load the loader/patcher into the correct address vramcpy (LCDC_BANK_D, loader, loaderSize); @@ -332,9 +350,9 @@ eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool ini *(tLauncherSettings*)LAUNCH_DATA = launchdata; // Give the VRAM to the ARM7 - VRAM_D_CR = VRAM_ENABLE | VRAM_D_ARM7_0x06020000; + VRAM_D_CR = (VRAM_ENABLE | VRAM_D_ARM7_0x06020000); // Reset into a passme loop - REG_EXMEMCNT |= ARM7_OWNS_ROM | ARM7_OWNS_CARD; + REG_EXMEMCNT |= (ARM7_OWNS_ROM | ARM7_OWNS_CARD); *((vu32*)0x02FFFFFC) = 0; *((vu32*)0x02FFFE04) = (u32)0xE59FF018; *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; diff --git a/arm9/include/dsCardInvalid.bin b/arm9/include/dsCardInvalid.bin new file mode 100644 index 0000000000000000000000000000000000000000..db63766f36c568f3d86a89a5ebc1c79c976c953e GIT binary patch literal 2112 zcmeHHIS#@w5FDOhBvONEKok`Ig1^865Ktf?Xd>EtkS8I*tnD?ih|(jx(RkyXy?i7S zJU182>#{+J(ZPD_*rTv5j*2CdH SZ>{UE=U?xC{r>;;|Mv# 0) { + // if (twlRAM > 0) { REG_SCFG_EXT = 0x82076100; } else { REG_SCFG_EXT = 0x82073100; } + // if (twlRAM > 0) { REG_SCFG_EXT = 0x8207611F; } else { REG_SCFG_EXT = 0x8207311F; } + // if (twlRAM > 0) { REG_SCFG_EXT = 0x8207711F; } else { REG_SCFG_EXT = 0x8207311F; } + // REG_SCFG_EXT = 0x8207711F; + REG_SCFG_EXT = 0x8307F100; + REG_SCFG_RST = 1; + } else { + // if (twlRAM == 0) { REG_SCFG_EXT = 0x83002000; } else { REG_SCFG_EXT = 0x83006000; } + // REG_SCFG_EXT = 0x83006000; + REG_SCFG_EXT = 0x8300E000; + } + if (twlVRAM == 0)REG_SCFG_EXT &= ~(1UL << 13); + if (twlRAM == 0)REG_SCFG_EXT &= ~(1UL << 14); + if (twlRAM == 0)REG_SCFG_EXT &= ~(1UL << 15); for(int i = 0; i < 8; i++) { while(REG_VCOUNT!=191); while(REG_VCOUNT==191); } } void runLaunchEngine (tLauncherSettings launchdata) { // Always init console so bootloader's new console can display error codes if needed. - if (!launchdata.debugMode || !ConsoleInit) { InitConsole(); } else { consoleClear(); } - + if (!launchdata.debugMode || !ConsoleInit) { InitConsole(); } else { consoleClear(); } + irqDisable(IRQ_ALL); // Direct CPU access to VRAM bank D - VRAM_D_CR = VRAM_ENABLE | VRAM_D_LCD; - - // Clear VRAM - // memset (LCDC_BANK_D, 0x00, 128 * 1024); - + VRAM_D_CR = (VRAM_ENABLE | VRAM_D_LCD); + // Load the loader/patcher into the correct address vramcpy (LCDC_BANK_D, load_bin, load_bin_size); @@ -64,17 +75,17 @@ void runLaunchEngine (tLauncherSettings launchdata) { VRAM_D_CR = VRAM_ENABLE | VRAM_D_ARM7_0x06020000; // Reset into a passme loop - nocashMessage("Reset into a passme loop"); + // nocashMessage("Reset into a passme loop"); REG_EXMEMCNT |= ARM7_OWNS_ROM | ARM7_OWNS_CARD; *(tLauncherSettings*)LAUNCH_DATA = launchdata; - SETSCFG(); + SETSCFG(launchdata.twlMode, launchdata.twlVRAM, launchdata.twlRAM); // Return to passme loop - *(vu32*)0x027FFFFC = 0; - *(vu32*)0x027FFE04 = (u32)0xE59FF018; // ldr pc, 0x02FFFE24 - *(vu32*)0x027FFE24 = (u32)0x02FFFE04; // Set ARM9 Loop address --> resetARM9(0x027FFE04); + *(vu32*)0x02FFFFFC = 0; + *(vu32*)0x02FFFE04 = (u32)0xE59FF018; // ldr pc, 0x02FFFE24 + *(vu32*)0x02FFFE24 = (u32)0x02FFFE04; // Set ARM9 Loop address --> resetARM9(0x02FFFE04); // Reset ARM7 // nocashMessage("resetARM7"); resetARM7(0x06020000); diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp index 6434d8b..d6aef9d 100755 --- a/arm9/source/main.cpp +++ b/arm9/source/main.cpp @@ -49,7 +49,7 @@ static char gameTitle[13] = {0}; // static char gameCode[7] = {0}; static char gameCode[5] = {0}; static bool nitroFSMounted = false; -static ALIGN(4) sNDSHeaderExt ntrHeader; +// static ALIGN(4) sNDSHeaderExt ntrHeader; const char* PROGVERSION = "3.1"; @@ -75,7 +75,7 @@ void DisplayText(const char* text, bool clear = false, bool noText = false){ if (clear)consoleClear(); printf("--------------------------------\n"); printf("----[NTR Launcher Debug Mode]---\n"); - printf("----------[Version: 3.1]--------\n"); + printf("----------[Version: 3.2]--------\n"); printf("--------------------------------\n\n"); if (!noText)printf(text); } @@ -110,8 +110,8 @@ bool DoCardInit(bool DebugMode, bool fastBoot) { DisplayText("Loading Cart details.\nPlease Wait...\n", true); } switch (REG_SCFG_MC) { - case 0x10: { enableSlot1(); DoWait(10); FastBoot = false; }break; - case 0x11: { enableSlot1(); DoWait(10); FastBoot = false; }break; + case 0x10: { enableSlot1(); DoWait(15); FastBoot = false; }break; + case 0x11: { enableSlot1(); DoWait(15); FastBoot = false; }break; } // Do cart init stuff to wake cart up. DLDI init may fail otherwise! /*if (FastBoot) { @@ -121,10 +121,14 @@ bool DoCardInit(bool DebugMode, bool fastBoot) { } else {*/ CardReset(true); cardReadHeader((u8*)&ndsHeader); + while(REG_ROMCTRL & CARD_BUSY); CardReset(false); tonccpy(gameTitle, ndsHeader.header.gameTitle, 12); tonccpy(gameCode, ndsHeader.header.gameCode, 4); - // cardInit(&ntrHeader); + /*cardInit((sNDSHeaderExt*)&ndsHeader.header); + tonccpy(gameTitle, ndsHeader.header.gameTitle, 12); + tonccpy(gameCode, ndsHeader.header.gameCode, 4); + CardReset(false);*/ // } if (DebugMode) { DisplayText("CLR", true, true); @@ -170,8 +174,19 @@ int main() { defaultExceptionHandler(); sysSetCardOwner(BUS_OWNER_ARM9); + + if (!isDSiMode()) { + InitConsole(); + printf("--------------------------------\n"); + printf("----------[NTR Launcher]--------\n"); + printf("----------[Version: 3.2]--------\n"); + printf("--------------------------------\n\n"); + printf("\nError has occured.!\nDSi Mode not detected.\nDS/DS Lite Unsupported!"); + do { swiWaitForVBlank(); scanKeys(); } while (!keysDown()); + return 0; + } - tLauncherSettings LaunchData = { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }; + tLauncherSettings LaunchData = { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFFFFFF }; // Create backup of Cart header (if present) saved by DSi System Menu. // It will not survive memory reallocation after dropping to NTR ram spec during bootloader. @@ -181,6 +196,7 @@ int main() { bool scfgunlock = true; bool twlmode = false; bool twlclk = false; + bool twlram = false; bool twlvram = false; bool debugmode = false; // bool fastBoot = false; @@ -220,6 +236,7 @@ int main() { twlclk = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLCLOCK",0); twlvram = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLVRAM",0); + twlram = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLRAM",0); twlmode = ntrlauncher_config.GetInt("NTRLAUNCHER","TWLMODE",0); scfgunlock = ntrlauncher_config.GetInt("NTRLAUNCHER","SCFGUNLOCK",0); useAnimatedSplash = ntrlauncher_config.GetInt("NTRLAUNCHER","ANIMATEDSPLASH",0); @@ -236,10 +253,19 @@ int main() { scanKeys(); swiWaitForVBlank(); + /*scfgunlock = true; + twlmode = true; + twlclk = true; + twlram = true; + twlvram = true; + debugmode = true;*/ + + if (scfgunlock)LaunchData.scfgUnlock = 0x01; if (twlmode)LaunchData.twlMode = 0x01; if (twlclk)LaunchData.twlCLK = 0x01; if (twlvram)LaunchData.twlVRAM = 0x01; + if (twlram)LaunchData.twlRAM = 0x01; if (!autoBoot)stage2Menu = true; // if (fastBoot)LaunchData.fastBoot = 0x01; @@ -251,6 +277,8 @@ int main() { if ((REG_SCFG_MC == 0x10) && !stage2Menu) { sysSetCardOwner (BUS_OWNER_ARM9); DoCardInit(false, false); } + if (!fatInit)stage2Menu = false; + if (!stage2Menu) { if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) { debugmode = true; @@ -261,7 +289,6 @@ int main() { } } - if (!fatInit)stage2Menu = false; ConsoleInit = debugmode; if (stage2Menu) { ConsoleInit = false; debugmode = false; } @@ -307,7 +334,9 @@ int main() { LaunchData.twlVRAM = 0x00; LaunchData.twlMode = 0x00; if (!useAnimatedSplash)SimpleSplashInit(); - cardInit(&ntrHeader); + // cardInit(&ntrHeader); + } else { + if (ndsHeader.header.unitCode & BIT(1))LaunchData.isTWLSRL = 0x01; } while(1) { // If SCFG_MC is returning as zero/null, this means SCFG_EXT registers are locked on arm9 or user attempted to run this while in NTR mode. @@ -320,6 +349,7 @@ int main() { } break; } else { + LaunchData.cachedChipID = *(u32*)InitialCartChipID; runLaunchEngine(LaunchData); break; } diff --git a/ndsbootloader/source/bios.s b/ndsbootloader/source/bios.s deleted file mode 100644 index e98f57c..0000000 --- a/ndsbootloader/source/bios.s +++ /dev/null @@ -1,13 +0,0 @@ - .text - .align 4 - - .thumb - -@--------------------------------------------------------------------------------- - .global swiDelay - .thumb_func -@--------------------------------------------------------------------------------- -swiDelay: -@--------------------------------------------------------------------------------- - swi 0x03 - bx lr diff --git a/ndsbootloader/source/biosCalls.s b/ndsbootloader/source/biosCalls.s new file mode 100644 index 0000000..fb1bc13 --- /dev/null +++ b/ndsbootloader/source/biosCalls.s @@ -0,0 +1,233 @@ +/*--------------------------------------------------------------------------------- + + Copyright (C) 2005 + Michael Noland (joat) + Jason Rogers (dovoto) + Dave Murphy (WinterMute) + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source + distribution. + +---------------------------------------------------------------------------------*/ +#include + + .text + .align 4 + + .thumb + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDelay +@--------------------------------------------------------------------------------- + swi 0x03 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiSleep +@--------------------------------------------------------------------------------- + swi 0x07 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiChangeSoundBias +@--------------------------------------------------------------------------------- + swi 0x08 + bx lr + + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDivide +@--------------------------------------------------------------------------------- + swi 0x09 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiRemainder +@--------------------------------------------------------------------------------- + swi 0x09 + mov r0, r1 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDivMod +@--------------------------------------------------------------------------------- + push {r2, r3} + swi 0x09 + pop {r2, r3} + str r0, [r2] + str r1, [r3] + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiCopy +@--------------------------------------------------------------------------------- + swi 0x0B + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiFastCopy +@--------------------------------------------------------------------------------- + swi 0x0C + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiSqrt +@--------------------------------------------------------------------------------- + swi 0x0D + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiCRC16 +@--------------------------------------------------------------------------------- + swi 0x0E + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiIsDebugger +@--------------------------------------------------------------------------------- + swi 0x0F + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiUnpackBits +@--------------------------------------------------------------------------------- + swi 0x10 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecompressLZSSWram +@--------------------------------------------------------------------------------- + swi 0x11 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecompressLZSSVramNTR +@--------------------------------------------------------------------------------- + swi 0x12 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecompressLZSSVramTWL +@--------------------------------------------------------------------------------- + swi 0x02 + bx lr + + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecompressHuffman +@--------------------------------------------------------------------------------- + swi 0x13 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecompressRLEWram +@--------------------------------------------------------------------------------- + swi 0x14 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecompressRLEVram +@--------------------------------------------------------------------------------- + swi 0x15 + bx lr + +@--------------------------------------------------------------------------------- +@ ARM7 only bios calls +@--------------------------------------------------------------------------------- +#ifdef ARM7 + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiHalt +@--------------------------------------------------------------------------------- + swi 0x06 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiGetSineTable +@--------------------------------------------------------------------------------- + swi 0x1A + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiGetPitchTable +@--------------------------------------------------------------------------------- + swi 0x1B + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiGetVolumeTable +@--------------------------------------------------------------------------------- + swi 0x1C + bx lr + + +@ ARM7 function, but no real point in exposing it, at least not +@ without adding a way to get the 3 arguments back into C +@ .global swiGetFptrs +@ .thumb_func +@swiGetFptrs: +@ swi 0x1D +@ bx lr + + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiSwitchToGBAMode +@--------------------------------------------------------------------------------- + mov r0, #0x40 + swi 0x1F +@ does not return, of course + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiSetHaltCR +@--------------------------------------------------------------------------------- + mov r2, r0 + swi 0x1F + bx lr + +#endif // ARM7 + +@--------------------------------------------------------------------------------- +@ ARM9 only bios calls +@--------------------------------------------------------------------------------- +#ifdef ARM9 + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiWaitForIRQ +@--------------------------------------------------------------------------------- + swi 0x06 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecodeDelta8 +@--------------------------------------------------------------------------------- + swi 0x16 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiDecodeDelta16 +@--------------------------------------------------------------------------------- + swi 0x18 + bx lr + +@--------------------------------------------------------------------------------- +BEGIN_ASM_FUNC swiSetHaltCR +@--------------------------------------------------------------------------------- + swi 0x1F + bx lr + +#endif // ARM9 + diff --git a/ndsbootloader/source/boot.c b/ndsbootloader/source/boot.c index 52c4b15..32bcdc1 100644 --- a/ndsbootloader/source/boot.c +++ b/ndsbootloader/source/boot.c @@ -40,19 +40,29 @@ Helpful information: #include #include #include +#include +#include #include "fat.h" #include "card.h" #include "boot.h" #include "sdmmc.h" +#include "tonccpy.h" + +#include "../../arm9/common/launcherData.h" void arm7clearRAM(); //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Important things -#define TEMP_MEM 0x02FFD000 -#define TWL_HEAD 0x02FFE000 -#define NDS_HEAD 0x02FFFE00 +#define TEMP_MEM 0x02FFD000 +#define TEMP_LAUNCHMEM 0x02FFDFF0 +#define TWL_HEAD 0x02FFE000 +#define NDS_HEAD 0x02FFFE00 +#define NDS_HEAD_POKEMON 0x02FFF000 + #define TEMP_ARM9_START_ADDRESS (*(vu32*)0x02FFFFF4) +tTWLHeader* ntrHeader; +tTWLHeader* twlHeader; const char* bootName = "BOOT.NDS"; @@ -65,61 +75,39 @@ extern unsigned long argSize; extern unsigned long dsiSD; extern unsigned long dsiMode; -static bool scfgunlock = false; -static bool twlmode = false; -static bool twlclk = false; -static bool twlvram = false; +volatile u16 scfgunlock = 0; +volatile u16 twlmode = 0; +volatile u16 twlclk = 0; +volatile u16 twlvram = 0; +volatile u16 twlram = 0; +volatile u32 chipID = 0; + +static volatile u32 ROM_TID = 0; + +static void setTWLMBK() { + *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; + *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; + *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; + *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; + *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; + REG_MBK6 = *(u32*)0x02FFE1A0; + REG_MBK7 = *(u32*)0x02FFE1A4; + REG_MBK8 = *(u32*)0x02FFE1A8; + REG_MBK9 = *(u32*)0x02FFE1AC; +} -#define LAUNCH_DATA 0x020007F0 -typedef struct sLauncherSettings { - u8 language; - u8 scfgUnlock; - u8 twlMode; - u8 twlCLK; - u8 twlVRAM; - u8 debugMode; - u8 fastBoot; - u8 unused2; -} tLauncherSettings; +const char* getRomTid(const tNDSHeader* ndsHeader) { + static char romTid[5]; + strncpy(romTid, ndsHeader->gameCode, 4); + romTid[4] = '\0'; + return romTid; +} //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Firmware stuff - #define FW_READ 0x03 -void boot_readFirmware (uint32 address, uint8 * buffer, uint32 size) { - uint32 index; - - // Read command - while (REG_SPICNT & SPI_BUSY); - REG_SPICNT = SPI_ENABLE | SPI_CONTINUOUS | SPI_DEVICE_NVRAM; - REG_SPIDATA = FW_READ; - while (REG_SPICNT & SPI_BUSY); - - // Set the address - REG_SPIDATA = (address>>16) & 0xFF; - while (REG_SPICNT & SPI_BUSY); - REG_SPIDATA = (address>>8) & 0xFF; - while (REG_SPICNT & SPI_BUSY); - REG_SPIDATA = (address) & 0xFF; - while (REG_SPICNT & SPI_BUSY); - - for (index = 0; index < size; index++) { - REG_SPIDATA = 0; - while (REG_SPICNT & SPI_BUSY); - buffer[index] = REG_SPIDATA & 0xFF; - } - REG_SPICNT = 0; -} - -static inline void copyLoop (u32* dest, const u32* src, u32 size) { - size = (size +3) & ~3; - do { *dest++ = *src++; } while (size -= 4); -} - -//#define resetCpu() __asm volatile("\tswi 0x000000\n"); - void mpu_reset(); void mpu_reset_end(); @@ -173,17 +161,17 @@ void passArgs_ARM7 (void) { argDst = (u32*)((ARM9_DST + ARM9_LEN + 3) & ~3); // Word aligned - /* if (dsiMode && (*(u8*)(NDS_HEAD + 0x012) & BIT(1))) { + if ((twlmode > 0) && dsiMode && (*(u8*)(NDS_HEAD + 0x012) & BIT(1))) { u32 ARM9i_DST = *((u32*)(TWL_HEAD + 0x1C8)); u32 ARM9i_LEN = *((u32*)(TWL_HEAD + 0x1CC)); if (ARM9i_LEN) { u32* argDst2 = (u32*)((ARM9i_DST + ARM9i_LEN + 3) & ~3); // Word aligned - if (argDst2 > argDst) - argDst = argDst2; + if (argDst2 > argDst)argDst = argDst2; } - }*/ + } - copyLoop(argDst, argSrc, argSize); + // copyLoop(argDst, argSrc, argSize); + tonccpy(argDst, argSrc, argSize); __system_argv->argvMagic = ARGV_MAGIC; __system_argv->commandLine = (char*)argDst; @@ -197,10 +185,8 @@ Written by Darkain. Modified by Chishm: * Added STMIA clear mem loop --------------------------------------------------------------------------*/ -void resetMemory_ARM7 (void) { - int i; - u8 settings1, settings2; - u32 settingsOffset = 0; +void resetMemory_ARM7(void) { + int i, reg; REG_IME = 0; @@ -212,82 +198,281 @@ void resetMemory_ARM7 (void) { } REG_SOUNDCNT = 0; + REG_SNDCAP0CNT = 0; + REG_SNDCAP1CNT = 0; + + REG_SNDCAP0DAD = 0; + REG_SNDCAP0LEN = 0; + REG_SNDCAP1DAD = 0; + REG_SNDCAP1LEN = 0; - //clear out ARM7 DMA channels and timers + // Clear out ARM7 DMA channels and timers for (i=0; i<4; i++) { DMA_CR(i) = 0; DMA_SRC(i) = 0; DMA_DEST(i) = 0; TIMER_CR(i) = 0; TIMER_DATA(i) = 0; + if ((twlmode > 0) || dsiMode) { + for (reg=0; reg<0x1c; reg+=4)*((u32*)(0x04004104 + ((i*0x1c)+reg))) = 0; //Reset NDMA. + } } + REG_RCNT = 0; + + // Clear out FIFO + REG_IPC_SYNC = 0; + REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR; + REG_IPC_FIFO_CR = 0; + arm7clearRAM(); + // clear most of EXRAM + toncset((u8*)0x02000000, 0, 0x3FC000); // clear more of EXRAM, skipping the arm9 temp area used by bootloader + REG_IE = 0; REG_IF = ~0; + REG_AUXIE = 0; + REG_AUXIF = ~0; (*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version (*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version - REG_POWERCNT = 1; //turn off power to stuff + REG_POWERCNT = 1; //turn off power to stuffs +} + +static void XMenuFix() { + *((vu8*)0x02FFFF70) = 0x91; + if (twlmode > 0)*((vu8*)0x027FFF70) = 0x91; + +} + +static void setMemoryAddressTWL(const tNDSHeader* ndsHeader) { + if (ndsHeader->unitCode > 0) { + // copyLoop((u32*)0x027FFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header + tonccpy((u32*)0x027FFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header + + *(u32*)(0x027FA680) = 0x02FD4D80; + *(u32*)(0x027FA684) = 0x00000000; + *(u32*)(0x027FA688) = 0x00001980; + + *(u32*)(0x027FF00C) = 0x0000007F; + *(u32*)(0x027FF010) = 0x550E25B8; + *(u32*)(0x027FF014) = 0x02FF4000; + + // Set region flag + if (strncmp(getRomTid(ndsHeader)+3, "J", 1) == 0) { + *(u8*)(0x027FFD70) = 0; + } else if (strncmp(getRomTid(ndsHeader)+3, "E", 1) == 0) { + *(u8*)(0x027FFD70) = 1; + } else if (strncmp(getRomTid(ndsHeader)+3, "P", 1) == 0) { + *(u8*)(0x027FFD70) = 2; + } else if (strncmp(getRomTid(ndsHeader)+3, "U", 1) == 0) { + *(u8*)(0x027FFD70) = 3; + } else if (strncmp(getRomTid(ndsHeader)+3, "C", 1) == 0) { + *(u8*)(0x027FFD70) = 4; + } else if (strncmp(getRomTid(ndsHeader)+3, "K", 1) == 0) { + *(u8*)(0x027FFD70) = 5; + } + } + + // Set memory values expected by loaded NDS + // from NitroHax, thanks to Chism + *((u32*)0x027FF800) = *(u32*)0x02FFFC00; // CurrentCardID + // *((u32*)0x027FF804) = *(u32*)0x02FFFC00; // Command10CardID + *((u16*)0x027FF808) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] + *((u16*)0x027FF80A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] + *((u16*)0x027FF850) = 0x5835; + // Copies of above + *((u32*)0x027FFC00) = *(u32*)0x02FFFC00; // CurrentCardID + // *((u32*)0x027FFC04) = *(u32*)0x02FFFC00; // Command10CardID + *((u16*)0x027FFC08) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] + *((u16*)0x027FFC0A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] + *((u16*)0x027FFC10) = 0x5835; + *((u16*)0x027FFC40) = 0x01; // Boot Indicator -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr + + tonccpy((u32*)0x027FC000, (u32*)0x02FFC000, 0x1000); + tonccpy((u32*)0x027FF000, (u32*)NDS_HEAD_POKEMON, 0x170); + tonccpy((u32*)0x027FFE00, (u32*)NDS_HEAD, 0x160); + tonccpy((u32*)0x027FE000, (u32*)TWL_HEAD, 0x1000); + + tonccpy((u32*)0x027FF830, (u32*)0x02FFF830, 0x20); + tonccpy((u32*)0x027FFC80, (u32*)0x02FFFC80, 0x70); + tonccpy((u32*)0x027FFD80, (u32*)0x02FFFD80, 0x70); +} + +static void setMemoryAddress(const tNDSHeader* ndsHeader) { + if (ndsHeader->unitCode > 0) { + // copyLoop((u32*)0x02FFFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header + tonccpy((u32*)0x02FFFA80, (u32*)ndsHeader, 0x160); // Make a duplicate of DS header + + *(u32*)(0x02FFA680) = 0x02FD4D80; + *(u32*)(0x02FFA684) = 0x00000000; + *(u32*)(0x02FFA688) = 0x00001980; + + *(u32*)(0x02FFF00C) = 0x0000007F; + *(u32*)(0x02FFF010) = 0x550E25B8; + *(u32*)(0x02FFF014) = 0x02FF4000; + + // Set region flag + if (strncmp(getRomTid(ndsHeader)+3, "J", 1) == 0) { + *(u8*)(0x02FFFD70) = 0; + } else if (strncmp(getRomTid(ndsHeader)+3, "E", 1) == 0) { + *(u8*)(0x02FFFD70) = 1; + } else if (strncmp(getRomTid(ndsHeader)+3, "P", 1) == 0) { + *(u8*)(0x02FFFD70) = 2; + } else if (strncmp(getRomTid(ndsHeader)+3, "U", 1) == 0) { + *(u8*)(0x02FFFD70) = 3; + } else if (strncmp(getRomTid(ndsHeader)+3, "C", 1) == 0) { + *(u8*)(0x02FFFD70) = 4; + } else if (strncmp(getRomTid(ndsHeader)+3, "K", 1) == 0) { + *(u8*)(0x02FFFD70) = 5; + } + } + + // Fix Pokemon games needing header data. + // copyLoop((u32*)NDS_HEAD_POKEMON, (u32*)NDS_HEAD, 0x170); + tonccpy((u32*)NDS_HEAD_POKEMON, (u32*)NDS_HEAD, 0x170); + + // Set memory values expected by loaded NDS + // from NitroHax, thanks to Chism + *((u32*)0x02FFF800) = chipID; // CurrentCardID + // *((u32*)0x02FFF804) = chipID; // Command10CardID + *((u16*)0x02FFF808) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] + *((u16*)0x02FFF80A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] + *((u16*)0x02FFF850) = 0x5835; + // Copies of above + *((u32*)0x02FFFC00) = chipID; // CurrentCardID + // *((u32*)0x02FFFC04) = chipID; // Command10CardID + *((u16*)0x02FFFC08) = ndsHeader->headerCRC16; // Header Checksum, CRC-16 of [000h-15Dh] + *((u16*)0x02FFFC0A) = ndsHeader->secureCRC16; // Secure Area Checksum, CRC-16 of [ [20h]..7FFFh] + *((u16*)0x02FFFC10) = 0x5835; + *((u16*)0x02FFFC40) = 0x01; // Boot Indicator -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr + + if ((twlram > 0) && (ndsHeader->unitCode == 0))setMemoryAddressTWL(ndsHeader); + + switch (*((vu16*)0x02FFFF5E)) { + case 0xF63D: XMenuFix(); break; + case 0x0695: XMenuFix(); break; + case 0xE4C4: XMenuFix(); break; + case 0x918C: XMenuFix(); break; + } +} + +static u8 readwriteSPI(u8 data) { + REG_SPIDATA = data; + SerialWaitBusy(); + return REG_SPIDATA; +} + +//--------------------------------------------------------------------------------- +void readFirmware(u32 address, void * destination, u32 size) { +//--------------------------------------------------------------------------------- + int oldIME=enterCriticalSection(); + u8 *buffer = destination; + + // Read command + REG_SPICNT = SPI_ENABLE | SPI_BYTE_MODE | SPI_CONTINUOUS | SPI_DEVICE_FIRMWARE; + readwriteSPI(FIRMWARE_READ); + + // Set the address + readwriteSPI((address>>16) & 0xFF); + readwriteSPI((address>> 8) & 0xFF); + readwriteSPI((address) & 0xFF); + + u32 i; + + // Read the data + for(i=0;ilanguage != 6 && ndsHeader->reserved1[8] == 0x80) { + ndsHeader->reserved1[8] = 0; // Patch iQue game to be region-free + ndsHeader->headerCRC16 = swiCRC16(0xFFFF, ndsHeader, 0x15E); // Fix CRC + } } void loadBinary_ARM7 (u32 fileCluster) { - u32 ndsHeader[0x170>>2]; - // read NDS header - fileRead ((char*)ndsHeader, fileCluster, 0, 0x170); - // read ARM9 info from NDS header - u32 ARM9_SRC = ndsHeader[0x020>>2]; - char* ARM9_DST = (char*)ndsHeader[0x028>>2]; - u32 ARM9_LEN = ndsHeader[0x02C>>2]; - // read ARM7 info from NDS header - u32 ARM7_SRC = ndsHeader[0x030>>2]; - char* ARM7_DST = (char*)ndsHeader[0x038>>2]; - u32 ARM7_LEN = ndsHeader[0x03C>>2]; - + fileRead ((char*)NDS_HEAD, fileCluster, 0, 0x170); + // Load binaries into memory - fileRead(ARM9_DST, fileCluster, ARM9_SRC, ARM9_LEN); - fileRead(ARM7_DST, fileCluster, ARM7_SRC, ARM7_LEN); + fileRead((char*)ntrHeader->arm9destination, fileCluster, ntrHeader->arm9romOffset, ntrHeader->arm9binarySize); + fileRead((char*)ntrHeader->arm7destination, fileCluster, ntrHeader->arm7romOffset, ntrHeader->arm7binarySize); // first copy the header to its proper location, excluding // the ARM9 start address, so as not to start it - TEMP_ARM9_START_ADDRESS = ndsHeader[0x024>>2]; // Store for later - ndsHeader[0x024>>2] = 0; - dmaCopyWords(3, (void*)ndsHeader, (void*)NDS_HEAD, 0x170); - - /*if (dsiMode && (ndsHeader[0x10>>2]&BIT(16+1))) { + TEMP_ARM9_START_ADDRESS = ntrHeader->arm9executeAddress; // Store for later + *(u32*)ntrHeader->arm9executeAddress = 0; + dmaCopyWords(3, (void*)ntrHeader, (void*)NDS_HEAD, 0x170); + + ROM_TID = *(u32*)0x02FFFE0C; + + setMemoryAddress((tNDSHeader*)ntrHeader); + + if ((twlram > 0) && (ntrHeader->unitCode & BIT(1))) { // Read full TWL header fileRead((char*)TWL_HEAD, fileCluster, 0, 0x1000); + tonccpy((void*)0x02FFC000, (void*)TWL_HEAD, 0x1000); + + if (twlHeader->arm9ibinarySize > 0)fileRead((char*)twlHeader->arm9idestination, fileCluster, twlHeader->arm9iromOffset, twlHeader->arm9ibinarySize); + if (twlHeader->arm7ibinarySize > 0)fileRead((char*)twlHeader->arm7idestination, fileCluster, twlHeader->arm7iromOffset, twlHeader->arm7ibinarySize); + + if (twlmode > 0) { + setTWLMBK(); + if (twlHeader->unitCode == 0x02)scfgunlock = 0; + } + } - u32 ARM9i_SRC = *(u32*)(TWL_HEAD+0x1C0); - char* ARM9i_DST = (char*)*(u32*)(TWL_HEAD+0x1C8); - u32 ARM9i_LEN = *(u32*)(TWL_HEAD+0x1CC); - u32 ARM7i_SRC = *(u32*)(TWL_HEAD+0x1D0); - char* ARM7i_DST = (char*)*(u32*)(TWL_HEAD+0x1D8); - u32 ARM7i_LEN = *(u32*)(TWL_HEAD+0x1DC); - - if (ARM9i_LEN)fileRead(ARM9i_DST, fileCluster, ARM9i_SRC, ARM9i_LEN); - if (ARM7i_LEN)fileRead(ARM7i_DST, fileCluster, ARM7i_SRC, ARM7i_LEN); - }*/ + my_readUserSettings((tNDSHeader*)ntrHeader); } /*------------------------------------------------------------------------- @@ -310,20 +495,19 @@ void startBinary_ARM7 (void) { } int main (void) { - + ntrHeader = (tTWLHeader*)NDS_HEAD; + twlHeader = (tTWLHeader*)TWL_HEAD; + tLauncherSettings* tmpData = (tLauncherSettings*)LAUNCH_DATA; - if (tmpData->scfgUnlock > 0x00)scfgunlock = true; - if (tmpData->twlMode > 0x00)twlmode = true; - if (tmpData->twlCLK > 0x00)twlclk = true; - if (tmpData->twlVRAM > 0x00)twlvram = true; + if (tmpData->scfgUnlock > 0x00)scfgunlock = 0xFFFF; + if (tmpData->twlMode > 0x00)twlmode = 0xFFFF; + if (tmpData->twlCLK > 0x00)twlclk = 0xFFFF; + if (tmpData->twlRAM > 0x00)twlram = 0xFFFF; + if (tmpData->twlVRAM > 0x00)twlvram = 0xFFFF; + if (tmpData->cachedChipID > 0x00)chipID = tmpData->cachedChipID; - if (twlmode) { - REG_MBK9=0x0300000F; - REG_MBK6=0x080037C0; - REG_MBK7=0x07C03740; - REG_MBK8=0x07403700; - } else { + if ((twlmode == 0) && (twlram == 0)) { REG_MBK9=0xFCFFFF0F; REG_MBK6=0x09403900; REG_MBK7=0x09803940; @@ -350,14 +534,14 @@ int main (void) { // ARM9 clears its memory part 2 // copy ARM9 function to RAM, and make the ARM9 jump to it - copyLoop((void*)TEMP_MEM, (void*)resetMemory2_ARM9, resetMemory2_ARM9_size); + tonccpy((void*)TEMP_MEM, (void*)resetMemory2_ARM9, resetMemory2_ARM9_size); (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function // Wait until the ARM9 has completed its task while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM); // ARM9 sets up mpu // copy ARM9 function to RAM, and make the ARM9 jump to it - copyLoop((void*)TEMP_MEM, (void*)mpu_reset, mpu_reset_end - mpu_reset); + tonccpy((void*)TEMP_MEM, (void*)mpu_reset, mpu_reset_end - mpu_reset); (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function // Wait until the ARM9 has completed its task @@ -365,15 +549,22 @@ int main (void) { // Get ARM7 to clear RAM resetMemory_ARM7(); - + // ARM9 enters a wait loop // copy ARM9 function to RAM, and make the ARM9 jump to it - copyLoop((void*)TEMP_MEM, (void*)startBinary_ARM9, startBinary_ARM9_size); + tonccpy((void*)TEMP_MEM, (void*)startBinary_ARM9, startBinary_ARM9_size); (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function - + // Load the NDS file loadBinary_ARM7(fileCluster); - + + // Fix for Pictochat and DLP + if (ROM_TID == 0x41444E48 || ROM_TID == 0x41454E48 + || ROM_TID == 0x43444E48 || ROM_TID == 0x43454E48 + || ROM_TID == 0x4B444E48 || ROM_TID == 0x4B454E48) { + (*(vu16*)0x02FFFCFA) = 0x1041; // NoCash: channel ch1+7+13 + } + #ifndef NO_SDMMC if (dsiSD && dsiMode) { sdmmc_controller_init(true); @@ -385,17 +576,18 @@ int main (void) { // Pass command line arguments to loaded program passArgs_ARM7(); - if (twlclk) { REG_SCFG_CLK = 0x0187; } else { REG_SCFG_CLK = 0x0101; } - if (twlmode) { - REG_SCFG_EXT = 0x92FBFB06; + if (twlmode > 0) { + // REG_SCFG_EXT = 0x92FBFB06; + // REG_SCFG_EXT = 0x8307F100; + REG_SCFG_EXT = 0x93FFFB06; } else { - REG_SCFG_EXT = 0x92A00000; + REG_SCFG_EXT = 0x92A40000; // REG_SCFG_EXT |= BIT(18); REG_SCFG_ROM = 0x703; } - if (scfgunlock) { REG_SCFG_EXT |= BIT(18); } else { REG_SCFG_EXT &= ~(1UL << 31); } + if (twlclk > 0) { REG_SCFG_CLK = 0x187; } else { REG_SCFG_CLK = 0x107; } + if (scfgunlock == 0)REG_SCFG_EXT &= ~(1UL << 31); startBinary_ARM7(); - return 0; } diff --git a/ndsbootloader/source/boot.h b/ndsbootloader/source/boot.h index 20c8cc0..1222e52 100644 --- a/ndsbootloader/source/boot.h +++ b/ndsbootloader/source/boot.h @@ -1,11 +1,140 @@ #ifndef _BOOT_H_ #define _BOOT_H_ +typedef struct sTWLHeader { + char gameTitle[12]; //!< 12 characters for the game title. + char gameCode[4]; //!< 4 characters for the game code. + char makercode[2]; //!< identifies the (commercial) developer. + u8 unitCode; //!< identifies the required hardware. + u8 deviceType; //!< type of device in the game card + u8 deviceSize; //!< capacity of the device (1 << n Mbit) + u8 reserved1[9]; + u8 romversion; //!< version of the ROM. + u8 flags; //!< bit 2: auto-boot flag. + + u32 arm9romOffset; //!< offset of the arm9 binary in the nds file. + u32 arm9executeAddress; //!< adress that should be executed after the binary has been copied. + u32 arm9destination; //!< destination address to where the arm9 binary should be copied. + u32 arm9binarySize; //!< size of the arm9 binary. + + u32 arm7romOffset; //!< offset of the arm7 binary in the nds file. + u32 arm7executeAddress; //!< adress that should be executed after the binary has been copied. + u32 arm7destination; //!< destination address to where the arm7 binary should be copied. + u32 arm7binarySize; //!< size of the arm7 binary. + + u32 filenameOffset; //!< File Name Table (FNT) offset. + u32 filenameSize; //!< File Name Table (FNT) size. + u32 fatOffset; //!< File Allocation Table (FAT) offset. + u32 fatSize; //!< File Allocation Table (FAT) size. + + u32 arm9overlaySource; //!< File arm9 overlay offset. + u32 arm9overlaySize; //!< File arm9 overlay size. + u32 arm7overlaySource; //!< File arm7 overlay offset. + u32 arm7overlaySize; //!< File arm7 overlay size. + + u32 cardControl13; //!< Port 40001A4h setting for normal commands (used in modes 1 and 3) + u32 cardControlBF; //!< Port 40001A4h setting for KEY1 commands (used in mode 2) + u32 bannerOffset; //!< offset to the banner with icon and titles etc. + + u16 secureCRC16; //!< Secure Area Checksum, CRC-16. + + u16 readTimeout; //!< Secure Area Loading Timeout. + + u32 unknownRAM1; //!< ARM9 Auto Load List RAM Address (?) + u32 unknownRAM2; //!< ARM7 Auto Load List RAM Address (?) + + u32 bfPrime1; //!< Secure Area Disable part 1. + u32 bfPrime2; //!< Secure Area Disable part 2. + u32 romSize; //!< total size of the ROM. + + u32 headerSize; //!< ROM header size. + u32 zeros88[14]; + u8 gbaLogo[156]; //!< Nintendo logo needed for booting the game. + u16 logoCRC16; //!< Nintendo Logo Checksum, CRC-16. + u16 headerCRC16; //!< header checksum, CRC-16. + + u32 debugRomSource; //!< debug ROM offset. + u32 debugRomSize; //!< debug size. + u32 debugRomDestination; //!< debug RAM destination. + u32 offset_0x16C; //reserved? + + u8 zero[0x10]; + + u32 arm9MBK1; + u32 arm9MBK2; + u32 arm9MBK3; + u32 arm9MBK4; + u32 arm9MBK5; + u32 arm9MBK6; + u32 arm9MBK7; + u32 arm9MBK8; + u32 arm7MBK6; + u32 arm7MBK7; + u32 arm7MBK8; + u32 arm9MBKMaster; + + u32 region; + u32 accessControl; + u32 arm7SCFGSettings; + u16 dsi_unk1; + u8 dsi_unk2; + u8 dsi_flags; + + u32 arm9iromOffset; //!< offset of the arm9 binary in the nds file. + u32 arm9iexecuteAddress; + u32 arm9idestination; //!< destination address to where the arm9 binary should be copied. + u32 arm9ibinarySize; //!< size of the arm9 binary. + + u32 arm7iromOffset; //!< offset of the arm7 binary in the nds file. + u32 deviceListDestination; + u32 arm7idestination; //!< destination address to where the arm7 binary should be copied. + u32 arm7ibinarySize; //!< size of the arm7 binary. + + u8 zero2[0x20]; + + // 0x200 + // TODO: More DSi-specific fields. + u32 dsi1[0x10/4]; + u32 twlRomSize; + u32 dsi_unk3; + u32 dsi_unk4; + u32 dsi_unk5; + + u32 modCrypt1Offset; + u32 modcrypt1Size; + u32 modcrypt2Offset; + u32 modcrypt2Size; + + u32 dsi_tid; + u32 dsi_tid2; + u32 pubSavSize; + u32 prvSavSize; + + u8 reserved3[176]; + u8 age_ratings[0x10]; + + unsigned char hmac_arm9[16]; + unsigned char hmac_arm7[16]; + u8 hmac_digest_master[0x14]; + u8 hmac_icon_title[0x14]; + u8 hmac_arm9i[0x14]; + u8 hmac_arm7i[0x14]; + u8 reserved4[0x28]; + u8 hmac_arm9_no_secure[0x14]; + u8 reserved5[0xA4C]; + u8 debug_args[0x180]; + u8 rsa_signature[0x80]; +} tTWLHeader; + + #define resetMemory2_ARM9_size 0x400 -void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9(); #define startBinary_ARM9_size 0x100 -void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9(); +// #define setSCFG_ARM9_size 0x400 #define ARM9_START_FLAG (*(vu8*)0x02FFFDFB) +void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9(); +// void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) setSCFG_ARM9(); +void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9(); + #endif // _BOOT_H_ diff --git a/arm9/common/tonccpy.c b/ndsbootloader/source/tonccpy.c similarity index 66% rename from arm9/common/tonccpy.c rename to ndsbootloader/source/tonccpy.c index a51437e..09f80ec 100644 --- a/arm9/common/tonccpy.c +++ b/ndsbootloader/source/tonccpy.c @@ -1,3 +1,4 @@ +#include #include "tonccpy.h" //# tonccpy.c @@ -11,18 +12,16 @@ \param size Fill-length in bytes. \note The pointers and size need not be word-aligned. */ -void tonccpy(void *dst, const void *src, uint size) -{ - if(size==0 || dst==0 || src==0) - return; +void tonccpy(void *dst, const void *src, uint size) { + + if(size==0 || dst==NULL || src==NULL) { return; } uint count; u16 *dst16; // hword destination u8 *src8; // byte source // Ideal case: copy by 4x words. Leaves tail for later. - if( ((u32)src|(u32)dst)%4==0 && size>=4) - { + if( ((u32)src|(u32)dst)%4==0 && size>=4) { u32 *src32= (u32*)src, *dst32= (u32*)dst; count= size/4; @@ -30,49 +29,43 @@ void tonccpy(void *dst, const void *src, uint size) count /= 4; // Duff's Device, good friend! + // Added fall through attribute to silance the compiler about this. ;) switch(tmp) { - do { *dst32++ = *src32++; - case 3: *dst32++ = *src32++; - case 2: *dst32++ = *src32++; - case 1: *dst32++ = *src32++; - case 0: ; } while(count--); + do { *dst32++ = *src32++; // fallthrough + case 3: *dst32++ = *src32++; // fallthrough + case 2: *dst32++ = *src32++; // fallthrough + case 1: *dst32++ = *src32++; // fallthrough + case 0: ;} while(count--); // fallthrough } // Check for tail size &= 3; - if(size == 0) - return; - + if(size == 0) { return; } src8= (u8*)src32; dst16= (u16*)dst32; - } - else // Unaligned. - { + } else { + // Unaligned. uint dstOfs= (u32)dst&1; src8= (u8*)src; dst16= (u16*)(dst-dstOfs); // Head: 1 byte. - if(dstOfs != 0) - { + if(dstOfs != 0) { *dst16= (*dst16 & 0xFF) | *src8++<<8; dst16++; - if(--size==0) - return; + if(--size==0) { return; } } } // Unaligned main: copy by 2x byte. count= size/2; - while(count--) - { + while(count--) { *dst16++ = src8[0] | src8[1]<<8; src8 += 2; } // Tail: 1 byte. - if(size&1) - *dst16= (*dst16 &~ 0xFF) | *src8; + if(size&1) { *dst16= (*dst16 &~ 0xFF) | *src8; } } //# toncset.c @@ -87,21 +80,17 @@ void tonccpy(void *dst, const void *src, uint size) word-aligned. In the case of unaligned fills, \a fill will be masked off to match the situation. */ -void __toncset(void *dst, u32 fill, uint size) -{ - if(size==0 || dst==0) - return; +void __toncset(void *dst, u32 fill, uint size) { + if(size==0 || dst==NULL) { return; } uint left= (u32)dst&3; u32 *dst32= (u32*)(dst-left); u32 count, mask; // Unaligned head. - if(left != 0) - { + if(left != 0) { // Adjust for very small stint. - if(left+size<4) - { + if(left+size<4) { mask= BIT_MASK(size*8)<<(left*8); *dst32= (*dst32 &~ mask) | (fill & mask); return; @@ -117,20 +106,21 @@ void __toncset(void *dst, u32 fill, uint size) count= size/4; uint tmp= count&3; count /= 4; - + + // Added fall through attribute to silance the compiler about this. ;) switch(tmp) { - do { *dst32++ = fill; - case 3: *dst32++ = fill; - case 2: *dst32++ = fill; - case 1: *dst32++ = fill; - case 0: ; } while(count--); + do { *dst32++ = fill; // fallthrough + case 3: *dst32++ = fill; // fallthrough + case 2: *dst32++ = fill; // fallthrough + case 1: *dst32++ = fill; // fallthrough + case 0: ;} while(count--); // fallthrough } // Tail size &= 3; - if(size) - { + if(size) { mask= BIT_MASK(size*8); *dst32= (*dst32 &~ mask) | (fill & mask); } } + diff --git a/ndsbootloader/source/tonccpy.h b/ndsbootloader/source/tonccpy.h new file mode 100644 index 0000000..dd4267d --- /dev/null +++ b/ndsbootloader/source/tonccpy.h @@ -0,0 +1,43 @@ +//# Stuff you may not have yet. + +#ifndef TONCCPY_H +#define TONCCPY_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef unsigned int uint; +#define BIT_MASK(len) ( (1<<(len))-1 ) +static inline u32 quad8(u16 x) { x |= x<<8; return x | x<<16; } + + +//# Declarations and inlines. + +void tonccpy(void *dst, const void *src, uint size); + +void __toncset(void *dst, u32 fill, uint size); +static inline void toncset(void *dst, u8 src, uint size); +static inline void toncset16(void *dst, u16 src, uint size); +static inline void toncset32(void *dst, u32 src, uint size); + + +//! VRAM-safe memset, byte version. Size in bytes. +static inline void toncset(void *dst, u8 src, uint size) +{ __toncset(dst, quad8(src), size); } + +//! VRAM-safe memset, halfword version. Size in hwords. +static inline void toncset16(void *dst, u16 src, uint size) +{ __toncset(dst, src|src<<16, size*2); } + +//! VRAM-safe memset, word version. Size in words. +static inline void toncset32(void *dst, u32 src, uint size) +{ __toncset(dst, src, size*4); } + +#ifdef __cplusplus +} +#endif +#endif