diff --git a/Include/Aurora/Crypto/CA/ICertificateStore.hpp b/Include/Aurora/Crypto/CA/ICertificateStore.hpp index caf8ca85..3861f37e 100755 --- a/Include/Aurora/Crypto/CA/ICertificateStore.hpp +++ b/Include/Aurora/Crypto/CA/ICertificateStore.hpp @@ -16,10 +16,15 @@ namespace Aurora::Crypto::CA { struct ICertificateStore : IPinCertificate { - virtual bool AddCertificate(const AuMemoryViewRead &x509Certificate) = 0; - virtual bool AddCertificateChain(X509::ICertificateChain *pChain) = 0; + virtual void Serialize(Memory::ByteBuffer &buffer) = 0; + virtual bool Deserialize(Memory::ByteBuffer &buffer) = 0; - virtual void Serialize(Memory::ByteBuffer &buffer) = 0; - virtual bool Deserialize(Memory::ByteBuffer &buffer) = 0; + virtual bool AddCertificate(const AuMemoryViewRead &x509Certificate) = 0; + virtual bool AddCertificateChain(X509::ICertificateChain *pChain) = 0; + + virtual bool RemoveCertificate(const AuMemoryViewRead &x509Certificate) = 0; + virtual AuUInt32 RemoveCertificatesFromChain(X509::ICertificateChain *pChain) = 0; + + virtual AuUInt32 Size() = 0; }; } \ No newline at end of file diff --git a/Source/Crypto/CA/AuCertificateStore.cpp b/Source/Crypto/CA/AuCertificateStore.cpp index 22390ef1..cd976a88 100755 --- a/Source/Crypto/CA/AuCertificateStore.cpp +++ b/Source/Crypto/CA/AuCertificateStore.cpp @@ -18,9 +18,37 @@ namespace Aurora::Crypto::X509 namespace Aurora::Crypto::CA { + // ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... // Digicerts "high assurance" and other special roots trusted by payment insitutions use 2048... FUCKING WHY? // These certs should be expiring in early 2030. Hopefully PayPal, Stripe, and others will get their act // together soon. Oh, and guess what, Amazon and tons of AWS customers use Amazon Root CA 1 with a 2048-bit PK as well. + // ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... + // + // ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... + // Please also note that this profile is only used to verify certificate *chains* + // + // When chain[0].publicKey is trusted by your client, and you've performed a key exchanged based upon that keypair, we don't care. + // Certificate parameter validation is your issue - the clients' implementation side with your expected profile parameters and state container + // of the peers connection and hostname,common-name pair. Such validation is not an issue for this relatively stateless * certificate store *. + // + // However, when *we* recurse a *chain*, we need to verify that the previous certificate the signator to the next, at this point it is our + // responsibility to ensure some unsound certificates weren't injected into the middle of the chain to violate the "is trusted by a known root + // via a series of unknown middle-man certificates" test. Noting we dont care for testing CN and SANs; we only care about testing the PK infra + // which just to happens to include a signature test of the serialized ASN.1 properties from the anonymous root down to the chain[0] common name. + // These certificate chains, we have to assume they're arbitrary remote user data, and therefore could be severely malformed or manipulated. + // Any provided TLS stack will verify the chain[0] common name and profile parameters of the peer, however as the root-store validator, we need + // to vertify cert[1...n] eventually references a good known anon root authority without a breakage the chains of signatures. + // + // PS: Emphasis on anonymous. I've seen some certificate roots, *ahem google*, change their x509 container without changing their key. + // If we're hard-coding a series of CAs to trust, or even storing long term keychains, we're saying "we trust the owner of this key" to not + // be a dipshit, therefore we shouldn't need to worry about every minute detail of the CA - we only care about: 1) the binary public key, + // and 2) expiration time. It is therefore the case we only serialized these two properties. Do we really care that somebody we trust changed + // their their expiration time by an hour after reserializing their root cert? No, not really. Do we care that they added some SANs? Again, + // no. What about extended key uses? What are those? The extension RFC specifies use cases we couldn't care less about. + // Any certificate revocation or further tests must come from another validator, perhaps connected to us via a "PinCheckTwoAnd" object. + // + // kAssumeRootIsSmart is used to satisfy this relationship test. + // ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... static const mbedtls_x509_crt_profile kAssumeRootIsSmart = { MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | // TODO(by 2030): delete me @@ -42,15 +70,35 @@ namespace Aurora::Crypto::CA 2048, // TODO(by 2030): Raise to 4k }; - bool CertificateStore::CheckCertificate(const AuSPtr &pChain, + bool CertificateStore::CheckCertificate(const AuSPtr &pChain2, const AuMemoryViewRead &derCertificate) { - AU_LOCK_GUARD(this->rwLock->AsReadable()); + AuUPtr pTempChain; + X509::ICertificateChain *pChain; bool bFoundTrustedCA {}; - if (!pChain) + AU_LOCK_GUARD(this->rwLock->AsReadable()); + + if (!pChain2) { - return false; + if (derCertificate) + { + pTempChain = X509::NewChainFromOneDerGenericUnique(derCertificate); + if (!pTempChain) + { + return false; + } + + pChain = pTempChain.get(); + } + else + { + return false; + } + } + else + { + pChain = pChain2.get(); } auto uCount = pChain->GetCertificateCount(); @@ -199,8 +247,104 @@ namespace Aurora::Crypto::CA return true; } + bool CertificateStore::RemoveCertificate(const AuMemoryViewRead &x509Certificate) + { + bool bRet {}; + mbedtls_x509_crt crt; + + if (!x509Certificate) + { + SysPushErrorArg(); + return false; + } + + mbedtls_x509_crt_init(&crt); + + if (mbedtls_x509_crt_parse(&crt, + x509Certificate.Begin(), + x509Certificate.Size()) < 0) + { + SysPushErrorGeneric(); + return false; + } + + AuUInt32 uCode { AuFnv1a32Runtime(crt.pk_raw.p, crt.pk_raw.len) }; + + { + AU_LOCK_GUARD(this->rwLock->AsWritable()); + + auto itr = this->storage.find(uCode); + if (itr != this->storage.end()) + { + if (AuTryRemoveByTupleN<0>(itr->second, AuMemoryViewRead { crt.pk_raw.p, crt.pk_raw.len })) + { + bRet = true; + } + } + } + + mbedtls_x509_crt_free(&crt); + return bRet; + } + + AuUInt32 CertificateStore::RemoveCertificatesFromChain(X509::ICertificateChain *pChain) + { + AuUInt32 uRet {}; + + if (!pChain) + { + SysPushErrorArg(); + return uRet; + } + + AU_LOCK_GUARD(this->rwLock->AsWritable()); + + auto uCount = pChain->GetCertificateCount(); + for (AU_ITERATE_N(i, uCount)) + { + auto pCert = AuStaticCast(pChain)->GetCertificateInternal(i); + if (!pCert) + { + continue; + } + + AuUInt32 uCode { AuFnv1a32Runtime(pCert->pk_raw.p, pCert->pk_raw.len) }; + + auto itr = this->storage.find(uCode); + if (itr == this->storage.end()) + { + continue; + } + + if (!AuTryRemoveByTupleN<0>(itr->second, AuMemoryViewRead { pCert->pk_raw.p, pCert->pk_raw.len })) + { + continue; + } + + uRet++; + } + + return uRet; + } + + AuUInt32 CertificateStore::Size() + { + AuUInt32 uRet {}; + + AU_LOCK_GUARD(this->rwLock->AsReadable()); + + for (auto &[uID, storageList] : this->storage) + { + uRet += storageList.size(); + } + + return uRet; + } + void CertificateStore::Serialize(Memory::ByteBuffer &buffer) { + AU_LOCK_GUARD(this->rwLock->AsReadable()); + for (auto &[uID, storageList] : this->storage) { for (auto &[cert, uValidTo] : storageList) diff --git a/Source/Crypto/CA/AuCertificateStore.hpp b/Source/Crypto/CA/AuCertificateStore.hpp index e048e52e..3ecb39f6 100755 --- a/Source/Crypto/CA/AuCertificateStore.hpp +++ b/Source/Crypto/CA/AuCertificateStore.hpp @@ -17,6 +17,10 @@ namespace Aurora::Crypto::CA bool AddCertificate(const AuMemoryViewRead &x509Certificate) override; bool AddCertificateChain(X509::ICertificateChain *pChain) override; + bool RemoveCertificate(const AuMemoryViewRead &x509Certificate) override; + AuUInt32 RemoveCertificatesFromChain(X509::ICertificateChain *pChain) override; + + AuUInt32 Size() override; void Serialize(Memory::ByteBuffer &buffer) override; bool Deserialize(Memory::ByteBuffer &buffer) override;