From 0aebf43f4bb76904ba8b9c772c882185e2f27d60 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 30 Jul 2018 03:18:02 +0800 Subject: [PATCH] [nstool|es] Add cert validation. --- lib/libes/include/es/SignUtils.h | 14 +++++ lib/libes/include/es/SignatureBlock.h | 6 +- lib/libes/include/es/sign.h | 27 ++++++--- lib/libes/source/SignUtils.cpp | 45 ++++++++++++++ lib/libes/source/SignatureBlock.cpp | 44 +++++++------- programs/nstool/source/EsCertProcess.cpp | 77 +++++++++++++++++++----- programs/nstool/source/EsCertProcess.h | 4 +- programs/nstool/source/EsTikProcess.cpp | 24 ++++---- programs/nstool/source/UserSettings.cpp | 4 +- 9 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 lib/libes/include/es/SignUtils.h create mode 100644 lib/libes/source/SignUtils.cpp diff --git a/lib/libes/include/es/SignUtils.h b/lib/libes/include/es/SignUtils.h new file mode 100644 index 0000000..26996a2 --- /dev/null +++ b/lib/libes/include/es/SignUtils.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +namespace es +{ + +namespace sign +{ + es::sign::SignatureAlgo getSignatureAlgo(es::sign::SignatureId sign_id); + es::sign::HashAlgo getHashAlgo(es::sign::SignatureId sign_id); +} + +} \ No newline at end of file diff --git a/lib/libes/include/es/SignatureBlock.h b/lib/libes/include/es/SignatureBlock.h index 46d5c98..111d5d7 100644 --- a/lib/libes/include/es/SignatureBlock.h +++ b/lib/libes/include/es/SignatureBlock.h @@ -24,8 +24,8 @@ namespace es // variables void clear(); - es::sign::SignType getSignType() const; - void setSignType(es::sign::SignType type); + es::sign::SignatureId getSignType() const; + void setSignType(es::sign::SignatureId type); bool isLittleEndian() const; void setLittleEndian(bool isLE); @@ -41,7 +41,7 @@ namespace es fnd::Vec mRawBinary; // variables - es::sign::SignType mSignType; + es::sign::SignatureId mSignType; bool mIsLittleEndian; fnd::Vec mSignature; }; diff --git a/lib/libes/include/es/sign.h b/lib/libes/include/es/sign.h index 4e3f85c..47b4d3d 100644 --- a/lib/libes/include/es/sign.h +++ b/lib/libes/include/es/sign.h @@ -9,14 +9,27 @@ namespace es { namespace sign { - enum SignType + enum SignatureId { - SIGN_RSA4096_SHA1 = 0x10000, - SIGN_RSA2048_SHA1, - SIGN_ECDSA240_SHA1, - SIGN_RSA4096_SHA256, - SIGN_RSA2048_SHA256, - SIGN_ECDSA240_SHA256, + SIGN_ID_RSA4096_SHA1 = 0x10000, + SIGN_ID_RSA2048_SHA1, + SIGN_ID_ECDSA240_SHA1, + SIGN_ID_RSA4096_SHA256, + SIGN_ID_RSA2048_SHA256, + SIGN_ID_ECDSA240_SHA256, + }; + + enum SignatureAlgo + { + SIGN_ALGO_RSA4096, + SIGN_ALGO_RSA2048, + SIGN_ALGO_ECDSA240 + }; + + enum HashAlgo + { + HASH_ALGO_SHA1, + HASH_ALGO_SHA256 }; static const size_t kEcdsaSigSize = 0x3C; diff --git a/lib/libes/source/SignUtils.cpp b/lib/libes/source/SignUtils.cpp new file mode 100644 index 0000000..29b594d --- /dev/null +++ b/lib/libes/source/SignUtils.cpp @@ -0,0 +1,45 @@ +#include + +es::sign::SignatureAlgo es::sign::getSignatureAlgo(es::sign::SignatureId sign_id) +{ + SignatureAlgo sign_algo = SIGN_ALGO_RSA4096; + + switch (sign_id) + { + case (es::sign::SIGN_ID_RSA4096_SHA1): + case (es::sign::SIGN_ID_RSA4096_SHA256): + sign_algo = SIGN_ALGO_RSA4096; + break; + case (es::sign::SIGN_ID_RSA2048_SHA1): + case (es::sign::SIGN_ID_RSA2048_SHA256): + sign_algo = SIGN_ALGO_RSA2048; + break; + case (es::sign::SIGN_ID_ECDSA240_SHA1): + case (es::sign::SIGN_ID_ECDSA240_SHA256): + sign_algo = SIGN_ALGO_ECDSA240; + break; + }; + + return sign_algo; +} + +es::sign::HashAlgo es::sign::getHashAlgo(es::sign::SignatureId sign_id) +{ + HashAlgo hash_algo = HASH_ALGO_SHA1; + + switch (sign_id) + { + case (es::sign::SIGN_ID_RSA4096_SHA1): + case (es::sign::SIGN_ID_RSA2048_SHA1): + case (es::sign::SIGN_ID_ECDSA240_SHA1): + hash_algo = HASH_ALGO_SHA1; + break; + case (es::sign::SIGN_ID_RSA4096_SHA256): + case (es::sign::SIGN_ID_RSA2048_SHA256): + case (es::sign::SIGN_ID_ECDSA240_SHA256): + hash_algo = HASH_ALGO_SHA256; + break; + }; + + return hash_algo; +} \ No newline at end of file diff --git a/lib/libes/source/SignatureBlock.cpp b/lib/libes/source/SignatureBlock.cpp index 4aa591d..b568e7c 100644 --- a/lib/libes/source/SignatureBlock.cpp +++ b/lib/libes/source/SignatureBlock.cpp @@ -37,18 +37,18 @@ void es::SignatureBlock::toBytes() switch (mSignType) { - case (sign::SIGN_RSA4096_SHA1): - case (sign::SIGN_RSA4096_SHA256): + case (sign::SIGN_ID_RSA4096_SHA1): + case (sign::SIGN_ID_RSA4096_SHA256): totalSize = sizeof(sRsa4096SignBlock); sigSize = crypto::rsa::kRsa4096Size; break; - case (sign::SIGN_RSA2048_SHA1): - case (sign::SIGN_RSA2048_SHA256): + case (sign::SIGN_ID_RSA2048_SHA1): + case (sign::SIGN_ID_RSA2048_SHA256): totalSize = sizeof(sRsa2048SignBlock); sigSize = crypto::rsa::kRsa2048Size; break; - case (sign::SIGN_ECDSA240_SHA1): - case (sign::SIGN_ECDSA240_SHA256): + case (sign::SIGN_ID_ECDSA240_SHA1): + case (sign::SIGN_ID_ECDSA240_SHA256): totalSize = sizeof(sEcdsa240SignBlock); sigSize = sign::kEcdsaSigSize; break; @@ -80,18 +80,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) signType = ((be_uint32_t*)src)->get(); switch (signType) { - case (sign::SIGN_RSA4096_SHA1): - case (sign::SIGN_RSA4096_SHA256): + case (sign::SIGN_ID_RSA4096_SHA1): + case (sign::SIGN_ID_RSA4096_SHA256): totalSize = sizeof(sRsa4096SignBlock); sigSize = crypto::rsa::kRsa4096Size; break; - case (sign::SIGN_RSA2048_SHA1): - case (sign::SIGN_RSA2048_SHA256): + case (sign::SIGN_ID_RSA2048_SHA1): + case (sign::SIGN_ID_RSA2048_SHA256): totalSize = sizeof(sRsa2048SignBlock); sigSize = crypto::rsa::kRsa2048Size; break; - case (sign::SIGN_ECDSA240_SHA1): - case (sign::SIGN_ECDSA240_SHA256): + case (sign::SIGN_ID_ECDSA240_SHA1): + case (sign::SIGN_ID_ECDSA240_SHA256): totalSize = sizeof(sEcdsa240SignBlock); sigSize = sign::kEcdsaSigSize; break; @@ -103,18 +103,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) signType = ((le_uint32_t*)src)->get(); switch (signType) { - case (sign::SIGN_RSA4096_SHA1): - case (sign::SIGN_RSA4096_SHA256): + case (sign::SIGN_ID_RSA4096_SHA1): + case (sign::SIGN_ID_RSA4096_SHA256): totalSize = sizeof(sRsa4096SignBlock); sigSize = crypto::rsa::kRsa4096Size; break; - case (sign::SIGN_RSA2048_SHA1): - case (sign::SIGN_RSA2048_SHA256): + case (sign::SIGN_ID_RSA2048_SHA1): + case (sign::SIGN_ID_RSA2048_SHA256): totalSize = sizeof(sRsa2048SignBlock); sigSize = crypto::rsa::kRsa2048Size; break; - case (sign::SIGN_ECDSA240_SHA1): - case (sign::SIGN_ECDSA240_SHA256): + case (sign::SIGN_ID_ECDSA240_SHA1): + case (sign::SIGN_ID_ECDSA240_SHA256): totalSize = sizeof(sEcdsa240SignBlock); sigSize = sign::kEcdsaSigSize; break; @@ -133,7 +133,7 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) mRawBinary.alloc(totalSize); memcpy(mRawBinary.data(), src, totalSize); - mSignType = (sign::SignType)signType; + mSignType = (sign::SignatureId)signType; mSignature.alloc(sigSize); memcpy(mSignature.data(), mRawBinary.data() + 4, sigSize); } @@ -146,17 +146,17 @@ const fnd::Vec& es::SignatureBlock::getBytes() const void es::SignatureBlock::clear() { mRawBinary.clear(); - mSignType = sign::SIGN_RSA4096_SHA1; + mSignType = sign::SIGN_ID_RSA4096_SHA1; mIsLittleEndian = false; mSignature.clear(); } -es::sign::SignType es::SignatureBlock::getSignType() const +es::sign::SignatureId es::SignatureBlock::getSignType() const { return mSignType; } -void es::SignatureBlock::setSignType(es::sign::SignType type) +void es::SignatureBlock::setSignType(es::sign::SignatureId type) { mSignType = type; } diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 1299b6a..4e31423 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "OffsetAdjustedIFile.h" #include "EsCertProcess.h" @@ -83,25 +84,50 @@ void EsCertProcess::validateCerts() void EsCertProcess::validateCert(const es::SignedData& cert) { std::string cert_ident = cert.getBody().getIssuer() + es::sign::kIdentDelimiter + cert.getBody().getSubject(); + + es::sign::SignatureAlgo cert_sign_algo = es::sign::getSignatureAlgo(cert.getSignature().getSignType()); + es::sign::HashAlgo cert_hash_algo = es::sign::getHashAlgo(cert.getSignature().getSignType()); + byte_t cert_hash[crypto::sha::kSha256HashLen]; + memset(cert_hash, 0, crypto::sha::kSha256HashLen); + try { + // special case if signed by Root if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) { throw fnd::Exception(kModuleName, "Signed by Root"); } - const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); + // get cert hash + switch (cert_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + crypto::sha::Sha1(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash); + break; + case (es::sign::HASH_ALGO_SHA256): + crypto::sha::Sha256(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash); + break; + default: + throw fnd::Exception(kModuleName, "Unrecognised hash type"); + } - if (issuer.getPublicKeyType() == es::cert::RSA4096 && (cert.getSignature().getSignType() == es::sign::SIGN_RSA4096_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_RSA4096_SHA256)) + // try to find issuer cert + const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); + es::cert::PublicKeyType issuer_pubk_type = issuer.getPublicKeyType(); + + // validate signature + int sig_validate_res = -1; + + if (issuer_pubk_type == es::cert::RSA4096 && cert_sign_algo == es::sign::SIGN_ALGO_RSA4096) { - throw fnd::Exception(kModuleName, "RSA4096 signatures are not supported"); + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); } - else if (issuer.getPublicKeyType() == es::cert::RSA2048 && (cert.getSignature().getSignType() == es::sign::SIGN_RSA2048_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_RSA2048_SHA256)) + else if (issuer_pubk_type == es::cert::RSA2048 && cert_sign_algo == es::sign::SIGN_ALGO_RSA2048) { - throw fnd::Exception(kModuleName, "RSA2048 signatures are not supported"); + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); } - else if (issuer.getPublicKeyType() == es::cert::ECDSA240 && (cert.getSignature().getSignType() == es::sign::SIGN_ECDSA240_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_ECDSA240_SHA256)) + else if (issuer_pubk_type == es::cert::ECDSA240 && cert_sign_algo == es::sign::SIGN_ALGO_ECDSA240) { throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); } @@ -109,10 +135,15 @@ void EsCertProcess::validateCert(const es::SignedData& cert { throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); } + + if (sig_validate_res != 0) + { + throw fnd::Exception(kModuleName, "Incorrect signature"); + } } catch (const fnd::Exception& e) { - printf("[WARNING] Failed to validate %s (%s)\n", cert_ident.c_str(), e.error()); + std::cout << "[WARNING] Failed to validate " << cert_ident << " (" << e.error() << ")" << std::endl; return; } @@ -194,27 +225,45 @@ const es::SignedData& EsCertProcess::getIssuerCert(const st throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); } -const char* EsCertProcess::getSignTypeStr(es::sign::SignType type) const +crypto::sha::HashType EsCertProcess::getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo es_hash_algo) const +{ + crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1; + + switch (es_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + hash_type = crypto::sha::HASH_SHA1; + break; + case (es::sign::HASH_ALGO_SHA256): + hash_type = crypto::sha::HASH_SHA256; + break; + }; + + return hash_type; +} + + +const char* EsCertProcess::getSignTypeStr(es::sign::SignatureId type) const { const char* str; switch (type) { - case (es::sign::SIGN_RSA4096_SHA1): + case (es::sign::SIGN_ID_RSA4096_SHA1): str = "RSA4096-SHA1"; break; - case (es::sign::SIGN_RSA2048_SHA1): + case (es::sign::SIGN_ID_RSA2048_SHA1): str = "RSA2048-SHA1"; break; - case (es::sign::SIGN_ECDSA240_SHA1): + case (es::sign::SIGN_ID_ECDSA240_SHA1): str = "ECDSA240-SHA1"; break; - case (es::sign::SIGN_RSA4096_SHA256): + case (es::sign::SIGN_ID_RSA4096_SHA256): str = "RSA4096-SHA256"; break; - case (es::sign::SIGN_RSA2048_SHA256): + case (es::sign::SIGN_ID_RSA2048_SHA256): str = "RSA2048-SHA256"; break; - case (es::sign::SIGN_ECDSA240_SHA256): + case (es::sign::SIGN_ID_ECDSA240_SHA256): str = "ECDSA240-SHA256"; break; default: diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h index 42db9e8..8017a4f 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/EsCertProcess.h @@ -40,7 +40,9 @@ private: const es::SignedData& getIssuerCert(const std::string& issuer_name) const; - const char* getSignTypeStr(es::sign::SignType type) const; + crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const; + + const char* getSignTypeStr(es::sign::SignatureId type) const; const char* getEndiannessStr(bool isLittleEndian) const; const char* getPublicKeyTypeStr(es::cert::PublicKeyType type) const; }; \ No newline at end of file diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp index 301607f..4ef721e 100644 --- a/programs/nstool/source/EsTikProcess.cpp +++ b/programs/nstool/source/EsTikProcess.cpp @@ -132,23 +132,23 @@ const char* EsTikProcess::getSignTypeStr(uint32_t type) const const char* str = nullptr; switch(type) { - case (es::sign::SIGN_RSA4096_SHA1): - str = "RSA4096_SHA1"; + case (es::sign::SIGN_ID_RSA4096_SHA1): + str = "RSA4096-SHA1"; break; - case (es::sign::SIGN_RSA2048_SHA1): - str = "RSA2048_SHA1"; + case (es::sign::SIGN_ID_RSA2048_SHA1): + str = "RSA2048-SHA1"; break; - case (es::sign::SIGN_ECDSA240_SHA1): - str = "ECDSA240_SHA1"; + case (es::sign::SIGN_ID_ECDSA240_SHA1): + str = "ECDSA240-SHA1"; break; - case (es::sign::SIGN_RSA4096_SHA256): - str = "RSA4096_SHA256"; + case (es::sign::SIGN_ID_RSA4096_SHA256): + str = "RSA4096-SHA256"; break; - case (es::sign::SIGN_RSA2048_SHA256): - str = "RSA2048_SHA256"; + case (es::sign::SIGN_ID_RSA2048_SHA256): + str = "RSA2048-SHA256"; break; - case (es::sign::SIGN_ECDSA240_SHA256): - str = "ECDSA240_SHA256"; + case (es::sign::SIGN_ID_ECDSA240_SHA256): + str = "ECDSA240-SHA256"; break; default: str = "Unknown"; diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index ca5a65a..aaf2477 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -882,7 +882,7 @@ bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample if (sign.isLittleEndian() == true) return false; - if (sign.getSignType() != es::sign::SIGN_RSA4096_SHA256 && sign.getSignType() != es::sign::SIGN_RSA2048_SHA256 && sign.getSignType() != es::sign::SIGN_ECDSA240_SHA256) + if (sign.getSignType() != es::sign::SIGN_ID_RSA4096_SHA256 && sign.getSignType() != es::sign::SIGN_ID_RSA2048_SHA256 && sign.getSignType() != es::sign::SIGN_ID_ECDSA240_SHA256) return false; return true; @@ -904,7 +904,7 @@ bool UserSettings::determineValidEsTikFromSample(const fnd::Vec& sample) if (sign.isLittleEndian() == false) return false; - if (sign.getSignType() != es::sign::SIGN_RSA2048_SHA256) + if (sign.getSignType() != es::sign::SIGN_ID_RSA2048_SHA256) return false; return true;