diff --git a/src/main/java/helpers/ScryptHelper.java b/src/main/java/helpers/ScryptHelper.java index 8471f5b..1750a15 100644 --- a/src/main/java/helpers/ScryptHelper.java +++ b/src/main/java/helpers/ScryptHelper.java @@ -9,8 +9,8 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; -import java.util.Base64; import org.bouncycastle.crypto.generators.SCrypt; +import org.bouncycastle.util.encoders.Base64; /** * Wrapper for Bouncy Castle's scrypt implementation. @@ -45,13 +45,11 @@ byte[] salt = salt(); byte[] hash = hash(password, salt, N, r, p, dkLen); - Base64.Encoder b64encoder = Base64.getEncoder(); - int costParams = (log2(N)) << 16 | r << 8 | p; byte[] dollar = "$".getBytes(utf8); byte[] params = String.valueOf(costParams).getBytes(utf8); - byte[] b64salt = b64encoder.encode(salt); - byte[] b64hash = b64encoder.encode(hash); + byte[] b64salt = Base64.encode(salt); + byte[] b64hash = Base64.encode(hash); int size = (4 * dollar.length) + params.length + b64salt.length + b64hash.length; @@ -83,7 +81,6 @@ */ public static boolean check(CharSequence mcfHash, CharSequence password) { - // expected format: $params$salt$hash$ if (mcfHash == null || mcfHash.length() == 0 || mcfHash.charAt(0) != '$') { throw new IllegalArgumentException("Hash is not in a recognisable format"); } @@ -91,7 +88,6 @@ int[] indices = new int[4]; int count = 0; - // find positions of the delimiters for (int i = 0; i < mcfHash.length() && count < 4; i++) { if (mcfHash.charAt(i) == '$') { indices[count++] = i; @@ -102,16 +98,16 @@ throw new IllegalArgumentException("Invalid format: missing delimiters"); } - CharSequence paramsSeq = mcfHash.subSequence(indices[0] + 1, indices[1]); - int costParams = parseAsInt(paramsSeq); + // Parse costParams + int costParams = parseAsInt(mcfHash.subSequence(indices[0] + 1, indices[1])); - Base64.Decoder decoder = Base64.getDecoder(); + // Use toBytes to get a clean byte array of the B64 encoded segments + byte[] saltB64 = toBytes(mcfHash.subSequence(indices[1] + 1, indices[2])); + byte[] hashB64 = toBytes(mcfHash.subSequence(indices[2] + 1, indices[3])); - CharSequence saltSeq = mcfHash.subSequence(indices[1] + 1, indices[2]); - byte[] salt = decoder.decode(toBytes(saltSeq)); - - CharSequence hashSeq = mcfHash.subSequence(indices[2] + 1, indices[3]); - byte[] hash = decoder.decode(toBytes(hashSeq)); + // Bouncy Castle decode to pre-allocated arrays + byte[] salt = Base64.decode(saltB64); + byte[] hash = Base64.decode(hashB64); try { int hN = 1 << (costParams >> 16); @@ -123,10 +119,12 @@ boolean matches = MessageDigest.isEqual(cHash, hash); Arrays.fill(cHash, (byte) 0); - return matches; } finally { + // Thorough cleanup + Arrays.fill(saltB64, (byte) 0); + Arrays.fill(hashB64, (byte) 0); Arrays.fill(salt, (byte) 0); Arrays.fill(hash, (byte) 0); }