LibTLS: Replace TLSv12 implementation with OpenSSL

This commit is contained in:
devgianlu 2025-02-14 09:49:33 +01:00 committed by Ali Mohammad Pur
parent d6e9d2cdbb
commit 7a38a3e994
Notes: github-actions[bot] 2025-02-17 18:53:51 +00:00
15 changed files with 182 additions and 4709 deletions

View file

@ -2,12 +2,6 @@ add_cxx_compile_options(-Wvla)
set(SOURCES
DefaultRootCACertificates.cpp
Handshake.cpp
HandshakeCertificate.cpp
HandshakeClient.cpp
HandshakeServer.cpp
Record.cpp
Socket.cpp
TLSv12.cpp
)
@ -15,3 +9,6 @@ serenity_lib(LibTLS tls)
target_link_libraries(LibTLS PRIVATE LibCore LibCrypto LibFileSystem)
include(ca_certificates_data)
find_package(OpenSSL REQUIRED)
target_link_libraries(LibTLS PUBLIC OpenSSL::SSL)

View file

@ -1,90 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <LibTLS/Extensions.h>
namespace TLS {
// Defined in RFC 5246 section 7.4.1.4.1
struct SignatureAndHashAlgorithm {
HashAlgorithm hash;
SignatureAlgorithm signature;
};
enum class KeyExchangeAlgorithm {
Invalid,
// Defined in RFC 5246 section 7.4.2 / RFC 4279 section 4
RSA_PSK,
// Defined in RFC 5246 section 7.4.3
DHE_DSS,
DHE_RSA,
DH_anon,
RSA,
DH_DSS,
DH_RSA,
// Defined in RFC 4492 section 2
ECDHE_RSA,
ECDH_ECDSA,
ECDH_RSA,
ECDHE_ECDSA,
ECDH_anon,
};
// Defined in RFC 5246 section 7.4.1.4.1
constexpr SignatureAlgorithm signature_for_key_exchange_algorithm(KeyExchangeAlgorithm algorithm)
{
switch (algorithm) {
case KeyExchangeAlgorithm::RSA:
case KeyExchangeAlgorithm::DHE_RSA:
case KeyExchangeAlgorithm::DH_RSA:
case KeyExchangeAlgorithm::RSA_PSK:
case KeyExchangeAlgorithm::ECDH_RSA:
case KeyExchangeAlgorithm::ECDHE_RSA:
return SignatureAlgorithm::RSA;
case KeyExchangeAlgorithm::DHE_DSS:
case KeyExchangeAlgorithm::DH_DSS:
return SignatureAlgorithm::DSA;
case KeyExchangeAlgorithm::ECDH_ECDSA:
case KeyExchangeAlgorithm::ECDHE_ECDSA:
return SignatureAlgorithm::ECDSA;
case KeyExchangeAlgorithm::DH_anon:
case KeyExchangeAlgorithm::ECDH_anon:
default:
return SignatureAlgorithm::ANONYMOUS;
}
}
enum class CipherAlgorithm {
Invalid,
AES_128_CBC,
AES_128_GCM,
AES_128_CCM,
AES_128_CCM_8,
AES_256_CBC,
AES_256_GCM,
};
constexpr size_t cipher_key_size(CipherAlgorithm algorithm)
{
switch (algorithm) {
case CipherAlgorithm::AES_128_CBC:
case CipherAlgorithm::AES_128_GCM:
case CipherAlgorithm::AES_128_CCM:
case CipherAlgorithm::AES_128_CCM_8:
return 128;
case CipherAlgorithm::AES_256_CBC:
case CipherAlgorithm::AES_256_GCM:
return 256;
case CipherAlgorithm::Invalid:
default:
return 0;
}
}
}

View file

