--- /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import java.util.Arrays;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.InvalidAlgorithmParameterException;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+public class AESEAXCipher {
+
+ private static final byte[] zeroBlock = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ private static final byte[] prefixBlock0 = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ private static final byte[] prefixBlock1 = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
+ private static final byte[] prefixBlock2 = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2};
+ private static final int[] lut = {0x0,0x87,0x0e,0x89};
+
+ public AESEAXCipher(byte[] key)
+ {
+ try {
+ Cipher blockCipher = Cipher.getInstance("AES");
+ cbcCipher = Cipher.getInstance("AES/CBC/NOPADDING");
+ ctrCipher = Cipher.getInstance("AES/CTR/NOPADDING");
+ keySpec = new SecretKeySpec(key, "AES");
+ blockCipher.init(Cipher.ENCRYPT_MODE, keySpec);
+ cbcCipher.init(Cipher.ENCRYPT_MODE, keySpec,
+ new IvParameterSpec(zeroBlock));
+ subKey1 = Arrays.copyOfRange(blockCipher.doFinal(zeroBlock), 0, 16);
+ subKey2 = new byte[16];
+ int v = (subKey1[0] & 0xff) >>> 6;
+ for (int i = 0; i < 15; i++) {
+ subKey2[i] = (byte)(((subKey1[i + 1] & 0xff) >>> 6) |
+ ((subKey1[i] & 0xff) << 2));
+ subKey1[i] = (byte)(((subKey1[i + 1] & 0xff) >>> 7) |
+ ((subKey1[i] & 0xff) << 1));
+ }
+ subKey2[14] ^= v >>> 1;
+ subKey2[15] = (byte)(((subKey1[15] & 0xff) << 2) ^ lut[v]);
+ subKey1[15] = (byte)(((subKey1[15] & 0xff) << 1) ^ lut[v >>> 1]);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new Exception("AESEAXCipher: AES algorithm is not supported");
+ } catch (IllegalBlockSizeException | BadPaddingException |
+ InvalidKeyException | InvalidAlgorithmParameterException e) {
+ throw new Exception("AESEAXCipher: invalid key");
+ }
+ }
+
+ private void encryptCTR(byte[] input, int inputOffset, int inputLength,
+ byte[] output, int outputOffset, byte[] iv)
+ {
+ try {
+ ctrCipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
+ ctrCipher.doFinal(input, inputOffset, inputLength, output, outputOffset);
+ } catch (java.lang.Exception e) {
+ throw new Exception("AESEAXCipher: " + e.toString());
+ }
+ }
+
+ private byte[] computeCMAC(byte[] input, int offset,
+ int length, byte[] prefix)
+ {
+ int n = length / 16;
+ int m = (length + 15) / 16;
+ int r = length - n * 16;
+ byte[] cbcData = new byte[(m + 1) * 16];
+ System.arraycopy(prefix, 0, cbcData, 0, 16);
+ System.arraycopy(input, offset, cbcData, 16, length);
+
+ if (r == 0) {
+ for (int i = 0; i < 16; i++) {
+ cbcData[n * 16 + i] ^= subKey1[i] & 0xff;
+ }
+ } else {
+ cbcData[(n + 1) * 16 + r] = (byte)0x80;
+ for (int i = 0; i < 16; i++) {
+ cbcData[(n + 1) * 16 + i] ^= subKey2[i] & 0xff;
+ }
+ }
+ try {
+ byte[] encrypted = cbcCipher.doFinal(cbcData);
+ return Arrays.copyOfRange(encrypted, encrypted.length - 16, encrypted.length - 0);
+ } catch (java.lang.Exception e) {
+ throw new Exception("AESEAXCipher: " + e.getMessage());
+ }
+ }
+
+ public void encrypt(byte[] input, int inputOffset, int inputLength,
+ byte[] ad, int adOffset, int adLength,
+ byte[] nonce,
+ byte[] output, int outputOffset,
+ byte[] mac, int macOffset)
+ {
+ byte[] nCMAC = computeCMAC(nonce, 0, nonce.length, prefixBlock0);
+ encryptCTR(input, inputOffset, inputLength, output, outputOffset, nCMAC);
+ byte[] adCMAC = computeCMAC(ad, adOffset, adLength, prefixBlock1);
+ byte[] m = computeCMAC(output, outputOffset, inputLength, prefixBlock2);
+ for (int i = 0; i < 16; i++) {
+ mac[macOffset + i] = (byte)((m[i] & 0xff) ^
+ (nCMAC[i] & 0xff) ^
+ (adCMAC[i] & 0xff));
+ }
+ }
+
+ public void decrypt(byte[] input, int inputOffset, int inputLength,
+ byte[] ad, int adOffset, int adLength,
+ byte[] nonce,
+ byte[] output, int outputOffset,
+ byte[] mac, int macOffset)
+ {
+ byte[] nCMAC = computeCMAC(nonce, 0, nonce.length, prefixBlock0);
+ byte[] adCMAC = computeCMAC(ad, adOffset, adLength, prefixBlock1);
+ byte[] m = computeCMAC(input, inputOffset, inputLength, prefixBlock2);
+ for (int i = 0; i < 16; i++) {
+ byte x = (byte)((m[i] & 0xff) ^ (nCMAC[i] & 0xff) ^ (adCMAC[i] & 0xff));
+ if (x != mac[macOffset + i])
+ throw new Exception("AESEAXCipher: failed to authenticate message");
+ }
+ encryptCTR(input, inputOffset, inputLength, output, outputOffset, nCMAC);
+ }
+
+ private SecretKeySpec keySpec;
+ private Cipher ctrCipher;
+ private Cipher cbcCipher;
+ private byte[] subKey1;
+ private byte[] subKey2;
+}
--- /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import java.nio.ByteBuffer;
+
+public class AESInStream extends InStream {
+
+ private static final int maxMessageSize = 65536;
+
+ public AESInStream(InStream _in, byte[] key)
+ {
+ in = _in;
+ offset = 0;
+ bufSize = maxMessageSize;
+ b = new byte[bufSize];
+ ptr = end = start = 0;
+ cipher = new AESEAXCipher(key);
+ messageSize = 0;
+ messageOffset = 0;
+ message = new byte[maxMessageSize + 16];
+ decryptedMessageOffset = 0;
+ decryptedMessage = new byte[maxMessageSize];
+ counter = new byte[16];
+ state = 0;
+ }
+
+ public final int pos()
+ {
+ return offset + ptr - start;
+ }
+
+ protected final int overrun(int itemSize, int nItems, boolean wait)
+ {
+ if (itemSize > bufSize)
+ throw new Exception("AESInStream overrun: max itemSize exceeded");
+
+ if (end - ptr != 0)
+ System.arraycopy(b, ptr, b, 0, end - ptr);
+
+ offset += ptr - start;
+ end -= ptr - start;
+ ptr = start;
+
+ while ((end - start) < itemSize) {
+ int n = readMessage(b, end, start + bufSize - end, wait);
+ if (!wait && n == 0)
+ return 0;
+ end += n;
+ }
+
+ int nAvail;
+ nAvail = (end - ptr) / itemSize;
+ if (nAvail < nItems)
+ return nAvail;
+
+ return nItems;
+ }
+
+ private int readMessage(byte[] buf, int bufPtr, int len, boolean wait)
+ {
+ if (state == 0 || state == 1) {
+ if (!fillDecryptedMessageBuffer(wait) && !wait)
+ return 0;
+ }
+ if (state == 2) {
+ int readSize = messageSize - decryptedMessageOffset;
+ if (readSize > len)
+ readSize = len;
+ System.arraycopy(decryptedMessage, decryptedMessageOffset,
+ buf, bufPtr, readSize);
+ decryptedMessageOffset += readSize;
+ if (decryptedMessageOffset == messageSize)
+ state = 0;
+ return readSize;
+ }
+ return 0;
+ }
+
+ private boolean fillDecryptedMessageBuffer(boolean wait)
+ {
+ if (state == 0) {
+ while (true) {
+ if (in.check(2, 1, wait) != 0) {
+ messageSize = in.readU16();
+ messageOffset = 0;
+ state = 1;
+ break;
+ } else if (!wait) {
+ return false;
+ }
+ }
+ }
+ if (state == 1) {
+ if (wait) {
+ in.readBytes(ByteBuffer.wrap(message, messageOffset,
+ messageSize + 16 - messageOffset),
+ messageSize + 16 - messageOffset);
+ } else {
+ while (true) {
+ int readSize = messageSize + 16 - messageOffset;
+ if (in.check(1, readSize, false) != 0) {
+ int availSize = in.getend() - in.getptr();
+ if (readSize > availSize)
+ readSize = availSize;
+ in.readBytes(ByteBuffer.wrap(message, messageOffset, readSize),
+ readSize);
+ messageOffset += readSize;
+ if (messageSize + 16 == messageOffset) {
+ break;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ byte[] ad = new byte[] {
+ (byte)((messageSize & 0xff00) >> 8),
+ (byte)(messageSize & 0xff)
+ };
+ cipher.decrypt(message, 0, messageSize,
+ ad, 0, 2,
+ counter,
+ decryptedMessage, 0,
+ message, messageSize);
+ // 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;
+ }
+ }
+ decryptedMessageOffset = 0;
+ state = 2;
+ return true;
+ }
+
+
+ private AESEAXCipher cipher;
+ private int offset;
+ private int start;
+ private int bufSize;
+ private int state;
+ private int messageSize;
+ private int messageOffset;
+ private byte[] message;
+ private int decryptedMessageOffset;
+ private byte[] decryptedMessage;
+ private byte[] counter;
+ private InStream in;
+}
--- /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import java.nio.ByteBuffer;
+
+public class AESOutStream extends OutStream {
+
+ static final int maxMessageSize = 8192;
+
+ public AESOutStream(OutStream _out, byte[] key)
+ {
+ out = _out;
+ bufSize = maxMessageSize;
+ b = new byte[bufSize];
+ buffer = new byte[bufSize + 16 + 2];
+ ptr = offset = start = 0;
+ end = start + bufSize;
+ cipher = new AESEAXCipher(key);
+ counter = new byte[16];
+ }
+
+ public int length()
+ {
+ return offset + ptr - start;
+ }
+
+ public void flush()
+ {
+ int sentUpTo = start;
+ while (sentUpTo < ptr) {
+ int n = writeMessage(b, sentUpTo, ptr - sentUpTo);
+ sentUpTo += n;
+ offset += n;
+ }
+
+ ptr = start;
+ }
+
+ protected int overrun(int itemSize, int nItems)
+ {
+ if (itemSize > bufSize)
+ throw new Exception("AESOutStream overrun: max itemSize exceeded");
+
+ flush();
+
+ int nAvail;
+ nAvail = (end - ptr) / itemSize;
+ if (nAvail < nItems)
+ return nAvail;
+
+ return nItems;
+ }
+
+ protected int writeMessage(byte[] data, int dataPtr, int length)
+ {
+ if (length == 0)
+ return 0;
+ buffer[0] = (byte)((length & 0xff00) >> 8);
+ buffer[1] = (byte)(length & 0xff);
+ cipher.encrypt(data, dataPtr, length,
+ buffer, 0, 2,
+ counter,
+ buffer, 2,
+ buffer, 2 + length);
+ out.writeBytes(ByteBuffer.wrap(buffer, 0, length + 16 + 2),
+ length + 16 + 2);
+ 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;
+ }
+ }
+ return length;
+ }
+
+ private AESEAXCipher cipher;
+ private int start;
+ private int offset;
+ private int bufSize;
+ private byte[] buffer;
+ private byte[] counter;
+ private OutStream out;
+}
* It MUST be set by viewer.
*/
public static UserPasswdGetter upg;
+ public static UserMsgBox msg;
}
--- /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+package com.tigervnc.rfb;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.swing.JOptionPane;
+
+import com.tigervnc.rdr.*;
+import com.tigervnc.vncviewer.*;
+
+public class CSecurityRSAAES extends CSecurity {
+
+ private static final int MinKeyLength = 1024;
+ private static final int MaxKeyLength = 8192;
+
+ private static final byte[] bigIntToBytes(BigInteger n, int bytes) {
+ int bits = n.bitCount();
+ byte[] arr = n.toByteArray();
+ //int len = bits % 8 == 0 ? arr.length - 1 : arr.length;
+ //System.out.printf("%d %d %d\n", bits, len, bytes);
+ //if (len > bytes)
+ // return null;
+ int len = arr.length < bytes ? arr.length : bytes;
+ byte[] res = new byte[bytes];
+ System.arraycopy(arr, arr.length - len, res, bytes - len, len);
+ return res;
+ }
+
+ public CSecurityRSAAES(int secType, int keySize, boolean isAllEncrypted) {
+ this.secType = secType;
+ this.keySize = keySize;
+ this.isAllEncrypted = isAllEncrypted;
+ }
+
+ public boolean processMsg(CConnection cc) {
+ readPubclicKey(cc);
+ verifyServer();
+ writePublicKey(cc);
+ writeRandom(cc);
+ readRandom(cc);
+ setCipher(cc);
+ writeHash(cc);
+ readHash();
+ readSubtype();
+ writeCredentials();
+ return true;
+ }
+
+ private void readPubclicKey(CConnection cc) {
+ InStream is = cc.getInStream();
+ serverKeyLength = is.readU32();
+ if (serverKeyLength < MinKeyLength)
+ throw new AuthFailureException("server key is too short");
+ if (serverKeyLength > MaxKeyLength)
+ throw new AuthFailureException("server key is too long");
+ int size = (serverKeyLength + 7) / 8;
+ serverKeyN = new byte[size];
+ serverKeyE = new byte[size];
+ is.readBytes(ByteBuffer.wrap(serverKeyN), size);
+ is.readBytes(ByteBuffer.wrap(serverKeyE), size);
+ BigInteger modulus = new BigInteger(1, serverKeyN);
+ BigInteger publicExponent = new BigInteger(1, serverKeyE);
+ RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
+ try {
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ serverKey = factory.generatePublic(spec);
+ } catch (NoSuchAlgorithmException e) {
+ throw new AuthFailureException("RSA algorithm is not supported");
+ } catch (InvalidKeySpecException e) {
+ throw new AuthFailureException("server key is invalid");
+ }
+ }
+
+ private void verifyServer() {
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AuthFailureException("SHA-1 algorithm is not supported");
+ }
+ byte[] length = new byte[4];
+ length[0] = (byte) ((serverKeyLength & 0xff000000) >> 24);
+ length[1] = (byte) ((serverKeyLength & 0xff0000) >> 16);
+ length[2] = (byte) ((serverKeyLength & 0xff00) >> 8);
+ length[3] = (byte) (serverKeyLength & 0xff);
+ digest.update(length);
+ digest.update(serverKeyN);
+ digest.update(serverKeyE);
+ byte[] f = digest.digest();
+ String title = "Server key fingerprint";
+ String text = String.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(JOptionPane.YES_NO_OPTION, title, text))
+ throw new AuthFailureException("server key mismatch");
+ }
+
+ private void writePublicKey(CConnection cc) {
+ OutStream os = cc.getOutStream();
+ clientKeyLength = serverKeyLength;
+ KeyPairGenerator kpg;
+ try {
+ kpg = KeyPairGenerator.getInstance("RSA");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AuthFailureException("RSA algorithm is not supported");
+ }
+ kpg.initialize(clientKeyLength);
+ KeyPair kp = kpg.generateKeyPair();
+ clientKey = kp.getPrivate();
+ clientPublicKey = kp.getPublic();
+ RSAPublicKey rsaKey = (RSAPublicKey) clientPublicKey;
+ BigInteger modulus = rsaKey.getModulus();
+ BigInteger publicExponent = rsaKey.getPublicExponent();
+
+ clientKeyN = bigIntToBytes(modulus, (clientKeyLength + 7) / 8);
+ clientKeyE = bigIntToBytes(publicExponent, (clientKeyLength + 7) / 8);
+ if (clientKeyN == null || clientKeyN == null) {
+ throw new AuthFailureException("failed to generate RSA keys");
+ }
+ os.writeU32(clientKeyLength);
+ os.writeBytes(clientKeyN, 0, clientKeyN.length);
+ os.writeBytes(clientKeyE, 0, clientKeyE.length);
+ os.flush();
+ }
+
+ private void writeRandom(CConnection cc) {
+ OutStream os = cc.getOutStream();
+ SecureRandom sr = new SecureRandom();
+ clientRandom = new byte[keySize / 8];
+ sr.nextBytes(clientRandom);
+ byte[] encrypted;
+ try {
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(Cipher.ENCRYPT_MODE, serverKey);
+ encrypted = cipher.doFinal(clientRandom);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new AuthFailureException("RSA algorithm is not supported");
+ } catch (InvalidKeyException | IllegalBlockSizeException |
+ BadPaddingException e) {
+ throw new AuthFailureException("failed to encrypt random");
+ }
+ os.writeU16(encrypted.length);
+ os.writeBytes(encrypted, 0, encrypted.length);
+ os.flush();
+ }
+
+ private void readRandom(CConnection cc) {
+ InStream is = cc.getInStream();
+ int size = is.readU16();
+ if (size != clientKeyN.length)
+ throw new AuthFailureException("client key length doesn't match");
+ byte[] buffer = new byte[size];
+ is.readBytes(ByteBuffer.wrap(buffer), size);
+ try {
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(Cipher.DECRYPT_MODE, clientKey);
+ serverRandom = cipher.doFinal(buffer);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new AuthFailureException("RSA algorithm is not supported");
+ } catch (InvalidKeyException | IllegalBlockSizeException |
+ BadPaddingException e) {
+ System.out.println(e.getMessage());
+ throw new AuthFailureException("failed to decrypt server random");
+ }
+ if (serverRandom.length != keySize / 8)
+ throw new AuthFailureException("server random length doesn't match");
+ }
+
+ private void setCipher(CConnection cc) {
+ rawis = cc.getInStream();
+ rawos = cc.getOutStream();
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance(keySize == 128 ? "SHA-1" : "SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AuthFailureException("hash algorithm is not supported");
+ }
+ digest.update(clientRandom);
+ digest.update(serverRandom);
+ byte[] key = Arrays.copyOfRange(digest.digest(), 0, keySize / 8);
+ rais = new AESInStream(rawis, key);
+ digest.reset();
+ digest.update(serverRandom);
+ digest.update(clientRandom);
+ key = Arrays.copyOfRange(digest.digest(), 0, keySize / 8);
+ raos = new AESOutStream(rawos, key);
+ if (isAllEncrypted)
+ cc.setStreams(rais, raos);
+ }
+
+ private void writeHash(CConnection cc) {
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance(keySize == 128 ? "SHA-1" : "SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AuthFailureException("hash algorithm is not supported");
+ }
+ int len = serverKeyLength;
+ byte[] lenServerKey = new byte[] {
+ (byte) ((len & 0xff000000) >> 24),
+ (byte) ((len & 0xff0000) >> 16),
+ (byte) ((len & 0xff00) >> 8),
+ (byte) (len & 0xff)
+ };
+ len = clientKeyLength;
+ byte[] lenClientKey = new byte[] {
+ (byte) ((len & 0xff000000) >> 24),
+ (byte) ((len & 0xff0000) >> 16),
+ (byte) ((len & 0xff00) >> 8),
+ (byte) (len & 0xff)
+ };
+ digest.update(lenClientKey);
+ digest.update(clientKeyN);
+ digest.update(clientKeyE);
+ digest.update(lenServerKey);
+ digest.update(serverKeyN);
+ digest.update(serverKeyE);
+ byte[] hash = digest.digest();
+ raos.writeBytes(hash, 0, hash.length);
+ raos.flush();
+ }
+
+ void readHash() {
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance(keySize == 128 ? "SHA-1" : "SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AuthFailureException("hash algorithm is not supported");
+ }
+ int len = serverKeyLength;
+ byte[] lenServerKey = new byte[] {
+ (byte)((len & 0xff000000) >> 24),
+ (byte)((len & 0xff0000) >> 16),
+ (byte)((len & 0xff00) >> 8),
+ (byte)(len & 0xff)
+ };
+ len = clientKeyLength;
+ byte[] lenClientKey = new byte[] {
+ (byte)((len & 0xff000000) >> 24),
+ (byte)((len & 0xff0000) >> 16),
+ (byte)((len & 0xff00) >> 8),
+ (byte)(len & 0xff)
+ };
+ digest.update(lenServerKey);
+ digest.update(serverKeyN);
+ digest.update(serverKeyE);
+ digest.update(lenClientKey);
+ digest.update(clientKeyN);
+ digest.update(clientKeyE);
+ byte[] realHash = digest.digest();
+ ByteBuffer hash = ByteBuffer.allocate(realHash.length);
+ rais.readBytes(hash, realHash.length);
+ if (!Arrays.equals(hash.array(), realHash)) {
+ throw new AuthFailureException("hash doesn't match");
+ }
+ }
+
+ private void readSubtype() {
+ subtype = rais.readU8();
+ if (subtype != Security.secTypeRA2UserPass &&
+ subtype != Security.secTypeRA2Pass)
+ throw new AuthFailureException("unknown RSA-AES subtype");
+ }
+
+ private void writeCredentials() {
+ StringBuffer username = new StringBuffer();
+ StringBuffer password = new StringBuffer();
+ CSecurity.upg.getUserPasswd(secType == Security.secTypeRA256,
+ subtype == Security.secTypeRA2UserPass ?
+ username : null,
+ password);
+ if (username.length() > 255)
+ throw new AuthFailureException("username is too long");
+ byte[] usernameBytes;
+ try {
+ usernameBytes = username.toString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new AuthFailureException("UTF-8 is not supported");
+ }
+ raos.writeU8(usernameBytes.length);
+ if (usernameBytes.length != 0)
+ raos.writeBytes(usernameBytes, 0, usernameBytes.length);
+ if (password.length() > 255)
+ throw new AuthFailureException("password is too long");
+ byte[] passwordBytes;
+ try {
+ passwordBytes = password.toString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new AuthFailureException("UTF-8 is not supported");
+ }
+ raos.writeU8(passwordBytes.length);
+ if (passwordBytes.length != 0)
+ raos.writeBytes(passwordBytes, 0, passwordBytes.length);
+ raos.flush();
+ }
+
+ public int getType() {
+ return secType;
+ }
+
+ public String description() {
+ return "RSA-ASE security types";
+ }
+
+ private int secType;
+ private int subtype;
+ private int keySize;
+ private boolean isAllEncrypted;
+
+ private PrivateKey clientKey;
+ private PublicKey clientPublicKey;
+ private PublicKey serverKey;
+ private int serverKeyLength;
+ private byte[] serverKeyN;
+ private byte[] serverKeyE;
+ private int clientKeyLength;
+ private byte[] clientKeyN;
+ private byte[] clientKeyE;
+ private byte[] serverRandom;
+ private byte[] clientRandom;
+
+ private AESInStream rais;
+ private AESOutStream raos;
+ private InStream rawis;
+ private OutStream rawos;
+}
public static StringParameter X509CRL
= new StringParameter("X509CRL",
"X509 CRL file", "", Configuration.ConfigurationObject.ConfViewer);
- public static UserMsgBox msg;
private void initGlobal()
{
public static final int secTypeTLS = 18;
public static final int secTypeVeNCrypt = 19;
+ public static final int secTypeRA256 = 129;
+ public static final int secTypeRAne256 = 130;
+
/* VeNCrypt subtypes */
public static final int secTypePlain = 256;
public static final int secTypeTLSNone = 257;
public static final int secTypeTLSIdent = 266;
public static final int secTypeX509Ident = 267;
+ public static final int secTypeRA2UserPass = 1;
+ public static final int secTypeRA2Pass = 2;
+
// result types
public static final int secResultOK = 0;
if (name.equalsIgnoreCase("Tight")) return secTypeTight;
if (name.equalsIgnoreCase("RA2")) return secTypeRA2;
if (name.equalsIgnoreCase("RA2ne")) return secTypeRA2ne;
+ if (name.equalsIgnoreCase("RA2_256")) return secTypeRA256;
+ if (name.equalsIgnoreCase("RA2ne_256")) return secTypeRAne256;
if (name.equalsIgnoreCase("SSPI")) return secTypeSSPI;
if (name.equalsIgnoreCase("SSPIne")) return secTypeSSPIne;
//if (name.equalsIgnoreCase("ultra")) return secTypeUltra;
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 secTypeUltra: return "Ultra";
case Security.secTypeX509Ident:
return (new CSecurityStack(secTypeX509Ident, "X509 with username only",
new CSecurityTLS(false), new CSecurityIdent()));
+ case Security.secTypeRA2:
+ return (new CSecurityRSAAES(secType, 128, true));
+ case Security.secTypeRA2ne:
+ return (new CSecurityRSAAES(secType, 128, false));
+ case Security.secTypeRA256:
+ return (new CSecurityRSAAES(secType, 256, true));
+ case Security.secTypeRAne256:
+ return (new CSecurityRSAAES(secType, 256, false));
default:
throw new Exception("Security type not supported");
}
public static StringParameter secTypes
= new StringParameter("SecurityTypes",
- "Specify which security scheme to use (None, VncAuth, Plain, Ident, TLSNone, TLSVnc, TLSPlain, TLSIdent, X509None, X509Vnc, X509Plain, X509Ident)",
- "X509Ident,X509Plain,TLSIdent,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,Ident,VncAuth,None", Configuration.ConfigurationObject.ConfViewer);
+ "Specify which security scheme to use (None, VncAuth, Plain, Ident, TLSNone, TLSVnc, TLSPlain, TLSIdent, X509None, X509Vnc, X509Plain, X509Ident, RA2, RA2ne, RA2_256, RA2ne_256)",
+ "X509Ident,X509Plain,TLSIdent,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,Ident,RA2_256,RA2,RA2ne_256,RA2ne,VncAuth,None", Configuration.ConfigurationObject.ConfViewer);
}
JCheckBox encNoneCheckbox;
JCheckBox encTLSCheckbox;
JCheckBox encX509Checkbox;
+ JCheckBox encRSAAESCheckbox;
JTextField caInput;
JTextField crlInput;
JButton caChooser;
encNoneCheckbox.setSelected(false);
encTLSCheckbox.setSelected(false);
encX509Checkbox.setSelected(false);
+ encRSAAESCheckbox.setSelected(false);
authNoneCheckbox.setSelected(false);
authVncCheckbox.setSelected(false);
encX509Checkbox.setSelected(true);
authIdentCheckbox.setSelected(true);
break;
+ case Security.secTypeRA2:
+ case Security.secTypeRA256:
+ encRSAAESCheckbox.setSelected(true);
+ case Security.secTypeRA2ne:
+ case Security.secTypeRAne256:
+ authVncCheckbox.setSelected(true);
+ authPlainCheckbox.setSelected(true);
+ break;
}
}
if (encNoneCheckbox.isSelected()) {
if (authNoneCheckbox.isSelected())
security.EnableSecType(Security.secTypeNone);
- if (authVncCheckbox.isSelected())
+ if (authVncCheckbox.isSelected()) {
security.EnableSecType(Security.secTypeVncAuth);
- if (authPlainCheckbox.isSelected())
+ security.EnableSecType(Security.secTypeRA2ne);
+ security.EnableSecType(Security.secTypeRAne256);
+ }
+ if (authPlainCheckbox.isSelected()) {
security.EnableSecType(Security.secTypePlain);
+ security.EnableSecType(Security.secTypeRA2ne);
+ security.EnableSecType(Security.secTypeRAne256);
+ }
if (authIdentCheckbox.isSelected())
security.EnableSecType(Security.secTypeIdent);
}
security.EnableSecType(Security.secTypeX509Ident);
}
+ if (encRSAAESCheckbox.isSelected()) {
+ security.EnableSecType(Security.secTypeRA2);
+ security.EnableSecType(Security.secTypeRA256);
+ }
+
if (authIdentCheckbox.isSelected() ||
authPlainCheckbox.isSelected()) {
sendLocalUsername.setParam(sendLocalUsernameCheckbox.isSelected());
crlInput.setText(f.getAbsolutePath());
}
});
+ encRSAAESCheckbox = new JCheckBox("RSA-AES");
+ encRSAAESCheckbox.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ handleRSAAES();
+ }
+ });
encrPanel.add(encNoneCheckbox,
new GridBagConstraints(0, 0,
REMAINDER, 1,
LINE_START, VERTICAL,
new Insets(0, 5, 0, 0),
0, 0));
+ encrPanel.add(encRSAAESCheckbox,
+ new GridBagConstraints(0, 5,
+ REMAINDER, 1,
+ HEAVY, LIGHT,
+ LINE_START, NONE,
+ new Insets(0, 0, 4, 0),
+ NONE, NONE));
JPanel authPanel = new JPanel(new GridBagLayout());
authPanel.setBorder(BorderFactory.createTitledBorder("Authentication"));
crlChooser.setEnabled(encX509Checkbox.isSelected());
}
+ private void handleRSAAES()
+ {
+ authVncCheckbox.setSelected(true);
+ authPlainCheckbox.setSelected(true);
+ }
+
private void handleSendLocalUsername()
{
boolean value = authIdentCheckbox.isSelected() ||
cc = null;
UserDialog dlg = new UserDialog();
CSecurity.upg = dlg;
- CSecurityTLS.msg = dlg;
+ CSecurity.msg = dlg;
Socket sock = null;
/* Specifying -via and -listen together is nonsense */