LibTLS: Add ECDSA support with the secp256r1 curve

This commit is contained in:
Michiel Visser 2022-03-20 21:20:04 +01:00 committed by Andrew Kaster
parent caf533bddf
commit c548dca174
Notes: sideshowbarker 2024-07-17 02:35:27 +09:00
3 changed files with 118 additions and 6 deletions

View File

@ -393,11 +393,11 @@ ByteBuffer TLSv12::build_client_key_exchange()
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::ECDHE_ECDSA:
case KeyExchangeAlgorithm::ECDH_anon:
dbgln("Client key exchange for ECDHE algorithms is not implemented");
TODO();

View File

@ -11,6 +11,7 @@
#include <LibCore/Timer.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/Curves/Ed25519.h>
#include <LibCrypto/Curves/EllipticCurve.h>
#include <LibCrypto/Curves/SECP256r1.h>
#include <LibCrypto/Curves/X25519.h>
@ -237,9 +238,10 @@ ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes buffer)
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::ECDHE_ECDSA:
case KeyExchangeAlgorithm::ECDH_anon:
dbgln("Server key exchange for ECDHE algorithms is not implemented");
TODO();
@ -292,7 +294,7 @@ ssize_t TLSv12::handle_dhe_rsa_server_key_exchange(ReadonlyBytes buffer)
return verify_rsa_server_key_exchange(server_key_info, signature);
}
ssize_t TLSv12::handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes buffer)
ssize_t TLSv12::handle_ecdhe_server_key_exchange(ReadonlyBytes buffer, u8& server_public_key_length)
{
if (buffer.size() < 7)
return (i8)Error::NeedMoreData;
@ -319,7 +321,7 @@ ssize_t TLSv12::handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes buffer)
return (i8)Error::NotUnderstood;
}
auto server_public_key_length = buffer[6];
server_public_key_length = buffer[6];
if (server_public_key_length != m_context.server_key_exchange_curve->key_size())
return (i8)Error::NotUnderstood;
@ -338,6 +340,16 @@ ssize_t TLSv12::handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes buffer)
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);
@ -412,4 +424,98 @@ ssize_t TLSv12::verify_rsa_server_key_exchange(ReadonlyBytes server_key_info_buf
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::X25519: {
Crypto::Curves::Ed25519 curve;
res = curve.verify(public_key.raw_key, signature, message);
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;
}
}

View File

@ -98,7 +98,8 @@ enum ClientVerificationStaus {
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)
C(true, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \
C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true)
constexpr KeyExchangeAlgorithm get_key_exchange_algorithm(CipherSuite suite)
{
@ -161,7 +162,9 @@ struct Options {
{ HashAlgorithm::SHA512, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA384, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA256, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA1, SignatureAlgorithm::RSA });
{ HashAlgorithm::SHA1, SignatureAlgorithm::RSA },
{ HashAlgorithm::SHA256, SignatureAlgorithm::ECDSA },
{ HashAlgorithm::INTRINSIC, SignatureAlgorithm::ECDSA });
OPTION_WITH_DEFAULTS(Vector<SupportedGroup>, elliptic_curves,
SupportedGroup::X25519,
SupportedGroup::SECP256R1,
@ -384,7 +387,9 @@ private:
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);
@ -393,6 +398,7 @@ private:
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
{