-
Guillaume ROUSSE authoredGuillaume ROUSSE authored
Tools.pm 3.96 KiB
package AccessCheck::Tools;
use Mojo::Base -strict;
use Crypt::Bcrypt;
use Crypt::OpenSSL::Random ();
use Encode;
use English qw(-no_match_vars);
use List::Util qw(shuffle);
use List::MoreUtils qw(pairwise);
use MIME::Base64;
use Template;
use Template::Constants qw(:chomp);
use AccessCheck::Template::Plugin::Quote;
use constant {
SIG_BCRYPT => '2y',
PASSWORD_BCRYPT_DEFAULT_COST => 10,
PASSWORD_BCRYPT_MAX_PASSWORD_LEN => 72,
};
sub encrypt {
my ($string, $key) = @_;
my @string_chars = split(//, $string);
my @key_chars = split(//, $key);
return encode_base64(otp(\@string_chars, \@key_chars));
}
sub decrypt {
my ($string, $key) = @_;
my @string_chars = split(//, decode_base64($string));
my @key_chars = split(//, $key);
return otp(\@string_chars, \@key_chars);
}
sub otp {
my ($string, $key) = @_;
my @chars =
pairwise { chr(ord($a) ^ ord($b)) }
@$string,
@$key;
return join('', @chars);
}
# shamelessly stolen from PHP::Functions::Password
# https://metacpan.org/dist/PHP-Functions-Password/source/lib/PHP/Functions/Password.pm
sub hash {
my ($string) = @_;
my $salt = Crypt::OpenSSL::Random::random_bytes(16);
my $cost = PASSWORD_BCRYPT_DEFAULT_COST;
# Treat passwords as strings of bytes
# "\x{100}" becomes "\xc4\x80"; preferred equivalent of Encode::is_utf8($string) && Encode::_utf8_off($password);
utf8::is_utf8($string) && utf8::encode($string);
# Everything beyond the max password length in bytes for bcrypt is silently ignored.
require bytes;
if (bytes::length($string) > PASSWORD_BCRYPT_MAX_PASSWORD_LEN) {
# $string is already bytes, so the bytes:: prefix is redundant here
$string = substr($string, 0, PASSWORD_BCRYPT_MAX_PASSWORD_LEN);
}