package com.example.passrepo.crypto; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.util.Arrays; import android.util.Base64; import com.google.common.base.Charsets; import com.lambdaworks.crypto.SCrypt; public class PasswordHasher { public static class ScryptParameters { public ScryptParameters(byte[] salt) { saltBase64 = Base64.encode(salt, Base64.NO_WRAP); } /** computational difficulty. Need to test on older devices to see if we can increase this. */ public final int n = 1024; /** memory difficulty. RAM needed should be log2(N) * P * R * 128 bytes */ public final int r = 4; /** parallelization */ public final int p = 1; // no parallelization public final byte[] saltBase64; public byte[] getSalt() { return Base64.decode(saltBase64, Base64.NO_WRAP); } } private static final int KEY_LENGTH_BYTES = 32; public static class Keys { public final byte[] encryptionKey; public final byte[] hmacKey; public Keys(byte[] encryptionKey, byte[] hmacKey) { this.encryptionKey = encryptionKey; this.hmacKey = hmacKey; } } public static Keys hash(String password, ScryptParameters scryptParameters) { try { // can't use getBytes(Charset) in Android API 8 final byte[] passwordBytes = password.getBytes(Charsets.UTF_8.name()); // Generate enough bytes for 2 keys final byte[] generatedBytes = SCrypt.scrypt(passwordBytes, scryptParameters.getSalt(), scryptParameters.n, scryptParameters.r, scryptParameters.p, KEY_LENGTH_BYTES * 2); final byte[] encryptionKey = Arrays.copyOfRange(generatedBytes, 0, KEY_LENGTH_BYTES); final byte[] hmacKey = Arrays.copyOfRange(generatedBytes, KEY_LENGTH_BYTES, KEY_LENGTH_BYTES*2); return new Keys(encryptionKey, hmacKey); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } }