package Crypt::Passphrase::SaltedHash;

use v5.6;

use strict;
use warnings;

use parent 'Crypt::Passphrase::Validator';

use Digest 1.19  ();
use MIME::Base64 ();

our $VERSION = '0.02';

# ABSTRACT: Validate against Crypt::SaltedHash hashes with Crypt::Passphrase


my  %DIGESTS = (
    GOST    => [ 256, "GOST" ],
    HMACMD5 => [ 128, "HMAC-MD5" ],     # Note: Digest::HMAC does not work with Crypt::SaltedHash
    HMACSHA => [ 160, "HMAC-SHA-1" ],
    MD2     => [128],
    MD4     => [128],
    MD5     => [128],
    MD6     => [256],
    SHA     => [ 160, "SHA-1" ],
    SHA224  => [ 224, "SHA-224" ],
    SHA256  => [ 256, "SHA-256" ],
    SHA384  => [ 384, "SHA-384" ],
    SHA512  => [ 512, "SHA-512" ],
);


sub new {
    my $class = shift;
    return bless {}, $class;
}

sub accepts_hash {
    my ( $self,     $hash ) = @_;
    my ( $has_salt, $alg )  = $hash =~ /^\{(S)?(\w+)\}/ or return;
    return exists $DIGESTS{ uc $alg };
}

sub verify_password {
    my ( $self, $password, $hash ) = @_;

    my ( $has_salt, $alg ) = $hash =~ /^\{(S)?(\w+)\}/ or return;
    my $meta = $DIGESTS{ uc $alg } or return;

    my $name   = $meta->[1] || $alg;
    my $bytes  = $meta->[0] / 8;
    my $digest = Digest->new($name);
    my $bin    = MIME::Base64::decode_base64( substr( $hash, length($alg) + 2 + ( $has_salt ? 1 : 0 ) ) );
    my $salt   = $has_salt ? substr( $bin, $bytes ) : "";
    my $maybe  = $digest->add( $password . $salt )->digest;

    return $self->secure_compare( $maybe, substr( $bin, 0, $bytes ) );
}


1;

__END__

=pod

=encoding UTF-8

=for stopwords validator

=head1 NAME

Crypt::Passphrase::SaltedHash - Validate against Crypt::SaltedHash hashes with Crypt::Passphrase

=head1 VERSION

version 0.02

=head1 SYNOPSIS

    my $authenticator = Crypt::Passphrase->new(
        encoder    => 'Argon2',
        validators => [ 'SaltedHash' ],
    );

    ...

    if (!$authenticator->verify_password($password, $hash)) {
        die "Invalid password";
    }
    elsif ($authenticator->needs_rehash($hash)) {
        my $new_hash = $authenticator->hash_password($password);
        ...
    }

=head1 DESCRIPTION

This module implements a validator against hashes generated by L<Crypt::SaltedHash>.

=for Pod::Coverage new

=for Pod::Coverage accepts_hash

=for Pod::Coverage verify_password

=head1 SEE ALSO

L<Crypt::Passphrase>

L<Crypt::SaltedHash>

=head1 SOURCE

The development version is on github at L<https://github.com/robrwo/perl-Crypt-Passphrase-SaltedHash>
and may be cloned from L<https://github.com/robrwo/perl-Crypt-Passphrase-SaltedHash.git>

=head1 SUPPORT

Please report any bugs or feature requests on the bugtracker website
L<https://github.com/robrwo/perl-Crypt-Passphrase-SaltedHash/issues>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

Robert Rothenberg <perl@rhizomnic.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2026 by Robert Rothenberg <perl@rhizomnic.com>.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut
