package App::Raps2::Password; use strict; use warnings; use autodie; use 5.010; use base 'Exporter'; use Carp 'confess'; use Crypt::CBC; use Crypt::Eksblowfish; use Crypt::Eksblowfish::Bcrypt qw(bcrypt_hash en_base64 de_base64); our @EXPORT_OK = (); our $VERSION = '0.1'; sub new { my ($obj, %conf) = @_; $conf{'cost'} //= 12; if (not (defined $conf{'salt'} and length($conf{'salt'}) == 16)) { confess('incorrect salt length'); } if (not (defined $conf{'passphrase'} and length $conf{'passphrase'})) { confess('no passphrase given'); } my $ref = \%conf; return bless($ref, $obj); } sub salt { my ($self, $salt) = @_; if (not (defined $salt and length($salt) == 16)) { confess('incorrect salt length'); } $self->{'salt'} = $salt; } sub encrypt { my ($self, $in) = @_; my $eksblowfish = Crypt::Eksblowfish->new( $self->{'cost'}, $self->{'salt'}, $self->{'passphrase'}, ); my $cbc = Crypt::CBC->new(-cipher => $eksblowfish); return $cbc->encrypt_hex($in); } sub decrypt { my ($self, $in) = @_; my $eksblowfish = Crypt::Eksblowfish->new( $self->{'cost'}, $self->{'salt'}, $self->{'passphrase'}, ); my $cbc = Crypt::CBC->new(-cipher => $eksblowfish); return $cbc->decrypt_hex($in); } sub crypt { my ($self) = @_; return en_base64( bcrypt_hash({ key_nul => 1, cost => $self->{'cost'}, salt => $self->{'salt'}, }, $self->{'passphrase'}, )); } sub verify { my ($self, $testhash) = @_; my $myhash = $self->crypt(); if ($testhash eq $myhash) { return 1; } confess('Passwords did not match'); } 1;