From 26a94d056e189bfc1ba5a2cedb31d36b87d4cb29 Mon Sep 17 00:00:00 2001 From: firaja Date: Wed, 15 Feb 2023 20:11:45 +0100 Subject: [PATCH] #99: scrypt conversion to operation on bytes --- .../java/com/password4j/ScryptFunction.java | 128 ++++++++++-------- src/main/java/com/password4j/Utils.java | 21 ++- 2 files changed, 94 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/password4j/ScryptFunction.java b/src/main/java/com/password4j/ScryptFunction.java index 01b88d2d..9ea39a83 100644 --- a/src/main/java/com/password4j/ScryptFunction.java +++ b/src/main/java/com/password4j/ScryptFunction.java @@ -19,6 +19,7 @@ import com.password4j.types.Hmac; import java.security.GeneralSecurityException; +import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -143,6 +144,79 @@ public static ScryptFunction getInstance(int workFactor, int resources, int para } } + @Override + public Hash hash(CharSequence plainTextPassword) + { + byte[] salt = SaltGenerator.generate(); + return internalHash(Utils.fromCharSequenceToBytes(plainTextPassword), salt); + } + + public Hash hash(byte[] plainTextPasswordAsBytes) + { + byte[] salt = SaltGenerator.generate(); + return internalHash(plainTextPasswordAsBytes, salt); + } + + @Override + public Hash hash(CharSequence plainTextPassword, String salt) + { + byte[] saltAsBytes = Utils.fromCharSequenceToBytes(salt); + byte[] plainTextPasswordAsBytes = Utils.fromCharSequenceToBytes(plainTextPassword); + return internalHash(plainTextPasswordAsBytes, saltAsBytes); + } + + public Hash hash(byte[] plainTextPasswordAsBytes, byte[] salt) + { + return internalHash(plainTextPasswordAsBytes, salt); + } + + private Hash internalHash(byte[] plainTextPassword, byte[] salt) + { + String stringedSalt = Utils.fromBytesToString(salt); + try + { + byte[] derived = scrypt(plainTextPassword, salt, derivedKeyLength); + String params = Long.toString((long) Utils.log2(workFactor) << 16 | (long) resources << 8 | parallelization, 16); + String sb = "$" + params + '$' + Utils.encodeBase64(salt) + '$' + + Utils.encodeBase64(derived); + return new Hash(this, sb, derived, stringedSalt); + } + catch (IllegalArgumentException | GeneralSecurityException e) + { + String message = "Invalid specification with salt=" + stringedSalt + ", N=" + workFactor + ", r=" + resources + " and p=" + parallelization; + throw new BadParametersException(message, e); + } + } + + @Override + public boolean check(CharSequence plainTextPassword, String hashed) + { + return check(Utils.fromCharSequenceToBytes(plainTextPassword), Utils.fromCharSequenceToBytes(hashed)); + } + + public boolean check(byte[] plainTextPassword, byte[] hashed) + { + try + { + List parts = Utils.split(hashed, (byte) 36); + if (parts.size() == 4) + { + byte[] salt = Utils.decodeBase64(parts.get(2)); + byte[] derived0 = Utils.decodeBase64(parts.get(3)); + byte[] derived1 = scrypt(plainTextPassword, salt, derivedKeyLength); + return slowEquals(derived0, derived1); + } + else + { + throw new BadParametersException("Invalid hashed value"); + } + } + catch (GeneralSecurityException gse) + { + throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?", gse); + } + } + protected static String toString(int resources, int workFactor, int parallelization, int derivedKeyLength) { return "N=" + workFactor + ", r=" + resources + ", p=" + parallelization + ", l=" + derivedKeyLength; @@ -234,61 +308,7 @@ public static void blockXOR(byte[] sArray, int si, byte[] dArray, int di, int le } - @Override - public Hash hash(CharSequence plainTextPassword) - { - byte[] salt = SaltGenerator.generate(); - return internalHash(plainTextPassword, salt); - } - - @Override - public Hash hash(CharSequence plainTextPassword, String salt) - { - byte[] saltAsBytes = Utils.fromCharSequenceToBytes(salt); - return internalHash(plainTextPassword, saltAsBytes); - } - private Hash internalHash(CharSequence plainTextPassword, byte[] salt) - { - String stringedSalt = Utils.fromBytesToString(salt); - try - { - byte[] derived = scrypt(Utils.fromCharSequenceToBytes(plainTextPassword), salt, derivedKeyLength); - String params = Long.toString((long) Utils.log2(workFactor) << 16 | (long) resources << 8 | parallelization, 16); - String sb = "$" + params + '$' + Utils.encodeBase64(salt) + '$' - + Utils.encodeBase64(derived); - return new Hash(this, sb, derived, stringedSalt); - } - catch (IllegalArgumentException | GeneralSecurityException e) - { - String message = "Invalid specification with salt=" + stringedSalt + ", N=" + workFactor + ", r=" + resources + " and p=" + parallelization; - throw new BadParametersException(message, e); - } - } - - @Override - public boolean check(CharSequence plainTextPassword, String hashed) - { - try - { - String[] parts = hashed.split("\\$"); - if (parts.length == 4) - { - byte[] salt = Utils.decodeBase64(parts[2]); - byte[] derived0 = Utils.decodeBase64(parts[3]); - byte[] derived1 = scrypt(Utils.fromCharSequenceToBytes(plainTextPassword), salt, derivedKeyLength); - return slowEquals(derived0, derived1); - } - else - { - throw new BadParametersException("Invalid hashed value"); - } - } - catch (GeneralSecurityException gse) - { - throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?", gse); - } - } /** * Estimates the required memory to calculate an hash with diff --git a/src/main/java/com/password4j/Utils.java b/src/main/java/com/password4j/Utils.java index f7ee589d..9fad549a 100644 --- a/src/main/java/com/password4j/Utils.java +++ b/src/main/java/com/password4j/Utils.java @@ -27,6 +27,7 @@ import java.nio.charset.StandardCharsets; import java.security.*; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.regex.Matcher; @@ -322,7 +323,7 @@ private static int scale(int initialLength, float bytesPerChar) static byte[] decodeBase64(String src) { - return decodeBase64(src.getBytes(StandardCharsets.ISO_8859_1)); + return decodeBase64(src.getBytes(DEFAULT_CHARSET)); } static String encodeBase64(byte[] src) @@ -603,4 +604,22 @@ static void printBanner(PrintStream printStream) } } + static List split(byte[] array, byte delimiter) { + List byteArrays = new LinkedList<>(); + + int begin = 0; + + for (int i = 0; i < array.length; i++) { + + if (array[i] != delimiter) { + continue; + } + + byteArrays.add(Arrays.copyOfRange(array, begin, i)); + begin = i + 1; + } + byteArrays.add(Arrays.copyOfRange(array, begin, array.length)); + return byteArrays; + } + }