/** * @see phpsecStore::write() */ public function write($type, $id, $data) { $fileName = $this->fileName($type, $id); $data = serialize($data); $saveData['id'] = base64_encode($id); $saveData['mac'] = base64_encode(phpsecCrypt::pbkdf2($data, $id, 1000, 32)); $saveData['time'] = time(); $jsonData = json_encode($saveData); $fp = fopen($fileName, 'w'); if ($fp !== false) { if (flock($fp, LOCK_EX)) { fwrite($fp, $jsonData . "\n\n"); fwrite($fp, $data); flock($fp, LOCK_UN); fclose($fp); return true; } else { phpsec::error('Could not lock file while writing to store'); } } return false; }
/** * @brief Check if the password is correct * @param $uid The username * @param $password The password * @returns true/false * * Check if the password is correct without logging in the user */ public function checkPassword($uid, $password) { $query = OC_DB::prepare('SELECT username, password FROM auth_user WHERE username = ?'); $result = $query->execute(array($uid)); $row = $result->fetchRow(); if ($row) { $storedHash = $row['password']; if (self::beginsWith($storedHash, 'sha1')) { $chunks = preg_split('/\\$/', $storedHash, 3); $salt = $chunks[1]; $hash = $chunks[2]; if (sha1($salt . $password) === $hash) { return $uid; } else { return false; } } elseif (self::beginsWith($storedHash, 'pbkdf2')) { $chunks = preg_split('/\\$/', $storedHash, 4); list($pbkdf, $algorithm) = preg_split('/_/', $chunks[0]); $iter = $chunks[1]; $salt = $chunks[2]; $hash = $chunks[3]; if ($algorithm === 'sha1') { $digest_size = 20; } elseif ($algorithm === 'sha256') { $digest_size = 32; } else { return false; } if (base64_encode(phpsecCrypt::pbkdf2($password, $salt, $iter, $digest_size, $algorithm)) === $hash) { return $uid; } else { return false; } } } else { return false; } }
/** * Check a string against a hash. * * @param string $str * String to check. * * @param string $hash * The hash to check the string against. * * @return bool * Returns true on match. */ public static function check($str, $hash) { $regex_pattern = '/^\\$[a-z, 1-6]{1,6}\\$/i'; preg_match($regex_pattern, $hash, $matches); list($method) = $matches; switch ($method) { case self::PBKDF2: $param = array(); list(, , $params, $hash, $salt) = explode('$', $hash); parse_str($params, $param); if (base64_decode($hash) === phpsecCrypt::pbkdf2($str, base64_decode($salt), $param['c'], $param['dk'], $param['f'])) { return true; } return false; break; default: if (crypt($str, $hash) === $hash) { return true; } return false; break; } }
/** * Validate a user-supplied password against a stored password generated * using the phpsecPw::hash() method. * * @param string $password * The password supplied by the user in the login form. * * @param string $dbPassword * The json string fetched from the database, in the exact format * as created by phpsecPw::hash(). * * @return boolean * True on password match, false otherwise. */ public static function check($password, $dbPassword) { /** * Unserialize registered password array and validate it to ensure * we got a valid array. */ $data = json_decode($dbPassword, true); $dataStructure = array('hash' => true, 'salt' => true, 'algo' => true); /* Check structure of array. */ if ($data !== null && phpsec::arrayCheck($data, $dataStructure)) { /* Try to Base64 decode the salt. base64_decode() will return false * if the string passed is not Base64 encoded. This way we can separate * binary salts from the old type of salts. */ $decodedSalt = base64_decode($data['salt'], true); if ($decodedSalt !== false) { /* The salt was Base64 encoded. Use the decoded version. */ $data['salt'] = $decodedSalt; } /** * We do a switch on the 6 first characters on the used hashing method. * This way we are able to catch when pbkdf2 is used, since this has * it's iteration count, derived key length and PRF attached to it: * "pbkdf2:iteration count:derived key length:PRF" */ switch (substr($data['algo'], 0, 6)) { case self::phpsecPw_PBKDF2: /* As described above, we need to seperate out the iteration count * and derived key length. */ list($method, $iterationCount, $dkLen, $prf) = explode(':', $data['algo']); /* Just to make sure anything fishy isn't going on. */ if (!is_numeric($iterationCount) || !is_numeric($dkLen)) { return false; } /* Create a new derived key, with the iteration count and derived key length * that were used when generating the original dk. */ $dk = phpsecCrypt::pbkdf2($password, $data['salt'], $iterationCount, $dkLen, $prf); /* Check the new dk against the old base64 encoded dk. */ if ($dk === base64_decode($data['hash'])) { return true; } break; default: /* If not pbkdf2, we assume normal hash. */ $pwInjected = self::inject($password, $data['salt']); /* Create a hash and see if it matches. */ if (hash($data['algo'], $pwInjected) == $data['hash']) { return true; } } } else { /* Invalid array supplied. */ phpsec::error('Invalid data supplied. Expected serialized array as returned by pwHash()'); } return false; }
/** * @see phpsecStore::write() */ public function write($type, $id, $data) { /* Delete existing data first, to prevent a huge database. */ $this->delete($type, $id); /* Prepeare query. */ $sth = $this->dbh->prepare('INSERT INTO ' . $this->table . ' (`id`, `mac`, `time`, `type`, `data`)' . 'VALUES(:id, :mac, :time, :type, :data)'); /* Serialize data, and create a MAC. */ $data = serialize($data); $mac = phpsecCrypt::pbkdf2($data, $id, 1000, 32); /* We use this array to say what data goes where in the query. */ $data = array('id' => $id, 'mac' => $mac, 'time' => time(), 'type' => $type, 'data' => $data); /* And, insert. */ return $sth->execute($data); }