Authen-Passphrase-Scrypt
view release on metacpan or search on metacpan
lib/Authen/Passphrase/Scrypt.pm view on Meta::CPAN
package Authen::Passphrase::Scrypt;
use 5.014000;
use strict;
use warnings;
use Carp;
use parent qw/Exporter Authen::Passphrase Class::Accessor::Fast/;
our @EXPORT = qw/crypto_scrypt/;
our @EXPORT_OK = @EXPORT;
our $VERSION = '0.001001';
use Data::Entropy::Algorithms qw/rand_bits/;
use Digest::SHA qw/sha256 hmac_sha256/;
use MIME::Base64;
require XSLoader;
XSLoader::load('Authen::Passphrase::Scrypt', $VERSION);
__PACKAGE__->mk_accessors(qw/data logN r p salt hmac passphrase/);
sub compute_hash {
my ($self, $passphrase) = @_;
crypto_scrypt ($passphrase, $self->salt, (1 << $self->logN), $self->r, $self->p, 64);
}
sub truncated_sha256 {
my $sha = sha256 shift;
substr $sha, 0, 16
}
sub truncate_hash {
substr shift, 32
}
sub new {
my ($class, @args) = @_;
my $self = $class->SUPER::new(@args);
$self->logN(14) unless defined $self->logN;
$self->r(16) unless defined $self->r;
$self->p(1) unless defined $self->p;
croak "passphrase not set" unless defined $self->passphrase;
$self->salt(rand_bits 256) unless $self->salt;
my $data = "scrypt\x00" . pack 'CNNa32',
$self->logN, $self->r, $self->p, $self->salt;
$data .= truncated_sha256 $data;
$self->data($data);
$self->hmac(hmac_sha256 $self->data, truncate_hash $self->compute_hash($self->passphrase));
$self
}
sub from_rfc2307 {
my ($class, $rfc2307) = @_;
croak "Invalid Scrypt RFC2307" unless $rfc2307 =~ m,^{SCRYPT}([A-Za-z0-9+/]{128})$,;
my $data = decode_base64 $1;
my ($scrypt, $logN, $r, $p, $salt, $cksum, $hmac) =
unpack 'Z7CNNa32a16a32', $data;
croak 'Invalid Scrypt hash: should start with "scrypt"' unless $scrypt eq 'scrypt';
croak 'Invalid Scrypt hash: bad checksum', unless $cksum eq truncated_sha256 (substr $data, 0, 48);
$class->SUPER::new({data => (substr $data, 0, 64), logN => $logN, r => $r, p => $p, salt => $salt, hmac => $hmac});
}
sub match {
my ($self, $passphrase) = @_;
my $correct_hmac = hmac_sha256 $self->data, truncate_hash $self->compute_hash($passphrase);
$self->hmac eq $correct_hmac
}
sub as_rfc2307 {
my ($self) = @_;
'{SCRYPT}' . encode_base64 ($self->data . $self->hmac, '')
}
sub from_crypt {
croak __PACKAGE__ ." does not support crypt strings, use from_rfc2307 instead";
}
sub as_crypt {
croak __PACKAGE__ ." does not support crypt strings, use as_rfc2307 instead";
}
1;
__END__
=encoding utf-8
=head1 NAME
Authen::Passphrase::Scrypt - passphrases using Tarsnap's scrypt algorithm
=head1 SYNOPSIS
use Authen::Passphrase::Scrypt;
# Hash a password
my $sc = Authen::Passphrase::Scrypt->new({
passphrase => 'correcthorsebatterystaple'
});
my $hash = $sc->as_rfc2307;
say "The given password hashes to $hash";
# Verify a password
$sc = Authen::Passphrase::Scrypt->from_rfc2307($hash);
say 'The password was "correcthorsebatterystaple"'
if $sc->match('correcthorsebatterystaple');
say 'The password was "xkcd"' if $sc->match('xkcd');
# Advanced hashing
my $sc = Authen::Passphrase::Scrypt->new({
passphrase => 'xkcd',
logN => 14, # General work factor
r => 16, # Memory work factor
p => 1, # CPU (parallellism) work factor
salt => 'SodiumChloride && sODIUMcHLORIDE', # Must be 32 bytes
});
say 'The given password now hashes to ', $sc->as_rfc2307;
=head1 DESCRIPTION
B<This is experimental code, DO NOT USE in security-critical software>.
( run in 1.948 second using v1.01-cache-2.11-cpan-c221a9de4ec )