changeset 510:7953570e5210

update to ganymed-ssh2 tag 263 and fix hmac-sha2-512
author Carl Byington <carl@five-ten-sg.com>
date Wed, 01 Feb 2023 17:55:29 -0700
parents 2eb4fa13b9ef
children 6dbddfa27f6c
files TODO app/src/main/java/ch/ethz/ssh2/Connection.java app/src/main/java/ch/ethz/ssh2/KnownHosts.java app/src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java app/src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java app/src/main/java/ch/ethz/ssh2/crypto/digest/SHA512.java app/src/main/java/ch/ethz/ssh2/transport/ClientKexManager.java app/src/main/java/ch/ethz/ssh2/transport/KexManager.java
diffstat 8 files changed, 131 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Sun Jan 29 10:25:21 2023 -0700
+++ b/TODO	Wed Feb 01 17:55:29 2023 -0700
@@ -81,16 +81,19 @@
 
 ==================================
 
-TODO:
+2023-02-01 - 5250 ssl uses javax.net.ssl with security providers, so
+that automatically gets better with modern android releases. Remove
+SSLv2 and SSLv3, add TLSv1.2 and TLSv1.3
 
-5250 ssl uses javax.net.ssl with security providers, so that
-automatically gets better with modern android releases. Remove
-SSLv2 and SSLv3, change to TLSv1.2 and TLSv1.3
+update Ganymed to https://github.com/SoftwareAG/ganymed-ssh-2 tag
+ganymed-ssh2-263, with stronger algorithms for message authentication
+and key exchange.
 
-ssh uses encryption and signature algorithms from bouncycastle.
-Verify those.
+==================================
 
 
+TODO
+
 change all System.*.println -> android Log.d(TAG, "") calls
 
 possible move to https://github.com/hierynomus/sshj
--- a/app/src/main/java/ch/ethz/ssh2/Connection.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/Connection.java	Wed Feb 01 17:55:29 2023 -0700
@@ -1242,14 +1242,55 @@
     }
 
     /**
+     * Removes duplicates from a String array, keeps only first occurence
+     * of each element. Does not destroy order of elements; can handle nulls.
+     * Uses a very efficient O(N^2) algorithm =)
+     *
+     * @param list a String array.
+     * @return a cleaned String array.
+     */
+    private String[] removeDuplicates(String[] list) {
+        if((list == null) || (list.length < 2)) {
+            return list;
+        }
+
+        String[] list2 = new String[list.length];
+
+        int count = 0;
+
+        for(final String element : list) {
+            boolean duplicate = false;
+            for(int j = 0; j < count; j++) {
+                if(((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j])))) {
+                    duplicate = true;
+                    break;
+                }
+            }
+            if(duplicate) {
+                continue;
+            }
+            list2[count++] = element;
+        }
+
+        if(count == list2.length) {
+            return list2;
+        }
+
+        String[] tmp = new String[count];
+        System.arraycopy(list2, 0, tmp, 0, count);
+
+        return tmp;
+    }
+
+    /**
      * Unless you know what you are doing, you will never need this.
      */
 
