Skip to content

Commit

Permalink
[#24] Passwords are now char[]s instead of Strings
Browse files Browse the repository at this point in the history
  • Loading branch information
kocakosm committed Nov 15, 2019
1 parent 35c2b05 commit 346d76b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 21 deletions.
39 changes: 24 additions & 15 deletions src/org/kocakosm/pitaya/security/Passwords.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import org.kocakosm.pitaya.charset.UTF8;
import org.kocakosm.pitaya.util.ByteBuffer;
import org.kocakosm.pitaya.util.Strings;

import java.security.SecureRandom;
import java.util.Arrays;
Expand Down Expand Up @@ -53,9 +52,13 @@ public final class Passwords
*
* @return a pseudo-random password.
*/
public static String generate()
public static char[] generate()
{
return Strings.random(10, PRNG, ALPHABET);
char[] password = new char[10];
for (int i = 0; i < password.length; i++) {
password[i] = ALPHABET[PRNG.nextInt(ALPHABET.length)];
}
return password;
}

/**
Expand All @@ -68,7 +71,7 @@ public static String generate()
*
* @throws NullPointerException if {@code password} is {@code null}.
*/
public static byte[] hash(String password)
public static byte[] hash(char[] password)
{
return hash(password, salt(), R, N, P);
}
Expand All @@ -83,28 +86,21 @@ public static byte[] hash(String password)
*
* @throws NullPointerException if one of the arguments is {@code null}.
*/
public static boolean verify(String password, byte[] hash)
public static boolean verify(char[] password, byte[] hash)
{
byte[] h = Arrays.copyOf(hash, HASH_LENGTH + SALT_LENGTH + 3);
int n = 1 << (h[HASH_LENGTH + SALT_LENGTH] & 0xFF);
int r = h[HASH_LENGTH + SALT_LENGTH + 1] & 0xFF;
int p = h[HASH_LENGTH + SALT_LENGTH + 2] & 0xFF;
if (n > N || n < N_MIN || r > R || r < R_MIN || p > P || p < P_MIN) {
n = N;
r = R;
p = P;
return false;
}
byte[] salt = new byte[SALT_LENGTH];
System.arraycopy(h, HASH_LENGTH, salt, 0, SALT_LENGTH);
byte[] expected = hash(password, salt, r, n, p);
int result = 0;
for (int i = 0; i < h.length; i++) {
result |= h[i] ^ expected[i];
}
return result == 0;
return areEqual(h, hash(password, salt, r, n, p));
}

private static byte[] hash(String password, byte[] salt, int r, int n, int p)
private static byte[] hash(char[] password, byte[] salt, int r, int n, int p)
{
KDF scrypt = KDFs.scrypt(r, n, p, HASH_LENGTH);
ByteBuffer buf = new ByteBuffer(HASH_LENGTH + SALT_LENGTH + 3);
Expand All @@ -122,6 +118,19 @@ private static byte[] salt()
return salt;
}

/* Compares the given byte arrays for equality in constant-time. */
private static boolean areEqual(byte[] a, byte[] b)
{
if (a.length != b.length) {
return false;
}
int result = 0;
for (int i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result == 0;
}

private Passwords()
{
/* ... */
Expand Down
13 changes: 7 additions & 6 deletions test/org/kocakosm/pitaya/security/PasswordsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,25 @@ public final class PasswordsTest
@Test
public void testGenerate()
{
assertTrue(ASCII.isAlphaNumeric(Passwords.generate()));
assertEquals(10, Passwords.generate().length());
char[] password = Passwords.generate();
assertTrue(ASCII.isAlphaNumeric(password));
assertEquals(10, password.length);
}

@Test
public void testValidPassword()
{
String password = "password";
char[] password = "password".toCharArray();
byte[] hash = Passwords.hash(password);
assertTrue(Passwords.verify(password, hash));
}

@Test
public void testInvalidPassword()
{
byte[] hash = Passwords.hash("password");
assertFalse(Passwords.verify("Password", hash));
assertFalse(Passwords.verify("Password", new byte[0]));
byte[] hash = Passwords.hash("password".toCharArray());
assertFalse(Passwords.verify("Password".toCharArray(), hash));
assertFalse(Passwords.verify("Password".toCharArray(), new byte[0]));
}

@Test
Expand Down

0 comments on commit 346d76b

Please sign in to comment.