Skip to content

Commit

Permalink
SCryptUtil now accepts char[] passwords in addition to Strings
Browse files Browse the repository at this point in the history
- issue wg#31
  • Loading branch information
bannmann committed Jul 20, 2015
1 parent 0675236 commit 0179c43
Showing 1 changed file with 63 additions and 8 deletions.
71 changes: 63 additions & 8 deletions src/main/java/com/lambdaworks/crypto/SCryptUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

package com.lambdaworks.crypto;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Arrays;

import static com.lambdaworks.codec.Base64.*;

Expand Down Expand Up @@ -40,11 +43,35 @@ public class SCryptUtil {
* @return The hashed password.
*/
public static String scrypt(String passwd, int N, int r, int p) {
byte[] bytes = passwd.getBytes(StandardCharsets.UTF_8);
String result = scryptInternal(bytes, N, r, p);
wipeArray(bytes);
return result;
}

/**
* Hash the supplied plaintext password and generate output in the format described
* in {@link SCryptUtil}.
*
* @param passwd Password.
* @param N CPU cost parameter.
* @param r Memory cost parameter.
* @param p Parallelization parameter.
* @return The hashed password.
*/
public static String scrypt(char[] passwd, int N, int r, int p) {
byte[] bytes = toBytes(passwd);
String result = scryptInternal(bytes, N, r, p);
wipeArray(bytes);
return result;
}

private static String scryptInternal(byte[] passwordBytes, int N, int r, int p) {
try {
byte[] salt = new byte[16];
SecureRandom.getInstance("SHA1PRNG").nextBytes(salt);

byte[] derived = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32);
byte[] derived = SCrypt.scrypt(passwordBytes, salt, N, r, p, 32);

String params = Long.toString(log2(N) << 16L | r << 8 | p, 16);

Expand All @@ -54,22 +81,48 @@ public static String scrypt(String passwd, int N, int r, int p) {
sb.append(encode(derived));

return sb.toString();
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("JVM doesn't support UTF-8?");
} catch (GeneralSecurityException e) {
throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?");
}
}

private static byte[] toBytes(char[] chars) {
CharBuffer charBuffer = CharBuffer.wrap(chars);
ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer);
byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
wipeArray(byteBuffer.array());
return bytes;
}

/**
* Compare the supplied plaintext password to a hashed password.
*
* @param passwd Plaintext password.
* @param hashed scrypt hashed password.
*
* @return true if passwd matches hashed value.
*/
public static boolean check(String passwd, String hashed) {
byte[] bytes = passwd.getBytes(StandardCharsets.UTF_8);
boolean result = checkInternal(bytes, hashed);
wipeArray(bytes);
return result;
}

/**
* Compare the supplied plaintext password to a hashed password.
*
* @param passwd Plaintext password.
* @param hashed scrypt hashed password.
* @return true if passwd matches hashed value.
*/
public static boolean check(char[] passwd, String hashed) {
byte[] bytes = toBytes(passwd);
boolean result = checkInternal(bytes, hashed);
wipeArray(bytes);
return result;
}

private static boolean checkInternal(byte[] passwordBytes, String hashed) {
try {
String[] parts = hashed.split("\\$");

Expand All @@ -85,7 +138,7 @@ public static boolean check(String passwd, String hashed) {
int r = (int) params >> 8 & 0xff;
int p = (int) params & 0xff;

byte[] derived1 = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32);
byte[] derived1 = SCrypt.scrypt(passwordBytes, salt, N, r, p, 32);

if (derived0.length != derived1.length) return false;

Expand All @@ -94,8 +147,6 @@ public static boolean check(String passwd, String hashed) {
result |= derived0[i] ^ derived1[i];
}
return result == 0;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("JVM doesn't support UTF-8?");
} catch (GeneralSecurityException e) {
throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?");
}
Expand All @@ -109,4 +160,8 @@ private static int log2(int n) {
if (n >= 4 ) { n >>>= 2; log += 2; }
return log + (n >>> 1);
}

private static void wipeArray(byte[] array) {
Arrays.fill(array, (byte) 0);
}
}

0 comments on commit 0179c43

Please sign in to comment.