-    public synchronized void setClient2ServerCiphers(final String[] ciphers) {
+    public synchronized void setClient2ServerCiphers(String[] ciphers) {
         if ((ciphers == null) || (ciphers.length == 0)) {
             throw new IllegalArgumentException();
         }
-
+        ciphers = removeDuplicates(ciphers);
         BlockCipherFactory.checkCipherList(ciphers);
         cryptoWishList.c2s_enc_algos = ciphers;
     }
@@ -1258,7 +1299,11 @@
      * Unless you know what you are doing, you will never need this.
      */
 
-    public synchronized void setClient2ServerMACs(final String[] macs) {
+    public synchronized void setClient2ServerMACs(String[] macs) {
+        if((macs == null) || (macs.length == 0)) {
+            throw new IllegalArgumentException();
+        }
+        macs = removeDuplicates(macs);
         MAC.checkMacList(macs);
         cryptoWishList.c2s_mac_algos = macs;
     }
@@ -1283,7 +1328,11 @@
      * Unless you know what you are doing, you will never need this.
      */
 
-    public synchronized void setServer2ClientCiphers(final String[] ciphers) {
+    public synchronized void setServer2ClientCiphers(String[] ciphers) {
+        if((ciphers == null) || (ciphers.length == 0)) {
+            throw new IllegalArgumentException();
+        }
+        ciphers = removeDuplicates(ciphers);
         BlockCipherFactory.checkCipherList(ciphers);
         cryptoWishList.s2c_enc_algos = ciphers;
     }
@@ -1292,7 +1341,11 @@
      * Unless you know what you are doing, you will never need this.
      */
 
-    public synchronized void setServer2ClientMACs(final String[] macs) {
+    public synchronized void setServer2ClientMACs(String[] macs) {
+        if((macs == null) || (macs.length == 0)) {
+            throw new IllegalArgumentException();
+        }
+        macs = removeDuplicates(macs);
         MAC.checkMacList(macs);
         cryptoWishList.s2c_mac_algos = macs;
     }
@@ -1310,12 +1363,36 @@
      *              at least one entry.
      */
 
-    public synchronized void setServerHostKeyAlgorithms(final String[] algos) {
+    public synchronized void setServerHostKeyAlgorithms(String[] algos) {
+        if ((algos == null) || (algos.length == 0)) {
+            throw new IllegalArgumentException();
+        }
+        algos = removeDuplicates(algos);
         KexManager.checkServerHostkeyAlgorithmsList(algos);
         cryptoWishList.serverHostKeyAlgorithms = algos;
     }
 
     /**
+     * Define the set of allowed key exchange methods.
+     *
+     * @param algos An array of allowed key exchange methods. The following are supported:
+     *              diffie-hellman-group14-sha256,
+     *              diffie-hellman-group16-sha512,
+     *              diffie-hellman-group18-sha512,
+     *              diffie-hellman-group14-sha1,
+     *              diffie-hellman-group1-sha1,
+     *              diffie-hellman-group-exchange-sha1
+     */
+    public synchronized void setClientKexAlgorithms(String[] algos) {
+        if ((algos == null) || (algos.length == 0)) {
+            throw new IllegalArgumentException();
+        }
+        algos = removeDuplicates(algos);
+        KexManager.checkClientKexAlgorithmList(algos);
+        cryptoWishList.kexAlgorithms = algos;
+    }
+
+    /**
      * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
      * <p/>
      * Can be called at any time. If the connection has not yet been established
--- a/app/src/main/java/ch/ethz/ssh2/KnownHosts.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/KnownHosts.java	Wed Feb 01 17:55:29 2023 -0700
@@ -180,7 +180,7 @@
         }
 
         try {
-            HMAC hmac = new HMAC(sha1, salt, salt.length);
+            HMAC hmac = new HMAC(sha1, salt, salt.length, 64);
             hmac.update(StringEncoder.GetBytes(hostname));
             byte[] dig = new byte[hmac.getDigestLength()];
             hmac.digest(dig);
--- a/app/src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java	Wed Feb 01 17:55:29 2023 -0700
@@ -21,15 +21,14 @@
 
     int size;
 
-    public HMAC(Digest md, byte[] key, int size) throws DigestException {
+    public HMAC(Digest md, byte[] key, int digestsize, int blocksize) throws DigestException {
         this.md = md;
-        this.size = size;
+        this.size = digestsize;
         tmp = new byte[md.getDigestLength()];
-        final int BLOCKSIZE = 64;
-        k_xor_ipad = new byte[BLOCKSIZE];
-        k_xor_opad = new byte[BLOCKSIZE];
+        k_xor_ipad = new byte[blocksize];
+        k_xor_opad = new byte[blocksize];
 
-        if (key.length > BLOCKSIZE) {
+        if (key.length > blocksize) {
             md.reset();
             md.update(key);
             md.digest(tmp);
@@ -39,7 +38,7 @@
         System.arraycopy(key, 0, k_xor_ipad, 0, key.length);
         System.arraycopy(key, 0, k_xor_opad, 0, key.length);
 
-        for (int i = 0; i < BLOCKSIZE; i++) {
+        for (int i = 0; i < blocksize; i++) {
             k_xor_ipad[i] ^= 0x36;
             k_xor_opad[i] ^= 0x5C;
         }
--- a/app/src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java	Wed Feb 01 17:55:29 2023 -0700
@@ -20,7 +20,7 @@
     public static String[] getMacList() {
         // Higher priority (stronger) first. Added SHA-2 algorithms as in RFC 6668
         return new String[] {
-                             // "hmac-sha2-512",    // fails interop w/ centos6
+                             "hmac-sha2-512",
                              "hmac-sha2-256",
                              "hmac-sha1",
                              "hmac-sha1-96",
@@ -36,51 +36,33 @@
     }
 
     public static int getKeyLen(final String type) {
-        if (type.equals("hmac-sha1")) {
-            return 20;
-        }
-
-        if (type.equals("hmac-sha1-96")) {
-            return 20;
-        }
-
-        if (type.equals("hmac-md5")) {
-            return 16;
-        }
-
-        if (type.equals("hmac-md5-96")) {
-            return 16;
-        }
-
-        if (type.equals("hmac-sha2-256")) {
-            return 32;
-        }
-
-        if (type.equals("hmac-sha2-512")) {
-            return 64;
-        }
-
+        if (type.equals("hmac-sha2-512")) return 64;
+        if (type.equals("hmac-sha2-256")) return 32;
+        if (type.equals("hmac-sha1"))     return 20;
+        if (type.equals("hmac-sha1-96"))  return 20;
+        if (type.equals("hmac-md5"))      return 16;
+        if (type.equals("hmac-md5-96"))   return 16;
         throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
     }
 
     public MAC(final String type, final byte[] key) throws DigestException {
-        if (type.equals("hmac-sha1")) {
-            mac = new HMAC(new SHA1(), key, 20);
+        if (type.equals("hmac-sha2-512")) {
+            mac = new HMAC(new SHA512(), key, 64, 128);
+        }
+        else if (type.equals("hmac-sha2-256")) {
+            mac = new HMAC(new SHA256(), key, 32, 64);
+        }
+        else if (type.equals("hmac-sha1")) {
+            mac = new HMAC(new SHA1(), key, 20, 64);
         }
         else if (type.equals("hmac-sha1-96")) {
-            mac = new HMAC(new SHA1(), key, 12);
+            mac = new HMAC(new SHA1(), key, 12, 64);
         }
         else if (type.equals("hmac-md5")) {
-            mac = new HMAC(new MD5(), key, 16);
+            mac = new HMAC(new MD5(), key, 16, 64);
         }
         else if (type.equals("hmac-md5-96")) {
-            mac = new HMAC(new MD5(), key, 12);
-        }
-        else if (type.equals("hmac-sha2-256")) {
-            mac = new HMAC(new SHA256(), key, 32);
-        }
-        else if (type.equals("hmac-sha2-512")) {
-            mac = new HMAC(new SHA512(), key, 64);
+            mac = new HMAC(new MD5(), key, 12, 64);
         }
         else {
             throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
--- a/app/src/main/java/ch/ethz/ssh2/crypto/digest/SHA512.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/crypto/digest/SHA512.java	Wed Feb 01 17:55:29 2023 -0700
@@ -33,7 +33,7 @@
     }
 
     public final void update(byte b[]) {
-        md.update(b);
+        this.update(b, 0, b.length);
     }
 
     public final void update(byte b[], int off, int len) {
@@ -44,11 +44,11 @@
         md.update(b);
     }
 
-    public final void digest(byte[] out) {
-        md.digest(out);
+    public final void digest(byte[] out) throws DigestException {
+        this.digest(out, 0);
     }
 
     public final void digest(byte[] out, int off) throws DigestException {
-        md.digest(out, off, out.length);
+        md.digest(out, off, out.length - off);
     }
 }
--- a/app/src/main/java/ch/ethz/ssh2/transport/ClientKexManager.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/transport/ClientKexManager.java	Wed Feb 01 17:55:29 2023 -0700
@@ -161,6 +161,9 @@
 
             if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")  ||
                 kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group14-sha256") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group16-sha512") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group18-sha512") ||
                 kxs.np.kex_algo.equals("ecdh-sha2-nistp256")          ||
                 kxs.np.kex_algo.equals("ecdh-sha2-nistp384")          ||
                 kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
@@ -282,6 +285,9 @@
 
         if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")  ||
                 kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group14-sha256") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group16-sha512") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group18-sha512") ||
                 kxs.np.kex_algo.equals("ecdh-sha2-nistp256")          ||
                 kxs.np.kex_algo.equals("ecdh-sha2-nistp384")          ||
                 kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
--- a/app/src/main/java/ch/ethz/ssh2/transport/KexManager.java	Sun Jan 29 10:25:21 2023 -0700
+++ b/app/src/main/java/ch/ethz/ssh2/transport/KexManager.java	Wed Feb 01 17:55:29 2023 -0700
@@ -52,6 +52,9 @@
         KEX_ALGS.add("diffie-hellman-group-exchange-sha256");
         KEX_ALGS.add("diffie-hellman-group-exchange-sha1");
         KEX_ALGS.add("diffie-hellman-group14-sha1");
+        KEX_ALGS.add("diffie-hellman-group14-sha256");
+        KEX_ALGS.add("diffie-hellman-group16-sha512");
+        KEX_ALGS.add("diffie-hellman-group18-sha512");
         KEX_ALGS.add("diffie-hellman-group1-sha1");
         KEX_ALGS.add("ecdh-sha2-nistp256");
         KEX_ALGS.add("ecdh-sha2-nistp384");
@@ -295,7 +298,7 @@
         return KEX_ALGS.toArray(new String[KEX_ALGS.size()]);
     }
 
-    public static void checkKexAlgorithmList(String[] algos) {
+    public static void checkClientKexAlgorithmList(String[] algos) {
         for (final String algo : algos) {
             if (!KEX_ALGS.contains(algo))
                 throw new IllegalArgumentException("Unknown kex algorithm '" + algo + "'");