revoke_all(); } /** * Getting the stored API key. * * @return string API key, or an empty string when the site has none. */ public static function key(): string { return Options::get_string( self::KEY_OPTION ); } /** * Getting the stored API secret. * * @return string API secret, or an empty string when the site has none. */ public static function secret(): string { return Options::get_string( self::SECRET_OPTION ); } /** * Checking whether the site has a credential pair. * * @return bool Whether both values are present. */ public static function exist(): bool { return '' !== self::key() && '' !== self::secret(); } /** * Verifying a presented key and secret. * * A site without credentials matches nothing (two empty strings would compare equal). * Both comparisons always run, so timing does not reveal which half was wrong. * * @param string $key Presented API key. * @param string $secret Presented API secret. * * @return bool Whether both match what is stored. */ public static function match( string $key, string $secret ): bool { if ( ! self::exist() ) { return false; } $key_matches = hash_equals( self::key(), $key ); $secret_matches = hash_equals( self::secret(), $secret ); return $key_matches && $secret_matches; } /** * Generating a random hexadecimal value. * * @param int $bytes Number of random bytes behind the value. * * @return string Lowercase hexadecimal, twice $bytes long. * * @throws \Exception When the system CSPRNG is unavailable. */ private static function random( int $bytes ): string { return bin2hex( random_bytes( $bytes ) ); } }