/** * Register a failed login attempt * * @param string $username * @param string $ip * @param int $numFailures * @param HiddenString|null $password * @return bool */ public function registerLoginFailure(string $username, string $ip, int $numFailures = 0, HiddenString $password = null) : bool { $logAfter = $this->config['log-after'] ?? null; if (!\is_null($logAfter)) { $logAfter = (int) $logAfter; } $publicKey = (string) ($this->config['log-public-key'] ?? ''); $this->db->beginTransaction(); $inserts = ['action' => self::ACTION_LOGIN, 'occurred' => (new \DateTime())->format(\AIRSHIP_DATE_FORMAT), 'username' => $username, 'ipaddress' => $ip, 'subnet' => $this->getSubnet($ip)]; if (\is_int($logAfter) && !empty($publicKey)) { if ($numFailures >= $logAfter) { // Encrypt the password guess with the admin's public key $inserts['sealed_password'] = Asymmetric::seal($password->getString(), $this->getLogPublicKey($publicKey)); } } $this->db->insert('airship_failed_logins', $inserts); return $this->db->commit(); }
/** * @covers Asymmetric::seal() * @covers Asymmetric::unseal() */ public function testSealFail() { if (\Sodium\library_version_major() < 7 || \Sodium\library_version_major() == 7 && \Sodium\library_version_minor() < 5) { $this->markTestSkipped("Your version of libsodium is too old"); } $alice = KeyFactory::generateEncryptionKeyPair(); $message = new HiddenString('This is for your eyes only'); $sealed = Asymmetric::seal($message, $alice->getPublicKey(), true); // Let's flip one bit, randomly: $r = \Sodium\randombytes_uniform(\mb_strlen($sealed, '8bit')); $amt = 1 << \Sodium\randombytes_uniform(8); $sealed[$r] = \chr(\ord($sealed[$r]) ^ $amt); // This should throw an exception try { $opened = Asymmetric::unseal($sealed, $alice->getSecretKey()); $this->assertSame($opened->getString(), $message); $this->fail('This should have thrown an InvalidMessage exception!'); } catch (CryptoException\InvalidKey $e) { $this->assertTrue($e instanceof CryptoException\InvalidKey); } catch (CryptoException\InvalidMessage $e) { $this->assertTrue($e instanceof CryptoException\InvalidMessage); } }