@ -1,981 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2023, stelar7 <dudedbz@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace TLS {
#define _ENUM_KEY(name) name,
#define _ENUM_KEY_VALUE(name, value) name = value,
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-5
#define __ENUM_CONTENT_TYPES \
_ENUM_KEY_VALUE(CHANGE_CIPHER_SPEC, 20) \
_ENUM_KEY_VALUE(ALERT, 21) \
_ENUM_KEY_VALUE(HANDSHAKE, 22) \
_ENUM_KEY_VALUE(APPLICATION_DATA, 23) \
_ENUM_KEY_VALUE(HEARTBEAT, 24) \
_ENUM_KEY_VALUE(TLS12_CID, 25) \
_ENUM_KEY_VALUE(ACK, 26)
enum class ContentType : u8 {
__ENUM_CONTENT_TYPES
};
#define __ENUM_PROTOCOL_VERSIONS \
_ENUM_KEY_VALUE(VERSION_1_3, 0x0304) \
_ENUM_KEY_VALUE(VERSION_1_2, 0x0303) \
_ENUM_KEY_VALUE(VERSION_1_1, 0x0302) \
_ENUM_KEY_VALUE(VERSION_1_0, 0x0301) \
_ENUM_KEY_VALUE(GREASE_0, 0x0A0A) \
_ENUM_KEY_VALUE(GREASE_1, 0x1A1A) \
_ENUM_KEY_VALUE(GREASE_2, 0x2A2A) \
_ENUM_KEY_VALUE(GREASE_3, 0x3A3A) \
_ENUM_KEY_VALUE(GREASE_4, 0x4A4A) \
_ENUM_KEY_VALUE(GREASE_5, 0x5A5A) \
_ENUM_KEY_VALUE(GREASE_6, 0x6A6A) \
_ENUM_KEY_VALUE(GREASE_7, 0x7A7A) \
_ENUM_KEY_VALUE(GREASE_8, 0x8A8A) \
_ENUM_KEY_VALUE(GREASE_9, 0x9A9A) \
_ENUM_KEY_VALUE(GREASE_A, 0xAAAA) \
_ENUM_KEY_VALUE(GREASE_B, 0xBABA) \
_ENUM_KEY_VALUE(GREASE_C, 0xCACA) \
_ENUM_KEY_VALUE(GREASE_D, 0xDADA) \
_ENUM_KEY_VALUE(GREASE_E, 0xEAEA) \
_ENUM_KEY_VALUE(GREASE_F, 0xFAFA)
enum class ProtocolVersion : u16 {
__ENUM_PROTOCOL_VERSIONS
};
#define __ENUM_ALERT_LEVELS \
_ENUM_KEY_VALUE(WARNING, 1) \
_ENUM_KEY_VALUE(FATAL, 2)
enum class AlertLevel : u8 {
__ENUM_ALERT_LEVELS
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7
#define __ENUM_HANDSHAKE_TYPES \
_ENUM_KEY_VALUE(HELLO_REQUEST_RESERVED, 0) \
_ENUM_KEY_VALUE(CLIENT_HELLO, 1) \
_ENUM_KEY_VALUE(SERVER_HELLO, 2) \
_ENUM_KEY_VALUE(HELLO_VERIFY_REQUEST_RESERVED, 3) \
_ENUM_KEY_VALUE(NEW_SESSION_TICKET, 4) \
_ENUM_KEY_VALUE(END_OF_EARLY_DATA, 5) \
_ENUM_KEY_VALUE(HELLO_RETRY_REQUEST_RESERVED, 6) \
_ENUM_KEY_VALUE(ENCRYPTED_EXTENSIONS, 8) \
_ENUM_KEY_VALUE(REQUEST_CONNECTION_ID, 9) \
_ENUM_KEY_VALUE(NEW_CONNECTION_ID, 10) \
_ENUM_KEY_VALUE(CERTIFICATE, 11) \
_ENUM_KEY_VALUE(SERVER_KEY_EXCHANGE_RESERVED, 12) \
_ENUM_KEY_VALUE(CERTIFICATE_REQUEST, 13) \
_ENUM_KEY_VALUE(SERVER_HELLO_DONE_RESERVED, 14) \
_ENUM_KEY_VALUE(CERTIFICATE_VERIFY, 15) \
_ENUM_KEY_VALUE(CLIENT_KEY_EXCHANGE_RESERVED, 16) \
_ENUM_KEY_VALUE(FINISHED, 20) \
_ENUM_KEY_VALUE(CERTIFICATE_URL_RESERVED, 21) \
_ENUM_KEY_VALUE(CERTIFICATE_STATUS_RESERVED, 22) \
_ENUM_KEY_VALUE(SUPPLEMENTAL_DATA_RESERVED, 23) \
_ENUM_KEY_VALUE(KEY_UPDATE, 24) \
_ENUM_KEY_VALUE(COMPRESSED_CERTIFICATE, 25) \
_ENUM_KEY_VALUE(EKT_KEY, 26) \
_ENUM_KEY_VALUE(MESSAGE_HASH, 254)
enum class HandshakeType : u8 {
__ENUM_HANDSHAKE_TYPES
};
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1
#define __ENUM_EXTENSION_TYPES \
_ENUM_KEY_VALUE(SERVER_NAME, 0) \
_ENUM_KEY_VALUE(MAX_FRAGMENT_LENGTH, 1) \
_ENUM_KEY_VALUE(CLIENT_CERTIFICATE_URL, 2) \
_ENUM_KEY_VALUE(TRUSTED_CA_KEYS, 3) \
_ENUM_KEY_VALUE(TRUNCATED_HMAC, 4) \
_ENUM_KEY_VALUE(STATUS_REQUEST, 5) \
_ENUM_KEY_VALUE(USER_MAPPING, 6) \
_ENUM_KEY_VALUE(CLIENT_AUTHZ, 7) \
_ENUM_KEY_VALUE(SERVER_AUTHZ, 8) \
_ENUM_KEY_VALUE(CERT_TYPE, 9) \
_ENUM_KEY_VALUE(SUPPORTED_GROUPS, 10) \
_ENUM_KEY_VALUE(EC_POINT_FORMATS, 11) \
_ENUM_KEY_VALUE(SRP, 12) \
_ENUM_KEY_VALUE(SIGNATURE_ALGORITHMS, 13) \
_ENUM_KEY_VALUE(USE_SRTP, 14) \
_ENUM_KEY_VALUE(HEARTBEAT, 15) \
_ENUM_KEY_VALUE(APPLICATION_LAYER_PROTOCOL_NEGOTIATION, 16) \
_ENUM_KEY_VALUE(STATUS_REQUEST_V2, 17) \
_ENUM_KEY_VALUE(SIGNED_CERTIFICATE_TIMESTAMP, 18) \
_ENUM_KEY_VALUE(CLIENT_CERTIFICATE_TYPE, 19) \
_ENUM_KEY_VALUE(SERVER_CERTIFICATE_TYPE, 20) \
_ENUM_KEY_VALUE(PADDING, 21) \
_ENUM_KEY_VALUE(ENCRYPT_THEN_MAC, 22) \
_ENUM_KEY_VALUE(EXTENDED_MASTER_SECRET, 23) \
_ENUM_KEY_VALUE(TOKEN_BINDING, 24) \
_ENUM_KEY_VALUE(CACHED_INFO, 25) \
_ENUM_KEY_VALUE(TLS_LTS, 26) \
_ENUM_KEY_VALUE(COMPRESS_CERTIFICATE, 27) \
_ENUM_KEY_VALUE(RECORD_SIZE_LIMIT, 28) \
_ENUM_KEY_VALUE(PWD_PROTECT, 29) \
_ENUM_KEY_VALUE(PWD_CLEAR, 30) \
_ENUM_KEY_VALUE(PASSWORD_SALT, 31) \
_ENUM_KEY_VALUE(TICKET_PINNING, 32) \
_ENUM_KEY_VALUE(TLS_CERT_WITH_EXTERN_PSK, 33) \
_ENUM_KEY_VALUE(DELEGATED_CREDENTIALS, 34) \
_ENUM_KEY_VALUE(SESSION_TICKET, 35) \
_ENUM_KEY_VALUE(TLMSP, 36) \
_ENUM_KEY_VALUE(TLMSP_PROXYING, 37) \
_ENUM_KEY_VALUE(TLMSP_DELEGATE, 38) \
_ENUM_KEY_VALUE(SUPPORTED_EKT_CIPHERS, 39) \
_ENUM_KEY_VALUE(PRE_SHARED_KEY, 41) \
_ENUM_KEY_VALUE(EARLY_DATA, 42) \
_ENUM_KEY_VALUE(SUPPORTED_VERSIONS, 43) \
_ENUM_KEY_VALUE(COOKIE, 44) \
_ENUM_KEY_VALUE(PSK_KEY_EXCHANGE_MODES, 45) \
_ENUM_KEY_VALUE(CERTIFICATE_AUTHORITIES, 47) \
_ENUM_KEY_VALUE(OID_FILTERS, 48) \
_ENUM_KEY_VALUE(POST_HANDSHAKE_AUTH, 49) \
_ENUM_KEY_VALUE(SIGNATURE_ALGORITHMS_CERT, 50) \
_ENUM_KEY_VALUE(KEY_SHARE, 51) \
_ENUM_KEY_VALUE(TRANSPARENCY_INFO, 52) \
_ENUM_KEY_VALUE(CONNECTION_ID_DEPRECATED, 53) \
_ENUM_KEY_VALUE(CONNECTION_ID, 54) \
_ENUM_KEY_VALUE(EXTERNAL_ID_HASH, 55) \
_ENUM_KEY_VALUE(EXTERNAL_SESSION_ID, 56) \
_ENUM_KEY_VALUE(QUIC_TRANSPORT_PARAMETERS, 57) \
_ENUM_KEY_VALUE(TICKET_REQUEST, 58) \
_ENUM_KEY_VALUE(DNSSEC_CHAIN, 59) \
_ENUM_KEY_VALUE(RENEGOTIATION_INFO, 65281)
enum class ExtensionType : u16 {
__ENUM_EXTENSION_TYPES
};
#define __ENUM_NAME_TYPES \
_ENUM_KEY_VALUE(HOST_NAME, 0)
enum class NameType : u8 {
__ENUM_NAME_TYPES
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
#define __ENUM_EC_CURVE_TYPES \
_ENUM_KEY_VALUE(EXPLICIT_PRIME, 1) \
_ENUM_KEY_VALUE(EXPLICIT_CHAR2, 2) \
_ENUM_KEY_VALUE(NAMED_CURVE, 3)
enum class ECCurveType : u8 {
__ENUM_EC_CURVE_TYPES
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
#define __ENUM_SUPPORTED_GROUPS \
_ENUM_KEY_VALUE(SECT163K1, 0x0001) \
_ENUM_KEY_VALUE(SECT163R1, 0x0002) \
_ENUM_KEY_VALUE(SECT163R2, 0x0003) \
_ENUM_KEY_VALUE(SECT193R1, 0x0004) \
_ENUM_KEY_VALUE(SECT193R2, 0x0005) \
_ENUM_KEY_VALUE(SECT233K1, 0x0006) \
_ENUM_KEY_VALUE(SECT233R1, 0x0007) \
_ENUM_KEY_VALUE(SECT239K1, 0x0008) \
_ENUM_KEY_VALUE(SECT283K1, 0x0009) \
_ENUM_KEY_VALUE(SECT283R1, 0x000a) \
_ENUM_KEY_VALUE(SECT409K1, 0x000b) \
_ENUM_KEY_VALUE(SECT409R1, 0x000c) \
_ENUM_KEY_VALUE(SECT571K1, 0x000d) \
_ENUM_KEY_VALUE(SECT571R1, 0x000e) \
_ENUM_KEY_VALUE(SECP160K1, 0x000f) \
_ENUM_KEY_VALUE(SECP160R1, 0x0010) \
_ENUM_KEY_VALUE(SECP160R2, 0x0011) \
_ENUM_KEY_VALUE(SECP192K1, 0x0012) \
_ENUM_KEY_VALUE(SECP192R1, 0x0013) \
_ENUM_KEY_VALUE(SECP224K1, 0x0014) \
_ENUM_KEY_VALUE(SECP224R1, 0x0015) \
_ENUM_KEY_VALUE(SECP256K1, 0x0016) \
_ENUM_KEY_VALUE(SECP256R1, 0x0017) \
_ENUM_KEY_VALUE(SECP384R1, 0x0018) \
_ENUM_KEY_VALUE(SECP521R1, 0x0019) \
_ENUM_KEY_VALUE(BRAINPOOLP256R1, 0x001a) \
_ENUM_KEY_VALUE(BRAINPOOLP384R1, 0x001b) \
_ENUM_KEY_VALUE(BRAINPOOLP512R1, 0x001c) \
_ENUM_KEY_VALUE(X25519, 0x001d) \
_ENUM_KEY_VALUE(X448, 0x001e) \
_ENUM_KEY_VALUE(BRAINPOOLP256R1TLS13, 0x001f) \
_ENUM_KEY_VALUE(BRAINPOOLP384R1TLS13, 0x0020) \
_ENUM_KEY_VALUE(BRAINPOOLP512R1TLS13, 0x0021) \
_ENUM_KEY_VALUE(GC256A, 0x0022) \
_ENUM_KEY_VALUE(GC256B, 0x0023) \
_ENUM_KEY_VALUE(GC256C, 0x0024) \
_ENUM_KEY_VALUE(GC256D, 0x0025) \
_ENUM_KEY_VALUE(GC512A, 0x0026) \
_ENUM_KEY_VALUE(GC512B, 0x0027) \
_ENUM_KEY_VALUE(GC512C, 0x0028) \
_ENUM_KEY_VALUE(CURVESM2, 0x0029) \
_ENUM_KEY_VALUE(FFDHE2048, 0x0100) \
_ENUM_KEY_VALUE(FFDHE3072, 0x0101) \
_ENUM_KEY_VALUE(FFDHE4096, 0x0102) \
_ENUM_KEY_VALUE(FFDHE6144, 0x0103) \
_ENUM_KEY_VALUE(FFDHE8192, 0x0104) \
_ENUM_KEY_VALUE(ARBITRARY_EXPLICIT_PRIME_CURVES, 0xff01) \
_ENUM_KEY_VALUE(ARBITRARY_EXPLICIT_CHAR2_CURVES, 0xff02) \
_ENUM_KEY_VALUE(GREASE_0, 0x0A0A) \
_ENUM_KEY_VALUE(GREASE_1, 0x1A1A) \
_ENUM_KEY_VALUE(GREASE_2, 0x2A2A) \
_ENUM_KEY_VALUE(GREASE_3, 0x3A3A) \
_ENUM_KEY_VALUE(GREASE_4, 0x4A4A) \
_ENUM_KEY_VALUE(GREASE_5, 0x5A5A) \
_ENUM_KEY_VALUE(GREASE_6, 0x6A6A) \
_ENUM_KEY_VALUE(GREASE_7, 0x7A7A) \
_ENUM_KEY_VALUE(GREASE_8, 0x8A8A) \
_ENUM_KEY_VALUE(GREASE_9, 0x9A9A) \
_ENUM_KEY_VALUE(GREASE_A, 0xAAAA) \
_ENUM_KEY_VALUE(GREASE_B, 0xBABA) \
_ENUM_KEY_VALUE(GREASE_C, 0xCACA) \
_ENUM_KEY_VALUE(GREASE_D, 0xDADA) \
_ENUM_KEY_VALUE(GREASE_E, 0xEAEA) \
_ENUM_KEY_VALUE(GREASE_F, 0xFAFA)
enum class SupportedGroup : u16 {
__ENUM_SUPPORTED_GROUPS
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-9
#define __ENUM_EC_POINT_FORMATS \
_ENUM_KEY_VALUE(UNCOMPRESSED, 0) \
_ENUM_KEY_VALUE(ANSIX962_COMPRESSED_PRIME, 1) \
_ENUM_KEY_VALUE(ANSIX962_COMPRESSED_CHAR2, 2)
enum class ECPointFormat : u8 {
__ENUM_EC_POINT_FORMATS
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
#define __ENUM_SIGNATURE_ALGORITHM \
_ENUM_KEY_VALUE(ANONYMOUS, 0) \
_ENUM_KEY_VALUE(RSA, 1) \
_ENUM_KEY_VALUE(DSA, 2) \
_ENUM_KEY_VALUE(ECDSA, 3) \
_ENUM_KEY_VALUE(ED25519, 7) \
_ENUM_KEY_VALUE(ED448, 8) \
_ENUM_KEY_VALUE(GOSTR34102012_256, 64) \
_ENUM_KEY_VALUE(GOSTR34102012_512, 65)
enum class SignatureAlgorithm : u8 {
__ENUM_SIGNATURE_ALGORITHM
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
#define __ENUM_HASH_ALGORITHM \
_ENUM_KEY_VALUE(NONE, 0) \
_ENUM_KEY_VALUE(MD5, 1) \
_ENUM_KEY_VALUE(SHA1, 2) \
_ENUM_KEY_VALUE(SHA224, 3) \
_ENUM_KEY_VALUE(SHA256, 4) \
_ENUM_KEY_VALUE(SHA384, 5) \
_ENUM_KEY_VALUE(SHA512, 6) \
_ENUM_KEY_VALUE(INTRINSIC, 8)
enum class HashAlgorithm : u8 {
__ENUM_HASH_ALGORITHM
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
#define __ENUM_CIPHER_SUITES \
_ENUM_KEY_VALUE(TLS_NULL_WITH_NULL_NULL, 0x0000) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_NULL_MD5, 0x0001) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_NULL_SHA, 0x0002) \
_ENUM_KEY_VALUE(TLS_RSA_EXPORT_WITH_RC4_40_MD5, 0x0003) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_RC4_128_MD5, 0x0004) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_RC4_128_SHA, 0x0005) \
_ENUM_KEY_VALUE(TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, 0x0006) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_IDEA_CBC_SHA, 0x0007) \
_ENUM_KEY_VALUE(TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x0008) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_DES_CBC_SHA, 0x0009) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_3DES_EDE_CBC_SHA, 0x000A) \
_ENUM_KEY_VALUE(TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, 0x000B) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_DES_CBC_SHA, 0x000C) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, 0x000D) \
_ENUM_KEY_VALUE(TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x000E) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_DES_CBC_SHA, 0x000F) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, 0x0010) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, 0x0011) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_DES_CBC_SHA, 0x0012) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 0x0013) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, 0x0014) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_DES_CBC_SHA, 0x0015) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 0x0016) \
_ENUM_KEY_VALUE(TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, 0x0017) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_RC4_128_MD5, 0x0018) \
_ENUM_KEY_VALUE(TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, 0x0019) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_DES_CBC_SHA, 0x001A) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, 0x001B) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_DES_CBC_SHA, 0x001E) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_3DES_EDE_CBC_SHA, 0x001F) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_RC4_128_SHA, 0x0020) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_IDEA_CBC_SHA, 0x0021) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_DES_CBC_MD5, 0x0022) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_3DES_EDE_CBC_MD5, 0x0023) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_RC4_128_MD5, 0x0024) \
_ENUM_KEY_VALUE(TLS_KRB5_WITH_IDEA_CBC_MD5, 0x0025) \
_ENUM_KEY_VALUE(TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, 0x0026) \
_ENUM_KEY_VALUE(TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA, 0x0027) \
_ENUM_KEY_VALUE(TLS_KRB5_EXPORT_WITH_RC4_40_SHA, 0x0028) \
_ENUM_KEY_VALUE(TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5, 0x0029) \
_ENUM_KEY_VALUE(TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5, 0x002A) \
_ENUM_KEY_VALUE(TLS_KRB5_EXPORT_WITH_RC4_40_MD5, 0x002B) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_NULL_SHA, 0x002C) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_NULL_SHA, 0x002D) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_NULL_SHA, 0x002E) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_128_CBC_SHA, 0x002F) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_AES_128_CBC_SHA, 0x0030) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_AES_128_CBC_SHA, 0x0031) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 0x0032) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 0x0033) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_AES_128_CBC_SHA, 0x0034) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_256_CBC_SHA, 0x0035) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_AES_256_CBC_SHA, 0x0036) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_AES_256_CBC_SHA, 0x0037) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 0x0038) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 0x0039) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_AES_256_CBC_SHA, 0x003A) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_NULL_SHA256, 0x003B) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_128_CBC_SHA256, 0x003C) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_256_CBC_SHA256, 0x003D) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, 0x003E) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, 0x003F) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, 0x0040) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, 0x0041) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA, 0x0042) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA, 0x0043) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, 0x0044) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, 0x0045) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA, 0x0046) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 0x0067) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, 0x0068) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, 0x0069) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, 0x006A) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, 0x006B) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_AES_128_CBC_SHA256, 0x006C) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_AES_256_CBC_SHA256, 0x006D) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, 0x0084) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA, 0x0085) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA, 0x0086) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, 0x0087) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, 0x0088) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA, 0x0089) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_RC4_128_SHA, 0x008A) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_3DES_EDE_CBC_SHA, 0x008B) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_128_CBC_SHA, 0x008C) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_256_CBC_SHA, 0x008D) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_RC4_128_SHA, 0x008E) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, 0x008F) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, 0x0090) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, 0x0091) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_RC4_128_SHA, 0x0092) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, 0x0093) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, 0x0094) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, 0x0095) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_SEED_CBC_SHA, 0x0096) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_SEED_CBC_SHA, 0x0097) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_SEED_CBC_SHA, 0x0098) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_SEED_CBC_SHA, 0x0099) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_SEED_CBC_SHA, 0x009A) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_SEED_CBC_SHA, 0x009B) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_128_GCM_SHA256, 0x009C) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_256_GCM_SHA384, 0x009D) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 0x009E) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 0x009F) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, 0x00A0) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, 0x00A1) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 0x00A2) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, 0x00A3) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, 0x00A4) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, 0x00A5) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_AES_128_GCM_SHA256, 0x00A6) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_AES_256_GCM_SHA384, 0x00A7) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_128_GCM_SHA256, 0x00A8) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_256_GCM_SHA384, 0x00A9) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, 0x00AA) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, 0x00AB) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, 0x00AC) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, 0x00AD) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_128_CBC_SHA256, 0x00AE) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_256_CBC_SHA384, 0x00AF) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_NULL_SHA256, 0x00B0) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_NULL_SHA384, 0x00B1) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, 0x00B2) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, 0x00B3) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_NULL_SHA256, 0x00B4) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_NULL_SHA384, 0x00B5) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, 0x00B6) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, 0x00B7) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_NULL_SHA256, 0x00B8) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_NULL_SHA384, 0x00B9) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, 0x00BA) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256, 0x00BB) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256, 0x00BC) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, 0x00BD) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, 0x00BE) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256, 0x00BF) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, 0x00C0) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256, 0x00C1) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256, 0x00C2) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, 0x00C3) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, 0x00C4) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, 0x00C5) \
_ENUM_KEY_VALUE(TLS_SM4_GCM_SM3, 0x00C6) \
_ENUM_KEY_VALUE(TLS_SM4_CCM_SM3, 0x00C7) \
_ENUM_KEY_VALUE(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, 0x00FF) \
_ENUM_KEY_VALUE(TLS_AES_128_GCM_SHA256, 0x1301) \
_ENUM_KEY_VALUE(TLS_AES_256_GCM_SHA384, 0x1302) \
_ENUM_KEY_VALUE(TLS_CHACHA20_POLY1305_SHA256, 0x1303) \
_ENUM_KEY_VALUE(TLS_AES_128_CCM_SHA256, 0x1304) \
_ENUM_KEY_VALUE(TLS_AES_128_CCM_8_SHA256, 0x1305) \
_ENUM_KEY_VALUE(TLS_FALLBACK_SCSV, 0x5600) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_NULL_SHA, 0xC001) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, 0xC002) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC003) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, 0xC004) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, 0xC005) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_NULL_SHA, 0xC006) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 0xC007) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC008) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 0xC009) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 0xC00A) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_NULL_SHA, 0xC00B) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_RC4_128_SHA, 0xC00C) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, 0xC00D) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, 0xC00E) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, 0xC00F) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_NULL_SHA, 0xC010) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_RC4_128_SHA, 0xC011) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 0xC012) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0xC013) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0xC014) \
_ENUM_KEY_VALUE(TLS_ECDH_anon_WITH_NULL_SHA, 0xC015) \
_ENUM_KEY_VALUE(TLS_ECDH_anon_WITH_RC4_128_SHA, 0xC016) \
_ENUM_KEY_VALUE(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, 0xC017) \
_ENUM_KEY_VALUE(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, 0xC018) \
_ENUM_KEY_VALUE(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, 0xC019) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, 0xC01A) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, 0xC01B) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, 0xC01C) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_WITH_AES_128_CBC_SHA, 0xC01D) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, 0xC01E) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, 0xC01F) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_WITH_AES_256_CBC_SHA, 0xC020) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, 0xC021) \
_ENUM_KEY_VALUE(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, 0xC022) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 0xC023) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 0xC024) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, 0xC025) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, 0xC026) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0xC027) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 0xC028) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, 0xC029) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, 0xC02A) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02B) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02C) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02D) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02E) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0xC02F) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0xC030) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, 0xC031) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, 0xC032) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_RC4_128_SHA, 0xC033) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, 0xC034) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 0xC035) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 0xC036) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, 0xC037) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, 0xC038) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_NULL_SHA, 0xC039) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_NULL_SHA256, 0xC03A) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_NULL_SHA384, 0xC03B) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_ARIA_128_CBC_SHA256, 0xC03C) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_ARIA_256_CBC_SHA384, 0xC03D) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256, 0xC03E) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384, 0xC03F) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256, 0xC040) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384, 0xC041) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, 0xC042) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, 0xC043) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, 0xC044) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, 0xC045) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_ARIA_128_CBC_SHA256, 0xC046) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_ARIA_256_CBC_SHA384, 0xC047) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, 0xC048) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, 0xC049) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, 0xC04A) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, 0xC04B) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, 0xC04C) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, 0xC04D) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, 0xC04E) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, 0xC04F) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_ARIA_128_GCM_SHA256, 0xC050) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_ARIA_256_GCM_SHA384, 0xC051) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, 0xC052) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, 0xC053) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256, 0xC054) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384, 0xC055) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256, 0xC056) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384, 0xC057) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256, 0xC058) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384, 0xC059) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_ARIA_128_GCM_SHA256, 0xC05A) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_ARIA_256_GCM_SHA384, 0xC05B) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, 0xC05C) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, 0xC05D) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, 0xC05E) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, 0xC05F) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, 0xC060) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, 0xC061) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, 0xC062) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, 0xC063) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_ARIA_128_CBC_SHA256, 0xC064) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_ARIA_256_CBC_SHA384, 0xC065) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, 0xC066) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, 0xC067) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, 0xC068) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, 0xC069) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_ARIA_128_GCM_SHA256, 0xC06A) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_ARIA_256_GCM_SHA384, 0xC06B) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, 0xC06C) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, 0xC06D) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, 0xC06E) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, 0xC06F) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, 0xC070) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, 0xC071) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, 0xC072) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, 0xC073) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, 0xC074) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, 0xC075) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, 0xC076) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, 0xC077) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, 0xC078) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, 0xC079) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC07A) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC07B) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC07C) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC07D) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC07E) \
_ENUM_KEY_VALUE(TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC07F) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256, 0xC080) \
_ENUM_KEY_VALUE(TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384, 0xC081) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256, 0xC082) \
_ENUM_KEY_VALUE(TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384, 0xC083) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256, 0xC084) \
_ENUM_KEY_VALUE(TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384, 0xC085) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC086) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC087) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC088) \
_ENUM_KEY_VALUE(TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC089) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC08A) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC08B) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, 0xC08C) \
_ENUM_KEY_VALUE(TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, 0xC08D) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, 0xC08E) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, 0xC08F) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, 0xC090) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, 0xC091) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, 0xC092) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, 0xC093) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, 0xC094) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, 0xC095) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, 0xC096) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, 0xC097) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, 0xC098) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, 0xC099) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, 0xC09A) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, 0xC09B) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_128_CCM, 0xC09C) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_256_CCM, 0xC09D) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_128_CCM, 0xC09E) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_256_CCM, 0xC09F) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_128_CCM_8, 0xC0A0) \
_ENUM_KEY_VALUE(TLS_RSA_WITH_AES_256_CCM_8, 0xC0A1) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_128_CCM_8, 0xC0A2) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_AES_256_CCM_8, 0xC0A3) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_128_CCM, 0xC0A4) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_256_CCM, 0xC0A5) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_128_CCM, 0xC0A6) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_AES_256_CCM, 0xC0A7) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_128_CCM_8, 0xC0A8) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_AES_256_CCM_8, 0xC0A9) \
_ENUM_KEY_VALUE(TLS_PSK_DHE_WITH_AES_128_CCM_8, 0xC0AA) \
_ENUM_KEY_VALUE(TLS_PSK_DHE_WITH_AES_256_CCM_8, 0xC0AB) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, 0xC0AC) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, 0xC0AD) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, 0xC0AE) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, 0xC0AF) \
_ENUM_KEY_VALUE(TLS_ECCPWD_WITH_AES_128_GCM_SHA256, 0xC0B0) \
_ENUM_KEY_VALUE(TLS_ECCPWD_WITH_AES_256_GCM_SHA384, 0xC0B1) \
_ENUM_KEY_VALUE(TLS_ECCPWD_WITH_AES_128_CCM_SHA256, 0xC0B2) \
_ENUM_KEY_VALUE(TLS_ECCPWD_WITH_AES_256_CCM_SHA384, 0xC0B3) \
_ENUM_KEY_VALUE(TLS_SHA256_SHA256, 0xC0B4) \
_ENUM_KEY_VALUE(TLS_SHA384_SHA384, 0xC0B5) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC, 0xC100) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC, 0xC101) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_28147_CNT_IMIT, 0xC102) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L, 0xC103) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_MAGMA_MGM_L, 0xC104) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S, 0xC105) \
_ENUM_KEY_VALUE(TLS_GOSTR341112_256_WITH_MAGMA_MGM_S, 0xC106) \
_ENUM_KEY_VALUE(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0xCCA8) \
_ENUM_KEY_VALUE(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0xCCA9) \
_ENUM_KEY_VALUE(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0xCCAA) \
_ENUM_KEY_VALUE(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAB) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAC) \
_ENUM_KEY_VALUE(TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAD) \
_ENUM_KEY_VALUE(TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAE) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, 0xD001) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, 0xD002) \
_ENUM_KEY_VALUE(TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256, 0xD003) \
_ENUM_KEY_VALUE(GREASE_0, 0x0A0A) \
_ENUM_KEY_VALUE(GREASE_1, 0x1A1A) \
_ENUM_KEY_VALUE(GREASE_2, 0x2A2A) \
_ENUM_KEY_VALUE(GREASE_3, 0x3A3A) \
_ENUM_KEY_VALUE(GREASE_4, 0x4A4A) \
_ENUM_KEY_VALUE(GREASE_5, 0x5A5A) \
_ENUM_KEY_VALUE(GREASE_6, 0x6A6A) \
_ENUM_KEY_VALUE(GREASE_7, 0x7A7A) \
_ENUM_KEY_VALUE(GREASE_8, 0x8A8A) \
_ENUM_KEY_VALUE(GREASE_9, 0x9A9A) \
_ENUM_KEY_VALUE(GREASE_A, 0xAAAA) \
_ENUM_KEY_VALUE(GREASE_B, 0xBABA) \
_ENUM_KEY_VALUE(GREASE_C, 0xCACA) \
_ENUM_KEY_VALUE(GREASE_D, 0XDADA) \
_ENUM_KEY_VALUE(GREASE_E, 0xEAEA) \
_ENUM_KEY_VALUE(GREASE_F, 0xFAFA)
enum class CipherSuite : u16 {
__ENUM_CIPHER_SUITES
};
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-6
#define __ENUM_ALERT_DESCRIPTIONS \
_ENUM_KEY_VALUE(CLOSE_NOTIFY, 0) \
_ENUM_KEY_VALUE(UNEXPECTED_MESSAGE, 10) \
_ENUM_KEY_VALUE(BAD_RECORD_MAC, 20) \
_ENUM_KEY_VALUE(DECRYPTION_FAILED_RESERVED, 21) \
_ENUM_KEY_VALUE(RECORD_OVERFLOW, 22) \
_ENUM_KEY_VALUE(DECOMPRESSION_FAILURE_RESERVED, 30) \
_ENUM_KEY_VALUE(HANDSHAKE_FAILURE, 40) \
_ENUM_KEY_VALUE(NO_CERTIFICATE_RESERVED, 41) \
_ENUM_KEY_VALUE(BAD_CERTIFICATE, 42) \
_ENUM_KEY_VALUE(UNSUPPORTED_CERTIFICATE, 43) \
_ENUM_KEY_VALUE(CERTIFICATE_REVOKED, 44) \
_ENUM_KEY_VALUE(CERTIFICATE_EXPIRED, 45) \
_ENUM_KEY_VALUE(CERTIFICATE_UNKNOWN, 46) \
_ENUM_KEY_VALUE(ILLEGAL_PARAMETER, 47) \
_ENUM_KEY_VALUE(UNKNOWN_CA, 48) \
_ENUM_KEY_VALUE(ACCESS_DENIED, 49) \
_ENUM_KEY_VALUE(DECODE_ERROR, 50) \
_ENUM_KEY_VALUE(DECRYPT_ERROR, 51) \
_ENUM_KEY_VALUE(TOO_MANY_CIDS_REQUESTED, 52) \
_ENUM_KEY_VALUE(EXPORT_RESTRICTION_RESERVED, 60) \
_ENUM_KEY_VALUE(PROTOCOL_VERSION, 70) \
_ENUM_KEY_VALUE(INSUFFICIENT_SECURITY, 71) \
_ENUM_KEY_VALUE(INTERNAL_ERROR, 80) \
_ENUM_KEY_VALUE(INAPPROPRIATE_FALLBACK, 86) \
_ENUM_KEY_VALUE(USER_CANCELED, 90) \
_ENUM_KEY_VALUE(NO_RENEGOTIATION_RESERVED, 100) \
_ENUM_KEY_VALUE(MISSING_EXTENSION, 109) \
_ENUM_KEY_VALUE(UNSUPPORTED_EXTENSION, 110) \
_ENUM_KEY_VALUE(CERTIFICATE_UNOBTAINABLE_RESERVED, 111) \
_ENUM_KEY_VALUE(UNRECOGNIZED_NAME, 112) \
_ENUM_KEY_VALUE(BAD_CERTIFICATE_STATUS_RESPONSE, 113) \
_ENUM_KEY_VALUE(BAD_CERTIFICATE_HASH_VALUE_RESERVED, 114) \
_ENUM_KEY_VALUE(UNKNOWN_PSK_IDENTITY, 115) \
_ENUM_KEY_VALUE(CERTIFICATE_REQUIRED, 116) \
_ENUM_KEY_VALUE(NO_APPLICATION_PROTOCOL, 120)
enum class AlertDescription : u8 {
__ENUM_ALERT_DESCRIPTIONS
};
#undef _ENUM_KEY
#undef _ENUM_KEY_VALUE
constexpr static StringView enum_to_string(CipherSuite descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case CipherSuite::name: \
return #name##sv;
switch (descriptor) {
__ENUM_CIPHER_SUITES
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView enum_to_string(ExtensionType descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case ExtensionType::name: \
return #name##sv;
switch (descriptor) {
__ENUM_EXTENSION_TYPES
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView enum_to_string(ContentType descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case ContentType::name: \
return #name##sv;
switch (descriptor) {
__ENUM_CONTENT_TYPES
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView enum_to_string(ProtocolVersion descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case ProtocolVersion::name: \
return #name##sv;
switch (descriptor) {
__ENUM_PROTOCOL_VERSIONS
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView enum_to_string(HandshakeType descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case HandshakeType::name: \
return #name##sv;
switch (descriptor) {
__ENUM_HANDSHAKE_TYPES
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView enum_to_string(SignatureAlgorithm descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case SignatureAlgorithm::name: \
return #name##sv;
switch (descriptor) {
__ENUM_SIGNATURE_ALGORITHM
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView enum_to_string(AlertDescription descriptor)
{
#define _ENUM_KEY_VALUE(name, value) \
case AlertDescription::name: \
return #name##sv;
switch (descriptor) {
__ENUM_ALERT_DESCRIPTIONS
}
return "Unknown"sv;
#undef _ENUM_KEY_VALUE
}
constexpr static StringView const enum_to_value(AlertDescription descriptor)
{
switch (descriptor) {
case AlertDescription::UNEXPECTED_MESSAGE:
return "An inappropriate message was received. "
"This alert is always fatal and should never be observed in communication between proper implementations."sv;
case AlertDescription::BAD_RECORD_MAC:
return "This alert is returned if a record is received with an incorrect MAC. "
"This alert also MUST be returned if an alert is sent because a TLSCiphertext decrypted in an invalid way: "
"either it wasn't an even multiple of the block length, "
"or its padding values, when checked, weren't correct. "
"This message is always fatal."sv;
case AlertDescription::DECRYPTION_FAILED_RESERVED:
return "This alert MAY be returned if a TLSCiphertext decrypted in an invalid way: "
"either it wasn't an even multiple of the block length, "
"or its padding values, when checked, weren't correct. "
"This message is always fatal."sv;
case AlertDescription::RECORD_OVERFLOW:
return "A TLSCiphertext record was received that had a length more than 2^14 + 2048 bytes, "
"or a record decrypted to a TLSCompressed record with more than 2^14 + 1024 bytes. "
"This message is always fatal."sv;
case AlertDescription::DECOMPRESSION_FAILURE_RESERVED:
return "The decompression function received improper input (e.g., data that would expand to excessive length). "
"This message is always fatal."sv;
case AlertDescription::HANDSHAKE_FAILURE:
return "Reception of a handshake_failure alert message indicates that the sender "
"was unable to negotiate an acceptable set of security parameters given the options available. "
"This is a fatal error."sv;
case AlertDescription::NO_CERTIFICATE_RESERVED:
return "This alert was used in SSLv3 but not in TLS. It should not be sent by compliant implementations."sv;
case AlertDescription::BAD_CERTIFICATE:
return "A certificate was corrupt, contained signatures that did not verify correctly, etc."sv;
case AlertDescription::UNSUPPORTED_CERTIFICATE:
return "A certificate was of an unsupported type."sv;
case AlertDescription::CERTIFICATE_REVOKED:
return "A certificate was revoked by its signer."sv;
case AlertDescription::CERTIFICATE_EXPIRED:
return "A certificate has expired or is not currently valid."sv;
case AlertDescription::CERTIFICATE_UNKNOWN:
return "Some other (unspecified) issue arose in processing the certificate, rendering it unacceptable."sv;
case AlertDescription::ILLEGAL_PARAMETER:
return "A field in the handshake was out of range or inconsistent with other fields. "
"This is always fatal."sv;
case AlertDescription::UNKNOWN_CA:
return "A valid certificate chain or partial chain was received, but the certificate was not accepted "
"because the CA certificate could not be located "
"or couldn't be matched with a known, trusted CA. "
"This message is always fatal."sv;
case AlertDescription::ACCESS_DENIED:
return "A valid certificate was received, but when access control was applied, "
"the sender decided not to proceed with negotiation. "
"This message is always fatal."sv;
case AlertDescription::DECODE_ERROR:
return "A message could not be decoded because some field was out of the specified range "
"or the length of the message was incorrect. "
"This message is always fatal."sv;
case AlertDescription::DECRYPT_ERROR:
return "A handshake cryptographic operation failed, "
"including being unable to correctly verify a signature, "
"decrypt a key exchange, or validate a finished message."sv;
case AlertDescription::EXPORT_RESTRICTION_RESERVED:
return "This alert was used in TLS 1.0 but not TLS 1.1."sv;
case AlertDescription::PROTOCOL_VERSION:
return "The protocol version the client has attempted to negotiate is recognized but not supported. "
"(For example, old protocol versions might be avoided for security reasons). "
"This message is always fatal."sv;
case AlertDescription::INSUFFICIENT_SECURITY:
return "Returned instead of handshake_failure when a negotiation has failed"
"specifically because the server requires ciphers more secure than those supported by the client."
"This message is always fatal."sv;
case AlertDescription::INTERNAL_ERROR:
return "An internal error unrelated to the peer "
"or the correctness of the protocol (such as a memory allocation failure) "
"makes it impossible to continue. "
"This message is always fatal."sv;
case AlertDescription::USER_CANCELED:
return "This handshake is being canceled for some reason unrelated to a protocol failure. "
"If the user cancels an operation after the handshake is complete, "
"just closing the connection by sending a close_notify is more appropriate. "
"This alert should be followed by a close_notify. "
"This message is generally a warning."sv;
case AlertDescription::NO_RENEGOTIATION_RESERVED:
return "Sent by the client in response to a hello request "
"or by the server in response to a client hello after initial handshaking. "
"Either of these would normally lead to renegotiation; "
"when that is not appropriate, the recipient should respond with this alert. "
"At that point, the original requester can decide whether to proceed with the connection. "
"One case where this would be appropriate is where a server has spawned a process to satisfy a request; "
"the process might receive security parameters(key length, authentication, etc.) at startup "
"and it might be difficult to communicate changes to these parameters after that point. "
"This message is always a warning."sv;
case AlertDescription::CLOSE_NOTIFY:
return "This alert notifies the recipient that the sender will not send any more messages on this connection. "
"Any data received after a closure alert has been received MUST be ignored."sv;
case AlertDescription::INAPPROPRIATE_FALLBACK:
return "Sent by a server in response to an invalid connection retry attempt from a client (see [RFC7507])."sv;
case AlertDescription::MISSING_EXTENSION:
return "Sent by endpoints that receive a handshake message not containing an extension "
"that is mandatory to send for the offered TLS version or other negotiated parameters."sv;
case AlertDescription::CERTIFICATE_REQUIRED:
return "Sent by servers when a client certificate is desired but none was provided by the client."sv;
case AlertDescription::NO_APPLICATION_PROTOCOL:
return "Sent by servers when a client \"application_layer_protocol_negotiation\" extension "
"advertises only protocols that the server does not support (see [RFC7301])."sv;
case AlertDescription::UNSUPPORTED_EXTENSION:
return "Sent by endpoints receiving any handshake message containing an extension known to be prohibited "
"for inclusion in the given handshake message, "
"or including any extensions in a ServerHello "
"or Certificate not first offered in the corresponding ClientHello or CertificateRequest."sv;
case AlertDescription::CERTIFICATE_UNOBTAINABLE_RESERVED:
return "If a server is unable to obtain certificates in a given CertificateURL, "
"it MUST send a fatal certificate_unobtainable(111) alert "
"if it requires the certificates to complete the handshake."sv;
case AlertDescription::TOO_MANY_CIDS_REQUESTED:
return "Endpoints MAY handle an excessive number of RequestConnectionId messages by terminating the connection."sv;
case AlertDescription::UNRECOGNIZED_NAME:
return "The ServerNameList MUST NOT contain more than one name of the same name_type. "
"If the server understood the ClientHello extension but does not recognize the server name, "
"the server SHOULD take one of two actions: "
"either abort the handshake by sending a fatal-level unrecognized_name(112) alert or continue the handshake. "
"It is NOT RECOMMENDED to send a warning-level unrecognized_name(112) alert, "
"because the client's behavior in response to warning-level alerts is unpredictable."sv;
case AlertDescription::BAD_CERTIFICATE_STATUS_RESPONSE:
return "Clients requesting an OCSP response and receiving an OCSP response "
"in a \"CertificateStatus\" message MUST check the OCSP response "
"and abort the handshake if the response is not satisfactory. "
"This alert is always fatal."sv;
case AlertDescription::BAD_CERTIFICATE_HASH_VALUE_RESERVED:
return "The server MUST check that the SHA-1 hash of the contents of the object retrieved from that URL "
"(after decoding any MIME Content-Transfer-Encoding) matches the given hash. "
"If any retrieved object does not have the correct SHA-1 hash, "
"the server MUST abort the handshake. "
"This alert is always fatal."sv;
case AlertDescription::UNKNOWN_PSK_IDENTITY:
return "If the server does not recognize the PSK identity, it MAY respond with this message."
"Alternatively, if the server wishes to hide the fact that the PSK identity was not known, "
"it MAY continue the protocol as if the PSK identity existed but the key was incorrect: "
"that is, respond with a \"DECRYPT_ERROR\" alert."sv;
}
return "Unknown alert"sv;
}
}

View file

@ -1,558 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Endian.h>
#include <AK/Random.h>
#include <LibCore/Timer.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/SecureRandom.h>
#include <LibTLS/TLSv12.h>
namespace TLS {
ByteBuffer TLSv12::build_hello()
{
::Crypto::fill_with_secure_random(m_context.local_random);
auto packet_version = (u16)m_context.options.version;
auto version = (u16)m_context.options.version;
PacketBuilder builder { ContentType::HANDSHAKE, packet_version };
builder.append(to_underlying(HandshakeType::CLIENT_HELLO));
// hello length (for later)
u8 dummy[3] = {};
builder.append(dummy, 3);
auto start_length = builder.length();
builder.append(version);
builder.append(m_context.local_random, sizeof(m_context.local_random));
builder.append(m_context.session_id_size);
if (m_context.session_id_size)
builder.append(m_context.session_id, m_context.session_id_size);
size_t extension_length = 0;
size_t alpn_length = 0;
size_t alpn_negotiated_length = 0;
// ALPN
if (!m_context.negotiated_alpn.is_empty()) {
alpn_negotiated_length = m_context.negotiated_alpn.length();
alpn_length = alpn_negotiated_length + 1;
extension_length += alpn_length + 6;
} else if (m_context.alpn.size()) {
for (auto& alpn : m_context.alpn) {
size_t length = alpn.length();
alpn_length += length + 1;
}
if (alpn_length)
extension_length += alpn_length + 6;
}
// Ciphers
builder.append((u16)(m_context.options.usable_cipher_suites.size() * sizeof(u16)));
for (auto suite : m_context.options.usable_cipher_suites)
builder.append((u16)suite);
// we don't like compression
VERIFY(!m_context.options.use_compression);
builder.append((u8)1);
builder.append((u8)m_context.options.use_compression);
// set SNI if we have one, and the user hasn't explicitly asked us to omit it.
auto sni_length = 0;
if (!m_context.extensions.SNI.is_empty() && m_context.options.use_sni)
sni_length = m_context.extensions.SNI.length();
auto elliptic_curves_length = 2 * m_context.options.elliptic_curves.size();
auto supported_ec_point_formats_length = m_context.options.supported_ec_point_formats.size();
bool supports_elliptic_curves = elliptic_curves_length && supported_ec_point_formats_length;
bool enable_extended_master_secret = m_context.options.enable_extended_master_secret;
// signature_algorithms: 2b extension ID, 2b extension length, 2b vector length, 2xN signatures and hashes
extension_length += 2 + 2 + 2 + 2 * m_context.options.supported_signature_algorithms.size();
if (sni_length)
extension_length += sni_length + 9;
// Only send elliptic_curves and ec_point_formats extensions if both are supported
if (supports_elliptic_curves)
extension_length += 6 + elliptic_curves_length + 5 + supported_ec_point_formats_length;
if (enable_extended_master_secret)
extension_length += 4;
builder.append((u16)extension_length);
if (sni_length) {
// SNI extension
builder.append((u16)ExtensionType::SERVER_NAME);
// extension length
builder.append((u16)(sni_length + 5));
// SNI length
builder.append((u16)(sni_length + 3));
// SNI type
builder.append((u8)0);
// SNI host length + value
builder.append((u16)sni_length);
builder.append((u8 const*)m_context.extensions.SNI.characters(), sni_length);
}
// signature_algorithms extension
builder.append((u16)ExtensionType::SIGNATURE_ALGORITHMS);
// Extension length
builder.append((u16)(2 + 2 * m_context.options.supported_signature_algorithms.size()));
// Vector count
builder.append((u16)(m_context.options.supported_signature_algorithms.size() * 2));
// Entries
for (auto& entry : m_context.options.supported_signature_algorithms) {
builder.append((u8)entry.hash);
builder.append((u8)entry.signature);
}
if (supports_elliptic_curves) {
// elliptic_curves extension
builder.append((u16)ExtensionType::SUPPORTED_GROUPS);
builder.append((u16)(2 + elliptic_curves_length));
builder.append((u16)elliptic_curves_length);
for (auto& curve : m_context.options.elliptic_curves)
builder.append((u16)curve);
// ec_point_formats extension
builder.append((u16)ExtensionType::EC_POINT_FORMATS);
builder.append((u16)(1 + supported_ec_point_formats_length));
builder.append((u8)supported_ec_point_formats_length);
for (auto& format : m_context.options.supported_ec_point_formats)
builder.append((u8)format);
}
if (enable_extended_master_secret) {
// extended_master_secret extension
builder.append((u16)ExtensionType::EXTENDED_MASTER_SECRET);
builder.append((u16)0);
}
if (alpn_length) {
// TODO
VERIFY_NOT_REACHED();
}
// set the "length" field of the packet
size_t remaining = builder.length() - start_length;
size_t payload_position = 6;
builder.set(payload_position, remaining / 0x10000);
remaining %= 0x10000;
builder.set(payload_position + 1, remaining / 0x100);
remaining %= 0x100;
builder.set(payload_position + 2, remaining);
auto packet = builder.build();
update_packet(packet);
return packet;
}
ByteBuffer TLSv12::build_change_cipher_spec()
{
PacketBuilder builder { ContentType::CHANGE_CIPHER_SPEC, m_context.options.version, 64 };
builder.append((u8)1);
auto packet = builder.build();
update_packet(packet);
m_context.local_sequence_number = 0;
return packet;
}
ByteBuffer TLSv12::build_handshake_finished()
{
PacketBuilder builder { ContentType::HANDSHAKE, m_context.options.version, 12 + 64 };
builder.append((u8)HandshakeType::FINISHED);
// RFC 5246 section 7.4.9: "In previous versions of TLS, the verify_data was always 12 octets
// long. In the current version of TLS, it depends on the cipher
// suite. Any cipher suite which does not explicitly specify
// verify_data_length has a verify_data_length equal to 12."
// Simplification: Assume that verify_data_length is always 12.
constexpr u32 verify_data_length = 12;
builder.append_u24(verify_data_length);
u8 out[verify_data_length];
auto outbuffer = Bytes { out, verify_data_length };
ByteBuffer dummy;
auto digest = m_context.handshake_hash.digest();
auto hashbuf = ReadonlyBytes { digest.immutable_data(), m_context.handshake_hash.digest_size() };
pseudorandom_function(outbuffer, m_context.master_key, (u8 const*)"client finished", 15, hashbuf, dummy);
builder.append(outbuffer);
auto packet = builder.build();
update_packet(packet);
return packet;
}
ssize_t TLSv12::handle_handshake_finished(ReadonlyBytes buffer, WritePacketStage& write_packets)
{
if (m_context.connection_status < ConnectionStatus::KeyExchange || m_context.connection_status == ConnectionStatus::Established) {
dbgln("unexpected finished message");
return (i8)Error::UnexpectedMessage;
}
write_packets = WritePacketStage::Initial;
if (buffer.size() < 3) {
return (i8)Error::NeedMoreData;
}
size_t index = 3;
u32 size = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2];
if (size < 12) {
dbgln_if(TLS_DEBUG, "finished packet smaller than minimum size: {}", size);
return (i8)Error::BrokenPacket;
}
if (size < buffer.size() - index) {
dbgln_if(TLS_DEBUG, "not enough data after length: {} > {}", size, buffer.size() - index);
return (i8)Error::NeedMoreData;
}
// TODO: Compare Hashes
dbgln_if(TLS_DEBUG, "FIXME: handle_handshake_finished :: Check message validity");
m_context.connection_status = ConnectionStatus::Established;
if (m_handshake_timeout_timer) {
// Disable the handshake timeout timer as handshake has been established.
m_handshake_timeout_timer->stop();
m_handshake_timeout_timer->remove_from_parent();
m_handshake_timeout_timer = nullptr;
}
if (on_connected)
on_connected();
return index + size;
}
ssize_t TLSv12::handle_handshake_payload(ReadonlyBytes vbuffer)
{
if (m_context.connection_status == ConnectionStatus::Established) {
dbgln_if(TLS_DEBUG, "Renegotiation attempt ignored");
// FIXME: We should properly say "NoRenegotiation", but that causes a handshake failure
// so we just roll with it and pretend that we _did_ renegotiate
// This will cause issues when we decide to have long-lasting connections, but
// we do not have those at the moment :^)
return 1;
}
auto buffer = vbuffer;
auto buffer_length = buffer.size();
auto original_length = buffer_length;
while (buffer_length >= 4 && !m_context.critical_error) {
ssize_t payload_res = 0;
auto type = static_cast<HandshakeType>(buffer[0]);
auto write_packets { WritePacketStage::Initial };
size_t payload_size = buffer[1] * 0x10000 + buffer[2] * 0x100 + buffer[3] + 3;
dbgln_if(TLS_DEBUG, "payload size: {} buffer length: {}", payload_size, buffer_length);
if (payload_size + 1 > buffer_length)
return (i8)Error::NeedMoreData;
switch (type) {
case HandshakeType::HELLO_REQUEST_RESERVED:
if (m_context.handshake_messages[0] >= 1) {
dbgln("unexpected hello request message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[0];
dbgln("hello request (renegotiation?)");
if (m_context.connection_status == ConnectionStatus::Established) {
// renegotiation
payload_res = (i8)Error::NoRenegotiation;
} else {
// :shrug:
payload_res = (i8)Error::UnexpectedMessage;
}
break;
case HandshakeType::CLIENT_HELLO:
// FIXME: We only support client mode right now
if (m_context.is_server) {
VERIFY_NOT_REACHED();
}
payload_res = (i8)Error::UnexpectedMessage;
break;
case HandshakeType::SERVER_HELLO:
if (m_context.handshake_messages[2] >= 1) {
dbgln("unexpected server hello message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[2];
dbgln_if(TLS_DEBUG, "server hello");
if (m_context.is_server) {
dbgln("unsupported: server mode");
VERIFY_NOT_REACHED();
}
payload_res = handle_server_hello(buffer.slice(1, payload_size), write_packets);
break;
case HandshakeType::HELLO_VERIFY_REQUEST_RESERVED:
dbgln("unsupported: DTLS");
payload_res = (i8)Error::UnexpectedMessage;
break;
case HandshakeType::CERTIFICATE:
if (m_context.handshake_messages[4] >= 1) {
dbgln("unexpected certificate message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[4];
dbgln_if(TLS_DEBUG, "certificate");
if (m_context.connection_status == ConnectionStatus::Negotiating) {
if (m_context.is_server) {
dbgln("unsupported: server mode");
VERIFY_NOT_REACHED();
}
payload_res = handle_certificate(buffer.slice(1, payload_size));
} else {
payload_res = (i8)Error::UnexpectedMessage;
}
break;
case HandshakeType::SERVER_KEY_EXCHANGE_RESERVED:
if (m_context.handshake_messages[5] >= 1) {
dbgln("unexpected server key exchange message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[5];
dbgln_if(TLS_DEBUG, "server key exchange");
if (m_context.is_server) {
dbgln("unsupported: server mode");
VERIFY_NOT_REACHED();
} else {
payload_res = handle_server_key_exchange(buffer.slice(1, payload_size));
}
break;
case HandshakeType::CERTIFICATE_REQUEST:
if (m_context.handshake_messages[6] >= 1) {
dbgln("unexpected certificate request message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[6];
if (m_context.is_server) {
dbgln("invalid request");
dbgln("unsupported: server mode");
VERIFY_NOT_REACHED();
} else {
// we do not support "certificate request"
dbgln("certificate request");
if (on_tls_certificate_request)
on_tls_certificate_request(*this);
m_context.client_verified = VerificationNeeded;
}
break;
case HandshakeType::SERVER_HELLO_DONE_RESERVED:
if (m_context.handshake_messages[7] >= 1) {
dbgln("unexpected server hello done message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[7];
dbgln_if(TLS_DEBUG, "server hello done");
if (m_context.is_server) {
dbgln("unsupported: server mode");
VERIFY_NOT_REACHED();
} else {
payload_res = handle_server_hello_done(buffer.slice(1, payload_size));
if (payload_res > 0)
write_packets = WritePacketStage::ClientHandshake;
}
break;
case HandshakeType::CERTIFICATE_VERIFY:
if (m_context.handshake_messages[8] >= 1) {
dbgln("unexpected certificate verify message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[8];
dbgln_if(TLS_DEBUG, "certificate verify");
if (m_context.connection_status == ConnectionStatus::KeyExchange) {
payload_res = handle_certificate_verify(buffer.slice(1, payload_size));
} else {
payload_res = (i8)Error::UnexpectedMessage;
}
break;
case HandshakeType::CLIENT_KEY_EXCHANGE_RESERVED:
if (m_context.handshake_messages[9] >= 1) {
dbgln("unexpected client key exchange message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[9];
dbgln_if(TLS_DEBUG, "client key exchange");
if (m_context.is_server) {
dbgln("unsupported: server mode");
VERIFY_NOT_REACHED();
} else {
payload_res = (i8)Error::UnexpectedMessage;
}
break;
case HandshakeType::FINISHED:
m_context.cached_handshake.clear();
if (m_context.handshake_messages[10] >= 1) {
dbgln("unexpected finished message");
payload_res = (i8)Error::UnexpectedMessage;
break;
}
++m_context.handshake_messages[10];
dbgln_if(TLS_DEBUG, "finished");
payload_res = handle_handshake_finished(buffer.slice(1, payload_size), write_packets);
if (payload_res > 0) {
memset(m_context.handshake_messages, 0, sizeof(m_context.handshake_messages));
}
break;
default:
dbgln("message type not understood: {}", enum_to_string(type));
return (i8)Error::NotUnderstood;
}
if (type != HandshakeType::HELLO_REQUEST_RESERVED) {
update_hash(buffer.slice(0, payload_size + 1), 0);
}
// if something went wrong, send an alert about it
if (payload_res < 0) {
switch ((Error)payload_res) {
case Error::UnexpectedMessage: {
auto packet = build_alert(true, (u8)AlertDescription::UNEXPECTED_MESSAGE);
write_packet(packet);
break;
}
case Error::CompressionNotSupported: {
auto packet = build_alert(true, (u8)AlertDescription::DECOMPRESSION_FAILURE_RESERVED);
write_packet(packet);
break;
}
case Error::BrokenPacket: {
auto packet = build_alert(true, (u8)AlertDescription::DECODE_ERROR);
write_packet(packet);
break;
}
case Error::NotVerified: {
auto packet = build_alert(true, (u8)AlertDescription::BAD_RECORD_MAC);
write_packet(packet);
break;
}
case Error::BadCertificate: {
auto packet = build_alert(true, (u8)AlertDescription::BAD_CERTIFICATE);
write_packet(packet);
break;
}
case Error::UnsupportedCertificate: {
auto packet = build_alert(true, (u8)AlertDescription::UNSUPPORTED_CERTIFICATE);
write_packet(packet);
break;
}
case Error::NoCommonCipher: {
auto packet = build_alert(true, (u8)AlertDescription::INSUFFICIENT_SECURITY);
write_packet(packet);
break;
}
case Error::NotUnderstood:
case Error::OutOfMemory: {
auto packet = build_alert(true, (u8)AlertDescription::INTERNAL_ERROR);
write_packet(packet);
break;
}
case Error::NoRenegotiation: {
auto packet = build_alert(true, (u8)AlertDescription::NO_RENEGOTIATION_RESERVED);
write_packet(packet);
break;
}
case Error::DecryptionFailed: {
auto packet = build_alert(true, (u8)AlertDescription::DECRYPTION_FAILED_RESERVED);
write_packet(packet);
break;
}
case Error::NotSafe: {
auto packet = build_alert(true, (u8)AlertDescription::DECRYPT_ERROR);
write_packet(packet);
break;
}
case Error::NeedMoreData:
// Ignore this, as it's not an "error"
dbgln_if(TLS_DEBUG, "More data needed");
break;
default:
dbgln("Unknown TLS::Error with value {}", payload_res);
VERIFY_NOT_REACHED();
break;
}
if (payload_res < 0)
return payload_res;
}
switch (write_packets) {
case WritePacketStage::Initial:
// nothing to write
break;
case WritePacketStage::ClientHandshake:
if (m_context.client_verified == VerificationNeeded) {
dbgln_if(TLS_DEBUG, "> Client Certificate");
auto packet = build_certificate();
write_packet(packet);
m_context.client_verified = Verified;
}
{
dbgln_if(TLS_DEBUG, "> Key exchange");
auto packet = build_client_key_exchange();
write_packet(packet);
}
{
dbgln_if(TLS_DEBUG, "> change cipher spec");
auto packet = build_change_cipher_spec();
write_packet(packet);
}
m_context.cipher_spec_set = 1;
m_context.local_sequence_number = 0;
{
dbgln_if(TLS_DEBUG, "> client finished");
auto packet = build_handshake_finished();
write_packet(packet);
}
m_context.cipher_spec_set = 0;
break;
case WritePacketStage::ServerHandshake:
// server handshake
dbgln("UNSUPPORTED: Server mode");
VERIFY_NOT_REACHED();
break;
case WritePacketStage::Finished:
// finished
{
dbgln_if(TLS_DEBUG, "> change cipher spec");
auto packet = build_change_cipher_spec();
write_packet(packet);
}
{
dbgln_if(TLS_DEBUG, "> client finished");
auto packet = build_handshake_finished();
write_packet(packet);
}
m_context.connection_status = ConnectionStatus::Established;
break;
}
payload_size++;
buffer_length -= payload_size;
buffer = buffer.slice(payload_size, buffer_length);
}
return original_length;
}
}

View file

@ -1,110 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Endian.h>
#include <AK/Random.h>
#include <LibCore/Timer.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibTLS/TLSv12.h>
namespace TLS {
ssize_t TLSv12::handle_certificate(ReadonlyBytes buffer)
{
ssize_t res = 0;
if (buffer.size() < 3) {
dbgln_if(TLS_DEBUG, "not enough certificate header data");
return (i8)Error::NeedMoreData;
}
u32 certificate_total_length = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2];
dbgln_if(TLS_DEBUG, "total length: {}", certificate_total_length);
if (certificate_total_length <= 4)
return 3 * certificate_total_length;
res += 3;
if (certificate_total_length > buffer.size() - res) {
dbgln_if(TLS_DEBUG, "not enough data for claimed total cert length");
return (i8)Error::NeedMoreData;
}
size_t size = certificate_total_length;
bool valid_certificate = false;
while (size > 0) {
if (buffer.size() - res < 3) {
dbgln_if(TLS_DEBUG, "not enough data for certificate length");
return (i8)Error::NeedMoreData;
}
size_t certificate_size = buffer[res] * 0x10000 + buffer[res + 1] * 0x100 + buffer[res + 2];
res += 3;
if (buffer.size() - res < certificate_size) {
dbgln_if(TLS_DEBUG, "not enough data for certificate body");
return (i8)Error::NeedMoreData;
}
auto res_cert = res;
auto remaining = certificate_size;
do {
if (remaining <= 3) {
dbgln("Ran out of data");
break;
}
if (buffer.size() < (size_t)res_cert + 3) {
dbgln("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;
if (certificate_size_specific > remaining) {
dbgln("invalid certificate size (expected {} but got {})", remaining, certificate_size_specific);
break;
}
remaining -= certificate_size_specific;
auto certificate = Certificate::parse_certificate(buffer.slice(res_cert, certificate_size_specific), false);
if (!certificate.is_error()) {
m_context.certificates.empend(certificate.value());
valid_certificate = true;
} else {
dbgln("Failed to parse client cert: {}", certificate.error());
dbgln("{:hex-dump}", buffer.slice(res_cert, certificate_size_specific));
dbgln("");
}
res_cert += certificate_size_specific;
} while (remaining > 0);
if (remaining) {
dbgln("extraneous {} bytes left over after parsing certificates", remaining);
}
size -= certificate_size + 3;
res += certificate_size;
}
if (!valid_certificate)
return (i8)Error::UnsupportedCertificate;
if ((size_t)res != buffer.size())
dbgln("some data left unread: {} bytes out of {}", res, buffer.size());
return res;
}
ssize_t TLSv12::handle_certificate_verify(ReadonlyBytes)
{
dbgln("FIXME: parse_verify");
return 0;
}
}

View file

@ -1,409 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Hex.h>
#include <AK/Random.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibCrypto/NumberTheory/ModularFunctions.h>
#include <LibCrypto/SecureRandom.h>
#include <LibTLS/TLSv12.h>
namespace TLS {
bool TLSv12::expand_key()
{
u8 key[192]; // soooooooo many constants
auto key_buffer = Bytes { key, sizeof(key) };
auto is_aead = this->is_aead();
if (m_context.master_key.size() == 0) {
dbgln("expand_key() with empty master key");
return false;
}
auto key_size = key_length();
VERIFY(key_size);
auto mac_size = mac_length();
auto iv_size = iv_length();
pseudorandom_function(
key_buffer,
m_context.master_key,
(u8 const*)"key expansion", 13,
ReadonlyBytes { m_context.remote_random, sizeof(m_context.remote_random) },
ReadonlyBytes { m_context.local_random, sizeof(m_context.local_random) });
size_t offset = 0;
if (is_aead) {
iv_size = 4; // Explicit IV size.
} else {
memcpy(m_context.crypto.local_mac, key + offset, mac_size);
offset += mac_size;
memcpy(m_context.crypto.remote_mac, key + offset, mac_size);
offset += mac_size;
}
auto client_key = key + offset;
offset += key_size;
auto server_key = key + offset;
offset += key_size;
auto client_iv = key + offset;
offset += iv_size;
auto server_iv = key + offset;
offset += iv_size;
if constexpr (TLS_DEBUG) {
dbgln("client key");
print_buffer(client_key, key_size);
dbgln("server key");
print_buffer(server_key, key_size);
dbgln("client iv");
print_buffer(client_iv, iv_size);
dbgln("server iv");
print_buffer(server_iv, iv_size);
if (!is_aead) {
dbgln("client mac key");
print_buffer(m_context.crypto.local_mac, mac_size);
dbgln("server mac key");
print_buffer(m_context.crypto.remote_mac, mac_size);
}
}
switch (get_cipher_algorithm(m_context.cipher)) {
case CipherAlgorithm::AES_128_CBC:
case CipherAlgorithm::AES_256_CBC: {
VERIFY(!is_aead);
memcpy(m_context.crypto.local_iv, client_iv, iv_size);
memcpy(m_context.crypto.remote_iv, server_iv, iv_size);
m_cipher_local = Crypto::Cipher::AESCipher::CBCMode(ReadonlyBytes { client_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246);
m_cipher_remote = Crypto::Cipher::AESCipher::CBCMode(ReadonlyBytes { server_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246);
break;
}
case CipherAlgorithm::AES_128_GCM:
case CipherAlgorithm::AES_256_GCM: {
VERIFY(is_aead);
memcpy(m_context.crypto.local_aead_iv, client_iv, iv_size);
memcpy(m_context.crypto.remote_aead_iv, server_iv, iv_size);
m_cipher_local = Crypto::Cipher::AESCipher::GCMMode(ReadonlyBytes { client_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246);
m_cipher_remote = Crypto::Cipher::AESCipher::GCMMode(ReadonlyBytes { server_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246);
break;
}
case CipherAlgorithm::AES_128_CCM:
dbgln("Requested unimplemented AES CCM cipher");
TODO();
case CipherAlgorithm::AES_128_CCM_8:
dbgln("Requested unimplemented AES CCM-8 block cipher");
TODO();
default:
dbgln("Requested unknown block cipher");
VERIFY_NOT_REACHED();
}
m_context.crypto.created = 1;
return true;
}
bool TLSv12::compute_master_secret_from_pre_master_secret(size_t length)
{
if (m_context.premaster_key.size() == 0 || length < 48) {
dbgln("there's no way I can make a master secret like this");
dbgln("I'd like to talk to your manager about this length of {}", length);
return false;
}
if (m_context.master_key.try_resize(length).is_error()) {
dbgln("Couldn't allocate enough space for the master key :(");
return false;
}
if (m_context.extensions.extended_master_secret) {
Crypto::Hash::Manager handshake_hash_copy = m_context.handshake_hash.copy();
auto digest = handshake_hash_copy.digest();
auto session_hash = ReadonlyBytes { digest.immutable_data(), handshake_hash_copy.digest_size() };
pseudorandom_function(
m_context.master_key,
m_context.premaster_key,
(u8 const*)"extended master secret", 22,
session_hash,
{});
} else {
pseudorandom_function(
m_context.master_key,
m_context.premaster_key,
(u8 const*)"master secret", 13,
ReadonlyBytes { m_context.local_random, sizeof(m_context.local_random) },
ReadonlyBytes { m_context.remote_random, sizeof(m_context.remote_random) });
}
m_context.premaster_key.clear();
if constexpr (TLS_DEBUG) {
dbgln("master key:");
print_buffer(m_context.master_key);
}
expand_key();
return true;
}
void TLSv12::build_rsa_pre_master_secret(PacketBuilder& builder)
{
u8 random_bytes[48];
size_t bytes = 48;
Crypto::fill_with_secure_random(random_bytes);
// remove zeros from the random bytes
for (size_t i = 0; i < bytes; ++i) {
if (!random_bytes[i])
random_bytes[i--] = get_random<u8>();
}
if (m_context.is_server) {
dbgln("Server mode not supported");
return;
} else {
*(u16*)random_bytes = AK::convert_between_host_and_network_endian((u16)ProtocolVersion::VERSION_1_2);
}
auto premaster_key_result = ByteBuffer::copy(random_bytes, bytes);
if (premaster_key_result.is_error()) {
dbgln("RSA premaster key generation failed, not enough memory");
return;
}
m_context.premaster_key = premaster_key_result.release_value();
// RFC5246 section 7.4.2: The sender's certificate MUST come first in the list.
auto& certificate = m_context.certificates.first();
if constexpr (TLS_DEBUG) {
dbgln("PreMaster secret");
print_buffer(m_context.premaster_key);
}
Crypto::PK::RSA_PKCS1_EME rsa(certificate.public_key.rsa);
auto outbuf = MUST(rsa.encrypt(m_context.premaster_key));
if constexpr (TLS_DEBUG) {
dbgln("Encrypted: ");
print_buffer(outbuf);
}
builder.append_u24(outbuf.size() + 2);
builder.append((u16)outbuf.size());
builder.append(outbuf);
}
void TLSv12::build_dhe_rsa_pre_master_secret(PacketBuilder& builder)
{
auto& dh = m_context.server_diffie_hellman_params;
auto dh_p = Crypto::UnsignedBigInteger::import_data(dh.p.data(), dh.p.size());
auto dh_g = Crypto::UnsignedBigInteger::import_data(dh.g.data(), dh.g.size());
auto dh_Ys = Crypto::UnsignedBigInteger::import_data(dh.Ys.data(), dh.Ys.size());
auto dh_key_size = dh.p.size();
auto dh_random = Crypto::NumberTheory::random_number(0, dh_p);
auto dh_Yc = Crypto::NumberTheory::ModularPower(dh_g, dh_random, dh_p);
auto dh_Yc_bytes_result = ByteBuffer::create_uninitialized(dh_key_size);
if (dh_Yc_bytes_result.is_error()) {
dbgln("Failed to build DHE_RSA premaster secret: not enough memory");
return;
}
auto dh_Yc_bytes = dh_Yc_bytes_result.release_value();
dh_Yc.export_data(dh_Yc_bytes);
auto premaster_key = Crypto::NumberTheory::ModularPower(dh_Ys, dh_random, dh_p);
auto premaster_key_result = ByteBuffer::create_uninitialized(dh_key_size);
if (premaster_key_result.is_error()) {
dbgln("Failed to build DHE_RSA premaster secret: not enough memory");
return;
}
m_context.premaster_key = premaster_key_result.release_value();
premaster_key.export_data(m_context.premaster_key, true);
dh.p.clear();
dh.g.clear();
dh.Ys.clear();
if constexpr (TLS_DEBUG) {
dbgln("dh_random: {}", dh_random.to_base_deprecated(16));
dbgln("dh_Yc: {:hex-dump}", (ReadonlyBytes)dh_Yc_bytes);
dbgln("premaster key: {:hex-dump}", (ReadonlyBytes)m_context.premaster_key);
}
builder.append_u24(dh_key_size + 2);
builder.append((u16)dh_key_size);
builder.append(dh_Yc_bytes);
}
void TLSv12::build_ecdhe_rsa_pre_master_secret(PacketBuilder& builder)
{
// Create a random private key
auto private_key_result = m_context.server_key_exchange_curve->generate_private_key();
if (private_key_result.is_error()) {
dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory");
return;
}
auto private_key = private_key_result.release_value();
// Calculate the public key from the private key
auto public_key_result = m_context.server_key_exchange_curve->generate_public_key(private_key);
if (public_key_result.is_error()) {
dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory");
return;
}
auto public_key = public_key_result.release_value();
// Calculate the shared point by multiplying the client private key and the server public key
ReadonlyBytes server_public_key_bytes = m_context.server_diffie_hellman_params.p;
auto shared_point_result = m_context.server_key_exchange_curve->compute_coordinate(private_key, server_public_key_bytes);
if (shared_point_result.is_error()) {
dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory");
return;
}
auto shared_point = shared_point_result.release_value();
// Derive the premaster key from the shared point
auto premaster_key_result = m_context.server_key_exchange_curve->derive_premaster_key(shared_point);
if (premaster_key_result.is_error()) {
dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory");
return;
}
m_context.premaster_key = premaster_key_result.release_value();
if constexpr (TLS_DEBUG) {
dbgln("Build ECDHE_RSA pre master secret");
dbgln("client private key: {:hex-dump}", (ReadonlyBytes)private_key);
dbgln("client public key: {:hex-dump}", (ReadonlyBytes)public_key);
dbgln("premaster key: {:hex-dump}", (ReadonlyBytes)m_context.premaster_key);
}
builder.append_u24(public_key.size() + 1);
builder.append((u8)public_key.size());
builder.append(public_key);
}
ByteBuffer TLSv12::build_certificate()
{
PacketBuilder builder { ContentType::HANDSHAKE, m_context.options.version };
Vector<Certificate const&> certificates;
Vector<Certificate>* local_certificates = nullptr;
if (m_context.is_server) {
dbgln("Unsupported: Server mode");
VERIFY_NOT_REACHED();
} else {
local_certificates = &m_context.client_certificates;
}
constexpr size_t der_length_delta = 3;
constexpr size_t certificate_vector_header_size = 3;
size_t total_certificate_size = 0;
for (size_t i = 0; i < local_certificates->size(); ++i) {
auto& certificate = local_certificates->at(i);
if (!certificate.der.is_empty()) {
total_certificate_size += certificate.der.size() + der_length_delta;
// FIXME: Check for and respond with only the requested certificate types.
if (true) {
certificates.append(certificate);
}
}
}
builder.append((u8)HandshakeType::CERTIFICATE);
if (!total_certificate_size) {
dbgln_if(TLS_DEBUG, "No certificates, sending empty certificate message");
builder.append_u24(certificate_vector_header_size);
builder.append_u24(total_certificate_size);
} else {
builder.append_u24(total_certificate_size + certificate_vector_header_size); // 3 bytes for header
builder.append_u24(total_certificate_size);
for (auto& certificate : certificates) {
if (!certificate.der.is_empty()) {
builder.append_u24(certificate.der.size());
builder.append(certificate.der.bytes());
}
}
}
auto packet = builder.build();
update_packet(packet);
return packet;
}
ByteBuffer TLSv12::build_client_key_exchange()
{
bool chain_verified = m_context.verify_chain(m_context.extensions.SNI);
if (!chain_verified) {
dbgln("certificate verification failed :(");
alert(AlertLevel::FATAL, AlertDescription::BAD_CERTIFICATE);
return {};
}
PacketBuilder builder { ContentType::HANDSHAKE, m_context.options.version };
builder.append((u8)HandshakeType::CLIENT_KEY_EXCHANGE_RESERVED);
switch (get_key_exchange_algorithm(m_context.cipher)) {
case KeyExchangeAlgorithm::RSA:
build_rsa_pre_master_secret(builder);
break;
case KeyExchangeAlgorithm::DHE_DSS:
dbgln("Client key exchange for DHE_DSS is not implemented");
TODO();
break;
case KeyExchangeAlgorithm::DH_DSS:
case KeyExchangeAlgorithm::DH_RSA:
dbgln("Client key exchange for DH algorithms is not implemented");
TODO();
break;
case KeyExchangeAlgorithm::DHE_RSA:
build_dhe_rsa_pre_master_secret(builder);
break;
case KeyExchangeAlgorithm::DH_anon:
dbgln("Client key exchange for DH_anon is not implemented");
TODO();
break;
case KeyExchangeAlgorithm::ECDHE_RSA:
case KeyExchangeAlgorithm::ECDHE_ECDSA:
build_ecdhe_rsa_pre_master_secret(builder);
break;
case KeyExchangeAlgorithm::ECDH_ECDSA:
case KeyExchangeAlgorithm::ECDH_RSA:
case KeyExchangeAlgorithm::ECDH_anon:
dbgln("Client key exchange for ECDHE algorithms is not implemented");
TODO();
break;
default:
dbgln("Unknown client key exchange algorithm");
VERIFY_NOT_REACHED();
break;
}
m_context.connection_status = ConnectionStatus::KeyExchange;
auto packet = builder.build();
update_packet(packet);
if (!compute_master_secret_from_pre_master_secret(48)) {
dbgln("oh noes we could not derive a master key :(");
}
return packet;
}
}

View file

@ -1,532 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Endian.h>
#include <AK/Random.h>
#include <LibCore/Timer.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/Curves/Ed25519.h>
#include <LibCrypto/Curves/EllipticCurve.h>
#include <LibCrypto/Curves/SECPxxxr1.h>
#include <LibCrypto/Curves/X25519.h>
#include <LibCrypto/Curves/X448.h>
#include <LibTLS/TLSv12.h>
namespace TLS {
ssize_t TLSv12::handle_server_hello(ReadonlyBytes buffer, WritePacketStage& write_packets)
{
write_packets = WritePacketStage::Initial;
if (m_context.connection_status != ConnectionStatus::Disconnected && m_context.connection_status != ConnectionStatus::Renegotiating) {
dbgln("unexpected hello message");
return (i8)Error::UnexpectedMessage;
}
ssize_t res = 0;
size_t min_hello_size = 41;
if (min_hello_size > buffer.size()) {
dbgln("need more data");
return (i8)Error::NeedMoreData;
}
size_t following_bytes = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2];
res += 3;
if (buffer.size() - res < following_bytes) {
dbgln("not enough data after header: {} < {}", buffer.size() - res, following_bytes);
return (i8)Error::NeedMoreData;
}
if (buffer.size() - res < 2) {
dbgln("not enough data for version");
return (i8)Error::NeedMoreData;
}
auto version = static_cast<ProtocolVersion>(AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res))));
res += 2;
if (!supports_version(version))
return (i8)Error::NotSafe;
memcpy(m_context.remote_random, buffer.offset_pointer(res), sizeof(m_context.remote_random));
res += sizeof(m_context.remote_random);
u8 session_length = buffer[res++];
if (buffer.size() - res < session_length) {
dbgln("not enough data for session id");
return (i8)Error::NeedMoreData;
}
if (session_length && session_length <= 32) {
memcpy(m_context.session_id, buffer.offset_pointer(res), session_length);
m_context.session_id_size = session_length;
if constexpr (TLS_DEBUG) {
dbgln("Remote session ID:");
print_buffer(ReadonlyBytes { m_context.session_id, session_length });
}
} else {
m_context.session_id_size = 0;
}
res += session_length;
if (buffer.size() - res < 2) {
dbgln("not enough data for cipher suite listing");
return (i8)Error::NeedMoreData;
}
auto cipher = static_cast<CipherSuite>(AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res))));
res += 2;
if (!supports_cipher(cipher)) {
m_context.cipher = CipherSuite::TLS_NULL_WITH_NULL_NULL;
dbgln("No supported cipher could be agreed upon");
return (i8)Error::NoCommonCipher;
}
m_context.cipher = cipher;
dbgln_if(TLS_DEBUG, "Cipher: {}", enum_to_string(cipher));
// Simplification: We only support handshake hash functions via HMAC
m_context.handshake_hash.initialize(hmac_hash());
// Compression method
if (buffer.size() - res < 1)
return (i8)Error::NeedMoreData;
u8 compression = buffer[res++];
if (compression != 0)
return (i8)Error::CompressionNotSupported;
if (m_context.connection_status != ConnectionStatus::Renegotiating)
m_context.connection_status = ConnectionStatus::Negotiating;
if (m_context.is_server) {
dbgln("unsupported: server mode");
write_packets = WritePacketStage::ServerHandshake;
}
// Presence of extensions is determined by availability of bytes after compression_method
if (buffer.size() - res >= 2) {
auto extensions_bytes_total = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res += 2)));
dbgln_if(TLS_DEBUG, "Extensions bytes total: {}", extensions_bytes_total);
}
while (buffer.size() - res >= 4) {
auto extension_type = (ExtensionType)AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res)));
res += 2;
u16 extension_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res)));
res += 2;
dbgln_if(TLS_DEBUG, "Extension {} with length {}", enum_to_string(extension_type), extension_length);
if (buffer.size() - res < extension_length)
return (i8)Error::NeedMoreData;
if (extension_type == ExtensionType::SERVER_NAME) {
// RFC6066 section 3: SNI extension_data can be empty in the server hello
if (extension_length > 0) {
// ServerNameList total size
if (buffer.size() - res < 2)
return (i8)Error::NeedMoreData;
auto sni_name_list_bytes = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res += 2)));
dbgln_if(TLS_DEBUG, "SNI: expecting ServerNameList of {} bytes", sni_name_list_bytes);
// Exactly one ServerName should be present
if (buffer.size() - res < 3)
return (i8)Error::NeedMoreData;
auto sni_name_type = (NameType)(*(u8 const*)buffer.offset_pointer(res++));
auto sni_name_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res += 2)));
if (sni_name_type != NameType::HOST_NAME)
return (i8)Error::NotUnderstood;
if (sizeof(sni_name_type) + sizeof(sni_name_length) + sni_name_length != sni_name_list_bytes)
return (i8)Error::BrokenPacket;
// Read out the host_name
if (buffer.size() - res < sni_name_length)
return (i8)Error::NeedMoreData;
m_context.extensions.SNI = ByteString { (char const*)buffer.offset_pointer(res), sni_name_length };
res += sni_name_length;
dbgln("SNI host_name: {}", m_context.extensions.SNI);
}
} else if (extension_type == ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION && m_context.alpn.size()) {
if (buffer.size() - res > 2) {
auto alpn_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res)));
if (alpn_length && alpn_length <= extension_length - 2) {
u8 const* alpn = buffer.offset_pointer(res + 2);
size_t alpn_position = 0;
while (alpn_position < alpn_length) {
u8 alpn_size = alpn[alpn_position++];
if (alpn_size + alpn_position >= extension_length)
break;
ByteString alpn_str { (char const*)alpn + alpn_position, alpn_length };
if (alpn_size && m_context.alpn.contains_slow(alpn_str)) {
m_context.negotiated_alpn = alpn_str;
dbgln("negotiated alpn: {}", alpn_str);
break;
}
alpn_position += alpn_length;
if (!m_context.is_server) // server hello must contain one ALPN
break;
}
}
}
res += extension_length;
} else if (extension_type == ExtensionType::SIGNATURE_ALGORITHMS) {
dbgln("supported signatures: ");
print_buffer(buffer.slice(res, extension_length));
res += extension_length;
// FIXME: what are we supposed to do here?
} else if (extension_type == ExtensionType::EC_POINT_FORMATS) {
// RFC8422 section 5.2: A server that selects an ECC cipher suite in response to a ClientHello message
// including a Supported Point Formats Extension appends this extension (along with others) to its
// ServerHello message, enumerating the point formats it can parse. The Supported Point Formats Extension,
// when used, MUST contain the value 0 (uncompressed) as one of the items in the list of point formats.
//
// The current implementation only supports uncompressed points, and the server is required to support
// uncompressed points. Therefore, this extension can be safely ignored as it should always inform us
// that the server supports uncompressed points.
res += extension_length;
} else if (extension_type == ExtensionType::EXTENDED_MASTER_SECRET) {
m_context.extensions.extended_master_secret = true;
res += extension_length;
} else {
dbgln("Encountered unknown extension {} with length {}", enum_to_string(extension_type), extension_length);
res += extension_length;
}
}
return res;
}
ssize_t TLSv12::handle_server_hello_done(ReadonlyBytes buffer)
{
if (buffer.size() < 3)
return (i8)Error::NeedMoreData;
size_t size = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2];
if (buffer.size() - 3 < size)
return (i8)Error::NeedMoreData;
return size + 3;
}
ByteBuffer TLSv12::build_server_key_exchange()
{
dbgln("FIXME: build_server_key_exchange");
return {};
}
ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes buffer)
{
switch (get_key_exchange_algorithm(m_context.cipher)) {
case KeyExchangeAlgorithm::RSA:
case KeyExchangeAlgorithm::DH_DSS:
case KeyExchangeAlgorithm::DH_RSA:
// RFC 5246 section 7.4.3. Server Key Exchange Message
// It is not legal to send the server key exchange message for RSA, DH_DSS, DH_RSA
dbgln("Server key exchange received for RSA, DH_DSS or DH_RSA is not legal");
return (i8)Error::UnexpectedMessage;
case KeyExchangeAlgorithm::DHE_DSS:
dbgln("Server key exchange for DHE_DSS is not implemented");
TODO();
break;
case KeyExchangeAlgorithm::DHE_RSA:
return handle_dhe_rsa_server_key_exchange(buffer);
case KeyExchangeAlgorithm::DH_anon:
dbgln("Server key exchange for DH_anon is not implemented");
TODO();
break;
case KeyExchangeAlgorithm::ECDHE_RSA:
return handle_ecdhe_rsa_server_key_exchange(buffer);
case KeyExchangeAlgorithm::ECDHE_ECDSA:
return handle_ecdhe_ecdsa_server_key_exchange(buffer);
case KeyExchangeAlgorithm::ECDH_ECDSA:
case KeyExchangeAlgorithm::ECDH_RSA:
case KeyExchangeAlgorithm::ECDH_anon:
dbgln("Server key exchange for ECDHE algorithms is not implemented");
TODO();
break;
default:
dbgln("Unknown server key exchange algorithm");
VERIFY_NOT_REACHED();
break;
}
return 0;
}
ssize_t TLSv12::handle_dhe_rsa_server_key_exchange(ReadonlyBytes buffer)
{
auto dh_p_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(3)));
auto dh_p = buffer.slice(5, dh_p_length);
auto p_result = ByteBuffer::copy(dh_p);
if (p_result.is_error()) {
dbgln("dhe_rsa_server_key_exchange failed: Not enough memory");
return (i8)Error::OutOfMemory;
}
m_context.server_diffie_hellman_params.p = p_result.release_value();
auto dh_g_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(5 + dh_p_length)));
auto dh_g = buffer.slice(7 + dh_p_length, dh_g_length);
auto g_result = ByteBuffer::copy(dh_g);
if (g_result.is_error()) {
dbgln("dhe_rsa_server_key_exchange failed: Not enough memory");
return (i8)Error::OutOfMemory;
}
m_context.server_diffie_hellman_params.g = g_result.release_value();
auto dh_Ys_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(7 + dh_p_length + dh_g_length)));
auto dh_Ys = buffer.slice(9 + dh_p_length + dh_g_length, dh_Ys_length);
auto Ys_result = ByteBuffer::copy(dh_Ys);
if (Ys_result.is_error()) {
dbgln("dhe_rsa_server_key_exchange failed: Not enough memory");
return (i8)Error::OutOfMemory;
}
m_context.server_diffie_hellman_params.Ys = Ys_result.release_value();
if constexpr (TLS_DEBUG) {
dbgln("dh_p: {:hex-dump}", dh_p);
dbgln("dh_g: {:hex-dump}", dh_g);
dbgln("dh_Ys: {:hex-dump}", dh_Ys);
}
auto server_key_info = buffer.slice(3, 6 + dh_p_length + dh_g_length + dh_Ys_length);
auto signature = buffer.slice(9 + dh_p_length + dh_g_length + dh_Ys_length);
return verify_rsa_server_key_exchange(server_key_info, signature);
}
ssize_t TLSv12::handle_ecdhe_server_key_exchange(ReadonlyBytes buffer, u8& server_public_key_length)
{
if (buffer.size() < 7)
return (i8)Error::NeedMoreData;
auto curve_type = buffer[3];
if (curve_type != (u8)ECCurveType::NAMED_CURVE)
return (i8)Error::NotUnderstood;
auto curve = static_cast<SupportedGroup>(AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(4))));
if (!m_context.options.elliptic_curves.contains_slow(curve))
return (i8)Error::NotUnderstood;
switch ((SupportedGroup)curve) {
case SupportedGroup::X25519:
m_context.server_key_exchange_curve = make<Crypto::Curves::X25519>();
break;
case SupportedGroup::X448:
m_context.server_key_exchange_curve = make<Crypto::Curves::X448>();
break;
case SupportedGroup::SECP256R1:
m_context.server_key_exchange_curve = make<Crypto::Curves::SECP256r1>();
break;
case SupportedGroup::SECP384R1:
m_context.server_key_exchange_curve = make<Crypto::Curves::SECP384r1>();
break;
default:
return (i8)Error::NotUnderstood;
}
server_public_key_length = buffer[6];
if (server_public_key_length != m_context.server_key_exchange_curve->key_size())
return (i8)Error::NotUnderstood;
if (buffer.size() < 7u + server_public_key_length)
return (i8)Error::NeedMoreData;
auto server_public_key = buffer.slice(7, server_public_key_length);
auto server_public_key_copy_result = ByteBuffer::copy(server_public_key);
if (server_public_key_copy_result.is_error()) {
dbgln("ecdhe_rsa_server_key_exchange failed: Not enough memory");
return (i8)Error::OutOfMemory;
}
m_context.server_diffie_hellman_params.p = server_public_key_copy_result.release_value();
if constexpr (TLS_DEBUG) {
dbgln("ECDHE server public key: {:hex-dump}", server_public_key);
}
return 0;
}
ssize_t TLSv12::handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes buffer)
{
u8 server_public_key_length;
if (auto result = handle_ecdhe_server_key_exchange(buffer, server_public_key_length)) {
return result;
}
auto server_key_info = buffer.slice(3, 4 + server_public_key_length);
auto signature = buffer.slice(7 + server_public_key_length);
return verify_rsa_server_key_exchange(server_key_info, signature);
}
ssize_t TLSv12::verify_rsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer)
{
auto signature_hash = signature_buffer[0];
auto signature_algorithm = static_cast<SignatureAlgorithm>(signature_buffer[1]);
if (signature_algorithm != SignatureAlgorithm::RSA) {
dbgln("verify_rsa_server_key_exchange failed: Signature algorithm is not RSA, instead {}", enum_to_string(signature_algorithm));
return (i8)Error::NotUnderstood;
}
auto signature_length = AK::convert_between_host_and_network_endian(ByteReader::load16(signature_buffer.offset_pointer(2)));
auto signature = signature_buffer.slice(4, signature_length);
if (m_context.certificates.is_empty()) {
dbgln("verify_rsa_server_key_exchange failed: Attempting to verify signature without certificates");
return (i8)Error::NotSafe;
}
// RFC5246 section 7.4.2: The sender's certificate MUST come first in the list.
auto message_result = ByteBuffer::create_uninitialized(64 + server_key_info_buffer.size());
if (message_result.is_error()) {
dbgln("verify_rsa_server_key_exchange failed: Not enough memory");
return (i8)Error::OutOfMemory;
}
auto message = message_result.release_value();
message.overwrite(0, m_context.local_random, 32);
message.overwrite(32, m_context.remote_random, 32);
message.overwrite(64, server_key_info_buffer.data(), server_key_info_buffer.size());
Crypto::Hash::HashKind hash_kind;
switch ((HashAlgorithm)signature_hash) {
case HashAlgorithm::SHA1:
hash_kind = Crypto::Hash::HashKind::SHA1;
break;
case HashAlgorithm::SHA256:
hash_kind = Crypto::Hash::HashKind::SHA256;
break;
case HashAlgorithm::SHA384:
hash_kind = Crypto::Hash::HashKind::SHA384;
break;
case HashAlgorithm::SHA512:
hash_kind = Crypto::Hash::HashKind::SHA512;
break;
default:
dbgln("verify_rsa_server_key_exchange failed: Hash algorithm is not SHA1/256/384/512, instead {}", signature_hash);
return (i8)Error::NotUnderstood;
}
auto certificate_public_key = m_context.certificates.first().public_key;
auto rsa = Crypto::PK::RSA_PKCS1_EMSA(hash_kind, certificate_public_key.rsa);
auto verification = MUST(rsa.verify(message, signature));
if (!verification) {
dbgln("verify_rsa_server_key_exchange failed: Verification of signature inconsistent");
return (i8)Error::NotSafe;
}
return 0;
}
ssize_t TLSv12::handle_ecdhe_ecdsa_server_key_exchange(ReadonlyBytes buffer)
{
u8 server_public_key_length;
if (auto result = handle_ecdhe_server_key_exchange(buffer, server_public_key_length)) {
return result;
}
auto server_key_info = buffer.slice(3, 4 + server_public_key_length);
auto signature = buffer.slice(7 + server_public_key_length);
return verify_ecdsa_server_key_exchange(server_key_info, signature);
}
ssize_t TLSv12::verify_ecdsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer)
{
auto signature_hash = signature_buffer[0];
auto signature_algorithm = signature_buffer[1];
if (signature_algorithm != (u8)SignatureAlgorithm::ECDSA) {
dbgln("verify_ecdsa_server_key_exchange failed: Signature algorithm is not ECDSA, instead {}", signature_algorithm);
return (i8)Error::NotUnderstood;
}
auto signature_length = AK::convert_between_host_and_network_endian(ByteReader::load16(signature_buffer.offset_pointer(2)));
auto signature_bytes = signature_buffer.slice(4, signature_length);
if (m_context.certificates.is_empty()) {
dbgln("verify_ecdsa_server_key_exchange failed: Attempting to verify signature without certificates");
return (i8)Error::NotSafe;
}
auto server_point = m_context.certificates.first().public_key.ec.to_secpxxxr1_point();
auto message_result = ByteBuffer::create_uninitialized(64 + server_key_info_buffer.size());
if (message_result.is_error()) {
dbgln("verify_ecdsa_server_key_exchange failed: Not enough memory");
return (i8)Error::OutOfMemory;
}
auto message = message_result.release_value();
message.overwrite(0, m_context.local_random, 32);
message.overwrite(32, m_context.remote_random, 32);
message.overwrite(64, server_key_info_buffer.data(), server_key_info_buffer.size());
Crypto::Hash::HashKind hash_kind;
switch ((HashAlgorithm)signature_hash) {
case HashAlgorithm::SHA256:
hash_kind = Crypto::Hash::HashKind::SHA256;
break;
case HashAlgorithm::SHA384:
hash_kind = Crypto::Hash::HashKind::SHA384;
break;
case HashAlgorithm::SHA512:
hash_kind = Crypto::Hash::HashKind::SHA512;
break;
default:
dbgln("verify_ecdsa_server_key_exchange failed: Hash algorithm is not SHA256/384/512, instead {}", signature_hash);
return (i8)Error::NotUnderstood;
}
ErrorOr<bool> res = AK::Error::from_errno(ENOTSUP);
auto& public_key = m_context.certificates.first().public_key;
auto ec_curve = oid_to_curve(public_key.algorithm.ec_parameters.value_or({}));
if (ec_curve.is_error()) {
dbgln("verify_ecdsa_server_key_exchange failed: Unknown curve for ECDSA signature verification");
return (i8)Error::NotUnderstood;
}
auto maybe_signature = Crypto::Curves::SECPxxxr1Signature::from_asn(*public_key.algorithm.ec_parameters, signature_bytes, {});
if (maybe_signature.is_error()) {
dbgln("verify_ecdsa_server_key_exchange failed: Signature is not ASN.1 DER encoded");
return (i8)Error::NotUnderstood;
}
auto signature = maybe_signature.release_value();
switch (ec_curve.release_value()) {
case SupportedGroup::SECP256R1: {
Crypto::Hash::Manager manager(hash_kind);
manager.update(message);
auto digest = manager.digest();
Crypto::Curves::SECP256r1 curve;
res = curve.verify_point(digest.bytes(), server_point, signature);
break;
}
case SupportedGroup::SECP384R1: {
Crypto::Hash::Manager manager(hash_kind);
manager.update(message);
auto digest = manager.digest();
Crypto::Curves::SECP384r1 curve;
res = curve.verify_point(digest.bytes(), server_point, signature);
break;
}
default: {
dbgln("verify_ecdsa_server_key_exchange failed: Server certificate public key algorithm is not supported: {}", to_underlying(ec_curve.release_value()));
break;
}
}
if (res.is_error()) {
dbgln("verify_ecdsa_server_key_exchange failed: {}", res.error());
return (i8)Error::NotUnderstood;
}
bool verification_ok = res.release_value();
if (!verification_ok) {
dbgln("verify_ecdsa_server_key_exchange failed: Verification of signature failed");
return (i8)Error::NotSafe;
}
return 0;
}
}

