mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 11:36:10 +00:00
LibTLS: Replace TLSv12
implementation with OpenSSL
This commit is contained in:
parent
d6e9d2cdbb
commit
7a38a3e994
Notes:
github-actions[bot]
2025-02-17 18:53:51 +00:00
Author: https://github.com/devgianlu Commit: https://github.com/LadybirdBrowser/ladybird/commit/7a38a3e9947 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3571 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/alimpfard ✅
15 changed files with 182 additions and 4709 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
13
Libraries/LibTLS/OpenSSLForward.h
Normal file
13
Libraries/LibTLS/OpenSSLForward.h
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue