Serenity Operating System
at master 115 lines 3.8 kB view raw
1/* 2 * Copyright (c) 2021, Peter Bocan <me@pbocan.net> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Base64.h> 8#include <LibCore/ConfigFile.h> 9#include <LibCore/DeprecatedFile.h> 10#include <LibCore/EventLoop.h> 11#include <LibCrypto/ASN1/ASN1.h> 12#include <LibTLS/TLSv12.h> 13#include <LibTest/TestCase.h> 14 15static StringView ca_certs_file = "./ca_certs.ini"sv; 16static int port = 443; 17 18constexpr auto DEFAULT_SERVER = "www.google.com"sv; 19 20static ByteBuffer operator""_b(char const* string, size_t length) 21{ 22 return ByteBuffer::copy(string, length).release_value(); 23} 24 25Vector<Certificate> load_certificates(); 26DeprecatedString locate_ca_certs_file(); 27 28DeprecatedString locate_ca_certs_file() 29{ 30 if (Core::DeprecatedFile::exists(ca_certs_file)) { 31 return ca_certs_file; 32 } 33 auto on_target_path = DeprecatedString("/etc/ca_certs.ini"); 34 if (Core::DeprecatedFile::exists(on_target_path)) { 35 return on_target_path; 36 } 37 return ""; 38} 39 40Vector<Certificate> load_certificates() 41{ 42 Vector<Certificate> certificates; 43 auto ca_certs_filepath = locate_ca_certs_file(); 44 if (ca_certs_filepath == "") { 45 warnln("Could not locate ca_certs.ini file."); 46 return certificates; 47 } 48 49 auto config = Core::ConfigFile::open(ca_certs_filepath).release_value_but_fixme_should_propagate_errors(); 50 for (auto& entity : config->groups()) { 51 for (auto& subject : config->keys(entity)) { 52 auto certificate_base64 = config->read_entry(entity, subject); 53 auto certificate_data_result = decode_base64(certificate_base64); 54 if (certificate_data_result.is_error()) { 55 dbgln("Skipping CA Certificate {} {}: out of memory", entity, subject); 56 continue; 57 } 58 auto certificate_data = certificate_data_result.release_value(); 59 auto certificate_result = Certificate::parse_asn1(certificate_data.bytes()); 60 // If the certificate does not parse it is likely using elliptic curve keys/signatures, which are not 61 // supported right now. Currently, ca_certs.ini should only contain certificates with RSA keys/signatures. 62 if (!certificate_result.has_value()) { 63 dbgln("Skipping CA Certificate {} {}: unable to parse", entity, subject); 64 continue; 65 } 66 auto certificate = certificate_result.release_value(); 67 certificates.append(move(certificate)); 68 } 69 } 70 return certificates; 71} 72 73static Vector<Certificate> s_root_ca_certificates = load_certificates(); 74 75TEST_CASE(test_TLS_hello_handshake) 76{ 77 Core::EventLoop loop; 78 TLS::Options options; 79 options.set_root_certificates(s_root_ca_certificates); 80 options.set_alert_handler([&](TLS::AlertDescription) { 81 FAIL("Connection failure"); 82 loop.quit(1); 83 }); 84 options.set_finish_callback([&] { 85 loop.quit(0); 86 }); 87 88 auto tls = MUST(TLS::TLSv12::connect(DEFAULT_SERVER, port, move(options))); 89 ByteBuffer contents; 90 tls->on_ready_to_read = [&] { 91 auto read_bytes = MUST(tls->read_some(contents.must_get_bytes_for_writing(4 * KiB))); 92 if (read_bytes.is_empty()) { 93 FAIL("No data received"); 94 loop.quit(1); 95 } 96 loop.quit(0); 97 }; 98 99 if (tls->write_until_depleted("GET / HTTP/1.1\r\nHost: "_b).is_error()) { 100 FAIL("write(0) failed"); 101 return; 102 } 103 104 auto the_server = DEFAULT_SERVER; 105 if (tls->write_until_depleted(the_server.bytes()).is_error()) { 106 FAIL("write(1) failed"); 107 return; 108 } 109 if (tls->write_until_depleted("\r\nConnection : close\r\n\r\n"_b).is_error()) { 110 FAIL("write(2) failed"); 111 return; 112 } 113 114 loop.exec(); 115}