View file

@ -0,0 +1,13 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
extern "C" {
typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_st SSL;
typedef struct bio_st BIO;
}

View file

@ -1,570 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Endian.h>
#include <AK/MemoryStream.h>
#include <AK/Random.h>
#include <LibCore/EventLoop.h>
#include <LibCore/Timer.h>
#include <LibCrypto/SecureRandom.h>
#include <LibTLS/TLSv12.h>
namespace TLS {
ByteBuffer TLSv12::build_alert(bool critical, u8 code)
{
PacketBuilder builder(ContentType::ALERT, (u16)m_context.options.version);
builder.append((u8)(critical ? AlertLevel::FATAL : AlertLevel::WARNING));
builder.append(code);
if (critical)
m_context.critical_error = code;
auto packet = builder.build();
update_packet(packet);
return packet;
}
void TLSv12::alert(AlertLevel level, AlertDescription code)
{
auto the_alert = build_alert(level == AlertLevel::FATAL, (u8)code);
write_packet(the_alert, true);
MUST(flush());
}
void TLSv12::write_packet(ByteBuffer& packet, bool immediately)
{
auto schedule_or_perform_flush = [&](bool immediate) {
if (m_context.connection_status > ConnectionStatus::Disconnected) {
if (!m_has_scheduled_write_flush && !immediate) {
dbgln_if(TLS_DEBUG, "Scheduling write of {}", m_context.tls_buffer.size());
Core::deferred_invoke([this] { write_into_socket(); });
m_has_scheduled_write_flush = true;
} else {
// multiple packet are available, let's flush some out
dbgln_if(TLS_DEBUG, "Flushing scheduled write of {}", m_context.tls_buffer.size());
write_into_socket();
// the deferred invoke is still in place
m_has_scheduled_write_flush = true;
}
}
};
// Record size limit is 18432 bytes, leave some headroom and flush at 16K.
if (m_context.tls_buffer.size() + packet.size() > 16 * KiB)
schedule_or_perform_flush(true);
if (m_context.tls_buffer.try_append(packet.data(), packet.size()).is_error()) {
// Toooooo bad, drop the record on the ground.
return;
}
schedule_or_perform_flush(immediately);
}
void TLSv12::update_packet(ByteBuffer& packet)
{
u32 header_size = 5;
ByteReader::store(packet.offset_pointer(3), AK::convert_between_host_and_network_endian((u16)(packet.size() - header_size)));
if (packet[0] != (u8)ContentType::CHANGE_CIPHER_SPEC) {
if (packet[0] == (u8)ContentType::HANDSHAKE && packet.size() > header_size) {
auto handshake_type = static_cast<HandshakeType>(packet[header_size]);
if (handshake_type != HandshakeType::HELLO_REQUEST_RESERVED && handshake_type != HandshakeType::HELLO_VERIFY_REQUEST_RESERVED) {
update_hash(packet.bytes(), header_size);
}
}
if (m_context.cipher_spec_set && m_context.crypto.created) {
size_t length = packet.size() - header_size;
size_t block_size = 0;
size_t padding = 0;
size_t mac_size = 0;
m_cipher_local.visit(
[&](Empty&) { VERIFY_NOT_REACHED(); },
[&](Crypto::Cipher::AESCipher::GCMMode& gcm) {
VERIFY(is_aead());
block_size = gcm.cipher().block_size();
padding = 0;
mac_size = 0; // AEAD provides its own authentication scheme.
},
[&](Crypto::Cipher::AESCipher::CBCMode& cbc) {
VERIFY(!is_aead());
block_size = cbc.cipher().block_size();
// If the length is already a multiple a block_size,
// an entire block of padding is added.
// In short, we _never_ have no padding.
mac_size = mac_length();
length += mac_size;
padding = block_size - length % block_size;
length += padding;
});
if (m_context.crypto.created == 1) {
// `buffer' will continue to be encrypted
auto buffer_result = ByteBuffer::create_uninitialized(length);
if (buffer_result.is_error()) {
dbgln("LibTLS: Failed to allocate enough memory");
VERIFY_NOT_REACHED();
}
auto buffer = buffer_result.release_value();
size_t buffer_position = 0;
auto iv_size = iv_length();
// copy the packet, sans the header
buffer.overwrite(buffer_position, packet.offset_pointer(header_size), packet.size() - header_size);
buffer_position += packet.size() - header_size;
ByteBuffer ct;
m_cipher_local.visit(
[&](Empty&) { VERIFY_NOT_REACHED(); },
[&](Crypto::Cipher::AESCipher::GCMMode& gcm) {
VERIFY(is_aead());
// We need enough space for a header, the data, a tag, and the IV
auto ct_buffer_result = ByteBuffer::create_uninitialized(length + header_size + iv_size + 16);
if (ct_buffer_result.is_error()) {
dbgln("LibTLS: Failed to allocate enough memory for the ciphertext");
VERIFY_NOT_REACHED();
}
ct = ct_buffer_result.release_value();
// copy the header over
ct.overwrite(0, packet.data(), header_size - 2);
// AEAD AAD (13)
// Seq. no (8)
// content type (1)
// version (2)
// length (2)
u8 aad[13];
Bytes aad_bytes { aad, 13 };
FixedMemoryStream aad_stream { aad_bytes };
u64 seq_no = AK::convert_between_host_and_network_endian(m_context.local_sequence_number);
u16 len = AK::convert_between_host_and_network_endian((u16)(packet.size() - header_size));
MUST(aad_stream.write_value(seq_no)); // sequence number
MUST(aad_stream.write_until_depleted(packet.bytes().slice(0, 3))); // content-type + version
MUST(aad_stream.write_value(len)); // length
VERIFY(MUST(aad_stream.tell()) == MUST(aad_stream.size()));
// AEAD IV (12)
// IV (4)
// (Nonce) (8)
u8 iv[12];
Bytes iv_bytes { iv, 12 };
Bytes { m_context.crypto.local_aead_iv, 4 }.copy_to(iv_bytes);
Crypto::fill_with_secure_random(iv_bytes.slice(4, 8));
// write the random part of the iv out
iv_bytes.slice(4, 8).copy_to(ct.bytes().slice(header_size));
// Write the encrypted data and the tag
gcm.encrypt(
packet.bytes().slice(header_size, length),
ct.bytes().slice(header_size + 8, length),
iv_bytes,
aad_bytes,
ct.bytes().slice(header_size + 8 + length, 16));
VERIFY(header_size + 8 + length + 16 == ct.size());
},
[&](Crypto::Cipher::AESCipher::CBCMode& cbc) {
VERIFY(!is_aead());
// We need enough space for a header, iv_length bytes of IV and whatever the packet contains
auto ct_buffer_result = ByteBuffer::create_uninitialized(length + header_size + iv_size);
if (ct_buffer_result.is_error()) {
dbgln("LibTLS: Failed to allocate enough memory for the ciphertext");
VERIFY_NOT_REACHED();
}
ct = ct_buffer_result.release_value();
// copy the header over
ct.overwrite(0, packet.data(), header_size - 2);
// get the appropriate HMAC value for the entire packet
auto mac = hmac_message(packet, {}, mac_size, true);
// write the MAC
buffer.overwrite(buffer_position, mac.data(), mac.size());
buffer_position += mac.size();
// Apply the padding (a packet MUST always be padded)
memset(buffer.offset_pointer(buffer_position), padding - 1, padding);
buffer_position += padding;
VERIFY(buffer_position == buffer.size());
auto iv_buffer_result = ByteBuffer::create_uninitialized(iv_size);
if (iv_buffer_result.is_error()) {
dbgln("LibTLS: Failed to allocate memory for IV");
VERIFY_NOT_REACHED();
}
auto iv = iv_buffer_result.release_value();
Crypto::fill_with_secure_random(iv);
// write it into the ciphertext portion of the message
ct.overwrite(header_size, iv.data(), iv.size());
VERIFY(header_size + iv_size + length == ct.size());
VERIFY(length % block_size == 0);
// get a block to encrypt into
auto view = ct.bytes().slice(header_size + iv_size, length);
cbc.encrypt(buffer, view, iv);
// Note: 'view' is dropped without checking 'view.size()'.
// This is okay because TLSv12::expand_key sets PaddingMode::RFC5246, which never adds a block.
});
// store the correct ciphertext length into the packet
u16 ct_length = (u16)ct.size() - header_size;
ByteReader::store(ct.offset_pointer(header_size - 2), AK::convert_between_host_and_network_endian(ct_length));
// replace the packet with the ciphertext
packet = ct;
}
}
}
++m_context.local_sequence_number;
}
void TLSv12::update_hash(ReadonlyBytes message, size_t header_size)
{
dbgln_if(TLS_DEBUG, "Update hash with message of size {}", message.size());
m_context.handshake_hash.update(message.slice(header_size));
}
void TLSv12::ensure_hmac(size_t digest_size, bool local)
{
if (local && m_hmac_local)
return;
if (!local && m_hmac_remote)
return;
auto hash_kind = Crypto::Hash::HashKind::None;
switch (digest_size) {
case Crypto::Hash::SHA1::DigestSize:
hash_kind = Crypto::Hash::HashKind::SHA1;
break;
case Crypto::Hash::SHA256::DigestSize:
hash_kind = Crypto::Hash::HashKind::SHA256;
break;
case Crypto::Hash::SHA384::DigestSize:
hash_kind = Crypto::Hash::HashKind::SHA384;
break;
case Crypto::Hash::SHA512::DigestSize:
hash_kind = Crypto::Hash::HashKind::SHA512;
break;
default:
dbgln("Failed to find a suitable hash for size {}", digest_size);
break;
}
auto hmac = make<Crypto::Authentication::HMAC<Crypto::Hash::Manager>>(ReadonlyBytes { local ? m_context.crypto.local_mac : m_context.crypto.remote_mac, digest_size }, hash_kind);
if (local)
m_hmac_local = move(hmac);
else
m_hmac_remote = move(hmac);
}
ByteBuffer TLSv12::hmac_message(ReadonlyBytes buf, Optional<ReadonlyBytes> const buf2, size_t mac_length, bool local)
{
u64 sequence_number = AK::convert_between_host_and_network_endian(local ? m_context.local_sequence_number : m_context.remote_sequence_number);
ensure_hmac(mac_length, local);
auto& hmac = local ? *m_hmac_local : *m_hmac_remote;
if constexpr (TLS_DEBUG) {
dbgln("========================= PACKET DATA ==========================");
print_buffer((u8 const*)&sequence_number, sizeof(u64));
print_buffer(buf.data(), buf.size());
if (buf2.has_value())
print_buffer(buf2.value().data(), buf2.value().size());
dbgln("========================= PACKET DATA ==========================");
}
hmac.update((u8 const*)&sequence_number, sizeof(u64));
hmac.update(buf);
if (buf2.has_value() && buf2.value().size()) {
hmac.update(buf2.value());
}
auto digest = hmac.digest();
auto mac_result = ByteBuffer::copy(digest.immutable_data(), digest.data_length());
if (mac_result.is_error()) {
dbgln("Failed to calculate message HMAC: Not enough memory");
return {};
}
if constexpr (TLS_DEBUG) {
dbgln("HMAC of the block for sequence number {}", sequence_number);
print_buffer(mac_result.value());
}
return mac_result.release_value();
}
ssize_t TLSv12::handle_message(ReadonlyBytes buffer)
{
auto res { 5ll };
size_t header_size = res;
ssize_t payload_res = 0;
dbgln_if(TLS_DEBUG, "buffer size: {}", buffer.size());
if (buffer.size() < 5) {
return (i8)Error::NeedMoreData;
}
auto type = (ContentType)buffer[0];
size_t buffer_position { 1 };
// FIXME: Read the version and verify it
if constexpr (TLS_DEBUG) {
auto version = static_cast<ProtocolVersion>(ByteReader::load16(buffer.offset_pointer(buffer_position)));
dbgln("type={}, version={}", enum_to_string(type), enum_to_string(version));
}
buffer_position += 2;
auto length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(buffer_position)));
dbgln_if(TLS_DEBUG, "record length: {} at offset: {}", length, buffer_position);
buffer_position += 2;
if (buffer_position + length > buffer.size()) {
dbgln_if(TLS_DEBUG, "record length more than what we have: {}", buffer.size());
return (i8)Error::NeedMoreData;
}
dbgln_if(TLS_DEBUG, "message type: {}, length: {}", enum_to_string(type), length);
auto plain = buffer.slice(buffer_position, buffer.size() - buffer_position);
ByteBuffer decrypted;
if (m_context.cipher_spec_set && type != ContentType::CHANGE_CIPHER_SPEC) {
if constexpr (TLS_DEBUG) {
dbgln("Encrypted: ");
print_buffer(buffer.slice(header_size, length));
}
Error return_value = Error::NoError;
m_cipher_remote.visit(
[&](Empty&) { VERIFY_NOT_REACHED(); },
[&](Crypto::Cipher::AESCipher::GCMMode& gcm) {
VERIFY(is_aead());
if (length < 24) {
dbgln("Invalid packet length");
auto packet = build_alert(true, (u8)AlertDescription::DECRYPT_ERROR);
write_packet(packet);
return_value = Error::BrokenPacket;
return;
}
auto packet_length = length - iv_length() - 16;
auto payload = plain;
auto decrypted_result = ByteBuffer::create_uninitialized(packet_length);
if (decrypted_result.is_error()) {
dbgln("Failed to allocate memory for the packet");
return_value = Error::DecryptionFailed;
return;
}
decrypted = decrypted_result.release_value();
// AEAD AAD (13)
// Seq. no (8)
// content type (1)
// version (2)
// length (2)
u8 aad[13];
Bytes aad_bytes { aad, 13 };
FixedMemoryStream aad_stream { aad_bytes };
u64 seq_no = AK::convert_between_host_and_network_endian(m_context.remote_sequence_number);
u16 len = AK::convert_between_host_and_network_endian((u16)packet_length);
MUST(aad_stream.write_value(seq_no)); // sequence number
MUST(aad_stream.write_until_depleted(buffer.slice(0, header_size - 2))); // content-type + version
MUST(aad_stream.write_value(len)); // length
VERIFY(MUST(aad_stream.tell()) == MUST(aad_stream.size()));
auto nonce = payload.slice(0, iv_length());
payload = payload.slice(iv_length());
// AEAD IV (12)
// IV (4)
// (Nonce) (8)
u8 iv[12];
Bytes iv_bytes { iv, 12 };
Bytes { m_context.crypto.remote_aead_iv, 4 }.copy_to(iv_bytes);
nonce.copy_to(iv_bytes.slice(4));
auto ciphertext = payload.slice(0, payload.size() - 16);
auto tag = payload.slice(ciphertext.size());
auto consistency = gcm.decrypt(
ciphertext,
decrypted,
iv_bytes,
aad_bytes,
tag);
if (consistency != Crypto::VerificationConsistency::Consistent) {
dbgln("integrity check failed (tag length {})", tag.size());
auto packet = build_alert(true, (u8)AlertDescription::BAD_RECORD_MAC);
write_packet(packet);
return_value = Error::IntegrityCheckFailed;
return;
}
plain = decrypted;
},
[&](Crypto::Cipher::AESCipher::CBCMode& cbc) {
VERIFY(!is_aead());
auto iv_size = iv_length();
auto decrypted_result = cbc.create_aligned_buffer(length - iv_size);
if (decrypted_result.is_error()) {
dbgln("Failed to allocate memory for the packet");
return_value = Error::DecryptionFailed;
return;
}
decrypted = decrypted_result.release_value();
auto iv = buffer.slice(header_size, iv_size);
Bytes decrypted_span = decrypted;
cbc.decrypt(buffer.slice(header_size + iv_size, length - iv_size), decrypted_span, iv);
length = decrypted_span.size();
if constexpr (TLS_DEBUG) {
dbgln("Decrypted: ");
print_buffer(decrypted);
}
auto mac_size = mac_length();
if (length < mac_size) {
dbgln("broken packet");
auto packet = build_alert(true, (u8)AlertDescription::DECRYPT_ERROR);
write_packet(packet);
return_value = Error::BrokenPacket;
return;
}
length -= mac_size;
u8 const* message_hmac = decrypted_span.offset(length);
u8 temp_buf[5];
memcpy(temp_buf, buffer.offset_pointer(0), 3);
*(u16*)(temp_buf + 3) = AK::convert_between_host_and_network_endian(length);
auto hmac = hmac_message({ temp_buf, 5 }, decrypted_span.slice(0, length), mac_size);
auto message_mac = ReadonlyBytes { message_hmac, mac_size };
if (hmac != message_mac) {
dbgln("integrity check failed (mac length {})", mac_size);
dbgln("mac received:");
print_buffer(message_mac);
dbgln("mac computed:");
print_buffer(hmac);
auto packet = build_alert(true, (u8)AlertDescription::BAD_RECORD_MAC);
write_packet(packet);
return_value = Error::IntegrityCheckFailed;
return;
}
plain = decrypted.bytes().slice(0, length);
});
if (return_value != Error::NoError) {
return (i8)return_value;
}
}
m_context.remote_sequence_number++;
switch (type) {
case ContentType::APPLICATION_DATA:
if (m_context.connection_status != ConnectionStatus::Established) {
dbgln("unexpected application data");
payload_res = (i8)Error::UnexpectedMessage;
auto packet = build_alert(true, (u8)AlertDescription::UNEXPECTED_MESSAGE);
write_packet(packet);
} else {
dbgln_if(TLS_DEBUG, "application data message of size {}", plain.size());
if (m_context.application_buffer.try_append(plain).is_error()) {
payload_res = (i8)Error::DecryptionFailed;
auto packet = build_alert(true, (u8)AlertDescription::DECRYPTION_FAILED_RESERVED);
write_packet(packet);
} else {
notify_client_for_app_data();
}
}
break;
case ContentType::HANDSHAKE:
dbgln_if(TLS_DEBUG, "tls handshake message");
payload_res = handle_handshake_payload(plain);
break;
case ContentType::CHANGE_CIPHER_SPEC:
if (m_context.connection_status != ConnectionStatus::KeyExchange) {
dbgln("unexpected change cipher message");
auto packet = build_alert(true, (u8)AlertDescription::UNEXPECTED_MESSAGE);
write_packet(packet);
payload_res = (i8)Error::UnexpectedMessage;
} else {
dbgln_if(TLS_DEBUG, "change cipher spec message");
m_context.cipher_spec_set = true;
m_context.remote_sequence_number = 0;
}
break;
case ContentType::ALERT:
dbgln_if(TLS_DEBUG, "alert message of length {}", length);
if (length >= 2) {
if constexpr (TLS_DEBUG)
print_buffer(plain);
auto level = plain[0];
auto code = plain[1];
dbgln_if(TLS_DEBUG, "Alert received with level {}, code {}", level, code);
if (level == (u8)AlertLevel::FATAL) {
dbgln("We were alerted of a critical error: {} ({})", code, enum_to_string((AlertDescription)code));
m_context.critical_error = code;
try_disambiguate_error();
res = (i8)Error::UnknownError;
}
if (code == (u8)AlertDescription::CLOSE_NOTIFY) {
res += 2;
alert(AlertLevel::FATAL, AlertDescription::CLOSE_NOTIFY);
if (!m_context.cipher_spec_set) {
// AWS CloudFront hits this.
dbgln("Server sent a close notify and we haven't agreed on a cipher suite. Treating it as a handshake failure.");
m_context.critical_error = (u8)AlertDescription::HANDSHAKE_FAILURE;
try_disambiguate_error();
}
m_context.close_notify = true;
}
m_context.error_code = (Error)code;
check_connection_state(false);
notify_client_for_app_data(); // Give the user one more chance to observe the EOF
}
break;
default:
dbgln("message not understood");
return (i8)Error::NotUnderstood;
}
if (payload_res < 0)
return payload_res;
if (res > 0)
return header_size + length;
return res;
}
}

