From ba926299005301060792b53180126576c5ac4e2d Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Tue, 3 Jan 2017 02:32:39 -0800 Subject: [PATCH] Restore certificate check with re-written ECC code --- .gitmodules | 3 - CMakeLists.txt | 10 +- extern/CMakeLists.txt | 1 - extern/easy-ecc | 1 - include/athena/WiiSaveReader.hpp | 2 +- include/bn.hpp | 15 ++ include/ec.hpp | 13 ++ src/athena/WiiSaveReader.cpp | 25 +-- src/athena/WiiSaveWriter.cpp | 14 +- src/bn.cpp | 98 ++++++++++ src/ec.cpp | 320 +++++++++++++++++++++++++++++++ 11 files changed, 466 insertions(+), 36 deletions(-) delete mode 160000 extern/easy-ecc create mode 100644 include/bn.hpp create mode 100644 include/ec.hpp create mode 100644 src/bn.cpp create mode 100644 src/ec.cpp diff --git a/.gitmodules b/.gitmodules index 4226383..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "easy-ecc"] - path = extern/easy-ecc - url = https://github.com/libAthena/easy-ecc.git diff --git a/CMakeLists.txt b/CMakeLists.txt index b3c37b4..bbb8636 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(ATHENA_VERSION add_subdirectory(extern) -include_directories(include ${LZO_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${EASYECC_INCLUDE_DIR}) +include_directories(include ${LZO_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) if (NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") @@ -103,8 +103,8 @@ add_library(athena-wiisave EXCLUDE_FROM_ALL src/athena/WiiSave.cpp src/athena/WiiSaveReader.cpp src/athena/WiiSaveWriter.cpp -# src/bn.cpp -# src/ec.cpp + src/bn.cpp + src/ec.cpp src/md5.cpp src/sha1.cpp src/aes.cpp @@ -116,8 +116,8 @@ add_library(athena-wiisave EXCLUDE_FROM_ALL include/athena/WiiSaveReader.hpp include/athena/WiiSaveWriter.hpp include/aes.hpp -# include/bn.h -# include/ec.h + include/bn.hpp + include/ec.hpp include/md5.h include/sha1.h ) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 4600c53..211f1c0 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -1,4 +1,3 @@ add_subdirectory(lzo) add_subdirectory(zlib) add_subdirectory(yaml) -add_subdirectory(easy-ecc) diff --git a/extern/easy-ecc b/extern/easy-ecc deleted file mode 160000 index da27485..0000000 --- a/extern/easy-ecc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit da2748507cbe429d3e1be918c54cfba3be06d4b2 diff --git a/include/athena/WiiSaveReader.hpp b/include/athena/WiiSaveReader.hpp index 1c15242..9cf9a6d 100644 --- a/include/athena/WiiSaveReader.hpp +++ b/include/athena/WiiSaveReader.hpp @@ -21,7 +21,7 @@ namespace io * all work is done using a memory buffer, and not read directly from the disk. * \sa BinaryReader */ -class WiiSaveReader : protected MemoryCopyReader +class WiiSaveReader : public MemoryCopyReader { MEMORYCOPYREADER_BASE(); public: diff --git a/include/bn.hpp b/include/bn.hpp new file mode 100644 index 0000000..a3c1d43 --- /dev/null +++ b/include/bn.hpp @@ -0,0 +1,15 @@ +#ifndef BN_H +#define BN_H + +#include "athena/Types.hpp" +namespace bignum +{ +int compare(const atUint8* a, const atUint8* b, atUint32 n); +void subModulus(atUint8* a, const atUint8* N, atUint32 n); +void add(atUint8* d, atUint8* a, const atUint8* b, const atUint8* N, atUint32 n); +void mul(atUint8* d, atUint8* a, const atUint8* b, const atUint8* N, atUint32 n); +void exp(atUint8* d, const atUint8* a, const atUint8* N, atUint32 n, atUint8* e, atUint32 en); +void inv(atUint8* d, atUint8* a, const atUint8* N, atUint32 n); +} + +#endif // BN_H diff --git a/include/ec.hpp b/include/ec.hpp new file mode 100644 index 0000000..8a9b42c --- /dev/null +++ b/include/ec.hpp @@ -0,0 +1,13 @@ +#ifndef EC_H +#define EC_H +#include "athena/Types.hpp" + +namespace ecc +{ +void checkEC(atUint8* ng, atUint8* ap, atUint8* sig, atUint8* sigHash, bool& apValid, bool& ngValid); +void makeECCert(atUint8* cert, atUint8* sig, const char* signer, const char* name, atUint8* priv, atUint32 keyId); +void createECDSA(atUint8* R, atUint8* S, atUint8* k, atUint8* hash); +} + +#endif // EC_H + diff --git a/src/athena/WiiSaveReader.cpp b/src/athena/WiiSaveReader.cpp index 1bbf069..2dfaf1c 100644 --- a/src/athena/WiiSaveReader.cpp +++ b/src/athena/WiiSaveReader.cpp @@ -7,7 +7,7 @@ #include "athena/FileWriter.hpp" #include "md5.h" #include "aes.hpp" -#include "ecc.h" +#include "ec.hpp" #include "sha1.h" #include #include @@ -294,7 +294,6 @@ WiiFile* WiiSaveReader::readFile() void WiiSaveReader::readCerts(atUint32 totalSize) { -#if 0 std::cout << "Reading certs..." << std::endl; atUint32 dataSize = totalSize - 0x340; std::unique_ptr sig = base::readUBytes(0x40); @@ -307,22 +306,14 @@ void WiiSaveReader::readCerts(atUint32 totalSize) std::cout << "validating..." << std::endl; hash = getSha1(data.get(), dataSize); atUint8* hash2 = getSha1(hash, 20); - bool failed = false; + bool ngValid = false; + bool apValid = false; + ecc::checkEC(ngCert.get(), apCert.get(), sig.get(), hash2, apValid, ngValid); - if (!ecdsa_verify(ngCert.get(), hash, sig.get())) - { - std::cout << "NGCert failure" << std::endl; - failed = true; - } - if (!ecdsa_verify(apCert.get(), hash2, sig.get())) - { - std::cout << "APCert failure" << std::endl; - failed = true; - } - - if (!failed) - std::cout << "certificates ok" << std::endl; -#endif + if (apValid) + std::cout << "AP Certificate ok" << std::endl; + if (ngValid) + std::cout << "NG Certificate ok" << std::endl; } WiiFile* WiiSaveReader::buildTree(std::vector files) diff --git a/src/athena/WiiSaveWriter.cpp b/src/athena/WiiSaveWriter.cpp index 5c48906..7ffc239 100644 --- a/src/athena/WiiSaveWriter.cpp +++ b/src/athena/WiiSaveWriter.cpp @@ -10,7 +10,7 @@ #include "athena/Utility.hpp" #include "aes.hpp" -#include "ecc.h" +#include "ec.hpp" #include "md5.h" #include "sha1.h" @@ -194,7 +194,6 @@ void WiiSaveWriter::writeImage(WiiImage* image) void WiiSaveWriter::writeCerts(atUint32 filesSize, atUint32 ngId, atUint8* ngPriv, atUint8* ngSig, atUint32 ngKeyId) { -#if 0 atUint8 sig[0x40]; atUint8 ngCert[0x180]; atUint8 apCert[0x180]; @@ -208,7 +207,7 @@ void WiiSaveWriter::writeCerts(atUint32 filesSize, atUint32 ngId, atUint8* ngPri sprintf(signer, "Root-CA00000001-MS00000002"); sprintf(name, "NG%08x", ngId); - make_ec_cert(ngCert, ngSig, signer, name, ngPriv, ngKeyId); + ecc::makeECCert(ngCert, ngSig, signer, name, ngPriv, ngKeyId); memset(apPriv, 0, 30); apPriv[10] = 1; @@ -217,11 +216,11 @@ void WiiSaveWriter::writeCerts(atUint32 filesSize, atUint32 ngId, atUint8* ngPri sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ngId); sprintf(name, "AP%08x%08x", 1, 2); - make_ec_cert(apCert, apSig, signer, name, apPriv, 0); + ecc::makeECCert(apCert, apSig, signer, name, apPriv, 0); hash = getSha1(apCert + 0x80, 0x100); - generate_ecdsa(apSig, apSig + 30, ngPriv, hash); - make_ec_cert(apCert, apSig, signer, name, apPriv, 0); + ecc::createECDSA(apSig, apSig + 30, ngPriv, hash); + ecc::makeECCert(apCert, apSig, signer, name, apPriv, 0); delete[] hash; dataSize = filesSize + 0x80; @@ -234,7 +233,7 @@ void WiiSaveWriter::writeCerts(atUint32 filesSize, atUint32 ngId, atUint8* ngPri delete[] hash; delete[] data; - generate_ecdsa(sig, sig + 30, apPriv, hash2); + ecc::createECDSA(sig, sig + 30, apPriv, hash2); int stuff = 0x2f536969; if (!utility::isSystemBigEndian()) @@ -246,7 +245,6 @@ void WiiSaveWriter::writeCerts(atUint32 filesSize, atUint32 ngId, atUint8* ngPri base::writeBytes((atInt8*)sig, 0x40); base::writeBytes((atInt8*)ngCert, 0x180); base::writeBytes((atInt8*)apCert, 0x180); -#endif } } // io diff --git a/src/bn.cpp b/src/bn.cpp new file mode 100644 index 0000000..a4d0a62 --- /dev/null +++ b/src/bn.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include "bn.hpp" + +namespace bignum +{ + +void subModulus(atUint8* a, const atUint8* N, atUint32 n) +{ + atUint8 c = 0; + + for (atUint32 i = n - 1; i < n; i--) + { + atUint32 dig = N[i] + c; + c = (a[i] < dig); + a[i] -= dig; + } +} + +void add(atUint8* d, atUint8* a, const atUint8* b, const atUint8* N, atUint32 n) +{ + atUint8 c = 0; + + for (atUint32 i = n - 1; i < n; i--) + { + atUint32 dig = a[i] + b[i] + c; + c = (dig >= 0x100); + d[i] = dig; + } + + if (c) + subModulus(d, N, n); + + if (compare(d, N, n) >= 0) + subModulus(d, N, n); +} + +void mul(atUint8* d, atUint8* a, const atUint8* b, const atUint8* N, atUint32 n) +{ + memset(d, 0, n); + + for (atUint32 i = 0; i < n; i++) + { + for (atUint8 mask = 0x80; mask != 0; mask >>= 1) + { + add(d, d, d, N, n); + + if ((a[i] & mask) != 0) + add(d, d, b, N, n); + } + } +} + +void exp(atUint8* d, const atUint8* a, const atUint8* N, atUint32 n, atUint8* e, atUint32 en) +{ + atUint8 t[512]; + memset(d, 0, n); + d[n - 1] = 1; + + for (atUint32 i = 0; i < en; i++) + { + for (atUint8 mask = 0x80; mask != 0; mask >>= 1) + { + mul(t, d, d, N, n); + + if ((e[i] & mask) != 0) + mul(d, t, a, N, n); + else + memcpy(d, t, n); + } + } +} + +void inv(atUint8* d, atUint8* a, const atUint8* N, atUint32 n) +{ + atUint8 t[512], s[512]; + + memcpy(t, N, n); + memset(s, 0, n); + s[n - 1] = 2; + subModulus(t, s, n); + exp(d, a, N, n, t, n); +} + +int compare(const atUint8* a, const atUint8* b, atUint32 n) +{ + for (atUint32 i = 0; i < n; i++) + { + if (a[i] < b[i]) + return -1; + + if (a[i] > b[i]) + return 1; + } + return 0; +} +} diff --git a/src/ec.cpp b/src/ec.cpp new file mode 100644 index 0000000..7241827 --- /dev/null +++ b/src/ec.cpp @@ -0,0 +1,320 @@ +#include +#include "athena/Utility.hpp" + +#include "bn.hpp" +#include "ec.hpp" +#include "sha1.h" + +namespace ecc +{ +static int checkZero(const atUint8* d) { return !memcmp(d, d + 1, 29) && d[0] == 0; } + +static void add(atUint8* d, const atUint8* a, const atUint8* b) +{ + for (atUint32 i = 0; i < 30; i++) + d[i] = a[i] ^ b[i]; +} + +static void multiply(atUint8* d, const atUint8* a, const atUint8* b) +{ + memset(d, 0, 30); + for (atUint32 n = 0, i = 0, mask = 1; n < 233; n++) + { + atUint8 x, y; + atUint8 carry = d[0] & 1; + x = 0; + for (atUint32 i = 0; i < 29; i++) + { + y = d[i + 1]; + d[i] = x ^ (y >> 7); + x = y << 1; + } + + d[29] = x ^ carry; + d[20] ^= carry << 2; + if ((a[i] & mask) != 0) + add(d, d, b); + + mask >>= 1; + if (mask == 0) + { + mask = 0x80; + i++; + } + } +} + +static void squareToWide(atUint8* d, const atUint8* a) +{ + static const atUint8 sq[16] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15, + 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55}; + for (atUint32 i = 0; i < 30; i++) + { + d[2 * i] = sq[a[i] >> 4]; + d[2 * i + 1] = sq[a[i] & 15]; + } +} + +static void wideReduce(atUint8* d) +{ + atUint32 i; + atUint8 x; + + for (i = 0; i < 30; i++) + { + x = d[i]; + + d[i + 19] ^= x >> 7; + d[i + 20] ^= x << 1; + d[i + 29] ^= x >> 1; + d[i + 30] ^= x << 7; + } + + x = d[30] & ~1; + d[49] ^= x >> 7; + d[50] ^= x << 1; + d[59] ^= x >> 1; + d[30] &= 1; +} + +static void square(atUint8* d, const atUint8* a) +{ + atUint8 wide[60]; + + squareToWide(wide, a); + wideReduce(wide); + + memcpy(d, wide + 30, 30); +} + +static void itInvert(atUint8* d, const atUint8* a, const atUint8* b, atUint32 j) +{ + atUint8 t[30]; + memcpy(t, a, 32); + while (j--) + { + square(d, t); + memcpy(t, d, 30); + } + multiply(d, t, b); +} + +static void invert(atUint8* d, const atUint8* a) +{ + atUint8 t[30]; + atUint8 s[30]; + + itInvert(t, a, a, 1); + itInvert(s, t, a, 1); + itInvert(t, s, s, 3); + itInvert(s, t, a, 1); + itInvert(t, s, s, 7); + itInvert(s, t, t, 14); + itInvert(t, s, a, 1); + itInvert(s, t, t, 29); + itInvert(t, s, s, 58); + itInvert(s, t, t, 116); + square(d, s); +} + +static void pointDouble(atUint8* r, const atUint8* p) +{ + atUint8 s[30], t[30]; + const atUint8* px = p; + const atUint8* py = p + 30; + atUint8* rx = r; + atUint8* ry = r + 30; + + if (checkZero(px)) + { + memset(rx, 0, 30); + memset(ry, 0, 30); + + return; + } + + invert(t, px); + multiply(s, py, t); + add(s, s, px); + + square(t, px); + + square(rx, s); + add(rx, rx, s); + rx[29] ^= 1; + + multiply(ry, s, rx); + add(ry, ry, rx); + add(ry, ry, t); +} + +static void pointAdd(atUint8* r, const atUint8* p, const atUint8* q) +{ + atUint8 s[30], t[30], u[30]; + const atUint8* px = p; + const atUint8* py = p + 30; + const atUint8* qx = q; + const atUint8* qy = q + 30; + atUint8* rx = r; + atUint8* ry = r + 30; + + if (checkZero(p) && checkZero(p + 30)) + { + memcpy(rx, qx, 30); + memcpy(ry, qy, 30); + return; + } + + if (checkZero(p) && checkZero(p + 30)) + { + memcpy(rx, px, 30); + memcpy(ry, py, 30); + return; + } + + add(u, px, qx); + + if (checkZero(u)) + { + add(u, py, qy); + + if (checkZero(u)) + pointDouble(r, p); + else + { + memset(rx, 0, 30); + memset(ry, 0, 30); + } + + return; + } + + invert(t, u); + add(u, py, qy); + multiply(s, t, u); + + square(t, s); + add(t, t, s); + add(t, t, qx); + t[29] ^= 1; + + multiply(u, s, t); + add(s, u, py); + add(rx, t, px); + add(ry, s, rx); +} + +static void pointMultiply(atUint8* d, const atUint8* a, const atUint8* b) +{ + memset(d, 0, 30); + memset(d + 30, 0, 30); + for (atUint32 i = 0; i < 30; i++) + { + for (atUint8 mask = 0x80; mask != 0; mask >>= 1) + { + pointDouble(d, d); + + if ((a[i] & mask) != 0) + pointAdd(d, d, b); + } + } +} + +static const atUint8 ecG[60] = {0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1, + 0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, + 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, + 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef, + 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52}; + +static const atUint8 ecN[30] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xe9, 0x74, 0xe7, 0x2f, + 0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7}; + +bool checkECDSA(atUint8* Q, atUint8* R, atUint8* S, atUint8* hash) +{ + atUint8 Sinv[30]; + atUint8 e[30]; + atUint8 w1[30], w2[30]; + atUint8 r1[60], r2[60]; + + bignum::inv(Sinv, S, ecN, 30); + + memset(e, 0, 30); + memcpy(e + 10, hash, 20); + + bignum::mul(w1, e, Sinv, ecN, 30); + bignum::mul(w2, R, Sinv, ecN, 30); + + pointMultiply(r1, w1, ecG); + pointMultiply(r2, w2, Q); + + pointAdd(r1, r1, r2); + + if (bignum::compare(r1, ecN, 30) >= 0) + bignum::subModulus(r1, ecN, 30); + + return (bignum::compare(r1, R, 30) == 0); +} + +void makeECCert(atUint8* cert, atUint8* sig, const char* signer, const char* name, atUint8* priv, atUint32 keyId) +{ + memset(cert, 0, 0x180); + *(atUint32*)(cert) = 0x10002; + + if (!athena::utility::isSystemBigEndian()) + *(atUint32*)(cert) = athena::utility::swapU32(*(atUint32*)(cert)); + + memcpy((char*)cert + 4, sig, 60); + strcpy((char*)cert + 0x80, signer); + *(atUint32*)(cert + 0xc0) = 2; + + if (!athena::utility::isSystemBigEndian()) + *(atUint32*)(cert + 0xc0) = athena::utility::swapU32(*(atUint32*)(cert + 0xc0)); + + strcpy((char*)cert + 0xc4, name); + *(atUint32*)(cert + 0x104) = keyId; + + if (!athena::utility::isSystemBigEndian()) + *(atUint32*)(cert + 0x104) = athena::utility::swapU32(*(atUint32*)(cert + 0x104)); + + pointMultiply(cert + 0x108, priv, ecG); +} + +void createECDSA(atUint8* R, atUint8* S, atUint8* k, atUint8* hash) +{ + atUint8 e[30]; + atUint8 kk[30]; + atUint8 m[30]; + atUint8 minv[30]; + atUint8 mG[60]; + + memset(e, 0, 30); + memcpy(e + 10, hash, 20); + + athena::utility::fillRandom(m, sizeof(m)); + m[0] = 0; + + pointMultiply(mG, m, ecG); + memcpy(R, mG, 30); + + if (bignum::compare(R, ecN, 30) >= 0) + bignum::subModulus(R, ecN, 30); + + memcpy(kk, k, 30); + + if (bignum::compare(kk, ecN, 30) >= 0) + bignum::subModulus(kk, ecN, 30); + + bignum::mul(S, R, kk, ecN, 30); + bignum::add(kk, S, e, ecN, 30); + bignum::inv(minv, m, ecN, 30); + bignum::mul(S, minv, kk, ecN, 30); +} + +void checkEC(atUint8* ng, atUint8* ap, atUint8* sig, atUint8* sigHash, bool& apValid, bool& ngValid) +{ + atUint8* apHash = getSha1(ap + 0x80, 0x100); + ngValid = checkECDSA(ng + 0x0108, ap + 0x04, ap + 0x22, apHash); + apValid = checkECDSA(ap + 0x0108, sig, sig + 30, sigHash); +} +}