From 24fa6da66646eac114c896d387b53c4bf82a0d30 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 5 Aug 2018 23:09:07 +0800 Subject: [PATCH] [nstool] Encapsulate nnpki validation in PkiValidator. --- programs/nstool/source/EsCertProcess.cpp | 112 +------------ programs/nstool/source/EsCertProcess.h | 4 - programs/nstool/source/PkiValidator.cpp | 192 +++++++++++++++++++++++ programs/nstool/source/PkiValidator.h | 33 ++++ 4 files changed, 232 insertions(+), 109 deletions(-) create mode 100644 programs/nstool/source/PkiValidator.cpp create mode 100644 programs/nstool/source/PkiValidator.h diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 8c28f1f..9f65bf0 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -5,6 +5,7 @@ #include #include "OffsetAdjustedIFile.h" #include "EsCertProcess.h" +#include "PkiValidator.h" EsCertProcess::EsCertProcess() : mFile(nullptr), @@ -75,84 +76,18 @@ void EsCertProcess::importCerts() void EsCertProcess::validateCerts() { - for (size_t i = 0; i < mCert.size(); i++) - { - EsCertProcess::validateCert(mCert[i]); - } -} - -void EsCertProcess::validateCert(const es::SignedData& cert) -{ - std::string cert_ident = cert.getBody().getIssuer() + es::sign::kIdentDelimiter + cert.getBody().getSubject(); + PkiValidator pki; - 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 + try { - // 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"); - } - - // validate signature - int sig_validate_res = -1; - - // special case if signed by Root - if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) - { - if (cert_sign_algo != es::sign::SIGN_ALGO_RSA4096) - { - throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); - } - sig_validate_res = crypto::rsa::pkcs::rsaVerify(mKeyset->pki.root_sign_key, getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else - { - // try to find issuer cert - const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); - es::cert::PublicKeyType issuer_pubk_type = issuer.getPublicKeyType(); - - if (issuer_pubk_type == es::cert::RSA4096 && cert_sign_algo == es::sign::SIGN_ALGO_RSA4096) - { - sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else if (issuer_pubk_type == es::cert::RSA2048 && cert_sign_algo == es::sign::SIGN_ALGO_RSA2048) - { - sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - 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"); - } - else - { - throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); - } - } - - if (sig_validate_res != 0) - { - throw fnd::Exception(kModuleName, "Incorrect signature"); - } + pki.setRootKey(mKeyset->pki.root_sign_key); + pki.addCertificates(mCert); } - catch (const fnd::Exception& e) + catch (const fnd::Exception& e) { - std::cout << "[WARNING] Failed to validate " << cert_ident << " (" << e.error() << ")" << std::endl; + std::cout << "[WARNING] " << e.error() << std::endl; return; } - } void EsCertProcess::displayCerts() @@ -216,39 +151,6 @@ void EsCertProcess::displayCert(const es::SignedData& cert) #undef _SPLIT_VER } -const es::SignedData& EsCertProcess::getIssuerCert(const std::string& issuer_name) const -{ - std::string full_cert_name; - for (size_t i = 0; i < mCert.size(); i++) - { - full_cert_name = mCert[i].getBody().getIssuer() + es::sign::kIdentDelimiter + mCert[i].getBody().getSubject(); - if (full_cert_name == issuer_name) - { - return mCert[i]; - } - } - - throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); -} - -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; diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h index 8017a4f..ae88555 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/EsCertProcess.h @@ -34,13 +34,9 @@ private: void importCerts(); void validateCerts(); - void validateCert(const es::SignedData& cert); void displayCerts(); void displayCert(const es::SignedData& cert); - const es::SignedData& getIssuerCert(const std::string& issuer_name) 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; diff --git a/programs/nstool/source/PkiValidator.cpp b/programs/nstool/source/PkiValidator.cpp new file mode 100644 index 0000000..46ee72f --- /dev/null +++ b/programs/nstool/source/PkiValidator.cpp @@ -0,0 +1,192 @@ +#include "PkiValidator.h" +#include +#include +#include +#include + +PkiValidator::PkiValidator() +{ + clearCertificates(); +} + +void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key) +{ + // save a copy of the certificate bank + fnd::List> old_certs = mCertificateBank; + + // clear the certificate bank + mCertificateBank.clear(); + + // overwrite the root key + mRootKey = root_key; + + // if there were certificates before, reimport them (so they are checked against the new root key) + if (old_certs.size() > 0) + { + addCertificates(old_certs); + } +} + +void PkiValidator::addCertificates(const fnd::List>& certs) +{ + std::string cert_ident; + es::sign::SignatureAlgo cert_sign_algo; + es::sign::HashAlgo cert_hash_algo; + fnd::Vec cert_sign, cert_hash; + + try + { + for (size_t i = 0; i < certs.size(); i++) + { + makeCertIdent(certs[i], cert_ident); + + if (doesCertExist(cert_ident) == true) + { + throw fnd::Exception(kModuleName, "Certificate already exists"); + } + + cert_sign_algo = es::sign::getSignatureAlgo(certs[i].getSignature().getSignType()); + cert_hash_algo = es::sign::getHashAlgo(certs[i].getSignature().getSignType()); + + // get cert hash + switch (cert_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + cert_hash.alloc(crypto::sha::kSha1HashLen); + crypto::sha::Sha1(certs[i].getBody().getBytes().data(), certs[i].getBody().getBytes().size(), cert_hash.data()); + break; + case (es::sign::HASH_ALGO_SHA256): + cert_hash.alloc(crypto::sha::kSha256HashLen); + crypto::sha::Sha256(certs[i].getBody().getBytes().data(), certs[i].getBody().getBytes().size(), cert_hash.data()); + break; + default: + throw fnd::Exception(kModuleName, "Unrecognised hash type"); + } + + validateSignature(certs[i].getBody().getIssuer(), certs[i].getSignature().getSignType(), certs[i].getSignature().getSignature(), cert_hash); + + mCertificateBank.addElement(certs[i]); + } + } + catch (const fnd::Exception& e) + { + std::stringstream ss; + ss << "Failed to add certificate " << cert_ident << " (" << e.error() << ")"; + throw fnd::Exception(kModuleName, ss.str()); + } +} + +void PkiValidator::clearCertificates() +{ + mCertificateBank.clear(); +} + +void PkiValidator::validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const +{ + es::sign::SignatureAlgo sign_algo = es::sign::getSignatureAlgo(signature_id); + es::sign::HashAlgo hash_algo = es::sign::getHashAlgo(signature_id); + + + // validate signature + int sig_validate_res = -1; + + // special case if signed by Root + if (issuer == es::sign::kRootIssuerStr) + { + if (sign_algo != es::sign::SIGN_ALGO_RSA4096) + { + throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); + } + sig_validate_res = crypto::rsa::pkcs::rsaVerify(mRootKey, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else + { + // try to find issuer cert + const es::CertificateBody& issuer_cert = getCert(issuer).getBody(); + es::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType(); + + if (issuer_pubk_type == es::cert::RSA4096 && sign_algo == es::sign::SIGN_ALGO_RSA4096) + { + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else if (issuer_pubk_type == es::cert::RSA2048 && sign_algo == es::sign::SIGN_ALGO_RSA2048) + { + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else if (issuer_pubk_type == es::cert::ECDSA240 && sign_algo == es::sign::SIGN_ALGO_ECDSA240) + { + throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + } + else + { + throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + } + } + + if (sig_validate_res != 0) + { + throw fnd::Exception(kModuleName, "Incorrect signature"); + } + + +} + +void PkiValidator::makeCertIdent(const es::SignedData& cert, std::string& ident) const +{ + makeCertIdent(cert.getBody().getIssuer(), cert.getBody().getSubject(), ident); +} + +void PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const +{ + ident = issuer + es::sign::kIdentDelimiter + subject; + ident = ident.substr(0, _MIN(ident.length(),64)); +} + +bool PkiValidator::doesCertExist(const std::string& ident) const +{ + bool exists = false; + std::string full_cert_name; + for (size_t i = 0; i < mCertificateBank.size(); i++) + { + makeCertIdent(mCertificateBank[i], full_cert_name); + if (full_cert_name == ident) + { + exists = true; + break; + } + } + + return exists; +} + +const es::SignedData& PkiValidator::getCert(const std::string& ident) const +{ + std::string full_cert_name; + for (size_t i = 0; i < mCertificateBank.size(); i++) + { + makeCertIdent(mCertificateBank[i], full_cert_name); + if (full_cert_name == ident) + { + return mCertificateBank[i]; + } + } + + throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); +} + +crypto::sha::HashType PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const +{ + crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1; + + switch (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; +} \ No newline at end of file diff --git a/programs/nstool/source/PkiValidator.h b/programs/nstool/source/PkiValidator.h new file mode 100644 index 0000000..3ba54ec --- /dev/null +++ b/programs/nstool/source/PkiValidator.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +class PkiValidator +{ +public: + PkiValidator(); + + void setRootKey(const crypto::rsa::sRsa4096Key& root_key); + void addCertificates(const fnd::List>& certs); + void clearCertificates(); + + void validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const; + +private: + const std::string kModuleName = "NNPkiValidator"; + + + crypto::rsa::sRsa4096Key mRootKey; + fnd::List> mCertificateBank; + + void makeCertIdent(const es::SignedData& cert, std::string& ident) const; + void makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const; + bool doesCertExist(const std::string& ident) const; + const es::SignedData& getCert(const std::string& ident) const; + crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const; +}; \ No newline at end of file