package io.budgetapp.crypto;
/**
*
*/
public class PasswordEncoder {
private static final int DEFAULT_ITERATIONS = 1024;
private static final String DEFAULT_ALGORITHM = "SHA-256";
private final SaltGenerator saltGenerator;
private final Digester digester;
private final byte[] secret;
public PasswordEncoder() {
this("");
}
public PasswordEncoder(String secret) {
this.digester = new Digester(DEFAULT_ALGORITHM, DEFAULT_ITERATIONS);
this.secret = Utf8.encode(secret);
this.saltGenerator = new SaltGenerator();
}
public String encode(CharSequence rawPassword) {
return encode(rawPassword, saltGenerator.generateKey());
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
byte[] digested = decode(encodedPassword);
byte[] salt = subArray(digested, 0, saltGenerator.getKeyLength());
return matches(digested, digest(rawPassword, salt));
}
/**
* Constant time comparison to prevent against timing attacks.
*/
private boolean matches(byte[] expected, byte[] actual) {
if (expected.length != actual.length) {
return false;
}
int result = 0;
for (int i = 0; i < expected.length; i++) {
result |= expected[i] ^ actual[i];
}
return result == 0;
}
private String encode(CharSequence rawPassword, byte[] salt) {
byte[] digest = digest(rawPassword, salt);
return new String(Hex.encode(digest));
}
private byte[] decode(CharSequence encodedPassword) {
return Hex.decode(encodedPassword);
}
private byte[] digest(CharSequence rawPassword, byte[] salt) {
byte[] digest = digester.digest(concatenate(salt, secret, Utf8.encode(rawPassword)));
return concatenate(salt, digest);
}
/**
* Combine the individual byte arrays into one array.
*/
public static byte[] concatenate(byte[]... arrays) {
int length = 0;
for (byte[] array : arrays) {
length += array.length;
}
byte[] newArray = new byte[length];
int destPos = 0;
for (byte[] array : arrays) {
System.arraycopy(array, 0, newArray, destPos, array.length);
destPos += array.length;
}
return newArray;
}
/**
* Extract a sub array of bytes out of the byte array.
* @param array the byte array to extract from
* @param beginIndex the beginning index of the sub array, inclusive
* @param endIndex the ending index of the sub array, exclusive
*/
public static byte[] subArray(byte[] array, int beginIndex, int endIndex) {
int length = endIndex - beginIndex;
byte[] subarray = new byte[length];
System.arraycopy(array, beginIndex, subarray, 0, length);
return subarray;
}
}