Implement RSA-AES-256 security type
parent
396f4ed6c5
commit
4705c0cfcc
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
88
src/server.c
88
src/server.c
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue