Changeset: 3a477cf520f6 for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java/rev/3a477cf520f6
Modified Files:
        src/main/java/org/monetdb/mcl/net/MapiSocket.java
Branch: monetdbs
Log Message:

Debug and clarify pasword hashing code


diffs (129 lines):

diff --git a/src/main/java/org/monetdb/mcl/net/MapiSocket.java 
b/src/main/java/org/monetdb/mcl/net/MapiSocket.java
--- a/src/main/java/org/monetdb/mcl/net/MapiSocket.java
+++ b/src/main/java/org/monetdb/mcl/net/MapiSocket.java
@@ -460,7 +460,7 @@ public final class MapiSocket {
                String parts[] = challengeLine.split(":");
                if (parts.length < 3)
                        throw new MCLException("Invalid challenge: expect at 
least 3 fields");
-               String challengePart = parts[0];
+               String saltPart = parts[0];
                String serverTypePart = parts[1];
                String versionPart = parts[2];
                int version;
@@ -484,8 +484,6 @@ public final class MapiSocket {
                } else {
                        userResponse = target.getUser();
                }
-               String passwordResponse = hashPassword(challengePart, password, 
passwordHashPart, validated.getHash(), serverHashesPart);
-
                String optionsResponse = handleOptions(callback, optionsPart);
 
                // Response looks like this:
@@ -495,7 +493,8 @@ public final class MapiSocket {
                StringBuilder response = new StringBuilder(80);
                response.append("BIG:");
                response.append(userResponse).append(":");
-               response.append(passwordResponse).append(":");
+               hashPassword(response, saltPart, password, passwordHashPart, 
validated.getHash(), serverHashesPart);
+               response.append(":");
                response.append(validated.getLanguage()).append(":");
                response.append(validated.getDatabase()).append(":");
                response.append("FILETRANS:");
@@ -505,32 +504,53 @@ public final class MapiSocket {
        }
 
        // challengePart, passwordHashPart, supportedHashesPart, 
target.getPassword()
-       private String hashPassword(String challenge, String password, String 
passwordAlgo, String configuredHashes, String serverSupportedAlgos) throws 
MCLException {
-               int maxHashLength = 512;
-
-               StringBuilder output = new StringBuilder(10 + maxHashLength / 
4);
-               MessageDigest passwordDigest = 
pickBestAlgorithm(Collections.singleton(passwordAlgo), output);
-
+       private String hashPassword(String salt, String password, String 
passwordAlgo, String configuredHashes, String serverSupportedAlgos) throws 
MCLException {
+               // First determine which hash algorithms we can choose from for 
the challenge response.
+               // This defaults to whatever the server offers but may be 
restricted by the user.
                Set<String> algoSet  = new 
HashSet<>(Arrays.asList(serverSupportedAlgos.split(",")));
                if (!configuredHashes.isEmpty()) {
-                       Set<String> keep = new 
HashSet<>(Arrays.asList(configuredHashes.toUpperCase().split("[, ]")));
-                       algoSet.retainAll(keep);
+                       String[] allowedList = 
configuredHashes.toUpperCase().split("[, ]");
+                       Set<String> allowedSet = new 
HashSet<>(Arrays.asList(allowedList));
+                       algoSet.retainAll(allowedSet);
                        if (algoSet.isEmpty()) {
-                               throw new MCLException("None of the hash 
algorithms <" + configuredHashes + "> are supported, server only supports <" + 
serverSupportedAlgos + ">");
+                               throw new MCLException("None of the hash 
algorithms in <" + configuredHashes + "> are supported, server only supports <" 
+ serverSupportedAlgos + ">");
                        }
                }
-               MessageDigest challengeDigest = pickBestAlgorithm(algoSet, 
null);
+
+               int maxHashDigits = 512 / 4;
+
+               // We'll collect the result in the responseBuffer.
+               // It will start with '{' HASHNAME '}' followed by hexdigits
+               responseBuffer.append('{');
+
+               // This is where we accumulate what will eventually be hashed 
into the hexdigits above.
+               // It consists of the hexadecimal pre-hash of the password,
+               // followed by the salt from the server
+               StringBuilder intermediate = new StringBuilder(maxHashDigits + 
salt.length());
 
-               // First we use the password algo to hash the password.
-               // Then we use the challenge algo to hash the combination of 
the resulting hash digits and the challenge.
-               StringBuilder intermediate = new StringBuilder(maxHashLength / 
4 + challenge.length());
+               MessageDigest passwordDigest = 
pickBestAlgorithm(Collections.singleton(passwordAlgo), null);
+               // Here's the password..
                hexhash(intermediate, passwordDigest, password);
-               intermediate.append(challenge);
-               hexhash(output, challengeDigest, intermediate.toString());
-               return output.toString();
+               // .. and here's the salt
+               intermediate.append(salt);
+
+               MessageDigest responseDigest = pickBestAlgorithm(algoSet, 
responseBuffer);
+               responseBuffer.append('}');
+               // pickBestAlgorithm has appended HASHNAME, buffer now contains 
'{' HASHNAME '}'
+               hexhash(responseBuffer, responseDigest, 
intermediate.toString());
+               // response buffer now contains '{' HASHNAME '}' 
HEX_DIGITS_OF_INTERMEDIATE_BUFFER
+
+               return responseBuffer.toString();
        }
 
-       private MessageDigest pickBestAlgorithm(Set<String> algos, 
StringBuilder appendPrefixHere) throws MCLException {
+       /**
+        * Pick the most preferred digest algorithm and return a MessageDigest 
instance for that.
+        * @param algos the MAPI names of permitted algorithms
+        * @param appendMapiName if not null, append MAPI name of chose 
algorithm here
+        * @return instance of the chosen digester
+        * @throws MCLException if none of the options is supported
+        */
+       private MessageDigest pickBestAlgorithm(Set<String> algos, 
StringBuilder appendMapiName) throws MCLException {
                for (String[] choice: KNOWN_ALGORITHMS) {
             String mapiName = choice[0];
             String algoName = choice[1];
@@ -543,10 +563,8 @@ public final class MapiSocket {
                 continue;
             }
             // we found a match
-            if (appendPrefixHere != null) {
-                appendPrefixHere.append('{');
-                appendPrefixHere.append(mapiName);
-                appendPrefixHere.append('}');
+            if (appendMapiName != null) {
+                appendMapiName.append(mapiName);
             }
             return digest;
         }
@@ -554,6 +572,13 @@ public final class MapiSocket {
                throw new MCLException("No supported hash algorithm: " + 
algoNames);
        }
 
+       /**
+        * Hash the text into the digest and append the hexadecimal form of the
+        * resulting digest to buffer.
+        * @param buffer where the hex digits are appended
+        * @param digest where the hex digits come from after the text has been 
digested
+        * @param text text to digest
+        */
        private void hexhash(StringBuilder buffer, MessageDigest digest, String 
text) {
                byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
                digest.update(bytes);
_______________________________________________
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org

Reply via email to