View file

@ -1,356 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <LibCore/DateTime.h>
#include <LibCore/EventLoop.h>
#include <LibCore/Promise.h>
#include <LibCore/Timer.h>
#include <LibTLS/TLSv12.h>
// Each record can hold at most 18432 bytes, leaving some headroom and rounding down to
// a nice number gives us a maximum of 16 KiB for user-supplied application data,
// which will be sent as a single record containing a single ApplicationData message.
constexpr static size_t MaximumApplicationDataChunkSize = 16 * KiB;
namespace TLS {
ErrorOr<Bytes> TLSv12::read_some(Bytes bytes)
{
m_eof = false;
auto size_to_read = min(bytes.size(), m_context.application_buffer.size());
if (size_to_read == 0) {
m_eof = true;
return Bytes {};
}
m_context.application_buffer.transfer(bytes, size_to_read);
return Bytes { bytes.data(), size_to_read };
}
ErrorOr<size_t> TLSv12::write_some(ReadonlyBytes bytes)
{
if (m_context.connection_status != ConnectionStatus::Established) {
dbgln_if(TLS_DEBUG, "write request while not connected");
return AK::Error::from_string_literal("TLS write request while not connected");
}
for (size_t offset = 0; offset < bytes.size(); offset += MaximumApplicationDataChunkSize) {
PacketBuilder builder { ContentType::APPLICATION_DATA, m_context.options.version, bytes.size() - offset };
builder.append(bytes.slice(offset, min(bytes.size() - offset, MaximumApplicationDataChunkSize)));
auto packet = builder.build();
update_packet(packet);
write_packet(packet);
}
return bytes.size();
}
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, u16 port, Options options)
{
#ifdef AK_OS_WINDOWS
(void)host;
(void)port;
(void)options;
return AK::Error::from_string_literal("TODO: Unable to connect via hostname on Windows");
#else
auto promise = Core::Promise<Empty>::construct();
OwnPtr<Core::Socket> tcp_socket = TRY(Core::TCPSocket::connect(host, port));
TRY(tcp_socket->set_blocking(false));
auto tls_socket = make<TLSv12>(move(tcp_socket), move(options));
tls_socket->set_sni(host);
tls_socket->on_connected = [&] {
promise->resolve({});
};
tls_socket->on_tls_error = [&](auto alert) {
tls_socket->try_disambiguate_error();
promise->reject(AK::Error::from_string_view(enum_to_string(alert)));
};
TRY(promise->await());
tls_socket->on_tls_error = nullptr;
tls_socket->on_connected = nullptr;
tls_socket->m_context.should_expect_successful_read = true;
return tls_socket;
#endif
}
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(Core::SocketAddress address, ByteString const& host, Options options)
{
#ifdef AK_OS_WINDOWS
(void)address;
(void)host;
(void)options;
return AK::Error::from_string_literal("TODO: Unable to connect via address on Windows");
#else
auto promise = Core::Promise<Empty>::construct();
OwnPtr<Core::Socket> tcp_socket = TRY(Core::TCPSocket::connect(address));
TRY(tcp_socket->set_blocking(false));
auto tls_socket = make<TLSv12>(move(tcp_socket), move(options));
tls_socket->set_sni(host);
tls_socket->on_connected = [&] {
promise->resolve({});
};
tls_socket->on_tls_error = [&](auto alert) {
tls_socket->try_disambiguate_error();
promise->reject(AK::Error::from_string_view(enum_to_string(alert)));
};
TRY(promise->await());
tls_socket->on_tls_error = nullptr;
tls_socket->on_connected = nullptr;
tls_socket->m_context.should_expect_successful_read = true;
return tls_socket;
#endif
}
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, Core::Socket& underlying_stream, Options options)
{
auto promise = Core::Promise<Empty>::construct();
TRY(underlying_stream.set_blocking(false));
auto tls_socket = make<TLSv12>(&underlying_stream, move(options));
tls_socket->set_sni(host);
tls_socket->on_connected = [&] {
promise->resolve({});
};
tls_socket->on_tls_error = [&](auto alert) {
tls_socket->try_disambiguate_error();
promise->reject(AK::Error::from_string_view(enum_to_string(alert)));
};
TRY(promise->await());
tls_socket->on_tls_error = nullptr;
tls_socket->on_connected = nullptr;
tls_socket->m_context.should_expect_successful_read = true;
return tls_socket;
}
void TLSv12::setup_connection()
{
Core::deferred_invoke([this] {
auto& stream = underlying_stream();
stream.on_ready_to_read = [this] {
auto result = read_from_socket();
if (result.is_error())
dbgln("Read error: {}", result.error());
};
m_handshake_timeout_timer = Core::Timer::create_single_shot(
m_max_wait_time_for_handshake_in_seconds * 1000, [&] {
dbgln("Handshake timeout :(");
auto timeout_diff = Core::DateTime::now().timestamp() - m_context.handshake_initiation_timestamp;
// If the timeout duration was actually within the max wait time (with a margin of error),
// we're not operating slow, so the server timed out.
// otherwise, it's our fault that the negotiation is taking too long, so extend the timer :P
if (timeout_diff < m_max_wait_time_for_handshake_in_seconds + 1) {
// The server did not respond fast enough,
// time the connection out.
alert(AlertLevel::FATAL, AlertDescription::USER_CANCELED);
m_context.tls_buffer.clear();
m_context.error_code = Error::TimedOut;
m_context.critical_error = (u8)Error::TimedOut;
check_connection_state(false); // Notify the client.
} else {
// Extend the timer, we are too slow.
m_handshake_timeout_timer->restart(m_max_wait_time_for_handshake_in_seconds * 1000);
}
});
auto packet = build_hello();
write_packet(packet);
write_into_socket();
m_handshake_timeout_timer->start();
m_context.handshake_initiation_timestamp = Core::DateTime::now().timestamp();
});
m_has_scheduled_write_flush = true;
}
void TLSv12::notify_client_for_app_data()
{
if (m_context.application_buffer.size() > 0) {
if (on_ready_to_read)
on_ready_to_read();
} else {
if (m_context.connection_finished && !m_context.has_invoked_finish_or_error_callback) {
m_context.has_invoked_finish_or_error_callback = true;
if (on_tls_finished)
on_tls_finished();
}
}
m_has_scheduled_app_data_flush = false;
}
ErrorOr<void> TLSv12::read_from_socket()
{
// If there's anything before we consume stuff, let the client know
// since we won't be consuming things if the connection is terminated.
notify_client_for_app_data();
ScopeGuard notify_guard {
[this] {
// If anything new shows up, tell the client about the event.
notify_client_for_app_data();
}
};
if (!check_connection_state(true))
return {};
u8 buffer[16 * KiB];
Bytes bytes { buffer, array_size(buffer) };
Bytes read_bytes {};
auto& stream = underlying_stream();
do {
auto result = stream.read_some(bytes);
if (result.is_error()) {
if (result.error().is_errno() && result.error().code() != EINTR) {
if (result.error().code() != EAGAIN)
dbgln("TLS Socket read failed, error: {}", result.error());
break;
}
continue;
}
read_bytes = result.release_value();
consume(read_bytes);
} while (!read_bytes.is_empty() && !m_context.critical_error);
if (m_context.should_expect_successful_read && read_bytes.is_empty()) {
// read_some() returned an empty span, this is either an EOF (from improper closure)
// or some sort of weird even that is showing itself as an EOF.
// To guard against servers closing the connection weirdly or just improperly, make sure
// to check the connection state here and send the appropriate notifications.
stream.close();
check_connection_state(true);
}
return {};
}
void TLSv12::write_into_socket()
{
dbgln_if(TLS_DEBUG, "Flushing cached records: {} established? {}", m_context.tls_buffer.size(), is_established());
m_has_scheduled_write_flush = false;
if (!check_connection_state(false))
return;
MUST(flush());
}
bool TLSv12::check_connection_state(bool read)
{
if (m_context.connection_finished)
return false;
if (m_context.close_notify)
m_context.connection_finished = true;
auto& stream = underlying_stream();
if (!stream.is_open()) {
// an abrupt closure (the server is a jerk)
dbgln_if(TLS_DEBUG, "Socket not open, assuming abrupt closure");
m_context.connection_finished = true;
m_context.connection_status = ConnectionStatus::Disconnected;
close();
m_context.has_invoked_finish_or_error_callback = true;
if (on_ready_to_read)
on_ready_to_read(); // Notify the client about the weird event.
if (on_tls_finished)
on_tls_finished();
return false;
}
if (read && stream.is_eof()) {
if (m_context.application_buffer.size() == 0 && m_context.connection_status != ConnectionStatus::Disconnected) {
m_context.has_invoked_finish_or_error_callback = true;
if (on_tls_finished)
on_tls_finished();
}
return false;
}
if (m_context.critical_error) {
dbgln_if(TLS_DEBUG, "CRITICAL ERROR {} :(", m_context.critical_error);
m_context.has_invoked_finish_or_error_callback = true;
if (on_tls_error)
on_tls_error((AlertDescription)m_context.critical_error);
m_context.connection_finished = true;
m_context.connection_status = ConnectionStatus::Disconnected;
close();
return false;
}
if (((read && m_context.application_buffer.size() == 0) || !read) && m_context.connection_finished) {
if (m_context.application_buffer.size() == 0 && m_context.connection_status != ConnectionStatus::Disconnected) {
m_context.has_invoked_finish_or_error_callback = true;
if (on_tls_finished)
on_tls_finished();
}
m_context.connection_status = ConnectionStatus::Disconnected;
if (m_context.tls_buffer.size()) {
dbgln_if(TLS_DEBUG, "connection closed without finishing data transfer, {} bytes still in buffer and {} bytes in application buffer",
m_context.tls_buffer.size(),
m_context.application_buffer.size());
}
if (!m_context.application_buffer.size()) {
return false;
}
}
return true;
}
ErrorOr<bool> TLSv12::flush()
{
ByteBuffer out = move(m_context.tls_buffer);
auto out_bytes = out.bytes();
if (out_bytes.is_empty())
return true;
if constexpr (TLS_DEBUG) {
dbgln("SENDING...");
print_buffer(out_bytes);
}
auto& stream = underlying_stream();
Optional<AK::Error> error;
size_t written;
do {
auto result = stream.write_some(out_bytes);
if (result.is_error()) {
if (result.error().code() != EINTR && result.error().code() != EAGAIN) {
error = result.release_error();
dbgln("TLS Socket write error: {}", *error);
break;
}
continue;
}
written = result.value();
out_bytes = out_bytes.slice(written);
} while (!out_bytes.is_empty());
if (out_bytes.is_empty() && !error.has_value())
return true;
if (!out_bytes.is_empty())
dbgln("Dropping {} bytes worth of TLS records on the floor", out_bytes.size());
return false;
}
void TLSv12::close()
{
if (underlying_stream().is_open())
alert(AlertLevel::FATAL, AlertDescription::CLOSE_NOTIFY);
// bye bye.
m_context.connection_status = ConnectionStatus::Disconnected;
}
}

