Skip to content

Commit

Permalink
#99: scrypt conversion to operation on bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
firaja committed Feb 15, 2023
1 parent 10fce01 commit 26a94d0
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 55 deletions.
128 changes: 74 additions & 54 deletions src/main/java/com/password4j/ScryptFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<byte[]> 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;
Expand Down Expand Up @@ -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
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/com/password4j/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -603,4 +604,22 @@ static void printBanner(PrintStream printStream)
}
}

static List<byte[]> split(byte[] array, byte delimiter) {
List<byte[]> 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;
}

}

0 comments on commit 26a94d0

Please sign in to comment.