├── docs.pdf ├── src ├── META-INF │ └── MANIFEST.MF ├── sorcerersisle │ └── cfpassphrase │ │ ├── lucee │ │ ├── MANIFEST.MF │ │ ├── PassphraseCheck.java │ │ ├── PassphraseInfo.java │ │ ├── PassphraseHash.java │ │ ├── cfPassphrase.tld │ │ ├── PassphraseTag.java │ │ └── cfPassphrase.fld │ │ ├── coldfusion │ │ ├── PassphraseCheck.java │ │ ├── PassphraseInfo.java │ │ ├── PassphraseHash.java │ │ ├── Passphrase.cfc │ │ └── Passphrase.cfm │ │ ├── cfPassphrase.java │ │ ├── railo │ │ ├── PassphraseCheck.java │ │ ├── PassphraseInfo.java │ │ ├── PassphraseHash.java │ │ ├── cfPassphrase.tld │ │ ├── PassphraseTag.java │ │ └── cfPassphrase.fld │ │ ├── openbd │ │ ├── cfPassphrasePlugin.java │ │ ├── PassphraseInfo.java │ │ ├── PassphraseCheck.java │ │ ├── PassphraseHash.java │ │ └── PassphraseTag.java │ │ ├── Utils.java │ │ ├── coldbox │ │ └── Passphrase.cfc │ │ └── Impl.java ├── com │ └── lambdaworks │ │ ├── crypto │ │ ├── PBKDF.java │ │ ├── SCryptUtil.java │ │ └── SCrypt.java │ │ └── codec │ │ └── Base64.java ├── crackstation │ └── PBKDF2 │ │ └── PasswordHash.java └── org │ └── mindrot │ └── jbcrypt │ └── BCrypt.java ├── test ├── runtests.cfm ├── Pbkdf2Test.cfc ├── ScryptTest.cfc ├── TestBase.cfc ├── BcryptTest.cfc └── UsageTest.cfc ├── readme.md └── license.txt /docs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boughtonp/cfpassphrase/HEAD/docs.pdf -------------------------------------------------------------------------------- /src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Sealed: true 3 | Main-Class: sorcerersisle.cfpassphrase.cfPassphrase 4 | 5 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Sealed: true 3 | Main-Class: sorcerersisle.cfpassphrase.cfPassphrase 4 | Bundle-ManifestVersion: 2 5 | Bundle-SymbolicName: sorcerersisle.cfpassphrase 6 | Bundle-Version: 0.2 7 | Bundle-Name: cfPassphrase 8 | Bundle-Description: Provides functions PassphraseHash, PassphraseCheck and PassphraseInfo to Lucee. 9 | Bundle-License: LGPLv3 10 | Bundle-DocURL: https://docs.sorcerersisle.com/cfpassphrase 11 | Export-Package: sorcerersisle.cfpassphrase.lucee.PassphraseInfo,sorcerersisle.cfpassphrase.lucee.PassphraseHash,sorcerersisle.cfpassphrase.lucee.PassphraseCheck 12 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/coldfusion/PassphraseCheck.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.coldfusion; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | 6 | 7 | public final class PassphraseCheck 8 | { 9 | 10 | 11 | public static Boolean call 12 | ( String Passphrase , String Hash ) 13 | throws Exception 14 | { return call(Passphrase,Hash,null); } 15 | 16 | 17 | public static Boolean call 18 | ( String Passphrase 19 | , String Hash 20 | , String Algorithm 21 | ) 22 | throws Exception 23 | { 24 | return Impl.check(Passphrase,Hash,Algorithm); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /test/runtests.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | cfPassphrase Tests 6 | Testing... 7 | 8 | 9 | 10 | 11 |
  • #createObject('UsageTest').init( IncludeSlowTests )# 12 |
  • #createObject('BcryptTest').init( IncludeSlowTests )# 13 |
  • #createObject('Pbkdf2Test').init( IncludeSlowTests )# 14 |
  • #createObject('ScryptTest').init( IncludeSlowTests )# 15 |

    Total Time: ~#round((getTickCount()-s)/1000)# seconds 16 | 17 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/coldfusion/PassphraseInfo.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.coldfusion; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import coldfusion.runtime.Struct; 6 | 7 | 8 | public final class PassphraseInfo 9 | { 10 | 11 | 12 | public static Struct call 13 | ( String Passphrase ) 14 | throws Throwable 15 | { return call(Passphrase,null); } 16 | 17 | 18 | public static Struct call 19 | ( String Hash 20 | , String Algorithm 21 | ) 22 | throws Throwable 23 | { 24 | Struct Result = new Struct(); 25 | 26 | Result.putAll(Impl.info(Hash,Algorithm)); 27 | 28 | return Result; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/cfPassphrase.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase; 3 | 4 | import java.io.IOException; 5 | import javax.swing.JOptionPane; 6 | 7 | 8 | public final class cfPassphrase 9 | { 10 | 11 | public static void main 12 | ( String[] Args ) 13 | { 14 | String Msg = 15 | "cfPassphrase v0.2\n" 16 | + "\n" 17 | + "Project Info: https://www.sorcerersisle.com/software/cfpassphrase\n" 18 | + "Install Docs: https://docs.sorcerersisle.com/cfpassphrase/Installation\n" 19 | ; 20 | 21 | 22 | if (isJavaw() ) 23 | JOptionPane.showMessageDialog(null,Msg); 24 | else 25 | System.out.println(Msg); 26 | } 27 | 28 | 29 | private static boolean isJavaw() 30 | { 31 | try 32 | { 33 | System.in.available(); 34 | return false; 35 | } 36 | catch (IOException e) 37 | { 38 | return true; 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/coldfusion/PassphraseHash.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.coldfusion; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import coldfusion.runtime.Struct; 6 | import java.util.Map; 7 | 8 | 9 | public final class PassphraseHash 10 | { 11 | 12 | 13 | public static String call 14 | ( String Passphrase ) 15 | throws Exception 16 | { return call(Passphrase,null,null); } 17 | 18 | public static String call 19 | ( String Passphrase , String Algorithm ) 20 | throws Exception 21 | { return call(Passphrase,Algorithm,null); } 22 | 23 | 24 | @SuppressWarnings("unchecked") 25 | public static String call 26 | ( String Passphrase 27 | , String Algorithm 28 | , Struct AlgorithmParams 29 | ) 30 | throws Exception 31 | { 32 | return Impl.hash 33 | ( Passphrase 34 | , Algorithm 35 | , (Map) AlgorithmParams 36 | ); 37 | } 38 | 39 | 40 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/PassphraseCheck.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.lucee; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import lucee.runtime.ext.function.Function; 6 | import lucee.runtime.PageContext; 7 | import lucee.runtime.exp.PageException; 8 | import lucee.loader.engine.CFMLEngineFactory; 9 | 10 | 11 | @SuppressWarnings("serial") 12 | public final class PassphraseCheck 13 | implements Function 14 | { 15 | 16 | 17 | public static Boolean call 18 | ( PageContext pc , String Passphrase , String Hash ) 19 | throws PageException 20 | { return call(pc,Passphrase,Hash,null); } 21 | 22 | 23 | public static Boolean call 24 | ( PageContext pc 25 | , String Passphrase 26 | , String Hash 27 | , String Algorithm 28 | ) 29 | throws PageException 30 | { 31 | try 32 | { 33 | return Impl.check( Passphrase , Hash , Algorithm ); 34 | } 35 | catch(Exception Ex) 36 | { 37 | throw CFMLEngineFactory.getInstance().getCastUtil().toPageException( Ex ); 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/railo/PassphraseCheck.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.railo; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import railo.runtime.ext.function.Function; 6 | import railo.runtime.PageContext; 7 | import railo.runtime.exp.PageException; 8 | import railo.loader.engine.CFMLEngineFactory; 9 | 10 | 11 | @SuppressWarnings("serial") 12 | public final class PassphraseCheck 13 | implements Function 14 | { 15 | 16 | 17 | public static Boolean call 18 | ( PageContext pc , String Passphrase , String Hash ) 19 | throws PageException 20 | { return call(pc,Passphrase,Hash,null); } 21 | 22 | 23 | public static Boolean call 24 | ( PageContext pc 25 | , String Passphrase 26 | , String Hash 27 | , String Algorithm 28 | ) 29 | throws PageException 30 | { 31 | try 32 | { 33 | return Impl.check( Passphrase , Hash , Algorithm ); 34 | } 35 | catch(Exception Ex) 36 | { 37 | throw CFMLEngineFactory.getInstance().getCastUtil().toPageException( Ex ); 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/PassphraseInfo.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.lucee; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import lucee.runtime.ext.function.Function; 6 | import lucee.runtime.PageContext; 7 | import lucee.runtime.exp.PageException; 8 | import lucee.runtime.type.Struct; 9 | import lucee.runtime.util.Cast; 10 | import lucee.loader.engine.CFMLEngineFactory; 11 | 12 | 13 | @SuppressWarnings("serial") 14 | public final class PassphraseInfo 15 | implements Function 16 | { 17 | 18 | 19 | public static Struct call 20 | ( PageContext pc , String Passphrase ) 21 | throws PageException 22 | { return call(pc,Passphrase,null); } 23 | 24 | 25 | public static Struct call 26 | ( PageContext pc 27 | , String Hash 28 | , String Algorithm 29 | ) 30 | throws PageException 31 | { 32 | Cast Caster = CFMLEngineFactory.getInstance().getCastUtil(); 33 | 34 | try 35 | { 36 | return Caster.toStruct( Impl.info(Hash,Algorithm) ); 37 | } 38 | catch(Exception Ex) 39 | { 40 | throw Caster.toPageException( Ex ); 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/railo/PassphraseInfo.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.railo; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import railo.runtime.ext.function.Function; 6 | import railo.runtime.PageContext; 7 | import railo.runtime.exp.PageException; 8 | import railo.runtime.type.Struct; 9 | import railo.runtime.util.Cast; 10 | import railo.loader.engine.CFMLEngineFactory; 11 | 12 | 13 | @SuppressWarnings("serial") 14 | public final class PassphraseInfo 15 | implements Function 16 | { 17 | 18 | 19 | public static Struct call 20 | ( PageContext pc , String Passphrase ) 21 | throws PageException 22 | { return call(pc,Passphrase,null); } 23 | 24 | 25 | public static Struct call 26 | ( PageContext pc 27 | , String Hash 28 | , String Algorithm 29 | ) 30 | throws PageException 31 | { 32 | Cast Caster = CFMLEngineFactory.getInstance().getCastUtil(); 33 | 34 | try 35 | { 36 | return Caster.toStruct( Impl.info(Hash,Algorithm) ); 37 | } 38 | catch(Exception Ex) 39 | { 40 | throw Caster.toPageException( Ex ); 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/openbd/cfPassphrasePlugin.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.openbd; 3 | 4 | import com.bluedragon.plugin.Plugin; 5 | import com.bluedragon.plugin.PluginManagerInterface; 6 | import com.naryx.tagfusion.xmlConfig.xmlCFML; 7 | 8 | public final class cfPassphrasePlugin implements Plugin 9 | { 10 | 11 | public cfPassphrasePlugin() 12 | { 13 | 14 | } 15 | 16 | public String getPluginName() 17 | { 18 | return "cfPassphrase"; 19 | } 20 | 21 | public String getPluginVersion() 22 | { 23 | return "0.2-rc"; 24 | } 25 | 26 | public String getPluginDescription() 27 | { 28 | return "Provides functions PassphraseHash, PassphraseCheck and PassphraseInfo to OpenBD."; 29 | } 30 | 31 | public void pluginStart( PluginManagerInterface Manager, xmlCFML SystemParameters ) 32 | { 33 | Manager.registerFunction("PassphraseHash" ,"sorcerersisle.cfpassphrase.openbd.PassphraseHash"); 34 | Manager.registerFunction("PassphraseCheck","sorcerersisle.cfpassphrase.openbd.PassphraseCheck"); 35 | Manager.registerFunction("PassphraseInfo","sorcerersisle.cfpassphrase.openbd.PassphraseInfo"); 36 | Manager.registerTag("cfpassphrase","sorcerersisle.cfpassphrase.openbd.PassphraseTag"); 37 | } 38 | 39 | public void pluginStop( PluginManagerInterface Manager ) 40 | { 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/openbd/PassphraseInfo.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.openbd; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import com.naryx.tagfusion.expression.function.functionBase; 6 | import com.naryx.tagfusion.cfm.engine.cfArgStructData; 7 | import com.naryx.tagfusion.cfm.engine.cfData; 8 | import com.naryx.tagfusion.cfm.engine.cfSession; 9 | import com.naryx.tagfusion.cfm.engine.cfStructData; 10 | import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; 11 | 12 | 13 | @SuppressWarnings("serial") 14 | public final class PassphraseInfo 15 | extends functionBase 16 | { 17 | 18 | public PassphraseInfo() 19 | { 20 | min = 1; 21 | max = 2; 22 | setNamedParams( new String[] { "Hash" , "Algorithm" } ); 23 | } 24 | 25 | 26 | public cfData execute 27 | ( cfSession _session 28 | , cfArgStructData ArgStruct 29 | ) 30 | throws cfmRunTimeException 31 | { 32 | try 33 | { 34 | String Hash = getNamedStringParam( ArgStruct , "Hash" , null ); 35 | String Algorithm = getNamedStringParam( ArgStruct , "Algorithm" , null ); 36 | 37 | cfStructData Result = new cfStructData(); 38 | Result.putAll( Impl.info(Hash,Algorithm) ); 39 | 40 | return Result; 41 | } 42 | catch(Exception Ex) 43 | { 44 | throw new cfmRunTimeException(_session,Ex); 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/openbd/PassphraseCheck.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.openbd; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import com.naryx.tagfusion.expression.function.functionBase; 6 | import com.naryx.tagfusion.cfm.engine.cfArgStructData; 7 | import com.naryx.tagfusion.cfm.engine.cfBooleanData; 8 | import com.naryx.tagfusion.cfm.engine.cfSession; 9 | import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; 10 | 11 | @SuppressWarnings("serial") 12 | public final class PassphraseCheck 13 | extends functionBase 14 | { 15 | 16 | public PassphraseCheck() 17 | { 18 | min = 2; 19 | max = 3; 20 | setNamedParams( new String[] { "Passphrase" , "Hash" , "Algorithm" } ); 21 | } 22 | 23 | 24 | public cfBooleanData execute 25 | ( cfSession _session 26 | , cfArgStructData ArgStruct 27 | ) 28 | throws cfmRunTimeException 29 | { 30 | try 31 | { 32 | String Passphrase = getNamedStringParam( ArgStruct , "Passphrase" , null ); 33 | String Hash = getNamedStringParam( ArgStruct , "Hash" , null ); 34 | String Algorithm = getNamedStringParam( ArgStruct , "Algorithm" , null ); 35 | 36 | return cfBooleanData.getcfBooleanData 37 | ( Impl.check 38 | ( Passphrase , Hash , Algorithm ) 39 | ); 40 | } 41 | catch(Exception Ex) 42 | { 43 | throw new cfmRunTimeException(_session,Ex); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /test/Pbkdf2Test.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function init( IncludeSlowTests ) 5 | { 6 | super.init( argumentcollection=arguments ); 7 | 8 | this.Name = 'pbkdf2'; 9 | 10 | super.start(); 11 | 12 | this.test_info(); 13 | 14 | if ( IncludeSlowTests ) 15 | this.test_basic(); 16 | else 17 | skip( 30 ); 18 | 19 | return super.end(); 20 | } 21 | 22 | 23 | function test_info( TestData ) 24 | { 25 | var info2 = PassphraseInfo('180:5fabd8b160f5f9225ec5569ce2f02d5a2e29a29e0b280614:d11a602ecc7830280b25cdd29b539e9e0f8438a0a43e6637'); 26 | assertEqual(info2.Algorithm,'PBKDF2'); 27 | assertEqual(info2.Status,'Supported'); 28 | assertEqual(info2.Iterations,180); 29 | assertEqual(info2.Salt,'5fabd8b160f5f9225ec5569ce2f02d5a2e29a29e0b280614'); 30 | assertEqual(info2.Hash,'d11a602ecc7830280b25cdd29b539e9e0f8438a0a43e6637'); 31 | assertEqual(StructCount(info2),5); 32 | } 33 | 34 | 35 | function test_basic() 36 | { 37 | for ( var i=0 ; i<10 ; ++i ) 38 | { 39 | var password = ""&i; 40 | var hash = PassphraseHash(password,'pbkdf2'); 41 | var secondHash = PassphraseHash(password,'pbkdf2'); 42 | 43 | assertNotEqual( hash , secondHash , "Two hashes are equal." ); 44 | 45 | var wrongPassword = ""&(i+1); 46 | assertFalse( PassphraseCheck(wrongPassword,hash) , "Wrong password accepted." ); 47 | 48 | assertTrue( PassphraseCheck(password,hash) , "Good password not accepted." ); 49 | } 50 | } 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/PassphraseHash.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.lucee; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import lucee.runtime.ext.function.Function; 6 | import lucee.runtime.PageContext; 7 | import lucee.runtime.exp.PageException; 8 | import lucee.loader.engine.CFMLEngineFactory; 9 | import lucee.runtime.type.Struct; 10 | import lucee.runtime.util.Cast; 11 | 12 | 13 | @SuppressWarnings("serial") 14 | public final class PassphraseHash 15 | implements Function 16 | { 17 | 18 | 19 | public static String call 20 | ( PageContext pc , String Passphrase ) 21 | throws PageException 22 | { return call(pc,Passphrase,null,null); } 23 | 24 | public static String call 25 | ( PageContext pc , String Passphrase , String Algorithm ) 26 | throws PageException 27 | { return call(pc,Passphrase,Algorithm,null); } 28 | 29 | 30 | @SuppressWarnings("unchecked") 31 | public static String call 32 | ( PageContext pc 33 | , String Passphrase 34 | , String Algorithm 35 | , Struct AlgorithmParams 36 | ) 37 | throws PageException 38 | { 39 | Cast Caster = CFMLEngineFactory.getInstance().getCastUtil(); 40 | 41 | try 42 | { 43 | return Impl.hash 44 | ( Passphrase 45 | , Algorithm 46 | , Caster.toMap(AlgorithmParams,null) 47 | ); 48 | } 49 | catch(Exception Ex) 50 | { 51 | throw Caster.toPageException( Ex ); 52 | } 53 | 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/railo/PassphraseHash.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.railo; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import railo.runtime.ext.function.Function; 6 | import railo.runtime.PageContext; 7 | import railo.runtime.exp.PageException; 8 | import railo.loader.engine.CFMLEngineFactory; 9 | import railo.runtime.type.Struct; 10 | import railo.runtime.util.Cast; 11 | 12 | 13 | @SuppressWarnings("serial") 14 | public final class PassphraseHash 15 | implements Function 16 | { 17 | 18 | 19 | public static String call 20 | ( PageContext pc , String Passphrase ) 21 | throws PageException 22 | { return call(pc,Passphrase,null,null); } 23 | 24 | public static String call 25 | ( PageContext pc , String Passphrase , String Algorithm ) 26 | throws PageException 27 | { return call(pc,Passphrase,Algorithm,null); } 28 | 29 | 30 | @SuppressWarnings("unchecked") 31 | public static String call 32 | ( PageContext pc 33 | , String Passphrase 34 | , String Algorithm 35 | , Struct AlgorithmParams 36 | ) 37 | throws PageException 38 | { 39 | Cast Caster = CFMLEngineFactory.getInstance().getCastUtil(); 40 | 41 | try 42 | { 43 | return Impl.hash 44 | ( Passphrase 45 | , Algorithm 46 | , Caster.toMap(AlgorithmParams,null) 47 | ); 48 | } 49 | catch(Exception Ex) 50 | { 51 | throw Caster.toPageException( Ex ); 52 | } 53 | 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/openbd/PassphraseHash.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.openbd; 3 | 4 | import sorcerersisle.cfpassphrase.*; 5 | import com.naryx.tagfusion.expression.function.functionBase; 6 | import com.naryx.tagfusion.cfm.engine.cfSession; 7 | import com.naryx.tagfusion.cfm.engine.cfArgStructData; 8 | import com.naryx.tagfusion.cfm.engine.cfStringData; 9 | import com.naryx.tagfusion.cfm.engine.cfStructData; 10 | import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; 11 | 12 | @SuppressWarnings("serial") 13 | public final class PassphraseHash 14 | extends functionBase 15 | { 16 | 17 | public PassphraseHash() 18 | { 19 | min = 1; 20 | max = 3; 21 | setNamedParams( new String[] { "Passphrase" , "Algorithm" , "AlgorithmParams" } ); 22 | } 23 | 24 | 25 | @SuppressWarnings("unchecked") 26 | public cfStringData execute 27 | ( cfSession _session 28 | , cfArgStructData ArgStruct 29 | ) 30 | throws cfmRunTimeException 31 | { 32 | try 33 | { 34 | String Passphrase = getNamedStringParam( ArgStruct , "Passphrase" , null ); 35 | String Algorithm = getNamedStringParam( ArgStruct , "Algorithm" , null ); 36 | cfStructData AlgorithmParams = (cfStructData) getNamedParam( ArgStruct , "AlgorithmParams" , null ); 37 | 38 | return new cfStringData 39 | ( Impl.hash 40 | ( Passphrase 41 | , Algorithm 42 | , AlgorithmParams 43 | ) 44 | ); 45 | 46 | } 47 | catch(Exception Ex) 48 | { 49 | throw new cfmRunTimeException(_session,Ex); 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /test/ScryptTest.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function init( IncludeSlowTests ) 5 | { 6 | super.init( argumentcollection=arguments ); 7 | 8 | this.Name = 'scrypt'; 9 | 10 | super.start(); 11 | 12 | this.test_info(); 13 | 14 | if ( IncludeSlowTests ) 15 | this.test_basic(); 16 | else 17 | skip( 30 ); 18 | 19 | return super.end(); 20 | } 21 | 22 | 23 | function test_info( TestData ) 24 | { 25 | var info3 = PassphraseInfo('$s0$501ff$5JQALASFiKGKdfY9Z0GYMA==$8c4aMGktdRlLIeY+erIA62fNtgb2OxJrjyhw+XeWHk4='); 26 | assertEqual(info3.Algorithm,'SCrypt'); 27 | assertEqual(info3.Status,'Supported'); 28 | assertEqual(info3.Version,'0'); 29 | assertEqual(info3.CpuCost,32); 30 | assertEqual(info3.MemoryCost,1); 31 | assertEqual(info3.Parallelization,255); 32 | assertEqual(info3.Salt,'5JQALASFiKGKdfY9Z0GYMA=='); 33 | assertEqual(info3.Hash,'8c4aMGktdRlLIeY+erIA62fNtgb2OxJrjyhw+XeWHk4='); 34 | assertEqual(StructCount(info3),8); 35 | } 36 | 37 | 38 | function test_basic() 39 | { 40 | var Passwd = "secret"; 41 | var N = 16384; 42 | var r = 8; 43 | var p = 1; 44 | 45 | var hashed = PassphraseHash(Passwd,'scrypt',{CpuCost:N,MemoryCost:r,Parallelization:p}); 46 | 47 | var parts = hashed.split('\$'); 48 | 49 | assertEqual(len(parts),5); 50 | assertEqual(parts[1],""); 51 | assertEqual(parts[2],"s0"); 52 | assertEqual(len(BinaryDecode(parts[4],'base64')),16); 53 | assertEqual(len(BinaryDecode(parts[5],'base64')),32); 54 | 55 | var params = InputBaseN(parts[3],16); 56 | assertEqual( N , 2^( BitAnd(BitShRn(params,16),65535)) ); 57 | assertEqual( r , BitAnd(BitShRn(params,8),255) ); 58 | assertEqual( p , BitAnd(BitShRn(params,0),255) ); 59 | 60 | assertTrue(PassphraseCheck(passwd,hashed)); 61 | assertFalse(PassphraseCheck("s3cr3t",hashed)); 62 | } 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/cfPassphrase.tld: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.00 5 | 6 | cfpassphrase-tag 7 | https://www.sorcerersisle.com/software/cfpassphrase 8 | cfPassphrase Tag 9 | Provides tag cfpassphrase to Lucee. 10 | 11 | cf 12 | 13 | 14 | 15 | passphrase 16 | sorcerersisle.cfpassphrase.lucee.PassphraseTag 17 | empty 18 | false 19 | Checks or calculates a hash using the specified KDF algorithm. 20 | dynamic 21 | 3 22 | 6 23 | 24 | Action 25 | string 26 | true 27 | Specify [hash], [check], or [info] to determine what action tag takes. 28 | 29 | 30 | Variable 31 | string 32 | true 33 | Name of variable to contain result of action. 34 | 35 | 36 | Passphrase 37 | string 38 | false 39 | The passphrase to be hashed or checked. (action=hash,action=check) 40 | 41 | 42 | Hash 43 | string 44 | false 45 | The existing hash to be checked against. (action=check,action=info) 46 | 47 | 48 | Algorithm 49 | string 50 | false 51 | Algorithm to be used. 52 | 53 | 54 | AlgorithmParams 55 | struct 56 | false 57 | Optional parameters to be used by algorithm. (action=hash) 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/railo/cfPassphrase.tld: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.00 5 | 6 | cfpassphrase-tag 7 | https://www.sorcerersisle.com/software/cfpassphrase 8 | cfPassphrase Tag 9 | Provides tag cfpassphrase to Railo. 10 | 11 | cf 12 | 13 | 14 | 15 | passphrase 16 | sorcerersisle.cfpassphrase.railo.PassphraseTag 17 | empty 18 | false 19 | Checks or calculates a hash using the specified KDF algorithm. 20 | dynamic 21 | 3 22 | 6 23 | 24 | Action 25 | string 26 | true 27 | Specify [hash], [check], or [info] to determine what action tag takes. 28 | 29 | 30 | Variable 31 | string 32 | true 33 | Name of variable to contain result of action. 34 | 35 | 36 | Passphrase 37 | string 38 | false 39 | The passphrase to be hashed or checked. (action=hash,action=check) 40 | 41 | 42 | Hash 43 | string 44 | false 45 | The existing hash to be checked against. (action=check,action=info) 46 | 47 | 48 | Algorithm 49 | string 50 | false 51 | Algorithm to be used. 52 | 53 | 54 | AlgorithmParams 55 | struct 56 | false 57 | Optional parameters to be used by algorithm. (action=hash) 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/TestBase.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function init() 5 | { 6 | this.Results = []; 7 | this.Messages = []; 8 | 9 | variables.Status = 10 | { Pass : "." 11 | , Fail : "!" 12 | , Skip : "-" 13 | }; 14 | } 15 | 16 | 17 | function start() 18 | { 19 | this.StartTime = getTickCount(); 20 | } 21 | 22 | 23 | function end() 24 | { 25 | return '
    #this.Name#: #ArrayToList(this.Results,'')#' 26 | & '
    time: #getTickCount()-this.StartTime#ms' 27 | & '

    #ArrayToList(this.Messages,'
    ')#
    ' 28 | ; 29 | } 30 | 31 | 32 | function assertEqual(a,b,Msg='') 33 | { 34 | if ( len(Arguments.Msg) ) 35 | return assertTrue( a eq b , Arguments.Msg ); 36 | 37 | return assertTrue( a eq b , 'Expected [#a#] to be same as [#b#]' ); 38 | } 39 | 40 | 41 | function assertNotEqual(a,b,Msg='') 42 | { 43 | if ( len(Arguments.Msg) ) 44 | return assertFalse( a eq b , Arguments.Msg ); 45 | 46 | return assertFalse( a eq b , 'Expected [#a#] to differ from [#b#]' ); 47 | } 48 | 49 | 50 | function assertTrue(a,Msg='') 51 | { 52 | if ( not isBoolean(a) ) 53 | return fail('Expected boolean, received [#a#]'); 54 | 55 | if ( not a ) 56 | { 57 | if ( len(Arguments.Msg) ) 58 | return fail(Arguments.Msg); 59 | 60 | return fail('Expected True received [#a#]'); 61 | } 62 | 63 | return pass(); 64 | } 65 | 66 | 67 | function assertFalse(a,Msg='') 68 | { 69 | if ( not isBoolean(a) ) 70 | return fail('Expected boolean, received [#a#]'); 71 | 72 | if ( a ) 73 | { 74 | if ( len(Arguments.Msg) ) 75 | return fail(Arguments.Msg); 76 | 77 | return fail('Expected False received [#a#]'); 78 | } 79 | 80 | return pass(); 81 | } 82 | 83 | 84 | function pass() 85 | { 86 | ArrayAppend(this.Results,Status.Pass); 87 | return true; 88 | } 89 | 90 | 91 | function fail(Msg) 92 | { 93 | if ( len(Arguments.Msg) ) 94 | logMessage(Msg); 95 | 96 | ArrayAppend(this.Results,Status.Fail); 97 | return false; 98 | } 99 | 100 | 101 | function skip(n=1) 102 | { 103 | ArrayAppend(this.Results,RepeatString(Status.Skip,n)); 104 | logMessage('Skipped #n# assertions.'); 105 | } 106 | 107 | 108 | function logMessage(Msg) 109 | { 110 | ArrayAppend(this.Messages,Msg); 111 | } 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | cfPassphrase 2 | 3 | * Version: 0.2 4 | * License: LGPLv3 5 | * Homepage: https://www.sorcerersisle.com/software/cfpassphrase 6 | * Documentation: https://docs.sorcerersisle.com/cfpassphrase 7 | * Repository: https://code.sorcerersisle.com/cfpassphrase.git 8 | * Issues: https://github.com/boughtonp/cfpassphrase/issues 9 | 10 | 11 | Description 12 | ----------- 13 | 14 | cfPassphrase is a library for securely hashing and checking passwords and 15 | passphrases using proven industry standard algorithms. It aims to provide 16 | a simple and common implementation that can be used in any CFML engine. 17 | 18 | For further details of what is provided see docs.pdf for documentation. 19 | 20 | 21 | Requirements 22 | ------------ 23 | 24 | cfPassphrase supports any modern CFML engine, specifically: 25 | 26 | * ColdFusion 9 and above. 27 | * Lucee 5.3 and above. 28 | * Railo / Lucee 4.x 29 | * OpenBD 3.x 30 | 31 | It requires a minimum JRE/JDK version of 8. 32 | 33 | If you are interested in running it on a different environment, 34 | or have issues getting it to work, please raise an issue. 35 | 36 | 37 | Licensing & Credits 38 | ------------------- 39 | 40 | This project is available under the terms of the LGPLv3 license. 41 | See license.txt to understand your rights and obligations. 42 | 43 | cfPassphrase was created by Peter Boughton, and gratefully makes 44 | use of the third-party software detailed below, each available 45 | individually under their respective licenses. 46 | 47 | jBCrypt v0.4 (mindrot.org/projects/jBCrypt) 48 | * Source: http://mindrot.org/files/jBCrypt/ 49 | * License: ISC/BSD (http://mindrot.org/files/jBCrypt/LICENSE) 50 | * Files: src/org/mindrot/jbcrypt/BCrypt.java 51 | 52 | Java PBKDF2 (crackstation.net/hashing-security.htm) 53 | * Source: http://crackstation.net/hashing-security.htm 54 | * License: Public Domain 55 | * Files: src/crackstation/PBKDF2/PasswordHash.java 56 | 57 | Java SCrypt (github.com/wg/scrypt) 58 | * Source: https://github.com/wg/scrypt 59 | * License: Apache v2.0 (http://www.apache.org/licenses/LICENSE-2.0) 60 | * Files: src/com/lambdaworks/* 61 | 62 | 63 | Contributing 64 | ------------ 65 | 66 | This project was created with the aim of making secure hashing 67 | easy to implement for all CFML applications that need it. 68 | 69 | Bug fixes, backwards-compatible improvements and additions are welcome, 70 | but please discuss first (either raise an issue or send an email). 71 | 72 | Changes are accepted as either pull requests or patch files. 73 | 74 | 75 | /eof -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/Utils.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase; 3 | 4 | import java.util.Map; 5 | 6 | public final class Utils 7 | { 8 | 9 | public static enum Algorithm 10 | { bcrypt , pbkdf2 , scrypt 11 | // Unimplemented algorithms: 12 | , unix_crypt_md5 // $1$ 13 | , unix_crypt_nthash // $3$ 14 | , unix_crypt_sha256 // $5$ 15 | , unix_crypt_sha512 // $6$ 16 | , sun_crypt_md5 // $md5 17 | // 18 | ; public static Algorithm fromString(String Str) 19 | { 20 | if ( Str == null ) return DefaultAlgorithm; 21 | try { return valueOf(Str.toLowerCase());} 22 | catch (Exception ex){return null;} 23 | } 24 | } 25 | 26 | static Algorithm DefaultAlgorithm = Algorithm.bcrypt; 27 | 28 | 29 | static Algorithm identifyAlgorithm 30 | ( String Hash ) 31 | throws Exception 32 | { 33 | if ( Hash.matches("^\\$2[axy]?\\$\\d+\\$[0-9A-Za-z./]+$") ) 34 | return Algorithm.bcrypt; 35 | 36 | else if ( Hash.matches("^\\d+:[0-9a-f]+:[0-9a-f]+$") ) 37 | return Algorithm.pbkdf2; 38 | 39 | else if ( Hash.matches("^\\$s0\\$[0-9a-z]+(?:\\$[0-9A-Za-z+=/]+){2}$") ) 40 | return Algorithm.scrypt; 41 | 42 | else if ( Hash.matches("^\\$1\\$[0-9A-Za-z./]{8}\\$[0-9A-Za-z./]{22}$") ) 43 | return Algorithm.unix_crypt_md5; 44 | 45 | else if ( Hash.matches("^\\$3\\$\\$[0-9A-Fa-f]{32}$") ) 46 | return Algorithm.unix_crypt_nthash; 47 | 48 | else if ( Hash.matches("^\\$5\\$(?:rounds=\\d{1,9}\\$)?[0-9A-Za-z./]{1,16}\\$[0-9A-Za-z./]{43}$") ) 49 | return Algorithm.unix_crypt_sha256; 50 | 51 | else if ( Hash.matches("^\\$6\\$(?:rounds=\\d{1,9}\\$)?[0-9A-Za-z./]{1,16}\\$[0-9A-Za-z./]{86}$") ) 52 | return Algorithm.unix_crypt_sha512; 53 | 54 | else if ( Hash.matches("^\\$md5(?:[$,]rounds=\\d+)?\\$[./0-9A-Za-z]+\\$[./0-9A-Za-z]+$") ) 55 | return Algorithm.sun_crypt_md5; 56 | 57 | else 58 | throw new Exception("Unknown Algorithm Signature"); 59 | } 60 | 61 | 62 | static Integer StructGetInt 63 | ( Map Struct 64 | , String KeyName 65 | , Integer Default 66 | ) 67 | throws Exception 68 | { 69 | if ( Struct == null || ! Struct.containsKey(KeyName) ) 70 | return Default; 71 | 72 | Object Value = Struct.get(KeyName); 73 | 74 | if ( Value instanceof Integer ) 75 | return (Integer) Value; 76 | else if ( Value instanceof Double ) 77 | return (int) Math.round( (Double)Value ); 78 | else 79 | return Integer.valueOf( (String)Value ); 80 | } 81 | 82 | 83 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/PassphraseTag.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.lucee; 3 | 4 | import lucee.runtime.ext.tag.TagSupport; 5 | import lucee.runtime.ext.tag.DynamicAttributes; 6 | import lucee.runtime.type.Collection.Key; 7 | import lucee.runtime.type.Struct; 8 | import lucee.runtime.exp.PageException; 9 | import lucee.loader.engine.CFMLEngineFactory; 10 | 11 | @SuppressWarnings("deprecation") 12 | public final class PassphraseTag 13 | extends TagSupport 14 | implements DynamicAttributes 15 | { 16 | 17 | 18 | private Struct Attributes = CFMLEngineFactory.getInstance().getCreationUtil().createStruct(); 19 | 20 | 21 | public int doStartTag() 22 | throws PageException 23 | { 24 | String Action = (String)Attributes.get("action",null); 25 | String Variable = (String)Attributes.get("variable",null); 26 | String Passphrase = (String)Attributes.get("passphrase",null); 27 | String Algorithm = (String)Attributes.get("algorithm",null); 28 | Object Result; 29 | 30 | if (Action.equalsIgnoreCase("hash")) 31 | { 32 | Struct AlgorithmParams = Attributes.containsKey("algorithmparams") ? (Struct)Attributes.get("algorithmparams") : null; 33 | 34 | Result = PassphraseHash.call 35 | ( pageContext 36 | , Passphrase 37 | , Algorithm 38 | , AlgorithmParams 39 | ); 40 | } 41 | else if (Action.equalsIgnoreCase("check")) 42 | { 43 | String Hash = (String)Attributes.get("hash",null); 44 | 45 | Result = PassphraseCheck.call 46 | ( pageContext 47 | , Passphrase 48 | , Hash 49 | , Algorithm 50 | ); 51 | } 52 | else if (Action.equalsIgnoreCase("info")) 53 | { 54 | String Hash = (String)Attributes.get("hash",null); 55 | 56 | Result = PassphraseCheck.call 57 | ( pageContext 58 | , Hash 59 | , Algorithm 60 | ); 61 | } 62 | else 63 | { 64 | throw CFMLEngineFactory.getInstance().getCastUtil().toPageException 65 | ( new Exception("Invalid value for [Action] attribute. Accepted values are 'hash','check', or [info].") 66 | ); 67 | } 68 | 69 | pageContext.setVariable( Variable , Result ); 70 | 71 | 72 | return SKIP_BODY; 73 | } 74 | 75 | 76 | public void setDynamicAttribute(String uri, String localName, Object value) 77 | { 78 | Attributes.setEL(localName,value); 79 | } 80 | 81 | public void setDynamicAttribute(String uri, Key localName, Object value) 82 | { 83 | Attributes.setEL(localName,value); 84 | } 85 | 86 | 87 | public void release() 88 | { 89 | super.release(); 90 | Attributes.clear(); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/railo/PassphraseTag.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.railo; 3 | 4 | import railo.runtime.ext.tag.TagSupport; 5 | import railo.runtime.ext.tag.DynamicAttributes; 6 | import railo.runtime.type.Collection.Key; 7 | import railo.runtime.type.Struct; 8 | import railo.runtime.exp.PageException; 9 | import railo.loader.engine.CFMLEngineFactory; 10 | 11 | @SuppressWarnings("deprecation") 12 | public final class PassphraseTag 13 | extends TagSupport 14 | implements DynamicAttributes 15 | { 16 | 17 | 18 | private Struct Attributes = CFMLEngineFactory.getInstance().getCreationUtil().createStruct(); 19 | 20 | 21 | public int doStartTag() 22 | throws PageException 23 | { 24 | String Action = (String)Attributes.get("action",null); 25 | String Variable = (String)Attributes.get("variable",null); 26 | String Passphrase = (String)Attributes.get("passphrase",null); 27 | String Algorithm = (String)Attributes.get("algorithm",null); 28 | Object Result; 29 | 30 | if (Action.equalsIgnoreCase("hash")) 31 | { 32 | Struct AlgorithmParams = Attributes.containsKey("algorithmparams") ? (Struct)Attributes.get("algorithmparams") : null; 33 | 34 | Result = PassphraseHash.call 35 | ( pageContext 36 | , Passphrase 37 | , Algorithm 38 | , AlgorithmParams 39 | ); 40 | } 41 | else if (Action.equalsIgnoreCase("check")) 42 | { 43 | String Hash = (String)Attributes.get("hash",null); 44 | 45 | Result = PassphraseCheck.call 46 | ( pageContext 47 | , Passphrase 48 | , Hash 49 | , Algorithm 50 | ); 51 | } 52 | else if (Action.equalsIgnoreCase("info")) 53 | { 54 | String Hash = (String)Attributes.get("hash",null); 55 | 56 | Result = PassphraseCheck.call 57 | ( pageContext 58 | , Hash 59 | , Algorithm 60 | ); 61 | } 62 | else 63 | { 64 | throw CFMLEngineFactory.getInstance().getCastUtil().toPageException 65 | ( new Exception("Invalid value for [Action] attribute. Accepted values are 'hash','check', or [info].") 66 | ); 67 | } 68 | 69 | pageContext.setVariable( Variable , Result ); 70 | 71 | 72 | return SKIP_BODY; 73 | } 74 | 75 | 76 | public void setDynamicAttribute(String uri, String localName, Object value) 77 | { 78 | Attributes.setEL(localName,value); 79 | } 80 | 81 | public void setDynamicAttribute(String uri, Key localName, Object value) 82 | { 83 | Attributes.setEL(localName,value); 84 | } 85 | 86 | 87 | public void release() 88 | { 89 | super.release(); 90 | Attributes.clear(); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/coldfusion/Passphrase.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 24 | 25 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/lucee/cfPassphrase.fld: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.00 5 | 6 | cfpassphrase-functions 7 | https://www.sorcerersisle.com/software/cfpassphrase 8 | cfPassphrase Functions 9 | Provides functions PassphraseHash and PassphraseCheck to Lucee. 10 | 11 | 12 | PassphraseHash 13 | sorcerersisle.cfpassphrase.lucee.PassphraseHash 14 | PassphraseHash returns a hash using the specified KDF algorithm. 15 | Passphrase String yes The passphrase to hash. 16 | Algorithm String no The algorithm to use. ()Default is bcrypt 17 | AlgorithmParams Struct no Any parameters to pass into the algorithm. 18 | String 19 | 20 | 21 | 22 | PassphraseCheck 23 | sorcerersisle.cfpassphrase.lucee.PassphraseCheck 24 | PassphraseCheck returns true/false depending on whether the passphrase matches the provided hash, according to the algorithm provided. 25 | Passphrase String yes The passphrase to check. 26 | Hash String yes The existing passphrase hash. 27 | Algorithm String no The algorithm to use. (Default depends on hash signature.) 28 | Boolean 29 | 30 | 31 | 32 | PassphraseInfo 33 | sorcerersisle.cfpassphrase.lucee.PassphraseInfo 34 | PassphraseInfo Identifies the algorithm and parameters for the specified hash. 35 | Hash String yes The hash to return info about. 36 | Algorithm String no The algorithm to use. (Default depends on hash signature.) 37 | Struct 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/railo/cfPassphrase.fld: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.00 5 | 6 | cfpassphrase-functions 7 | https://www.sorcerersisle.com/software/cfpassphrase 8 | cfPassphrase Functions 9 | Provides functions PassphraseHash and PassphraseCheck to Railo. 10 | 11 | 12 | PassphraseHash 13 | sorcerersisle.cfpassphrase.railo.PassphraseHash 14 | PassphraseHash returns a hash using the specified KDF algorithm. 15 | Passphrase String yes The passphrase to hash. 16 | Algorithm String no The algorithm to use. ()Default is bcrypt 17 | AlgorithmParams Struct no Any parameters to pass into the algorithm. 18 | String 19 | 20 | 21 | 22 | PassphraseCheck 23 | sorcerersisle.cfpassphrase.railo.PassphraseCheck 24 | PassphraseCheck returns true/false depending on whether the passphrase matches the provided hash, according to the algorithm provided. 25 | Passphrase String yes The passphrase to check. 26 | Hash String yes The existing passphrase hash. 27 | Algorithm String no The algorithm to use. (Default depends on hash signature.) 28 | Boolean 29 | 30 | 31 | 32 | PassphraseInfo 33 | sorcerersisle.cfpassphrase.railo.PassphraseInfo 34 | PassphraseInfo Identifies the algorithm and parameters for the specified hash. 35 | Hash String yes The hash to return info about. 36 | Algorithm String no The algorithm to use. (Default depends on hash signature.) 37 | Struct 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/PBKDF.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import java.security.GeneralSecurityException; 8 | import static java.lang.System.arraycopy; 9 | 10 | /** 11 | * An implementation of the Password-Based Key Derivation Function as specified 12 | * in RFC 2898. 13 | * 14 | * @author Will Glozer 15 | */ 16 | public class PBKDF { 17 | /** 18 | * Implementation of PBKDF2 (RFC2898). 19 | * 20 | * @param alg HMAC algorithm to use. 21 | * @param P Password. 22 | * @param S Salt. 23 | * @param c Iteration count. 24 | * @param dkLen Intended length, in octets, of the derived key. 25 | * 26 | * @return The derived key. 27 | * 28 | * @throws GeneralSecurityException 29 | */ 30 | public static byte[] pbkdf2(String alg, byte[] P, byte[] S, int c, int dkLen) throws GeneralSecurityException { 31 | Mac mac = Mac.getInstance(alg); 32 | mac.init(new SecretKeySpec(P, alg)); 33 | byte[] DK = new byte[dkLen]; 34 | pbkdf2(mac, S, c, DK, dkLen); 35 | return DK; 36 | } 37 | 38 | /** 39 | * Implementation of PBKDF2 (RFC2898). 40 | * 41 | * @param mac Pre-initialized {@link Mac} instance to use. 42 | * @param S Salt. 43 | * @param c Iteration count. 44 | * @param DK Byte array that derived key will be placed in. 45 | * @param dkLen Intended length, in octets, of the derived key. 46 | * 47 | * @throws GeneralSecurityException 48 | */ 49 | public static void pbkdf2(Mac mac, byte[] S, int c, byte[] DK, int dkLen) throws GeneralSecurityException { 50 | int hLen = mac.getMacLength(); 51 | 52 | if (dkLen > (Math.pow(2, 32) - 1) * hLen) { 53 | throw new GeneralSecurityException("Requested key length too long"); 54 | } 55 | 56 | byte[] U = new byte[hLen]; 57 | byte[] T = new byte[hLen]; 58 | byte[] block1 = new byte[S.length + 4]; 59 | 60 | int l = (int) Math.ceil((double) dkLen / hLen); 61 | int r = dkLen - (l - 1) * hLen; 62 | 63 | arraycopy(S, 0, block1, 0, S.length); 64 | 65 | for (int i = 1; i <= l; i++) { 66 | block1[S.length + 0] = (byte) (i >> 24 & 0xff); 67 | block1[S.length + 1] = (byte) (i >> 16 & 0xff); 68 | block1[S.length + 2] = (byte) (i >> 8 & 0xff); 69 | block1[S.length + 3] = (byte) (i >> 0 & 0xff); 70 | 71 | mac.update(block1); 72 | mac.doFinal(U, 0); 73 | arraycopy(U, 0, T, 0, hLen); 74 | 75 | for (int j = 1; j < c; j++) { 76 | mac.update(U); 77 | mac.doFinal(U, 0); 78 | 79 | for (int k = 0; k < hLen; k++) { 80 | T[k] ^= U[k]; 81 | } 82 | } 83 | 84 | arraycopy(T, 0, DK, (i - 1) * hLen, (i == l ? r : hLen)); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/openbd/PassphraseTag.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase.openbd; 3 | 4 | import com.naryx.tagfusion.cfm.tag.cfTag; 5 | import com.naryx.tagfusion.cfm.tag.cfTagReturnType; 6 | import com.naryx.tagfusion.cfm.engine.cfArgStructData; 7 | import com.naryx.tagfusion.cfm.engine.cfSession; 8 | import com.naryx.tagfusion.cfm.engine.cfData; 9 | import com.naryx.tagfusion.cfm.engine.cfStructData; 10 | import com.naryx.tagfusion.cfm.engine.cfmBadFileException; 11 | import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; 12 | 13 | @SuppressWarnings("serial") 14 | public final class PassphraseTag 15 | extends cfTag 16 | { 17 | 18 | 19 | protected void defaultParameters 20 | ( String _tag ) 21 | throws cfmBadFileException 22 | { 23 | parseTagHeader( _tag ); 24 | 25 | if ( ! containsAttribute("action") ) 26 | throw newBadFileException( "You must specify the [Action] attribute. Accepted values are 'hash', 'check', 'info'." , "" ); 27 | 28 | if ( ! containsAttribute("variable") ) 29 | throw newBadFileException( "You must specify the [Variable] attribute." , "" ); 30 | 31 | if ( ! containsAttribute("passphrase") ) 32 | throw newBadFileException( "You must specify the [Passphrase] attribute." , "" ); 33 | } 34 | 35 | 36 | public cfTagReturnType render 37 | ( cfSession _session ) 38 | throws cfmRunTimeException 39 | { 40 | String Action = getDynamic(_session,"action").getString(); 41 | String Variable = getDynamic(_session, "variable").getString(); 42 | cfArgStructData Args = new cfArgStructData(); 43 | cfData Result; 44 | 45 | if (Action.equalsIgnoreCase("hash")) 46 | { 47 | Args.setData("passphrase", getDynamic(_session, "passphrase").getString()); 48 | if (containsAttribute("algorithm")) Args.setData("algorithm", getDynamic(_session, "algorithm").getString()); 49 | if (containsAttribute("algorithmparams")) Args.setData("algorithmparams", (cfStructData)getDynamic(_session, "algorithmparams")); 50 | 51 | Result = new PassphraseHash().execute(_session,Args); 52 | } 53 | else if (Action.equalsIgnoreCase("check")) 54 | { 55 | Args.setData("passphrase" , getDynamic(_session, "passphrase").getString()); 56 | Args.setData("hash" , getDynamic(_session, "hash").getString()); 57 | if (containsAttribute("algorithm")) Args.setData("algorithm", getDynamic(_session, "algorithm").getString()); 58 | 59 | Result = new PassphraseCheck().execute(_session,Args); 60 | } 61 | else if (Action.equalsIgnoreCase("info")) 62 | { 63 | Args.setData("hash" , getDynamic(_session, "hash").getString()); 64 | if (containsAttribute("algorithm")) Args.setData("algorithm", getDynamic(_session, "algorithm").getString()); 65 | 66 | Result = new PassphraseCheck().execute(_session,Args); 67 | } 68 | else 69 | { 70 | throw new cfmRunTimeException 71 | ( _session 72 | , new Exception("Invalid value ["+Action+"] for [Action] attribute. Accepted values are 'hash', 'check', 'info'.") 73 | ); 74 | } 75 | 76 | _session.setData( Variable , Result ); 77 | 78 | return cfTagReturnType.NORMAL; 79 | } 80 | 81 | 82 | } -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/coldbox/Passphrase.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/coldfusion/Passphrase.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 38 | 39 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 66 | 67 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 81 | 82 | 83 | 84 | 85 | 86 | 90 | 91 | 94 | 95 | 96 | 97 | 98 | 99 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /test/BcryptTest.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function init( IncludeSlowTests ) 5 | { 6 | super.init( argumentcollection=arguments ); 7 | 8 | this.Name = 'bcrypt'; 9 | 10 | var TestData = 11 | [ [ "", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s." ] 12 | , [ "", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye" ] 13 | , [ "", "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW" ] 14 | , [ "", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO" ] 15 | , [ "a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe" ] 16 | , [ "a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V." ] 17 | , [ "a", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u" ] 18 | , [ "a", "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS" ] 19 | , [ "abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i" ] 20 | , [ "abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm" ] 21 | , [ "abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi" ] 22 | , [ "abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q" ] 23 | , [ "abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC" ] 24 | , [ "abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz." ] 25 | , [ "abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq" ] 26 | , [ "abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" ] 27 | , [ "~!@##$%^&*() ~!@##$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO" ] 28 | , [ "~!@##$%^&*() ~!@##$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW" ] 29 | , [ "~!@##$%^&*() ~!@##$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS" ] 30 | , [ "~!@##$%^&*() ~!@##$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" ] 31 | ]; 32 | 33 | super.start(); 34 | 35 | this.test_info( TestData ); 36 | 37 | if ( IncludeSlowTests ) 38 | this.test_basic( TestData ); 39 | else 40 | skip( ArrayLen(TestData)*2 ); 41 | 42 | this.test_nonascii(); 43 | 44 | return super.end(); 45 | } 46 | 47 | 48 | function test_info( TestData ) 49 | { 50 | var info1 = PassphraseInfo('$2a$10$9zXu55aPNya8ek17DwCXZ.X6kExa5cK5bpGmyTBrqD1dg76rkWz4y'); 51 | assertEqual(info1.Algorithm,'BCrypt'); 52 | assertEqual(info1.Status,'Supported'); 53 | assertEqual(info1.Version,'2a'); 54 | assertEqual(info1.Rounds,10); 55 | assertEqual(info1.Salt,'9zXu55aPNya8ek17'); 56 | assertEqual(info1.Hash,'DwCXZ.X6kExa5cK5bpGmyTBrqD1dg76rkWz4y'); 57 | assertEqual(StructCount(info1),6); 58 | 59 | var info = PassphraseInfo(Arguments.TestData[1][2]); 60 | assertEqual(info.Algorithm,'BCrypt'); 61 | assertEqual(info.Status,'Supported'); 62 | assertEqual(info.Version,'2a'); 63 | assertEqual(info.Rounds,6); 64 | assertEqual(info.Salt,'DCq7YPn5Rq63x1La'); 65 | assertEqual(info.Hash,'d4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'); 66 | 67 | info = PassphraseInfo(Arguments.TestData[20][2]); 68 | assertEqual(info.Algorithm,'BCrypt'); 69 | assertEqual(info.Status,'Supported'); 70 | assertEqual(info.Version,'2a'); 71 | assertEqual(info.Rounds,12); 72 | assertEqual(info.Salt,'WApznUOJfkEGSmYR'); 73 | assertEqual(info.Hash,'fnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC'); 74 | } 75 | 76 | 77 | function test_basic( TestData ) 78 | { 79 | for ( var i=1 ; i<=ArrayLen(Arguments.TestData) ; ++i ) 80 | { 81 | assertTrue( PassphraseCheck(Arguments.TestData[i][1],Arguments.TestData[i][2]) ); 82 | assertFalse( PassphraseCheck(Arguments.TestData[i][1],Arguments.TestData[((i + 4) % ArrayLen(Arguments.TestData))+1][2]) ); 83 | } 84 | } 85 | 86 | 87 | function test_nonascii() 88 | { 89 | var TestData = 90 | [ RepeatString(chr(2605),8) 91 | , "????????" 92 | ]; 93 | 94 | var h1 = PassphraseHash(TestData[1],'bcrypt',{rounds:6}); 95 | var h2 = PassphraseHash(TestData[2],'bcrypt',{rounds:6}); 96 | assertTrue(PassphraseCheck(TestData[1],h1)); 97 | assertFalse(PassphraseCheck(TestData[1],h2)); 98 | assertTrue(PassphraseCheck(TestData[2],h2)); 99 | assertFalse(PassphraseCheck(TestData[2],h1)); 100 | } 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/SCryptUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.security.GeneralSecurityException; 7 | import java.security.SecureRandom; 8 | 9 | import static com.lambdaworks.codec.Base64.*; 10 | 11 | /** 12 | * Simple {@link SCrypt} interface for hashing passwords using the 13 | * scrypt key derivation function 14 | * and comparing a plain text password to a hashed one. The hashed output is an 15 | * extended implementation of the Modular Crypt Format that also includes the scrypt 16 | * algorithm parameters. 17 | * 18 | * Format: $s0$PARAMS$SALT$KEY. 19 | * 20 | *
    21 | *
    PARAMS
    32-bit hex integer containing log2(N) (16 bits), r (8 bits), and p (8 bits)
    22 | *
    SALT
    base64-encoded salt
    23 | *
    KEY
    base64-encoded derived key
    24 | *
    25 | * 26 | * s0 identifies version 0 of the scrypt format, using a 128-bit salt and 256-bit derived key. 27 | * 28 | * @author Will Glozer 29 | */ 30 | public class SCryptUtil { 31 | /** 32 | * Hash the supplied plaintext password and generate output in the format described 33 | * in {@link SCryptUtil}. 34 | * 35 | * @param passwd Password. 36 | * @param N CPU cost parameter. 37 | * @param r Memory cost parameter. 38 | * @param p Parallelization parameter. 39 | * 40 | * @return The hashed password. 41 | */ 42 | public static String scrypt(String passwd, int N, int r, int p) { 43 | try { 44 | byte[] salt = new byte[16]; 45 | SecureRandom.getInstance("SHA1PRNG").nextBytes(salt); 46 | 47 | byte[] derived = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); 48 | 49 | String params = Long.toString(log2(N) << 16L | r << 8 | p, 16); 50 | 51 | StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2); 52 | sb.append("$s0$").append(params).append('$'); 53 | sb.append(encode(salt)).append('$'); 54 | sb.append(encode(derived)); 55 | 56 | return sb.toString(); 57 | } catch (UnsupportedEncodingException e) { 58 | throw new IllegalStateException("JVM doesn't support UTF-8?"); 59 | } catch (GeneralSecurityException e) { 60 | throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?"); 61 | } 62 | } 63 | 64 | /** 65 | * Compare the supplied plaintext password to a hashed password. 66 | * 67 | * @param passwd Plaintext password. 68 | * @param hashed scrypt hashed password. 69 | * 70 | * @return true if passwd matches hashed value. 71 | */ 72 | public static boolean check(String passwd, String hashed) { 73 | try { 74 | String[] parts = hashed.split("\\$"); 75 | 76 | if (parts.length != 5 || !parts[1].equals("s0")) { 77 | throw new IllegalArgumentException("Invalid hashed value"); 78 | } 79 | 80 | long params = Long.parseLong(parts[2], 16); 81 | byte[] salt = decode(parts[3].toCharArray()); 82 | byte[] derived0 = decode(parts[4].toCharArray()); 83 | 84 | int N = (int) Math.pow(2, params >> 16 & 0xffff); 85 | int r = (int) params >> 8 & 0xff; 86 | int p = (int) params & 0xff; 87 | 88 | byte[] derived1 = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); 89 | 90 | if (derived0.length != derived1.length) return false; 91 | 92 | int result = 0; 93 | for (int i = 0; i < derived0.length; i++) { 94 | result |= derived0[i] ^ derived1[i]; 95 | } 96 | return result == 0; 97 | } catch (UnsupportedEncodingException e) { 98 | throw new IllegalStateException("JVM doesn't support UTF-8?"); 99 | } catch (GeneralSecurityException e) { 100 | throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?"); 101 | } 102 | } 103 | 104 | private static int log2(int n) { 105 | int log = 0; 106 | if ((n & 0xffff0000 ) != 0) { n >>>= 16; log = 16; } 107 | if (n >= 256) { n >>>= 8; log += 8; } 108 | if (n >= 16 ) { n >>>= 4; log += 4; } 109 | if (n >= 4 ) { n >>>= 2; log += 2; } 110 | return log + (n >>> 1); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test/UsageTest.cfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function init() 5 | { 6 | super.init( argumentcollection=arguments ); 7 | 8 | this.Name = 'usage'; 9 | 10 | super.start(); 11 | 12 | this.test_info(); 13 | this.test_hash(); 14 | this.test_check(); 15 | 16 | return super.end(); 17 | } 18 | 19 | 20 | function test_info() 21 | { 22 | try 23 | { 24 | var info = PassphraseInfo('$8$unknown-hash'); 25 | assertFalse(1,"Expected exception to be thrown"); 26 | } 27 | catch( any e ) 28 | { 29 | assertEqual(e.Message,"Unknown Algorithm Signature"); 30 | } 31 | 32 | var info = PassphraseInfo('$2a$10$9zXu55aPNya8ek17DwCXZ.X6kExa5cK5bpGmyTBrqD1dg76rkWz4y'); 33 | assertEqual(info.Algorithm,'BCrypt'); 34 | assertEqual(info.Status,'Supported'); 35 | 36 | var info = PassphraseInfo('180:5fabd8b160f5f9225ec5569ce2f02d5a2e29a29e0b280614:d11a602ecc7830280b25cdd29b539e9e0f8438a0a43e6637'); 37 | assertEqual(info.Algorithm,'PBKDF2'); 38 | assertEqual(info.Status,'Supported'); 39 | 40 | var info = PassphraseInfo('$s0$501ff$5JQALASFiKGKdfY9Z0GYMA==$8c4aMGktdRlLIeY+erIA62fNtgb2OxJrjyhw+XeWHk4='); 41 | assertEqual(info.Algorithm,'SCrypt'); 42 | assertEqual(info.Status,'Supported'); 43 | 44 | var info = PassphraseInfo('$1$etNnh7FA$OlM7eljE/B7F1J4XYNnk81'); 45 | assertEqual(info.Algorithm,'md5crypt'); 46 | assertEqual(info.Status,'Obsolete'); 47 | assertEqual(info.Salt,'etNnh7FA'); 48 | assertEqual(info.Hash,'OlM7eljE/B7F1J4XYNnk81'); 49 | 50 | var info = PassphraseInfo('$3$$8846f7eaee8fb117ad06bdd830b7586c'); 51 | assertEqual(info.Algorithm,'NT-Hash'); 52 | assertEqual(info.Status,'Obsolete'); 53 | assertEqual(info.Hash,'8846f7eaee8fb117ad06bdd830b7586c'); 54 | 55 | var info = PassphraseInfo('$5$9ks3nNEqv31FX.F$gdEoLFsCRsn/WRN3wxUnzfeZLoooVlzeF4WjLomTRFD'); 56 | assertEqual(info.Algorithm,'SHA-2'); 57 | assertEqual(info.Version,'256'); 58 | assertEqual(info.Status,'Unsupported'); 59 | assertEqual(info.Rounds,'5000'); 60 | assertEqual(info.Salt,'9ks3nNEqv31FX.F'); 61 | assertEqual(info.Hash,'gdEoLFsCRsn/WRN3wxUnzfeZLoooVlzeF4WjLomTRFD'); 62 | 63 | var info = PassphraseInfo('$6$qoE2letU$wWPRl.PVczjzeMVgjiA8LLy2nOyZbf7Amj3qLIL978o18gbMySdKZ7uepq9tmMQXxyTIrS12Pln.2Q/6Xscao0'); 64 | assertEqual(info.Algorithm,'SHA-2'); 65 | assertEqual(info.Version,'512'); 66 | assertEqual(info.Status,'Unsupported'); 67 | assertEqual(info.Rounds,'5000'); 68 | assertEqual(info.Salt,'qoE2letU'); 69 | assertEqual(info.Hash,'wWPRl.PVczjzeMVgjiA8LLy2nOyZbf7Amj3qLIL978o18gbMySdKZ7uepq9tmMQXxyTIrS12Pln.2Q/6Xscao0'); 70 | 71 | var info = PassphraseInfo('$md5,rounds=5000$GUBv0xjJ$mSwgIswdjlTY0YxV7HBVm0'); 72 | assertEqual(info.Algorithm,'SunMD5'); 73 | assertEqual(info.Status,'Obsolete'); 74 | assertEqual(info.Rounds,'5000'); 75 | assertEqual(info.Salt,'GUBv0xjJ'); 76 | assertEqual(info.Hash,'mSwgIswdjlTY0YxV7HBVm0'); 77 | 78 | } 79 | 80 | 81 | function test_hash() 82 | { 83 | var r = RandRange(4,6); 84 | var hash = PassphraseHash('x','bcrypt',{rounds:r}); 85 | var info = PassphraseInfo(hash); 86 | assertEqual(info.Algorithm,'BCrypt'); 87 | assertEqual(info.Rounds,r); 88 | assertNotEqual(hash,PassphraseHash('x','bcrypt',{rounds:r})); 89 | 90 | var i = RandRange(1000,2000); 91 | var hash = PassphraseHash('x','pbkdf2',{iterations:i}); 92 | var info = PassphraseInfo(hash); 93 | assertEqual(info.Algorithm,'PBKDF2'); 94 | assertEqual(info.Iterations,i); 95 | assertNotEqual(hash,PassphraseHash('x','pbkdf2',{iterations:i})); 96 | 97 | var c = 2^RandRange(1,3); 98 | var m = RandRange(1,3); 99 | var hash = PassphraseHash('x','scrypt',{CpuCost:c,MemoryCost:m}); 100 | var info = PassphraseInfo(hash); 101 | assertEqual(info.Algorithm,'SCrypt'); 102 | assertEqual(info.CpuCost,c); 103 | assertEqual(info.MemoryCost,m); 104 | assertNotEqual(hash,PassphraseHash('x','scrypt',{CpuCost:c,MemoryCost:m})); 105 | } 106 | 107 | 108 | function test_check() 109 | { 110 | var hash = PassphraseHash('x','bcrypt',{rounds:4}); 111 | assertTrue(PassphraseCheck('x',hash)); 112 | assertFalse(PassphraseCheck('x ',hash)); 113 | 114 | var hash = PassphraseHash('x','pbkdf2',{iterations:1000}); 115 | assertTrue(PassphraseCheck('x',hash)); 116 | assertFalse(PassphraseCheck('x ',hash)); 117 | 118 | var hash = PassphraseHash('x','scrypt',{CpuCost:2,MemoryCost:2}); 119 | assertTrue(PassphraseCheck('x',hash)); 120 | assertFalse(PassphraseCheck('x ',hash)); 121 | 122 | try 123 | { 124 | PassphraseCheck(hash,''); 125 | assertFalse(1,"Expected exception to be thrown"); 126 | } 127 | catch( any e ) 128 | { 129 | assertEqual(e.Message,"Unknown Algorithm Signature"); 130 | } 131 | } 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/com/lambdaworks/codec/Base64.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.codec; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * High-performance base64 codec based on the algorithm used in Mikael Grev's MiG Base64. 9 | * This implementation is designed to handle base64 without line splitting and with 10 | * optional padding. Alternative character tables may be supplied to the {@code encode} 11 | * and {@code decode} methods to implement modified base64 schemes. 12 | * 13 | * Decoding assumes correct input, the caller is responsible for ensuring that the input 14 | * contains no invalid characters. 15 | * 16 | * @author Will Glozer 17 | */ 18 | public class Base64 { 19 | private static final char[] encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); 20 | private static final int[] decode = new int[128]; 21 | private static final char pad = '='; 22 | 23 | static { 24 | Arrays.fill(decode, -1); 25 | for (int i = 0; i < encode.length; i++) { 26 | decode[encode[i]] = i; 27 | } 28 | decode[pad] = 0; 29 | } 30 | 31 | /** 32 | * Decode base64 chars to bytes. 33 | * 34 | * @param chars Chars to encode. 35 | * 36 | * @return Decoded bytes. 37 | */ 38 | public static byte[] decode(char[] chars) { 39 | return decode(chars, decode, pad); 40 | } 41 | 42 | /** 43 | * Encode bytes to base64 chars, with padding. 44 | * 45 | * @param bytes Bytes to encode. 46 | * 47 | * @return Encoded chars. 48 | */ 49 | public static char[] encode(byte[] bytes) { 50 | return encode(bytes, encode, pad); 51 | } 52 | 53 | /** 54 | * Encode bytes to base64 chars, with optional padding. 55 | * 56 | * @param bytes Bytes to encode. 57 | * @param padded Add padding to output. 58 | * 59 | * @return Encoded chars. 60 | */ 61 | public static char[] encode(byte[] bytes, boolean padded) { 62 | return encode(bytes, encode, padded ? pad : 0); 63 | } 64 | 65 | /** 66 | * Decode base64 chars to bytes using the supplied decode table and padding 67 | * character. 68 | * 69 | * @param src Base64 encoded data. 70 | * @param table Decode table. 71 | * @param pad Padding character. 72 | * 73 | * @return Decoded bytes. 74 | */ 75 | public static byte[] decode(char[] src, int[] table, char pad) { 76 | int len = src.length; 77 | 78 | if (len == 0) return new byte[0]; 79 | 80 | int padCount = (src[len - 1] == pad ? (src[len - 2] == pad ? 2 : 1) : 0); 81 | int bytes = (len * 6 >> 3) - padCount; 82 | int blocks = (bytes / 3) * 3; 83 | 84 | byte[] dst = new byte[bytes]; 85 | int si = 0, di = 0; 86 | 87 | while (di < blocks) { 88 | int n = table[src[si++]] << 18 | table[src[si++]] << 12 | table[src[si++]] << 6 | table[src[si++]]; 89 | dst[di++] = (byte) (n >> 16); 90 | dst[di++] = (byte) (n >> 8); 91 | dst[di++] = (byte) n; 92 | } 93 | 94 | if (di < bytes) { 95 | int n = 0; 96 | switch (len - si) { 97 | case 4: n |= table[src[si+3]]; 98 | case 3: n |= table[src[si+2]] << 6; 99 | case 2: n |= table[src[si+1]] << 12; 100 | case 1: n |= table[src[si]] << 18; 101 | } 102 | for (int r = 16; di < bytes; r -= 8) { 103 | dst[di++] = (byte) (n >> r); 104 | } 105 | } 106 | 107 | return dst; 108 | } 109 | 110 | /** 111 | * Encode bytes to base64 chars using the supplied encode table and with 112 | * optional padding. 113 | * 114 | * @param src Bytes to encode. 115 | * @param table Encoding table. 116 | * @param pad Padding character, or 0 for no padding. 117 | * 118 | * @return Encoded chars. 119 | */ 120 | public static char[] encode(byte[] src, char[] table, char pad) { 121 | int len = src.length; 122 | 123 | if (len == 0) return new char[0]; 124 | 125 | int blocks = (len / 3) * 3; 126 | int chars = ((len - 1) / 3 + 1) << 2; 127 | int tail = len - blocks; 128 | if (pad == 0 && tail > 0) chars -= 3 - tail; 129 | 130 | char[] dst = new char[chars]; 131 | int si = 0, di = 0; 132 | 133 | while (si < blocks) { 134 | int n = (src[si++] & 0xff) << 16 | (src[si++] & 0xff) << 8 | (src[si++] & 0xff); 135 | dst[di++] = table[(n >>> 18) & 0x3f]; 136 | dst[di++] = table[(n >>> 12) & 0x3f]; 137 | dst[di++] = table[(n >>> 6) & 0x3f]; 138 | dst[di++] = table[n & 0x3f]; 139 | } 140 | 141 | if (tail > 0) { 142 | int n = (src[si] & 0xff) << 10; 143 | if (tail == 2) n |= (src[++si] & 0xff) << 2; 144 | 145 | dst[di++] = table[(n >>> 12) & 0x3f]; 146 | dst[di++] = table[(n >>> 6) & 0x3f]; 147 | if (tail == 2) dst[di++] = table[n & 0x3f]; 148 | 149 | if (pad != 0) { 150 | if (tail == 1) dst[di++] = pad; 151 | dst[di] = pad; 152 | } 153 | } 154 | 155 | return dst; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/sorcerersisle/cfpassphrase/Impl.java: -------------------------------------------------------------------------------- 1 | // cfPassphrase v0.2 | (c) Peter Boughton | License: LGPLv3 | Website: https://www.sorcerersisle.com/software/cfpassphrase 2 | package sorcerersisle.cfpassphrase; 3 | 4 | import org.mindrot.jbcrypt.BCrypt; 5 | import crackstation.PBKDF2.PasswordHash; 6 | import com.lambdaworks.crypto.SCryptUtil; 7 | import java.util.Map; 8 | import java.util.HashMap; 9 | 10 | 11 | public final class Impl 12 | { 13 | 14 | 15 | public static String hash 16 | ( String Passphrase 17 | , String Algorithm 18 | , Map AlgorithmParams 19 | ) 20 | throws Exception 21 | { 22 | 23 | switch ( Utils.Algorithm.fromString(Algorithm) ) 24 | { 25 | 26 | case bcrypt: 27 | Integer Rounds = Utils.StructGetInt(AlgorithmParams,"Rounds",16); 28 | 29 | String Salt = ( Rounds == null ) 30 | ? BCrypt.gensalt() 31 | : BCrypt.gensalt(Rounds) 32 | ; 33 | 34 | return BCrypt.hashpw 35 | ( Passphrase 36 | , Salt 37 | ); 38 | 39 | case pbkdf2: 40 | return PasswordHash.createHash 41 | ( Passphrase 42 | , Utils.StructGetInt(AlgorithmParams,"Iterations",86000) 43 | , Utils.StructGetInt(AlgorithmParams,"SaltBytes",24) 44 | , Utils.StructGetInt(AlgorithmParams,"HashBytes",24) 45 | ); 46 | 47 | case scrypt: 48 | return SCryptUtil.scrypt 49 | ( Passphrase 50 | , Utils.StructGetInt(AlgorithmParams,"CpuCost",2^16) 51 | , Utils.StructGetInt(AlgorithmParams,"MemoryCost",8) 52 | , Utils.StructGetInt(AlgorithmParams,"Parallelization",1) 53 | ); 54 | 55 | default: 56 | throw new Exception("Unsupported Algorithm"); 57 | 58 | } 59 | } 60 | 61 | 62 | public static Boolean check 63 | ( String Passphrase 64 | , String Hash 65 | , String Algorithm 66 | ) 67 | throws Exception 68 | { 69 | switch 70 | ( Algorithm == null 71 | ? Utils.identifyAlgorithm(Hash) 72 | : Utils.Algorithm.fromString(Algorithm) 73 | ) 74 | { 75 | 76 | case bcrypt: 77 | return BCrypt.checkpw 78 | ( Passphrase 79 | , Hash 80 | ); 81 | 82 | case pbkdf2: 83 | return PasswordHash.validatePassword 84 | ( Passphrase 85 | , Hash 86 | ); 87 | 88 | case scrypt: 89 | return SCryptUtil.check 90 | ( Passphrase 91 | , Hash 92 | ); 93 | 94 | default: 95 | throw new Exception("Unsupported Algorithm"); 96 | 97 | } 98 | } 99 | 100 | 101 | public static Map info 102 | ( String Hash 103 | , String Algorithm 104 | ) 105 | throws Exception 106 | { 107 | String[] Parts; 108 | Map Info = new HashMap(); 109 | 110 | switch 111 | ( Algorithm == null 112 | ? Utils.identifyAlgorithm(Hash) 113 | : Utils.Algorithm.fromString(Algorithm) 114 | ) 115 | { 116 | 117 | case bcrypt: 118 | Parts = Hash.substring(1).split("\\$"); 119 | 120 | Info.put("Algorithm" , "BCrypt" ); 121 | Info.put("Version" , Parts[0] ); 122 | 123 | if ( Parts[0].equals("2a") ) 124 | Info.put("Status" , "Supported" ); 125 | else if ( Parts[0].equals("2") ) 126 | Info.put("Status" , "Obsolete" ); 127 | else 128 | Info.put("Status" , "Unsupported" ); 129 | 130 | Info.put("Rounds" , Parts[1] ); 131 | Info.put("Salt" , Parts[2].substring(0,16) ); 132 | Info.put("Hash" , Parts[2].substring(16) ); 133 | 134 | return Info; 135 | 136 | case pbkdf2: 137 | Parts = Hash.split(":"); 138 | 139 | Info.put("Algorithm" , "PBKDF2" ); 140 | Info.put("Status" , "Supported" ); 141 | 142 | Info.put("Iterations" , Parts[0] ); 143 | Info.put("Salt" , Parts[1] ); 144 | Info.put("Hash" , Parts[2] ); 145 | 146 | return Info; 147 | 148 | case scrypt: 149 | Parts = Hash.substring(2).split("\\$"); 150 | long Params = Long.parseLong(Parts[1],16); 151 | 152 | Info.put("Algorithm" , "SCrypt" ); 153 | Info.put("Version" , Parts[0] ); 154 | Info.put("Status" , "Supported" ); 155 | 156 | Info.put("CpuCost" , String.valueOf(Math.round((Math.pow(2, Params >> 16 & 0xffff)))) ); 157 | Info.put("MemoryCost" , String.valueOf(Params >> 8 & 0xff) ); 158 | Info.put("Parallelization" , String.valueOf(Params & 0xff) ); 159 | Info.put("Salt" , Parts[2] ); 160 | Info.put("Hash" , Parts[3] ); 161 | 162 | return Info; 163 | 164 | case unix_crypt_md5: 165 | Parts = Hash.substring(3).split("\\$"); 166 | 167 | Info.put("Algorithm" , "md5crypt" ); 168 | Info.put("Status" , "Obsolete" ); 169 | 170 | Info.put("Salt" , Parts[0] ); 171 | Info.put("Hash" , Parts[1] ); 172 | 173 | return Info; 174 | 175 | case unix_crypt_nthash: 176 | 177 | Info.put("Algorithm" , "NT-Hash" ); 178 | Info.put("Status" , "Obsolete" ); 179 | 180 | Info.put("Hash" , Hash.substring(4) ); 181 | 182 | return Info; 183 | 184 | case unix_crypt_sha256: 185 | case unix_crypt_sha512: 186 | Parts = Hash.substring(1).split("\\$"); 187 | 188 | Info.put("Algorithm" , "SHA-2" ); 189 | Info.put("Version" , Parts[0].equals("5") ? "256" : "512"); 190 | Info.put("Status" , "Unsupported" ); 191 | 192 | if ( Parts[1].startsWith("rounds=") ) 193 | { 194 | Info.put("Rounds" , Parts[1].split("=")[1] ); 195 | Info.put("Salt" , Parts[2] ); 196 | Info.put("Hash" , Parts[3] ); 197 | } 198 | else 199 | { 200 | Info.put("Rounds" , "5000" ); 201 | Info.put("Salt" , Parts[1] ); 202 | Info.put("Hash" , Parts[2] ); 203 | } 204 | 205 | return Info; 206 | 207 | case sun_crypt_md5: 208 | Parts = Hash.substring(5).split("\\$"); 209 | 210 | Info.put("Algorithm" , "SunMD5" ); 211 | Info.put("Status" , "Obsolete" ); 212 | 213 | if ( Parts[0].startsWith("rounds=") ) 214 | { 215 | Info.put("Rounds" , Parts[0].split("=")[1] ); 216 | Info.put("Salt" , Parts[1] ); 217 | Info.put("Hash" , Parts[2] ); 218 | } 219 | else 220 | { 221 | Info.put("Rounds" , "4096" ); 222 | Info.put("Salt" , Parts[0] ); 223 | Info.put("Hash" , Parts[1] ); 224 | } 225 | 226 | return Info; 227 | 228 | default: 229 | throw new Exception("Unknown Algorithm"); 230 | 231 | } 232 | } 233 | 234 | 235 | } -------------------------------------------------------------------------------- /src/com/lambdaworks/crypto/SCrypt.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - Will Glozer. All rights reserved. 2 | 3 | package com.lambdaworks.crypto; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import java.security.GeneralSecurityException; 8 | 9 | import static java.lang.Integer.MAX_VALUE; 10 | import static java.lang.System.arraycopy; 11 | 12 | /** 13 | * An implementation of the scrypt 14 | * key derivation function. This class will attempt to load a native library 15 | * containing the optimized C implementation from 16 | * http://www.tarsnap.com/scrypt.html and 17 | * fall back to the pure Java version if that fails. 18 | * 19 | * @author Will Glozer 20 | */ 21 | public class SCrypt { 22 | 23 | /** 24 | * Implementation of the scrypt KDF. 25 | * Calls the native implementation {@link #scryptN} when the native library was successfully 26 | * loaded, otherwise calls {@link #scryptJ}. 27 | * 28 | * @param passwd Password. 29 | * @param salt Salt. 30 | * @param N CPU cost parameter. 31 | * @param r Memory cost parameter. 32 | * @param p Parallelization parameter. 33 | * @param dkLen Intended length of the derived key. 34 | * 35 | * @return The derived key. 36 | * 37 | * @throws GeneralSecurityException when HMAC_SHA256 is not available. 38 | */ 39 | public static byte[] scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException { 40 | return scryptJ(passwd, salt, N, r, p, dkLen); 41 | } 42 | 43 | 44 | /** 45 | * Pure Java implementation of the scrypt KDF. 46 | * 47 | * @param passwd Password. 48 | * @param salt Salt. 49 | * @param N CPU cost parameter. 50 | * @param r Memory cost parameter. 51 | * @param p Parallelization parameter. 52 | * @param dkLen Intended length of the derived key. 53 | * 54 | * @return The derived key. 55 | * 56 | * @throws GeneralSecurityException when HMAC_SHA256 is not available. 57 | */ 58 | public static byte[] scryptJ(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException { 59 | if (N < 2 || (N & (N - 1)) != 0) throw new IllegalArgumentException("N must be a power of 2 greater than 1"); 60 | 61 | if (N > MAX_VALUE / 128 / r) throw new IllegalArgumentException("Parameter N is too large"); 62 | if (r > MAX_VALUE / 128 / p) throw new IllegalArgumentException("Parameter r is too large"); 63 | 64 | Mac mac = Mac.getInstance("HmacSHA256"); 65 | mac.init(new SecretKeySpec(passwd, "HmacSHA256")); 66 | 67 | byte[] DK = new byte[dkLen]; 68 | 69 | byte[] B = new byte[128 * r * p]; 70 | byte[] XY = new byte[256 * r]; 71 | byte[] V = new byte[128 * r * N]; 72 | int i; 73 | 74 | PBKDF.pbkdf2(mac, salt, 1, B, p * 128 * r); 75 | 76 | for (i = 0; i < p; i++) { 77 | smix(B, i * 128 * r, r, N, V, XY); 78 | } 79 | 80 | PBKDF.pbkdf2(mac, B, 1, DK, dkLen); 81 | 82 | return DK; 83 | } 84 | 85 | public static void smix(byte[] B, int Bi, int r, int N, byte[] V, byte[] XY) { 86 | int Xi = 0; 87 | int Yi = 128 * r; 88 | int i; 89 | 90 | arraycopy(B, Bi, XY, Xi, 128 * r); 91 | 92 | for (i = 0; i < N; i++) { 93 | arraycopy(XY, Xi, V, i * (128 * r), 128 * r); 94 | blockmix_salsa8(XY, Xi, Yi, r); 95 | } 96 | 97 | for (i = 0; i < N; i++) { 98 | int j = integerify(XY, Xi, r) & (N - 1); 99 | blockxor(V, j * (128 * r), XY, Xi, 128 * r); 100 | blockmix_salsa8(XY, Xi, Yi, r); 101 | } 102 | 103 | arraycopy(XY, Xi, B, Bi, 128 * r); 104 | } 105 | 106 | public static void blockmix_salsa8(byte[] BY, int Bi, int Yi, int r) { 107 | byte[] X = new byte[64]; 108 | int i; 109 | 110 | arraycopy(BY, Bi + (2 * r - 1) * 64, X, 0, 64); 111 | 112 | for (i = 0; i < 2 * r; i++) { 113 | blockxor(BY, i * 64, X, 0, 64); 114 | salsa20_8(X); 115 | arraycopy(X, 0, BY, Yi + (i * 64), 64); 116 | } 117 | 118 | for (i = 0; i < r; i++) { 119 | arraycopy(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64); 120 | } 121 | 122 | for (i = 0; i < r; i++) { 123 | arraycopy(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64); 124 | } 125 | } 126 | 127 | public static int R(int a, int b) { 128 | return (a << b) | (a >>> (32 - b)); 129 | } 130 | 131 | public static void salsa20_8(byte[] B) { 132 | int[] B32 = new int[16]; 133 | int[] x = new int[16]; 134 | int i; 135 | 136 | for (i = 0; i < 16; i++) { 137 | B32[i] = (B[i * 4 + 0] & 0xff) << 0; 138 | B32[i] |= (B[i * 4 + 1] & 0xff) << 8; 139 | B32[i] |= (B[i * 4 + 2] & 0xff) << 16; 140 | B32[i] |= (B[i * 4 + 3] & 0xff) << 24; 141 | } 142 | 143 | arraycopy(B32, 0, x, 0, 16); 144 | 145 | for (i = 8; i > 0; i -= 2) { 146 | x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); 147 | x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); 148 | x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); 149 | x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); 150 | x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); 151 | x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); 152 | x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); 153 | x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); 154 | x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); 155 | x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); 156 | x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); 157 | x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); 158 | x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); 159 | x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); 160 | x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); 161 | x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); 162 | } 163 | 164 | for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i]; 165 | 166 | for (i = 0; i < 16; i++) { 167 | B[i * 4 + 0] = (byte) (B32[i] >> 0 & 0xff); 168 | B[i * 4 + 1] = (byte) (B32[i] >> 8 & 0xff); 169 | B[i * 4 + 2] = (byte) (B32[i] >> 16 & 0xff); 170 | B[i * 4 + 3] = (byte) (B32[i] >> 24 & 0xff); 171 | } 172 | } 173 | 174 | public static void blockxor(byte[] S, int Si, byte[] D, int Di, int len) { 175 | for (int i = 0; i < len; i++) { 176 | D[Di + i] ^= S[Si + i]; 177 | } 178 | } 179 | 180 | public static int integerify(byte[] B, int Bi, int r) { 181 | int n; 182 | 183 | Bi += (2 * r - 1) * 64; 184 | 185 | n = (B[Bi + 0] & 0xff) << 0; 186 | n |= (B[Bi + 1] & 0xff) << 8; 187 | n |= (B[Bi + 2] & 0xff) << 16; 188 | n |= (B[Bi + 3] & 0xff) << 24; 189 | 190 | return n; 191 | } 192 | } -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /src/crackstation/PBKDF2/PasswordHash.java: -------------------------------------------------------------------------------- 1 | package crackstation.PBKDF2; 2 | 3 | import java.security.SecureRandom; 4 | import javax.crypto.spec.PBEKeySpec; 5 | import javax.crypto.SecretKeyFactory; 6 | import java.math.BigInteger; 7 | import java.security.NoSuchAlgorithmException; 8 | import java.security.spec.InvalidKeySpecException; 9 | 10 | /* 11 | * PBKDF2 salted password hashing. 12 | * Author: havoc AT defuse.ca 13 | * www: http://crackstation.net/hashing-security.htm 14 | * 15 | * Note: 16 | * Modified version with additional createHash methods 17 | * to receive arguments Iterations,SaltBytes,HashBytes; 18 | */ 19 | public class PasswordHash 20 | { 21 | public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1"; 22 | 23 | // The following constants may be changed without breaking existing hashes. 24 | public static final int SALT_BYTES = 24; 25 | public static final int HASH_BYTES = 24; 26 | public static final int PBKDF2_ITERATIONS = 86000; 27 | 28 | public static final int ITERATION_INDEX = 0; 29 | public static final int SALT_INDEX = 1; 30 | public static final int PBKDF2_INDEX = 2; 31 | 32 | /** 33 | * Returns a salted PBKDF2 hash of the password. 34 | * 35 | * @param password the password to hash 36 | * @return a salted PBKDF2 hash of the password 37 | */ 38 | public static String createHash(String password) 39 | throws NoSuchAlgorithmException, InvalidKeySpecException 40 | { 41 | return createHash(password.toCharArray(),null,null,null); 42 | } 43 | 44 | /** 45 | * Returns a salted PBKDF2 hash of the password. 46 | * 47 | * @param password the password to hash 48 | * @param Iterations the number of hash iterations. Default 1000 49 | * @param SaltBytes the size of the salt. Default 24 bytes. 50 | * @param HashBytes the size of the hash. Default 24 bytes. 51 | * @return a salted PBKDF2 hash of the password 52 | */ 53 | public static String createHash 54 | ( String password 55 | , Integer Iterations 56 | , Integer SaltBytes 57 | , Integer HashBytes 58 | ) 59 | throws NoSuchAlgorithmException, InvalidKeySpecException 60 | { 61 | return createHash(password.toCharArray(),Iterations,SaltBytes,HashBytes); 62 | } 63 | 64 | /** 65 | * Returns a salted PBKDF2 hash of the password. 66 | * 67 | * @param password the password to hash 68 | * @return a salted PBKDF2 hash of the password 69 | */ 70 | public static String createHash(char[] password) 71 | throws NoSuchAlgorithmException, InvalidKeySpecException 72 | { 73 | return createHash(password,null,null,null); 74 | } 75 | 76 | /** 77 | * Returns a salted PBKDF2 hash of the password. 78 | * 79 | * @param password the password to hash 80 | * @param Iterations the number of hash iterations. Default 1000 81 | * @param SaltBytes the size of the salt. Default 24 bytes. 82 | * @param HashBytes the size of the hash. Default 24 bytes. 83 | * @return a salted PBKDF2 hash of the password 84 | */ 85 | public static String createHash 86 | ( char[] password 87 | , Integer Iterations 88 | , Integer SaltBytes 89 | , Integer HashBytes 90 | ) 91 | throws NoSuchAlgorithmException, InvalidKeySpecException 92 | { 93 | if (Iterations == null) Iterations = PBKDF2_ITERATIONS; 94 | if (SaltBytes == null) SaltBytes = SALT_BYTES; 95 | if (HashBytes == null) HashBytes = HASH_BYTES; 96 | 97 | // Generate a random salt 98 | SecureRandom random = new SecureRandom(); 99 | byte[] salt = new byte[SaltBytes]; 100 | random.nextBytes(salt); 101 | 102 | // Hash the password 103 | byte[] hash = pbkdf2(password, salt, Iterations, HashBytes); 104 | // format iterations:salt:hash 105 | return Iterations + ":" + toHex(salt) + ":" + toHex(hash); 106 | } 107 | 108 | /** 109 | * Validates a password using a hash. 110 | * 111 | * @param password the password to check 112 | * @param goodHash the hash of the valid password 113 | * @return true if the password is correct, false if not 114 | */ 115 | public static boolean validatePassword(String password, String goodHash) 116 | throws NoSuchAlgorithmException, InvalidKeySpecException 117 | { 118 | return validatePassword(password.toCharArray(), goodHash); 119 | } 120 | 121 | /** 122 | * Validates a password using a hash. 123 | * 124 | * @param password the password to check 125 | * @param goodHash the hash of the valid password 126 | * @return true if the password is correct, false if not 127 | */ 128 | public static boolean validatePassword(char[] password, String goodHash) 129 | throws NoSuchAlgorithmException, InvalidKeySpecException 130 | { 131 | // Decode the hash into its parameters 132 | String[] params = goodHash.split(":"); 133 | int iterations = Integer.parseInt(params[ITERATION_INDEX]); 134 | byte[] salt = fromHex(params[SALT_INDEX]); 135 | byte[] hash = fromHex(params[PBKDF2_INDEX]); 136 | // Compute the hash of the provided password, using the same salt, 137 | // iteration count, and hash length 138 | byte[] testHash = pbkdf2(password, salt, iterations, hash.length); 139 | // Compare the hashes in constant time. The password is correct if 140 | // both hashes match. 141 | return slowEquals(hash, testHash); 142 | } 143 | 144 | /** 145 | * Compares two byte arrays in length-constant time. This comparison method 146 | * is used so that password hashes cannot be extracted from an on-line 147 | * system using a timing attack and then attacked off-line. 148 | * 149 | * @param a the first byte array 150 | * @param b the second byte array 151 | * @return true if both byte arrays are the same, false if not 152 | */ 153 | private static boolean slowEquals(byte[] a, byte[] b) 154 | { 155 | int diff = a.length ^ b.length; 156 | for(int i = 0; i < a.length && i < b.length; i++) 157 | diff |= a[i] ^ b[i]; 158 | return diff == 0; 159 | } 160 | 161 | /** 162 | * Computes the PBKDF2 hash of a password. 163 | * 164 | * @param password the password to hash. 165 | * @param salt the salt 166 | * @param iterations the iteration count (slowness factor) 167 | * @param bytes the length of the hash to compute in bytes 168 | * @return the PBDKF2 hash of the password 169 | */ 170 | private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) 171 | throws NoSuchAlgorithmException, InvalidKeySpecException 172 | { 173 | PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8); 174 | SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); 175 | return skf.generateSecret(spec).getEncoded(); 176 | } 177 | 178 | /** 179 | * Converts a string of hexadecimal characters into a byte array. 180 | * 181 | * @param hex the hex string 182 | * @return the hex string decoded into a byte array 183 | */ 184 | private static byte[] fromHex(String hex) 185 | { 186 | byte[] binary = new byte[hex.length() / 2]; 187 | for(int i = 0; i < binary.length; i++) 188 | { 189 | binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16); 190 | } 191 | return binary; 192 | } 193 | 194 | /** 195 | * Converts a byte array into a hexadecimal string. 196 | * 197 | * @param array the byte array to convert 198 | * @return a length*2 character string encoding the byte array 199 | */ 200 | private static String toHex(byte[] array) 201 | { 202 | BigInteger bi = new BigInteger(1, array); 203 | String hex = bi.toString(16); 204 | int paddingLength = (array.length * 2) - hex.length(); 205 | if(paddingLength > 0) 206 | return String.format("%0" + paddingLength + "d", 0) + hex; 207 | else 208 | return hex; 209 | } 210 | 211 | } -------------------------------------------------------------------------------- /src/org/mindrot/jbcrypt/BCrypt.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2006 Damien Miller 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | package org.mindrot.jbcrypt; 16 | 17 | import java.io.UnsupportedEncodingException; 18 | import java.security.SecureRandom; 19 | 20 | /** 21 | * BCrypt implements OpenBSD-style Blowfish password hashing using 22 | * the scheme described in "A Future-Adaptable Password Scheme" by 23 | * Niels Provos and David Mazieres. 24 | *

    25 | * This password hashing system tries to thwart off-line password 26 | * cracking using a computationally-intensive hashing algorithm, 27 | * based on Bruce Schneier's Blowfish cipher. The work factor of 28 | * the algorithm is parameterised, so it can be increased as 29 | * computers get faster. 30 | *

    31 | * Usage is really simple. To hash a password for the first time, 32 | * call the hashpw method with a random salt, like this: 33 | *

    34 | * 35 | * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
    36 | *
    37 | *

    38 | * To check whether a plaintext password matches one that has been 39 | * hashed previously, use the checkpw method: 40 | *

    41 | * 42 | * if (BCrypt.checkpw(candidate_password, stored_hash))
    43 | *     System.out.println("It matches");
    44 | * else
    45 | *     System.out.println("It does not match");
    46 | *
    47 | *

    48 | * The gensalt() method takes an optional parameter (log_rounds) 49 | * that determines the computational complexity of the hashing: 50 | *

    51 | * 52 | * String strong_salt = BCrypt.gensalt(10)
    53 | * String stronger_salt = BCrypt.gensalt(12)
    54 | *
    55 | *

    56 | * The amount of work increases exponentially (2**log_rounds), so 57 | * each increment is twice as much work. The default log_rounds is 58 | * 10, and the valid range is 4 to 30. 59 | * 60 | * @author Damien Miller 61 | * @version 0.2 62 | */ 63 | public class BCrypt { 64 | // BCrypt parameters 65 | private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; 66 | private static final int BCRYPT_SALT_LEN = 16; 67 | 68 | // Blowfish parameters 69 | private static final int BLOWFISH_NUM_ROUNDS = 16; 70 | 71 | // Initial contents of key schedule 72 | private static final int P_orig[] = { 73 | 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 74 | 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 75 | 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 76 | 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 77 | 0x9216d5d9, 0x8979fb1b 78 | }; 79 | private static final int S_orig[] = { 80 | 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 81 | 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 82 | 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 83 | 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 84 | 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 85 | 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 86 | 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 87 | 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 88 | 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 89 | 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 90 | 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 91 | 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 92 | 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 93 | 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 94 | 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 95 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 96 | 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 97 | 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 98 | 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 99 | 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 100 | 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 101 | 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 102 | 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 103 | 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 104 | 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 105 | 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 106 | 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 107 | 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 108 | 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 109 | 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 110 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 111 | 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 112 | 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 113 | 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 114 | 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 115 | 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 116 | 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 117 | 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 118 | 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 119 | 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 120 | 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 121 | 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 122 | 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 123 | 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 124 | 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 125 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 126 | 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 127 | 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 128 | 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 129 | 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 130 | 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 131 | 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 132 | 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 133 | 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 134 | 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 135 | 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 136 | 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 137 | 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 138 | 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 139 | 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 140 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 141 | 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 142 | 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 143 | 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 144 | 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 145 | 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 146 | 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 147 | 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 148 | 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 149 | 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 150 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 151 | 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 152 | 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 153 | 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 154 | 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 155 | 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 156 | 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 157 | 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 158 | 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 159 | 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 160 | 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 161 | 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 162 | 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 163 | 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 164 | 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 165 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 166 | 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 167 | 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 168 | 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 169 | 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 170 | 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 171 | 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 172 | 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 173 | 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 174 | 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 175 | 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 176 | 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 177 | 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 178 | 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 179 | 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 180 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 181 | 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 182 | 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 183 | 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 184 | 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 185 | 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 186 | 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 187 | 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 188 | 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 189 | 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 190 | 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 191 | 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 192 | 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 193 | 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 194 | 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 195 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 196 | 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 197 | 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 198 | 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 199 | 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 200 | 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 201 | 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 202 | 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 203 | 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 204 | 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 205 | 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 206 | 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 207 | 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 208 | 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 209 | 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 210 | 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 211 | 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 212 | 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 213 | 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 214 | 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 215 | 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 216 | 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 217 | 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 218 | 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 219 | 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 220 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 221 | 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 222 | 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 223 | 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 224 | 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 225 | 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 226 | 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 227 | 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 228 | 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 229 | 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 230 | 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 231 | 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 232 | 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 233 | 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 234 | 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 235 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 236 | 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 237 | 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 238 | 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 239 | 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 240 | 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 241 | 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 242 | 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 243 | 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 244 | 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 245 | 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 246 | 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 247 | 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 248 | 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 249 | 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 250 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 251 | 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 252 | 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 253 | 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 254 | 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 255 | 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 256 | 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 257 | 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 258 | 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 259 | 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 260 | 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 261 | 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 262 | 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 263 | 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 264 | 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 265 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 266 | 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 267 | 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 268 | 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 269 | 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 270 | 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 271 | 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 272 | 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 273 | 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 274 | 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 275 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 276 | 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 277 | 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 278 | 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 279 | 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 280 | 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 281 | 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 282 | 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 283 | 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 284 | 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 285 | 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 286 | 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 287 | 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 288 | 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 289 | 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 290 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 291 | 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 292 | 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 293 | 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 294 | 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 295 | 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 296 | 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 297 | 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 298 | 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 299 | 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 300 | 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 301 | 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 302 | 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 303 | 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 304 | 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 305 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 306 | 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 307 | 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 308 | 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 309 | 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 310 | 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 311 | 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 312 | 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 313 | 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 314 | 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 315 | 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 316 | 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 317 | 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 318 | 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 319 | 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 320 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 321 | 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 322 | 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 323 | 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 324 | 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 325 | 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 326 | 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 327 | 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 328 | 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 329 | 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 330 | 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 331 | 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 332 | 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 333 | 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 334 | 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 335 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 336 | }; 337 | 338 | // bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls 339 | // this "ciphertext", but it is really plaintext or an IV. We keep 340 | // the name to make code comparison easier. 341 | static private final int bf_crypt_ciphertext[] = { 342 | 0x4f727068, 0x65616e42, 0x65686f6c, 343 | 0x64657253, 0x63727944, 0x6f756274 344 | }; 345 | 346 | // Table for Base64 encoding 347 | static private final char base64_code[] = { 348 | '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 349 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 350 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 351 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 352 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', 353 | '6', '7', '8', '9' 354 | }; 355 | 356 | // Table for Base64 decoding 357 | static private final byte index_64[] = { 358 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 359 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 360 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 361 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 362 | -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 363 | 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, 364 | -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 365 | 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 366 | 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 367 | -1, -1, -1, -1, -1, -1, 28, 29, 30, 368 | 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 369 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 370 | 51, 52, 53, -1, -1, -1, -1, -1 371 | }; 372 | 373 | // Expanded Blowfish key 374 | private int P[]; 375 | private int S[]; 376 | 377 | /** 378 | * Encode a byte array using bcrypt's slightly-modified base64 379 | * encoding scheme. Note that this is *not* compatible with 380 | * the standard MIME-base64 encoding. 381 | * 382 | * @param d the byte array to encode 383 | * @param len the number of bytes to encode 384 | * @return base64-encoded string 385 | * @exception IllegalArgumentException if the length is invalid 386 | */ 387 | private static String encode_base64(byte d[], int len) 388 | throws IllegalArgumentException { 389 | int off = 0; 390 | StringBuffer rs = new StringBuffer(); 391 | int c1, c2; 392 | 393 | if (len <= 0 || len > d.length) 394 | throw new IllegalArgumentException ("Invalid len"); 395 | 396 | while (off < len) { 397 | c1 = d[off++] & 0xff; 398 | rs.append(base64_code[(c1 >> 2) & 0x3f]); 399 | c1 = (c1 & 0x03) << 4; 400 | if (off >= len) { 401 | rs.append(base64_code[c1 & 0x3f]); 402 | break; 403 | } 404 | c2 = d[off++] & 0xff; 405 | c1 |= (c2 >> 4) & 0x0f; 406 | rs.append(base64_code[c1 & 0x3f]); 407 | c1 = (c2 & 0x0f) << 2; 408 | if (off >= len) { 409 | rs.append(base64_code[c1 & 0x3f]); 410 | break; 411 | } 412 | c2 = d[off++] & 0xff; 413 | c1 |= (c2 >> 6) & 0x03; 414 | rs.append(base64_code[c1 & 0x3f]); 415 | rs.append(base64_code[c2 & 0x3f]); 416 | } 417 | return rs.toString(); 418 | } 419 | 420 | /** 421 | * Look up the 3 bits base64-encoded by the specified character, 422 | * range-checking againt conversion table 423 | * @param x the base64-encoded value 424 | * @return the decoded value of x 425 | */ 426 | private static byte char64(char x) { 427 | if ((int)x < 0 || (int)x > index_64.length) 428 | return -1; 429 | return index_64[(int)x]; 430 | } 431 | 432 | /** 433 | * Decode a string encoded using bcrypt's base64 scheme to a 434 | * byte array. Note that this is *not* compatible with 435 | * the standard MIME-base64 encoding. 436 | * @param s the string to decode 437 | * @param maxolen the maximum number of bytes to decode 438 | * @return an array containing the decoded bytes 439 | * @throws IllegalArgumentException if maxolen is invalid 440 | */ 441 | private static byte[] decode_base64(String s, int maxolen) 442 | throws IllegalArgumentException { 443 | StringBuffer rs = new StringBuffer(); 444 | int off = 0, slen = s.length(), olen = 0; 445 | byte ret[]; 446 | byte c1, c2, c3, c4, o; 447 | 448 | if (maxolen <= 0) 449 | throw new IllegalArgumentException ("Invalid maxolen"); 450 | 451 | while (off < slen - 1 && olen < maxolen) { 452 | c1 = char64(s.charAt(off++)); 453 | c2 = char64(s.charAt(off++)); 454 | if (c1 == -1 || c2 == -1) 455 | break; 456 | o = (byte)(c1 << 2); 457 | o |= (c2 & 0x30) >> 4; 458 | rs.append((char)o); 459 | if (++olen >= maxolen || off >= slen) 460 | break; 461 | c3 = char64(s.charAt(off++)); 462 | if (c3 == -1) 463 | break; 464 | o = (byte)((c2 & 0x0f) << 4); 465 | o |= (c3 & 0x3c) >> 2; 466 | rs.append((char)o); 467 | if (++olen >= maxolen || off >= slen) 468 | break; 469 | c4 = char64(s.charAt(off++)); 470 | o = (byte)((c3 & 0x03) << 6); 471 | o |= c4; 472 | rs.append((char)o); 473 | ++olen; 474 | } 475 | 476 | ret = new byte[olen]; 477 | for (off = 0; off < olen; off++) 478 | ret[off] = (byte)rs.charAt(off); 479 | return ret; 480 | } 481 | 482 | /** 483 | * Blowfish encipher a single 64-bit block encoded as 484 | * two 32-bit halves 485 | * @param lr an array containing the two 32-bit half blocks 486 | * @param off the position in the array of the blocks 487 | */ 488 | private final void encipher(int lr[], int off) { 489 | int i, n, l = lr[off], r = lr[off + 1]; 490 | 491 | l ^= P[0]; 492 | for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { 493 | // Feistel substitution on left word 494 | n = S[(l >> 24) & 0xff]; 495 | n += S[0x100 | ((l >> 16) & 0xff)]; 496 | n ^= S[0x200 | ((l >> 8) & 0xff)]; 497 | n += S[0x300 | (l & 0xff)]; 498 | r ^= n ^ P[++i]; 499 | 500 | // Feistel substitution on right word 501 | n = S[(r >> 24) & 0xff]; 502 | n += S[0x100 | ((r >> 16) & 0xff)]; 503 | n ^= S[0x200 | ((r >> 8) & 0xff)]; 504 | n += S[0x300 | (r & 0xff)]; 505 | l ^= n ^ P[++i]; 506 | } 507 | lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; 508 | lr[off + 1] = l; 509 | } 510 | 511 | /** 512 | * Cycically extract a word of key material 513 | * @param data the string to extract the data from 514 | * @param offp a "pointer" (as a one-entry array) to the 515 | * current offset into data 516 | * @return the next word of material from data 517 | */ 518 | private static int streamtoword(byte data[], int offp[]) { 519 | int i; 520 | int word = 0; 521 | int off = offp[0]; 522 | 523 | for (i = 0; i < 4; i++) { 524 | word = (word << 8) | (data[off] & 0xff); 525 | off = (off + 1) % data.length; 526 | } 527 | 528 | offp[0] = off; 529 | return word; 530 | } 531 | 532 | /** 533 | * Initialise the Blowfish key schedule 534 | */ 535 | private void init_key() { 536 | P = (int[])P_orig.clone(); 537 | S = (int[])S_orig.clone(); 538 | } 539 | 540 | /** 541 | * Key the Blowfish cipher 542 | * @param key an array containing the key 543 | */ 544 | private void key(byte key[]) { 545 | int i; 546 | int koffp[] = { 0 }; 547 | int lr[] = { 0, 0 }; 548 | int plen = P.length, slen = S.length; 549 | 550 | for (i = 0; i < plen; i++) 551 | P[i] = P[i] ^ streamtoword(key, koffp); 552 | 553 | for (i = 0; i < plen; i += 2) { 554 | encipher(lr, 0); 555 | P[i] = lr[0]; 556 | P[i + 1] = lr[1]; 557 | } 558 | 559 | for (i = 0; i < slen; i += 2) { 560 | encipher(lr, 0); 561 | S[i] = lr[0]; 562 | S[i + 1] = lr[1]; 563 | } 564 | } 565 | 566 | /** 567 | * Perform the "enhanced key schedule" step described by 568 | * Provos and Mazieres in "A Future-Adaptable Password Scheme" 569 | * http://www.openbsd.org/papers/bcrypt-paper.ps 570 | * @param data salt information 571 | * @param key password information 572 | */ 573 | private void ekskey(byte data[], byte key[]) { 574 | int i; 575 | int koffp[] = { 0 }, doffp[] = { 0 }; 576 | int lr[] = { 0, 0 }; 577 | int plen = P.length, slen = S.length; 578 | 579 | for (i = 0; i < plen; i++) 580 | P[i] = P[i] ^ streamtoword(key, koffp); 581 | 582 | for (i = 0; i < plen; i += 2) { 583 | lr[0] ^= streamtoword(data, doffp); 584 | lr[1] ^= streamtoword(data, doffp); 585 | encipher(lr, 0); 586 | P[i] = lr[0]; 587 | P[i + 1] = lr[1]; 588 | } 589 | 590 | for (i = 0; i < slen; i += 2) { 591 | lr[0] ^= streamtoword(data, doffp); 592 | lr[1] ^= streamtoword(data, doffp); 593 | encipher(lr, 0); 594 | S[i] = lr[0]; 595 | S[i + 1] = lr[1]; 596 | } 597 | } 598 | 599 | /** 600 | * Perform the central password hashing step in the 601 | * bcrypt scheme 602 | * @param password the password to hash 603 | * @param salt the binary salt to hash with the password 604 | * @param log_rounds the binary logarithm of the number 605 | * of rounds of hashing to apply 606 | * @param cdata the plaintext to encrypt 607 | * @return an array containing the binary hashed password 608 | */ 609 | public byte[] crypt_raw(byte password[], byte salt[], int log_rounds, 610 | int cdata[]) { 611 | int rounds, i, j; 612 | int clen = cdata.length; 613 | byte ret[]; 614 | 615 | if (log_rounds < 4 || log_rounds > 30) 616 | throw new IllegalArgumentException ("Bad number of rounds"); 617 | rounds = 1 << log_rounds; 618 | if (salt.length != BCRYPT_SALT_LEN) 619 | throw new IllegalArgumentException ("Bad salt length"); 620 | 621 | init_key(); 622 | ekskey(salt, password); 623 | for (i = 0; i != rounds; i++) { 624 | key(password); 625 | key(salt); 626 | } 627 | 628 | for (i = 0; i < 64; i++) { 629 | for (j = 0; j < (clen >> 1); j++) 630 | encipher(cdata, j << 1); 631 | } 632 | 633 | ret = new byte[clen * 4]; 634 | for (i = 0, j = 0; i < clen; i++) { 635 | ret[j++] = (byte)((cdata[i] >> 24) & 0xff); 636 | ret[j++] = (byte)((cdata[i] >> 16) & 0xff); 637 | ret[j++] = (byte)((cdata[i] >> 8) & 0xff); 638 | ret[j++] = (byte)(cdata[i] & 0xff); 639 | } 640 | return ret; 641 | } 642 | 643 | /** 644 | * Hash a password using the OpenBSD bcrypt scheme 645 | * @param password the password to hash 646 | * @param salt the salt to hash with (perhaps generated 647 | * using BCrypt.gensalt) 648 | * @return the hashed password 649 | */ 650 | public static String hashpw(String password, String salt) { 651 | BCrypt B; 652 | String real_salt; 653 | byte passwordb[], saltb[], hashed[]; 654 | char minor = (char)0; 655 | int rounds, off = 0; 656 | StringBuffer rs = new StringBuffer(); 657 | 658 | if (salt.charAt(0) != '$' || salt.charAt(1) != '2') 659 | throw new IllegalArgumentException ("Invalid salt version"); 660 | if (salt.charAt(2) == '$') 661 | off = 3; 662 | else { 663 | minor = salt.charAt(2); 664 | if (minor != 'a' || salt.charAt(3) != '$') 665 | throw new IllegalArgumentException ("Invalid salt revision"); 666 | off = 4; 667 | } 668 | 669 | // Extract number of rounds 670 | if (salt.charAt(off + 2) > '$') 671 | throw new IllegalArgumentException ("Missing salt rounds"); 672 | rounds = Integer.parseInt(salt.substring(off, off + 2)); 673 | 674 | real_salt = salt.substring(off + 3, off + 25); 675 | try { 676 | passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8"); 677 | } catch (UnsupportedEncodingException uee) { 678 | throw new AssertionError("UTF-8 is not supported"); 679 | } 680 | 681 | saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); 682 | 683 | B = new BCrypt(); 684 | hashed = B.crypt_raw(passwordb, saltb, rounds, 685 | (int[])bf_crypt_ciphertext.clone()); 686 | 687 | rs.append("$2"); 688 | if (minor >= 'a') 689 | rs.append(minor); 690 | rs.append("$"); 691 | if (rounds < 10) 692 | rs.append("0"); 693 | if (rounds > 30) { 694 | throw new IllegalArgumentException( 695 | "rounds exceeds maximum (30)"); 696 | } 697 | rs.append(Integer.toString(rounds)); 698 | rs.append("$"); 699 | rs.append(encode_base64(saltb, saltb.length)); 700 | rs.append(encode_base64(hashed, 701 | bf_crypt_ciphertext.length * 4 - 1)); 702 | return rs.toString(); 703 | } 704 | 705 | /** 706 | * Generate a salt for use with the BCrypt.hashpw() method 707 | * @param log_rounds the log2 of the number of rounds of 708 | * hashing to apply - the work factor therefore increases as 709 | * 2**log_rounds. 710 | * @param random an instance of SecureRandom to use 711 | * @return an encoded salt value 712 | */ 713 | public static String gensalt(int log_rounds, SecureRandom random) { 714 | StringBuffer rs = new StringBuffer(); 715 | byte rnd[] = new byte[BCRYPT_SALT_LEN]; 716 | 717 | random.nextBytes(rnd); 718 | 719 | rs.append("$2a$"); 720 | if (log_rounds < 10) 721 | rs.append("0"); 722 | if (log_rounds > 30) { 723 | throw new IllegalArgumentException( 724 | "log_rounds exceeds maximum (30)"); 725 | } 726 | rs.append(Integer.toString(log_rounds)); 727 | rs.append("$"); 728 | rs.append(encode_base64(rnd, rnd.length)); 729 | return rs.toString(); 730 | } 731 | 732 | /** 733 | * Generate a salt for use with the BCrypt.hashpw() method 734 | * @param log_rounds the log2 of the number of rounds of 735 | * hashing to apply - the work factor therefore increases as 736 | * 2**log_rounds. 737 | * @return an encoded salt value 738 | */ 739 | public static String gensalt(int log_rounds) { 740 | return gensalt(log_rounds, new SecureRandom()); 741 | } 742 | 743 | /** 744 | * Generate a salt for use with the BCrypt.hashpw() method, 745 | * selecting a reasonable default for the number of hashing 746 | * rounds to apply 747 | * @return an encoded salt value 748 | */ 749 | public static String gensalt() { 750 | return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); 751 | } 752 | 753 | /** 754 | * Check that a plaintext password matches a previously hashed 755 | * one 756 | * @param plaintext the plaintext password to verify 757 | * @param hashed the previously-hashed password 758 | * @return true if the passwords match, false otherwise 759 | */ 760 | public static boolean checkpw(String plaintext, String hashed) { 761 | byte hashed_bytes[]; 762 | byte try_bytes[]; 763 | try { 764 | String try_pw = hashpw(plaintext, hashed); 765 | hashed_bytes = hashed.getBytes("UTF-8"); 766 | try_bytes = try_pw.getBytes("UTF-8"); 767 | } catch (UnsupportedEncodingException uee) { 768 | return false; 769 | } 770 | if (hashed_bytes.length != try_bytes.length) 771 | return false; 772 | byte ret = 0; 773 | for (int i = 0; i < try_bytes.length; i++) 774 | ret |= hashed_bytes[i] ^ try_bytes[i]; 775 | return ret == 0; 776 | } 777 | } 778 | --------------------------------------------------------------------------------