diff options
author | Florian Zschocke <florian.zschocke@devolo.de> | 2019-11-06 12:39:55 +0100 |
---|---|---|
committer | Florian Zschocke <florian.zschocke@devolo.de> | 2019-11-06 12:39:55 +0100 |
commit | 719afbacd0b0e5333bbb72953a9df9d876b3ea33 (patch) | |
tree | aec9cd23760a20b40f02b942a2fe4b497b9a1b3c /src/test/java/com/gitblit | |
parent | 41e6a701953c6f3ec0c4b2375426e4205a1c6a00 (diff) | |
parent | b85267c81bac8168186ec78dace3ef2ec6b8cf24 (diff) | |
download | gitblit-719afbacd0b0e5333bbb72953a9df9d876b3ea33.tar.gz gitblit-719afbacd0b0e5333bbb72953a9df9d876b3ea33.zip |
Merge branch 'pingunaut-1166_more_secure_password_hashes' into master.
Diffstat (limited to 'src/test/java/com/gitblit')
-rw-r--r-- | src/test/java/com/gitblit/tests/AuthenticationManagerTest.java | 32 | ||||
-rw-r--r-- | src/test/java/com/gitblit/utils/PasswordHashTest.java | 666 |
2 files changed, 698 insertions, 0 deletions
diff --git a/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java b/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java index f8dc8885..45009856 100644 --- a/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java +++ b/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java @@ -43,6 +43,7 @@ import javax.servlet.http.HttpSessionContext; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; +import com.gitblit.utils.PasswordHash; import org.junit.Test; import com.gitblit.IUserService; @@ -665,6 +666,37 @@ public class AuthenticationManagerTest extends GitblitUnitTest { users.deleteUserModel(user); } + + @Test + public void testAuthenticateUpgradePlaintext() throws Exception { + IAuthenticationManager auth = newAuthenticationManager(); + + UserModel user = new UserModel("sunnyjim"); + user.password = "password"; + users.updateUserModel(user); + + assertNotNull(auth.authenticate(user.username, user.password.toCharArray(), null)); + + // validate that plaintext password was automatically updated to hashed one + assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":")); + } + + + @Test + public void testAuthenticateUpgradeMD5() throws Exception { + IAuthenticationManager auth = newAuthenticationManager(); + + UserModel user = new UserModel("sunnyjim"); + user.password = "MD5:5F4DCC3B5AA765D61D8327DEB882CF99"; + users.updateUserModel(user); + + assertNotNull(auth.authenticate(user.username, "password".toCharArray(), null)); + + // validate that MD5 password was automatically updated to hashed one + assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":")); + } + + @Test public void testContenairAuthenticate() throws Exception { settings.put(Keys.realm.container.autoCreateAccounts, "true"); diff --git a/src/test/java/com/gitblit/utils/PasswordHashTest.java b/src/test/java/com/gitblit/utils/PasswordHashTest.java new file mode 100644 index 00000000..2fbf6580 --- /dev/null +++ b/src/test/java/com/gitblit/utils/PasswordHashTest.java @@ -0,0 +1,666 @@ +/* + * Copyright 2017 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitblit.utils; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * @author Florian Zschocke + * + */ +public class PasswordHashTest { + + static final String MD5_PASSWORD_0 = "password"; + static final String MD5_HASHED_ENTRY_0 = "MD5:5F4DCC3B5AA765D61D8327DEB882CF99"; + static final String MD5_PASSWORD_1 = "This is a test password"; + static final String MD5_HASHED_ENTRY_1 = "md5:8e1901831af502c0f842d4efb9083bcf"; + static final String MD5_PASSWORD_2 = "版本库管理方案"; + static final String MD5_HASHED_ENTRY_2 = "MD5:980017891ff67cf8a20f23aa810e7b5a"; + static final String MD5_PASSWORD_3 = "PÿrâṃiĐ"; + static final String MD5_HASHED_ENTRY_3 = "MD5:60359b7e22941164708ae2040040521f"; + + static final String CMD5_USERNAME_0 = "Jane Doe"; + static final String CMD5_PASSWORD_0 = "password"; + static final String CMD5_HASHED_ENTRY_0 = "CMD5:DB9639A6E5F21457F9DFD7735FAFA68B"; + static final String CMD5_USERNAME_1 = "Joe Black"; + static final String CMD5_PASSWORD_1 = "ThisIsAWeirdScheme.Weird"; + static final String CMD5_HASHED_ENTRY_1 = "cmd5:5c154768287e32fa605656b98894da89"; + static final String CMD5_USERNAME_2 = "快速便"; + static final String CMD5_PASSWORD_2 = "版本库管理方案"; + static final String CMD5_HASHED_ENTRY_2 = "CMD5:f38575ee8af23ba6d923c0d98ee767fc"; + static final String CMD5_USERNAME_3 = "İńa"; + static final String CMD5_PASSWORD_3 = "PÿrâṃiĐ"; + static final String CMD5_HASHED_ENTRY_3 = "CMD5:f1cdc2348c907677529e0e1b011f6793"; + + static final String PBKDF2_PASSWORD_0 = "password"; + static final String PBKDF2_HASHED_ENTRY_0 = "PBKDF2:70617373776f726450415353574f524470617373776f726450415353574f52440f17d16621b32ae1bb2b1041fcb19e294b35d514d361c08eed385766e38f6f3a"; + static final String PBKDF2_PASSWORD_1 = "A REALLY better scheme than MD5"; + static final String PBKDF2_HASHED_ENTRY_1 = "PBKDF2:$0$46726573682066726f6d207468652053414c54206d696e65206f6620446f6f6de8e50b035679b25ce8b6ab41440938b7b1f97fc0c797fcf59302c2916f6c8fef"; + static final String PBKDF2_PASSWORD_2 = "passwordPASSWORDpassword"; + static final String PBKDF2_HASHED_ENTRY_2 = "pbkdf2:$0$73616c7453414c5473616c7453414c5473616c7453414c5473616c7453414c54560d0f02b565e37695da15141044506d54cb633a5a70b41c574069ea50a1247a"; + static final String PBKDF2_PASSWORD_3 = "foo"; + static final String PBKDF2_HASHED_ENTRY_3 = "PBKDF2WITHHMACSHA256:2d7d3ccaa277787f288e9f929247361bfc83607c6a8447bf496267512e360ba0a97b3114937213b23230072517d65a2e00695a1cbc47a732510840817f22c1bc"; + + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#instanceOf(java.lang.String)} for MD5. + */ + @Test + public void testInstanceOfMD5() { + + PasswordHash pwdh = PasswordHash.instanceOf("md5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.MD5, pwdh.type); + assertTrue("Failed to match " +MD5_HASHED_ENTRY_1, pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), null)); + + pwdh = PasswordHash.instanceOf("MD5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.MD5, pwdh.type); + assertTrue("Failed to match " +MD5_HASHED_ENTRY_0, pwdh.matches(MD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), null)); + + pwdh = PasswordHash.instanceOf("mD5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.MD5, pwdh.type); + assertTrue("Failed to match " +MD5_HASHED_ENTRY_1, pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), null)); + + + pwdh = PasswordHash.instanceOf("CMD5"); + assertNotNull(pwdh); + assertNotEquals(PasswordHash.Type.MD5, pwdh.type); + assertFalse("Failed to match " +MD5_HASHED_ENTRY_1, pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), null)); + } + + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#instanceOf(java.lang.String)} for combined MD5. + */ + @Test + public void testInstanceOfCombinedMD5() { + + PasswordHash pwdh = PasswordHash.instanceOf("cmd5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + assertTrue("Failed to match " +CMD5_HASHED_ENTRY_1, pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_1)); + + pwdh = PasswordHash.instanceOf("cMD5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + assertTrue("Failed to match " +CMD5_HASHED_ENTRY_1, pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_1)); + + pwdh = PasswordHash.instanceOf("CMD5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + assertTrue("Failed to match " +CMD5_HASHED_ENTRY_0, pwdh.matches(CMD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + + + pwdh = PasswordHash.instanceOf("combined-md5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + + pwdh = PasswordHash.instanceOf("COMBINED-MD5"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + + + pwdh = PasswordHash.instanceOf("MD5"); + assertNotNull(pwdh); + assertNotEquals(PasswordHash.Type.CMD5, pwdh.type); + assertFalse("Failed to match " +CMD5_HASHED_ENTRY_1, pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_1)); + } + + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#instanceOf(java.lang.String)} for PBKDF2. + */ + @Test + public void testInstanceOfPBKDF2() { + PasswordHash pwdh = PasswordHash.instanceOf("PBKDF2"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertTrue("Failed to match " +PBKDF2_HASHED_ENTRY_0, pwdh.matches(PBKDF2_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), null)); + + pwdh = PasswordHash.instanceOf("pbkdf2"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertTrue("Failed to match " +PBKDF2_HASHED_ENTRY_1, pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), null)); + + pwdh = PasswordHash.instanceOf("pbKDF2"); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertTrue("Failed to match " +PBKDF2_HASHED_ENTRY_1, pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), null)); + + + pwdh = PasswordHash.instanceOf("md5"); + assertNotNull(pwdh); + assertNotEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertFalse("Failed to match " +PBKDF2_HASHED_ENTRY_1, pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), null)); + } + + + + + /** + * Test that no instance is returned for plaintext or unknown or not + * yet implemented hashing schemes. + */ + @Test + public void testNoInstanceOf() { + PasswordHash pwdh = PasswordHash.instanceOf("plain"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf("PLAIN"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf("Plain"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf("scrypt"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf("bCrypt"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf("BCRYPT"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf("nixe"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceOf(null); + assertNull(pwdh); + } + + + + /** + * Test that for all known hash types an instance is created for a hashed entry + * that can verify the known password. + * + * Test method for {@link com.gitblit.utils.PasswordHash#instanceFor(java.lang.String)}. + */ + @Test + public void testInstanceFor() { + PasswordHash pwdh = PasswordHash.instanceFor(MD5_HASHED_ENTRY_0); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.MD5, pwdh.type); + assertTrue("Failed to match " +MD5_HASHED_ENTRY_0, pwdh.matches(MD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), null)); + + pwdh = PasswordHash.instanceFor(MD5_HASHED_ENTRY_1); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.MD5, pwdh.type); + assertTrue("Failed to match " +MD5_HASHED_ENTRY_1, pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), null)); + + + pwdh = PasswordHash.instanceFor(CMD5_HASHED_ENTRY_0); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + assertTrue("Failed to match " +CMD5_HASHED_ENTRY_0, pwdh.matches(CMD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + + pwdh = PasswordHash.instanceFor(CMD5_HASHED_ENTRY_1); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.CMD5, pwdh.type); + assertTrue("Failed to match " +CMD5_HASHED_ENTRY_1, pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_1)); + + + pwdh = PasswordHash.instanceFor(PBKDF2_HASHED_ENTRY_0); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertTrue("Failed to match " +PBKDF2_HASHED_ENTRY_0, pwdh.matches(PBKDF2_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), null)); + + pwdh = PasswordHash.instanceFor(PBKDF2_HASHED_ENTRY_1); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertTrue("Failed to match " +PBKDF2_HASHED_ENTRY_1, pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), null)); + + pwdh = PasswordHash.instanceFor(PBKDF2_HASHED_ENTRY_3); + assertNotNull(pwdh); + assertEquals(PasswordHash.Type.PBKDF2, pwdh.type); + assertTrue("Failed to match " +PBKDF2_HASHED_ENTRY_3, pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_3.toCharArray(), null)); + } + + /** + * Test that for no instance is returned for plaintext or unknown or + * not yet implemented hashing schemes. + * + * Test method for {@link com.gitblit.utils.PasswordHash#instanceFor(java.lang.String)}. + */ + @Test + public void testInstanceForNaught() { + PasswordHash pwdh = PasswordHash.instanceFor("password"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor("top secret"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor("pass:word"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor("PLAIN:password"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor("SCRYPT:1232rwv12w"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor("BCRYPT:urbvahiaufbvhabaiuevuzggubsbliue"); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor(""); + assertNull(pwdh); + + pwdh = PasswordHash.instanceFor(null); + assertNull(pwdh); + } + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#isHashedEntry(java.lang.String)}. + */ + @Test + public void testIsHashedEntry() { + assertTrue(MD5_HASHED_ENTRY_0, PasswordHash.isHashedEntry(MD5_HASHED_ENTRY_0)); + assertTrue(MD5_HASHED_ENTRY_1, PasswordHash.isHashedEntry(MD5_HASHED_ENTRY_1)); + assertTrue(CMD5_HASHED_ENTRY_0, PasswordHash.isHashedEntry(CMD5_HASHED_ENTRY_0)); + assertTrue(CMD5_HASHED_ENTRY_1, PasswordHash.isHashedEntry(CMD5_HASHED_ENTRY_1)); + assertTrue(PBKDF2_HASHED_ENTRY_0, PasswordHash.isHashedEntry(PBKDF2_HASHED_ENTRY_0)); + assertTrue(PBKDF2_HASHED_ENTRY_1, PasswordHash.isHashedEntry(PBKDF2_HASHED_ENTRY_1)); + assertTrue(PBKDF2_HASHED_ENTRY_2, PasswordHash.isHashedEntry(PBKDF2_HASHED_ENTRY_2)); + assertTrue(PBKDF2_HASHED_ENTRY_3, PasswordHash.isHashedEntry(PBKDF2_HASHED_ENTRY_3)); + + assertFalse(MD5_PASSWORD_1, PasswordHash.isHashedEntry(MD5_PASSWORD_1)); + assertFalse("topsecret", PasswordHash.isHashedEntry("topsecret")); + assertFalse("top:secret", PasswordHash.isHashedEntry("top:secret")); + assertFalse("secret Password", PasswordHash.isHashedEntry("secret Password")); + assertFalse("Empty string", PasswordHash.isHashedEntry("")); + assertFalse("Null", PasswordHash.isHashedEntry(null)); + } + + /** + * Test that hashed entry detection is not case sensitive on the hash type identifier. + * + * Test method for {@link com.gitblit.utils.PasswordHash#isHashedEntry(java.lang.String)}. + */ + @Test + public void testIsHashedEntryCaseInsenitive() { + assertTrue(MD5_HASHED_ENTRY_1.toLowerCase(), PasswordHash.isHashedEntry(MD5_HASHED_ENTRY_1.toLowerCase())); + assertTrue(CMD5_HASHED_ENTRY_1.toLowerCase(), PasswordHash.isHashedEntry(CMD5_HASHED_ENTRY_1.toLowerCase())); + assertTrue(PBKDF2_HASHED_ENTRY_1.toLowerCase(), PasswordHash.isHashedEntry(PBKDF2_HASHED_ENTRY_1.toLowerCase())); + assertTrue(PBKDF2_HASHED_ENTRY_3.toLowerCase(), PasswordHash.isHashedEntry(PBKDF2_HASHED_ENTRY_3.toLowerCase())); + } + + /** + * Test that unknown or not yet implemented hashing schemes are not detected as hashed entries. + * + * Test method for {@link com.gitblit.utils.PasswordHash#isHashedEntry(java.lang.String)}. + */ + @Test + public void testIsHashedEntryUnknown() { + assertFalse("BCRYPT:thisismypassword", PasswordHash.isHashedEntry("BCRYPT:thisismypassword")); + assertFalse("TSTHSH:asdchabufzuzfbhbakrzburzbcuzkuzcbajhbcasjdhbckajsbc", PasswordHash.isHashedEntry("TSTHSH:asdchabufzuzfbhbakrzburzbcuzkuzcbajhbcasjdhbckajsbc")); + } + + + + + /** + * Test creating a hashed entry for scheme MD5. In this scheme there is no salt, so a direct + * comparison to a constant value is possible. + * + * Test method for {@link PasswordHash#toHashedEntry(String, String)} for MD5. + */ + @Test + public void testToHashedEntryMD5() { + PasswordHash pwdh = PasswordHash.instanceOf("MD5"); + String hashedEntry = pwdh.toHashedEntry(MD5_PASSWORD_1, null); + assertTrue(MD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry(MD5_PASSWORD_2, null); + assertTrue(MD5_HASHED_ENTRY_2.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry(MD5_PASSWORD_1, "charlie"); + assertTrue(MD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry(MD5_PASSWORD_3, CMD5_USERNAME_3); + assertTrue(MD5_HASHED_ENTRY_3.equalsIgnoreCase(hashedEntry)); + + + hashedEntry = pwdh.toHashedEntry("badpassword", "charlie"); + assertFalse(MD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry("badpassword", null); + assertFalse(MD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + } + + @Test(expected = IllegalArgumentException.class) + public void testToHashedEntryMD5NullPassword() { + PasswordHash pwdh = PasswordHash.instanceOf("MD5"); + pwdh.toHashedEntry((String)null, null); + } + + + /** + * Test creating a hashed entry for scheme Combined-MD5. In this scheme there is no salt, so a direct + * comparison to a constant value is possible. + * + * Test method for {@link PasswordHash#toHashedEntry(String, String)} for CMD5. + */ + @Test + public void testToHashedEntryCMD5() { + PasswordHash pwdh = PasswordHash.instanceOf("CMD5"); + String hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_1, CMD5_USERNAME_1); + assertTrue(CMD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_2, CMD5_USERNAME_2); + assertTrue(CMD5_HASHED_ENTRY_2.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_3, CMD5_USERNAME_3); + assertTrue(CMD5_HASHED_ENTRY_3.equalsIgnoreCase(hashedEntry)); + + + hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_1, "charlie"); + assertFalse(CMD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + + hashedEntry = pwdh.toHashedEntry("badpassword", "charlie"); + assertFalse(MD5_HASHED_ENTRY_1.equalsIgnoreCase(hashedEntry)); + } + + @Test(expected = IllegalArgumentException.class) + public void testToHashedEntryCMD5NullPassword() { + PasswordHash pwdh = PasswordHash.instanceOf("CMD5"); + pwdh.toHashedEntry((String)null, CMD5_USERNAME_1); + } + + /** + * Test creating a hashed entry for scheme Combined-MD5, when no user is given. + * This should never happen in the application, so we expect an exception to be thrown. + * + * Test method for {@link PasswordHash#toHashedEntry(String, String)} for broken CMD5. + */ + @Test + public void testToHashedEntryCMD5NoUsername() { + PasswordHash pwdh = PasswordHash.instanceOf("CMD5"); + try { + String hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_1, ""); + fail("CMD5 cannot work with an empty '' username. Got: " + hashedEntry); + } + catch (IllegalArgumentException ignored) { /*success*/ } + + try { + String hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_1, " "); + fail("CMD5 cannot work with an empty ' ' username. Got: " + hashedEntry); + } + catch (IllegalArgumentException ignored) { /*success*/ } + + try { + String hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_1, " "); + fail("CMD5 cannot work with an empty ' ' username. Got: " + hashedEntry); + } + catch (IllegalArgumentException ignored) { /*success*/ } + + try { + String hashedEntry = pwdh.toHashedEntry(CMD5_PASSWORD_1, null); + fail("CMD5 cannot work with a null username. Got: " + hashedEntry); + } + catch (IllegalArgumentException ignored) { /*success*/ } + } + + /** + * Test creating a hashed entry for scheme PBKDF2. + * Since this scheme uses a salt, we test by running a match. This is a bit backwards, + * but recreating the PBKDF2 here seems a little overkill. + * + * Test method for {@link PasswordHash#toHashedEntry(String, String)} for PBKDF2. + */ + @Test + public void testToHashedEntryPBKDF2() { + PasswordHash pwdh = PasswordHash.instanceOf("PBKDF2"); + String hashedEntry = pwdh.toHashedEntry(PBKDF2_PASSWORD_1, null); + assertTrue("Type identifier is incorrect.", hashedEntry.startsWith(PasswordHash.Type.PBKDF2.name())); + PasswordHash pwdhverify = PasswordHash.instanceFor(hashedEntry); + assertNotNull(pwdhverify); + assertTrue(PBKDF2_PASSWORD_1, pwdhverify.matches(hashedEntry, PBKDF2_PASSWORD_1.toCharArray(), null)); + + hashedEntry = pwdh.toHashedEntry(PBKDF2_PASSWORD_2, ""); + assertTrue("Type identifier is incorrect.", hashedEntry.startsWith(PasswordHash.Type.PBKDF2.name())); + pwdhverify = PasswordHash.instanceFor(hashedEntry); + assertNotNull(pwdhverify); + assertTrue(PBKDF2_PASSWORD_2, pwdhverify.matches(hashedEntry, PBKDF2_PASSWORD_2.toCharArray(), null)); + + hashedEntry = pwdh.toHashedEntry(PBKDF2_PASSWORD_0, "alpha"); + assertTrue("Type identifier is incorrect.", hashedEntry.startsWith(PasswordHash.Type.PBKDF2.name())); + pwdhverify = PasswordHash.instanceFor(hashedEntry); + assertNotNull(pwdhverify); + assertTrue(PBKDF2_PASSWORD_0, pwdhverify.matches(hashedEntry, PBKDF2_PASSWORD_0.toCharArray(), null)); + } + + @Test(expected = IllegalArgumentException.class) + public void testToHashedEntryPBKDF2NullPassword() { + PasswordHash pwdh = PasswordHash.instanceOf("PBKDF2"); + pwdh.toHashedEntry((String)null, null); + } + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#matches(String, char[], String)} for MD5. + */ + @Test + public void testMatchesMD5() { + PasswordHash pwdh = PasswordHash.instanceOf("MD5"); + + assertTrue("PWD0, Null user", pwdh.matches(MD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), null)); + assertTrue("PWD0, Empty user", pwdh.matches(MD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), "")); + assertTrue("PWD0, With user", pwdh.matches(MD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), "maxine")); + + assertTrue("PWD1, Null user", pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), null)); + assertTrue("PWD1, Empty user", pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), "")); + assertTrue("PWD1, With user", pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_1.toCharArray(), "maxine")); + + assertTrue("PWD2", pwdh.matches(MD5_HASHED_ENTRY_2, MD5_PASSWORD_2.toCharArray(), null)); + assertTrue("PWD3", pwdh.matches(MD5_HASHED_ENTRY_3, MD5_PASSWORD_3.toCharArray(), null)); + + + assertFalse("Matched wrong password", pwdh.matches(MD5_HASHED_ENTRY_1, "wrongpassword".toCharArray(), null)); + assertFalse("Matched wrong password, with empty user", pwdh.matches(MD5_HASHED_ENTRY_1, "wrongpassword".toCharArray(), " ")); + assertFalse("Matched wrong password, with user", pwdh.matches(MD5_HASHED_ENTRY_1, "wrongpassword".toCharArray(), "someuser")); + + assertFalse("Matched empty password", pwdh.matches(MD5_HASHED_ENTRY_1, "".toCharArray(), null)); + assertFalse("Matched empty password, with empty user", pwdh.matches(MD5_HASHED_ENTRY_1, " ".toCharArray(), " ")); + assertFalse("Matched empty password, with user", pwdh.matches(MD5_HASHED_ENTRY_1, " ".toCharArray(), "someuser")); + + assertFalse("Matched null password", pwdh.matches(MD5_HASHED_ENTRY_1, null, null)); + assertFalse("Matched null password, with empty user", pwdh.matches(MD5_HASHED_ENTRY_1, null, " ")); + assertFalse("Matched null password, with user", pwdh.matches(MD5_HASHED_ENTRY_1, null, "someuser")); + + + assertFalse("Matched wrong hashed entry", pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched wrong hashed entry, with empty user", pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched wrong hashed entry, with user", pwdh.matches(MD5_HASHED_ENTRY_1, MD5_PASSWORD_0.toCharArray(), "someuser")); + + assertFalse("Matched empty hashed entry", pwdh.matches("", MD5_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched empty hashed entry, with empty user", pwdh.matches(" ", MD5_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched empty hashed entry, with user", pwdh.matches(" ", MD5_PASSWORD_0.toCharArray(), "someuser")); + + assertFalse("Matched null entry", pwdh.matches(null, MD5_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched null entry, with empty user", pwdh.matches(null, MD5_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched null entry, with user", pwdh.matches(null, MD5_PASSWORD_0.toCharArray(), "someuser")); + + + assertFalse("Matched wrong scheme", pwdh.matches(CMD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched wrong scheme", pwdh.matches(PBKDF2_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched wrong scheme", pwdh.matches(CMD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + } + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#matches(String, char[], String)} for Combined-MD5. + */ + @Test + public void testMatchesCombinedMD5() { + PasswordHash pwdh = PasswordHash.instanceOf("CMD5"); + + assertTrue("PWD0", pwdh.matches(CMD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + assertTrue("PWD1", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_1)); + assertTrue("PWD2", pwdh.matches(CMD5_HASHED_ENTRY_2, CMD5_PASSWORD_2.toCharArray(), CMD5_USERNAME_2)); + assertTrue("PWD3", pwdh.matches(CMD5_HASHED_ENTRY_3, CMD5_PASSWORD_3.toCharArray(), CMD5_USERNAME_3)); + + + + assertFalse("Matched wrong password", pwdh.matches(CMD5_HASHED_ENTRY_1, "wrongpassword".toCharArray(), CMD5_USERNAME_1)); + assertFalse("Matched wrong password", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_1)); + + assertFalse("Matched wrong user", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_0)); + assertFalse("Matched wrong user", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), "Samantha Jones")); + + assertFalse("Matched empty user", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), "")); + assertFalse("Matched empty user", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), " ")); + assertFalse("Matched null user", pwdh.matches(CMD5_HASHED_ENTRY_1, CMD5_PASSWORD_1.toCharArray(), null)); + + assertFalse("Matched empty hashed entry", pwdh.matches("", CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + assertFalse("Matched empty hashed entry, with empty user", pwdh.matches(" ", CMD5_PASSWORD_1.toCharArray(), "")); + assertFalse("Matched empty hashed entry, with null user", pwdh.matches(" ", CMD5_PASSWORD_0.toCharArray(), null)); + + assertFalse("Matched null entry, with user", pwdh.matches(null, CMD5_PASSWORD_1.toCharArray(), CMD5_USERNAME_1)); + assertFalse("Matched null entry, with empty user", pwdh.matches(null, CMD5_PASSWORD_1.toCharArray(), "")); + assertFalse("Matched null entry, with null user", pwdh.matches(null, CMD5_PASSWORD_1.toCharArray(), null)); + + + assertFalse("Matched wrong scheme", pwdh.matches(MD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched wrong scheme", pwdh.matches(PBKDF2_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched wrong scheme", pwdh.matches(MD5_HASHED_ENTRY_0, CMD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + assertFalse("Matched wrong scheme", pwdh.matches(MD5_HASHED_ENTRY_0, MD5_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + } + + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#matches(String, char[], String)} for PBKDF2. + */ + @Test + public void testMatchesPBKDF2() { + PasswordHash pwdh = PasswordHash.instanceOf("PBKDF2"); + + assertTrue("PWD0, Null user", pwdh.matches(PBKDF2_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), null)); + assertTrue("PWD0, Empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), "")); + assertTrue("PWD0, With user", pwdh.matches(PBKDF2_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), "maxine")); + + assertTrue("PWD1, Null user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), null)); + assertTrue("PWD1, Empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), "")); + assertTrue("PWD1, With user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_1.toCharArray(), "Maxim Gorki")); + + assertTrue("PWD2, Null user", pwdh.matches(PBKDF2_HASHED_ENTRY_2, PBKDF2_PASSWORD_2.toCharArray(), null)); + assertTrue("PWD2, Empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_2, PBKDF2_PASSWORD_2.toCharArray(), "")); + assertTrue("PWD2, With user", pwdh.matches(PBKDF2_HASHED_ENTRY_2, PBKDF2_PASSWORD_2.toCharArray(), "Epson")); + + + + assertFalse("Matched wrong password", pwdh.matches(PBKDF2_HASHED_ENTRY_1, "wrongpassword".toCharArray(), null)); + assertFalse("Matched wrong password, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, "wrongpassword".toCharArray(), " ")); + assertFalse("Matched wrong password, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, "wrongpassword".toCharArray(), "someuser")); + + assertFalse("Matched empty password", pwdh.matches(PBKDF2_HASHED_ENTRY_2, "".toCharArray(), null)); + assertFalse("Matched empty password, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_2, " ".toCharArray(), " ")); + assertFalse("Matched empty password, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_2, " ".toCharArray(), "someuser")); + + assertFalse("Matched null password", pwdh.matches(PBKDF2_HASHED_ENTRY_0, null, null)); + assertFalse("Matched null password, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_0, null, " ")); + assertFalse("Matched null password, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_0, null, "someuser")); + + + assertFalse("Matched wrong hashed entry", pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched wrong hashed entry, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched wrong hashed entry, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_1, PBKDF2_PASSWORD_0.toCharArray(), "someuser")); + + assertFalse("Matched empty hashed entry", pwdh.matches("", PBKDF2_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched empty hashed entry, with empty user", pwdh.matches(" ", PBKDF2_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched empty hashed entry, with user", pwdh.matches(" ", PBKDF2_PASSWORD_0.toCharArray(), "someuser")); + + assertFalse("Matched null entry", pwdh.matches(null, PBKDF2_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched null entry, with empty user", pwdh.matches(null, PBKDF2_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched null entry, with user", pwdh.matches(null, PBKDF2_PASSWORD_0.toCharArray(), "someuser")); + + + assertFalse("Matched wrong scheme", pwdh.matches(CMD5_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched wrong scheme", pwdh.matches(MD5_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched wrong scheme", pwdh.matches(CMD5_HASHED_ENTRY_0, PBKDF2_PASSWORD_0.toCharArray(), CMD5_USERNAME_0)); + } + + + /** + * Test method for {@link com.gitblit.utils.PasswordHash#matches(String, char[], String)} + * for old existing entries with the "PBKDF2WITHHMACSHA256" type identifier. + */ + @Test + public void testMatchesPBKDF2Compat() { + PasswordHash pwdh = PasswordHash.instanceOf("PBKDF2"); + + assertTrue("PWD3, Null user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_3.toCharArray(), null)); + assertTrue("PWD3, Empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_3.toCharArray(), "")); + assertTrue("PWD3, With user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_3.toCharArray(), "maxine")); + + + assertFalse("Matched wrong password", pwdh.matches(PBKDF2_HASHED_ENTRY_3, "bar".toCharArray(), null)); + assertFalse("Matched wrong password, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, "bar".toCharArray(), " ")); + assertFalse("Matched wrong password, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, "bar".toCharArray(), "someuser")); + + assertFalse("Matched empty password", pwdh.matches(PBKDF2_HASHED_ENTRY_3, "".toCharArray(), null)); + assertFalse("Matched empty password, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, " ".toCharArray(), " ")); + assertFalse("Matched empty password, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, " ".toCharArray(), "someuser")); + + assertFalse("Matched null password", pwdh.matches(PBKDF2_HASHED_ENTRY_3, null, null)); + assertFalse("Matched null password, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, null, " ")); + assertFalse("Matched null password, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, null, "someuser")); + + + assertFalse("Matched wrong hashed entry", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_0.toCharArray(), null)); + assertFalse("Matched wrong hashed entry, with empty user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_0.toCharArray(), "")); + assertFalse("Matched wrong hashed entry, with user", pwdh.matches(PBKDF2_HASHED_ENTRY_3, PBKDF2_PASSWORD_0.toCharArray(), "someuser")); + } + + @Test + public void getEntryType() { + assertEquals(PasswordHash.Type.MD5, PasswordHash.getEntryType("MD5:blah")); + assertEquals(PasswordHash.Type.MD5, PasswordHash.getEntryType("md5:blah")); + assertEquals(PasswordHash.Type.MD5, PasswordHash.getEntryType("mD5:blah")); + + assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("CMD5:blah")); + assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("cmd5:blah")); + assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("Cmd5:blah")); + + assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("combined-md5:blah")); + assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("COMBINED-MD5:blah")); + assertEquals(PasswordHash.Type.CMD5, PasswordHash.getEntryType("combined-MD5:blah")); + + assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("PBKDF2:blah")); + assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("pbkdf2:blah")); + assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("Pbkdf2:blah")); + assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("pbKDF2:blah")); + + assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("PBKDF2WithHmacSHA256:blah")); + assertEquals(PasswordHash.Type.PBKDF2, PasswordHash.getEntryType("PBKDF2WITHHMACSHA256:blah")); + } + + @Test + public void getEntryValue() { + assertEquals("value", PasswordHash.getEntryValue("MD5:value")); + assertEquals("plain text", PasswordHash.getEntryValue("plain text")); + assertEquals("what this", PasswordHash.getEntryValue(":what this")); + assertEquals("", PasswordHash.getEntryValue(":")); + } +} |