mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 09:34:57 +09:00
Everywhere: Hoist the Libraries folder to the top-level
This commit is contained in:
parent
950e819ee7
commit
93712b24bf
Notes:
github-actions[bot]
2024-11-10 11:51:52 +00:00
Author: https://github.com/trflynn89
Commit: 93712b24bf
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2256
Reviewed-by: https://github.com/sideshowbarker
4547 changed files with 104 additions and 113 deletions
17
Libraries/LibTLS/CMakeLists.txt
Normal file
17
Libraries/LibTLS/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
add_cxx_compile_options(-Wvla)
|
||||
|
||||
set(SOURCES
|
||||
Certificate.cpp
|
||||
Handshake.cpp
|
||||
HandshakeCertificate.cpp
|
||||
HandshakeClient.cpp
|
||||
HandshakeServer.cpp
|
||||
Record.cpp
|
||||
Socket.cpp
|
||||
TLSv12.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibTLS tls)
|
||||
target_link_libraries(LibTLS PRIVATE LibCore LibCrypto LibFileSystem)
|
||||
|
||||
include(ca_certificates_data)
|
910
Libraries/LibTLS/Certificate.cpp
Normal file
910
Libraries/LibTLS/Certificate.cpp
Normal file
|
@ -0,0 +1,910 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
* Copyright (c) 2023, stelar7 <dudedbz@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Certificate.h"
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/IPv4Address.h>
|
||||
#include <LibCrypto/ASN1/ASN1.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/ASN1/PEM.h>
|
||||
|
||||
namespace {
|
||||
static String s_error_string;
|
||||
}
|
||||
|
||||
namespace TLS {
|
||||
|
||||
#define ERROR_WITH_SCOPE(error) \
|
||||
do { \
|
||||
s_error_string = TRY(String::formatted("{}: {}", current_scope, error)); \
|
||||
return Error::from_string_view(s_error_string.bytes_as_string_view()); \
|
||||
} while (0)
|
||||
|
||||
#define ENTER_TYPED_SCOPE(tag_kind_name, scope) \
|
||||
do { \
|
||||
if (auto tag = decoder.peek(); tag.is_error() || tag.value().kind != Crypto::ASN1::Kind::tag_kind_name) { \
|
||||
if (tag.is_error()) \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted(scope " data was invalid: {}", tag.error()))); \
|
||||
else \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted(scope " data was not of kind " #tag_kind_name " was {}", Crypto::ASN1::kind_name(tag.value().kind)))); \
|
||||
} \
|
||||
ENTER_SCOPE(scope); \
|
||||
} while (0)
|
||||
|
||||
#define ENTER_SCOPE(scope) \
|
||||
do { \
|
||||
if (auto result = decoder.enter(); result.is_error()) { \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Failed to enter scope: {}", scope))); \
|
||||
} \
|
||||
PUSH_SCOPE(scope) \
|
||||
} while (0)
|
||||
|
||||
#define PUSH_SCOPE(scope) current_scope.append(#scope##sv);
|
||||
|
||||
#define EXIT_SCOPE() \
|
||||
do { \
|
||||
if (auto error = decoder.leave(); error.is_error()) { \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Failed to exit scope: {}", error.error()))); \
|
||||
} \
|
||||
POP_SCOPE(); \
|
||||
} while (0)
|
||||
|
||||
#define POP_SCOPE() current_scope.remove(current_scope.size() - 1);
|
||||
|
||||
#define READ_OBJECT(kind_name, type_name, value_name) \
|
||||
auto value_name##_result = decoder.read<type_name>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::kind_name); \
|
||||
if (value_name##_result.is_error()) { \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Read of kind " #kind_name " failed: {}", value_name##_result.error()))); \
|
||||
} \
|
||||
auto value_name = value_name##_result.release_value();
|
||||
|
||||
#define REWRITE_TAG(kind_name) \
|
||||
auto value_name##_result = decoder.rewrite_tag(Crypto::ASN1::Kind::kind_name); \
|
||||
if (value_name##_result.is_error()) { \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Rewrite of kind " #kind_name " failed: {}", value_name##_result.error()))); \
|
||||
}
|
||||
|
||||
#define DROP_OBJECT() \
|
||||
do { \
|
||||
if (auto error = decoder.drop(); error.is_error()) { \
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Drop failed: {}", error.error()))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static ErrorOr<SupportedGroup> oid_to_curve(Vector<int> curve)
|
||||
{
|
||||
if (curve == curve_ansip384r1)
|
||||
return SupportedGroup::SECP384R1;
|
||||
else if (curve == curve_prime256)
|
||||
return SupportedGroup::SECP256R1;
|
||||
|
||||
return Error::from_string_literal("Unknown curve oid");
|
||||
}
|
||||
|
||||
static ErrorOr<Crypto::UnsignedBigInteger> parse_certificate_version(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// Version ::= INTEGER {v1(0), v2(1), v3(2)}
|
||||
if (auto tag = decoder.peek(); !tag.is_error() && tag.value().type == Crypto::ASN1::Type::Constructed) {
|
||||
ENTER_SCOPE("Version"sv);
|
||||
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, version);
|
||||
if (version > 3) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Invalid version value at {}", current_scope)));
|
||||
}
|
||||
EXIT_SCOPE();
|
||||
return version;
|
||||
} else {
|
||||
return Crypto::UnsignedBigInteger { 0 };
|
||||
}
|
||||
}
|
||||
|
||||
static ErrorOr<Crypto::UnsignedBigInteger> parse_serial_number(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// CertificateSerialNumber ::= INTEGER
|
||||
PUSH_SCOPE("CertificateSerialNumber"sv);
|
||||
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, serial);
|
||||
POP_SCOPE();
|
||||
return serial;
|
||||
}
|
||||
|
||||
static ErrorOr<SupportedGroup> parse_ec_parameters(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// ECParameters ::= CHOICE {
|
||||
// namedCurve OBJECT IDENTIFIER
|
||||
// }
|
||||
PUSH_SCOPE("ECParameters"sv);
|
||||
READ_OBJECT(ObjectIdentifier, Vector<int>, named_curve);
|
||||
// Note: namedCurve sometimes has 5 nodes, but we need 7 for the comparison below to work.
|
||||
while (named_curve.size() < 7) {
|
||||
named_curve.append(0);
|
||||
}
|
||||
POP_SCOPE();
|
||||
|
||||
bool is_known_curve = false;
|
||||
for (auto const& curves : known_curve_identifiers) {
|
||||
if (curves.span() == named_curve.span()) {
|
||||
is_known_curve = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_known_curve) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Unknown named curve {}", named_curve)));
|
||||
}
|
||||
|
||||
return oid_to_curve(named_curve);
|
||||
}
|
||||
|
||||
static ErrorOr<AlgorithmIdentifier> parse_algorithm_identifier(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// AlgorithmIdentifier{ALGORITHM:SupportedAlgorithms} ::= SEQUENCE {
|
||||
// algorithm ALGORITHM.&id({SupportedAlgorithms}),
|
||||
// parameters ALGORITHM.&Type({SupportedAlgorithms}{@algorithm}) OPTIONAL,
|
||||
// ... }
|
||||
ENTER_TYPED_SCOPE(Sequence, "AlgorithmIdentifier"sv);
|
||||
PUSH_SCOPE("algorithm"sv);
|
||||
READ_OBJECT(ObjectIdentifier, Vector<int>, algorithm);
|
||||
// Note: ecPublicKey only has 6 nodes, but we need 7 for the comparison below to work.
|
||||
while (algorithm.size() < 7) {
|
||||
algorithm.append(0);
|
||||
}
|
||||
POP_SCOPE();
|
||||
|
||||
bool is_known_algorithm = false;
|
||||
for (auto const& inner : known_algorithm_identifiers) {
|
||||
if (inner.span() == algorithm.span()) {
|
||||
is_known_algorithm = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_known_algorithm) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Unknown algorithm {}", algorithm)));
|
||||
}
|
||||
|
||||
// -- When the following OIDs are used in an AlgorithmIdentifier, the
|
||||
// -- parameters MUST be present and MUST be NULL.
|
||||
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 }
|
||||
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
|
||||
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
|
||||
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
|
||||
// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
|
||||
// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
|
||||
// sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 }
|
||||
Array<Array<int, 7>, 8> rsa_null_algorithms = {
|
||||
rsa_encryption_oid,
|
||||
rsa_md5_encryption_oid,
|
||||
rsa_sha1_encryption_oid,
|
||||
rsa_sha256_encryption_oid,
|
||||
rsa_sha384_encryption_oid,
|
||||
rsa_sha512_encryption_oid,
|
||||
rsa_sha224_encryption_oid,
|
||||
};
|
||||
|
||||
bool is_rsa_null_algorithm = false;
|
||||
for (auto const& inner : rsa_null_algorithms) {
|
||||
if (inner.span() == algorithm.span()) {
|
||||
is_rsa_null_algorithm = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_rsa_null_algorithm) {
|
||||
PUSH_SCOPE("RSA null parameter"sv);
|
||||
READ_OBJECT(Null, void*, forced_null);
|
||||
(void)forced_null;
|
||||
POP_SCOPE();
|
||||
|
||||
EXIT_SCOPE();
|
||||
return AlgorithmIdentifier(algorithm);
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc5758.txt
|
||||
// When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
|
||||
// ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
|
||||
// as an AlgorithmIdentifier, the encoding MUST omit the parameters
|
||||
// field.
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8410#section-9
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
Array<Array<int, 7>, 8> no_parameter_algorithms = {
|
||||
ecdsa_with_sha224_encryption_oid,
|
||||
ecdsa_with_sha256_encryption_oid,
|
||||
ecdsa_with_sha384_encryption_oid,
|
||||
ecdsa_with_sha512_encryption_oid,
|
||||
x25519_oid,
|
||||
x448_oid,
|
||||
ed25519_oid,
|
||||
ed448_oid
|
||||
};
|
||||
|
||||
bool is_no_parameter_algorithm = false;
|
||||
for (auto const& inner : no_parameter_algorithms) {
|
||||
if (inner.span() == algorithm.span()) {
|
||||
is_no_parameter_algorithm = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_no_parameter_algorithm) {
|
||||
EXIT_SCOPE();
|
||||
|
||||
return AlgorithmIdentifier(algorithm);
|
||||
}
|
||||
|
||||
if (algorithm.span() == ec_public_key_encryption_oid.span()) {
|
||||
// The parameters associated with id-ecPublicKey SHOULD be absent or ECParameters,
|
||||
// and NULL is allowed to support legacy implementations.
|
||||
if (decoder.eof()) {
|
||||
EXIT_SCOPE();
|
||||
|
||||
return AlgorithmIdentifier(algorithm);
|
||||
}
|
||||
|
||||
auto tag = TRY(decoder.peek());
|
||||
if (tag.kind == Crypto::ASN1::Kind::Null) {
|
||||
PUSH_SCOPE("ecPublicKey null parameter"sv);
|
||||
READ_OBJECT(Null, void*, forced_null);
|
||||
(void)forced_null;
|
||||
POP_SCOPE();
|
||||
|
||||
EXIT_SCOPE();
|
||||
return AlgorithmIdentifier(algorithm);
|
||||
}
|
||||
|
||||
auto algorithm_identifier = AlgorithmIdentifier(algorithm);
|
||||
algorithm_identifier.ec_parameters = TRY(parse_ec_parameters(decoder, current_scope));
|
||||
|
||||
EXIT_SCOPE();
|
||||
return algorithm_identifier;
|
||||
}
|
||||
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled parameters for algorithm {}", algorithm)));
|
||||
}
|
||||
|
||||
static ErrorOr<RelativeDistinguishedName> parse_name(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
RelativeDistinguishedName rdn {};
|
||||
// Name ::= Choice {
|
||||
// rdn_sequence RDNSequence
|
||||
// } // NOTE: since this is the only alternative, there's no index
|
||||
// RDNSequence ::= Sequence OF RelativeDistinguishedName
|
||||
ENTER_TYPED_SCOPE(Sequence, "Name"sv);
|
||||
while (!decoder.eof()) {
|
||||
// RelativeDistinguishedName ::= Set OF AttributeTypeAndValue
|
||||
ENTER_TYPED_SCOPE(Set, "RDNSequence"sv);
|
||||
while (!decoder.eof()) {
|
||||
// AttributeTypeAndValue ::= Sequence {
|
||||
// type AttributeType,
|
||||
// value AttributeValue
|
||||
// }
|
||||
ENTER_TYPED_SCOPE(Sequence, "AttributeTypeAndValue"sv);
|
||||
// AttributeType ::= ObjectIdentifier
|
||||
PUSH_SCOPE("AttributeType"sv)
|
||||
READ_OBJECT(ObjectIdentifier, Vector<int>, attribute_type_oid);
|
||||
POP_SCOPE();
|
||||
|
||||
// AttributeValue ::= Any
|
||||
PUSH_SCOPE("AttributeValue"sv)
|
||||
READ_OBJECT(PrintableString, StringView, attribute_value);
|
||||
POP_SCOPE();
|
||||
|
||||
auto attribute_type_string = TRY(String::join("."sv, attribute_type_oid));
|
||||
auto attribute_value_string = TRY(String::from_utf8(attribute_value));
|
||||
TRY(rdn.set(move(attribute_type_string), move(attribute_value_string)));
|
||||
|
||||
EXIT_SCOPE();
|
||||
}
|
||||
EXIT_SCOPE();
|
||||
}
|
||||
EXIT_SCOPE();
|
||||
|
||||
return rdn;
|
||||
}
|
||||
|
||||
static ErrorOr<UnixDateTime> parse_time(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// Time ::= Choice {
|
||||
// utc_time UTCTime,
|
||||
// general_time GeneralizedTime
|
||||
// }
|
||||
auto tag = TRY(decoder.peek());
|
||||
if (tag.kind == Crypto::ASN1::Kind::UTCTime) {
|
||||
PUSH_SCOPE("UTCTime"sv);
|
||||
|
||||
READ_OBJECT(UTCTime, StringView, utc_time);
|
||||
auto parse_result = Crypto::ASN1::parse_utc_time(utc_time);
|
||||
if (!parse_result.has_value()) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Failed to parse UTCTime {}", utc_time)));
|
||||
}
|
||||
|
||||
POP_SCOPE();
|
||||
return parse_result.release_value();
|
||||
}
|
||||
|
||||
if (tag.kind == Crypto::ASN1::Kind::GeneralizedTime) {
|
||||
PUSH_SCOPE("GeneralizedTime"sv);
|
||||
|
||||
READ_OBJECT(UTCTime, StringView, generalized_time);
|
||||
auto parse_result = Crypto::ASN1::parse_generalized_time(generalized_time);
|
||||
if (!parse_result.has_value()) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Failed to parse GeneralizedTime {}", generalized_time)));
|
||||
}
|
||||
|
||||
POP_SCOPE();
|
||||
return parse_result.release_value();
|
||||
}
|
||||
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Unrecognised Time format {}", kind_name(tag.kind))));
|
||||
}
|
||||
|
||||
static ErrorOr<Validity> parse_validity(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
Validity validity {};
|
||||
|
||||
// Validity ::= SEQUENCE {
|
||||
// notBefore Time,
|
||||
// notAfter Time }
|
||||
ENTER_TYPED_SCOPE(Sequence, "Validity"sv);
|
||||
|
||||
validity.not_before = TRY(parse_time(decoder, current_scope));
|
||||
validity.not_after = TRY(parse_time(decoder, current_scope));
|
||||
|
||||
EXIT_SCOPE();
|
||||
|
||||
return validity;
|
||||
}
|
||||
|
||||
ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// SubjectPublicKeyInfo ::= Sequence {
|
||||
// algorithm AlgorithmIdentifier,
|
||||
// subject_public_key BitString
|
||||
// }
|
||||
|
||||
SubjectPublicKey public_key;
|
||||
ENTER_TYPED_SCOPE(Sequence, "SubjectPublicKeyInfo"sv);
|
||||
|
||||
public_key.algorithm = TRY(parse_algorithm_identifier(decoder, current_scope));
|
||||
|
||||
PUSH_SCOPE("subjectPublicKey"sv);
|
||||
READ_OBJECT(BitString, Crypto::ASN1::BitStringView, value);
|
||||
POP_SCOPE();
|
||||
|
||||
public_key.raw_key = TRY(ByteBuffer::copy(TRY(value.raw_bytes())));
|
||||
|
||||
if (public_key.algorithm.identifier.span() == rsa_encryption_oid.span()) {
|
||||
auto key = Crypto::PK::RSA::parse_rsa_key(TRY(value.raw_bytes()));
|
||||
if (!key.public_key.length()) {
|
||||
return Error::from_string_literal("Invalid RSA key");
|
||||
}
|
||||
|
||||
public_key.rsa = move(key.public_key);
|
||||
|
||||
EXIT_SCOPE();
|
||||
return public_key;
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8410#section-9
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
Array<Array<int, 7>, 5> no_parameter_algorithms = {
|
||||
ec_public_key_encryption_oid,
|
||||
x25519_oid,
|
||||
x448_oid,
|
||||
ed25519_oid,
|
||||
ed448_oid
|
||||
};
|
||||
|
||||
for (auto const& inner : no_parameter_algorithms) {
|
||||
if (public_key.algorithm.identifier.span() == inner.span()) {
|
||||
// Note: Raw key is already stored, so we can just exit out at this point.
|
||||
EXIT_SCOPE();
|
||||
return public_key;
|
||||
}
|
||||
}
|
||||
|
||||
String algo_oid = TRY(String::join("."sv, public_key.algorithm.identifier));
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled algorithm {}", algo_oid)));
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5208#section-5
|
||||
ErrorOr<PrivateKey> parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// PrivateKeyInfo ::= SEQUENCE {
|
||||
// version Version,
|
||||
// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||
// privateKey PrivateKey,
|
||||
// attributes [0] IMPLICIT Attributes OPTIONAL
|
||||
// }
|
||||
|
||||
PrivateKey private_key;
|
||||
ENTER_TYPED_SCOPE(Sequence, "PrivateKeyInfo"sv);
|
||||
|
||||
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, version);
|
||||
if (version != 0) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Invalid version value at {}", current_scope)));
|
||||
}
|
||||
private_key.algorithm = TRY(parse_algorithm_identifier(decoder, current_scope));
|
||||
|
||||
PUSH_SCOPE("privateKey"sv);
|
||||
READ_OBJECT(OctetString, StringView, value);
|
||||
POP_SCOPE();
|
||||
|
||||
private_key.raw_key = TRY(ByteBuffer::copy(value.bytes()));
|
||||
|
||||
if (private_key.algorithm.identifier.span() == rsa_encryption_oid.span()) {
|
||||
auto key = Crypto::PK::RSA::parse_rsa_key(value.bytes());
|
||||
if (key.private_key.length() == 0) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Invalid RSA key at {}", current_scope)));
|
||||
}
|
||||
|
||||
private_key.rsa = move(key.private_key);
|
||||
|
||||
EXIT_SCOPE();
|
||||
return private_key;
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8410#section-9
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
Array<Array<int, 7>, 5> no_parameter_algorithms = {
|
||||
ec_public_key_encryption_oid,
|
||||
x25519_oid,
|
||||
x448_oid,
|
||||
ed25519_oid,
|
||||
ed448_oid
|
||||
};
|
||||
|
||||
for (auto const& inner : no_parameter_algorithms) {
|
||||
if (private_key.algorithm.identifier.span() == inner.span()) {
|
||||
// Note: Raw key is already stored, so we can just exit out at this point.
|
||||
EXIT_SCOPE();
|
||||
return private_key;
|
||||
}
|
||||
}
|
||||
|
||||
String algo_oid = TRY(String::join("."sv, private_key.algorithm.identifier));
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled algorithm {}", algo_oid)));
|
||||
}
|
||||
|
||||
static ErrorOr<Crypto::ASN1::BitStringView> parse_unique_identifier(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// UniqueIdentifier ::= BIT STRING
|
||||
PUSH_SCOPE("UniqueIdentifier"sv);
|
||||
READ_OBJECT(BitString, Crypto::ASN1::BitStringView, value);
|
||||
POP_SCOPE();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static ErrorOr<String> parse_general_name(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// GeneralName ::= CHOICE {
|
||||
// otherName [0] INSTANCE OF OTHER-NAME,
|
||||
// rfc822Name [1] IA5String,
|
||||
// dNSName [2] IA5String,
|
||||
// x400Address [3] ORAddress,
|
||||
// directoryName [4] Name,
|
||||
// ediPartyName [5] EDIPartyName,
|
||||
// uniformResourceIdentifier [6] IA5String,
|
||||
// iPAddress [7] OCTET STRING,
|
||||
// registeredID [8] OBJECT IDENTIFIER,
|
||||
// }
|
||||
auto tag = TRY(decoder.peek());
|
||||
auto tag_value = static_cast<u8>(tag.kind);
|
||||
switch (tag_value) {
|
||||
case 0:
|
||||
// Note: We don't know how to use this.
|
||||
PUSH_SCOPE("otherName"sv)
|
||||
DROP_OBJECT();
|
||||
POP_SCOPE();
|
||||
break;
|
||||
case 1: {
|
||||
PUSH_SCOPE("rfc822Name"sv)
|
||||
READ_OBJECT(IA5String, StringView, name);
|
||||
POP_SCOPE();
|
||||
return String::from_utf8(name);
|
||||
}
|
||||
case 2: {
|
||||
PUSH_SCOPE("dNSName"sv)
|
||||
READ_OBJECT(IA5String, StringView, name);
|
||||
POP_SCOPE();
|
||||
return String::from_utf8(name);
|
||||
}
|
||||
case 3:
|
||||
// Note: We don't know how to use this.
|
||||
PUSH_SCOPE("x400Address"sv)
|
||||
DROP_OBJECT();
|
||||
POP_SCOPE();
|
||||
break;
|
||||
case 4: {
|
||||
PUSH_SCOPE("directoryName"sv);
|
||||
READ_OBJECT(OctetString, StringView, directory_name);
|
||||
Crypto::ASN1::Decoder decoder { directory_name.bytes() };
|
||||
auto names = TRY(parse_name(decoder, current_scope));
|
||||
POP_SCOPE();
|
||||
return names.to_string();
|
||||
}
|
||||
case 5:
|
||||
// Note: We don't know how to use this.
|
||||
PUSH_SCOPE("ediPartyName");
|
||||
DROP_OBJECT();
|
||||
POP_SCOPE();
|
||||
break;
|
||||
case 6: {
|
||||
PUSH_SCOPE("uniformResourceIdentifier"sv);
|
||||
READ_OBJECT(IA5String, StringView, name);
|
||||
POP_SCOPE();
|
||||
return String::from_utf8(name);
|
||||
}
|
||||
case 7: {
|
||||
PUSH_SCOPE("iPAddress"sv);
|
||||
READ_OBJECT(OctetString, StringView, ip_addr_sv);
|
||||
IPv4Address ip_addr { ip_addr_sv.bytes().data() };
|
||||
POP_SCOPE();
|
||||
return ip_addr.to_string();
|
||||
}
|
||||
case 8: {
|
||||
PUSH_SCOPE("registeredID"sv);
|
||||
READ_OBJECT(ObjectIdentifier, Vector<int>, identifier);
|
||||
POP_SCOPE();
|
||||
return String::join("."sv, identifier);
|
||||
}
|
||||
default:
|
||||
ERROR_WITH_SCOPE("Unknown tag in GeneralNames choice"sv);
|
||||
}
|
||||
|
||||
ERROR_WITH_SCOPE("Unknown tag in GeneralNames choice"sv);
|
||||
}
|
||||
|
||||
static ErrorOr<Vector<String>> parse_general_names(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// GeneralNames ::= Sequence OF GeneralName
|
||||
ENTER_TYPED_SCOPE(Sequence, "GeneralNames");
|
||||
|
||||
Vector<String> names;
|
||||
while (!decoder.eof()) {
|
||||
names.append(TRY(parse_general_name(decoder, current_scope)));
|
||||
}
|
||||
|
||||
EXIT_SCOPE();
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
static ErrorOr<Vector<String>> parse_subject_alternative_names(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// SubjectAlternativeName ::= GeneralNames
|
||||
PUSH_SCOPE("SubjectAlternativeName"sv);
|
||||
auto values = TRY(parse_general_names(decoder, current_scope));
|
||||
POP_SCOPE();
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
static ErrorOr<Vector<String>> parse_issuer_alternative_names(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// issuerAltName ::= GeneralNames
|
||||
PUSH_SCOPE("issuerAltName"sv);
|
||||
auto values = TRY(parse_general_names(decoder, current_scope));
|
||||
POP_SCOPE();
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
static ErrorOr<Crypto::ASN1::BitStringView> parse_key_usage(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// KeyUsage ::= BIT STRING {
|
||||
// digitalSignature (0),
|
||||
// contentCommitment (1),
|
||||
// keyEncipherment (2),
|
||||
// dataEncipherment (3),
|
||||
// keyAgreement (4),
|
||||
// keyCertSign (5),
|
||||
// cRLSign (6),
|
||||
// encipherOnly (7),
|
||||
// decipherOnly (8)
|
||||
// }
|
||||
|
||||
PUSH_SCOPE("KeyUsage"sv);
|
||||
READ_OBJECT(BitString, Crypto::ASN1::BitStringView, usage);
|
||||
POP_SCOPE();
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
static ErrorOr<BasicConstraints> parse_basic_constraints(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// BasicConstraints ::= SEQUENCE {
|
||||
// cA BOOLEAN DEFAULT FALSE,
|
||||
// pathLenConstraint INTEGER (0..MAX) OPTIONAL
|
||||
// }
|
||||
|
||||
BasicConstraints constraints {};
|
||||
|
||||
ENTER_TYPED_SCOPE(Sequence, "BasicConstraints"sv);
|
||||
|
||||
if (decoder.eof()) {
|
||||
EXIT_SCOPE();
|
||||
return constraints;
|
||||
}
|
||||
|
||||
auto ca_tag = TRY(decoder.peek());
|
||||
if (ca_tag.kind == Crypto::ASN1::Kind::Boolean) {
|
||||
PUSH_SCOPE("cA"sv);
|
||||
READ_OBJECT(Boolean, bool, is_certificate_authority);
|
||||
constraints.is_certificate_authority = is_certificate_authority;
|
||||
POP_SCOPE();
|
||||
}
|
||||
|
||||
if (decoder.eof()) {
|
||||
EXIT_SCOPE();
|
||||
return constraints;
|
||||
}
|
||||
|
||||
auto path_length_tag = TRY(decoder.peek());
|
||||
if (path_length_tag.kind == Crypto::ASN1::Kind::Integer) {
|
||||
PUSH_SCOPE("pathLenConstraint"sv);
|
||||
READ_OBJECT(Integer, Crypto::UnsignedBigInteger, path_length_constraint);
|
||||
constraints.path_length_constraint = path_length_constraint;
|
||||
POP_SCOPE();
|
||||
}
|
||||
|
||||
EXIT_SCOPE();
|
||||
return constraints;
|
||||
}
|
||||
|
||||
static ErrorOr<void> parse_extension(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope, Certificate& certificate)
|
||||
{
|
||||
// Extension ::= Sequence {
|
||||
// extension_id ObjectIdentifier,
|
||||
// critical Boolean DEFAULT false,
|
||||
// extension_value OctetString (DER-encoded)
|
||||
// }
|
||||
ENTER_TYPED_SCOPE(Sequence, "Extension"sv);
|
||||
|
||||
PUSH_SCOPE("extension_id"sv);
|
||||
READ_OBJECT(ObjectIdentifier, Vector<int>, extension_id);
|
||||
POP_SCOPE();
|
||||
|
||||
bool is_critical = false;
|
||||
auto peek = TRY(decoder.peek());
|
||||
if (peek.kind == Crypto::ASN1::Kind::Boolean) {
|
||||
PUSH_SCOPE("critical"sv);
|
||||
READ_OBJECT(Boolean, bool, extension_critical);
|
||||
is_critical = extension_critical;
|
||||
POP_SCOPE();
|
||||
}
|
||||
|
||||
PUSH_SCOPE("extension_value"sv);
|
||||
READ_OBJECT(OctetString, StringView, extension_value);
|
||||
POP_SCOPE();
|
||||
|
||||
bool is_known_extension = false;
|
||||
|
||||
Crypto::ASN1::Decoder extension_decoder { extension_value.bytes() };
|
||||
Vector<StringView, 8> extension_scope {};
|
||||
if (extension_id == subject_alternative_name_oid) {
|
||||
is_known_extension = true;
|
||||
auto alternate_names = TRY(parse_subject_alternative_names(extension_decoder, extension_scope));
|
||||
certificate.SAN = alternate_names;
|
||||
}
|
||||
|
||||
if (extension_id == key_usage_oid) {
|
||||
is_known_extension = true;
|
||||
auto usage = TRY(parse_key_usage(extension_decoder, extension_scope));
|
||||
certificate.is_allowed_to_sign_certificate = usage.get(5);
|
||||
}
|
||||
|
||||
if (extension_id == basic_constraints_oid) {
|
||||
is_known_extension = true;
|
||||
auto constraints = TRY(parse_basic_constraints(extension_decoder, extension_scope));
|
||||
certificate.is_certificate_authority = constraints.is_certificate_authority;
|
||||
certificate.path_length_constraint = constraints.path_length_constraint.to_u64();
|
||||
}
|
||||
|
||||
if (extension_id == issuer_alternative_name_oid) {
|
||||
is_known_extension = true;
|
||||
auto alternate_names = TRY(parse_issuer_alternative_names(extension_decoder, extension_scope));
|
||||
certificate.IAN = alternate_names;
|
||||
}
|
||||
|
||||
EXIT_SCOPE();
|
||||
|
||||
if (is_critical && !is_known_extension) {
|
||||
ERROR_WITH_SCOPE(TRY(String::formatted("Extension {} is critical, but we do not support it", extension_id)));
|
||||
}
|
||||
|
||||
if (!is_known_extension) {
|
||||
dbgln_if(TLS_DEBUG, TRY(String::formatted("{}: Unhandled extension: {}", current_scope, extension_id)));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<void> parse_extensions(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope, Certificate& certificate)
|
||||
{
|
||||
// Extensions ::= Sequence OF Extension
|
||||
ENTER_TYPED_SCOPE(Sequence, "Extensions"sv);
|
||||
|
||||
while (!decoder.eof()) {
|
||||
TRY(parse_extension(decoder, current_scope, certificate));
|
||||
}
|
||||
|
||||
EXIT_SCOPE();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<Certificate> parse_tbs_certificate(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
|
||||
{
|
||||
// TBSCertificate ::= SEQUENCE {
|
||||
// version [0] Version DEFAULT v1,
|
||||
// serialNumber CertificateSerialNumber,
|
||||
// signature AlgorithmIdentifier{{SupportedAlgorithms}},
|
||||
// issuer Name,
|
||||
// validity Validity,
|
||||
// subject Name,
|
||||
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||||
// issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// ...,
|
||||
// [[2: -- if present, version shall be v2 or v3
|
||||
// subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL]],
|
||||
// [[3: -- if present, version shall be v2 or v3
|
||||
// extensions [3] Extensions OPTIONAL]]
|
||||
// -- If present, version shall be v3]]
|
||||
// }
|
||||
|
||||
// Note: Parse out the ASN.1 of this object, since its used for TLS verification.
|
||||
// To do this, we get the bytes of our parent, the size of ourself, and slice the parent buffer.
|
||||
auto pre_cert_buffer = TRY(decoder.peek_entry_bytes());
|
||||
|
||||
// FIXME: Dont assume this value.
|
||||
// Note: we assume this to be 4. 1 for the tag, and 3 for the length.
|
||||
auto entry_length_byte_count = 4;
|
||||
|
||||
ENTER_TYPED_SCOPE(Sequence, "TBSCertificate"sv);
|
||||
|
||||
auto post_cert_buffer = TRY(decoder.peek_entry_bytes());
|
||||
if (pre_cert_buffer.size() < post_cert_buffer.size() + entry_length_byte_count) {
|
||||
ERROR_WITH_SCOPE("Unexpected end of file");
|
||||
}
|
||||
|
||||
Certificate certificate;
|
||||
certificate.version = TRY(parse_certificate_version(decoder, current_scope)).to_u64();
|
||||
certificate.serial_number = TRY(parse_serial_number(decoder, current_scope));
|
||||
certificate.algorithm = TRY(parse_algorithm_identifier(decoder, current_scope));
|
||||
certificate.issuer = TRY(parse_name(decoder, current_scope));
|
||||
certificate.validity = TRY(parse_validity(decoder, current_scope));
|
||||
certificate.subject = TRY(parse_name(decoder, current_scope));
|
||||
certificate.public_key = TRY(parse_subject_public_key_info(decoder, current_scope));
|
||||
certificate.tbs_asn1 = TRY(ByteBuffer::copy(pre_cert_buffer.slice(0, post_cert_buffer.size() + entry_length_byte_count)));
|
||||
|
||||
if (!decoder.eof()) {
|
||||
auto tag = TRY(decoder.peek());
|
||||
if (static_cast<u8>(tag.kind) == 1) {
|
||||
REWRITE_TAG(BitString)
|
||||
TRY(parse_unique_identifier(decoder, current_scope));
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoder.eof()) {
|
||||
auto tag = TRY(decoder.peek());
|
||||
if (static_cast<u8>(tag.kind) == 2) {
|
||||
REWRITE_TAG(BitString)
|
||||
TRY(parse_unique_identifier(decoder, current_scope));
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoder.eof()) {
|
||||
auto tag = TRY(decoder.peek());
|
||||
if (static_cast<u8>(tag.kind) == 3) {
|
||||
REWRITE_TAG(Sequence)
|
||||
ENTER_TYPED_SCOPE(Sequence, "extensions"sv);
|
||||
|
||||
TRY(parse_extensions(decoder, current_scope, certificate));
|
||||
|
||||
EXIT_SCOPE();
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoder.eof()) {
|
||||
ERROR_WITH_SCOPE("Reached end of TBS parse with more data left"sv);
|
||||
}
|
||||
|
||||
certificate.is_self_issued = TRY(certificate.issuer.to_string()) == TRY(certificate.subject.to_string());
|
||||
|
||||
EXIT_SCOPE();
|
||||
|
||||
return certificate;
|
||||
}
|
||||
|
||||
ErrorOr<Certificate> Certificate::parse_certificate(ReadonlyBytes buffer, bool)
|
||||
{
|
||||
Crypto::ASN1::Decoder decoder { buffer };
|
||||
Vector<StringView, 8> current_scope {};
|
||||
|
||||
// Certificate ::= SIGNED{TBSCertificate}
|
||||
|
||||
// SIGNED{ToBeSigned} ::= SEQUENCE {
|
||||
// toBeSigned ToBeSigned,
|
||||
// COMPONENTS OF SIGNATURE{ToBeSigned},
|
||||
// }
|
||||
|
||||
// SIGNATURE{ToBeSigned} ::= SEQUENCE {
|
||||
// algorithmIdentifier AlgorithmIdentifier{{SupportedAlgorithms}},
|
||||
// encrypted ENCRYPTED-HASH{ToBeSigned},
|
||||
// }
|
||||
|
||||
// ENCRYPTED-HASH{ToBeSigned} ::= BIT STRING (CONSTRAINED BY {
|
||||
// -- shall be the result of applying a hashing procedure to the DER-encoded (see 6.2)
|
||||
// -- octets of a value of -- ToBeSigned -- and then applying an encipherment procedure
|
||||
// -- to those octets -- } )
|
||||
|
||||
ENTER_TYPED_SCOPE(Sequence, "Certificate"sv);
|
||||
|
||||
Certificate certificate = TRY(parse_tbs_certificate(decoder, current_scope));
|
||||
certificate.original_asn1 = TRY(ByteBuffer::copy(buffer));
|
||||
|
||||
certificate.signature_algorithm = TRY(parse_algorithm_identifier(decoder, current_scope));
|
||||
|
||||
PUSH_SCOPE("signature"sv);
|
||||
READ_OBJECT(BitString, Crypto::ASN1::BitStringView, signature);
|
||||
certificate.signature_value = TRY(ByteBuffer::copy(TRY(signature.raw_bytes())));
|
||||
POP_SCOPE();
|
||||
|
||||
if (!decoder.eof()) {
|
||||
ERROR_WITH_SCOPE("Reached end of Certificate parse with more data left"sv);
|
||||
}
|
||||
|
||||
EXIT_SCOPE();
|
||||
|
||||
return certificate;
|
||||
}
|
||||
|
||||
#undef PUSH_SCOPE
|
||||
#undef ENTER_SCOPE
|
||||
#undef ENTER_TYPED_SCOPE
|
||||
#undef POP_SCOPE
|
||||
#undef EXIT_SCOPE
|
||||
#undef READ_OBJECT
|
||||
#undef DROP_OBJECT
|
||||
#undef REWRITE_TAG
|
||||
|
||||
ErrorOr<String> RelativeDistinguishedName::to_string() const
|
||||
{
|
||||
#define ADD_IF_RECOGNIZED(identifier, shorthand_code) \
|
||||
if (member_identifier == identifier) { \
|
||||
cert_name.appendff("\\{}={}", shorthand_code, value); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
StringBuilder cert_name;
|
||||
|
||||
for (auto const& [member_identifier, value] : m_members) {
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::SerialNumber), "SERIALNUMBER");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Email), "MAIL");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Title), "T");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::PostalCode), "PC");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::DnQualifier), "DNQ");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::GivenName), "GIVENNAME");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Surname), "SN");
|
||||
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Cn), "CN");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::L), "L");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::St), "ST");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::O), "O");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Ou), "OU");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::C), "C");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Street), "STREET");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Dc), "DC");
|
||||
ADD_IF_RECOGNIZED(enum_value(AttributeType::Uid), "UID");
|
||||
|
||||
cert_name.appendff("\\{}={}", member_identifier, value);
|
||||
}
|
||||
#undef ADD_IF_RECOGNIZED
|
||||
|
||||
return cert_name.to_string();
|
||||
}
|
||||
}
|
322
Libraries/LibTLS/Certificate.h
Normal file
322
Libraries/LibTLS/Certificate.h
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Time.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibCrypto/PK/RSA.h>
|
||||
#include <LibTLS/Extensions.h>
|
||||
|
||||
namespace TLS {
|
||||
|
||||
constexpr static Array<int, 7>
|
||||
rsa_encryption_oid { 1, 2, 840, 113549, 1, 1, 1 },
|
||||
rsa_md5_encryption_oid { 1, 2, 840, 113549, 1, 1, 4 },
|
||||
rsa_sha1_encryption_oid { 1, 2, 840, 113549, 1, 1, 5 },
|
||||
rsa_sha256_encryption_oid { 1, 2, 840, 113549, 1, 1, 11 },
|
||||
rsa_sha384_encryption_oid { 1, 2, 840, 113549, 1, 1, 12 },
|
||||
rsa_sha512_encryption_oid { 1, 2, 840, 113549, 1, 1, 13 },
|
||||
rsa_sha224_encryption_oid { 1, 2, 840, 113549, 1, 1, 14 },
|
||||
ecdsa_with_sha224_encryption_oid { 1, 2, 840, 10045, 4, 3, 1 },
|
||||
ecdsa_with_sha256_encryption_oid { 1, 2, 840, 10045, 4, 3, 2 },
|
||||
ecdsa_with_sha384_encryption_oid { 1, 2, 840, 10045, 4, 3, 3 },
|
||||
ecdsa_with_sha512_encryption_oid { 1, 2, 840, 10045, 4, 3, 4 },
|
||||
ec_public_key_encryption_oid { 1, 2, 840, 10045, 2, 1 },
|
||||
x25519_oid { 1, 3, 101, 110 },
|
||||
x448_oid { 1, 3, 101, 111 },
|
||||
ed25519_oid { 1, 3, 101, 112 },
|
||||
ed448_oid { 1, 3, 101, 113 };
|
||||
|
||||
constexpr static Array<Array<int, 7>, 10> known_algorithm_identifiers {
|
||||
rsa_encryption_oid,
|
||||
rsa_md5_encryption_oid,
|
||||
rsa_sha1_encryption_oid,
|
||||
rsa_sha256_encryption_oid,
|
||||
rsa_sha384_encryption_oid,
|
||||
rsa_sha512_encryption_oid,
|
||||
ecdsa_with_sha256_encryption_oid,
|
||||
ecdsa_with_sha384_encryption_oid,
|
||||
ec_public_key_encryption_oid,
|
||||
x25519_oid
|
||||
};
|
||||
|
||||
constexpr static Array<int, 7>
|
||||
curve_ansip384r1 { 1, 3, 132, 0, 34 },
|
||||
curve_prime256 { 1, 2, 840, 10045, 3, 1, 7 };
|
||||
|
||||
constexpr static Array<Array<int, 7>, 9> known_curve_identifiers {
|
||||
curve_ansip384r1,
|
||||
curve_prime256
|
||||
};
|
||||
|
||||
constexpr static Array<int, 4>
|
||||
key_usage_oid { 2, 5, 29, 15 },
|
||||
subject_alternative_name_oid { 2, 5, 29, 17 },
|
||||
issuer_alternative_name_oid { 2, 5, 29, 18 },
|
||||
basic_constraints_oid { 2, 5, 29, 19 };
|
||||
|
||||
#define _ENUM(key, value) key,
|
||||
|
||||
#define __ENUM_OBJECT_CLASS \
|
||||
_ENUM(ApplicationProcess, "2.5.6.11"sv) \
|
||||
_ENUM(Country, "2.5.6.2"sv) \
|
||||
_ENUM(DcObject, "1.3.6.1.4.1.1466.344"sv) \
|
||||
_ENUM(Device, "2.5.6.14"sv) \
|
||||
_ENUM(GroupOfNames, "2.5.6.9"sv) \
|
||||
_ENUM(GroupOfUniqueNames, "2.5.6.17"sv) \
|
||||
_ENUM(Locality, "2.5.6.3"sv) \
|
||||
_ENUM(Organization, "2.5.6.4"sv) \
|
||||
_ENUM(OrganizationalPerson, "2.5.6.7"sv) \
|
||||
_ENUM(OrganizationalRole, "2.5.6.8"sv) \
|
||||
_ENUM(OrganizationalUnit, "2.5.6.5"sv) \
|
||||
_ENUM(Person, "2.5.6.6"sv) \
|
||||
_ENUM(ResidentialPerson, "2.5.6.10"sv) \
|
||||
_ENUM(UidObject, "1.3.6.1.1.3.1"sv)
|
||||
|
||||
// NOTE: Type = O
|
||||
// NOTE: This list is not exhaustive. If more members are needed, find them at the link below.
|
||||
// https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3
|
||||
enum class ObjectClass {
|
||||
__ENUM_OBJECT_CLASS
|
||||
};
|
||||
|
||||
#define __ENUM_ATTRIBUTE_TYPE \
|
||||
_ENUM(BusinessCategory, "2.5.4.15"sv) \
|
||||
_ENUM(C, "2.5.4.6"sv) \
|
||||
_ENUM(Cn, "2.5.4.3"sv) \
|
||||
_ENUM(Dc, "0.9.2342.19200300.100.1.25"sv) \
|
||||
_ENUM(Description, "2.5.4.13"sv) \
|
||||
_ENUM(DestinationIndicator, "2.5.4.27"sv) \
|
||||
_ENUM(DistinguishedName, "2.5.4.49"sv) \
|
||||
_ENUM(DnQualifier, "2.5.4.46"sv) \
|
||||
_ENUM(EnhancedSearchGuide, "2.5.4.47"sv) \
|
||||
_ENUM(Email, "1.2.840.113549.1.9.1"sv) \
|
||||
_ENUM(FacsimileTelephoneNumber, "2.5.4.23"sv) \
|
||||
_ENUM(GenerationQualifier, "2.5.4.44"sv) \
|
||||
_ENUM(GivenName, "2.5.4.42"sv) \
|
||||
_ENUM(HouseIdentifier, "2.5.4.51"sv) \
|
||||
_ENUM(Initials, "2.5.4.43"sv) \
|
||||
_ENUM(InternationalISDNNumber, "2.5.4.25"sv) \
|
||||
_ENUM(L, "2.5.4.7"sv) \
|
||||
_ENUM(Member, "2.5.4.31"sv) \
|
||||
_ENUM(Name, "2.5.4.41"sv) \
|
||||
_ENUM(O, "2.5.4.10"sv) \
|
||||
_ENUM(Ou, "2.5.4.11"sv) \
|
||||
_ENUM(Owner, "2.5.4.32"sv) \
|
||||
_ENUM(PhysicalDeliveryOfficeName, "2.5.4.19"sv) \
|
||||
_ENUM(PostalAddress, "2.5.4.16"sv) \
|
||||
_ENUM(PostalCode, "2.5.4.17"sv) \
|
||||
_ENUM(PostOfficeBox, "2.5.4.18"sv) \
|
||||
_ENUM(PreferredDeliveryMethod, "2.5.4.28"sv) \
|
||||
_ENUM(RegisteredAddress, "2.5.4.26"sv) \
|
||||
_ENUM(RoleOccupant, "2.5.4.33"sv) \
|
||||
_ENUM(SearchGuide, "2.5.4.14"sv) \
|
||||
_ENUM(SeeAlso, "2.5.4.34"sv) \
|
||||
_ENUM(SerialNumber, "2.5.4.5"sv) \
|
||||
_ENUM(Sn, "2.5.4.4"sv) \
|
||||
_ENUM(St, "2.5.4.8"sv) \
|
||||
_ENUM(Street, "2.5.4.9"sv) \
|
||||
_ENUM(Surname, "2.5.4.4"sv) \
|
||||
_ENUM(TelephoneNumber, "2.5.4.20"sv) \
|
||||
_ENUM(TeletexTerminalIdentifier, "2.5.4.22"sv) \
|
||||
_ENUM(TelexNumber, "2.5.4.21"sv) \
|
||||
_ENUM(Title, "2.5.4.12"sv) \
|
||||
_ENUM(Uid, "0.9.2342.19200300.100.1.1"sv) \
|
||||
_ENUM(UniqueMember, "2.5.4.50"sv) \
|
||||
_ENUM(UserPassword, "2.5.4.35"sv) \
|
||||
_ENUM(X121Address, "2.5.4.24"sv) \
|
||||
_ENUM(X500UniqueIdentifier, "2.5.4.45"sv)
|
||||
|
||||
// NOTE: Type = A
|
||||
// NOTE: This list is not exhaustive. If more members are needed, find them at the link below.
|
||||
// https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3
|
||||
enum class AttributeType {
|
||||
__ENUM_ATTRIBUTE_TYPE
|
||||
};
|
||||
|
||||
#undef _ENUM
|
||||
|
||||
constexpr static StringView enum_value(ObjectClass object_class)
|
||||
{
|
||||
#define _ENUM(key, value) \
|
||||
case ObjectClass::key: \
|
||||
return value;
|
||||
|
||||
switch (object_class) {
|
||||
__ENUM_OBJECT_CLASS
|
||||
}
|
||||
|
||||
return "Unknown"sv;
|
||||
#undef _ENUM
|
||||
#undef __ENUM_OBJECT_CLASS
|
||||
}
|
||||
|
||||
constexpr static StringView enum_value(AttributeType object_class)
|
||||
{
|
||||
#define _ENUM(key, value) \
|
||||
case AttributeType::key: \
|
||||
return value;
|
||||
|
||||
switch (object_class) {
|
||||
__ENUM_ATTRIBUTE_TYPE
|
||||
}
|
||||
|
||||
return "Unknown"sv;
|
||||
#undef _ENUM
|
||||
#undef __ENUM_ATTRIBUTE_TYPE
|
||||
}
|
||||
|
||||
struct AlgorithmIdentifier {
|
||||
AlgorithmIdentifier()
|
||||
{
|
||||
}
|
||||
|
||||
explicit AlgorithmIdentifier(Vector<int, 9> identifier)
|
||||
: identifier(identifier)
|
||||
{
|
||||
}
|
||||
|
||||
Vector<int, 9> identifier;
|
||||
SupportedGroup ec_parameters {};
|
||||
};
|
||||
|
||||
struct BasicConstraints {
|
||||
bool is_certificate_authority;
|
||||
Crypto::UnsignedBigInteger path_length_constraint;
|
||||
};
|
||||
|
||||
class RelativeDistinguishedName {
|
||||
public:
|
||||
ErrorOr<String> to_string() const;
|
||||
|
||||
ErrorOr<AK::HashSetResult> set(String key, String value)
|
||||
{
|
||||
return m_members.try_set(move(key), move(value));
|
||||
}
|
||||
|
||||
Optional<String> get(StringView key) const
|
||||
{
|
||||
return m_members.get(key);
|
||||
}
|
||||
|
||||
Optional<String> get(AttributeType key) const
|
||||
{
|
||||
return m_members.get(enum_value(key));
|
||||
}
|
||||
|
||||
Optional<String> get(ObjectClass key) const
|
||||
{
|
||||
return m_members.get(enum_value(key));
|
||||
}
|
||||
|
||||
String common_name() const
|
||||
{
|
||||
auto entry = get(AttributeType::Cn);
|
||||
if (entry.has_value()) {
|
||||
return entry.value();
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
String organizational_unit() const
|
||||
{
|
||||
return get(AttributeType::Ou).value_or({});
|
||||
}
|
||||
|
||||
private:
|
||||
HashMap<String, String> m_members;
|
||||
};
|
||||
|
||||
struct Validity {
|
||||
UnixDateTime not_before;
|
||||
UnixDateTime not_after;
|
||||
};
|
||||
|
||||
class SubjectPublicKey {
|
||||
public:
|
||||
Crypto::PK::RSAPublicKey<Crypto::UnsignedBigInteger> rsa;
|
||||
|
||||
AlgorithmIdentifier algorithm;
|
||||
ByteBuffer raw_key;
|
||||
};
|
||||
ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5208#section-5
|
||||
class PrivateKey {
|
||||
public:
|
||||
Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger> rsa;
|
||||
|
||||
AlgorithmIdentifier algorithm;
|
||||
ByteBuffer raw_key;
|
||||
|
||||
// FIXME: attributes [0] IMPLICIT Attributes OPTIONAL
|
||||
};
|
||||
ErrorOr<PrivateKey> parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
|
||||
|
||||
class Certificate {
|
||||
public:
|
||||
u16 version { 0 };
|
||||
AlgorithmIdentifier algorithm;
|
||||
SubjectPublicKey public_key;
|
||||
ByteBuffer exponent {};
|
||||
Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger> private_key {};
|
||||
RelativeDistinguishedName issuer, subject;
|
||||
Validity validity {};
|
||||
Vector<String> SAN;
|
||||
Vector<String> IAN;
|
||||
u8* ocsp { nullptr };
|
||||
Crypto::UnsignedBigInteger serial_number;
|
||||
ByteBuffer sign_key {};
|
||||
ByteBuffer fingerprint {};
|
||||
ByteBuffer der {};
|
||||
ByteBuffer data {};
|
||||
AlgorithmIdentifier signature_algorithm;
|
||||
ByteBuffer signature_value {};
|
||||
ByteBuffer original_asn1 {};
|
||||
ByteBuffer tbs_asn1 {};
|
||||
bool is_allowed_to_sign_certificate { false };
|
||||
bool is_certificate_authority { false };
|
||||
Optional<size_t> path_length_constraint {};
|
||||
bool is_self_issued { false };
|
||||
|
||||
static ErrorOr<Certificate> parse_certificate(ReadonlyBytes, bool client_cert = false);
|
||||
|
||||
bool is_self_signed();
|
||||
bool is_valid() const;
|
||||
|
||||
private:
|
||||
Optional<bool> m_is_self_signed;
|
||||
};
|
||||
|
||||
class DefaultRootCACertificates {
|
||||
public:
|
||||
DefaultRootCACertificates();
|
||||
|
||||
Vector<Certificate> const& certificates() const { return m_ca_certificates; }
|
||||
|
||||
static ErrorOr<Vector<Certificate>> parse_pem_root_certificate_authorities(ByteBuffer&);
|
||||
static ErrorOr<Vector<Certificate>> load_certificates(Span<ByteString> custom_cert_paths = {});
|
||||
|
||||
static DefaultRootCACertificates& the();
|
||||
|
||||
static void set_default_certificate_paths(Span<ByteString> paths);
|
||||
|
||||
private:
|
||||
Vector<Certificate> m_ca_certificates;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using TLS::Certificate;
|
||||
using TLS::DefaultRootCACertificates;
|
90
Libraries/LibTLS/CipherSuite.h
Normal file
90
Libraries/LibTLS/CipherSuite.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
981
Libraries/LibTLS/Extensions.h
Normal file
981
Libraries/LibTLS/Extensions.h
Normal file
|
@ -0,0 +1,981 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
559
Libraries/LibTLS/Handshake.cpp
Normal file
559
Libraries/LibTLS/Handshake.cpp
Normal file
|
@ -0,0 +1,559 @@
|
|||
/*
|
||||
* 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 <LibTLS/TLSv12.h>
|
||||
|
||||
namespace TLS {
|
||||
|
||||
ByteBuffer TLSv12::build_hello()
|
||||
{
|
||||
fill_with_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;
|
||||
if (buffer_length < 1)
|
||||
return (i8)Error::NeedMoreData;
|
||||
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;
|
||||
}
|
||||
}
|
110
Libraries/LibTLS/HandshakeCertificate.cpp
Normal file
110
Libraries/LibTLS/HandshakeCertificate.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
412
Libraries/LibTLS/HandshakeClient.cpp
Normal file
412
Libraries/LibTLS/HandshakeClient.cpp
Normal file
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* 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 <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;
|
||||
|
||||
fill_with_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.modulus(), 0, certificate.public_key.rsa.public_exponent());
|
||||
|
||||
Vector<u8, 32> out;
|
||||
out.resize(rsa.output_size());
|
||||
auto outbuf = out.span();
|
||||
rsa.encrypt(m_context.premaster_key, outbuf);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
530
Libraries/LibTLS/HandshakeServer.cpp
Normal file
530
Libraries/LibTLS/HandshakeServer.cpp
Normal file
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* 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 <LibCrypto/PK/Code/EMSA_PKCS1_V1_5.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 certificate_public_key = m_context.certificates.first().public_key;
|
||||
Crypto::PK::RSAPrivateKey dummy_private_key;
|
||||
auto rsa = Crypto::PK::RSA(certificate_public_key.rsa, dummy_private_key);
|
||||
|
||||
auto signature_verify_buffer_result = ByteBuffer::create_uninitialized(signature_length);
|
||||
if (signature_verify_buffer_result.is_error()) {
|
||||
dbgln("verify_rsa_server_key_exchange failed: Not enough memory");
|
||||
return (i8)Error::OutOfMemory;
|
||||
}
|
||||
auto signature_verify_buffer = signature_verify_buffer_result.release_value();
|
||||
auto signature_verify_bytes = signature_verify_buffer.bytes();
|
||||
rsa.verify(signature, signature_verify_bytes);
|
||||
|
||||
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 pkcs1 = Crypto::PK::EMSA_PKCS1_V1_5<Crypto::Hash::Manager>(hash_kind);
|
||||
auto verification = pkcs1.verify(message, signature_verify_bytes, signature_length * 8);
|
||||
|
||||
if (verification == Crypto::VerificationConsistency::Inconsistent) {
|
||||
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 = 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;
|
||||
}
|
||||
ReadonlyBytes server_point = m_context.certificates.first().public_key.raw_key;
|
||||
|
||||
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;
|
||||
switch (public_key.algorithm.ec_parameters) {
|
||||
case SupportedGroup::SECP256R1: {
|
||||
Crypto::Hash::Manager manager(hash_kind);
|
||||
manager.update(message);
|
||||
auto digest = manager.digest();
|
||||
|
||||
Crypto::Curves::SECP256r1 curve;
|
||||
res = curve.verify(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(digest.bytes(), server_point, signature);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dbgln("verify_ecdsa_server_key_exchange failed: Server certificate public key algorithm is not supported: {}", to_underlying(public_key.algorithm.ec_parameters));
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
574
Libraries/LibTLS/Record.cpp
Normal file
574
Libraries/LibTLS/Record.cpp
Normal file
|
@ -0,0 +1,574 @@
|
|||
/*
|
||||
* 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 <LibCore/EventLoop.h>
|
||||
#include <LibCore/Timer.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)
|
||||
// -- Our GCM impl takes 16 bytes
|
||||
// zero (4)
|
||||
u8 iv[16];
|
||||
Bytes iv_bytes { iv, 16 };
|
||||
Bytes { m_context.crypto.local_aead_iv, 4 }.copy_to(iv_bytes);
|
||||
fill_with_random(iv_bytes.slice(4, 8));
|
||||
memset(iv_bytes.offset(12), 0, 4);
|
||||
|
||||
// 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();
|
||||
fill_with_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)
|
||||
// -- Our GCM impl takes 16 bytes
|
||||
// zero (4)
|
||||
u8 iv[16];
|
||||
Bytes iv_bytes { iv, 16 };
|
||||
Bytes { m_context.crypto.remote_aead_iv, 4 }.copy_to(iv_bytes);
|
||||
nonce.copy_to(iv_bytes.slice(4));
|
||||
memset(iv_bytes.offset(12), 0, 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;
|
||||
}
|
||||
|
||||
}
|
323
Libraries/LibTLS/Socket.cpp
Normal file
323
Libraries/LibTLS/Socket.cpp
Normal file
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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()
|
||||
{
|
||||
auto out_bytes = m_context.tls_buffer.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()) {
|
||||
m_context.tls_buffer.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_context.send_retries++ == 10) {
|
||||
// drop the records, we can't send
|
||||
dbgln_if(TLS_DEBUG, "Dropping {} bytes worth of TLS records as max retries has been reached", m_context.tls_buffer.size());
|
||||
m_context.tls_buffer.clear();
|
||||
m_context.send_retries = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TLSv12::close()
|
||||
{
|
||||
if (underlying_stream().is_open())
|
||||
alert(AlertLevel::FATAL, AlertDescription::CLOSE_NOTIFY);
|
||||
// bye bye.
|
||||
m_context.connection_status = ConnectionStatus::Disconnected;
|
||||
}
|
||||
|
||||
}
|
90
Libraries/LibTLS/TLSPacketBuilder.h
Normal file
90
Libraries/LibTLS/TLSPacketBuilder.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
}
|
625
Libraries/LibTLS/TLSv12.cpp
Normal file
625
Libraries/LibTLS/TLSv12.cpp
Normal file
|
@ -0,0 +1,625 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* 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 <LibCore/File.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibCrypto/ASN1/ASN1.h>
|
||||
#include <LibCrypto/ASN1/PEM.h>
|
||||
#include <LibCrypto/Curves/Ed25519.h>
|
||||
#include <LibCrypto/Curves/SECPxxxr1.h>
|
||||
#include <LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibTLS/Certificate.h>
|
||||
#include <LibTLS/TLSv12.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef SOCK_NONBLOCK
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
namespace TLS {
|
||||
|
||||
void TLSv12::consume(ReadonlyBytes record)
|
||||
{
|
||||
if (m_context.critical_error) {
|
||||
dbgln("There has been a critical error ({}), refusing to continue", (i8)m_context.critical_error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (record.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dbgln_if(TLS_DEBUG, "Consuming {} bytes", record.size());
|
||||
|
||||
if (m_context.message_buffer.try_append(record).is_error()) {
|
||||
dbgln("Not enough space in message buffer, dropping the record");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index { 0 };
|
||||
size_t buffer_length = m_context.message_buffer.size();
|
||||
|
||||
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;
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
bool Certificate::is_valid() const
|
||||
{
|
||||
auto now = UnixDateTime::now();
|
||||
|
||||
if (now < validity.not_before) {
|
||||
dbgln("certificate expired (not yet valid, signed for {})", Core::DateTime::from_timestamp(validity.not_before.seconds_since_epoch()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (validity.not_after < now) {
|
||||
dbgln("certificate expired (expiry date {})", Core::DateTime::from_timestamp(validity.not_after.seconds_since_epoch()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc5280.html#page-12
|
||||
bool Certificate::is_self_signed()
|
||||
{
|
||||
if (m_is_self_signed.has_value())
|
||||
return *m_is_self_signed;
|
||||
|
||||
// Self-signed certificates are self-issued certificates where the digital
|
||||
// signature may be verified by the public key bound into the certificate.
|
||||
if (!this->is_self_issued)
|
||||
m_is_self_signed.emplace(false);
|
||||
|
||||
// FIXME: Actually check if we sign ourself
|
||||
|
||||
m_is_self_signed.emplace(true);
|
||||
return *m_is_self_signed;
|
||||
}
|
||||
|
||||
void TLSv12::try_disambiguate_error() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 == rsa_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::None;
|
||||
} else if (identifier == rsa_md5_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::MD5;
|
||||
} else if (identifier == rsa_sha1_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::SHA1;
|
||||
} else if (identifier == rsa_sha256_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::SHA256;
|
||||
} else if (identifier == rsa_sha384_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::SHA384;
|
||||
} else if (identifier == rsa_sha512_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::SHA512;
|
||||
} else if (identifier == ecdsa_with_sha256_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::SHA256;
|
||||
is_rsa = false;
|
||||
} else if (identifier == ecdsa_with_sha384_encryption_oid) {
|
||||
kind = Crypto::Hash::HashKind::SHA384;
|
||||
is_rsa = false;
|
||||
} else if (identifier == 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) {
|
||||
Crypto::PK::RSAPrivateKey dummy_private_key;
|
||||
Crypto::PK::RSAPublicKey public_key_copy { issuer.public_key.rsa };
|
||||
auto rsa = Crypto::PK::RSA(public_key_copy, dummy_private_key);
|
||||
auto verification_buffer_result = ByteBuffer::create_uninitialized(subject.signature_value.size());
|
||||
if (verification_buffer_result.is_error()) {
|
||||
dbgln("verify_certificate_pair: Unable to allocate buffer for verification");
|
||||
return false;
|
||||
}
|
||||
auto verification_buffer = verification_buffer_result.release_value();
|
||||
auto verification_buffer_bytes = verification_buffer.bytes();
|
||||
rsa.verify(subject.signature_value, verification_buffer_bytes);
|
||||
|
||||
ReadonlyBytes message = subject.tbs_asn1.bytes();
|
||||
auto pkcs1 = Crypto::PK::EMSA_PKCS1_V1_5<Crypto::Hash::Manager>(kind);
|
||||
auto verification = pkcs1.verify(message, verification_buffer_bytes, subject.signature_value.size() * 8);
|
||||
return verification == Crypto::VerificationConsistency::Consistent;
|
||||
}
|
||||
|
||||
// ECDSA hash verification: hash, then check signature against the specific curve
|
||||
switch (issuer.public_key.algorithm.ec_parameters) {
|
||||
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(hash.bytes(), issuer.public_key.raw_key, subject.signature_value);
|
||||
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(hash.bytes(), issuer.public_key.raw_key, subject.signature_value);
|
||||
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(issuer.public_key.algorithm.ec_parameters));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename HMACType>
|
||||
static void hmac_pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b)
|
||||
{
|
||||
if (!secret.size()) {
|
||||
dbgln("null secret");
|
||||
return;
|
||||
}
|
||||
|
||||
auto append_label_seed = [&](auto& hmac) {
|
||||
hmac.update(label, label_length);
|
||||
hmac.update(seed);
|
||||
if (seed_b.size() > 0)
|
||||
hmac.update(seed_b);
|
||||
};
|
||||
|
||||
HMACType hmac(secret);
|
||||
append_label_seed(hmac);
|
||||
|
||||
constexpr auto digest_size = hmac.digest_size();
|
||||
u8 digest[digest_size];
|
||||
auto digest_0 = Bytes { digest, digest_size };
|
||||
|
||||
digest_0.overwrite(0, hmac.digest().immutable_data(), digest_size);
|
||||
|
||||
size_t index = 0;
|
||||
while (index < output.size()) {
|
||||
hmac.update(digest_0);
|
||||
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).immutable_data(), digest_size);
|
||||
}
|
||||
}
|
||||
|
||||
void TLSv12::pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
TLSv12::TLSv12(StreamVariantType stream, Options options)
|
||||
: m_stream(move(stream))
|
||||
{
|
||||
m_context.options = move(options);
|
||||
m_context.is_server = false;
|
||||
m_context.tls_buffer = {};
|
||||
|
||||
set_root_certificates(m_context.options.root_certificates.has_value()
|
||||
? *m_context.options.root_certificates
|
||||
: DefaultRootCACertificates::the().certificates());
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
auto decoded_certificate = Crypto::decode_pem(certificate_pem_buffer);
|
||||
if (decoded_certificate.is_empty()) {
|
||||
dbgln("Certificate not PEM");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto maybe_certificate = Certificate::parse_certificate(decoded_certificate);
|
||||
if (!maybe_certificate.is_error()) {
|
||||
dbgln("Invalid certificate");
|
||||
return {};
|
||||
}
|
||||
|
||||
Crypto::PK::RSA rsa(rsa_key);
|
||||
auto certificate = maybe_certificate.release_value();
|
||||
certificate.private_key = rsa.private_key();
|
||||
|
||||
return { move(certificate) };
|
||||
}
|
||||
|
||||
static Vector<ByteString> s_default_ca_certificate_paths;
|
||||
|
||||
void DefaultRootCACertificates::set_default_certificate_paths(Span<ByteString> paths)
|
||||
{
|
||||
s_default_ca_certificate_paths.clear();
|
||||
s_default_ca_certificate_paths.ensure_capacity(paths.size());
|
||||
for (auto& path : paths)
|
||||
s_default_ca_certificate_paths.unchecked_append(path);
|
||||
}
|
||||
|
||||
DefaultRootCACertificates::DefaultRootCACertificates()
|
||||
{
|
||||
auto load_result = load_certificates(s_default_ca_certificate_paths);
|
||||
if (load_result.is_error()) {
|
||||
dbgln("Failed to load CA Certificates: {}", load_result.error());
|
||||
return;
|
||||
}
|
||||
|
||||
m_ca_certificates = load_result.release_value();
|
||||
}
|
||||
|
||||
DefaultRootCACertificates& DefaultRootCACertificates::the()
|
||||
{
|
||||
static thread_local DefaultRootCACertificates s_the;
|
||||
return s_the;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<Certificate>> DefaultRootCACertificates::load_certificates(Span<ByteString> custom_cert_paths)
|
||||
{
|
||||
auto cacert_file_or_error = Core::File::open("/etc/cacert.pem"sv, Core::File::OpenMode::Read);
|
||||
ByteBuffer data;
|
||||
if (!cacert_file_or_error.is_error())
|
||||
data = TRY(cacert_file_or_error.value()->read_until_eof());
|
||||
|
||||
auto user_cert_path = TRY(String::formatted("{}/.config/certs.pem", Core::StandardPaths::home_directory()));
|
||||
if (FileSystem::exists(user_cert_path)) {
|
||||
auto user_cert_file = TRY(Core::File::open(user_cert_path, Core::File::OpenMode::Read));
|
||||
TRY(data.try_append(TRY(user_cert_file->read_until_eof())));
|
||||
}
|
||||
|
||||
for (auto& custom_cert_path : custom_cert_paths) {
|
||||
if (FileSystem::exists(custom_cert_path)) {
|
||||
auto custom_cert_file = TRY(Core::File::open(custom_cert_path, Core::File::OpenMode::Read));
|
||||
TRY(data.try_append(TRY(custom_cert_file->read_until_eof())));
|
||||
}
|
||||
}
|
||||
|
||||
return TRY(parse_pem_root_certificate_authorities(data));
|
||||
}
|
||||
|
||||
ErrorOr<Vector<Certificate>> DefaultRootCACertificates::parse_pem_root_certificate_authorities(ByteBuffer& data)
|
||||
{
|
||||
Vector<Certificate> certificates;
|
||||
|
||||
auto certs = TRY(Crypto::decode_pems(data));
|
||||
|
||||
for (auto& cert : certs) {
|
||||
auto certificate_result = Certificate::parse_certificate(cert.bytes());
|
||||
if (certificate_result.is_error()) {
|
||||
// FIXME: It would be nice to have more informations about the certificate we failed to parse.
|
||||
// Like: Issuer, Algorithm, CN, etc
|
||||
dbgln("Failed to load certificate: {}", certificate_result.error());
|
||||
continue;
|
||||
}
|
||||
auto certificate = certificate_result.release_value();
|
||||
if (certificate.is_certificate_authority && certificate.is_self_signed()) {
|
||||
TRY(certificates.try_append(move(certificate)));
|
||||
} else {
|
||||
dbgln("Skipped '{}' because it is not a valid root CA", TRY(certificate.subject.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
dbgln_if(TLS_DEBUG, "Loaded {} of {} ({:.2}%) provided CA Certificates", certificates.size(), certs.size(), (certificates.size() * 100.0) / certs.size());
|
||||
|
||||
return certificates;
|
||||
}
|
||||
}
|
551
Libraries/LibTLS/TLSv12.h
Normal file
551
Libraries/LibTLS/TLSv12.h
Normal file
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Certificate.h"
|
||||
#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/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibCrypto/Cipher/AES.h>
|
||||
#include <LibCrypto/Curves/EllipticCurve.h>
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
#include <LibCrypto/PK/RSA.h>
|
||||
#include <LibTLS/CipherSuite.h>
|
||||
#include <LibTLS/TLSPacketBuilder.h>
|
||||
|
||||
namespace TLS {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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() \
|
||||
{ \
|
||||
return typ { __VA_ARGS__ }; \
|
||||
} \
|
||||
typ name = default_##name(); \
|
||||
Options& set_##name(typ new_value)& \
|
||||
{ \
|
||||
name = move(new_value); \
|
||||
return *this; \
|
||||
} \
|
||||
Options&& set_##name(typ new_value)&& \
|
||||
{ \
|
||||
name = move(new_value); \
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
/// 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.
|
||||
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_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); }
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
void setup_connection();
|
||||
|
||||
void consume(ReadonlyBytes record);
|
||||
|
||||
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 update_packet(ByteBuffer& packet);
|
||||
void update_hash(ReadonlyBytes in, size_t header_size);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue