[nstool] Encapsulate nnpki validation in PkiValidator.

This commit is contained in:
jakcron 2018-08-05 23:09:07 +08:00
parent 104fecde82
commit 24fa6da666
4 changed files with 232 additions and 109 deletions

View file

@ -5,6 +5,7 @@
#include <es/SignUtils.h>
#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<es::CertificateBody>& 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<es::CertificateBody>& cert)
#undef _SPLIT_VER
}
const es::SignedData<es::CertificateBody>& 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;

View file

@ -34,13 +34,9 @@ private:
void importCerts();
void validateCerts();
void validateCert(const es::SignedData<es::CertificateBody>& cert);
void displayCerts();
void displayCert(const es::SignedData<es::CertificateBody>& cert);
const es::SignedData<es::CertificateBody>& 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;

View file

@ -0,0 +1,192 @@
#include "PkiValidator.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <es/SignUtils.h>
PkiValidator::PkiValidator()
{
clearCertificates();
}
void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key)
{
// save a copy of the certificate bank
fnd::List<es::SignedData<es::CertificateBody>> 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<es::SignedData<es::CertificateBody>>& certs)
{
std::string cert_ident;
es::sign::SignatureAlgo cert_sign_algo;
es::sign::HashAlgo cert_hash_algo;
fnd::Vec<byte_t> 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<byte_t>& signature, const fnd::Vec<byte_t>& 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<es::CertificateBody>& 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<es::CertificateBody>& 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;
}

View file

@ -0,0 +1,33 @@
#pragma once
#include <fnd/types.h>
#include <fnd/List.h>
#include <fnd/Vec.h>
#include <crypto/rsa.h>
#include <es/SignedData.h>
#include <es/CertificateBody.h>
#include <string>
class PkiValidator
{
public:
PkiValidator();
void setRootKey(const crypto::rsa::sRsa4096Key& root_key);
void addCertificates(const fnd::List<es::SignedData<es::CertificateBody>>& certs);
void clearCertificates();
void validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec<byte_t>& signature, const fnd::Vec<byte_t>& hash) const;
private:
const std::string kModuleName = "NNPkiValidator";
crypto::rsa::sRsa4096Key mRootKey;
fnd::List<es::SignedData<es::CertificateBody>> mCertificateBank;
void makeCertIdent(const es::SignedData<es::CertificateBody>& 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<es::CertificateBody>& getCert(const std::string& ident) const;
crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const;
};