View file

@ -1,90 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/ByteReader.h>
#include <AK/Endian.h>
#include <AK/Types.h>
#include <LibTLS/Extensions.h>
namespace TLS {
class PacketBuilder {
public:
PacketBuilder(ContentType type, u16 version, size_t size_hint = 0xfdf)
: PacketBuilder(type, (ProtocolVersion)version, size_hint)
{
}
PacketBuilder(ContentType type, ProtocolVersion version, size_t size_hint = 0xfdf)
{
// FIXME: Handle possible OOM situation.
m_packet_data = ByteBuffer::create_uninitialized(size_hint + 16).release_value_but_fixme_should_propagate_errors();
m_current_length = 5;
m_packet_data[0] = (u8)type;
ByteReader::store(m_packet_data.offset_pointer(1), AK::convert_between_host_and_network_endian((u16)version));
}
inline void append(u16 value)
{
value = AK::convert_between_host_and_network_endian(value);
append((u8 const*)&value, sizeof(value));
}
inline void append(u8 value)
{
append((u8 const*)&value, sizeof(value));
}
inline void append(ReadonlyBytes data)
{
append(data.data(), data.size());
}
inline void append_u24(u32 value)
{
u8 buf[3];
buf[0] = value / 0x10000;
value %= 0x10000;
buf[1] = value / 0x100;
value %= 0x100;
buf[2] = value;
append(buf, 3);
}
inline void append(u8 const* data, size_t bytes)
{
if (bytes == 0)
return;
auto old_length = m_current_length;
m_current_length += bytes;
if (m_packet_data.size() < m_current_length) {
m_packet_data.resize(m_current_length);
}
m_packet_data.overwrite(old_length, data, bytes);
}
inline ByteBuffer build()
{
auto length = m_current_length;
m_current_length = 0;
// FIXME: Propagate errors.
return MUST(m_packet_data.slice(0, length));
}
inline void set(size_t offset, u8 value)
{
VERIFY(offset < m_current_length);
m_packet_data[offset] = value;
}
size_t length() const { return m_current_length; }
private:
ByteBuffer m_packet_data;
size_t m_current_length;
};
}

