Implement RSA-AES-256 security type

pull/100/head
Andri Yngvason 2023-09-05 20:31:09 +00:00
parent 396f4ed6c5
commit 4705c0cfcc
5 changed files with 74 additions and 46 deletions

View File

@ -26,6 +26,10 @@
#include "neatvnc.h" #include "neatvnc.h"
#include "config.h" #include "config.h"
#ifdef HAVE_CRYPTO
#include "crypto.h"
#endif
#ifdef ENABLE_TLS #ifdef ENABLE_TLS
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#endif #endif
@ -108,8 +112,11 @@ struct nvnc_client {
struct crypto_key* apple_dh_secret; struct crypto_key* apple_dh_secret;
struct { struct {
struct crypto_rsa_pub_key *pub; enum crypto_hash_type hash_type;
uint8_t challenge[16]; enum crypto_cipher_type cipher_type;
size_t challenge_len;
uint8_t challenge[32];
struct crypto_rsa_pub_key* pub;
} rsa; } rsa;
#endif #endif
}; };

View File

@ -30,10 +30,10 @@ enum rfb_security_type {
RFB_SECURITY_TYPE_NONE = 1, RFB_SECURITY_TYPE_NONE = 1,
RFB_SECURITY_TYPE_VNC_AUTH = 2, RFB_SECURITY_TYPE_VNC_AUTH = 2,
RFB_SECURITY_TYPE_RSA_AES = 5, RFB_SECURITY_TYPE_RSA_AES = 5,
RFB_SECURITY_TYPE_RSA_AES_UNENCRYPTED = 6,
RFB_SECURITY_TYPE_TIGHT = 16, RFB_SECURITY_TYPE_TIGHT = 16,
RFB_SECURITY_TYPE_VENCRYPT = 19, RFB_SECURITY_TYPE_VENCRYPT = 19,
RFB_SECURITY_TYPE_APPLE_DH = 30, RFB_SECURITY_TYPE_APPLE_DH = 30,
RFB_SECURITY_TYPE_RSA_AES256 = 129,
}; };
enum rfb_security_handshake_result { enum rfb_security_handshake_result {

View File

@ -21,6 +21,10 @@
#include "rcbuf.h" #include "rcbuf.h"
#include "vec.h" #include "vec.h"
#ifdef HAVE_CRYPTO
#include "crypto.h"
#endif
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -114,5 +118,8 @@ void stream_exec_and_send(struct stream* self, stream_exec_fn, void* userdata);
int stream_upgrade_to_tls(struct stream* self, void* context); int stream_upgrade_to_tls(struct stream* self, void* context);
#endif #endif
int stream_upgrade_to_rsa_eas(struct stream* base, const uint8_t* enc_key, #ifdef HAVE_CRYPTO
const uint8_t* dec_key); int stream_upgrade_to_rsa_eas(struct stream* base,
enum crypto_cipher_type cipher_type,
const uint8_t* enc_key, const uint8_t* dec_key);
#endif

View File

@ -235,6 +235,7 @@ static int on_version_message(struct nvnc_client* client)
#endif #endif
#ifdef HAVE_CRYPTO #ifdef HAVE_CRYPTO
security->types[security->n++] = RFB_SECURITY_TYPE_RSA_AES256;
security->types[security->n++] = RFB_SECURITY_TYPE_RSA_AES; security->types[security->n++] = RFB_SECURITY_TYPE_RSA_AES;
security->types[security->n++] = RFB_SECURITY_TYPE_APPLE_DH; security->types[security->n++] = RFB_SECURITY_TYPE_APPLE_DH;
#endif #endif
@ -525,7 +526,7 @@ static int rsa_aes_send_public_key(struct nvnc_client* client)
static int rsa_aes_send_challenge(struct nvnc_client* client, static int rsa_aes_send_challenge(struct nvnc_client* client,
struct crypto_rsa_pub_key* pub) struct crypto_rsa_pub_key* pub)
{ {
crypto_random(client->rsa.challenge, sizeof(client->rsa.challenge)); crypto_random(client->rsa.challenge, client->rsa.challenge_len);
uint8_t buffer[1024]; uint8_t buffer[1024];
struct rfb_rsa_aes_challenge_msg *msg = struct rfb_rsa_aes_challenge_msg *msg =
@ -533,7 +534,7 @@ static int rsa_aes_send_challenge(struct nvnc_client* client,
ssize_t len = crypto_rsa_encrypt(pub, msg->challenge, ssize_t len = crypto_rsa_encrypt(pub, msg->challenge,
RSA_AES_SERVER_KEY_LENGTH, client->rsa.challenge, RSA_AES_SERVER_KEY_LENGTH, client->rsa.challenge,
sizeof(client->rsa.challenge)); client->rsa.challenge_len);
msg->length = htons(len); msg->length = htons(len);
nvnc_trace("Challenge length is %zd", len); nvnc_trace("Challenge length is %zd", len);
@ -575,6 +576,17 @@ static int on_rsa_aes_public_key(struct nvnc_client* client)
return sizeof(*msg) + byte_length * 2; return sizeof(*msg) + byte_length * 2;
} }
static size_t client_rsa_aes_hash_len(const struct nvnc_client* client)
{
switch (client->rsa.hash_type) {
case CRYPTO_HASH_SHA1: return 20;
case CRYPTO_HASH_SHA256: return 32;
default:;
}
abort();
return 0;
}
static int on_rsa_aes_challenge(struct nvnc_client* client) static int on_rsa_aes_challenge(struct nvnc_client* client)
{ {
struct rfb_rsa_aes_challenge_msg* msg = struct rfb_rsa_aes_challenge_msg* msg =
@ -595,9 +607,9 @@ static int on_rsa_aes_challenge(struct nvnc_client* client)
nvnc_trace("Encrypted challenge has length: %d", length); nvnc_trace("Encrypted challenge has length: %d", length);
uint8_t client_random[16] = {}; uint8_t client_random[32] = {};
ssize_t len = crypto_rsa_decrypt(server->rsa_priv, client_random, ssize_t len = crypto_rsa_decrypt(server->rsa_priv, client_random,
sizeof(client_random), msg->challenge, length); client->rsa.challenge_len, msg->challenge, length);
if (len < 0) { if (len < 0) {
nvnc_log(NVNC_LOG_ERROR, "Failed to decrypt client's challenge"); nvnc_log(NVNC_LOG_ERROR, "Failed to decrypt client's challenge");
client->state = VNC_CLIENT_STATE_ERROR; client->state = VNC_CLIENT_STATE_ERROR;
@ -605,34 +617,26 @@ static int on_rsa_aes_challenge(struct nvnc_client* client)
goto done; goto done;
} }
nvnc_trace("Decrypted challenge has length: %zd", len);
crypto_dump_base16("Got challenge", client_random, 16);
// ClientSessionKey = the first 16 bytes of SHA1(ServerRandom || ClientRandom) // ClientSessionKey = the first 16 bytes of SHA1(ServerRandom || ClientRandom)
uint8_t client_session_key[16]; uint8_t client_session_key[32];
crypto_hash_many(client_session_key, sizeof(client_session_key), crypto_hash_many(client_session_key, client_rsa_aes_hash_len(client),
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) { client->rsa.hash_type, (const struct crypto_data_entry[]) {
{ client->rsa.challenge, sizeof(client->rsa.challenge) }, { client->rsa.challenge, client->rsa.challenge_len },
{ client_random, sizeof(client_random) }, { client_random, client->rsa.challenge_len },
{} {}
}); });
// ServerSessionKey = the first 16 bytes of SHA1(ClientRandom || ServerRandom) // ServerSessionKey = the first 16 bytes of SHA1(ClientRandom || ServerRandom)
uint8_t server_session_key[16]; uint8_t server_session_key[32];
crypto_hash_many(server_session_key, sizeof(server_session_key), crypto_hash_many(server_session_key, client_rsa_aes_hash_len(client),
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) { client->rsa.hash_type, (const struct crypto_data_entry[]) {
{ client_random, sizeof(client_random) }, { client_random, client->rsa.challenge_len },
{ client->rsa.challenge, sizeof(client->rsa.challenge) }, { client->rsa.challenge, client->rsa.challenge_len },
{} {}
}); });
crypto_dump_base64("Client session key", client_session_key, stream_upgrade_to_rsa_eas(client->net_stream, client->rsa.cipher_type,
sizeof(client_session_key)); server_session_key, client_session_key);
crypto_dump_base64("Server session key", server_session_key,
sizeof(server_session_key));
stream_upgrade_to_rsa_eas(client->net_stream, server_session_key,
client_session_key);
uint8_t server_modulus[RSA_AES_SERVER_KEY_LENGTH]; uint8_t server_modulus[RSA_AES_SERVER_KEY_LENGTH];
uint8_t server_exponent[RSA_AES_SERVER_KEY_LENGTH]; uint8_t server_exponent[RSA_AES_SERVER_KEY_LENGTH];
@ -653,9 +657,9 @@ static int on_rsa_aes_challenge(struct nvnc_client* client)
uint32_t server_key_len_be = htonl(RSA_AES_SERVER_KEY_LENGTH * 8); uint32_t server_key_len_be = htonl(RSA_AES_SERVER_KEY_LENGTH * 8);
uint32_t client_key_len_be = htonl(client_key_len * 8); uint32_t client_key_len_be = htonl(client_key_len * 8);
uint8_t server_hash[20] = {}; uint8_t server_hash[32] = {};
crypto_hash_many(server_hash, sizeof(server_hash), crypto_hash_many(server_hash, client_rsa_aes_hash_len(client),
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) { client->rsa.hash_type, (const struct crypto_data_entry[]) {
{ (uint8_t*)&server_key_len_be, 4 }, { (uint8_t*)&server_key_len_be, 4 },
{ server_modulus, RSA_AES_SERVER_KEY_LENGTH }, { server_modulus, RSA_AES_SERVER_KEY_LENGTH },
{ server_exponent, RSA_AES_SERVER_KEY_LENGTH }, { server_exponent, RSA_AES_SERVER_KEY_LENGTH },
@ -667,7 +671,8 @@ static int on_rsa_aes_challenge(struct nvnc_client* client)
free(client_modulus); free(client_modulus);
stream_write(client->net_stream, server_hash, 20, NULL, NULL); stream_write(client->net_stream, server_hash,
client_rsa_aes_hash_len(client), NULL, NULL);
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CLIENT_HASH; client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CLIENT_HASH;
done: done:
@ -678,7 +683,7 @@ static int on_rsa_aes_client_hash(struct nvnc_client* client)
{ {
const char* msg = (void*)(client->msg_buffer + client->buffer_index); const char* msg = (void*)(client->msg_buffer + client->buffer_index);
if (client->buffer_len - client->buffer_index < 20) if (client->buffer_len - client->buffer_index < client_rsa_aes_hash_len(client))
return 0; return 0;
struct nvnc* server = client->server; struct nvnc* server = client->server;
@ -702,9 +707,9 @@ static int on_rsa_aes_client_hash(struct nvnc_client* client)
uint32_t server_key_len_be = htonl(RSA_AES_SERVER_KEY_LENGTH * 8); uint32_t server_key_len_be = htonl(RSA_AES_SERVER_KEY_LENGTH * 8);
uint32_t client_key_len_be = htonl(client_key_len * 8); uint32_t client_key_len_be = htonl(client_key_len * 8);
uint8_t client_hash[20] = {}; uint8_t client_hash[32] = {};
crypto_hash_many(client_hash, sizeof(client_hash), crypto_hash_many(client_hash, client_rsa_aes_hash_len(client),
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) { client->rsa.hash_type, (const struct crypto_data_entry[]) {
{ (uint8_t*)&client_key_len_be, 4 }, { (uint8_t*)&client_key_len_be, 4 },
{ client_modulus, client_key_len }, { client_modulus, client_key_len },
{ client_exponent, client_key_len }, { client_exponent, client_key_len },
@ -716,11 +721,10 @@ static int on_rsa_aes_client_hash(struct nvnc_client* client)
free(client_modulus); free(client_modulus);
crypto_dump_base16("Client hash", client_hash, 20); if (memcmp(msg, client_hash, client_rsa_aes_hash_len(client)) != 0) {
if (memcmp(msg, client_hash, 20) != 0) {
nvnc_log(NVNC_LOG_INFO, "Client hash mismatch"); nvnc_log(NVNC_LOG_INFO, "Client hash mismatch");
// TODO: Close the connection or something nvnc_client_close(client);
return 0;
} }
// TODO: Read this from config // TODO: Read this from config
@ -728,7 +732,7 @@ static int on_rsa_aes_client_hash(struct nvnc_client* client)
stream_write(client->net_stream, &subtype, 1, NULL, NULL); stream_write(client->net_stream, &subtype, 1, NULL, NULL);
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CREDENTIALS; client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CREDENTIALS;
return 20; return client_rsa_aes_hash_len(client);
} }
static int on_rsa_aes_credentials(struct nvnc_client* client) static int on_rsa_aes_credentials(struct nvnc_client* client)
@ -795,6 +799,16 @@ static int on_security_message(struct nvnc_client* client)
client->state = VNC_CLIENT_STATE_WAITING_FOR_APPLE_DH_RESPONSE; client->state = VNC_CLIENT_STATE_WAITING_FOR_APPLE_DH_RESPONSE;
break; break;
case RFB_SECURITY_TYPE_RSA_AES: case RFB_SECURITY_TYPE_RSA_AES:
client->rsa.hash_type = CRYPTO_HASH_SHA1;
client->rsa.cipher_type = CRYPTO_CIPHER_AES_EAX;
client->rsa.challenge_len = 16;
rsa_aes_send_public_key(client);
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY;
break;
case RFB_SECURITY_TYPE_RSA_AES256:
client->rsa.hash_type = CRYPTO_HASH_SHA256;
client->rsa.cipher_type = CRYPTO_CIPHER_AES256_EAX;
client->rsa.challenge_len = 32;
rsa_aes_send_public_key(client); rsa_aes_send_public_key(client);
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY; client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY;
break; break;

View File

@ -186,8 +186,9 @@ static struct stream_impl impl = {
.send = stream_rsa_aes_send, .send = stream_rsa_aes_send,
}; };
int stream_upgrade_to_rsa_eas(struct stream* base, const uint8_t* enc_key, int stream_upgrade_to_rsa_eas(struct stream* base,
const uint8_t* dec_key) enum crypto_cipher_type cipher_type,
const uint8_t* enc_key, const uint8_t* dec_key)
{ {
struct stream_rsa_aes* self = (struct stream_rsa_aes*)base; struct stream_rsa_aes* self = (struct stream_rsa_aes*)base;
@ -196,8 +197,7 @@ int stream_upgrade_to_rsa_eas(struct stream* base, const uint8_t* enc_key,
if (!self->read_buffer) if (!self->read_buffer)
return -1; return -1;
self->cipher = crypto_cipher_new(enc_key, dec_key, self->cipher = crypto_cipher_new(enc_key, dec_key, cipher_type);
CRYPTO_CIPHER_AES_EAX);
if (!self->cipher) { if (!self->cipher) {
free(self->read_buffer); free(self->read_buffer);
return -1; return -1;