endif()
endif()
+option(ENABLE_NETTLE "Enable RSA-AES security types" ON)
+if (ENABLE_NETTLE)
+ find_package(Nettle)
+ if (NETTLE_FOUND)
+ add_definitions("-DHAVE_NETTLE")
+ endif()
+endif()
+
# Check for PAM library
if(UNIX AND NOT APPLE)
check_include_files(security/pam_appl.h HAVE_PAM_H)
--- /dev/null
+find_package(PkgConfig)
+
+if (PKG_CONFIG_FOUND)
+ pkg_check_modules(NETTLE nettle>=3.0)
+ pkg_check_modules(HOGWEED hogweed)
+ pkg_check_modules(GMP gmp)
+else()
+ find_path(NETTLE_INCLUDE_DIRS NAMES eax.h PATH_SUFFIXES nettle)
+ find_library(NETTLE_LIBRARIES NAMES nettle)
+ find_package_handle_standard_args(NETTLE DEFAULT_MSG NETTLE_LIBRARIES NETTLE_INCLUDE_DIRS)
+ find_path(GMP_INCLUDE_DIRS NAMES gmp.h PATH_SUFFIXES)
+ find_library(GMP_LIBRARIES NAMES gmp)
+ find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARIES GMP_INCLUDE_DIRS)
+ find_library(HOGWEED_LIBRARIES NAMES hogweed)
+ find_package_handle_standard_args(HOGWEED DEFAULT_MSG HOGWEED_LIBRARIES)
+endif()
+
+if (NOT HOGWEED_FOUND OR NOT GMP_FOUND)
+ set(NETTLE_FOUND 0)
+endif()
string(STRIP ${GNUTLS_LIBRARIES} GNUTLS_LIBRARIES)
endif()
+ if(NETTLE_FOUND)
+ set(NETTLE_LIBRARIES "-Wl,-Bstatic -lnettle -Wl,-Bdynamic")
+ set(HOGWEED_LIBRARIES "-Wl,-Bstatic -lhogweed -Wl,-Bdynamic")
+ set(GMP_LIBRARIES "-Wl,-Bstatic -lgmp -Wl,-Bdynamic")
+ endif()
+
if(DEFINED FLTK_LIBRARIES)
set(FLTK_LIBRARIES "-Wl,-Bstatic -lfltk_images -lpng -ljpeg -lfltk -Wl,-Bdynamic")
--- /dev/null
+/* Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <rdr/AESInStream.h>
+#include <rdr/Exception.h>
+
+#ifdef HAVE_NETTLE
+using namespace rdr;
+
+AESInStream::AESInStream(InStream* _in, const U8* key, int _keySize)
+ : keySize(_keySize), in(_in), counter()
+{
+ if (keySize == 128)
+ EAX_SET_KEY(&eaxCtx128, aes128_set_encrypt_key, aes128_encrypt, key);
+ else if (keySize == 256)
+ EAX_SET_KEY(&eaxCtx256, aes256_set_encrypt_key, aes256_encrypt, key);
+ else
+ assert(!"incorrect key size");
+}
+
+AESInStream::~AESInStream() {}
+
+bool AESInStream::fillBuffer()
+{
+ if (!in->hasData(2))
+ return false;
+ const U8* ptr = in->getptr(2);
+ size_t length = ((int)ptr[0] << 8) | (int)ptr[1];
+ if (!in->hasData(2 + length + 16))
+ return false;
+ ensureSpace(length);
+ ptr = in->getptr(2 + length + 16);
+ const U8* ad = ptr;
+ const U8* data = ptr + 2;
+ const U8* mac = ptr + 2 + length;
+ U8 macComputed[16];
+
+ if (keySize == 128) {
+ EAX_SET_NONCE(&eaxCtx128, aes128_encrypt, 16, counter);
+ EAX_UPDATE(&eaxCtx128, aes128_encrypt, 2, ad);
+ EAX_DECRYPT(&eaxCtx128, aes128_encrypt, length, (rdr::U8*)end, data);
+ EAX_DIGEST(&eaxCtx128, aes128_encrypt, 16, macComputed);
+ } else {
+ EAX_SET_NONCE(&eaxCtx256, aes256_encrypt, 16, counter);
+ EAX_UPDATE(&eaxCtx256, aes256_encrypt, 2, ad);
+ EAX_DECRYPT(&eaxCtx256, aes256_encrypt, length, (rdr::U8*)end, data);
+ EAX_DIGEST(&eaxCtx256, aes256_encrypt, 16, macComputed);
+ }
+ if (memcmp(mac, macComputed, 16) != 0)
+ throw Exception("AESInStream: failed to authenticate message");
+ in->setptr(2 + length + 16);
+ end += length;
+
+ // Update nonce by incrementing the counter as a
+ // 128bit little endian unsigned integer
+ for (int i = 0; i < 16; ++i) {
+ // increment until there is no carry
+ if (++counter[i] != 0) {
+ break;
+ }
+ }
+ return true;
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+/* Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RDR_AESINSTREAM_H__
+#define __RDR_AESINSTREAM_H__
+
+#ifdef HAVE_NETTLE
+
+#include <nettle/eax.h>
+#include <nettle/aes.h>
+#include <rdr/BufferedInStream.h>
+
+namespace rdr {
+
+ class AESInStream : public BufferedInStream {
+ public:
+ AESInStream(InStream* in, const U8* key, int keySize);
+ virtual ~AESInStream();
+
+ private:
+ virtual bool fillBuffer();
+
+ int keySize;
+ InStream* in;
+ union {
+ struct EAX_CTX(aes128_ctx) eaxCtx128;
+ struct EAX_CTX(aes256_ctx) eaxCtx256;
+ };
+ U8 counter[16];
+ };
+}
+
+#endif
+#endif
--- /dev/null
+/* Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <rdr/Exception.h>
+#include <rdr/AESOutStream.h>
+
+#ifdef HAVE_NETTLE
+using namespace rdr;
+
+const int MaxMessageSize = 8192;
+
+AESOutStream::AESOutStream(OutStream* _out, const U8* key, int _keySize)
+ : keySize(_keySize), out(_out), counter()
+{
+ msg = new U8[MaxMessageSize + 16 + 2];
+ if (keySize == 128)
+ EAX_SET_KEY(&eaxCtx128, aes128_set_encrypt_key, aes128_encrypt, key);
+ else if (keySize == 256)
+ EAX_SET_KEY(&eaxCtx256, aes256_set_encrypt_key, aes256_encrypt, key);
+ else
+ assert(!"incorrect key size");
+}
+
+AESOutStream::~AESOutStream()
+{
+ delete[] msg;
+}
+
+void AESOutStream::flush()
+{
+ BufferedOutStream::flush();
+ out->flush();
+}
+
+void AESOutStream::cork(bool enable)
+{
+ BufferedOutStream::cork(enable);
+ out->cork(enable);
+}
+
+bool AESOutStream::flushBuffer()
+{
+ while (sentUpTo < ptr) {
+ size_t n = ptr - sentUpTo;
+ if (n > MaxMessageSize)
+ n = MaxMessageSize;
+ writeMessage(sentUpTo, n);
+ sentUpTo += n;
+ }
+ return true;
+}
+
+
+void AESOutStream::writeMessage(const U8* data, size_t length)
+{
+ msg[0] = (length & 0xff00) >> 8;
+ msg[1] = length & 0xff;
+
+ if (keySize == 128) {
+ EAX_SET_NONCE(&eaxCtx128, aes128_encrypt, 16, counter);
+ EAX_UPDATE(&eaxCtx128, aes128_encrypt, 2, msg);
+ EAX_ENCRYPT(&eaxCtx128, aes128_encrypt, length, msg + 2, data);
+ EAX_DIGEST(&eaxCtx128, aes128_encrypt, 16, msg + 2 + length);
+ } else {
+ EAX_SET_NONCE(&eaxCtx256, aes256_encrypt, 16, counter);
+ EAX_UPDATE(&eaxCtx256, aes256_encrypt, 2, msg);
+ EAX_ENCRYPT(&eaxCtx256, aes256_encrypt, length, msg + 2, data);
+ EAX_DIGEST(&eaxCtx256, aes256_encrypt, 16, msg + 2 + length);
+ }
+ out->writeBytes(msg, 2 + length + 16);
+ out->flush();
+
+ // Update nonce by incrementing the counter as a
+ // 128bit little endian unsigned integer
+ for (int i = 0; i < 16; ++i) {
+ // increment until there is no carry
+ if (++counter[i] != 0) {
+ break;
+ }
+ }
+}
+
+#endif
--- /dev/null
+/* Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __RDR_AESOUTSTREAM_H__
+#define __RDR_AESOUTSTREAM_H__
+
+#ifdef HAVE_NETTLE
+#include <nettle/eax.h>
+#include <nettle/aes.h>
+#include <rdr/BufferedOutStream.h>
+
+namespace rdr {
+
+ class AESOutStream : public BufferedOutStream {
+ public:
+ AESOutStream(OutStream* out, const U8* key, int keySize);
+ virtual ~AESOutStream();
+
+ virtual void flush();
+ virtual void cork(bool enable);
+
+ private:
+ virtual bool flushBuffer();
+ void writeMessage(const U8* data, size_t length);
+
+ int keySize;
+ OutStream* out;
+ U8* msg;
+ union {
+ struct EAX_CTX(aes128_ctx) eaxCtx128;
+ struct EAX_CTX(aes256_ctx) eaxCtx256;
+ };
+ U8 counter[16];
+ };
+};
+
+#endif
+#endif
include_directories(${ZLIB_INCLUDE_DIRS})
add_library(rdr STATIC
+ AESInStream.cxx
+ AESOutStream.cxx
BufferedInStream.cxx
BufferedOutStream.cxx
Exception.cxx
include_directories(${GNUTLS_INCLUDE_DIR})
target_link_libraries(rdr ${GNUTLS_LIBRARIES})
endif()
+if (NETTLE_FOUND)
+ include_directories(${NETTLE_INCLUDE_DIRS})
+ target_link_libraries(rdr ${NETTLE_LINK_LIBRARIES})
+endif()
if(WIN32)
target_link_libraries(rdr ws2_32)
endif()
target_link_libraries(rfb ${GNUTLS_LIBRARIES})
endif()
+if (NETTLE_FOUND)
+ target_sources(rfb PRIVATE CSecurityRSAAES.cxx SSecurityRSAAES.cxx)
+ include_directories(${NETTLE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS})
+ target_link_libraries(rfb ${HOGWEED_LINK_LIBRARIES} ${NETTLE_LINK_LIBRARIES} ${GMP_LINK_LIBRARIES})
+endif()
+
if(UNIX)
libtool_create_control_file(rfb)
endif()
#define __RFB_CSECURITY_H__
#include <rfb/UserPasswdGetter.h>
+#include <rfb/UserMsgBox.h>
namespace rfb {
class CConnection;
* It MUST be set by viewer.
*/
static UserPasswdGetter *upg;
+ static UserMsgBox *msg;
protected:
CConnection* cc;
--- /dev/null
+/*
+ * Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef HAVE_NETTLE
+#error "This header should not be compiled without HAVE_NETTLE defined"
+#endif
+
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <assert.h>
+
+#include <nettle/bignum.h>
+#include <nettle/sha1.h>
+#include <nettle/sha2.h>
+#include <rfb/CSecurityRSAAES.h>
+#include <rfb/CConnection.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rfb/UserMsgBox.h>
+#include <rdr/AESInStream.h>
+#include <rdr/AESOutStream.h>
+#include <os/os.h>
+
+enum {
+ ReadPublicKey,
+ ReadRandom,
+ ReadHash,
+ ReadSubtype,
+};
+
+const int MinKeyLength = 1024;
+const int MaxKeyLength = 8192;
+
+using namespace rfb;
+
+CSecurityRSAAES::CSecurityRSAAES(CConnection* cc, rdr::U32 _secType,
+ int _keySize, bool _isAllEncrypted)
+ : CSecurity(cc), state(ReadPublicKey),
+ keySize(_keySize), isAllEncrypted(_isAllEncrypted), secType(_secType),
+ clientKey(), clientPublicKey(), serverKey(),
+ serverKeyN(NULL), serverKeyE(NULL),
+ clientKeyN(NULL), clientKeyE(NULL),
+ rais(NULL), raos(NULL), rawis(NULL), rawos(NULL)
+{
+ assert(keySize == 128 || keySize == 256);
+}
+
+CSecurityRSAAES::~CSecurityRSAAES()
+{
+ cleanup();
+}
+
+void CSecurityRSAAES::cleanup()
+{
+ if (serverKeyN)
+ delete[] serverKeyN;
+ if (serverKeyE)
+ delete[] serverKeyE;
+ if (clientKeyN)
+ delete[] clientKeyN;
+ if (clientKeyE)
+ delete[] clientKeyE;
+ if (clientKey.size)
+ rsa_private_key_clear(&clientKey);
+ if (clientPublicKey.size)
+ rsa_public_key_clear(&clientPublicKey);
+ if (serverKey.size)
+ rsa_public_key_clear(&serverKey);
+ if (isAllEncrypted && rawis && rawos)
+ cc->setStreams(rawis, rawos);
+ if (rais)
+ delete rais;
+ if (raos)
+ delete raos;
+}
+
+bool CSecurityRSAAES::processMsg()
+{
+ switch (state) {
+ case ReadPublicKey:
+ if (readPublicKey()) {
+ verifyServer();
+ writePublicKey();
+ writeRandom();
+ state = ReadRandom;
+ }
+ return false;
+ case ReadRandom:
+ if (readRandom()) {
+ setCipher();
+ writeHash();
+ state = ReadHash;
+ }
+ return false;
+ case ReadHash:
+ if (readHash()) {
+ clearSecrets();
+ state = ReadSubtype;
+ }
+ case ReadSubtype:
+ if (readSubtype()) {
+ writeCredentials();
+ return true;
+ }
+ return false;
+ }
+ assert(!"unreachable");
+ return false;
+}
+
+static void random_func(void* ctx, size_t length, uint8_t* dst)
+{
+ rdr::RandomStream* rs = (rdr::RandomStream*)ctx;
+ if (!rs->hasData(length))
+ throw ConnFailedException("failed to generate random");
+ rs->readBytes(dst, length);
+}
+
+void CSecurityRSAAES::writePublicKey()
+{
+ rdr::OutStream* os = cc->getOutStream();
+ // generate client key
+ rsa_public_key_init(&clientPublicKey);
+ rsa_private_key_init(&clientKey);
+ // match the server key size
+ clientKeyLength = serverKeyLength;
+ int rsaKeySize = (clientKeyLength + 7) / 8;
+ // set key size to non-zero to allow clearing the keys when cleanup
+ clientPublicKey.size = rsaKeySize;
+ clientKey.size = rsaKeySize;
+ // set e = 65537
+ mpz_set_ui(clientPublicKey.e, 65537);
+ if (!rsa_generate_keypair(&clientPublicKey, &clientKey,
+ &rs, random_func, NULL, NULL, clientKeyLength, 0))
+ throw AuthFailureException("failed to generate key");
+ clientKeyN = new rdr::U8[rsaKeySize];
+ clientKeyE = new rdr::U8[rsaKeySize];
+ nettle_mpz_get_str_256(rsaKeySize, clientKeyN, clientPublicKey.n);
+ nettle_mpz_get_str_256(rsaKeySize, clientKeyE, clientPublicKey.e);
+ os->writeU32(clientKeyLength);
+ os->writeBytes(clientKeyN, rsaKeySize);
+ os->writeBytes(clientKeyE, rsaKeySize);
+ os->flush();
+}
+
+bool CSecurityRSAAES::readPublicKey()
+{
+ rdr::InStream* is = cc->getInStream();
+ if (!is->hasData(4))
+ return false;
+ is->setRestorePoint();
+ serverKeyLength = is->readU32();
+ if (serverKeyLength < MinKeyLength)
+ throw AuthFailureException("server key is too short");
+ if (serverKeyLength > MaxKeyLength)
+ throw AuthFailureException("server key is too long");
+ size_t size = (serverKeyLength + 7) / 8;
+ if (!is->hasDataOrRestore(size * 2))
+ return false;
+ is->clearRestorePoint();
+ serverKeyE = new rdr::U8[size];
+ serverKeyN = new rdr::U8[size];
+ is->readBytes(serverKeyN, size);
+ is->readBytes(serverKeyE, size);
+ rsa_public_key_init(&serverKey);
+ nettle_mpz_set_str_256_u(serverKey.n, size, serverKeyN);
+ nettle_mpz_set_str_256_u(serverKey.e, size, serverKeyE);
+ if (!rsa_public_key_prepare(&serverKey))
+ throw AuthFailureException("server key is invalid");
+ return true;
+}
+
+void CSecurityRSAAES::verifyServer()
+{
+ rdr::U8 lenServerKey[4] = {
+ (rdr::U8)((serverKeyLength & 0xff000000) >> 24),
+ (rdr::U8)((serverKeyLength & 0xff0000) >> 16),
+ (rdr::U8)((serverKeyLength & 0xff00) >> 8),
+ (rdr::U8)(serverKeyLength & 0xff)
+ };
+ rdr::U8 f[8];
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 4, lenServerKey);
+ sha1_update(&ctx, serverKey.size, serverKeyN);
+ sha1_update(&ctx, serverKey.size, serverKeyE);
+ sha1_digest(&ctx, sizeof(f), f);
+ const char *title = "Server key fingerprint";
+ CharArray text;
+ text.format(
+ "The server has provided the following identifying information:\n"
+ "Fingerprint: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n"
+ "Please verify that the information is correct and press \"Yes\". "
+ "Otherwise press \"No\"", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7]);
+ if (!msg->showMsgBox(UserMsgBox::M_YESNO, title, text.buf))
+ throw AuthFailureException("server key mismatch");
+}
+
+void CSecurityRSAAES::writeRandom()
+{
+ rdr::OutStream* os = cc->getOutStream();
+ if (!rs.hasData(keySize / 8))
+ throw ConnFailedException("failed to generate random");
+ rs.readBytes(clientRandom, keySize / 8);
+ mpz_t x;
+ mpz_init(x);
+ int res;
+ try {
+ res = rsa_encrypt(&serverKey, &rs, random_func, keySize / 8,
+ clientRandom, x);
+ } catch (...) {
+ mpz_clear(x);
+ throw;
+ }
+ if (!res) {
+ mpz_clear(x);
+ throw AuthFailureException("failed to encrypt random");
+ }
+ rdr::U8* buffer = new rdr::U8[serverKey.size];
+ nettle_mpz_get_str_256(serverKey.size, buffer, x);
+ mpz_clear(x);
+ os->writeU16(serverKey.size);
+ os->writeBytes(buffer, serverKey.size);
+ os->flush();
+ delete[] buffer;
+}
+
+bool CSecurityRSAAES::readRandom()
+{
+ rdr::InStream* is = cc->getInStream();
+ if (!is->hasData(2))
+ return false;
+ is->setRestorePoint();
+ size_t size = is->readU16();
+ if (size != clientKey.size)
+ throw AuthFailureException("client key length doesn't match");
+ if (!is->hasDataOrRestore(size))
+ return false;
+ is->clearRestorePoint();
+ rdr::U8* buffer = new rdr::U8[size];
+ is->readBytes(buffer, size);
+ size_t randomSize = keySize / 8;
+ mpz_t x;
+ nettle_mpz_init_set_str_256_u(x, size, buffer);
+ delete[] buffer;
+ if (!rsa_decrypt(&clientKey, &randomSize, serverRandom, x) ||
+ randomSize != (size_t)keySize / 8) {
+ mpz_clear(x);
+ throw AuthFailureException("failed to decrypt server random");
+ }
+ mpz_clear(x);
+ return true;
+}
+
+void CSecurityRSAAES::setCipher()
+{
+ rawis = cc->getInStream();
+ rawos = cc->getOutStream();
+ rdr::U8 key[32];
+ if (keySize == 128) {
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 16, clientRandom);
+ sha1_update(&ctx, 16, serverRandom);
+ sha1_digest(&ctx, 16, key);
+ rais = new rdr::AESInStream(rawis, key, 128);
+ sha1_init(&ctx);
+ sha1_update(&ctx, 16, serverRandom);
+ sha1_update(&ctx, 16, clientRandom);
+ sha1_digest(&ctx, 16, key);
+ raos = new rdr::AESOutStream(rawos, key, 128);
+ } else {
+ struct sha256_ctx ctx;
+ sha256_init(&ctx);
+ sha256_update(&ctx, 32, clientRandom);
+ sha256_update(&ctx, 32, serverRandom);
+ sha256_digest(&ctx, 32, key);
+ rais = new rdr::AESInStream(rawis, key, 256);
+ sha256_init(&ctx);
+ sha256_update(&ctx, 32, serverRandom);
+ sha256_update(&ctx, 32, clientRandom);
+ sha256_digest(&ctx, 32, key);
+ raos = new rdr::AESOutStream(rawos, key, 256);
+ }
+ if (isAllEncrypted)
+ cc->setStreams(rais, raos);
+}
+
+void CSecurityRSAAES::writeHash()
+{
+ rdr::U8 hash[32];
+ size_t len = serverKeyLength;
+ rdr::U8 lenServerKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ len = clientKeyLength;
+ rdr::U8 lenClientKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ int hashSize;
+ if (keySize == 128) {
+ hashSize = 20;
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 4, lenClientKey);
+ sha1_update(&ctx, clientKey.size, clientKeyN);
+ sha1_update(&ctx, clientKey.size, clientKeyE);
+ sha1_update(&ctx, 4, lenServerKey);
+ sha1_update(&ctx, serverKey.size, serverKeyN);
+ sha1_update(&ctx, serverKey.size, serverKeyE);
+ sha1_digest(&ctx, hashSize, hash);
+ } else {
+ hashSize = 32;
+ struct sha256_ctx ctx;
+ sha256_init(&ctx);
+ sha256_update(&ctx, 4, lenClientKey);
+ sha256_update(&ctx, clientKey.size, clientKeyN);
+ sha256_update(&ctx, clientKey.size, clientKeyE);
+ sha256_update(&ctx, 4, lenServerKey);
+ sha256_update(&ctx, serverKey.size, serverKeyN);
+ sha256_update(&ctx, serverKey.size, serverKeyE);
+ sha256_digest(&ctx, hashSize, hash);
+ }
+ raos->writeBytes(hash, hashSize);
+ raos->flush();
+}
+
+bool CSecurityRSAAES::readHash()
+{
+ rdr::U8 hash[32];
+ rdr::U8 realHash[32];
+ int hashSize = keySize == 128 ? 20 : 32;
+ if (!rais->hasData(hashSize))
+ return false;
+ rais->readBytes(hash, hashSize);
+ size_t len = serverKeyLength;
+ rdr::U8 lenServerKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ len = clientKeyLength;
+ rdr::U8 lenClientKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ if (keySize == 128) {
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 4, lenServerKey);
+ sha1_update(&ctx, serverKey.size, serverKeyN);
+ sha1_update(&ctx, serverKey.size, serverKeyE);
+ sha1_update(&ctx, 4, lenClientKey);
+ sha1_update(&ctx, clientKey.size, clientKeyN);
+ sha1_update(&ctx, clientKey.size, clientKeyE);
+ sha1_digest(&ctx, hashSize, realHash);
+ } else {
+ struct sha256_ctx ctx;
+ sha256_init(&ctx);
+ sha256_update(&ctx, 4, lenServerKey);
+ sha256_update(&ctx, serverKey.size, serverKeyN);
+ sha256_update(&ctx, serverKey.size, serverKeyE);
+ sha256_update(&ctx, 4, lenClientKey);
+ sha256_update(&ctx, clientKey.size, clientKeyN);
+ sha256_update(&ctx, clientKey.size, clientKeyE);
+ sha256_digest(&ctx, hashSize, realHash);
+ }
+ if (memcmp(hash, realHash, hashSize) != 0)
+ throw AuthFailureException("hash doesn't match");
+ return true;
+}
+
+void CSecurityRSAAES::clearSecrets()
+{
+ rsa_private_key_clear(&clientKey);
+ rsa_public_key_clear(&clientPublicKey);
+ rsa_public_key_clear(&serverKey);
+ clientKey.size = 0;
+ clientPublicKey.size = 0;
+ serverKey.size = 0;
+ delete[] serverKeyN;
+ delete[] serverKeyE;
+ delete[] clientKeyN;
+ delete[] clientKeyE;
+ serverKeyN = NULL;
+ serverKeyE = NULL;
+ clientKeyN = NULL;
+ clientKeyE = NULL;
+ memset(serverRandom, 0, sizeof(serverRandom));
+ memset(clientRandom, 0, sizeof(clientRandom));
+}
+
+bool CSecurityRSAAES::readSubtype()
+{
+ if (!rais->hasData(1))
+ return false;
+ subtype = rais->readU8();
+ if (subtype != secTypeRA2UserPass && subtype != secTypeRA2Pass)
+ throw AuthFailureException("unknown RSA-AES subtype");
+ return true;
+}
+
+void CSecurityRSAAES::writeCredentials()
+{
+ CharArray username;
+ CharArray password;
+
+ (CSecurity::upg)->getUserPasswd(
+ isSecure(),
+ subtype == secTypeRA2UserPass ? &username.buf : NULL, &password.buf
+ );
+ size_t len;
+ if (username.buf) {
+ len = strlen(username.buf);
+ if (len > 255)
+ throw AuthFailureException("username is too long");
+ raos->writeU8(len);
+ if (len)
+ raos->writeBytes(username.buf, len);
+ } else {
+ raos->writeU8(0);
+ }
+ len = strlen(password.buf);
+ if (len > 255)
+ throw AuthFailureException("password is too long");
+ raos->writeU8(len);
+ if (len)
+ raos->writeBytes(password.buf, len);
+ raos->flush();
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __C_SECURITY_RSAAES_H__
+#define __C_SECURITY_RSAAES_H__
+
+#ifndef HAVE_NETTLE
+#error "This header should not be compiled without HAVE_NETTLE defined"
+#endif
+
+#include <nettle/rsa.h>
+#include <rfb/CSecurity.h>
+#include <rfb/Security.h>
+#include <rfb/UserMsgBox.h>
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rdr/RandomStream.h>
+
+namespace rfb {
+ class UserMsgBox;
+ class CSecurityRSAAES : public CSecurity {
+ public:
+ CSecurityRSAAES(CConnection* cc, rdr::U32 secType,
+ int keySize, bool isAllEncrypted);
+ virtual ~CSecurityRSAAES();
+ virtual bool processMsg();
+ virtual int getType() const { return secType; }
+ virtual bool isSecure() const { return secType == secTypeRA256; }
+
+ static IntParameter RSAKeyLength;
+
+ private:
+ void cleanup();
+ void writePublicKey();
+ bool readPublicKey();
+ void verifyServer();
+ void writeRandom();
+ bool readRandom();
+ void setCipher();
+ void writeHash();
+ bool readHash();
+ void clearSecrets();
+ bool readSubtype();
+ void writeCredentials();
+
+ int state;
+ int keySize;
+ bool isAllEncrypted;
+ rdr::U32 secType;
+ rdr::U8 subtype;
+ struct rsa_private_key clientKey;
+ struct rsa_public_key clientPublicKey;
+ struct rsa_public_key serverKey;
+ rdr::U32 serverKeyLength;
+ rdr::U8* serverKeyN;
+ rdr::U8* serverKeyE;
+ rdr::U32 clientKeyLength;
+ rdr::U8* clientKeyN;
+ rdr::U8* clientKeyE;
+ rdr::U8 serverRandom[32];
+ rdr::U8 clientRandom[32];
+
+ rdr::InStream* rais;
+ rdr::OutStream* raos;
+
+ rdr::InStream* rawis;
+ rdr::OutStream* rawos;
+
+ rdr::RandomStream rs;
+ };
+}
+
+#endif
#include <gnutls/gnutls.h>
namespace rfb {
- class UserMsgBox;
class CSecurityTLS : public CSecurity {
public:
CSecurityTLS(CConnection* cc, bool _anon);
static StringParameter X509CA;
static StringParameter X509CRL;
- static UserMsgBox *msg;
protected:
void shutdown();
StringParameter PasswordValidator::plainUsers
("PlainUsers",
- "Users permitted to access via Plain security type (including TLSPlain, X509Plain etc.)",
+ "Users permitted to access via Plain security type (including TLSPlain, X509Plain etc.)"
+#ifdef HAVE_NETTLE
+ " or RSA-AES security types (RA2, RA2ne, RA2_256, RA2ne_256)"
+#endif
+ ,
"");
bool PasswordValidator::validUser(const char* username)
--- /dev/null
+/* Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef HAVE_NETTLE
+#error "This source should not be compiled without HAVE_NETTLE defined"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <nettle/bignum.h>
+#include <nettle/sha1.h>
+#include <nettle/sha2.h>
+#include <nettle/base64.h>
+#include <nettle/asn1.h>
+#include <rfb/SSecurityRSAAES.h>
+#include <rfb/SConnection.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rdr/AESInStream.h>
+#include <rdr/AESOutStream.h>
+#if !defined(WIN32) && !defined(__APPLE__)
+#include <rfb/UnixPasswordValidator.h>
+#endif
+#ifdef WIN32
+#include <rfb/WinPasswdValidator.h>
+#endif
+#include <rfb/SSecurityVncAuth.h>
+
+enum {
+ SendPublicKey,
+ ReadPublicKey,
+ ReadRandom,
+ ReadHash,
+ ReadCredentials,
+};
+
+const int MinKeyLength = 1024;
+const int MaxKeyLength = 8192;
+const size_t MaxKeyFileSize = 32 * 1024;
+
+using namespace rfb;
+
+StringParameter SSecurityRSAAES::keyFile
+("RSAKey", "Path to the RSA key for the RSA-AES security types in "
+ "PEM format", "", ConfServer);
+BoolParameter SSecurityRSAAES::requireUsername
+("RequireUsername", "Require username for the RSA-AES security types",
+ false, ConfServer);
+
+SSecurityRSAAES::SSecurityRSAAES(SConnection* sc, rdr::U32 _secType,
+ int _keySize, bool _isAllEncrypted)
+ : SSecurity(sc), state(SendPublicKey),
+ keySize(_keySize), isAllEncrypted(_isAllEncrypted), secType(_secType),
+ serverKey(), clientKey(),
+ serverKeyN(NULL), serverKeyE(NULL), clientKeyN(NULL), clientKeyE(NULL),
+ accessRights(SConnection::AccessDefault),
+ rais(NULL), raos(NULL), rawis(NULL), rawos(NULL)
+{
+ assert(keySize == 128 || keySize == 256);
+}
+
+SSecurityRSAAES::~SSecurityRSAAES()
+{
+ cleanup();
+}
+
+void SSecurityRSAAES::cleanup()
+{
+ if (serverKeyN)
+ delete[] serverKeyN;
+ if (serverKeyE)
+ delete[] serverKeyE;
+ if (clientKeyN)
+ delete[] clientKeyN;
+ if (clientKeyE)
+ delete[] clientKeyE;
+ if (serverKey.size)
+ rsa_private_key_clear(&serverKey);
+ if (clientKey.size)
+ rsa_public_key_clear(&clientKey);
+ if (isAllEncrypted && rawis && rawos)
+ sc->setStreams(rawis, rawos);
+ if (rais)
+ delete rais;
+ if (raos)
+ delete raos;
+}
+
+static inline ssize_t findSubstr(rdr::U8* data, size_t size, const char *pattern)
+{
+ size_t patternLength = strlen(pattern);
+ for (size_t i = 0; i + patternLength < size; ++i) {
+ for (size_t j = 0; j < patternLength; ++j)
+ if (data[i + j] != pattern[j])
+ goto next;
+ return i;
+next:
+ continue;
+ }
+ return -1;
+}
+
+static bool loadPEM(rdr::U8* data, size_t size, const char *begin,
+ const char *end, rdr::U8** der, size_t *derSize)
+{
+ ssize_t pos1 = findSubstr(data, size, begin);
+ if (pos1 == -1)
+ return false;
+ pos1 += strlen(begin);
+ ssize_t base64Size = findSubstr(data + pos1, size - pos1, end);
+ if (base64Size == -1)
+ return false;
+ char *derBase64 = (char *)data + pos1;
+ if (!base64Size)
+ return false;
+ *der = new rdr::U8[BASE64_DECODE_LENGTH(base64Size)];
+ struct base64_decode_ctx ctx;
+ base64_decode_init(&ctx);
+ if (!base64_decode_update(&ctx, derSize, *der, base64Size, derBase64))
+ return false;
+ if (!base64_decode_final(&ctx))
+ return false;
+ return true;
+}
+
+void SSecurityRSAAES::loadPrivateKey()
+{
+ FILE* file = fopen(keyFile.getData(), "rb");
+ if (!file)
+ throw ConnFailedException("failed to open key file");
+ fseek(file, 0, SEEK_END);
+ size_t size = ftell(file);
+ if (size == 0 || size > MaxKeyFileSize) {
+ fclose(file);
+ throw ConnFailedException("size of key file is zero or too big");
+ }
+ fseek(file, 0, SEEK_SET);
+ rdr::U8Array data(size);
+ if (fread(data.buf, 1, size, file) != size) {
+ fclose(file);
+ throw ConnFailedException("failed to read key");
+ }
+ fclose(file);
+
+ rdr::U8Array der;
+ size_t derSize;
+ if (loadPEM(data.buf, size, "-----BEGIN RSA PRIVATE KEY-----\n",
+ "-----END RSA PRIVATE KEY-----", &der.buf, &derSize)) {
+ loadPKCS1Key(der.buf, derSize);
+ return;
+ }
+ if (der.buf)
+ delete[] der.takeBuf();
+ if (loadPEM(data.buf, size, "-----BEGIN PRIVATE KEY-----\n",
+ "-----END PRIVATE KEY-----", &der.buf, &derSize)) {
+ loadPKCS8Key(der.buf, derSize);
+ return;
+ }
+ throw ConnFailedException("failed to import key");
+}
+
+void SSecurityRSAAES::loadPKCS1Key(const rdr::U8* data, size_t size)
+{
+ struct rsa_public_key pub;
+ rsa_private_key_init(&serverKey);
+ rsa_public_key_init(&pub);
+ if (!rsa_keypair_from_der(&pub, &serverKey, 0, size, data)) {
+ rsa_private_key_clear(&serverKey);
+ rsa_public_key_clear(&pub);
+ throw ConnFailedException("failed to import key");
+ }
+ serverKeyLength = serverKey.size * 8;
+ serverKeyN = new rdr::U8[serverKey.size];
+ serverKeyE = new rdr::U8[serverKey.size];
+ nettle_mpz_get_str_256(serverKey.size, serverKeyN, pub.n);
+ nettle_mpz_get_str_256(serverKey.size, serverKeyE, pub.e);
+ rsa_public_key_clear(&pub);
+}
+
+void SSecurityRSAAES::loadPKCS8Key(const rdr::U8* data, size_t size)
+{
+ struct asn1_der_iterator i, j;
+ uint32_t version;
+ const char* rsaIdentifier = "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01";
+ const size_t rsaIdentifierLength = 9;
+ enum asn1_iterator_result res = asn1_der_iterator_first(&i, size, data);
+ if (res != ASN1_ITERATOR_CONSTRUCTED)
+ goto failed;
+ if (i.type != ASN1_SEQUENCE)
+ goto failed;
+ if (asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE)
+ goto failed;
+ if (!(i.type == ASN1_INTEGER &&
+ asn1_der_get_uint32(&i, &version) &&
+ version == 0))
+ goto failed;
+ if (!(asn1_der_iterator_next(&i) == ASN1_ITERATOR_CONSTRUCTED &&
+ i.type == ASN1_SEQUENCE &&
+ asn1_der_decode_constructed(&i, &j) == ASN1_ITERATOR_PRIMITIVE &&
+ j.type == ASN1_IDENTIFIER &&
+ j.length == rsaIdentifierLength &&
+ memcmp(j.data, rsaIdentifier, rsaIdentifierLength) == 0))
+ goto failed;
+ if (!(asn1_der_iterator_next(&i) == ASN1_ITERATOR_PRIMITIVE &&
+ i.type == ASN1_OCTETSTRING && i.length))
+ goto failed;
+ loadPKCS1Key(i.data, i.length);
+ return;
+failed:
+ throw ConnFailedException("failed to import key");
+}
+
+bool SSecurityRSAAES::processMsg()
+{
+ switch (state) {
+ case SendPublicKey:
+ loadPrivateKey();
+ writePublicKey();
+ state = ReadPublicKey;
+ // fall through
+ case ReadPublicKey:
+ if (readPublicKey()) {
+ writeRandom();
+ state = ReadRandom;
+ }
+ return false;
+ case ReadRandom:
+ if (readRandom()) {
+ setCipher();
+ writeHash();
+ state = ReadHash;
+ }
+ return false;
+ case ReadHash:
+ if (readHash()) {
+ clearSecrets();
+ writeSubtype();
+ state = ReadCredentials;
+ }
+ return false;
+ case ReadCredentials:
+ if (readCredentials()) {
+ if (requireUsername)
+ verifyUserPass();
+ else
+ verifyPass();
+ return true;
+ }
+ return false;
+ }
+ assert(!"unreachable");
+ return false;
+}
+
+void SSecurityRSAAES::writePublicKey()
+{
+ rdr::OutStream* os = sc->getOutStream();
+ os->writeU32(serverKeyLength);
+ os->writeBytes(serverKeyN, serverKey.size);
+ os->writeBytes(serverKeyE, serverKey.size);
+ os->flush();
+}
+
+bool SSecurityRSAAES::readPublicKey()
+{
+ rdr::InStream* is = sc->getInStream();
+ if (!is->hasData(4))
+ return false;
+ is->setRestorePoint();
+ clientKeyLength = is->readU32();
+ if (clientKeyLength < MinKeyLength)
+ throw ConnFailedException("client key is too short");
+ if (clientKeyLength > MaxKeyLength)
+ throw ConnFailedException("client key is too long");
+ size_t size = (clientKeyLength + 7) / 8;
+ if (!is->hasDataOrRestore(size * 2))
+ return false;
+ is->clearRestorePoint();
+ clientKeyE = new rdr::U8[size];
+ clientKeyN = new rdr::U8[size];
+ is->readBytes(clientKeyN, size);
+ is->readBytes(clientKeyE, size);
+ rsa_public_key_init(&clientKey);
+ nettle_mpz_set_str_256_u(clientKey.n, size, clientKeyN);
+ nettle_mpz_set_str_256_u(clientKey.e, size, clientKeyE);
+ if (!rsa_public_key_prepare(&clientKey))
+ throw ConnFailedException("client key is invalid");
+ return true;
+}
+
+static void random_func(void* ctx, size_t length, uint8_t* dst)
+{
+ rdr::RandomStream* rs = (rdr::RandomStream*)ctx;
+ if (!rs->hasData(length))
+ throw ConnFailedException("failed to encrypt random");
+ rs->readBytes(dst, length);
+}
+
+void SSecurityRSAAES::writeRandom()
+{
+ rdr::OutStream* os = sc->getOutStream();
+ if (!rs.hasData(keySize / 8))
+ throw ConnFailedException("failed to generate random");
+ rs.readBytes(serverRandom, keySize / 8);
+ mpz_t x;
+ mpz_init(x);
+ int res;
+ try {
+ res = rsa_encrypt(&clientKey, &rs, random_func, keySize / 8,
+ serverRandom, x);
+ } catch (...) {
+ mpz_clear(x);
+ throw;
+ }
+ if (!res) {
+ mpz_clear(x);
+ throw ConnFailedException("failed to encrypt random");
+ }
+ rdr::U8* buffer = new rdr::U8[clientKey.size];
+ nettle_mpz_get_str_256(clientKey.size, buffer, x);
+ mpz_clear(x);
+ os->writeU16(clientKey.size);
+ os->writeBytes(buffer, clientKey.size);
+ os->flush();
+ delete[] buffer;
+}
+
+bool SSecurityRSAAES::readRandom()
+{
+ rdr::InStream* is = sc->getInStream();
+ if (!is->hasData(2))
+ return false;
+ is->setRestorePoint();
+ size_t size = is->readU16();
+ if (size != serverKey.size)
+ throw ConnFailedException("server key length doesn't match");
+ if (!is->hasDataOrRestore(size))
+ return false;
+ is->clearRestorePoint();
+ rdr::U8* buffer = new rdr::U8[size];
+ is->readBytes(buffer, size);
+ size_t randomSize = keySize / 8;
+ mpz_t x;
+ nettle_mpz_init_set_str_256_u(x, size, buffer);
+ delete[] buffer;
+ if (!rsa_decrypt(&serverKey, &randomSize, clientRandom, x) ||
+ randomSize != (size_t)keySize / 8) {
+ mpz_clear(x);
+ throw ConnFailedException("failed to decrypt client random");
+ }
+ mpz_clear(x);
+ return true;
+}
+
+void SSecurityRSAAES::setCipher()
+{
+ rawis = sc->getInStream();
+ rawos = sc->getOutStream();
+ rdr::U8 key[32];
+ if (keySize == 128) {
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 16, serverRandom);
+ sha1_update(&ctx, 16, clientRandom);
+ sha1_digest(&ctx, 16, key);
+ rais = new rdr::AESInStream(rawis, key, 128);
+ sha1_init(&ctx);
+ sha1_update(&ctx, 16, clientRandom);
+ sha1_update(&ctx, 16, serverRandom);
+ sha1_digest(&ctx, 16, key);
+ raos = new rdr::AESOutStream(rawos, key, 128);
+ } else {
+ struct sha256_ctx ctx;
+ sha256_init(&ctx);
+ sha256_update(&ctx, 32, serverRandom);
+ sha256_update(&ctx, 32, clientRandom);
+ sha256_digest(&ctx, 32, key);
+ rais = new rdr::AESInStream(rawis, key, 256);
+ sha256_init(&ctx);
+ sha256_update(&ctx, 32, clientRandom);
+ sha256_update(&ctx, 32, serverRandom);
+ sha256_digest(&ctx, 32, key);
+ raos = new rdr::AESOutStream(rawos, key, 256);
+ }
+ if (isAllEncrypted)
+ sc->setStreams(rais, raos);
+}
+
+void SSecurityRSAAES::writeHash()
+{
+ rdr::U8 hash[32];
+ size_t len = serverKeyLength;
+ rdr::U8 lenServerKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ len = clientKeyLength;
+ rdr::U8 lenClientKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ int hashSize;
+ if (keySize == 128) {
+ hashSize = 20;
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 4, lenServerKey);
+ sha1_update(&ctx, serverKey.size, serverKeyN);
+ sha1_update(&ctx, serverKey.size, serverKeyE);
+ sha1_update(&ctx, 4, lenClientKey);
+ sha1_update(&ctx, clientKey.size, clientKeyN);
+ sha1_update(&ctx, clientKey.size, clientKeyE);
+ sha1_digest(&ctx, hashSize, hash);
+ } else {
+ hashSize = 32;
+ struct sha256_ctx ctx;
+ sha256_init(&ctx);
+ sha256_update(&ctx, 4, lenServerKey);
+ sha256_update(&ctx, serverKey.size, serverKeyN);
+ sha256_update(&ctx, serverKey.size, serverKeyE);
+ sha256_update(&ctx, 4, lenClientKey);
+ sha256_update(&ctx, clientKey.size, clientKeyN);
+ sha256_update(&ctx, clientKey.size, clientKeyE);
+ sha256_digest(&ctx, hashSize, hash);
+ }
+ raos->writeBytes(hash, hashSize);
+ raos->flush();
+}
+
+bool SSecurityRSAAES::readHash()
+{
+ rdr::U8 hash[32];
+ rdr::U8 realHash[32];
+ int hashSize = keySize == 128 ? 20 : 32;
+ if (!rais->hasData(hashSize))
+ return false;
+ rais->readBytes(hash, hashSize);
+ size_t len = serverKeyLength;
+ rdr::U8 lenServerKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ len = clientKeyLength;
+ rdr::U8 lenClientKey[4] = {
+ (rdr::U8)((len & 0xff000000) >> 24),
+ (rdr::U8)((len & 0xff0000) >> 16),
+ (rdr::U8)((len & 0xff00) >> 8),
+ (rdr::U8)(len & 0xff)
+ };
+ if (keySize == 128) {
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, 4, lenClientKey);
+ sha1_update(&ctx, clientKey.size, clientKeyN);
+ sha1_update(&ctx, clientKey.size, clientKeyE);
+ sha1_update(&ctx, 4, lenServerKey);
+ sha1_update(&ctx, serverKey.size, serverKeyN);
+ sha1_update(&ctx, serverKey.size, serverKeyE);
+ sha1_digest(&ctx, hashSize, realHash);
+ } else {
+ struct sha256_ctx ctx;
+ sha256_init(&ctx);
+ sha256_update(&ctx, 4, lenClientKey);
+ sha256_update(&ctx, clientKey.size, clientKeyN);
+ sha256_update(&ctx, clientKey.size, clientKeyE);
+ sha256_update(&ctx, 4, lenServerKey);
+ sha256_update(&ctx, serverKey.size, serverKeyN);
+ sha256_update(&ctx, serverKey.size, serverKeyE);
+ sha256_digest(&ctx, hashSize, realHash);
+ }
+ if (memcmp(hash, realHash, hashSize) != 0)
+ throw ConnFailedException("hash doesn't match");
+ return true;
+}
+
+void SSecurityRSAAES::clearSecrets()
+{
+ rsa_private_key_clear(&serverKey);
+ rsa_public_key_clear(&clientKey);
+ serverKey.size = 0;
+ clientKey.size = 0;
+ delete[] serverKeyN;
+ delete[] serverKeyE;
+ delete[] clientKeyN;
+ delete[] clientKeyE;
+ serverKeyN = NULL;
+ serverKeyE = NULL;
+ clientKeyN = NULL;
+ clientKeyE = NULL;
+ memset(serverRandom, 0, sizeof(serverRandom));
+ memset(clientRandom, 0, sizeof(clientRandom));
+}
+
+void SSecurityRSAAES::writeSubtype()
+{
+ if (requireUsername)
+ raos->writeU8(secTypeRA2UserPass);
+ else
+ raos->writeU8(secTypeRA2Pass);
+ raos->flush();
+}
+
+bool SSecurityRSAAES::readCredentials()
+{
+ rais->setRestorePoint();
+ if (!rais->hasData(1))
+ return false;
+ rdr::U8 lenUsername = rais->readU8();
+ if (!rais->hasDataOrRestore(lenUsername + 1))
+ return false;
+ if (!username.buf) {
+ username.replaceBuf(new char[lenUsername + 1]);
+ rais->readBytes(username.buf, lenUsername);
+ username.buf[lenUsername] = 0;
+ } else {
+ rais->skip(lenUsername);
+ }
+ rdr::U8 lenPassword = rais->readU8();
+ if (!rais->hasDataOrRestore(lenPassword))
+ return false;
+ password.replaceBuf(new char[lenPassword + 1]);
+ rais->readBytes(password.buf, lenPassword);
+ password.buf[lenPassword] = 0;
+ rais->clearRestorePoint();
+ return true;
+}
+
+void SSecurityRSAAES::verifyUserPass()
+{
+#ifndef __APPLE__
+#ifdef WIN32
+ WinPasswdValidator* valid = new WinPasswdValidator();
+#elif !defined(__APPLE__)
+ UnixPasswordValidator *valid = new UnixPasswordValidator();
+#endif
+ if (!valid->validate(sc, username.buf, password.buf)) {
+ delete valid;
+ throw AuthFailureException("invalid password or username");
+ }
+ delete valid;
+#else
+ throw AuthFailureException("No password validator configured");
+#endif
+}
+
+void SSecurityRSAAES::verifyPass()
+{
+ VncAuthPasswdGetter* pg = &SSecurityVncAuth::vncAuthPasswd;
+ PlainPasswd passwd, passwdReadOnly;
+ pg->getVncAuthPasswd(&passwd, &passwdReadOnly);
+
+ if (!passwd.buf)
+ throw AuthFailureException("No password configured for VNC Auth");
+
+ if (strcmp(password.buf, passwd.buf) == 0) {
+ accessRights = SConnection::AccessDefault;
+ return;
+ }
+
+ if (passwdReadOnly.buf && strcmp(password.buf, passwdReadOnly.buf) == 0) {
+ accessRights = SConnection::AccessView;
+ return;
+ }
+
+ throw AuthFailureException();
+}
+
+const char* SSecurityRSAAES::getUserName() const
+{
+ return username.buf;
+}
--- /dev/null
+/* Copyright (C) 2022 Dinglan Peng
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __S_SECURITY_RSAAES_H__
+#define __S_SECURITY_RSAAES_H__
+
+#ifndef HAVE_NETTLE
+#error "This header should not be included without HAVE_NETTLE defined"
+#endif
+
+#include <nettle/rsa.h>
+#include <rfb/SSecurity.h>
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rdr/RandomStream.h>
+
+namespace rfb {
+
+ class SSecurityRSAAES : public SSecurity {
+ public:
+ SSecurityRSAAES(SConnection* sc, rdr::U32 secType,
+ int keySize, bool isAllEncrypted);
+ virtual ~SSecurityRSAAES();
+ virtual bool processMsg();
+ virtual const char* getUserName() const;
+ virtual int getType() const { return secType; }
+ virtual SConnection::AccessRights getAccessRights() const
+ {
+ return accessRights;
+ }
+
+ static StringParameter keyFile;
+ static BoolParameter requireUsername;
+
+ private:
+ void cleanup();
+ void loadPrivateKey();
+ void loadPKCS1Key(const rdr::U8* data, size_t size);
+ void loadPKCS8Key(const rdr::U8* data, size_t size);
+ void writePublicKey();
+ bool readPublicKey();
+ void writeRandom();
+ bool readRandom();
+ void setCipher();
+ void writeHash();
+ bool readHash();
+ void clearSecrets();
+ void writeSubtype();
+ bool readCredentials();
+ void verifyUserPass();
+ void verifyPass();
+
+ int state;
+ int keySize;
+ bool isAllEncrypted;
+ rdr::U32 secType;
+ struct rsa_private_key serverKey;
+ struct rsa_public_key clientKey;
+ rdr::U32 serverKeyLength;
+ rdr::U8* serverKeyN;
+ rdr::U8* serverKeyE;
+ rdr::U32 clientKeyLength;
+ rdr::U8* clientKeyN;
+ rdr::U8* clientKeyE;
+ rdr::U8 serverRandom[32];
+ rdr::U8 clientRandom[32];
+
+ CharArray username;
+ CharArray password;
+ SConnection::AccessRights accessRights;
+
+ rdr::InStream* rais;
+ rdr::OutStream* raos;
+
+ rdr::InStream* rawis;
+ rdr::OutStream* rawos;
+
+ rdr::RandomStream rs;
+ };
+
+}
+
+#endif
if (strcasecmp(name, "Tight") == 0) return secTypeTight;
if (strcasecmp(name, "RA2") == 0) return secTypeRA2;
if (strcasecmp(name, "RA2ne") == 0) return secTypeRA2ne;
+ if (strcasecmp(name, "RA2_256") == 0) return secTypeRA256;
+ if (strcasecmp(name, "RA2ne_256") == 0) return secTypeRAne256;
if (strcasecmp(name, "SSPI") == 0) return secTypeSSPI;
if (strcasecmp(name, "SSPIne") == 0) return secTypeSSPIne;
if (strcasecmp(name, "VeNCrypt") == 0) return secTypeVeNCrypt;
case secTypeTight: return "Tight";
case secTypeRA2: return "RA2";
case secTypeRA2ne: return "RA2ne";
+ case secTypeRA256: return "RA2_256";
+ case secTypeRAne256: return "RA2ne_256";
case secTypeSSPI: return "SSPI";
case secTypeSSPIne: return "SSPIne";
case secTypeVeNCrypt: return "VeNCrypt";
const rdr::U8 secTypeTLS = 18;
const rdr::U8 secTypeVeNCrypt= 19;
+ const rdr::U8 secTypeRA256 = 129;
+ const rdr::U8 secTypeRAne256 = 130;
+
/* VeNCrypt subtypes */
const int secTypePlain = 256;
const int secTypeTLSNone = 257;
const int secTypeX509Vnc = 261;
const int secTypeX509Plain = 262;
+ /* RSA-AES subtypes */
+ const int secTypeRA2UserPass = 1;
+ const int secTypeRA2Pass = 2;
+
// result types
const rdr::U32 secResultOK = 0;
#ifdef HAVE_GNUTLS
#include <rfb/CSecurityTLS.h>
#endif
+#ifdef HAVE_NETTLE
+#include <rfb/CSecurityRSAAES.h>
+#endif
using namespace rdr;
using namespace rfb;
UserPasswdGetter *CSecurity::upg = NULL;
-#ifdef HAVE_GNUTLS
-UserMsgBox *CSecurityTLS::msg = NULL;
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
+UserMsgBox *CSecurity::msg = NULL;
#endif
StringParameter SecurityClient::secTypes
"Specify which security scheme to use (None, VncAuth, Plain"
#ifdef HAVE_GNUTLS
", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain"
+#endif
+#ifdef HAVE_NETTLE
+ ", RA2, RA2ne, RA2_256, RA2ne_256"
#endif
")",
#ifdef HAVE_GNUTLS
- "X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None",
-#else
- "VncAuth,None",
+ "X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,"
#endif
+#ifdef HAVE_NETTLE
+ "RA2,RA2_256,RA2ne,RA2ne_256,"
+#endif
+ "VncAuth,None",
ConfViewer);
CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType)
{
assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */
-#ifdef HAVE_GNUTLS
- assert (CSecurityTLS::msg != NULL);
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
+ assert (CSecurity::msg != NULL);
#endif
if (!IsSupported(secType))
return new CSecurityStack(cc, secTypeX509Plain,
new CSecurityTLS(cc, false),
new CSecurityPlain(cc));
+#endif
+#ifdef HAVE_NETTLE
+ case secTypeRA2:
+ return new CSecurityRSAAES(cc, secTypeRA2, 128, true);
+ case secTypeRA2ne:
+ return new CSecurityRSAAES(cc, secTypeRA2ne, 128, false);
+ case secTypeRA256:
+ return new CSecurityRSAAES(cc, secTypeRA256, 256, true);
+ case secTypeRAne256:
+ return new CSecurityRSAAES(cc, secTypeRAne256, 256, false);
#endif
}
#ifdef HAVE_GNUTLS
#include <rfb/SSecurityTLS.h>
#endif
+#ifdef HAVE_NETTLE
+#include <rfb/SSecurityRSAAES.h>
+#endif
using namespace rdr;
using namespace rfb;
"Specify which security scheme to use (None, VncAuth, Plain"
#ifdef HAVE_GNUTLS
", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain"
+#endif
+#ifdef HAVE_NETTLE
+ ", RA2, RA2ne, RA2_256, RA2ne_256"
#endif
")",
#ifdef HAVE_GNUTLS
- "TLSVnc,VncAuth",
-#else
- "VncAuth",
+ "TLSVnc,"
#endif
+#ifdef HAVE_NETTLE
+ "RA2_256,RA2,RA2ne_256,RA2ne,"
+#endif
+ "VncAuth",
ConfServer);
SSecurity* SecurityServer::GetSSecurity(SConnection* sc, U32 secType)
return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false), new SSecurityVncAuth(sc));
case secTypeX509Plain:
return new SSecurityStack(sc, secTypeX509Plain, new SSecurityTLS(sc, false), new SSecurityPlain(sc));
+#endif
+#ifdef HAVE_NETTLE
+ case secTypeRA2:
+ return new SSecurityRSAAES(sc, secTypeRA2, 128, true);
+ case secTypeRA2ne:
+ return new SSecurityRSAAES(sc, secTypeRA2ne, 128, false);
+ case secTypeRA256:
+ return new SSecurityRSAAES(sc, secTypeRA256, 256, true);
+ case secTypeRAne256:
+ return new SSecurityRSAAES(sc, secTypeRAne256, 256, false);
#endif
}
.B \-SecurityTypes \fIsec-types\fP
Specify which security scheme to use for incoming connections. Valid values
are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP,
-\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP
-and \fBX509Plain\fP. Default is \fBVncAuth,TLSVnc\fP.
+\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP,
+\fBX509Plain\fP, \fBRA2\fP, \fBRA2ne\fP, \fBRA2_256\fP and \fBRA2ne_256\fP.
+Default is \fBVncAuth,RA2_256,RA2,RA2ne_256,RA2ne,TLSVnc\fP.
.
.TP
.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP
See the GnuTLS manual for possible values. Default is \fBNORMAL\fP.
.
.TP
+.B \-RSAKey \fIpath\fP
+Path to the RSA key for the RSA-AES security types (\fBRA2\fP, \fBRA2ne\fP,
+\fBRA2_256\fP and \fBRA2ne_256\fP) in PEM format.
+.
+.TP
+.B \-RequireUsername
+Require username for the RSA-AES security types. Default is off.
+.
+.TP
.B \-UseBlacklist
Temporarily reject connections from a host if it repeatedly fails to
authenticate. Default is on.
.B \-SecurityTypes \fIsec-types\fP
Specify which security scheme to use for incoming connections. Valid values
are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP,
-\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP
-and \fBX509Plain\fP. Default is \fBVncAuth,TLSVnc\fP.
+\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP,
+\fBX509Plain\fP, \fBRA2\fP, \fBRA2ne\fP, \fBRA2_256\fP and \fBRA2ne_256\fP.
+Default is \fBVncAuth,RA2_256,RA2,RA2ne_256,RA2ne,TLSVnc\fP.
.
.TP
.B \-Password \fIpassword\fP
of GnuTLS system-wide crypto policy will be used.
.
.TP
+.B \-RSAKey \fIpath\fP
+Path to the RSA key for the RSA-AES security types (\fBRA2\fP, \fBRA2ne\fP,
+\fBRA2_256\fP and \fBRA2ne_256\fP) in PEM format.
+.
+.TP
+.B \-RequireUsername
+Require username for the RSA-AES security types. Default is off.
+.
+.TP
.B \-UseBlacklist
Temporarily reject connections from a host if it repeatedly fails to
authenticate. Default is on.
#include <rdr/types.h>
#include <rfb/encodings.h>
-#ifdef HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
#include <rfb/Security.h>
#include <rfb/SecurityClient.h>
+#ifdef HAVE_GNUTLS
#include <rfb/CSecurityTLS.h>
#endif
+#endif
#include "OptionsDialog.h"
#include "fltk_layout.h"
handleCompression(compressionCheckbox, this);
handleJpeg(jpegCheckbox, this);
-#ifdef HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
/* Security */
Security security(SecurityClient::secTypes);
list<U32>::iterator iterExt;
encNoneCheckbox->value(false);
+#ifdef HAVE_GNUTLS
encTLSCheckbox->value(false);
encX509Checkbox->value(false);
+#endif
+#ifdef HAVE_NETTLE
+ encRSAAESCheckbox->value(false);
+#endif
authNoneCheckbox->value(false);
authVncCheckbox->value(false);
encNoneCheckbox->value(true);
authPlainCheckbox->value(true);
break;
+#ifdef HAVE_GNUTLS
case secTypeTLSNone:
encTLSCheckbox->value(true);
authNoneCheckbox->value(true);
encX509Checkbox->value(true);
authPlainCheckbox->value(true);
break;
+#endif
+#ifdef HAVE_NETTLE
+ case secTypeRA2:
+ case secTypeRA256:
+ encRSAAESCheckbox->value(true);
+ case secTypeRA2ne:
+ case secTypeRAne256:
+ authVncCheckbox->value(true);
+ authPlainCheckbox->value(true);
+ break;
+#endif
+
}
}
+#ifdef HAVE_GNUTLS
caInput->value(CSecurityTLS::X509CA);
crlInput->value(CSecurityTLS::X509CRL);
handleX509(encX509Checkbox, this);
+#endif
#endif
/* Input */
compressLevel.setParam(atoi(compressionInput->value()));
qualityLevel.setParam(atoi(jpegInput->value()));
-#ifdef HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
/* Security */
Security security;
if (encNoneCheckbox->value()) {
if (authNoneCheckbox->value())
security.EnableSecType(secTypeNone);
- if (authVncCheckbox->value())
+ if (authVncCheckbox->value()) {
security.EnableSecType(secTypeVncAuth);
- if (authPlainCheckbox->value())
+#ifdef HAVE_NETTLE
+ security.EnableSecType(secTypeRAne256);
+#endif
+ }
+ if (authPlainCheckbox->value()) {
security.EnableSecType(secTypePlain);
+#ifdef HAVE_NETTLE
+ security.EnableSecType(secTypeRA2ne);
+ security.EnableSecType(secTypeRAne256);
+#endif
+ }
}
+#ifdef HAVE_GNUTLS
/* Process security types which use TLS encryption */
if (encTLSCheckbox->value()) {
if (authNoneCheckbox->value())
security.EnableSecType(secTypeX509Plain);
}
- SecurityClient::secTypes.setParam(security.ToString());
-
CSecurityTLS::X509CA.setParam(caInput->value());
CSecurityTLS::X509CRL.setParam(crlInput->value());
#endif
+#ifdef HAVE_NETTLE
+ if (encRSAAESCheckbox->value()) {
+ security.EnableSecType(secTypeRA2);
+ security.EnableSecType(secTypeRA256);
+ }
+#endif
+ SecurityClient::secTypes.setParam(security.ToString());
+#endif
+
/* Input */
viewOnly.setParam(viewOnlyCheckbox->value());
emulateMiddleButton.setParam(emulateMBCheckbox->value());
void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
{
-#ifdef HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Security"));
int orig_tx;
/* Encryption */
ty += GROUP_LABEL_OFFSET;
+
+#if defined(HAVE_GNUTLS) && defined(HAVE_NETTLE)
+ height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 5 + CHECK_HEIGHT * 4 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2;
+#elif defined(HAVE_GNUTLS)
height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 4 + CHECK_HEIGHT * 3 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2;
+#elif defined(HAVE_NETTLE)
+ height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 1 + CHECK_HEIGHT * 2;
+#endif
encryptionGroup = new Fl_Group(tx, ty, width, height, _("Encryption"));
encryptionGroup->box(FL_ENGRAVED_BOX);
encryptionGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
_("None")));
ty += CHECK_HEIGHT + TIGHT_MARGIN;
+#ifdef HAVE_GNUTLS
encTLSCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
CHECK_MIN_WIDTH,
CHECK_HEIGHT,
_("Path to X509 CRL file"));
crlInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
ty += INPUT_HEIGHT + TIGHT_MARGIN;
+#endif
+#ifdef HAVE_NETTLE
+ encRSAAESCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
+ CHECK_MIN_WIDTH,
+ CHECK_HEIGHT,
+ _("RSA-AES")));
+ encRSAAESCheckbox->callback(handleRSAAES, this);
+ ty += CHECK_HEIGHT + TIGHT_MARGIN;
+#endif
}
ty += GROUP_MARGIN - TIGHT_MARGIN;
}
+void OptionsDialog::handleRSAAES(Fl_Widget *widget, void *data)
+{
+ OptionsDialog *dialog = (OptionsDialog*)data;
+
+ if (dialog->encRSAAESCheckbox->value()) {
+ dialog->authVncCheckbox->value(true);
+ dialog->authPlainCheckbox->value(true);
+ }
+}
+
+
void OptionsDialog::handleClipboard(Fl_Widget *widget, void *data)
{
#if !defined(WIN32) && !defined(__APPLE__)
static void handleJpeg(Fl_Widget *widget, void *data);
static void handleX509(Fl_Widget *widget, void *data);
+ static void handleRSAAES(Fl_Widget *widget, void *data);
static void handleClipboard(Fl_Widget *widget, void *data);
Fl_Check_Button *encNoneCheckbox;
Fl_Check_Button *encTLSCheckbox;
Fl_Check_Button *encX509Checkbox;
+ Fl_Check_Button *encRSAAESCheckbox;
Fl_Input *caInput;
Fl_Input *crlInput;
mkvnchomedir();
CSecurity::upg = &dlg;
-#ifdef HAVE_GNUTLS
- CSecurityTLS::msg = &dlg;
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
+ CSecurity::msg = &dlg;
#endif
Socket *sock = NULL;
Specify which security schemes to attempt to use when authenticating with
the server. Valid values are a comma separated list of \fBNone\fP,
\fBVncAuth\fP, \fBPlain\fP, \fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP,
-\fBX509None\fP, \fBX509Vnc\fP and \fBX509Plain\fP. Default is to attempt
+\fBX509None\fP, \fBX509Vnc\fP, \fBX509Plain\fP, \fBRA2\fP, \fBRA2ne\fP,
+\fBRA2_256\fP and \fBRA2ne_256\fP. Default is to attempt
every supported scheme.
.
.TP