if ( !rs.next()) {
throw new GigiApiException("User not found... very bad.");
}
- if ( !PasswordHash.verifyHash(oldPass, rs.getString(1))) {
+ if (PasswordHash.verifyHash(oldPass, rs.getString(1)) == null) {
throw new GigiApiException("Old password does not match.");
}
rs.close();
ps.setString(1, un);
GigiResultSet rs = ps.executeQuery();
if (rs.next()) {
- if (PasswordHash.verifyHash(pw, rs.getString(1))) {
+ String dbHash = rs.getString(1);
+ String hash = PasswordHash.verifyHash(pw, dbHash);
+ if (hash != null) {
+ if ( !hash.equals(dbHash)) {
+ GigiPreparedStatement gps = DatabaseConnection.getInstance().prepare("UPDATE `users` SET `password`=? WHERE `email`=?");
+ gps.setString(1, hash);
+ gps.setString(2, un);
+ gps.executeUpdate();
+ }
loginSession(req, User.getById(rs.getInt(2)));
}
}
public class PasswordHash {
- public static boolean verifyHash(String password, String hash) {
+ /**
+ * Verifies a password hash.
+ *
+ * @param password
+ * The password that should result in the given hash.
+ * @param hash
+ * The hash to verify the password against.
+ * @return <ul>
+ * <li><code>null</code>, if the password was valid</li>
+ * <li><code>hash</code>, if the password is valid and the hash
+ * doesn't need to be updated</li>
+ * <li>a new hash, if the password is valid but the hash in the
+ * database needs to be updated.</li>
+ * </ul>
+ */
+ public static String verifyHash(String password, String hash) {
if (hash.contains("$")) {
- return SCryptUtil.check(password, hash);
+ if (SCryptUtil.check(password, hash)) {
+ return hash;
+ } else {
+ return null;
+ }
}
String newhash = sha1(password);
boolean match = true;
for (int i = 0; i < newhash.length(); i++) {
match &= newhash.charAt(i) == hash.charAt(i);
}
- return match;
+ if (match) {
+ return hash(password);
+ } else {
+ return null;
+ }
}
private static String sha1(String password) {
--- /dev/null
+package org.cacert.gigi.testUtils;
+
+import org.cacert.gigi.dbObjects.User;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class RegisteredUser implements TestRule {
+
+ User u;
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ u = User.getById(ManagedTest.createVerifiedUser("fn", "ln", ManagedTest.createUniqueName() + "@example.org", ManagedTest.TEST_PASSWORD));
+ try {
+ base.evaluate();
+ } finally {
+
+ }
+ }
+ };
+ }
+
+ public User getUser() {
+ return u;
+ }
+
+}
@Test
public void testVerify() {
- assertTrue(PasswordHash.verifyHash("a", PasswordHash.hash("a")));
- assertTrue(PasswordHash.verifyHash("", PasswordHash.hash("")));
- assertTrue(PasswordHash.verifyHash("a1234", PasswordHash.hash("a1234")));
- assertTrue(PasswordHash.verifyHash("auhlcb4 9x,IUQẞ&lvrvä", PasswordHash.hash("auhlcb4 9x,IUQẞ&lvrvä")));
+ assertTrue(PasswordHash.verifyHash("a", PasswordHash.hash("a")) != null);
+ assertTrue(PasswordHash.verifyHash("a1234", PasswordHash.hash("a1234")) != null);
+ assertTrue(PasswordHash.verifyHash("auhlcb4 9x,IUQẞ&lvrvä", PasswordHash.hash("auhlcb4 9x,IUQẞ&lvrvä")) != null);
}
@Test
public void testVerifyNegative() {
- assertFalse(PasswordHash.verifyHash("b", PasswordHash.hash("a")));
- assertFalse(PasswordHash.verifyHash("ae", PasswordHash.hash("auhlcb4 9x,IUQẞ&lvrvä")));
+ assertFalse(PasswordHash.verifyHash("b", PasswordHash.hash("a")) != null);
+ assertFalse(PasswordHash.verifyHash("ae", PasswordHash.hash("auhlcb4 9x,IUQẞ&lvrvä")) != null);
}
}
--- /dev/null
+package org.cacert.gigi.util;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.database.GigiPreparedStatement;
+import org.cacert.gigi.database.GigiResultSet;
+import org.cacert.gigi.testUtils.ManagedTest;
+import org.cacert.gigi.testUtils.RegisteredUser;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class TestPasswordMigration extends ManagedTest {
+
+ @Rule
+ public RegisteredUser ru = new RegisteredUser();
+
+ @Test
+ public void testPasswordMigration() throws IOException {
+ GigiPreparedStatement stmt = DatabaseConnection.getInstance().prepare("UPDATE users SET `password`=SHA1(?) WHERE id=?");
+ stmt.setString(1, "a");
+ stmt.setInt(2, ru.getUser().getId());
+ stmt.execute();
+ String cookie = login(ru.getUser().getEmail(), "a");
+ assertTrue(isLoggedin(cookie));
+
+ stmt = DatabaseConnection.getInstance().prepare("SELECT `password` FROM users WHERE id=?");
+ stmt.setInt(1, ru.getUser().getId());
+ GigiResultSet res = stmt.executeQuery();
+ assertTrue(res.next());
+ String newHash = res.getString(1);
+ assertThat(newHash, containsString("$"));
+ }
+}