public static function encrypt(
        string $plaintext,
        string $key,
        string $ad = '',
        string $format = null,
        bool $password = false
    ): string {
        if (\mb_strlen($key, '8bit') !== self::KEY_BYTE_SIZE) {
            throw new CryptoException('Bad key length.');
        }

        $salt = \random_bytes(self::SALT_BYTE_SIZE);

        [$authKey, $encryptKey] = self::deriveKeys($key, $salt, $password);

        $iv = \random_bytes(self::BLOCK_BYTE_SIZE);

        $ad = self::getAd($ad);

        $tag = '';
        if (self::$sodium) {
            $cipherText = \sodium_crypto_aead_aes256gcm_encrypt($plaintext, $ad, $iv, $encryptKey);
        } else {
            $cipherText = \openssl_encrypt(
                $plaintext,
                self::CIPHER_METHOD,
                $encryptKey,
                \OPENSSL_RAW_DATA,
                $iv, $tag, $ad
            );
        }

        if ($cipherText === false) {
            throw new CryptoException('Encrypt failed.');
        }

        $cipherText = self::CURRENT_VERSION . $salt . $iv . $cipherText . $tag;
        $auth = \hash_hmac(self::HASH_TYPE, $cipherText, $authKey, true);
        $cipherText .= $auth;

        return self::fromRaw($cipherText, $format);
    }