]> source.dussan.org Git - tigervnc.git/commitdiff
Add support for RSA-AES security types 1519/head
authorpdlan <pengdinglan@gmail.com>
Thu, 1 Sep 2022 04:58:24 +0000 (00:58 -0400)
committerpdlan <pengdinglan@gmail.com>
Thu, 1 Sep 2022 04:58:24 +0000 (00:58 -0400)
26 files changed:
CMakeLists.txt
cmake/Modules/FindNettle.cmake [new file with mode: 0644]
cmake/StaticBuild.cmake
common/rdr/AESInStream.cxx [new file with mode: 0644]
common/rdr/AESInStream.h [new file with mode: 0644]
common/rdr/AESOutStream.cxx [new file with mode: 0644]
common/rdr/AESOutStream.h [new file with mode: 0644]
common/rdr/CMakeLists.txt
common/rfb/CMakeLists.txt
common/rfb/CSecurity.h
common/rfb/CSecurityRSAAES.cxx [new file with mode: 0644]
common/rfb/CSecurityRSAAES.h [new file with mode: 0644]
common/rfb/CSecurityTLS.h
common/rfb/SSecurityPlain.cxx
common/rfb/SSecurityRSAAES.cxx [new file with mode: 0644]
common/rfb/SSecurityRSAAES.h [new file with mode: 0644]
common/rfb/Security.cxx
common/rfb/Security.h
common/rfb/SecurityClient.cxx
common/rfb/SecurityServer.cxx
unix/x0vncserver/x0vncserver.man
unix/xserver/hw/vnc/Xvnc.man
vncviewer/OptionsDialog.cxx
vncviewer/OptionsDialog.h
vncviewer/vncviewer.cxx
vncviewer/vncviewer.man

index 24336402da76ddb3ce145af134e7f2a787d361a3..3dbb7309a24d5f251462dbf7e77239a4a5cca792 100644 (file)
@@ -275,6 +275,14 @@ if(ENABLE_GNUTLS)
   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)
diff --git a/cmake/Modules/FindNettle.cmake b/cmake/Modules/FindNettle.cmake
new file mode 100644 (file)
index 0000000..cd9b424
--- /dev/null
@@ -0,0 +1,20 @@
+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()
index 7ce219de47408d75eaecfa616fcfc0b0cfb6fcfc..1a3bdb46202b557be8be0e12c5fd6e704b5efd42 100644 (file)
@@ -119,6 +119,12 @@ if(BUILD_STATIC)
     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")
 
diff --git a/common/rdr/AESInStream.cxx b/common/rdr/AESInStream.cxx
new file mode 100644 (file)
index 0000000..e6737e6
--- /dev/null
@@ -0,0 +1,85 @@
+/* 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
diff --git a/common/rdr/AESInStream.h b/common/rdr/AESInStream.h
new file mode 100644 (file)
index 0000000..3483bd3
--- /dev/null
@@ -0,0 +1,49 @@
+/* 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
diff --git a/common/rdr/AESOutStream.cxx b/common/rdr/AESOutStream.cxx
new file mode 100644 (file)
index 0000000..1559b78
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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
diff --git a/common/rdr/AESOutStream.h b/common/rdr/AESOutStream.h
new file mode 100644 (file)
index 0000000..e5d6aa7
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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
index 08c93d8b933e17dda223d819ef011b9f697a0eae..96ffe163feb8c78b14b534687f1c52a0f9b98e40 100644 (file)
@@ -2,6 +2,8 @@ include_directories(${CMAKE_SOURCE_DIR}/common)
 include_directories(${ZLIB_INCLUDE_DIRS})
 
 add_library(rdr STATIC
+  AESInStream.cxx
+  AESOutStream.cxx
   BufferedInStream.cxx
   BufferedOutStream.cxx
   Exception.cxx
@@ -23,6 +25,10 @@ if(GNUTLS_FOUND)
   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()
index 069664103f2e75da43a18d9d7c13001eba33b2b2..914dd894a176c6ea9244ecb940584eb8bb9a8c20 100644 (file)
@@ -102,6 +102,12 @@ if(GNUTLS_FOUND)
   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()
index 476f8770f4878549af83e175a5c4ec7977ee07d8..549db7940789733eca38e3122fd12df7b71852e8 100644 (file)
@@ -39,6 +39,7 @@
 #define __RFB_CSECURITY_H__
 
 #include <rfb/UserPasswdGetter.h>
+#include <rfb/UserMsgBox.h>
 
 namespace rfb {
   class CConnection;
@@ -55,6 +56,7 @@ namespace rfb {
      * It MUST be set by viewer.
      */
     static UserPasswdGetter *upg;
