Merge pull request #1495 from splitbrain/django_pbkdf2
add support for new Django hashing methods
This commit is contained in:
commit
7ca2e2cf7c
|
@ -17,6 +17,24 @@ class PassHash_test extends DokuWikiTest {
|
|||
$this->assertEquals('fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', PassHash::hmac('sha1','',''));
|
||||
$this->assertEquals('80070713463e7749b90c2dc24911e275', PassHash::hmac('md5','The quick brown fox jumps over the lazy dog','key'));
|
||||
$this->assertEquals('de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9', PassHash::hmac('sha1','The quick brown fox jumps over the lazy dog','key'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function test_djangopbkdf2() {
|
||||
if(!function_exists('hash_pbkdf2') || !in_array('sha256', hash_algos())){
|
||||
$this->markTestSkipped('missing hash functions for djangopbkdf2 password tests');
|
||||
return;
|
||||
}
|
||||
|
||||
$ph = new PassHash();
|
||||
$knownpasses = array (
|
||||
'pbkdf2_sha256$24000$LakQQ2OOTO1v$dmUgz8V7zcpaoBSA3MV76J5a4rzrszF0NpxGx6HRBbE=',
|
||||
'pbkdf2_sha256$24000$PXogIZpE4gaK$F/P/L5SRrbb6taOGEr4w6DhxjMzNAj1jEWTPyAUn8WU=',
|
||||
'pbkdf2_sha256$24000$vtn5APnhirmB$/jzJXYvm78X8/FCOMhGUmcCy0iWhtk0L1hcBWN1AYZc=',
|
||||
'pbkdf2_sha256$24000$meyCtGKrS5Ai$vkMfMzB/yGFKplmXujgtfl3OGR27AwOQmP+YeRP6lbw=',
|
||||
'pbkdf2_sha256$24000$M8ecC8zfqLmJ$l6cIa/Od+m56VMm9hJbdPNhTXZykPVbUGGTPx7/VRE4=',
|
||||
);
|
||||
foreach($knownpasses as $known) {
|
||||
$this->assertTrue($ph->verify_hash('P4zzW0rd!', $known));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class auth_password_test extends DokuWikiTest {
|
||||
|
||||
// hashes for the password foo$method, using abcdefgh as salt
|
||||
// hashes for the password foo$method, using abcdefgh12345678912345678912345678 as salt
|
||||
var $passes = array(
|
||||
'smd5' => '$1$abcdefgh$SYbjm2AEvSoHG7Xapi8so.',
|
||||
'apr1' => '$apr1$abcdefgh$C/GzYTF4kOVByYLEoD5X4.',
|
||||
|
@ -24,14 +24,24 @@ class auth_password_test extends DokuWikiTest {
|
|||
// Check SHA512 only if available in this PHP
|
||||
$this->passes['sha512'] = '$6$abcdefgh12345678$J9.zOcgx0lotwZdcz0uulA3IVQMinZvFZVjA5vapRLVAAqtay23XD4xeeUxQ3B4JvDWYFBIxVWW1tOYlHX13k1';
|
||||
}
|
||||
if(function_exists('hash_pbkdf2')) {
|
||||
if(in_array('sha256', hash_algos())) {
|
||||
$this->passes['djangopbkdf2_sha256'] = 'pbkdf2_sha256$24000$abcdefgh1234$R23OyZJ0nGHLG6MvPNfEkV5AOz3jUY5zthByPXs2gn0=';
|
||||
}
|
||||
if(in_array('sha1', hash_algos())) {
|
||||
$this->passes['djangopbkdf2_sha1'] = 'pbkdf2_sha1$24000$abcdefgh1234$pOliX4vV1hgOv7lFNURIHHx41HI=';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_cryptPassword(){
|
||||
foreach($this->passes as $method => $hash){
|
||||
$info = "testing method $method";
|
||||
$this->assertEquals(auth_cryptPassword('foo'.$method, $method,'abcdefgh12345678912345678912345678'),
|
||||
$hash, $info);
|
||||
$this->assertEquals(
|
||||
$hash,
|
||||
auth_cryptPassword('foo'.$method, $method,'abcdefgh12345678912345678912345678'),
|
||||
$info);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,8 +42,15 @@ class PassHash {
|
|||
$magic = 'P';
|
||||
} elseif(preg_match('/^\$H\$(.{31})$/', $hash, $m)) {
|
||||
$method = 'pmd5';
|
||||
$salt = $m[1];
|
||||
$magic = 'H';
|
||||
$salt = $m[1];
|
||||
$magic = 'H';
|
||||
} elseif(preg_match('/^pbkdf2_(\w+?)\$(\d+)\$(.{12})\$/', $hash, $m)) {
|
||||
$method = 'djangopbkdf2';
|
||||
$magic = array(
|
||||
'algo' => $m[1],
|
||||
'iter' => $m[2],
|
||||
);
|
||||
$salt = $m[3];
|
||||
} elseif(preg_match('/^sha1\$(.{5})\$/', $hash, $m)) {
|
||||
$method = 'djangosha1';
|
||||
$salt = $m[1];
|
||||
|
@ -83,7 +90,8 @@ class PassHash {
|
|||
|
||||
//crypt and compare
|
||||
$call = 'hash_'.$method;
|
||||
if($this->$call($clear, $salt, $magic) === $hash) {
|
||||
$newhash = $this->$call($clear, $salt, $magic);
|
||||
if($newhash === $hash) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -435,6 +443,69 @@ class PassHash {
|
|||
return 'md5$'.$salt.'$'.md5($salt.$clear);
|
||||
}
|
||||
|
||||
/**
|
||||
* Password hashing method 'djangopbkdf2'
|
||||
*
|
||||
* An algorithm and iteration count should be given in the opts array.
|
||||
* Defaults to sha256 and 24000 iterations
|
||||
*
|
||||
* @param string $clear The clear text to hash
|
||||
* @param string $salt The salt to use, null for random
|
||||
* @param array $opts ('algo' => hash algorithm, 'iter' => iterations)
|
||||
* @return string Hashed password
|
||||
* @throws Exception when PHP is missing support for the method/algo
|
||||
*/
|
||||
public function hash_djangopbkdf2($clear, $salt=null, $opts=array()) {
|
||||
$this->init_salt($salt, 12);
|
||||
if(empty($opts['algo'])) {
|
||||
$algo = 'sha256';
|
||||
} else {
|
||||
$algo = $opts['algo'];
|
||||
}
|
||||
if(empty($opts['iter'])) {
|
||||
$iter = 24000;
|
||||
} else {
|
||||
$iter = (int) $opts['iter'];
|
||||
}
|
||||
if(!function_exists('hash_pbkdf2')) {
|
||||
throw new Exception('This PHP installation has no PBKDF2 support');
|
||||
}
|
||||
if(!in_array($algo, hash_algos())) {
|
||||
throw new Exception("This PHP installation has no $algo support");
|
||||
}
|
||||
|
||||
$hash = base64_encode(hash_pbkdf2($algo, $clear, $salt, $iter, 0, true));
|
||||
return "pbkdf2_$algo\$$iter\$$salt\$$hash";
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for djangopbkdf2 defaulting to sha256 as hash algorithm
|
||||
*
|
||||
* @param string $clear The clear text to hash
|
||||
* @param string $salt The salt to use, null for random
|
||||
* @param array $opts ('iter' => iterations)
|
||||
* @return string Hashed password
|
||||
* @throws Exception when PHP is missing support for the method/algo
|
||||
*/
|
||||
public function hash_djangopbkdf2_sha256($clear, $salt=null, $opts=array()) {
|
||||
$opts['algo'] = 'sha256';
|
||||
return $this->hash_djangopbkdf2($clear, $salt, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for djangopbkdf2 defaulting to sha1 as hash algorithm
|
||||
*
|
||||
* @param string $clear The clear text to hash
|
||||
* @param string $salt The salt to use, null for random
|
||||
* @param array $opts ('iter' => iterations)
|
||||
* @return string Hashed password
|
||||
* @throws Exception when PHP is missing support for the method/algo
|
||||
*/
|
||||
public function hash_djangopbkdf2_sha1($clear, $salt=null, $opts=array()) {
|
||||
$opts['algo'] = 'sha1';
|
||||
return $this->hash_djangopbkdf2($clear, $salt, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passwordhashing method 'bcrypt'
|
||||
*
|
||||
|
|
|
@ -137,7 +137,10 @@ $meta['_authentication'] = array('fieldset');
|
|||
$meta['useacl'] = array('onoff','_caution' => 'danger');
|
||||
$meta['autopasswd'] = array('onoff');
|
||||
$meta['authtype'] = array('authtype','_caution' => 'danger');
|
||||
$meta['passcrypt'] = array('multichoice','_choices' => array('smd5','md5','apr1','sha1','ssha','lsmd5','crypt','mysql','my411','kmd5','pmd5','hmd5','mediawiki','bcrypt','djangomd5','djangosha1','sha512'));
|
||||
$meta['passcrypt'] = array('multichoice','_choices' => array(
|
||||
'smd5','md5','apr1','sha1','ssha','lsmd5','crypt','mysql','my411','kmd5','pmd5','hmd5',
|
||||
'mediawiki','bcrypt','djangomd5','djangosha1','djangopbkdf2_sha1','djangopbkdf2_sha256','sha512'
|
||||
));
|
||||
$meta['defaultgroup']= array('string');
|
||||
$meta['superuser'] = array('string','_caution' => 'danger');
|
||||
$meta['manager'] = array('string');
|
||||
|
|
Loading…
Reference in New Issue