View file

@ -1,518 +1,184 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Base64.h>
#include <AK/Debug.h>
#include <AK/Endian.h>
#include <LibCore/ConfigFile.h>
#include <LibCore/DateTime.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/Constants.h>
#include <LibCrypto/ASN1/PEM.h>
#include <LibCrypto/Certificate/Certificate.h>
#include <LibCrypto/Curves/Ed25519.h>
#include <LibCrypto/Curves/SECPxxxr1.h>
#include <LibTLS/DefaultRootCACertificates.h>
#include <LibCore/Promise.h>
#include <LibCrypto/OpenSSL.h>
#include <LibTLS/TLSv12.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
namespace TLS {
void TLSv12::consume(ReadonlyBytes record)
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, u16 port, Options options)
{
if (m_context.critical_error) {
dbgln("There has been a critical error ({}), refusing to continue", (i8)m_context.critical_error);
return;
}
auto tcp_socket = TRY(Core::TCPSocket::connect(host, port));
return connect_internal(move(tcp_socket), host, options);
}
if (record.size() == 0) {
return;
}
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(Core::SocketAddress address, ByteString const& host, Options options)
{
auto tcp_socket = TRY(Core::TCPSocket::connect(address));
return connect_internal(move(tcp_socket), host, options);
}
dbgln_if(TLS_DEBUG, "Consuming {} bytes", record.size());
void TLSv12::wait_for_activity(bool read)
{
auto sock = SSL_get_fd(m_ssl);
if (m_context.message_buffer.try_append(record).is_error()) {
dbgln("Not enough space in message buffer, dropping the record");
return;
}
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
size_t index { 0 };
size_t buffer_length = m_context.message_buffer.size();
if (read)
select(sock + 1, &fds, nullptr, nullptr, nullptr);
else
select(sock + 1, nullptr, &fds, nullptr, nullptr);
}
size_t size_offset { 3 }; // read the common record header
size_t header_size { 5 };
dbgln_if(TLS_DEBUG, "message buffer length {}", buffer_length);
while (buffer_length >= 5) {
auto length = AK::convert_between_host_and_network_endian(ByteReader::load16(m_context.message_buffer.offset_pointer(index + size_offset))) + header_size;
if (length > buffer_length) {
dbgln_if(TLS_DEBUG, "Need more data: {} > {}", length, buffer_length);
break;
}
auto consumed = handle_message(m_context.message_buffer.bytes().slice(index, length));
if constexpr (TLS_DEBUG) {
if (consumed > 0)
dbgln("consumed {} bytes", consumed);
else
dbgln("error: {}", consumed);
}
if (consumed != (i8)Error::NeedMoreData) {
if (consumed < 0) {
dbgln("Consumed an error: {}", consumed);
if (!m_context.critical_error)
m_context.critical_error = (i8)consumed;
m_context.error_code = (Error)consumed;
break;
ErrorOr<Bytes> TLSv12::read_some(Bytes bytes)
{
while (true) {
auto ret = SSL_read(m_ssl, bytes.data(), bytes.size());
if (ret <= 0) {
auto err = SSL_get_error(m_ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
return Bytes { bytes.data(), 0 };
case SSL_ERROR_WANT_READ:
wait_for_activity(true);
continue;
case SSL_ERROR_WANT_WRITE:
wait_for_activity(false);
continue;
default:
return AK::Error::from_string_literal("Failed reading from SSL connection");
}
} else {
continue;
}
index += length;
buffer_length -= length;
if (m_context.critical_error) {
dbgln("Broken connection");
m_context.error_code = Error::BrokenConnection;
break;
}
}
if (m_context.error_code != Error::NoError && m_context.error_code != Error::NeedMoreData) {
dbgln("consume error: {}", (i8)m_context.error_code);
m_context.message_buffer.clear();
return;
}
if (index) {
// FIXME: Propagate errors.
m_context.message_buffer = MUST(m_context.message_buffer.slice(index, m_context.message_buffer.size() - index));
return Bytes { bytes.data(), static_cast<unsigned long>(ret) };
}
}
void TLSv12::try_disambiguate_error() const
ErrorOr<size_t> TLSv12::write_some(ReadonlyBytes bytes)
{
dbgln("Possible failure cause(s): ");
switch ((AlertDescription)m_context.critical_error) {
case AlertDescription::HANDSHAKE_FAILURE:
if (!m_context.cipher_spec_set) {
dbgln("- No cipher suite in common with {}", m_context.extensions.SNI);
} else {
dbgln("- Unknown internal issue");
}
break;
case AlertDescription::INSUFFICIENT_SECURITY:
dbgln("- No cipher suite in common with {} (the server is oh so secure)", m_context.extensions.SNI);
break;
case AlertDescription::PROTOCOL_VERSION:
dbgln("- The server refused to negotiate with TLS 1.2 :(");
break;
case AlertDescription::UNEXPECTED_MESSAGE:
dbgln("- We sent an invalid message for the state we're in.");
break;
case AlertDescription::BAD_RECORD_MAC:
dbgln("- Bad MAC record from our side.");
dbgln("- Ciphertext wasn't an even multiple of the block length.");
dbgln("- Bad block cipher padding.");
dbgln("- If both sides are compliant, the only cause is messages being corrupted in the network.");
break;
case AlertDescription::RECORD_OVERFLOW:
dbgln("- Sent a ciphertext record which has a length bigger than 18432 bytes.");
dbgln("- Sent record decrypted to a compressed record that has a length bigger than 18432 bytes.");
dbgln("- If both sides are compliant, the only cause is messages being corrupted in the network.");
break;
case AlertDescription::DECOMPRESSION_FAILURE_RESERVED:
dbgln("- We sent invalid input for decompression (e.g. data that would expand to excessive length)");
break;
case AlertDescription::ILLEGAL_PARAMETER:
dbgln("- We sent a parameter in the handshake that is out of range or inconsistent with the other parameters.");
break;
case AlertDescription::DECODE_ERROR:
dbgln("- The message we sent cannot be decoded because a field was out of range or the length was incorrect.");
dbgln("- If both sides are compliant, the only cause is messages being corrupted in the network.");
break;
case AlertDescription::DECRYPT_ERROR:
dbgln("- A handshake crypto operation failed. This includes signature verification and validating Finished.");
break;
case AlertDescription::ACCESS_DENIED:
dbgln("- The certificate is valid, but once access control was applied, the sender decided to stop negotiation.");
break;
case AlertDescription::INTERNAL_ERROR:
dbgln("- No one knows, but it isn't a protocol failure.");
break;
case AlertDescription::DECRYPTION_FAILED_RESERVED:
case AlertDescription::NO_CERTIFICATE_RESERVED:
case AlertDescription::EXPORT_RESTRICTION_RESERVED:
dbgln("- No one knows, the server sent a non-compliant alert.");
break;
default:
dbgln("- No one knows.");
break;
}
dbgln("- {}", enum_to_value((AlertDescription)m_context.critical_error));
}
void TLSv12::set_root_certificates(Vector<Certificate> certificates)
{
if (!m_context.root_certificates.is_empty()) {
dbgln("TLS warn: resetting root certificates!");
m_context.root_certificates.clear();
}
for (auto& cert : certificates) {
if (!cert.is_valid()) {
dbgln("Certificate for {} is invalid, things may or may not work!", cert.subject.to_string());
}
// FIXME: Figure out what we should do when our root certs are invalid.
m_context.root_certificates.set(MUST(cert.subject.to_string()).to_byte_string(), cert);
}
dbgln_if(TLS_DEBUG, "{}: Set {} root certificates", this, m_context.root_certificates.size());
}
static bool wildcard_matches(StringView host, StringView subject)
{
if (host == subject)
return true;
if (subject.starts_with("*."sv)) {
auto maybe_first_dot_index = host.find('.');
if (maybe_first_dot_index.has_value()) {
auto first_dot_index = maybe_first_dot_index.release_value();
return wildcard_matches(host.substring_view(first_dot_index + 1), subject.substring_view(2));
}
}
return false;
}
static bool certificate_subject_matches_host(Certificate const& cert, StringView host)
{
if (wildcard_matches(host, cert.subject.common_name()))
return true;
for (auto& san : cert.SAN) {
if (wildcard_matches(host, san))
return true;
}
return false;
}
bool Context::verify_chain(StringView host) const
{
if (!options.validate_certificates)
return true;
Vector<Certificate> const* local_chain = nullptr;
if (is_server) {
dbgln("Unsupported: Server mode");
TODO();
} else {
local_chain = &certificates;
}
if (local_chain->is_empty()) {
dbgln("verify_chain: Attempting to verify an empty chain");
return false;
}
// RFC5246 section 7.4.2: The sender's certificate MUST come first in the list. Each following certificate
// MUST directly certify the one preceding it. Because certificate validation requires that root keys be
// distributed independently, the self-signed certificate that specifies the root certificate authority MAY be
// omitted from the chain, under the assumption that the remote end must already possess it in order to validate
// it in any case.
if (!host.is_empty()) {
auto const& first_certificate = local_chain->first();
auto subject_matches = certificate_subject_matches_host(first_certificate, host);
if (!subject_matches) {
dbgln("verify_chain: First certificate does not match the hostname");
return false;
}
} else {
// FIXME: The host is taken from m_context.extensions.SNI, when is this empty?
dbgln("FIXME: verify_chain called without host");
return false;
}
for (size_t cert_index = 0; cert_index < local_chain->size(); ++cert_index) {
auto const& cert = local_chain->at(cert_index);
auto subject_string = MUST(cert.subject.to_string());
auto issuer_string = MUST(cert.issuer.to_string());
if (!cert.is_valid()) {
dbgln("verify_chain: Certificate is not valid {}", subject_string);
return false;
}
auto maybe_root_certificate = root_certificates.get(issuer_string.to_byte_string());
if (maybe_root_certificate.has_value()) {
auto& root_certificate = *maybe_root_certificate;
auto verification_correct = verify_certificate_pair(cert, root_certificate);
if (!verification_correct) {
dbgln("verify_chain: Signature inconsistent, {} was not signed by {} (root certificate)", subject_string, issuer_string);
return false;
while (true) {
auto ret = SSL_write(m_ssl, bytes.data(), bytes.size());
if (ret <= 0) {
auto err = SSL_get_error(m_ssl, ret);
switch (err) {
case SSL_ERROR_WANT_READ:
wait_for_activity(true);
continue;
case SSL_ERROR_WANT_WRITE:
wait_for_activity(false);
continue;
default:
return AK::Error::from_string_literal("Failed writing to SSL connection");
}
// Root certificate reached, and correctly verified, so we can stop now
return true;
}
if (subject_string == issuer_string) {
dbgln("verify_chain: Non-root self-signed certificate");
return options.allow_self_signed_certificates;
}
if ((cert_index + 1) >= local_chain->size()) {
dbgln("verify_chain: No trusted root certificate found before end of certificate chain");
dbgln("verify_chain: Last certificate in chain was signed by {}", issuer_string);
return false;
}
auto const& parent_certificate = local_chain->at(cert_index + 1);
if (issuer_string != MUST(parent_certificate.subject.to_string())) {
dbgln("verify_chain: Next certificate in the chain is not the issuer of this certificate");
return false;
}
if (!(parent_certificate.is_allowed_to_sign_certificate && parent_certificate.is_certificate_authority)) {
dbgln("verify_chain: {} is not marked as certificate authority", issuer_string);
return false;
}
if (parent_certificate.path_length_constraint.has_value() && cert_index > parent_certificate.path_length_constraint.value()) {
dbgln("verify_chain: Path length for certificate exceeded");
return false;
}
bool verification_correct = verify_certificate_pair(cert, parent_certificate);
if (!verification_correct) {
dbgln("verify_chain: Signature inconsistent, {} was not signed by {}", subject_string, issuer_string);
return false;
}
}
// Either a root certificate is reached, or parent validation fails as the end of the local chain is reached
VERIFY_NOT_REACHED();
}
bool Context::verify_certificate_pair(Certificate const& subject, Certificate const& issuer) const
{
Crypto::Hash::HashKind kind = Crypto::Hash::HashKind::Unknown;
auto identifier = subject.signature_algorithm.identifier;
bool is_rsa = true;
if (identifier == Crypto::ASN1::rsa_encryption_oid) {
kind = Crypto::Hash::HashKind::None;
} else if (identifier == Crypto::ASN1::rsa_md5_encryption_oid) {
kind = Crypto::Hash::HashKind::MD5;
} else if (identifier == Crypto::ASN1::rsa_sha1_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA1;
} else if (identifier == Crypto::ASN1::rsa_sha256_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA256;
} else if (identifier == Crypto::ASN1::rsa_sha384_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA384;
} else if (identifier == Crypto::ASN1::rsa_sha512_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA512;
} else if (identifier == Crypto::ASN1::ecdsa_with_sha256_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA256;
is_rsa = false;
} else if (identifier == Crypto::ASN1::ecdsa_with_sha384_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA384;
is_rsa = false;
} else if (identifier == Crypto::ASN1::ecdsa_with_sha512_encryption_oid) {
kind = Crypto::Hash::HashKind::SHA512;
is_rsa = false;
}
if (kind == Crypto::Hash::HashKind::Unknown) {
dbgln("verify_certificate_pair: Unknown signature algorithm, expected RSA or ECDSA with SHA1/256/384/512, got OID {}", identifier);
return false;
}
if (is_rsa) {
auto rsa = Crypto::PK::RSA_PKCS1_EMSA(kind, issuer.public_key.rsa);
return MUST(rsa.verify(subject.tbs_asn1, subject.signature_value));
}
// ECDSA hash verification: hash, then check signature against the specific curve
auto ec_curve = oid_to_curve(issuer.public_key.algorithm.ec_parameters.value_or({}));
if (ec_curve.is_error()) {
dbgln("verify_certificate_pair: Unknown curve for ECDSA signature verification");
return false;
}
auto public_point = issuer.public_key.ec.to_secpxxxr1_point();
auto maybe_signature = Crypto::Curves::SECPxxxr1Signature::from_asn(*issuer.public_key.algorithm.ec_parameters, subject.signature_value, {});
if (maybe_signature.is_error()) {
dbgln("verify_certificate_pair: Signature is not ASN.1 DER encoded");
return false;
}
auto signature = maybe_signature.release_value();
switch (ec_curve.release_value()) {
case SupportedGroup::SECP256R1: {
Crypto::Hash::Manager hasher(kind);
hasher.update(subject.tbs_asn1.bytes());
auto hash = hasher.digest();
Crypto::Curves::SECP256r1 curve;
auto result = curve.verify_point(hash.bytes(), public_point, signature);
if (result.is_error()) {
dbgln("verify_certificate_pair: Failed to check SECP256r1 signature {}", result.release_error());
return false;
}
return result.value();
}
case SupportedGroup::SECP384R1: {
Crypto::Hash::Manager hasher(kind);
hasher.update(subject.tbs_asn1.bytes());
auto hash = hasher.digest();
Crypto::Curves::SECP384r1 curve;
auto result = curve.verify_point(hash.bytes(), public_point, signature);
if (result.is_error()) {
dbgln("verify_certificate_pair: Failed to check SECP384r1 signature {}", result.release_error());
return false;
}
return result.value();
}
case SupportedGroup::X25519: {
Crypto::Curves::Ed25519 curve;
auto result = curve.verify(issuer.public_key.raw_key, subject.signature_value, subject.tbs_asn1.bytes());
if (!result) {
dbgln("verify_certificate_pair: Failed to check Ed25519 signature");
return false;
}
return result;
}
default:
dbgln("verify_certificate_pair: Don't know how to verify signature for curve {}", to_underlying(ec_curve.release_value()));
return false;
return ret;
}
}
template<typename HMACType>
static void hmac_pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b)
bool TLSv12::is_eof() const
{
if (!secret.size()) {
dbgln("null secret");
return;
}
return BIO_eof(m_bio) == 1;
}
auto append_label_seed = [&](auto& hmac) {
hmac.update(label, label_length);
hmac.update(seed);
if (seed_b.size() > 0)
hmac.update(seed_b);
bool TLSv12::is_open() const
{
return !BIO_get_close(m_bio);
}
void TLSv12::close()
{
SSL_shutdown(m_ssl);
}
ErrorOr<size_t> TLSv12::pending_bytes() const
{
return SSL_pending(m_ssl);
}
ErrorOr<bool> TLSv12::can_read_without_blocking(int count) const
{
return SSL_pending(m_ssl) >= count;
}
ErrorOr<void> TLSv12::set_blocking(bool block)
{
return m_socket->set_blocking(block);
}
ErrorOr<void> TLSv12::set_close_on_exec(bool enabled)
{
return m_socket->set_close_on_exec(enabled);
}
TLSv12::TLSv12(NonnullOwnPtr<Core::TCPSocket> socket, SSL_CTX* ssl_ctx, SSL* ssl, BIO* bio)
: m_ssl_ctx(ssl_ctx)
, m_ssl(ssl)
, m_bio(bio)
, m_socket(move(socket))
{
m_socket->on_ready_to_read = [this] {
if (on_ready_to_read)
on_ready_to_read();
};
HMACType hmac(secret);
append_label_seed(hmac);
auto digest_size = hmac.digest_size();
auto digest_0 = MUST(ByteBuffer::create_uninitialized(digest_size));
digest_0.overwrite(0, hmac.digest().immutable_data(), digest_size);
size_t index = 0;
while (index < output.size()) {
hmac.update(digest_0.bytes());
append_label_seed(hmac);
auto digest_1 = hmac.digest();
auto copy_size = min(digest_size, output.size() - index);
output.overwrite(index, digest_1.immutable_data(), copy_size);
index += copy_size;
digest_0.overwrite(0, hmac.process(digest_0.bytes()).immutable_data(), digest_size);
}
}
void TLSv12::pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b)
TLSv12::~TLSv12()
{
// Simplification: We only support the HMAC PRF with the hash function SHA-256 or stronger.
// RFC 5246: "In this section, we define one PRF, based on HMAC. This PRF with the
// SHA-256 hash function is used for all cipher suites defined in this
// document and in TLS documents published prior to this document when
// TLS 1.2 is negotiated. New cipher suites MUST explicitly specify a
// PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a
// stronger standard hash function."
switch (hmac_hash()) {
case Crypto::Hash::HashKind::SHA512:
hmac_pseudorandom_function<Crypto::Authentication::HMAC<Crypto::Hash::SHA512>>(output, secret, label, label_length, seed, seed_b);
break;
case Crypto::Hash::HashKind::SHA384:
hmac_pseudorandom_function<Crypto::Authentication::HMAC<Crypto::Hash::SHA384>>(output, secret, label, label_length, seed, seed_b);
break;
case Crypto::Hash::HashKind::SHA256:
hmac_pseudorandom_function<Crypto::Authentication::HMAC<Crypto::Hash::SHA256>>(output, secret, label, label_length, seed, seed_b);
break;
default:
dbgln("Failed to find a suitable HMAC hash");
VERIFY_NOT_REACHED();
break;
}
SSL_free(m_ssl);
SSL_CTX_free(m_ssl_ctx);
}
TLSv12::TLSv12(StreamVariantType stream, Options options)
: m_stream(move(stream))
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect_internal(NonnullOwnPtr<Core::TCPSocket> socket, ByteString const& host, Options options)
{
m_context.options = move(options);
m_context.is_server = false;
m_context.tls_buffer = {};
auto* ssl_ctx = OPENSSL_TRY_PTR(SSL_CTX_new(TLS_client_method()));
ArmedScopeGuard free_ssl_ctx = [&] { SSL_CTX_free(ssl_ctx); };
set_root_certificates(m_context.options.root_certificates.has_value()
? *m_context.options.root_certificates
: DefaultRootCACertificates::the().certificates());
// Configure the client to abort the handshake if certificate verification fails.
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr);
setup_connection();
}
Vector<Certificate> TLSv12::parse_pem_certificate(ReadonlyBytes certificate_pem_buffer, ReadonlyBytes rsa_key) // FIXME: This should not be bound to RSA
{
if (certificate_pem_buffer.is_empty() || rsa_key.is_empty()) {
return {};
if (options.root_certificates_path.has_value()) {
auto path = options.root_certificates_path.value();
SSL_CTX_load_verify_file(ssl_ctx, path.characters());
} else {
// Use the default trusted certificate store
OPENSSL_TRY(SSL_CTX_set_default_verify_paths(ssl_ctx));
}
auto decoded_certificate = Crypto::decode_pem(certificate_pem_buffer);
if (decoded_certificate.type != Crypto::PEMType::Certificate) {
dbgln("Certificate not PEM");
return {};
}
// Require a minimum TLS version of TLSv1.2.
OPENSSL_TRY(SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION));
auto maybe_certificate = Certificate::parse_certificate(decoded_certificate.data);
if (!maybe_certificate.is_error()) {
dbgln("Invalid certificate");
return {};
}
auto* ssl = OPENSSL_TRY_PTR(SSL_new(ssl_ctx));
ArmedScopeGuard free_ssl = [&] { SSL_free(ssl); };
Crypto::PK::RSA rsa(rsa_key);
auto certificate = maybe_certificate.release_value();
certificate.private_key = rsa.private_key();
// Tell the server which hostname we are attempting to connect to in case the server supports multiple hosts.
OPENSSL_TRY(SSL_set_tlsext_host_name(ssl, host.characters()));
return { move(certificate) };
}
// Ensure we check that the server has supplied a certificate for the hostname that we were expecting.
OPENSSL_TRY(SSL_set1_host(ssl, host.characters()));
ErrorOr<SupportedGroup> oid_to_curve(Vector<int> curve)
{
if (curve == Crypto::ASN1::secp384r1_oid)
return SupportedGroup::SECP384R1;
if (curve == Crypto::ASN1::secp256r1_oid)
return SupportedGroup::SECP256R1;
auto* bio = OPENSSL_TRY_PTR(BIO_new_socket(socket->fd(), 0));
return AK::Error::from_string_literal("Unknown curve oid");
// SSL takes ownership of the BIO and will handle freeing it
SSL_set_bio(ssl, bio, bio);
OPENSSL_TRY(SSL_connect(ssl));
free_ssl.disarm();
free_ssl_ctx.disarm();
return adopt_own(*new TLSv12(move(socket), ssl_ctx, ssl, bio));
}
}

View file

@ -1,158 +1,19 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IPv4Address.h>
#include <AK/Queue.h>
#include <AK/WeakPtr.h>
#include <LibCore/Notifier.h>
#include <LibCore/Socket.h>
#include <LibCore/Timer.h>
#include <LibCrypto/Authentication/HMAC.h>
#include <LibCrypto/Certificate/Certificate.h>
#include <LibCrypto/Cipher/AES.h>
#include <LibCrypto/Curves/EllipticCurve.h>
#include <LibCrypto/Hash/HashManager.h>
#include <LibTLS/CipherSuite.h>
#include <LibTLS/TLSPacketBuilder.h>
#include <LibTLS/OpenSSLForward.h>
namespace TLS {
using Crypto::Certificate::Certificate;
inline void print_buffer(ReadonlyBytes buffer)
{
dbgln("{:hex-dump}", buffer);
}
inline void print_buffer(ByteBuffer const& buffer)
{
print_buffer(buffer.bytes());
}
inline void print_buffer(u8 const* buffer, size_t size)
{
print_buffer(ReadonlyBytes { buffer, size });
}
class Socket;
enum class Error : i8 {
NoError = 0,
UnknownError = -1,
BrokenPacket = -2,
NotUnderstood = -3,
NoCommonCipher = -5,
UnexpectedMessage = -6,
CloseConnection = -7,
CompressionNotSupported = -8,
NotVerified = -9,
NotSafe = -10,
IntegrityCheckFailed = -11,
ErrorAlert = -12,
BrokenConnection = -13,
BadCertificate = -14,
UnsupportedCertificate = -15,
NoRenegotiation = -16,
FeatureNotSupported = -17,
DecryptionFailed = -20,
NeedMoreData = -21,
TimedOut = -22,
OutOfMemory = -23,
};
enum class WritePacketStage {
Initial = 0,
ClientHandshake = 1,
ServerHandshake = 2,
Finished = 3,
};
enum class ConnectionStatus {
Disconnected,
Negotiating,
KeyExchange,
Renegotiating,
Established,
};
enum ClientVerificationStaus {
Verified,
VerificationNeeded,
};
// Note for the 16 iv length instead of 8:
// 4 bytes of fixed IV, 8 random (nonce) bytes, 4 bytes for counter
// GCM specifically asks us to transmit only the nonce, the counter is zero
// and the fixed IV is derived from the premaster key.
//
// The cipher suite list below is ordered based on the recommendations from Mozilla.
// When changing the supported cipher suites, please consult the webpage below for
// the preferred order.
//
// https://wiki.mozilla.org/Security/Server_Side_TLS
#define ENUMERATE_CIPHERS(C) \
C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \
C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \
C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \
C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \
C(true, CipherSuite::TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \
C(true, CipherSuite::TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \
C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \
C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \
C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \
C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \
C(true, CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \
C(true, CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \
C(true, CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA256, 16, false) \
C(true, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA256, 16, false) \
C(true, CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \
C(true, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false)
constexpr KeyExchangeAlgorithm get_key_exchange_algorithm(CipherSuite suite)
{
switch (suite) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return key_exchange;
ENUMERATE_CIPHERS(C)
#undef C
default:
return KeyExchangeAlgorithm::Invalid;
}
}
constexpr CipherAlgorithm get_cipher_algorithm(CipherSuite suite)
{
switch (suite) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return cipher;
ENUMERATE_CIPHERS(C)
#undef C
default:
return CipherAlgorithm::Invalid;
}
}
ErrorOr<SupportedGroup> oid_to_curve(Vector<int> curve);
struct Options {
static Vector<CipherSuite> default_usable_cipher_suites()
{
Vector<CipherSuite> cipher_suites;
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
if constexpr (is_supported) \
cipher_suites.empend(suite);
ENUMERATE_CIPHERS(C)
#undef C
return cipher_suites;
}
Vector<CipherSuite> usable_cipher_suites = default_usable_cipher_suites();
#define OPTION_WITH_DEFAULTS(typ, name, ...) \
static typ default_##name() \
@ -171,384 +32,50 @@ struct Options {
return move(*this); \
}
OPTION_WITH_DEFAULTS(ProtocolVersion, version, ProtocolVersion::VERSION_1_2)
OPTION_WITH_DEFAULTS(Vector<SignatureAndHashAlgorithm>, supported_signature_algorithms,
{ HashAlgorithm::SHA512, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA384, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA256, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA1, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA256, SignatureAlgorithm::ECDSA },
{ HashAlgorithm::SHA384, SignatureAlgorithm::ECDSA },
{ HashAlgorithm::INTRINSIC, SignatureAlgorithm::ED25519 });
OPTION_WITH_DEFAULTS(Vector<SupportedGroup>, elliptic_curves,
SupportedGroup::X25519,
SupportedGroup::SECP256R1,
SupportedGroup::SECP384R1,
SupportedGroup::X448)
OPTION_WITH_DEFAULTS(Vector<ECPointFormat>, supported_ec_point_formats, ECPointFormat::UNCOMPRESSED)
OPTION_WITH_DEFAULTS(bool, use_sni, true)
OPTION_WITH_DEFAULTS(bool, use_compression, false)
OPTION_WITH_DEFAULTS(bool, validate_certificates, true)
OPTION_WITH_DEFAULTS(bool, allow_self_signed_certificates, false)
OPTION_WITH_DEFAULTS(Optional<Vector<Certificate>>, root_certificates, )
OPTION_WITH_DEFAULTS(Function<void(AlertDescription)>, alert_handler, [](auto) { })
OPTION_WITH_DEFAULTS(Function<void()>, finish_callback, [] { })
OPTION_WITH_DEFAULTS(Function<Vector<Certificate>()>, certificate_provider, [] { return Vector<Certificate> {}; })
OPTION_WITH_DEFAULTS(bool, enable_extended_master_secret, true)
#undef OPTION_WITH_DEFAULTS
};
class SegmentedBuffer {
public:
[[nodiscard]] size_t size() const { return m_size; }
[[nodiscard]] bool is_empty() const { return m_size == 0; }
void transfer(Bytes dest, size_t size)
{
VERIFY(size <= dest.size());
size_t transferred = 0;
while (transferred < size) {
auto& buffer = m_buffers.head();
size_t to_transfer = min(buffer.size() - m_offset_into_current_buffer, size - transferred);
memcpy(dest.offset(transferred), buffer.data() + m_offset_into_current_buffer, to_transfer);
transferred += to_transfer;
m_offset_into_current_buffer += to_transfer;
if (m_offset_into_current_buffer >= buffer.size()) {
m_buffers.dequeue();
m_offset_into_current_buffer = 0;
}
m_size -= to_transfer;
}
}
AK::ErrorOr<void> try_append(ReadonlyBytes data)
{
if (Checked<size_t>::addition_would_overflow(m_size, data.size()))
return AK::Error::from_errno(EOVERFLOW);
m_size += data.size();
m_buffers.enqueue(TRY(ByteBuffer::copy(data)));
return {};
}
private:
size_t m_size { 0 };
Queue<ByteBuffer> m_buffers;
size_t m_offset_into_current_buffer { 0 };
};
struct Context {
bool verify_chain(StringView host) const;
bool verify_certificate_pair(Certificate const& subject, Certificate const& issuer) const;
Options options;
u8 remote_random[32];
u8 local_random[32];
u8 session_id[32];
u8 session_id_size { 0 };
CipherSuite cipher;
bool is_server { false };
Vector<Certificate> certificates;
Certificate private_key;
Vector<Certificate> client_certificates;
ByteBuffer master_key;
ByteBuffer premaster_key;
u8 cipher_spec_set { 0 };
struct {
int created { 0 };
u8 remote_mac[32];
u8 local_mac[32];
u8 local_iv[16];
u8 remote_iv[16];
u8 local_aead_iv[4];
u8 remote_aead_iv[4];
} crypto;
Crypto::Hash::Manager handshake_hash;
ByteBuffer message_buffer;
u64 remote_sequence_number { 0 };
u64 local_sequence_number { 0 };
ConnectionStatus connection_status { ConnectionStatus::Disconnected };
bool should_expect_successful_read { false };
u8 critical_error { 0 };
Error error_code { Error::NoError };
ByteBuffer tls_buffer;
SegmentedBuffer application_buffer;
bool is_child { false };
struct {
// Server Name Indicator
ByteString SNI; // I hate your existence
bool extended_master_secret { false };
} extensions;
u8 request_client_certificate { 0 };
ByteBuffer cached_handshake;
ClientVerificationStaus client_verified { Verified };
bool connection_finished { false };
bool close_notify { false };
bool has_invoked_finish_or_error_callback { false };
// message flags
u8 handshake_messages[11] { 0 };
ByteBuffer user_data;
HashMap<ByteString, Certificate> root_certificates;
Vector<ByteString> alpn;
StringView negotiated_alpn;
size_t send_retries { 0 };
time_t handshake_initiation_timestamp { 0 };
struct {
ByteBuffer p;
ByteBuffer g;
ByteBuffer Ys;
} server_diffie_hellman_params;
OwnPtr<Crypto::Curves::EllipticCurve> server_key_exchange_curve;
OPTION_WITH_DEFAULTS(Optional<ByteString>, root_certificates_path, )
};
class TLSv12 final : public Core::Socket {
private:
Core::Socket& underlying_stream()
{
return *m_stream.visit([&](auto& stream) -> Core::Socket* { return stream; });
}
Core::Socket const& underlying_stream() const
{
return *m_stream.visit([&](auto& stream) -> Core::Socket const* { return stream; });
}
public:
/// Reads into a buffer, with the maximum size being the size of the buffer.
/// The amount of bytes read can be smaller than the size of the buffer.
/// Returns either the bytes that were read, or an errno in the case of
/// Returns either the bytes that were read, or an error in the case of
/// failure.
virtual ErrorOr<Bytes> read_some(Bytes) override;
/// Tries to write the entire contents of the buffer. It is possible for
/// less than the full buffer to be written. Returns either the amount of
/// bytes written into the stream, or an errno in the case of failure.
/// bytes written into the stream, or an error in the case of failure.
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
virtual bool is_eof() const override { return m_context.application_buffer.is_empty() && (m_context.connection_finished || underlying_stream().is_eof()); }
virtual bool is_eof() const override;
virtual bool is_open() const override;
virtual bool is_open() const override { return is_established(); }
virtual void close() override;
virtual ErrorOr<size_t> pending_bytes() const override { return m_context.application_buffer.size(); }
virtual ErrorOr<bool> can_read_without_blocking(int = 0) const override { return !m_context.application_buffer.is_empty(); }
virtual ErrorOr<void> set_blocking(bool block) override
{
VERIFY(!block);
return {};
}
virtual ErrorOr<void> set_close_on_exec(bool enabled) override { return underlying_stream().set_close_on_exec(enabled); }
virtual void set_notifications_enabled(bool enabled) override { underlying_stream().set_notifications_enabled(enabled); }
virtual ErrorOr<size_t> pending_bytes() const override;
virtual ErrorOr<bool> can_read_without_blocking(int = 0) const override;
virtual ErrorOr<void> set_blocking(bool block) override;
virtual ErrorOr<void> set_close_on_exec(bool enabled) override;
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(Core::SocketAddress, ByteString const& host, Options = {});
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, u16 port, Options = {});
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, Core::Socket& underlying_stream, Options = {});
using StreamVariantType = Variant<OwnPtr<Core::Socket>, Core::Socket*>;
explicit TLSv12(StreamVariantType, Options);
bool is_established() const { return m_context.connection_status == ConnectionStatus::Established; }
void set_sni(StringView sni)
{
if (m_context.is_server || m_context.critical_error || m_context.connection_status != ConnectionStatus::Disconnected) {
dbgln("invalid state for set_sni");
return;
}
m_context.extensions.SNI = sni;
}
void set_root_certificates(Vector<Certificate>);
static Vector<Certificate> parse_pem_certificate(ReadonlyBytes certificate_pem_buffer, ReadonlyBytes key_pem_buffer);
StringView alpn() const { return m_context.negotiated_alpn; }
bool supports_cipher(CipherSuite suite) const
{
switch (suite) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return is_supported;
ENUMERATE_CIPHERS(C)
#undef C
default:
return false;
}
}
bool supports_version(ProtocolVersion v) const
{
return v == ProtocolVersion::VERSION_1_2;
}
void alert(AlertLevel, AlertDescription);
Function<void(AlertDescription)> on_tls_error;
Function<void()> on_tls_finished;
Function<void(TLSv12&)> on_tls_certificate_request;
Function<void()> on_connected;
~TLSv12() override;
private:
void setup_connection();
explicit TLSv12(NonnullOwnPtr<Core::TCPSocket>, SSL_CTX*, SSL*, BIO*);
void consume(ReadonlyBytes record);
static ErrorOr<NonnullOwnPtr<TLSv12>> connect_internal(NonnullOwnPtr<Core::TCPSocket>, ByteString const&, Options);
ByteBuffer hmac_message(ReadonlyBytes buf, Optional<ReadonlyBytes> const buf2, size_t mac_length, bool local = false);
void ensure_hmac(size_t digest_size, bool local);
void wait_for_activity(bool);
void update_packet(ByteBuffer& packet);
void update_hash(ReadonlyBytes in, size_t header_size);
SSL_CTX* m_ssl_ctx { nullptr };
SSL* m_ssl { nullptr };
BIO* m_bio { nullptr };
void write_packet(ByteBuffer& packet, bool immediately = false);
ByteBuffer build_client_key_exchange();
ByteBuffer build_server_key_exchange();
ByteBuffer build_hello();
ByteBuffer build_handshake_finished();
ByteBuffer build_certificate();
ByteBuffer build_alert(bool critical, u8 code);
ByteBuffer build_change_cipher_spec();
void build_rsa_pre_master_secret(PacketBuilder&);
void build_dhe_rsa_pre_master_secret(PacketBuilder&);
void build_ecdhe_rsa_pre_master_secret(PacketBuilder&);
ErrorOr<bool> flush();
void write_into_socket();
ErrorOr<void> read_from_socket();
bool check_connection_state(bool read);
void notify_client_for_app_data();
ssize_t handle_server_hello(ReadonlyBytes, WritePacketStage&);
ssize_t handle_handshake_finished(ReadonlyBytes, WritePacketStage&);
ssize_t handle_certificate(ReadonlyBytes);
ssize_t handle_server_key_exchange(ReadonlyBytes);
ssize_t handle_dhe_rsa_server_key_exchange(ReadonlyBytes);
ssize_t handle_ecdhe_server_key_exchange(ReadonlyBytes, u8& server_public_key_length);
ssize_t handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes);
ssize_t handle_ecdhe_ecdsa_server_key_exchange(ReadonlyBytes);
ssize_t handle_server_hello_done(ReadonlyBytes);
ssize_t handle_certificate_verify(ReadonlyBytes);
ssize_t handle_handshake_payload(ReadonlyBytes);
ssize_t handle_message(ReadonlyBytes);
void pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b);
ssize_t verify_rsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer);
ssize_t verify_ecdsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer);
size_t key_length() const
{
switch (m_context.cipher) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return cipher_key_size(cipher) / 8;
ENUMERATE_CIPHERS(C)
#undef C
default:
return 128 / 8;
}
}
size_t mac_length() const
{
switch (m_context.cipher) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return hash ::digest_size();
ENUMERATE_CIPHERS(C)
#undef C
default:
return Crypto::Hash::SHA256::digest_size();
}
}
Crypto::Hash::HashKind hmac_hash() const
{
switch (mac_length()) {
case Crypto::Hash::SHA512::DigestSize:
return Crypto::Hash::HashKind::SHA512;
case Crypto::Hash::SHA384::DigestSize:
return Crypto::Hash::HashKind::SHA384;
case Crypto::Hash::SHA256::DigestSize:
case Crypto::Hash::SHA1::DigestSize:
default:
return Crypto::Hash::HashKind::SHA256;
}
}
size_t iv_length() const
{
switch (m_context.cipher) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return iv_size;
ENUMERATE_CIPHERS(C)
#undef C
default:
return 16;
}
}
bool is_aead() const
{
switch (m_context.cipher) {
#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \
case suite: \
return is_aead;
ENUMERATE_CIPHERS(C)
#undef C
default:
return false;
}
}
bool expand_key();
bool compute_master_secret_from_pre_master_secret(size_t length);
void try_disambiguate_error() const;
bool m_eof { false };
StreamVariantType m_stream;
Context m_context;
OwnPtr<Crypto::Authentication::HMAC<Crypto::Hash::Manager>> m_hmac_local;
OwnPtr<Crypto::Authentication::HMAC<Crypto::Hash::Manager>> m_hmac_remote;
using CipherVariant = Variant<
Empty,
Crypto::Cipher::AESCipher::CBCMode,
Crypto::Cipher::AESCipher::GCMMode>;
CipherVariant m_cipher_local {};
CipherVariant m_cipher_remote {};
bool m_has_scheduled_write_flush { false };
bool m_has_scheduled_app_data_flush { false };
i32 m_max_wait_time_for_handshake_in_seconds { 10 };
RefPtr<Core::Timer> m_handshake_timeout_timer;
// Keep this around or the socket will be closed
NonnullOwnPtr<Core::TCPSocket> m_socket;
};
}

View file

@ -45,13 +45,8 @@ void WebSocketImplSerenity::connect(ConnectionInfo const& connection_info)
auto socket_result = [&]() -> ErrorOr<NonnullOwnPtr<Core::BufferedSocketBase>> {
auto host = connection_info.url().serialized_host().to_byte_string();
if (connection_info.is_secure()) {
TLS::Options options;
options.set_alert_handler([this](auto) {
on_connection_error();
});
return TRY(Core::BufferedSocket<TLS::TLSv12>::create(
TRY(TLS::TLSv12::connect(host, connection_info.url().port_or_default(), move(options)))));
TRY(TLS::TLSv12::connect(host, connection_info.url().port_or_default()))));
}
return TRY(Core::BufferedTCPSocket::create(
@ -66,6 +61,7 @@ void WebSocketImplSerenity::connect(ConnectionInfo const& connection_info)
}
m_socket = socket_result.release_value();
MUST(m_socket->set_blocking(false));
m_socket->on_ready_to_read = [this] {
on_ready_to_read();

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Peter Bocan <me@pbocan.net>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -10,7 +11,6 @@
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/PEM.h>
#include <LibFileSystem/FileSystem.h>
#include <LibTLS/DefaultRootCACertificates.h>
#include <LibTLS/TLSv12.h>
#include <LibTest/TestCase.h>
@ -24,10 +24,7 @@ static ByteBuffer operator""_b(char const* string, size_t length)
return ByteBuffer::copy(string, length).release_value();
}
ErrorOr<Vector<Crypto::Certificate::Certificate>> load_certificates();
ByteString locate_ca_certs_file();
ByteString locate_ca_certs_file()
static Optional<ByteString> locate_ca_certs_file()
{
if (FileSystem::exists(ca_certs_file)) {
return ca_certs_file;
@ -36,50 +33,23 @@ ByteString locate_ca_certs_file()
if (FileSystem::exists(on_target_path)) {
return on_target_path;
}
return "";
}
ErrorOr<Vector<Crypto::Certificate::Certificate>> load_certificates()
{
auto cacert_file = TRY(Core::File::open(locate_ca_certs_file(), Core::File::OpenMode::Read));
auto data = TRY(cacert_file->read_until_eof());
return TRY(DefaultRootCACertificates::parse_pem_root_certificate_authorities(data));
return {};
}
TEST_CASE(test_TLS_hello_handshake)
{
Core::EventLoop loop;
TLS::Options options;
options.set_root_certificates(TRY_OR_FAIL(load_certificates()));
options.set_alert_handler([&](TLS::AlertDescription) {
FAIL("Connection failure");
loop.quit(1);
});
options.set_finish_callback([&] {
loop.quit(0);
});
options.set_root_certificates_path(locate_ca_certs_file());
auto tls = TRY_OR_FAIL(TLS::TLSv12::connect(DEFAULT_SERVER, port, move(options)));
ByteBuffer contents;
tls->on_ready_to_read = [&] {
(void)TRY_OR_FAIL(tls->read_some(contents.must_get_bytes_for_writing(4 * KiB)));
loop.quit(0);
};
if (tls->write_until_depleted("GET / HTTP/1.1\r\nHost: "_b).is_error()) {
FAIL("write(0) failed");
return;
}
TRY_OR_FAIL(tls->write_until_depleted("GET /generate_204 HTTP/1.1\r\nHost: "_b));
auto the_server = DEFAULT_SERVER;
if (tls->write_until_depleted(the_server.bytes()).is_error()) {
FAIL("write(1) failed");
return;
}
if (tls->write_until_depleted("\r\nConnection : close\r\n\r\n"_b).is_error()) {
FAIL("write(2) failed");
return;
}
TRY_OR_FAIL(tls->write_until_depleted(the_server.bytes()));
TRY_OR_FAIL(tls->write_until_depleted("\r\nConnection: close\r\n\r\n"_b));
loop.exec();
auto tmp = TRY_OR_FAIL(ByteBuffer::create_zeroed(128));
auto contents = TRY_OR_FAIL(tls->read_some(tmp));
EXPECT(contents.starts_with("HTTP/1.1 204 No Content\r\n"_b));
}