소스 검색

LibTLS: Verify server certificate expiry date

AnotherTest 5 년 전
부모
커밋
af1ce6c33d
3개의 변경된 파일141개의 추가작업 그리고 12개의 파일을 삭제
  1. 15 0
      Libraries/LibTLS/ClientHandshake.cpp
  2. 106 8
      Libraries/LibTLS/TLSv12.cpp
  3. 20 4
      Libraries/LibTLS/TLSv12.h

+ 15 - 0
Libraries/LibTLS/ClientHandshake.cpp

@@ -363,6 +363,21 @@ ssize_t TLSv12::handle_payload(const ByteBuffer& vbuffer)
                     ASSERT_NOT_REACHED();
                 }
                 payload_res = handle_certificate(buffer.slice_view(1, payload_size));
+                if (m_context.certificates.size()) {
+                    auto it = m_context.certificates.find([&](auto& cert) { return cert.is_valid(); });
+
+                    if (it.is_end()) {
+                        // no valid certificates
+                        dbg() << "No valid certificates found";
+                        payload_res = (i8)Error::BadCertificate;
+                        m_context.critical_error = payload_res;
+                        break;
+                    }
+
+                    // swap the first certificate with the valid one
+                    if (it.index() != 0)
+                        swap(m_context.certificates[0], m_context.certificates[it.index()]);
+                }
             } else {
                 payload_res = (i8)Error::UnexpectedMessage;
             }

+ 106 - 8
Libraries/LibTLS/TLSv12.cpp

@@ -24,6 +24,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <LibCore/DateTime.h>
 #include <LibCore/Timer.h>
 #include <LibCrypto/ASN1/DER.h>
 #include <LibCrypto/PK/Code/EMSA_PSS.h>
@@ -65,13 +66,58 @@ static bool _asn1_is_oid(const u8* oid, const u8* compare, size_t length = 3)
     return true;
 }
 
-static void _set_algorithm(u32&, const u8* value, size_t length)
+static bool _set_algorithm(CertificateKeyAlgorithm& algorithm, const u8* value, size_t length)
 {
+    if (length == 7) {
+        // Elliptic Curve pubkey
+        dbg() << "Cert.algorithm: EC, unsupported";
+        return false;
+    }
+
+    if (length == 8) {
+        // named EC key
+        dbg() << "Cert.algorithm: Named EC (" << *value << "), unsupported";
+        return false;
+    }
+
+    if (length == 5) {
+        // named EC SECP key
+        dbg() << "Cert.algorithm: Named EC secp (" << *value << "), unsupported";
+        return false;
+    }
+
     if (length != 9) {
-        dbg() << "unsupported algorithm " << value;
+        dbg() << "Invalid certificate algorithm";
+        return false;
+    }
+
+    if (_asn1_is_oid(value, Constants::RSA_SIGN_RSA_OID, 9)) {
+        algorithm = CertificateKeyAlgorithm::RSA_RSA;
+        return true;
+    }
+
+    if (_asn1_is_oid(value, Constants::RSA_SIGN_SHA256_OID, 9)) {
+        algorithm = CertificateKeyAlgorithm::RSA_SHA256;
+        return true;
     }
 
-    dbg() << "FIXME: Set algorithm";
+    if (_asn1_is_oid(value, Constants::RSA_SIGN_SHA512_OID, 9)) {
+        algorithm = CertificateKeyAlgorithm::RSA_SHA512;
+        return true;
+    }
+
+    if (_asn1_is_oid(value, Constants::RSA_SIGN_SHA1_OID, 9)) {
+        algorithm = CertificateKeyAlgorithm::RSA_SHA1;
+        return true;
+    }
+
+    if (_asn1_is_oid(value, Constants::RSA_SIGN_MD5_OID, 9)) {
+        algorithm = CertificateKeyAlgorithm::RSA_MD5;
+        return true;
+    }
+
+    dbg() << "Unsupported RSA Signature mode " << value[8];
+    return false;
 }
 
 static size_t _get_asn1_length(const u8* buffer, size_t length, size_t& octets)
@@ -306,7 +352,35 @@ static ssize_t _parse_asn1(const Context& context, Certificate& cert, const u8*
         position += length;
     }
     if (level == 2 && cert.sign_key.size() && cert_length && cert_data) {
-        dbg() << "FIXME: Cert.fingerprint";
+        cert.fingerprint.clear();
+        Crypto::Hash::Manager hash;
+        switch (cert.key_algorithm) {
+        case CertificateKeyAlgorithm::RSA_MD5:
+            hash.initialize(Crypto::Hash::HashKind::MD5);
+            break;
+        case CertificateKeyAlgorithm::RSA_SHA1:
+            hash.initialize(Crypto::Hash::HashKind::SHA1);
+            break;
+        case CertificateKeyAlgorithm::RSA_SHA256:
+            hash.initialize(Crypto::Hash::HashKind::SHA256);
+            break;
+        case CertificateKeyAlgorithm::RSA_SHA512:
+            hash.initialize(Crypto::Hash::HashKind::SHA512);
+            break;
+        default:
+            dbg() << "Unsupported hash mode " << (u32)cert.key_algorithm;
+            // fallback to md5, it will fail later
+            hash.initialize(Crypto::Hash::HashKind::MD5);
+            break;
+        }
+        hash.update(cert_data, cert_length);
+        auto fingerprint = hash.digest();
+        cert.fingerprint.grow(fingerprint.data_length());
+        cert.fingerprint.overwrite(0, fingerprint.immutable_data(), fingerprint.data_length());
+#ifdef TLS_DEBUG
+        dbg() << "Certificate fingerprint:";
+        print_buffer(cert.fingerprint);
+#endif
     }
     return position;
 }
