Implement RSA-AES-256 security type
parent
396f4ed6c5
commit
4705c0cfcc
|
@ -26,6 +26,10 @@
|
|||
#include "neatvnc.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_CRYPTO
|
||||
#include "crypto.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_TLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#endif
|
||||
|
@ -108,8 +112,11 @@ struct nvnc_client {
|
|||
struct crypto_key* apple_dh_secret;
|
||||
|
||||
struct {
|
||||
enum crypto_hash_type hash_type;
|
||||
enum crypto_cipher_type cipher_type;
|
||||
size_t challenge_len;
|
||||
uint8_t challenge[32];
|
||||
struct crypto_rsa_pub_key* pub;
|
||||
uint8_t challenge[16];
|
||||
} rsa;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -30,10 +30,10 @@ enum rfb_security_type {
|
|||
RFB_SECURITY_TYPE_NONE = 1,
|
||||
RFB_SECURITY_TYPE_VNC_AUTH = 2,
|
||||
RFB_SECURITY_TYPE_RSA_AES = 5,
|
||||
RFB_SECURITY_TYPE_RSA_AES_UNENCRYPTED = 6,
|
||||
RFB_SECURITY_TYPE_TIGHT = 16,
|
||||
RFB_SECURITY_TYPE_VENCRYPT = 19,
|
||||
RFB_SECURITY_TYPE_APPLE_DH = 30,
|
||||
RFB_SECURITY_TYPE_RSA_AES256 = 129,
|
||||
};
|
||||
|
||||
enum rfb_security_handshake_result {
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#include "rcbuf.h"
|
||||
#include "vec.h"
|
||||
|
||||
#ifdef HAVE_CRYPTO
|
||||
#include "crypto.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.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);
|
||||
#endif
|
||||
|
||||
int stream_upgrade_to_rsa_eas(struct stream* base, const uint8_t* enc_key,
|
||||
const uint8_t* dec_key);
|
||||
#ifdef HAVE_CRYPTO
|
||||
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
|
||||
|
||||
#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_APPLE_DH;
|
||||
#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,
|
||||
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];
|
||||
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,
|
||||
RSA_AES_SERVER_KEY_LENGTH, client->rsa.challenge,
|
||||
sizeof(client->rsa.challenge));
|
||||
client->rsa.challenge_len);
|
||||
msg->length = htons(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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
uint8_t client_random[16] = {};
|
||||
uint8_t client_random[32] = {};
|
||||
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) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to decrypt client's challenge");
|
||||
client->state = VNC_CLIENT_STATE_ERROR;
|
||||
|
@ -605,34 +617,26 @@ static int on_rsa_aes_challenge(struct nvnc_client* client)
|
|||
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)
|
||||
uint8_t client_session_key[16];
|
||||
crypto_hash_many(client_session_key, sizeof(client_session_key),
|
||||
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
||||
{ client->rsa.challenge, sizeof(client->rsa.challenge) },
|
||||
{ client_random, sizeof(client_random) },
|
||||
uint8_t client_session_key[32];
|
||||
crypto_hash_many(client_session_key, client_rsa_aes_hash_len(client),
|
||||
client->rsa.hash_type, (const struct crypto_data_entry[]) {
|
||||
{ client->rsa.challenge, client->rsa.challenge_len },
|
||||
{ client_random, client->rsa.challenge_len },
|
||||
{}
|
||||
});
|
||||
|
||||
// ServerSessionKey = the first 16 bytes of SHA1(ClientRandom || ServerRandom)
|
||||
uint8_t server_session_key[16];
|
||||
crypto_hash_many(server_session_key, sizeof(server_session_key),
|
||||
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
||||
{ client_random, sizeof(client_random) },
|
||||
{ client->rsa.challenge, sizeof(client->rsa.challenge) },
|
||||
uint8_t server_session_key[32];
|
||||
crypto_hash_many(server_session_key, client_rsa_aes_hash_len(client),
|
||||
client->rsa.hash_type, (const struct crypto_data_entry[]) {
|
||||
{ client_random, client->rsa.challenge_len },
|
||||
{ client->rsa.challenge, client->rsa.challenge_len },
|
||||
{}
|
||||
});
|
||||
|
||||
crypto_dump_base64("Client session key", client_session_key,
|
||||
sizeof(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);
|
||||
stream_upgrade_to_rsa_eas(client->net_stream, client->rsa.cipher_type,
|
||||
server_session_key, client_session_key);
|
||||
|
||||
uint8_t server_modulus[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 client_key_len_be = htonl(client_key_len * 8);
|
||||
|
||||
uint8_t server_hash[20] = {};
|
||||
crypto_hash_many(server_hash, sizeof(server_hash),
|
||||
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
||||
uint8_t server_hash[32] = {};
|
||||
crypto_hash_many(server_hash, client_rsa_aes_hash_len(client),
|
||||
client->rsa.hash_type, (const struct crypto_data_entry[]) {
|
||||
{ (uint8_t*)&server_key_len_be, 4 },
|
||||
{ server_modulus, 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);
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
if (client->buffer_len - client->buffer_index < 20)
|
||||
if (client->buffer_len - client->buffer_index < client_rsa_aes_hash_len(client))
|
||||
return 0;
|
||||
|
||||
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 client_key_len_be = htonl(client_key_len * 8);
|
||||
|
||||
uint8_t client_hash[20] = {};
|
||||
crypto_hash_many(client_hash, sizeof(client_hash),
|
||||
CRYPTO_HASH_SHA1, (const struct crypto_data_entry[]) {
|
||||
uint8_t client_hash[32] = {};
|
||||
crypto_hash_many(client_hash, client_rsa_aes_hash_len(client),
|
||||
client->rsa.hash_type, (const struct crypto_data_entry[]) {
|
||||
{ (uint8_t*)&client_key_len_be, 4 },
|
||||
{ client_modulus, 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);
|
||||
|
||||
crypto_dump_base16("Client hash", client_hash, 20);
|
||||
|
||||
if (memcmp(msg, client_hash, 20) != 0) {
|
||||
if (memcmp(msg, client_hash, client_rsa_aes_hash_len(client)) != 0) {
|
||||
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
|
||||
|
@ -728,7 +732,7 @@ static int on_rsa_aes_client_hash(struct nvnc_client* client)
|
|||
stream_write(client->net_stream, &subtype, 1, NULL, NULL);
|
||||
|
||||
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)
|
||||
|
@ -795,6 +799,16 @@ static int on_security_message(struct nvnc_client* client)
|
|||
client->state = VNC_CLIENT_STATE_WAITING_FOR_APPLE_DH_RESPONSE;
|
||||
break;
|
||||
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);
|
||||
client->state = VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY;
|
||||
break;
|
||||
|
|
|
@ -186,8 +186,9 @@ static struct stream_impl impl = {
|
|||
.send = stream_rsa_aes_send,
|
||||
};
|
||||
|
||||
int stream_upgrade_to_rsa_eas(struct stream* base, const uint8_t* enc_key,
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return -1;
|
||||
|
||||
self->cipher = crypto_cipher_new(enc_key, dec_key,
|
||||
CRYPTO_CIPHER_AES_EAX);
|
||||
self->cipher = crypto_cipher_new(enc_key, dec_key, cipher_type);
|
||||
if (!self->cipher) {
|
||||
free(self->read_buffer);
|
||||
return -1;
|
||||
|
|
Loading…
Reference in New Issue