+    static UserMsgBox *msg;
 
   protected:
     CConnection* cc;
diff --git a/common/rfb/CSecurityRSAAES.cxx b/common/rfb/CSecurityRSAAES.cxx
new file mode 100644 (file)
index 0000000..db6e43a
--- /dev/null
@@ -0,0 +1,461 @@
+/* 
+ * 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
diff --git a/common/rfb/CSecurityRSAAES.h b/common/rfb/CSecurityRSAAES.h
new file mode 100644 (file)
index 0000000..f58bb87
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+ * 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
index b2d68b6f4237a0cb5b26915d3aae55848275bcd7..fb317748e221ad06f13dc5b59126742015fa54e3 100644 (file)
@@ -34,7 +34,6 @@
 #include <gnutls/gnutls.h>
 
 namespace rfb {
-  class UserMsgBox;
   class CSecurityTLS : public CSecurity {
   public:
     CSecurityTLS(CConnection* cc, bool _anon);
@@ -45,7 +44,6 @@ namespace rfb {
 
     static StringParameter X509CA;
     static StringParameter X509CRL;
-    static UserMsgBox *msg;
 
   protected:
     void shutdown();
index 6ae19557129e30bd77bed8d952af830083f34d3d..6f65e87a72b5b4d826ac5e36239dc5640e150762 100644 (file)
@@ -36,7 +36,11 @@ using namespace rfb;
 
 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)
diff --git a/common/rfb/SSecurityRSAAES.cxx b/common/rfb/SSecurityRSAAES.cxx
new file mode 100644 (file)
index 0000000..15d2e97
--- /dev/null
@@ -0,0 +1,598 @@
+/* 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;
+}
diff --git a/common/rfb/SSecurityRSAAES.h b/common/rfb/SSecurityRSAAES.h
new file mode 100644 (file)
index 0000000..17e0d40
--- /dev/null
@@ -0,0 +1,98 @@
+/* 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
index 59deb78d3f6884126c808d88b5aa219414bc5dde..3ae4e0934d1c19845582370fdffd01a8d04c8233 100644 (file)
@@ -160,6 +160,8 @@ rdr::U32 rfb::secTypeNum(const char* name)
   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;
@@ -184,6 +186,8 @@ const char* rfb::secTypeName(rdr::U32 num)
   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";
index a4987f5f82e7cb97a42049ab0e162643d785519a..03a2218fee96a7a08d59a2d56ecdb9affa2c8009 100644 (file)
@@ -43,6 +43,9 @@ namespace rfb {
   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;
@@ -52,6 +55,10 @@ namespace rfb {
   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;
index dcd28bae1609cee369bc0caed58c5ac2ed5e79e0..b2e974f8316ac37e14574d395568aa450ed187dd 100644 (file)
 #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
@@ -46,20 +49,25 @@ 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))
@@ -93,6 +101,16 @@ CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 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
   }
 
index 97b133c7c6fc5db235a6a0266ae7fcb1419b60d3..04022a906ffa0ab9fd7ff08517f8f2fc0f9cb018 100644 (file)
@@ -31,6 +31,9 @@
 #ifdef HAVE_GNUTLS
 #include <rfb/SSecurityTLS.h>
 #endif
+#ifdef HAVE_NETTLE
+#include <rfb/SSecurityRSAAES.h>
+#endif
 
 using namespace rdr;
 using namespace rfb;
@@ -40,13 +43,18 @@ StringParameter SecurityServer::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
- "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)
@@ -72,6 +80,16 @@ 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
   }
 
index 713f9e3d8a198ddff8bf44e2881c2e17acb8fd4d..9575fc7d03335904c2a6e1a97e075472c10ea619 100644 (file)
@@ -104,8 +104,9 @@ Default is to accept connections from any IP address.
 .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
@@ -148,6 +149,15 @@ GnuTLS priority string that controls the TLS session’s handshake algorithms.
 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.
index b3837cf8670d2d4a02e78fd400bffc6678a29357..4735cf5cd97ede9941aaa391041a73b6e57369c4 100644 (file)
@@ -186,8 +186,9 @@ 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
@@ -225,6 +226,15 @@ value will be \fBNORMAL\fP to use upstream default. For newer versions
 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.
index 535e436161589cfda8437e32694973560832b825..2a904dd8584a492593ed122ac4d8e215ff8d9677 100644 (file)
 #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"
@@ -203,7 +205,7 @@ void OptionsDialog::loadOptions(void)
   handleCompression(compressionCheckbox, this);
   handleJpeg(jpegCheckbox, this);
 
-#ifdef HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
   /* Security */
   Security security(SecurityClient::secTypes);
 
@@ -214,8 +216,13 @@ void OptionsDialog::loadOptions(void)
    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);
@@ -242,6 +249,7 @@ void OptionsDialog::loadOptions(void)
       encNoneCheckbox->value(true);
       authPlainCheckbox->value(true);
       break;
+#ifdef HAVE_GNUTLS
     case secTypeTLSNone:
       encTLSCheckbox->value(true);
       authNoneCheckbox->value(true);
@@ -266,13 +274,27 @@ void OptionsDialog::loadOptions(void)
       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 */
@@ -352,7 +374,7 @@ void OptionsDialog::storeOptions(void)
   compressLevel.setParam(atoi(compressionInput->value()));
   qualityLevel.setParam(atoi(jpegInput->value()));
 
-#ifdef HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
   /* Security */
   Security security;
 
@@ -360,12 +382,22 @@ void OptionsDialog::storeOptions(void)
   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())
@@ -386,12 +418,19 @@ void OptionsDialog::storeOptions(void)
       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());
@@ -611,7 +650,7 @@ void OptionsDialog::createCompressionPage(int tx, int ty, int tw, int th)
 
 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;
@@ -626,7 +665,14 @@ void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
 
   /* 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);
@@ -641,6 +687,7 @@ void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
                                                    _("None")));
     ty += CHECK_HEIGHT + TIGHT_MARGIN;
 
+#ifdef HAVE_GNUTLS
     encTLSCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                   CHECK_MIN_WIDTH,
                                                   CHECK_HEIGHT,
@@ -667,6 +714,15 @@ void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
                             _("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;
@@ -1023,6 +1079,17 @@ void OptionsDialog::handleX509(Fl_Widget *widget, void *data)
 }
 
 
+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__)
index 42c075ed4317696ab0685614783bba6e3dbcc77b..14cfe619388d8eed903474e6638303d6ad91caa5 100644 (file)
@@ -62,6 +62,7 @@ protected:
   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);
 
@@ -101,6 +102,7 @@ protected:
   Fl_Check_Button *encNoneCheckbox;
   Fl_Check_Button *encTLSCheckbox;
   Fl_Check_Button *encX509Checkbox;
+  Fl_Check_Button *encRSAAESCheckbox;
   Fl_Input *caInput;
   Fl_Input *crlInput;
 
index 4afdba4613cac35260dff1d8b5779f3d8e1e9576..3f30fadafe5c5b2cb25bdfefb9de18934c8a3795 100644 (file)
@@ -754,8 +754,8 @@ int main(int argc, char** argv)
   mkvnchomedir();
 
   CSecurity::upg = &dlg;
-#ifdef HAVE_GNUTLS
-  CSecurityTLS::msg = &dlg;
+#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
+  CSecurity::msg = &dlg;
 #endif
 
   Socket *sock = NULL;
index 8c8b3b9092623fcfd1d78f45899d4bb0e54fce55..928896a13fb80820013cce46b8529c6e0bc9287d 100644 (file)
@@ -139,7 +139,8 @@ Xvnc supports reverse connections with a helper program called
 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