@@ -377,11 +451,15 @@ ssize_t TLSv12::handle_certificate(const ByteBuffer& buffer)
         size_t certificates_in_chain = 0;
 
         do {
-            if (remaining <= 3)
+            if (remaining <= 3) {
+                dbg() << "Ran out of data";
                 break;
+            }
             ++certificates_in_chain;
-            if (buffer.size() < (size_t)res_cert + 3)
+            if (buffer.size() < (size_t)res_cert + 3) {
+                dbg() << "not enough data to read cert size (" << buffer.size() << " < " << res_cert + 3 << ")";
                 break;
+            }
             size_t certificate_size_specific = buffer[res_cert] * 0x10000 + buffer[res_cert + 1] * 0x100 + buffer[res_cert + 2];
             res_cert += 3;
             remaining -= 3;
@@ -397,7 +475,7 @@ ssize_t TLSv12::handle_certificate(const ByteBuffer& buffer)
                 m_context.certificates.append(certificate.value());
                 valid_certificate = true;
             }
-            res_cert += certificate_size;
+            res_cert += certificate_size_specific;
         } while (remaining > 0);
         if (remaining) {
             dbg() << "extraneous " << remaining << " bytes left over after parsing certificates";
@@ -519,6 +597,27 @@ void TLSv12::ensure_hmac(size_t digest_size, bool local)
         m_hmac_remote = move(hmac);
 }
 
+bool Certificate::is_valid() const
+{
+    auto now = Core::DateTime::now();
+
+    if (!not_before.is_empty()) {
+        if (now.is_before(not_before)) {
+            dbg() << "certificate expired (not yet valid, signed for " << not_before << ")";
+            return false;
+        }
+    }
+
+    if (!not_after.is_empty()) {
+        if (!now.is_before(not_after)) {
+            dbg() << "certificate expired (expiry date " << not_after << ")";
+            return false;
+        }
+    }
+
+    return true;
+}
+
 void TLSv12::try_disambiguate_error() const
 {
     dbg() << "Possible failure cause: ";
@@ -542,5 +641,4 @@ void TLSv12::try_disambiguate_error() const
         break;
     }
 }
-
 }

+ 20 - 4
Libraries/LibTLS/TLSv12.h

@@ -189,11 +189,20 @@ enum ClientVerificationStaus {
     VerificationNeeded,
 };
 
+enum class CertificateKeyAlgorithm {
+    Unsupported = 0x00,
+    RSA_RSA = 0x01,
+    RSA_MD5 = 0x04,
+    RSA_SHA1 = 0x05,
+    RSA_SHA256 = 0x0b,
+    RSA_SHA512 = 0x0d,
+};
+
 struct Certificate {
     u16 version;
-    u32 algorithm;
-    u32 key_algorithm;
-    u32 ec_algorithm;
+    CertificateKeyAlgorithm algorithm;
+    CertificateKeyAlgorithm key_algorithm;
+    CertificateKeyAlgorithm ec_algorithm;
     ByteBuffer exponent;
     Crypto::PK::RSAPublicKey<Crypto::UnsignedBigInteger> public_key;
     String issuer_country;
@@ -216,6 +225,8 @@ struct Certificate {
     ByteBuffer fingerprint;
     ByteBuffer der;
     ByteBuffer data;
+
+    bool is_valid() const;
 };
 
 struct Context {
@@ -488,7 +499,12 @@ constexpr static const u8 subject_oid[] { 0x55, 0x04, 0x03, 0x00 };
 constexpr static const u8 san_oid[] { 0x55, 0x1D, 0x11, 0x00 };
 constexpr static const u8 ocsp_oid[] { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x00 };
 
-constexpr static const u8 TLS_RSA_SIGN_SHA256_OID[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x00 };
+static constexpr const u8 RSA_SIGN_RSA_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x00 };
+static constexpr const u8 RSA_SIGN_MD5_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x00 };
+static constexpr const u8 RSA_SIGN_SHA1_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x00 };
+static constexpr const u8 RSA_SIGN_SHA256_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x00 };
+static constexpr const u8 RSA_SIGN_SHA384_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x00 };
+static constexpr const u8 RSA_SIGN_SHA512_OID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x00 };
 
 }