diff --git a/.gitignore b/.gitignore index 9414fc5..e9da0c0 100644 --- a/.gitignore +++ b/.gitignore @@ -332,3 +332,10 @@ ASALocalRun/ build/ .* +*.kelf +*.xlf +*.elf +*.KELF +*.XLF +*.ELF +PS2KEYS.dat diff --git a/Makefile b/Makefile index 7414e66..0ef5139 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,10 @@ CXXFLAGS = --std=c++17 LDLIBS = -lcrypto # next flags only for macos -OUTPUT_OPTION = -I/usr/local/opt/openssl@1.1/include +# change paths to your location of openssl@1.1 +# OUTPUT_OPTION += -I$(HOME)/usr/local/Cellar/openssl@1.1/1.1.1m/include +# LDLIBS += -L$(HOME)/usr/local/Cellar/openssl@1.1/1.1.1m/lib +OUTPUT_OPTION += -I/usr/local/opt/openssl@1.1/include LDLIBS += -L/usr/local/opt/openssl@1.1/lib objects = $(patsubst $(dir_source)/%.cpp, $(dir_build)/%.o, \ diff --git a/src/kelf.cpp b/src/kelf.cpp index 2f057dc..bb02358 100644 --- a/src/kelf.cpp +++ b/src/kelf.cpp @@ -16,6 +16,7 @@ */ #include #include +#include #include "kelf.h" @@ -83,24 +84,155 @@ void xor_bit(const void *a, const void *b, void *Result, size_t Length) } } -int Kelf::LoadKelf(std::string filename) +int Kelf::LoadKelf(const std::string filename) { FILE *f = fopen(filename.c_str(), "rb"); + if (f == NULL) { + fprintf(stderr, "Couldn't open %s: %s\n", filename.c_str(), strerror(errno)); + return KELF_ERROR_UNSUPPORTED_FILE; + } KELFHeader header; fread(&header, sizeof(header), 1, f); if (header.Flags & 1 || header.Flags & 0xf0000 || header.BitCount != 0) { - printf("This file is not supported yet and looked after."); - printf("Please upload it and post it under that issue:"); - printf("https://github.com/xfwcfw/kelftool/issues/1"); - fclose(f); - return KELF_ERROR_UNSUPPORTED_FILE; + // TODO: check more unknown bit flags + printf("This file is not supported yet and looked after.\n"); + printf("Please upload it and post it under that issue:\n"); + printf("https://github.com/xfwcfw/kelftool/issues/1\n"); + // fclose(f); + // return KELF_ERROR_UNSUPPORTED_FILE; } + printf("header.UserDefined ="); + for (size_t i = 0; i < sizeof(header.UserDefined); ++i) + printf(" %02X", header.UserDefined[i]); + if (!memcmp(header.UserDefined, USER_HEADER_FMCB, 16)) + printf(" (FMCB)\n"); + else if (!memcmp(header.UserDefined, USER_HEADER_FHDB, 16)) + printf(" (FHDB)\n"); + else if (!memcmp(header.UserDefined, USER_HEADER_MBR, 16)) + printf(" (MBR)\n"); + else + printf("\n"); + + printf("header.ContentSize = %#X\n", header.ContentSize); + printf("header.HeaderSize = %#X\n", header.HeaderSize); + switch (header.SystemType) { + case 0: + printf("header.SystemType = 0 (SYSTEM_TYPE_PS2)\n"); + break; + case 1: + printf("header.SystemType = 1 (SYSTEM_TYPE_PSX)\n"); + break; + default: + printf("header.SystemType = %#X\n", header.SystemType); + printf(" This value is unknown.\n"); + printf(" Please upload file and post under that issue:\n"); + printf(" https://github.com/xfwcfw/kelftool/issues/1\n"); + break; + } + switch (header.ApplicationType) { + case 0: + printf("header.ApplicationType = 0 (disc wobble \?)\n"); + break; + case 1: + printf("header.ApplicationType = 1 (xosdmain)\n"); + break; + case 5: + printf("header.ApplicationType = 5 (dvdplayer kirx)\n"); + break; + case 7: + printf("header.ApplicationType = 7 (dvdplayer kelf)\n"); + break; + case 11: + printf("header.ApplicationType = 11 (early mbr \?)\n"); + break; + default: + printf("header.ApplicationType = %#X\n", header.ApplicationType); + printf(" This value is unknown.\n"); + printf(" Please upload file and post under that issue:\n"); + printf(" https://github.com/xfwcfw/kelftool/issues/1\n"); + break; + } + printf("header.Flags = %#X", header.Flags); + if (header.Flags == 0x022c) + printf(" - kelf:"); + else if (header.Flags == 0x021c) + printf(" - kirx:"); + else + printf(" - unknown:"); + if (header.Flags & HDR_FLAG0_BLACKLIST) + printf("HDR_FLAG0_BLACKLIST|"); + if (header.Flags & HDR_FLAG1_WHITELIST) + printf("HDR_FLAG1_WHITELIST|"); + if (header.Flags & HDR_FLAG2) + printf("HDR_FLAG2|"); + if (header.Flags & HDR_FLAG3) + printf("HDR_FLAG3|"); + if (header.Flags & HDR_FLAG4_1DES) + printf("HDR_FLAG4_1DES|"); + if (header.Flags & HDR_FLAG4_3DES) + printf("HDR_FLAG4_3DES|"); + if (header.Flags & HDR_FLAG6) + printf("HDR_FLAG6|"); + if (header.Flags & HDR_FLAG7) + printf("HDR_FLAG7|"); + if (header.Flags & HDR_FLAG8) + printf("HDR_FLAG8|"); + if (header.Flags & HDR_FLAG9) + printf("HDR_FLAG9|"); + if (header.Flags & HDR_FLAG10) + printf("HDR_FLAG10|"); + if (header.Flags & HDR_FLAG11) + printf("HDR_FLAG11|"); + if (header.Flags & HDR_FLAG12) + printf("HDR_FLAG12|"); + if (header.Flags & HDR_FLAG13) + printf("HDR_FLAG13|"); + if (header.Flags & HDR_FLAG14) + printf("HDR_FLAG14|"); + if (header.Flags & HDR_FLAG15) + printf("HDR_FLAG15|"); + printf("\n"); + + printf("header.BitCount = %#X\n", header.BitCount); + printf("header.MGZones = %#X |", header.MGZones); + if (header.MGZones == 0) + printf("All regions blocked (useless)|"); + else if (header.MGZones == 0xFF) + printf("All regions allowed|"); + else { + if (header.MGZones & REGION_JP) + printf("Japan|"); + if (header.MGZones & REGION_NA) + printf("North America|"); + if (header.MGZones & REGION_EU) + printf("Europe|"); + if (header.MGZones & REGION_AU) + printf("Australia|"); + if (header.MGZones & REGION_ASIA) + printf("Asia|"); + if (header.MGZones & REGION_RU) + printf("Russia|"); + if (header.MGZones & REGION_CH) + printf("China|"); + if (header.MGZones & REGION_MX) + printf("Mexico|"); + } + printf("\n"); + + printf("header.gap ="); + for (unsigned int i = 0; i < 3; ++i) + printf(" %02X", (unsigned char)header.gap[i]); + printf("\n"); std::string HeaderSignature; HeaderSignature.resize(8); fread(HeaderSignature.data(), 1, HeaderSignature.size(), f); + printf("HeaderSignature ="); + for (size_t i = 0; i < 8; ++i) + printf(" %02X", (unsigned char)HeaderSignature[i]); + printf("\n"); if (HeaderSignature != GetHeaderSignature(header)) { fclose(f); @@ -114,10 +246,18 @@ int Kelf::LoadKelf(std::string filename) Kc.resize(16); fread(Kc.data(), 1, Kc.size(), f); - DecryptKeys(KEK); + printf("\nKbit ="); + for (size_t i = 0; i < 16; ++i) + printf(" %02X", (unsigned char)Kbit[i]); + + printf("\nKc ="); + for (size_t i = 0; i < 16; ++i) + printf(" %02X", (unsigned char)Kc[i]); + int BitTableSize = header.HeaderSize - ftell(f) - 8 - 8; + printf("\nBitTableSize = %#X\n", BitTableSize); if (BitTableSize > sizeof(BitTable)) { fclose(f); return KELF_ERROR_INVALID_BIT_TABLE_SIZE; @@ -126,10 +266,46 @@ int Kelf::LoadKelf(std::string filename) fread(&bitTable, 1, BitTableSize, f); TdesCbcCfb64Decrypt((uint8_t *)&bitTable, (uint8_t *)&bitTable, BitTableSize, (uint8_t *)Kbit.data(), 2, ks.GetContentTableIV().data()); + printf("bitTable.HeaderSize = %#X\n", bitTable.HeaderSize); + printf("bitTable.BlockCount = %d\n", bitTable.BlockCount); + printf("bitTable.gap ="); + for (unsigned int i = 0; i < 3; ++i) + printf(" %02X", (unsigned char)bitTable.gap[i]); + printf("\n Size Signature Flags\n"); + for (unsigned int i = 0; i < bitTable.BlockCount; ++i) { + printf(" bitTable.Blocks[%d] = %08X ", (int)i, bitTable.Blocks[i].Size); + for (size_t j = 0; j < 8; ++j) + printf("%02X", (unsigned char)bitTable.Blocks[i].Signature[j]); + switch (bitTable.Blocks[i].Flags) { + case 0: + printf(" 0 (not encrypted, not signed)\n"); + break; + case 1: + printf(" 1 (encrypted only)\n"); + break; + case 2: + printf(" 2 (signed only)\n"); + break; + case 3: + printf(" 3 (encrypted and signed)\n"); + break; + default: + printf(" %08X (unknown set of flags\n)", bitTable.Blocks[i].Flags); + printf("This value is unknown.\n"); + printf("Please upload file and post under that issue:\n"); + printf("https://github.com/xfwcfw/kelftool/issues/1\n"); + break; + } + } + std::string BitTableSignature; BitTableSignature.resize(8); fread(BitTableSignature.data(), 1, BitTableSignature.size(), f); + printf("BitTableSignature ="); + for (size_t i = 0; i < 8; ++i) + printf(" %02X", (unsigned char)BitTableSignature[i]); + printf("\n"); if (BitTableSignature != GetBitTableSignature()) { fclose(f); @@ -139,12 +315,15 @@ int Kelf::LoadKelf(std::string filename) std::string RootSignature; RootSignature.resize(8); fread(RootSignature.data(), 1, RootSignature.size(), f); - if (RootSignature != GetRootSignature(HeaderSignature, BitTableSignature)) { - fclose(f); - return KELF_ERROR_INVALID_ROOT_SIGNATURE; - } + printf("\nWARNING: RootSignature does not match ="); + for (size_t i = 0; i < 8; ++i) + printf(" %02X", (unsigned char)RootSignature[i]); + printf("\n"); + // fclose(f); + // return KELF_ERROR_INVALID_ROOT_SIGNATURE; + } for (int i = 0; i < bitTable.BlockCount; i++) { std::string Block; Block.resize(bitTable.Blocks[i].Size); @@ -155,6 +334,7 @@ int Kelf::LoadKelf(std::string filename) DecryptContent(header.Flags >> 4 & 3); if (VerifyContentSignature() != 0) { + printf("WARNING: VerifyContentSignature does not match\n"); fclose(f); return KELF_ERROR_INVALID_CONTENT_SIGNATURE; } @@ -164,48 +344,52 @@ int Kelf::LoadKelf(std::string filename) return 0; } -int Kelf::SaveKelf(std::string filename, int headerid) +int Kelf::SaveKelf(const std::string filename, int headerid) { FILE *f = fopen(filename.c_str(), "wb"); - + if (f == NULL) { + fprintf(stderr, "Couldn't open %s: %s\n", filename.c_str(), strerror(errno)); + return KELF_ERROR_UNSUPPORTED_FILE; + } KELFHeader header; - static uint8_t *PSX_USER; - - static uint8_t USER_HEADER_FMCB[] = {0x01, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4A, 0x00, 0x01, 0x02, 0x19, 0x00, 0x00, 0x00, 0x56}; - static uint8_t USER_HEADER_FHDB[] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x4A, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1B}; - static uint8_t USER_HEADER_MBR[] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x01, 0x57, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A}; + static uint8_t *USER_HEADER; switch (headerid) { case 0: - PSX_USER = USER_HEADER_FMCB; + USER_HEADER = USER_HEADER_FMCB; break; case 1: - PSX_USER = USER_HEADER_FHDB; + USER_HEADER = USER_HEADER_FHDB; break; case 2: - PSX_USER = USER_HEADER_MBR; + USER_HEADER = USER_HEADER_MBR; break; } - memcpy(header.UserDefined, PSX_USER, 16); - header.ContentSize = Content.size(); - header.HeaderSize = sizeof(KELFHeader) + 8 + 16 + 16 + 8 + 16 + 16 + 8 + 8; // header + header signature + kbit + kc + bittable + bittable signature + root signature - header.SystemType = SYSTEM_TYPE_PS2; - header.ApplicationType = 1; // 1 = xosdmain, 5 = dvdplayer kirx 7 = dvdplayer kelf - header.Flags = 0x22C; - header.BitCount = 0; - header.MGZones = 0xFF; // ?? + memcpy(header.UserDefined, USER_HEADER, 16); + header.ContentSize = Content.size(); // sometimes zero + header.HeaderSize = bitTable.HeaderSize; // header + header signature + kbit + kc + bittable + bittable signature + root signature + header.SystemType = SYSTEM_TYPE_PS2; // same for COH (arcade) + header.ApplicationType = 1; // 1 = xosdmain, 5 = dvdplayer kirx 7 = dvdplayer kelf 0xB - ?? 0x00 - ?? + // TODO: implement and check 3DES/1DES difference based on header.Flags. In both - encryption and decryption. + header.Flags = 0x022C; // ?? 00000010 00101100 binary, 0x021C for kirx + header.MGZones = 0xFF; // region bit, 1 - allowed + header.BitCount = 0; + // ?? balika, wisi: strange value, represents number of blacklisted iLinkID, ConsoleID + // iLinkID (8 bytes), consoleID (8 bytes) placed between header.MGZones and HeaderSignature + // it is part of header, so HeaderSignature and header.HeaderSize should be recalculated + + std::fill(header.gap, header.gap + 3, 0); std::string HeaderSignature = GetHeaderSignature(header); std::string BitTableSignature = GetBitTableSignature(); std::string RootSignature = GetRootSignature(HeaderSignature, BitTableSignature); int BitTableSize = (bitTable.BlockCount * 2 + 1) * 8; - - TdesCbcCfb64Encrypt((uint8_t *)&bitTable, (uint8_t *)&bitTable, (bitTable.BlockCount * 2 + 1) * 8, (uint8_t *)Kbit.data(), 2, ks.GetContentTableIV().data()); + TdesCbcCfb64Encrypt((uint8_t *)&bitTable, (uint8_t *)&bitTable, BitTableSize, (uint8_t *)Kbit.data(), 2, ks.GetContentTableIV().data()); std::string KEK = DeriveKeyEncryptionKey(header); EncryptKeys(KEK); @@ -224,52 +408,140 @@ int Kelf::SaveKelf(std::string filename, int headerid) return 0; } -int Kelf::LoadContent(std::string filename) +int Kelf::LoadContent(const std::string filename, int headerid) { FILE *f = fopen(filename.c_str(), "rb"); + if (f == NULL) { + fprintf(stderr, "Couldn't open %s: %s\n", filename.c_str(), strerror(errno)); + return KELF_ERROR_UNSUPPORTED_FILE; + } fseek(f, 0, SEEK_END); Content.resize(ftell(f)); fseek(f, 0, SEEK_SET); fread(Content.data(), 1, Content.size(), f); fclose(f); - // TODO: random kbit? + // TODO: encrypted Kbit hold some useful data + static uint8_t *USER_Kbit; + switch (headerid) { + case 0: + USER_Kbit = USER_Kbit_FMCB; + break; + case 1: + USER_Kbit = USER_Kbit_FHDB; + break; + case 2: + USER_Kbit = USER_Kbit_MBR; + break; + } + Kbit.resize(16); - memset(Kbit.data(), 0xAA, Kbit.size()); + memcpy(Kbit.data(), USER_Kbit, 16); - // TODO: random kc? + // TODO: encrypted Kc hold some useful data Kc.resize(16); - memset(Kc.data(), 0xBB, Kc.size()); + // memset(Kc.data(), 0xBB, Kc.size()); + + // memcpy(Kbit.data(), USER_Kc_MBR, 16); + std::fill(Kc.data(), Kc.data() + 16, 0xBB); + std::fill(bitTable.gap, bitTable.gap + 3, 0); + + // You can create your own sets of files + // BlockCount - will be number of blocks (0-255) + // Blocks[i].Flags can be any combination of BIT_BLOCK_SIGNED and BIT_BLOCK_ENCRYPTED flags (4 different sets) + // Blocks[i].Size - block size, should be division of 8 if BIT_BLOCK_ENCRYPTED is set + + // This is old kelftool block set + // bitTable.BlockCount = 2; + // bitTable.Blocks[0].Size = 0x20; + // bitTable.Blocks[0].Flags = BIT_BLOCK_SIGNED | BIT_BLOCK_ENCRYPTED; + // bitTable.Blocks[1].Size = Content.size() - bitTable.Blocks[0].Size; + // bitTable.Blocks[1].Flags = 0; + + // bitTable.BlockCount = 1; + // bitTable.Blocks[0].Size = 0x20; + // bitTable.Blocks[0].Flags = BIT_BLOCK_SIGNED; + + // bitTable.BlockCount = 6; + // bitTable.Blocks[0].Size = 0x40; + // bitTable.Blocks[0].Flags = 2; + // bitTable.Blocks[1].Size = 0x10; + // bitTable.Blocks[1].Flags = BIT_BLOCK_SIGNED | BIT_BLOCK_ENCRYPTED; + // bitTable.Blocks[2].Size = 0x100; + // bitTable.Blocks[2].Flags = BIT_BLOCK_ENCRYPTED; + // bitTable.Blocks[3].Size = 0x40; + // bitTable.Blocks[3].Flags = 0; + // bitTable.Blocks[4].Size = 0x10; + // bitTable.Blocks[4].Flags = BIT_BLOCK_SIGNED; + // bitTable.Blocks[5].Size = 0x100; + // bitTable.Blocks[5].Flags = BIT_BLOCK_ENCRYPTED; + + // fastest solution, 1 block without signing and encryption + bitTable.BlockCount = 1; + bitTable.Blocks[0].Size = Content.size(); + bitTable.Blocks[0].Flags = 0; - bitTable.HeaderSize = sizeof(KELFHeader) + 8 + 16 + 16 + 8 + 16 + 16 + 8 + 8; // header + header signature + kbit + kc + bittable + bittable signature + root signature - bitTable.BlockCount = 2; - bitTable.Blocks[0].Size = 0x20; - bitTable.Blocks[0].Flags = BIT_BLOCK_SIGNED | BIT_BLOCK_ENCRYPTED; - memset(bitTable.Blocks[0].Signature, 0, 8); + uint32_t offset = 0; + for (int i = 0; i < bitTable.BlockCount; ++i) { + // ignore last block defined size, and just use the rest of elf + // the same if current block reaches end of file + if ((i == bitTable.BlockCount - 1) || (offset + bitTable.Blocks[i].Size > Content.size())) { + bitTable.Blocks[i].Size = Content.size() - offset; + bitTable.BlockCount = i + 1; + // TODO: zero padding last block, 8 bytes if signed, 16 bytes if encrypted + } - // Sign - for (int j = 0; j < 0x20; j += 8) - xor_bit(&Content.data()[j], bitTable.Blocks[0].Signature, bitTable.Blocks[0].Signature, 8); + memset(bitTable.Blocks[i].Signature, 0, 8); - uint8_t MG_SIG_MASTER_AND_HASH_KEY[16]; - memcpy(MG_SIG_MASTER_AND_HASH_KEY, ks.GetSignatureMasterKey().data(), 8); - memcpy(MG_SIG_MASTER_AND_HASH_KEY + 8, ks.GetSignatureHashKey().data(), 8); + // Sign + if (bitTable.Blocks[i].Flags & BIT_BLOCK_SIGNED) { + if (!(bitTable.Blocks[i].Flags & BIT_BLOCK_ENCRYPTED)) { + // TODO: fix BIT_BLOCK_SIGNED alone support + // TODO: implement 1DES/3DES difference + printf("bitTable.Blocks[%d].Flags = BIT_BLOCK_SIGNED is not implemented during encryption. Encryption aborted.\n", i); + fclose(f); + return KELF_ERROR_UNSUPPORTED_FILE; + } + if (bitTable.Blocks[i].Size % 0x8) { + printf("bitTable.Blocks[%d].Size = %08X is not bounded to 0x8 (BIT_BLOCK_SIGNED). Encryption aborted.\n", i, bitTable.Blocks[i].Size); + fclose(f); + return KELF_ERROR_UNSUPPORTED_FILE; + } + for (unsigned int j = 0; j < bitTable.Blocks[i].Size; j += 8) + xor_bit(&Content.data()[offset + j], bitTable.Blocks[i].Signature, bitTable.Blocks[i].Signature, 8); - TdesCbcCfb64Encrypt(bitTable.Blocks[0].Signature, bitTable.Blocks[0].Signature, 8, MG_SIG_MASTER_AND_HASH_KEY, 2, MG_IV_NULL); + uint8_t MG_SIG_MASTER_AND_HASH_KEY[16]; + memcpy(MG_SIG_MASTER_AND_HASH_KEY, ks.GetSignatureMasterKey().data(), 8); + memcpy(MG_SIG_MASTER_AND_HASH_KEY + 8, ks.GetSignatureHashKey().data(), 8); - // Encrypt - TdesCbcCfb64Encrypt(Content.data(), Content.data(), 0x20, Kc.data(), 2, ks.GetContentIV().data()); + TdesCbcCfb64Encrypt(bitTable.Blocks[i].Signature, bitTable.Blocks[i].Signature, 8, MG_SIG_MASTER_AND_HASH_KEY, 2, MG_IV_NULL); + } - bitTable.Blocks[1].Size = Content.size() - 0x20; - bitTable.Blocks[1].Flags = 0; - memset(bitTable.Blocks[1].Signature, 0, 8); + // Encrypt + if (bitTable.Blocks[i].Flags & BIT_BLOCK_ENCRYPTED) { + if (bitTable.Blocks[i].Size % 0x10) { + printf("bitTable.Blocks[%d].Size = %08X is not bounded to 0x10 (BIT_BLOCK_ENCRYPTED). Encryption aborted.\n", i, bitTable.Blocks[i].Size); + fclose(f); + return KELF_ERROR_UNSUPPORTED_FILE; + } + TdesCbcCfb64Encrypt(&Content.data()[offset], &Content.data()[offset], bitTable.Blocks[i].Size, Kc.data(), 2, ks.GetContentIV().data()); + } + // if we reach the end of file + offset += bitTable.Blocks[i].Size; + } + + bitTable.HeaderSize = sizeof(KELFHeader) + 8 + 16 + 16 + (bitTable.BlockCount * 2 + 1) * 8 + 8 + 8; // header + header signature + kbit + kc + bittable (2 blocks) + bittable signature + root signature return 0; } -int Kelf::SaveContent(std::string filename) +int Kelf::SaveContent(const std::string filename) { FILE *f = fopen(filename.c_str(), "wb"); + if (f == NULL) { + fprintf(stderr, "Couldn't open %s: %s\n", filename.c_str(), strerror(errno)); + return KELF_ERROR_UNSUPPORTED_FILE; + } fwrite(Content.data(), 1, Content.size(), f); fclose(f); @@ -305,7 +577,7 @@ std::string Kelf::DeriveKeyEncryptionKey(KELFHeader &header) return std::string((char *)KEK, 16); } -void Kelf::DecryptKeys(std::string KEK) +void Kelf::DecryptKeys(const std::string KEK) { TdesCbcCfb64Decrypt((uint8_t *)Kbit.data(), (uint8_t *)Kbit.data(), 8, (uint8_t *)KEK.data(), 2, MG_IV_NULL); TdesCbcCfb64Decrypt((uint8_t *)Kbit.data() + 8, (uint8_t *)Kbit.data() + 8, 8, (uint8_t *)KEK.data(), 2, MG_IV_NULL); @@ -314,7 +586,7 @@ void Kelf::DecryptKeys(std::string KEK) TdesCbcCfb64Decrypt((uint8_t *)Kc.data() + 8, (uint8_t *)Kc.data() + 8, 8, (uint8_t *)KEK.data(), 2, MG_IV_NULL); } -void Kelf::EncryptKeys(std::string KEK) +void Kelf::EncryptKeys(const std::string KEK) { TdesCbcCfb64Encrypt((uint8_t *)Kbit.data(), (uint8_t *)Kbit.data(), 8, (uint8_t *)KEK.data(), 2, MG_IV_NULL); TdesCbcCfb64Encrypt((uint8_t *)Kbit.data() + 8, (uint8_t *)Kbit.data() + 8, 8, (uint8_t *)KEK.data(), 2, MG_IV_NULL); @@ -396,13 +668,41 @@ int Kelf::VerifyContentSignature() std::string SigMasterEnc; SigMasterEnc.resize(bitTable.Blocks[i].Size); TdesCbcCfb64Encrypt(SigMasterEnc.data(), &Content.data()[offset], bitTable.Blocks[i].Size, ks.GetSignatureMasterKey().data(), 1, MG_IV_NULL); + // printf("SigMasterEnc.data() = "); + // for (unsigned int j = 0; j < 8; ++j) + // printf(" %02X", (unsigned char)SigMasterEnc.data()[j]); + // printf("\n"); + memcpy(signature, &SigMasterEnc.data()[bitTable.Blocks[i].Size - 8], 8); + // printf("signature = "); + // for (unsigned int j = 0; j < 8; ++j) + // printf(" %02X", (unsigned char)signature[j]); + // printf("\n"); + TdesCbcCfb64Decrypt(signature, signature, 8, ks.GetSignatureHashKey().data(), 1, MG_IV_NULL); + // printf("signature = "); + // for (unsigned int j = 0; j < 8; ++j) + // printf(" %02X", (unsigned char)signature[j]); + // printf("\n"); + TdesCbcCfb64Encrypt(signature, signature, 8, ks.GetSignatureMasterKey().data(), 1, MG_IV_NULL); + printf("signature = "); + for (unsigned int j = 0; j < 8; ++j) + printf(" %02X", (unsigned char)signature[j]); + printf("\n"); } - if (memcmp(bitTable.Blocks[i].Signature, signature, 8) != 0) + if (memcmp(bitTable.Blocks[i].Signature, signature, 8) != 0) { + printf("bitTable.Blocks[%u].Signature = ", i); + for (unsigned int j = 0; j < 8; ++j) + printf(" %02X", (unsigned char)bitTable.Blocks[i].Signature[j]); + printf("\n"); + printf("Signature calculated = "); + for (unsigned int j = 0; j < 8; ++j) + printf(" %02X", (unsigned char)signature[j]); + printf("\n"); return KELF_ERROR_INVALID_CONTENT_SIGNATURE; + } } offset += bitTable.Blocks[i].Size; diff --git a/src/kelf.h b/src/kelf.h index f6632e7..281e020 100644 --- a/src/kelf.h +++ b/src/kelf.h @@ -30,36 +30,81 @@ #define SYSTEM_TYPE_PS2 0 // same for COH (arcade) #define SYSTEM_TYPE_PSX 1 +static uint8_t USER_HEADER_FMCB[16] = {0x01, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4A, 0x00, 0x01, 0x02, 0x19, 0x00, 0x00, 0x00, 0x56}; +static uint8_t USER_HEADER_FHDB[16] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x4A, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1B}; +static uint8_t USER_HEADER_MBR[16] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x01, 0x57, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A}; + +// static uint8_t USER_Kc_MBR[16] = {0xD2 ,0xC6 ,0x8F ,0xC2 ,0xEB ,0xA0 ,0x5B ,0x63 ,0x3F ,0x0B ,0xF8 ,0x7B ,0x46 ,0x0E ,0x0D ,0x93}; +// static uint8_t USER_Kbit_MBR[16] = {0x83 ,0x1E ,0x4E ,0x4B ,0x42 ,0xCA ,0x7F ,0x39 ,0x0C ,0xB7 ,0xC5 ,0xFB ,0x81 ,0xB1 ,0x10 ,0xDA}; +static uint8_t USER_Kbit_MBR[16] = {0x82, 0xf0, 0x29, 0xad, 0xe9, 0x53, 0x23, 0xf5, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; +static uint8_t USER_Kbit_FHDB[16] = {0x40, 0xe9, 0x80, 0x4d, 0x2e, 0x92, 0xb0, 0xa8, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; +static uint8_t USER_Kbit_FMCB[16] = {0xd9, 0x4a, 0x2e, 0x56, 0x01, 0x6e, 0xa7, 0x31, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; + #pragma pack(push, 1) struct KELFHeader { uint8_t UserDefined[16]; - uint32_t ContentSize; // Sometimes not... + uint32_t ContentSize; // balika: Sometimes not... uint16_t HeaderSize; uint8_t SystemType; uint8_t ApplicationType; uint16_t Flags; uint16_t BitCount; - uint32_t MGZones; + uint8_t MGZones; + uint8_t gap[3]; // always zero + // struct IDList + // { + // uint64_t iLinkID; + // uint64_t consoleID; + // } Blocks[16]; }; -#define BIT_BLOCK_ENCRYPTED 1 -#define BIT_BLOCK_SIGNED 2 +// possible BitBlock.Flags. Other bit flags should be unset +#define HDR_FLAG0_BLACKLIST 0x1 // Unset. if set then BitCount should be non-zero, and header will change its size +#define HDR_FLAG1_WHITELIST 0x2 // Unset. maybe whitelist? ICVPS2 ?? +#define HDR_FLAG2 0x4 // Set. ?? +#define HDR_FLAG3 0x8 // Set. ?? +#define HDR_FLAG4_1DES 0x10 // Set in kirx. HDR_FLAG4_3DES should be unset. Represents Single DES encryption +#define HDR_FLAG4_3DES 0x20 // Set in kelf. HDR_FLAG4_1DES should be unset. Represents Single DES encryption +#define HDR_FLAG6 0x40 // Unset. ?? +#define HDR_FLAG7 0x80 // Unset. ?? +#define HDR_FLAG8 0x100 // Unset. ?? +#define HDR_FLAG9 0x200 // Unset. ?? +#define HDR_FLAG10 0x400 // Set. ?? +#define HDR_FLAG11 0x800 // Unset. ?? +#define HDR_FLAG12 0x1000 // Unset. ?? +#define HDR_FLAG13 0x2000 // Unset. ?? +#define HDR_FLAG14 0x4000 // Unset. ?? +#define HDR_FLAG15 0x8000 // Unset. ?? + +// MGZones region flags. If unset - blocked in that region +#define REGION_JP 0x1 // Japan +#define REGION_NA 0x2 // North America +#define REGION_EU 0x4 // Europe +#define REGION_AU 0x8 // Australia +#define REGION_ASIA 0x10 // Asia +#define REGION_RU 0x20 // Russia +#define REGION_CH 0x40 // China +#define REGION_MX 0x80 // Mexico struct BitTable { uint32_t HeaderSize; uint8_t BlockCount; - uint8_t gap[3]; + uint8_t gap[3]; // always zero struct BitBlock { uint32_t Size; - uint32_t Flags; + uint32_t Flags; // bits 2-8 always zero uint8_t Signature[8]; } Blocks[256]; }; +// possible BitBlock Flags. +#define BIT_BLOCK_ENCRYPTED 0x1 +#define BIT_BLOCK_SIGNED 0x2 + #pragma pack(pop) class Kelf @@ -71,14 +116,14 @@ class Kelf std::string Content; public: - Kelf(KeyStore &_ks) + explicit Kelf(KeyStore &_ks) : ks(_ks) { } int LoadKelf(std::string filename); int SaveKelf(std::string filename, int header); - int LoadContent(std::string filename); + int LoadContent(std::string filename, int header); int SaveContent(std::string filename); std::string GetHeaderSignature(KELFHeader &header); diff --git a/src/kelftool.cpp b/src/kelftool.cpp index 83350e5..89b74df 100644 --- a/src/kelftool.cpp +++ b/src/kelftool.cpp @@ -20,9 +20,11 @@ #include "keystore.h" #include "kelf.h" +// TODO: implement load/save kelf header configuration for byte-perfect encryption, decryption + std::string getKeyStorePath() { -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) return std::string(getenv("HOME")) + "/PS2KEYS.dat"; #else return std::string(getenv("USERPROFILE")) + "\\PS2KEYS.dat"; @@ -39,8 +41,12 @@ int decrypt(int argc, char **argv) KeyStore ks; int ret = ks.Load(getKeyStorePath()); if (ret != 0) { - printf("Failed to load keystore: %d - %s\n", ret, KeyStore::getErrorString(ret).c_str()); - return ret; + // try to load keys from working directory + ret = ks.Load("./PS2KEYS.dat"); + if (ret != 0) { + printf("Failed to load keystore: %d - %s\n", ret, KeyStore::getErrorString(ret).c_str()); + return ret; + } } Kelf kelf(ks); @@ -65,7 +71,7 @@ int encrypt(int argc, char **argv) if (argc < 4) { printf("%s encrypt \n", argv[0]); - printf(": fmcb,fhdb, mbr\n"); + printf(": fmcb, fhdb, mbr\n"); return -1; } @@ -87,12 +93,16 @@ int encrypt(int argc, char **argv) KeyStore ks; int ret = ks.Load(getKeyStorePath()); if (ret != 0) { - printf("Failed to load keystore: %d - %s\n", ret, KeyStore::getErrorString(ret).c_str()); - return ret; + // try to load keys from working directory + ret = ks.Load("./PS2KEYS.dat"); + if (ret != 0) { + printf("Failed to load keystore: %d - %s\n", ret, KeyStore::getErrorString(ret).c_str()); + return ret; + } } Kelf kelf(ks); - ret = kelf.LoadContent(argv[2]); + ret = kelf.LoadContent(argv[2], headerid); if (ret != 0) { printf("Failed to LoadContent!\n"); return ret; @@ -114,6 +124,12 @@ int main(int argc, char **argv) printf("Available submodules:\n"); printf("\tdecrypt - decrypt and check signature of kelf files\n"); printf("\tencrypt - encrypt and sign kelf files : fmcb, fhdb, mbr\n"); + printf("\t\tfmcb - for retail PS2 memory cards\n"); + printf("\t\tfhdb - for retail PS2 HDD (HDD OSD / BB Navigator)\n"); + printf("\t\tmbr - for retail PS2 HDD (mbr injection).\n"); + printf("\t\t Note: for mbr elf should load from 0x100000 and should be without headers:\n"); + printf("\t\t readelf -h should show 0x100000 or 0x100008\n"); + printf("\t\t $(EE_OBJCOPY) -O binary -v \n"); return -1; } diff --git a/src/keystore.cpp b/src/keystore.cpp index 4f4dd23..1feed52 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -116,7 +116,7 @@ std::string KeyStore::getErrorString(int err) case KEYSTORE_ERROR_OPEN_FAILED: return "Failed to open keystore!"; case KEYSTORE_ERROR_LINE_NOT_KEY_VALUE: - return "Line in the keystore file is not key-value pair!"; + return "Line in the keystore file is not key=value pair!"; case KEYSTORE_ERROR_ODD_LEN_VALUE: return "Odd length hex value in keystore!"; case KEYSTORE_ERROR_MISSING_KEY: