A very brief background: I created it while thinking of a way to
cleanly support the PasswordService concept.  The Hasher was my idea
of supporting this as an implementation strategy - not necessarily
something people would use regularly.  It could turn out that it is a
first-class citizen in Shiro's hashing support, but I haven't thought
about how to position it beyond supporting the PasswordService.
Ideas/thoughts are welcome!

On Fri, Jun 3, 2011 at 10:21 AM, Les Hazlewood <[email protected]> wrote:
> Thanks for this Maria and Kalle.
>
> Just a quick note though - the Hasher stuff I wrote is still in flux -
> it's not necessarily scoped out to how it should/could be before being
> 'releasable'.  Any thoughts or feedback is appreciated!
>
> Cheers,
>
> Les
>
> On Fri, Jun 3, 2011 at 7:52 AM,  <[email protected]> wrote:
>> Author: kaosko
>> Date: Fri Jun  3 14:52:04 2011
>> New Revision: 1131059
>>
>> URL: http://svn.apache.org/viewvc?rev=1131059&view=rev
>> Log:
>> FIXED - issue SHIRO-302: DefaultHasher does not generate random salt
>> https://issues.apache.org/jira/browse/SHIRO-302
>> - apply Maria Jurcovicova's patch as is, including unit tests
>>
>> Added:
>>    shiro/trunk/core/src/test/java/org/apache/shiro/crypto/hash/
>>    
>> shiro/trunk/core/src/test/java/org/apache/shiro/crypto/hash/DefaultHasherTest.java
>> Modified:
>>    
>> shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/DefaultHasher.java
>>
>> Modified: 
>> shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/DefaultHasher.java
>> URL: 
>> http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/DefaultHasher.java?rev=1131059&r1=1131058&r2=1131059&view=diff
>> ==============================================================================
>> --- 
>> shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/DefaultHasher.java
>>  (original)
>> +++ 
>> shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/DefaultHasher.java
>>  Fri Jun  3 14:52:04 2011
>> @@ -153,7 +153,7 @@ public class DefaultHasher implements Co
>>             publicSaltBytes = null;
>>         }
>>         if (publicSaltBytes == null) {
>> -            getRandomNumberGenerator().nextBytes().getBytes();
>> +               publicSaltBytes = 
>> getRandomNumberGenerator().nextBytes().getBytes();
>>         }
>>
>>         String algorithmName = getHashAlgorithmName();
>>
>> Added: 
>> shiro/trunk/core/src/test/java/org/apache/shiro/crypto/hash/DefaultHasherTest.java
>> URL: 
>> http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/crypto/hash/DefaultHasherTest.java?rev=1131059&view=auto
>> ==============================================================================
>> --- 
>> shiro/trunk/core/src/test/java/org/apache/shiro/crypto/hash/DefaultHasherTest.java
>>  (added)
>> +++ 
>> shiro/trunk/core/src/test/java/org/apache/shiro/crypto/hash/DefaultHasherTest.java
>>  Fri Jun  3 14:52:04 2011
>> @@ -0,0 +1,154 @@
>> +package org.apache.shiro.crypto.hash;
>> +
>> +import java.util.Arrays;
>> +
>> +import junit.framework.TestCase;
>> +
>> +import org.apache.shiro.crypto.SecureRandomNumberGenerator;
>> +import org.apache.shiro.util.ByteSource;
>> +import org.junit.Test;
>> +
>> +/**
>> + * Test for {@link DefaultHasher} class.
>> + *
>> + */
>> +public class DefaultHasherTest {
>> +
>> +       /**
>> +        * If the same string is hashed twice and no salt was supplied, 
>> hashed
>> +        * result should be different in each case.
>> +        */
>> +       @Test
>> +       public void testOnlyRandomSaltRandomness() {
>> +               Hasher hasher = createHasher();
>> +
>> +               HashResponse firstHash = hashString(hasher, "password");
>> +               HashResponse secondHash = hashString(hasher, "password");
>> +
>> +               assertNotEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       /**
>> +        * If a string is hashed and no salt was supplied, random salt is 
>> generated.
>> +        * Hash of the same string with generated random salt should return 
>> the
>> +        * same result.
>> +        */
>> +       @Test
>> +       public void testOnlyRandomSaltReturn() {
>> +               Hasher hasher = createHasher();
>> +
>> +               HashResponse firstHash = hashString(hasher, "password");
>> +               HashResponse secondHash = hashString(hasher, "password", 
>> firstHash.getSalt().getBytes());
>> +
>> +               TestCase.assertEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       /**
>> +        * Two different strings hashed with the same salt should result in 
>> two different
>> +        * hashes.
>> +        */
>> +       @Test
>> +       public void testOnlyRandomSaltHash() {
>> +               Hasher hasher = createHasher();
>> +
>> +               HashResponse firstHash = hashString(hasher, "password");
>> +               HashResponse secondHash = hashString(hasher, "password2", 
>> firstHash.getSalt().getBytes());
>> +
>> +               assertNotEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       /**
>> +        * If the same string is hashed twice and only base salt was 
>> supplied, hashed
>> +        * result should be different in each case.
>> +        */
>> +       @Test
>> +       public void testBothSaltsRandomness() {
>> +               Hasher hasher = createHasherWithSalt();
>> +
>> +               HashResponse firstHash = hashString(hasher, "password");
>> +               HashResponse secondHash = hashString(hasher, "password");
>> +
>> +               assertNotEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       /**
>> +        * If a string is hashed and only base salt was supplied, random 
>> salt is generated.
>> +        * Hash of the same string with generated random salt should return 
>> the
>> +        * same result.
>> +        */
>> +       @Test
>> +       public void testBothSaltsReturn() {
>> +               Hasher hasher = createHasherWithSalt();
>> +
>> +               HashResponse firstHash = hashString(hasher, "password");
>> +               HashResponse secondHash = hashString(hasher, "password", 
>> firstHash.getSalt().getBytes());
>> +
>> +               TestCase.assertEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       /**
>> +        * Two different strings hashed with the same salt should result in 
>> two different
>> +        * hashes.
>> +        */
>> +       @Test
>> +       public void testBothSaltsHash() {
>> +               Hasher hasher = createHasherWithSalt();
>> +
>> +               HashResponse firstHash = hashString(hasher, "password");
>> +               HashResponse secondHash = hashString(hasher, "password2", 
>> firstHash.getSalt().getBytes());
>> +
>> +               assertNotEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       /**
>> +        * Hash result is different if the base salt is added.
>> +        */
>> +       @Test
>> +       public void testBaseSaltChangesResult() {
>> +               Hasher saltedHasher = createHasherWithSalt();
>> +               Hasher hasher = createHasher();
>> +
>> +               HashResponse firstHash = hashStringPredictable(saltedHasher, 
>> "password");
>> +               HashResponse secondHash = hashStringPredictable(hasher, 
>> "password");
>> +
>> +               assertNotEquals(firstHash.getHash().toBase64(), 
>> secondHash.getHash().toBase64());
>> +       }
>> +
>> +       protected HashResponse hashString(Hasher hasher, String string) {
>> +               return hasher.computeHash(new 
>> SimpleHashRequest(ByteSource.Util.bytes(string)));
>> +       }
>> +
>> +       protected HashResponse hashString(Hasher hasher, String string, 
>> byte[] salt) {
>> +               return hasher.computeHash(new 
>> SimpleHashRequest(ByteSource.Util.bytes(string), 
>> ByteSource.Util.bytes(salt)));
>> +       }
>> +
>> +       private HashResponse hashStringPredictable(Hasher hasher, String 
>> string) {
>> +               byte[] salt = new byte[20];
>> +               Arrays.fill(salt, (byte) 2);
>> +               return hasher.computeHash(new 
>> SimpleHashRequest(ByteSource.Util.bytes(string), 
>> ByteSource.Util.bytes(salt)));
>> +       }
>> +
>> +       private Hasher createHasher() {
>> +               return new DefaultHasher();
>> +       }
>> +
>> +       private Hasher createHasherWithSalt() {
>> +               DefaultHasher defaultHasher = new DefaultHasher();
>> +               defaultHasher.setBaseSalt((new 
>> SecureRandomNumberGenerator()).nextBytes().getBytes());
>> +
>> +               return defaultHasher;
>> +       }
>> +
>> +       private void assertNotEquals(String str1, String str2) {
>> +               boolean equals = equals(str1, str2);
>> +               if (equals)
>> +                       TestCase.fail("Strings are supposed to be 
>> different.");
>> +       }
>> +
>> +       protected boolean equals(String str1, String str2) {
>> +               if (str1 == null)
>> +                       return str2 == null;
>> +
>> +               return str1.equals(str2);
>> +       }
>> +}
>

Reply via email to