As an Apache Shiro PMC member, I have occasionally contact to cryptographic functions. For example, Shiro 1.x allows hashed passwords in your shiro.ini
configuration.
Now, everyone should know by now that just hashing (and salting) a password is not a good protection against brute force attacks. Even with hundreds or thousands of iterations, such a password can be prone to brute force attacks nowadays. This it is not a surprise that Lez Hazlewood (the original creator of Apache Shiro) had the idea to add an bcrypt implementation.
Now, when starting to implement bcrypt, I quickly found existing storage mechanisms as not sufficient. For example, hashes could be stored as hex or plain string, as well as a Shiro1 format which is very similar to the Posix format used in /etc/shadow
.
New crypt formats
So I came up with the idea to create a new Shiro 2 Crypt Format and ditch the old ones for Shiro 2 (but keeping the classes for upgrading hints). There should really be no need for the insecure formats anymore. Also, as I find passwords potentially stored in your VCS of choice, it might an even better idea to ditch the old algorithms and include the new algorithms only if needed.
Shiro 1 crypt format
The Shiro1CryptFormat looks like this:
$mcfFormatId$algorithmName$iterationCount$base64EncodedSalt$base64EncodedDigest
Shiro 2 crypt format
This is the format I adapted from /etc/shadow
files. It looks like this:
class Argon2HashTest {
private static final TEST_PASSWORD = "secret#shiro,password;Jo8opech";
private static final TEST_PASSWORD_BS = new SimpleByteSource(TEST_PASSWORD)
@Test
void testArgon2Hash() {
// given
def shiro2Format = '$shiro2$argon2id$v=19$m=4096,t=3,p=4$MTIzNDU2Nzg5MDEyMzQ1Ng$bjcHqfb0LPHyS13eVaNcBga9LF12I3k34H5ULt2gyoI'
def expectedPassword = new SimpleByteSource(TEST_PASSWORD)
// when
def hash = new Shiro2CryptFormat().parse(shiro2Format) as Argon2Hash;
def matchesPassword = hash.matchesPassword expectedPassword;
// then
assertEquals Argon2Parameters.ARGON2_VERSION_13, hash.argonVersion
assertEquals 3, hash.iterations
assertEquals 4096, hash.memoryKiB
assertEquals 4, hash.parallelism
assertTrue matchesPassword
}
}
Pluggable Algorithm Providers
As Shiro does not use injection, I chose ServiceLoaders as a plug-in mechanism for loading algorithms. This way, Shiro can easily be extended. I also created an Argon2 implementation, while at it, contained in its own module.
You can see the Pull Request for SHIRO-290 on GitHub, waiting for more reviews. Be sure to add yours! π