From 42b8a3d898843c96f0f791c7fcbdf441abcf7ddc Mon Sep 17 00:00:00 2001 From: Blake Warner Date: Tue, 2 Jul 2019 18:41:00 -0400 Subject: [PATCH 1/4] added support for other file types, and refactored. --- main.c | 215 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 126 insertions(+), 89 deletions(-) diff --git a/main.c b/main.c index f2021b5..493fd96 100644 --- a/main.c +++ b/main.c @@ -10,6 +10,15 @@ #define SWAP(x) ((x >> 24) & 0xff | (x >> 8) & 0xff00 | (x << 8) & 0xff0000 | (x << 24) & 0xff000000) +struct mm_file_type +{ + char* name; + char* fileName; + size_t fileSize; + size_t offset; + uint32_t* key_table; +}; + void rand_init(uint32_t *rand_state, uint32_t in1, uint32_t in2, uint32_t in3, uint32_t in4) { int cond = in1 | in2 | in3 | in4; @@ -45,16 +54,92 @@ void gen_key(uint32_t *key_table, uint32_t *out_key, uint32_t *rand_state) } } -int main(int argc, char **argv) +int usage(int argc, char **argv) +{ + printf("Usage: %s input output\n", argv[0]); + return -1; +} + +char endsWith(const char* a, const char* b) { - if (argc != 3) + if (!a || !b) + { + return 0; + } + + int lenA = strlen(a); + int lenB = strlen(b); + + if (lenB > lenA) { - printf("Usage: %s input output\n", argv[0]); return 0; } + + return strcmp(a + lenA - lenB, b) == 0; +} + +struct mm_file_type file_types[] = { + { "save", "save.dat", 0xC000, 0x0, save_key_table }, + { "quest", "quest.dat", 0xC000, 0x0, quest_key_table }, + { "later", "later.dat", 0xC000, 0x0, later_key_table }, + { "replay", ".dat", 0x68000, 0x0, replay_key_table }, + { "network", "network.dat", 0x48000, 0x0, network_key_table }, + { "thumb", ".btl", 0x1C000, 0x0, thumb_key_table }, + { "course", ".bcd", 0x5C000, 0x10, course_key_table } +}; + +struct mm_file_type* getFileType(const char* fileName, size_t size) +{ + for (int i = 0; i < sizeof(file_types) / sizeof(struct mm_file_type); i++) + { + if (file_types[i].fileSize == size && endsWith(fileName, file_types[i].fileName)) + { + return &file_types[i]; + } + } + return NULL; +} + +int main(int argc, char **argv) +{ + char encrypt = 0; + char* inFileName = NULL; + char* outFileName = NULL; + + for (int i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'e': + encrypt = 1; + break; + default: + return usage(argc, argv); + } + } + else if(!inFileName) + { + inFileName = argv[i]; + } + else if (!outFileName) + { + outFileName = argv[i]; + } + else + { + return usage(argc, argv); + } + } + + if (!inFileName || !outFileName) + { + return usage(argc, argv); + } - FILE *in = fopen(argv[1], "rb"); - FILE *out = fopen(argv[2], "wb"); + FILE *in = fopen(inFileName, "rb"); + FILE *out = fopen(outFileName, "wb"); fseek(in, 0, SEEK_END); size_t sz = ftell(in); @@ -64,92 +149,44 @@ int main(int argc, char **argv) uint32_t rand_state[STATE_SIZE]; uint32_t key_state[STATE_SIZE]; - - switch(sz) // CMAC calculation isn't handled yet, will implement eventually + + + struct mm_file_type* file_type = getFileType(inFileName, sz); + + if (!file_type) { - case 0x1C000: // Thumbnail image data - { - printf("Decrypting thumbnail %s to %s...\n", argv[1], argv[2]); - - char *buf = malloc(0x1C000); - - fread(buf, 1, 0x1C000, in); - fclose(in); - - char *end = buf + sz - 0x30; - - rand_init(rand_state, *(uint32_t *)&end[0x10], *(uint32_t *)&end[0x14], *(uint32_t *)&end[0x18], *(uint32_t *)&end[0x1C]); - gen_key(thumb_key_table, key_state, rand_state); - - AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); - AES_CBC_decrypt_buffer(&ctx, buf, sz - 0x30); - - fwrite(buf, 1, sz - 0x30, out); - fclose(out); - - free(buf); - - break; - } - - case 0x5C000: // Course data - { - printf("Decrypting course %s to %s...\n", argv[1], argv[2]); - - char *buf = malloc(0x5C000); - - fread(buf, 1, 0x5C000, in); - fclose(in); - - char *end = buf + sz - 0x30; - - rand_init(rand_state, *(uint32_t *)&end[0x10], *(uint32_t *)&end[0x14], *(uint32_t *)&end[0x18], *(uint32_t *)&end[0x1C]); - gen_key(course_key_table, key_state, rand_state); - - AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); - AES_CBC_decrypt_buffer(&ctx, buf + 0x10, sz - 0x40); - - fwrite(buf + 0x10, 1, sz - 0x40, out); - fclose(out); - - free(buf); - - break; - } - - case 0x68000: // Replay data - { - printf("Decrypting replay %s to %s...\n", argv[1], argv[2]); - - char *buf = malloc(0x68000); - - fread(buf, 1, 0x68000, in); - fclose(in); - - char *end = buf + sz - 0x30; - - rand_init(rand_state, *(uint32_t *)&end[0x10], *(uint32_t *)&end[0x14], *(uint32_t *)&end[0x18], *(uint32_t *)&end[0x1C]); - gen_key(replay_key_table, key_state, rand_state); - - AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); - AES_CBC_decrypt_buffer(&ctx, buf, sz - 0x30); - - fwrite(buf, 1, sz - 0x30, out); - fclose(out); - - free(buf); - - break; - } - - default: - { - puts("Error: Unsupported file!"); - fclose(in); - fclose(out); - return 0; - } + printf("unsupported file: %s\n", inFileName); + return -1; } + + char *buf = malloc(file_type->fileSize); + memset(buf, 0, file_type->fileSize); + + fread(buf, 1, file_type->fileSize, in); + fclose(in); + + char *end = buf + sz - 0x30; + + rand_init(rand_state, *(uint32_t *)&end[0x10], *(uint32_t *)&end[0x14], *(uint32_t *)&end[0x18], *(uint32_t *)&end[0x1C]); + gen_key(file_type->key_table, key_state, rand_state); + + AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); + + if (encrypt) + { + printf("Encrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); + AES_CBC_encrypt_buffer(&ctx, buf + file_type->offset, sz - (0x30 + file_type->offset)); + } + else + { + printf("Decrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); + AES_CBC_decrypt_buffer(&ctx, buf + file_type->offset, sz - (0x30 + file_type->offset)); + } + + fwrite(buf + file_type->offset, 1, sz - (0x30 + file_type->offset), out); + fclose(out); + + free(buf); puts("Done!"); From 4a152fd613f907f8f25caa85d3469ebd0b484e35 Mon Sep 17 00:00:00 2001 From: Blake Warner Date: Fri, 7 Feb 2020 09:41:58 -0500 Subject: [PATCH 2/4] added encryptor and flag to preserve header. --- main.c | 194 ------------------------------- main.cpp | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 194 deletions(-) delete mode 100644 main.c create mode 100644 main.cpp diff --git a/main.c b/main.c deleted file mode 100644 index 493fd96..0000000 --- a/main.c +++ /dev/null @@ -1,194 +0,0 @@ -#include -#include -#include - -#include "aes.h" -#include "keys.h" - -#define STATE_SIZE 4 -#define NUM_ROUNDS 4 - -#define SWAP(x) ((x >> 24) & 0xff | (x >> 8) & 0xff00 | (x << 8) & 0xff0000 | (x << 24) & 0xff000000) - -struct mm_file_type -{ - char* name; - char* fileName; - size_t fileSize; - size_t offset; - uint32_t* key_table; -}; - -void rand_init(uint32_t *rand_state, uint32_t in1, uint32_t in2, uint32_t in3, uint32_t in4) -{ - int cond = in1 | in2 | in3 | in4; - - rand_state[0] = cond ? in1 : 1; - rand_state[1] = cond ? in2 : 0x6C078967; - rand_state[2] = cond ? in3 : 0x714ACB41; - rand_state[3] = cond ? in4 : 0x48077044; -} - -uint32_t rand_gen(uint32_t *rand_state) -{ - uint32_t n = rand_state[0] ^ rand_state[0] << 11; - rand_state[0] = rand_state[1]; - n ^= n >> 8 ^ rand_state[3] ^ rand_state[3] >> 19; - rand_state[1] = rand_state[2]; - rand_state[2] = rand_state[3]; - rand_state[3] = n; - return n; -} - -void gen_key(uint32_t *key_table, uint32_t *out_key, uint32_t *rand_state) -{ - out_key[0] = 0; - - for (int i = 0; i < STATE_SIZE; i++) - { - for (int j = 0; j < NUM_ROUNDS; j++) - { - out_key[i] <<= 8; - out_key[i] |= (key_table[rand_gen(rand_state) >> 26] >> ((rand_gen(rand_state) >> 27) & 24)) & 0xFF; - } - } -} - -int usage(int argc, char **argv) -{ - printf("Usage: %s input output\n", argv[0]); - return -1; -} - -char endsWith(const char* a, const char* b) -{ - if (!a || !b) - { - return 0; - } - - int lenA = strlen(a); - int lenB = strlen(b); - - if (lenB > lenA) - { - return 0; - } - - return strcmp(a + lenA - lenB, b) == 0; -} - -struct mm_file_type file_types[] = { - { "save", "save.dat", 0xC000, 0x0, save_key_table }, - { "quest", "quest.dat", 0xC000, 0x0, quest_key_table }, - { "later", "later.dat", 0xC000, 0x0, later_key_table }, - { "replay", ".dat", 0x68000, 0x0, replay_key_table }, - { "network", "network.dat", 0x48000, 0x0, network_key_table }, - { "thumb", ".btl", 0x1C000, 0x0, thumb_key_table }, - { "course", ".bcd", 0x5C000, 0x10, course_key_table } -}; - -struct mm_file_type* getFileType(const char* fileName, size_t size) -{ - for (int i = 0; i < sizeof(file_types) / sizeof(struct mm_file_type); i++) - { - if (file_types[i].fileSize == size && endsWith(fileName, file_types[i].fileName)) - { - return &file_types[i]; - } - } - return NULL; -} - -int main(int argc, char **argv) -{ - char encrypt = 0; - char* inFileName = NULL; - char* outFileName = NULL; - - for (int i = 1; i < argc; i++) - { - if (argv[i][0] == '-') - { - switch (argv[i][1]) - { - case 'e': - encrypt = 1; - break; - default: - return usage(argc, argv); - } - } - else if(!inFileName) - { - inFileName = argv[i]; - } - else if (!outFileName) - { - outFileName = argv[i]; - } - else - { - return usage(argc, argv); - } - } - - if (!inFileName || !outFileName) - { - return usage(argc, argv); - } - - FILE *in = fopen(inFileName, "rb"); - FILE *out = fopen(outFileName, "wb"); - - fseek(in, 0, SEEK_END); - size_t sz = ftell(in); - rewind(in); - - struct AES_ctx ctx; - - uint32_t rand_state[STATE_SIZE]; - uint32_t key_state[STATE_SIZE]; - - - struct mm_file_type* file_type = getFileType(inFileName, sz); - - if (!file_type) - { - printf("unsupported file: %s\n", inFileName); - return -1; - } - - char *buf = malloc(file_type->fileSize); - memset(buf, 0, file_type->fileSize); - - fread(buf, 1, file_type->fileSize, in); - fclose(in); - - char *end = buf + sz - 0x30; - - rand_init(rand_state, *(uint32_t *)&end[0x10], *(uint32_t *)&end[0x14], *(uint32_t *)&end[0x18], *(uint32_t *)&end[0x1C]); - gen_key(file_type->key_table, key_state, rand_state); - - AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); - - if (encrypt) - { - printf("Encrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); - AES_CBC_encrypt_buffer(&ctx, buf + file_type->offset, sz - (0x30 + file_type->offset)); - } - else - { - printf("Decrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); - AES_CBC_decrypt_buffer(&ctx, buf + file_type->offset, sz - (0x30 + file_type->offset)); - } - - fwrite(buf + file_type->offset, 1, sz - (0x30 + file_type->offset), out); - fclose(out); - - free(buf); - - puts("Done!"); - - return 0; -} \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..29ce952 --- /dev/null +++ b/main.cpp @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include + +extern "C" { +#include "aes.h" +#include "keys.h" +} + +#define STATE_SIZE 4 +#define NUM_ROUNDS 4 + + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +struct u128 +{ + u8 buffer[16]; +}; + +static_assert(sizeof(u8) == 1, "incorrect u8 size"); +static_assert(sizeof(u16) == 2, "incorrect u16 size"); +static_assert(sizeof(u32) == 4, "incorrect u32 size"); +static_assert(sizeof(u128) == 16, "incorrect u128 size"); + +#define SWAP(x) ((x >> 24) & 0xff | (x >> 8) & 0xff00 | (x << 8) & 0xff0000 | (x << 24) & 0xff000000) + +u32 calcCrc32(const void* buffer, u32 len) +{ + const u8* message = (const u8*)buffer; + int i, j; + u32 byte, crc, mask; + + i = 0; + crc = 0xFFFFFFFF; + while (len--) + { + byte = message[i]; + crc = crc ^ byte; + for (j = 7; j >= 0; j--) + { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + i = i + 1; + } + return ~crc; +} + +struct SavePartitionHeader +{ + u32 formatVersion; + u16 saveVersion; + u16 flags; + u32 crc32; + u32 magic; + + void setCrc32(const u32 sz) + { + crc32 = calcCrc32((const u8*)&formatVersion + sizeof(*this), sz); + } +}; + +struct CryptInfo +{ + u32 rand32(u128& value) const + { + u32* rand = (u32*)&value; + + u32 t = rand[0]; + t ^= (t << 11); + t ^= (t >> 8); + t ^= rand[3]; + t ^= (rand[3] >> 19); + + rand[0] = rand[1]; + rand[1] = rand[2]; + rand[2] = rand[3]; + rand[3] = t; + return t; + } + + const u128 randkey(u128& keyState, u32* lut) const + { + u32 key32[4]; + memset(&key32, 0, sizeof(key32)); + + for (u32 i = 0; i < 4; i++) + { + for (u32 j = 0; j < 4; j++) + { + u32 state = key32[i]; + + u32 lookup = lut[rand32(keyState) >> 26]; + + u32 t = (rand32(keyState) >> 27) & 0x18; + + t = lookup >> t; + state |= (t & 0xFF); + + + if (j != 3) + { + state = state << 8; + } + + key32[i] = state; + } + } + return reinterpret_cast(key32); + } + + u128 iv; + u128 rand; + u128 mac; +}; + +static_assert(sizeof(CryptInfo) == 0x30, "incorrect CryptInfo size"); + +struct mm_file_type +{ + char* name; + char* fileName; + size_t fileSize; + size_t offset; + uint32_t* key_table; +}; + +void rand_init(uint32_t *rand_state, const u128& in) +{ + const u32* in32 = (const u32*)∈ + int cond = in32[0] | in32[1] | in32[2] | in32[3]; + + rand_state[0] = cond ? in32[0] : 1; + rand_state[1] = cond ? in32[1] : 0x6C078967; + rand_state[2] = cond ? in32[2] : 0x714ACB41; + rand_state[3] = cond ? in32[3] : 0x48077044; +} + +uint32_t rand_gen(uint32_t *rand_state) +{ + uint32_t n = rand_state[0] ^ rand_state[0] << 11; + rand_state[0] = rand_state[1]; + n ^= n >> 8 ^ rand_state[3] ^ rand_state[3] >> 19; + rand_state[1] = rand_state[2]; + rand_state[2] = rand_state[3]; + rand_state[3] = n; + return n; +} + +void gen_key(uint32_t *key_table, uint32_t *out_key, uint32_t *rand_state) +{ + out_key[0] = 0; + + for (int i = 0; i < STATE_SIZE; i++) + { + for (int j = 0; j < NUM_ROUNDS; j++) + { + out_key[i] <<= 8; + out_key[i] |= (key_table[rand_gen(rand_state) >> 26] >> ((rand_gen(rand_state) >> 27) & 24)) & 0xFF; + } + } +} + +int usage(int argc, char **argv) +{ + printf("Usage: %s input output\nflag -h preserves header, required to re-encrypt later.\nflag -e encrypt\n", argv[0]); + return -1; +} + +char endsWith(const char* a, const char* b) +{ + if (!a || !b) + { + return 0; + } + + int lenA = strlen(a); + int lenB = strlen(b); + + if (lenB > lenA) + { + return 0; + } + + return strcmp(a + lenA - lenB, b) == 0; +} + +struct mm_file_type file_types[] = { + { "save", "save.dat", 0xC000, 0x0, save_key_table }, + { "quest", "quest.dat", 0xC000, 0x0, quest_key_table }, + { "later", "later.dat", 0xC000, 0x0, later_key_table }, + { "replay", ".dat", 0x68000, 0x0, replay_key_table }, + { "network", "network.dat", 0x48000, 0x0, network_key_table }, + { "thumb", ".btl", 0x1C000, 0x0, thumb_key_table }, + { "course", ".bcd", 0x5C000, sizeof(SavePartitionHeader), course_key_table } +}; + +struct mm_file_type* getFileType(const char* fileName, size_t size) +{ + for (int i = 0; i < sizeof(file_types) / sizeof(struct mm_file_type); i++) + { + if (file_types[i].fileSize == size && endsWith(fileName, file_types[i].fileName)) + { + return &file_types[i]; + } + } + return NULL; +} + +int main(int argc, char **argv) +{ + bool preserveHeader = false; + bool encrypt = false; + char* inFileName = NULL; + char* outFileName = NULL; + + for (int i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'e': + encrypt = true; + break; + case 'h': + preserveHeader = true; + break; + default: + return usage(argc, argv); + } + } + else if(!inFileName) + { + inFileName = argv[i]; + } + else if (!outFileName) + { + outFileName = argv[i]; + } + else + { + return usage(argc, argv); + } + } + + if (!inFileName || !outFileName) + { + return usage(argc, argv); + } + + FILE *in = fopen(inFileName, "rb"); + FILE *out = fopen(outFileName, "wb"); + + fseek(in, 0, SEEK_END); + size_t sz = ftell(in); + rewind(in); + + struct AES_ctx ctx; + + uint32_t rand_state[STATE_SIZE]; + uint32_t key_state[STATE_SIZE]; + + + struct mm_file_type* file_type = getFileType(inFileName, sz); + + if (!file_type) + { + if (encrypt) + { + printf("unsupported file: %s, did you forget to preserve headers?\n", inFileName); + } + else + { + printf("unsupported file: %s\n", inFileName); + } + return -1; + } + + auto *buf = (u8*)malloc(file_type->fileSize); + memset(buf, 0, file_type->fileSize); + + fread(buf, 1, file_type->fileSize, in); + fclose(in); + + + u8* end = buf + sz - sizeof(CryptInfo); + CryptInfo cryptInfo = *(CryptInfo*)(buf + sz - sizeof(CryptInfo)); + + rand_init(rand_state, cryptInfo.rand); + gen_key(file_type->key_table, key_state, rand_state); + + AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); + + u32 bodyLen = sz - (sizeof(CryptInfo) + file_type->offset); + u8* body = buf + file_type->offset; + + + if (encrypt) + { + if (file_type->offset) + { + auto header = (SavePartitionHeader*)buf; + header->setCrc32(bodyLen); + } + printf("Encrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); + AES_CBC_encrypt_buffer(&ctx, body, bodyLen); + fwrite(buf, 1, sz, out); + } + else + { + + + printf("Decrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); + AES_CBC_decrypt_buffer(&ctx, body, bodyLen); + + if (preserveHeader) + { + fwrite(buf, 1, sz, out); + } + else + { + fwrite(body, 1, bodyLen, out); + } + } + + fclose(out); + + free(buf); + + puts("Done!"); + + return 0; +} \ No newline at end of file From edf669088f30a0b8681cf2272fe14c53c910a433 Mon Sep 17 00:00:00 2001 From: Blake Warner Date: Sun, 26 Apr 2020 11:55:56 -0400 Subject: [PATCH 3/4] fixed cmac during encryption --- main.cpp | 648 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 339 insertions(+), 309 deletions(-) diff --git a/main.cpp b/main.cpp index 29ce952..af7c20d 100644 --- a/main.cpp +++ b/main.cpp @@ -1,34 +1,35 @@ -#include -#include -#include -#include -#include - -extern "C" { -#include "aes.h" -#include "keys.h" -} - -#define STATE_SIZE 4 -#define NUM_ROUNDS 4 - - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -struct u128 -{ - u8 buffer[16]; -}; - -static_assert(sizeof(u8) == 1, "incorrect u8 size"); -static_assert(sizeof(u16) == 2, "incorrect u16 size"); -static_assert(sizeof(u32) == 4, "incorrect u32 size"); -static_assert(sizeof(u128) == 16, "incorrect u128 size"); - -#define SWAP(x) ((x >> 24) & 0xff | (x >> 8) & 0xff00 | (x << 8) & 0xff0000 | (x << 24) & 0xff000000) - +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "aes.h" +#include "keys.h" +} + +#define STATE_SIZE 4 +#define NUM_ROUNDS 4 + + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +struct u128 +{ + u8 buffer[16]; +}; + +static_assert(sizeof(u8) == 1, "incorrect u8 size"); +static_assert(sizeof(u16) == 2, "incorrect u16 size"); +static_assert(sizeof(u32) == 4, "incorrect u32 size"); +static_assert(sizeof(u128) == 16, "incorrect u128 size"); + +#define SWAP(x) ((x >> 24) & 0xff | (x >> 8) & 0xff00 | (x << 8) & 0xff0000 | (x << 24) & 0xff000000) + u32 calcCrc32(const void* buffer, u32 len) { const u8* message = (const u8*)buffer; @@ -49,291 +50,320 @@ u32 calcCrc32(const void* buffer, u32 len) i = i + 1; } return ~crc; -} - -struct SavePartitionHeader -{ - u32 formatVersion; - u16 saveVersion; - u16 flags; - u32 crc32; - u32 magic; - - void setCrc32(const u32 sz) - { - crc32 = calcCrc32((const u8*)&formatVersion + sizeof(*this), sz); - } -}; - -struct CryptInfo -{ - u32 rand32(u128& value) const - { - u32* rand = (u32*)&value; - - u32 t = rand[0]; - t ^= (t << 11); - t ^= (t >> 8); - t ^= rand[3]; - t ^= (rand[3] >> 19); - - rand[0] = rand[1]; - rand[1] = rand[2]; - rand[2] = rand[3]; - rand[3] = t; - return t; - } - - const u128 randkey(u128& keyState, u32* lut) const - { - u32 key32[4]; - memset(&key32, 0, sizeof(key32)); - - for (u32 i = 0; i < 4; i++) - { - for (u32 j = 0; j < 4; j++) - { - u32 state = key32[i]; - - u32 lookup = lut[rand32(keyState) >> 26]; - - u32 t = (rand32(keyState) >> 27) & 0x18; - - t = lookup >> t; - state |= (t & 0xFF); - - - if (j != 3) - { - state = state << 8; - } - - key32[i] = state; - } - } - return reinterpret_cast(key32); - } - - u128 iv; - u128 rand; - u128 mac; -}; - -static_assert(sizeof(CryptInfo) == 0x30, "incorrect CryptInfo size"); - -struct mm_file_type -{ - char* name; - char* fileName; - size_t fileSize; - size_t offset; - uint32_t* key_table; -}; - -void rand_init(uint32_t *rand_state, const u128& in) -{ - const u32* in32 = (const u32*)∈ - int cond = in32[0] | in32[1] | in32[2] | in32[3]; - - rand_state[0] = cond ? in32[0] : 1; - rand_state[1] = cond ? in32[1] : 0x6C078967; - rand_state[2] = cond ? in32[2] : 0x714ACB41; - rand_state[3] = cond ? in32[3] : 0x48077044; -} - -uint32_t rand_gen(uint32_t *rand_state) -{ - uint32_t n = rand_state[0] ^ rand_state[0] << 11; - rand_state[0] = rand_state[1]; - n ^= n >> 8 ^ rand_state[3] ^ rand_state[3] >> 19; - rand_state[1] = rand_state[2]; - rand_state[2] = rand_state[3]; - rand_state[3] = n; - return n; -} - -void gen_key(uint32_t *key_table, uint32_t *out_key, uint32_t *rand_state) -{ - out_key[0] = 0; - - for (int i = 0; i < STATE_SIZE; i++) - { - for (int j = 0; j < NUM_ROUNDS; j++) - { - out_key[i] <<= 8; - out_key[i] |= (key_table[rand_gen(rand_state) >> 26] >> ((rand_gen(rand_state) >> 27) & 24)) & 0xFF; - } - } -} - -int usage(int argc, char **argv) -{ - printf("Usage: %s input output\nflag -h preserves header, required to re-encrypt later.\nflag -e encrypt\n", argv[0]); - return -1; -} - -char endsWith(const char* a, const char* b) -{ - if (!a || !b) - { - return 0; - } - - int lenA = strlen(a); - int lenB = strlen(b); - - if (lenB > lenA) - { - return 0; - } - - return strcmp(a + lenA - lenB, b) == 0; -} - -struct mm_file_type file_types[] = { - { "save", "save.dat", 0xC000, 0x0, save_key_table }, - { "quest", "quest.dat", 0xC000, 0x0, quest_key_table }, - { "later", "later.dat", 0xC000, 0x0, later_key_table }, - { "replay", ".dat", 0x68000, 0x0, replay_key_table }, - { "network", "network.dat", 0x48000, 0x0, network_key_table }, - { "thumb", ".btl", 0x1C000, 0x0, thumb_key_table }, - { "course", ".bcd", 0x5C000, sizeof(SavePartitionHeader), course_key_table } -}; - -struct mm_file_type* getFileType(const char* fileName, size_t size) -{ - for (int i = 0; i < sizeof(file_types) / sizeof(struct mm_file_type); i++) - { - if (file_types[i].fileSize == size && endsWith(fileName, file_types[i].fileName)) - { - return &file_types[i]; - } - } - return NULL; -} - -int main(int argc, char **argv) +} + +struct SavePartitionHeader +{ + u32 formatVersion; + u16 saveVersion; + u16 flags; + u32 crc32; + u32 magic; + + void setCrc32(const u32 sz) + { + crc32 = calcCrc32((const u8*)&formatVersion + sizeof(*this), sz); + } +}; + +struct CryptInfo +{ + u32 rand32(u128& value) const + { + u32* rand = (u32*)&value; + + u32 t = rand[0]; + t ^= (t << 11); + t ^= (t >> 8); + t ^= rand[3]; + t ^= (rand[3] >> 19); + + rand[0] = rand[1]; + rand[1] = rand[2]; + rand[2] = rand[3]; + rand[3] = t; + return t; + } + + const u128 randkey(u128& keyState, u32* lut) const + { + u32 key32[4]; + memset(&key32, 0, sizeof(key32)); + + for (u32 i = 0; i < 4; i++) + { + for (u32 j = 0; j < 4; j++) + { + u32 state = key32[i]; + + u32 lookup = lut[rand32(keyState) >> 26]; + + u32 t = (rand32(keyState) >> 27) & 0x18; + + t = lookup >> t; + state |= (t & 0xFF); + + + if (j != 3) + { + state = state << 8; + } + + key32[i] = state; + } + } + return reinterpret_cast(key32); + } + + u128 iv; + u128 rand; + u128 mac; +}; + +static_assert(sizeof(CryptInfo) == 0x30, "incorrect CryptInfo size"); + +struct mm_file_type +{ + char* name; + char* fileName; + size_t fileSize; + size_t offset; + uint32_t* key_table; +}; + +void rand_init(uint32_t *rand_state, const u128& in) +{ + const u32* in32 = (const u32*)∈ + int cond = in32[0] | in32[1] | in32[2] | in32[3]; + + rand_state[0] = cond ? in32[0] : 1; + rand_state[1] = cond ? in32[1] : 0x6C078967; + rand_state[2] = cond ? in32[2] : 0x714ACB41; + rand_state[3] = cond ? in32[3] : 0x48077044; +} + +uint32_t rand_gen(uint32_t *rand_state) +{ + uint32_t n = rand_state[0] ^ rand_state[0] << 11; + rand_state[0] = rand_state[1]; + n ^= n >> 8 ^ rand_state[3] ^ rand_state[3] >> 19; + rand_state[1] = rand_state[2]; + rand_state[2] = rand_state[3]; + rand_state[3] = n; + return n; +} + +const u128 cmac(const void* buffer, uint64_t size, const uint32_t* key) { - bool preserveHeader = false; - bool encrypt = false; - char* inFileName = NULL; - char* outFileName = NULL; - - for (int i = 1; i < argc; i++) - { - if (argv[i][0] == '-') - { - switch (argv[i][1]) - { - case 'e': - encrypt = true; - break; - case 'h': - preserveHeader = true; - break; - default: - return usage(argc, argv); - } - } - else if(!inFileName) - { - inFileName = argv[i]; - } - else if (!outFileName) - { - outFileName = argv[i]; - } - else - { - return usage(argc, argv); - } - } - - if (!inFileName || !outFileName) - { - return usage(argc, argv); - } - - FILE *in = fopen(inFileName, "rb"); - FILE *out = fopen(outFileName, "wb"); - - fseek(in, 0, SEEK_END); - size_t sz = ftell(in); - rewind(in); - - struct AES_ctx ctx; - - uint32_t rand_state[STATE_SIZE]; - uint32_t key_state[STATE_SIZE]; - - - struct mm_file_type* file_type = getFileType(inFileName, sz); + u128 result; - if (!file_type) - { - if (encrypt) - { - printf("unsupported file: %s, did you forget to preserve headers?\n", inFileName); - } - else - { - printf("unsupported file: %s\n", inFileName); - } - return -1; - } - - auto *buf = (u8*)malloc(file_type->fileSize); - memset(buf, 0, file_type->fileSize); - - fread(buf, 1, file_type->fileSize, in); - fclose(in); - - - u8* end = buf + sz - sizeof(CryptInfo); - CryptInfo cryptInfo = *(CryptInfo*)(buf + sz - sizeof(CryptInfo)); + unsigned char m[100], m_len = 32; + unsigned char out[16], out1[16], out2[16]; + size_t d_len; - rand_init(rand_state, cryptInfo.rand); - gen_key(file_type->key_table, key_state, rand_state); - AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); + mbedtls_cipher_context_t ctx; + const mbedtls_cipher_info_t* cipherInfo = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); - u32 bodyLen = sz - (sizeof(CryptInfo) + file_type->offset); - u8* body = buf + file_type->offset; - - - if (encrypt) + if (cipherInfo == NULL) { - if (file_type->offset) - { - auto header = (SavePartitionHeader*)buf; - header->setCrc32(bodyLen); - } - printf("Encrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); - AES_CBC_encrypt_buffer(&ctx, body, bodyLen); - fwrite(buf, 1, sz, out); + return result; } - else - { - - printf("Decrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); - AES_CBC_decrypt_buffer(&ctx, body, bodyLen); + mbedtls_cipher_init(&ctx); + mbedtls_cipher_setup(&ctx, cipherInfo); + mbedtls_cipher_cmac_starts(&ctx, (const u8*)key, sizeof(uint32_t) * 4 * 8); + mbedtls_cipher_cmac_update(&ctx, (const u8*)buffer, size); + mbedtls_cipher_cmac_finish(&ctx, (u8*)&result); - if (preserveHeader) - { - fwrite(buf, 1, sz, out); - } - else - { - fwrite(body, 1, bodyLen, out); - } - } - - fclose(out); - - free(buf); - - puts("Done!"); - - return 0; + return result; +} + +void gen_key(uint32_t *key_table, uint32_t *out_key, uint32_t *rand_state) +{ + out_key[0] = 0; + + for (int i = 0; i < STATE_SIZE; i++) + { + for (int j = 0; j < NUM_ROUNDS; j++) + { + out_key[i] <<= 8; + out_key[i] |= (key_table[rand_gen(rand_state) >> 26] >> ((rand_gen(rand_state) >> 27) & 24)) & 0xFF; + } + } +} + +int usage(int argc, char **argv) +{ + printf("Usage: %s input output\nflag -h preserves header, required to re-encrypt later.\nflag -e encrypt\n", argv[0]); + return -1; +} + +char endsWith(const char* a, const char* b) +{ + if (!a || !b) + { + return 0; + } + + int lenA = strlen(a); + int lenB = strlen(b); + + if (lenB > lenA) + { + return 0; + } + + return strcmp(a + lenA - lenB, b) == 0; +} + +struct mm_file_type file_types[] = { + { "save", "save.dat", 0xC000, 0x0, save_key_table }, + { "quest", "quest.dat", 0xC000, 0x0, quest_key_table }, + { "later", "later.dat", 0xC000, 0x0, later_key_table }, + { "replay", ".dat", 0x68000, 0x0, replay_key_table }, + { "network", "network.dat", 0x48000, 0x0, network_key_table }, + { "thumb", ".btl", 0x1C000, 0x0, thumb_key_table }, + { "course", ".bcd", 0x5C000, sizeof(SavePartitionHeader), course_key_table } +}; + +struct mm_file_type* getFileType(const char* fileName, size_t size) +{ + for (int i = 0; i < sizeof(file_types) / sizeof(struct mm_file_type); i++) + { + if (file_types[i].fileSize == size && endsWith(fileName, file_types[i].fileName)) + { + return &file_types[i]; + } + } + return NULL; +} + +int main(int argc, char **argv) +{ + bool preserveHeader = false; + bool encrypt = false; + char* inFileName = NULL; + char* outFileName = NULL; + + for (int i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'e': + encrypt = true; + break; + case 'h': + preserveHeader = true; + break; + default: + return usage(argc, argv); + } + } + else if(!inFileName) + { + inFileName = argv[i]; + } + else if (!outFileName) + { + outFileName = argv[i]; + } + else + { + return usage(argc, argv); + } + } + + if (!inFileName || !outFileName) + { + return usage(argc, argv); + } + + FILE *in = fopen(inFileName, "rb"); + FILE *out = fopen(outFileName, "wb"); + + fseek(in, 0, SEEK_END); + size_t sz = ftell(in); + rewind(in); + + struct AES_ctx ctx; + + uint32_t rand_state[STATE_SIZE]; + uint32_t key_state[STATE_SIZE]; + uint32_t auth_state[STATE_SIZE]; + + + struct mm_file_type* file_type = getFileType(inFileName, sz); + + if (!file_type) + { + if (encrypt) + { + printf("unsupported file: %s, did you forget to preserve headers?\n", inFileName); + } + else + { + printf("unsupported file: %s\n", inFileName); + } + return -1; + } + + auto *buf = (u8*)malloc(file_type->fileSize); + memset(buf, 0, file_type->fileSize); + + fread(buf, 1, file_type->fileSize, in); + fclose(in); + + + u8* end = buf + sz - sizeof(CryptInfo); + CryptInfo cryptInfo = *(CryptInfo*)(buf + sz - sizeof(CryptInfo)); + + rand_init(rand_state, cryptInfo.rand); + gen_key(file_type->key_table, key_state, rand_state); + gen_key(file_type->key_table, auth_state, rand_state); + + AES_init_ctx_iv(&ctx, (uint8_t *)key_state, end); + + u32 bodyLen = sz - (sizeof(CryptInfo) + file_type->offset); + u8* body = buf + file_type->offset; + + + if (encrypt) + { + if (file_type->offset) + { + auto header = (SavePartitionHeader*)buf; + header->setCrc32(bodyLen); + } + printf("Encrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); + cryptInfo.mac = cmac(body, bodyLen, auth_state); + AES_CBC_encrypt_buffer(&ctx, body, bodyLen); + fwrite(buf, 1, sz, out); + } + else + { + + + printf("Decrypting %s %s to %s...\n", file_type->name, inFileName, outFileName); + AES_CBC_decrypt_buffer(&ctx, body, bodyLen); + + if (preserveHeader) + { + fwrite(buf, 1, sz, out); + } + else + { + fwrite(body, 1, bodyLen, out); + } + } + + fclose(out); + + free(buf); + + puts("Done!"); + + return 0; } \ No newline at end of file From ef1d98a8d80569a1d9aca823e9779f28b6c1a1f7 Mon Sep 17 00:00:00 2001 From: Blake Warner Date: Sun, 26 Apr 2020 12:04:39 -0400 Subject: [PATCH 4/4] added file handler for jpg --- main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/main.cpp b/main.cpp index af7c20d..4b5190d 100644 --- a/main.cpp +++ b/main.cpp @@ -224,6 +224,7 @@ struct mm_file_type file_types[] = { { "replay", ".dat", 0x68000, 0x0, replay_key_table }, { "network", "network.dat", 0x48000, 0x0, network_key_table }, { "thumb", ".btl", 0x1C000, 0x0, thumb_key_table }, + { "thumb", ".jpg", 0x1C000, 0x0, thumb_key_table }, { "course", ".bcd", 0x5C000, sizeof(SavePartitionHeader), course_key_table } };