51dffb70d8ed0763548ce7be6fed104364682c30
[gigi.git] / src / club / wpia / gigi / util / PasswordHash.java
1 package club.wpia.gigi.util;
2
3 import java.io.UnsupportedEncodingException;
4 import java.security.MessageDigest;
5 import java.security.NoSuchAlgorithmException;
6 import java.util.Properties;
7
8 import com.lambdaworks.crypto.SCryptUtil;
9
10 public class PasswordHash {
11
12     /**
13      * Verifies a password hash.
14      * 
15      * @param password
16      *            The password that should result in the given hash.
17      * @param hash
18      *            The hash to verify the password against.
19      * @return
20      *         <ul>
21      *         <li><code>null</code>, if the password was valid</li>
22      *         <li><code>hash</code>, if the password is valid and the hash
23      *         doesn't need to be updated</li>
24      *         <li>a new hash, if the password is valid but the hash in the
25      *         database needs to be updated.</li>
26      *         </ul>
27      */
28     public static String verifyHash(String password, String hash) {
29         if (password == null || password.isEmpty()) {
30             return null;
31         }
32         if (hash.contains("$")) {
33             if (SCryptUtil.check(password, hash)) {
34                 return hash;
35             } else {
36                 return null;
37             }
38         }
39         String newhash = sha1(password);
40         boolean match = true;
41         if (newhash.length() != hash.length()) {
42             match = false;
43         }
44         for (int i = 0; i < newhash.length(); i++) {
45             match &= newhash.charAt(i) == hash.charAt(i);
46         }
47         if (match) {
48             return hash(password);
49         } else {
50             return null;
51         }
52     }
53
54     public static String sha1(String password) {
55         try {
56             MessageDigest md = MessageDigest.getInstance("SHA1");
57             byte[] digest = md.digest(password.getBytes("UTF-8"));
58             StringBuffer res = new StringBuffer(digest.length * 2);
59             for (int i = 0; i < digest.length; i++) {
60                 res.append(Integer.toHexString((digest[i] & 0xF0) >> 4));
61                 res.append(Integer.toHexString(digest[i] & 0xF));
62             }
63             return res.toString();
64         } catch (NoSuchAlgorithmException e) {
65             throw new Error(e);
66         } catch (UnsupportedEncodingException e) {
67             throw new Error(e);
68         }
69     }
70
71     public static String hash(String password) {
72         return SCryptUtil.scrypt(password, N, r, p);
73     }
74
75     private static int N = 1 << 14;
76
77     private static int r = 8;
78
79     private static int p = 1;
80
81     private static boolean initialized = false;
82
83     public static synchronized void init(Properties prop) {
84         if (initialized) {
85             throw new IllegalStateException("Already initialized.");
86         }
87         String val = prop.getProperty("scrypt.params", "14;8;1");
88         String[] parts = val.split(";", 3);
89         int N = 1 << Integer.parseInt(parts[0]);
90         int r = Integer.parseInt(parts[1]);
91         int p = Integer.parseInt(parts[2]);
92         checkScryptParams(N, r, p);
93         PasswordHash.N = N;
94         PasswordHash.r = r;
95         PasswordHash.p = p;
96         initialized = true;
97     }
98
99     private static void checkScryptParams(int N, int r, int p) {
100         if (N < 2 || (N & (N - 1)) != 0) {
101             throw new IllegalArgumentException("N must be a power of 2 greater than 1");
102         }
103         if (r <= 0) {
104             throw new IllegalArgumentException("Parameter r zero or negative");
105         }
106         if (p <= 0) {
107             throw new IllegalArgumentException("Parameter p zero or negative");
108         }
109
110         if (N > Integer.MAX_VALUE / 128 / r) {
111             throw new IllegalArgumentException("Parameter N is too large");
112         }
113         if (r > Integer.MAX_VALUE / 128 / p) {
114             throw new IllegalArgumentException("Parameter r is too large");
115         }
116     }
117 }