
com.lambdaworks.crypto.SCryptUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scrypt Show documentation
Show all versions of scrypt Show documentation
Java implementation of scrypt
// Copyright (C) 2011 - Will Glozer. All rights reserved.
package com.lambdaworks.crypto;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import static com.lambdaworks.crypto.Base64.*;
/**
* Simple {@link SCrypt} interface for hashing passwords using the
* scrypt key derivation function
* and comparing a plain text password to a hashed one. The hashed output is an
* extended implementation of the Modular Crypt Format that also includes the scrypt
* algorithm parameters.
*
* Format: $s0$PARAMS$SALT$KEY
.
*
*
* - PARAMS
- 32-bit hex integer containing log2(N) (16 bits), r (8 bits), and p (8 bits)
* - SALT
- base64-encoded salt
* - KEY
- base64-encoded derived key
*
*
* s0
identifies version 0 of the scrypt format, using a 128-bit salt and 256-bit derived key.
*
* @author Will Glozer
*/
public class SCryptUtil {
/**
* 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(String passwd, 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);
String params = Integer.toString(log2(N) << 16 | r << 8 | p, 16);
StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);
sb.append("$s0$").append(params).append('$');
sb.append(encodeToChar(salt, false)).append('$');
sb.append(encodeToChar(derived, false));
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?");
}
}
/**
* 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) {
try {
String[] parts = hashed.split("\\$");
if (parts.length != 5 || !parts[1].equals("s0")) {
throw new IllegalArgumentException("Invalid hashed value");
}
int params = Integer.parseInt(parts[2], 16);
byte[] salt = decodeFast(parts[3].toCharArray());
byte[] derived0 = decodeFast(parts[4].toCharArray());
int N = (int) Math.pow(2, params >> 16 & 0xff);
int r = params >> 8 & 0x0f;
int p = params & 0x0f;
byte[] derived1 = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32);
if (derived0.length != derived1.length) return false;
int result = 0;
for (int i = 0; i < derived0.length; i++) {
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?");
}
}
private static int log2(int n) {
int log = 0;
if ((n & 0xffff0000 ) != 0) { n >>>= 16; log = 16; }
if (n >= 256) { n >>>= 8; log += 8; }
if (n >= 16 ) { n >>>= 4; log += 4; }
if (n >= 4 ) { n >>>= 2; log += 2; }
return log + (n >>> 1);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy