public function compute($password, User $user) { $fields = ['name', 'email', 'familyName', 'firstName']; $parts = []; $index = 1; foreach ($fields as $field) { foreach (preg_split('~[@._\\s+]~', $user->{$field}) as $part) { $parts[$part] = $index++; $parts[Strings::toAscii($part)] = $index++; } } try { $result = $this->computer->passwordStrength($password, $parts); } catch (\Exception $e) { return NULL; } return number_format($result['entropy'], 2); }
public function testZxcvbn() { $zxcvbn = new Zxcvbn(); $result = $zxcvbn->passwordStrength(""); $this->assertEquals(0, $result['entropy'], "Entropy incorrect"); $this->assertEquals(0, $result['score'], "Score incorrect"); $result = $zxcvbn->passwordStrength("password"); $this->assertEquals(0, $result['entropy'], "Entropy incorrect"); $this->assertEquals(0, $result['score'], "Score incorrect"); $result = $zxcvbn->passwordStrength("jjjjj"); $this->assertSame('repeat', $result['match_sequence'][0]->pattern, "Pattern incorrect"); $password = '******'; $result = $zxcvbn->passwordStrength($password); $this->assertEquals(1, $result['score'], "Score incorrect"); $password = '******'; $result = $zxcvbn->passwordStrength($password); $this->assertEquals(4, $result['score'], "Score incorrect"); }
/** * @inheritdoc */ public function validateAttribute($model, $attribute) { if (!in_array($this->minScore, [1, 2, 3, 4])) { throw new InvalidConfigException('The "minScore" property must be in range 1-4.'); } $value = $model->{$attribute}; if ($this->allowEmpty && $this->isEmpty($value)) { return null; } $zxcvbn = new Zxcvbn(); $strength = $zxcvbn->passwordStrength($value); $score = ArrayHelper::getValue($strength, 'score'); if ($score < $this->minScore) { $message = $this->message !== null ? $this->message : 'A weak password. Use uppercase and lowercase letters, numbers and special characters.'; $this->addError($model, $attribute, $message); } }
/** * Changes a user's password * @param int $uid * @param string $currpass * @param string $newpass * @param string $repeatnewpass * @param string $captcha = NULL * @return array $return */ public function changePassword($uid, $currpass, $newpass, $repeatnewpass, $captcha = NULL) { $return['error'] = true; $block_status = $this->isBlocked(); if ($block_status == "verify") { if ($this->checkCaptcha($captcha) == false) { $return['message'] = $this->lang["user_verify_failed"]; return $return; } } if ($block_status == "block") { $return['message'] = $this->lang["user_blocked"]; return $return; } $validatePassword = $this->validatePassword($currpass); if ($validatePassword['error'] == 1) { $this->addAttempt(); $return['message'] = $validatePassword['message']; return $return; } $validatePassword = $this->validatePassword($newpass); if ($validatePassword['error'] == 1) { $return['message'] = $validatePassword['message']; return $return; } elseif ($newpass !== $repeatnewpass) { $return['message'] = $this->lang["newpassword_nomatch"]; return $return; } $zxcvbn = new Zxcvbn(); if ($zxcvbn->passwordStrength($newpass)['score'] < intval($this->config->password_min_score)) { $return['message'] = $this->lang['password_weak']; return $return; } $user = $this->getBaseUser($uid); if (!$user) { $this->addAttempt(); $return['message'] = $this->lang["system_error"] . " #13"; return $return; } if (!password_verify($currpass, $user['password'])) { $this->addAttempt(); $return['message'] = $this->lang["password_incorrect"]; return $return; } $newpass = $this->getHash($newpass); $query = $this->dbh->prepare("UPDATE {$this->config->table_users} SET password = ? WHERE id = ?"); $query->execute(array($newpass, $uid)); $return['error'] = false; $return['message'] = $this->lang["password_changed"]; return $return; }
/** * Execute the keygen command * * @param array $args - CLI arguments * @echo * @return null * @throws \Error */ public function fire(array $args = []) { if (count($this->config['suppliers']) === 1) { $supplier = \count($args) > 0 ? $args[0] : \array_keys($this->config['suppliers'])[0]; } else { $supplier = \count($args) > 0 ? $args[0] : $this->prompt("Please enter the name of the supplier: "); } if (!\array_key_exists($supplier, $this->config['suppliers'])) { echo 'Please authenticate before attempting to generate a key.', "\n"; echo 'Run this command: ', $this->c['yellow'], 'barge login', $this->c[''], "\n"; exit(255); } if (\count($this->config['suppliers'][$supplier]['signing_keys']) === 0) { // Your first key is a master key; always. $has_master = false; $key_type = 'master'; } else { $has_master = true; echo 'Please enter the key type you would like to generate (master, signing).', "\n"; do { $key_type = $this->prompt('Key type: '); switch ($key_type) { case 'm': case 'main': case 'master': case 'primary': $key_type = 'master'; break; case 's': case 'secondary': case 'sub': case 'subkey': case 'signing': $key_type = 'signing'; break; default: echo 'Acceptable key types: master, signing', "\n"; $key_type = null; } } while (empty($key_type)); } // Each key gets its own unique Argon2 salt echo 'Generating a unique salt...', "\n"; $salt = \random_bytes(\Sodium\CRYPTO_PWHASH_SALTBYTES); $store_in_cloud = null; // This is optional and not recommended, but some people prefer convenience. // We really hope this is adequate information to make an informed choice // based on personal risk tolerance: echo 'Do you wish to store the salt for generating your signing key in the Skyport?', "\n"; echo 'This is a security-convenience trade-off. The default is NO.', "\n\n"; echo $this->c['green'], 'Pro:', $this->c[''], ' It\'s there if you need it, and the salt alone is not enough for us to', "\n", ' reproduce your signing key.', "\n"; echo $this->c['red'], 'Con:', $this->c[''], ' If your salt is stored online, the security of your signing key depends', "\n", ' entirely on your password.', "\n\n"; // Iterate until we get a valid response while ($store_in_cloud === null) { $choice = $this->prompt('Store salt in the Skyport? (y/N): '); switch ($choice) { case 'YES': case 'yes': case 'Y': case 'y': $store_in_cloud = true; break; case 'N': case 'NO': case 'n': case 'no': case '': // Just pressing enter means "don't store it"! $store_in_cloud = false; break; default: echo "\n", $this->c['yellow'], 'Invalid response. Please enter yes or no.', $this->c[''], "\n"; } } $zxcvbn = new Zxcvbn(); $userInput = $this->getZxcvbnKeywords($supplier); // If we're storing in the cloud, our standards should be much higher. $min_score = $store_in_cloud ? 3 : 2; do { // Next, let's get a password. echo 'Please enter a strong passphrase to use for your signing key.', "\n"; $password = $this->silentPrompt("Passphrase:"); $password2 = $this->silentPrompt("Confirm passphrase:"); if (!\hash_equals($password, $password2)) { unset($password); echo $this->c['red'], 'Passwords did not match!', $this->c[''], "\n"; continue; } // Use zxcvbn to assess password strength $strength = $zxcvbn->passwordStrength($password, $userInput); if ($strength['score'] < $min_score) { echo $this->c['yellow'], 'Sorry, that password is not strong enough. Try making ', 'your password longer and use a wider variety of characters.', $this->c[''], "\n"; $password = false; } } while (empty($password)); echo 'Generating signing key...'; if ($key_type === 'master') { // Master keys are treated as sensitive. $sign_level = KeyFactory::SENSITIVE; } else { // Signing keys (day-to-day) are still moderately sensitive. // We're using a KDF locally so we don't have DDoS concerns // (which usually calls for INTERACTIVE). $sign_level = KeyFactory::MODERATE; } $keyPair = KeyFactory::deriveSignatureKeyPair($password, $salt, false, $sign_level); $sign_public = $keyPair->getPublicKey(); echo 'DONE!', "\n"; // Wipe the password from memory \Sodium\memzero($password); // Store this in the configuration $new_key = ['date_generated' => \date('Y-m-d\\TH:i:s'), 'store_in_cloud' => $store_in_cloud, 'salt' => \Sodium\bin2hex($salt), 'public_key' => \Sodium\bin2hex($sign_public->getRawKeyMaterial()), 'type' => $key_type]; // This is the message we are signing. $message = \json_encode(['action' => 'CREATE', 'date_generated' => $new_key['date_generated'], 'public_key' => $new_key['public_key'], 'supplier' => $supplier, 'type' => $new_key['type']]); if ($has_master) { list($masterSig, $masterPubKey) = $this->signNewKeyWithMasterKey($supplier, $message); } else { // This is our first key, so we don't need it. $masterSig = ''; $masterPubKey = ''; } // Save the configuration $this->config['suppliers'][$supplier]['signing_keys'][] = $new_key; // Send the public kay (and, maybe, the salt) to the Skyport. $response = $this->sendToSkyport($supplier, $new_key, $message, $masterSig, $masterPubKey); if (!empty($response['status'])) { if ($response['status'] === 'ERROR') { echo "Error message returned!\n"; throw new \Error($response['message']); } $pk = Base64UrlSafe::encode(\Sodium\hex2bin($new_key['public_key'])); if ($new_key['type'] === 'master') { echo 'New master key: ', $this->c['red'], $pk, $this->c[''], "\n"; } else { echo 'New signing key: ', $this->c['yellow'], $pk, $this->c[''], "\n"; } } }
/** * 检测输入密码的强壮程度,并返回检测结果,结果越大,密码就越健壮 * * @param $password * @return int */ public static function strength($password) { $zxcvbn = new Zxcvbn(); $result = $zxcvbn->passwordStrength($password); return (int) Arr::get($result, 'score'); }
/** * Is this password too weak? * * @param array $post * @return bool */ public function isPasswordWeak(array $post) : bool { $zxcvbn = new Zxcvbn(); $pw = $post['passphrase']; $userdata = \Airship\keySlice($post, ['username', 'display_name', 'realname', 'email']); $strength = $zxcvbn->passwordStrength($pw, \array_values($userdata)); return $strength['score'] < 3; }