Bivio::Type::Password
# Copyright (c) 1999-2023 bivio Software, Inc. All rights reserved.
package Bivio::Type::Password;
use strict;
use Bivio::Base 'Type.Line';
my($_HMACSHA512) = b_use('Type.PasswordHashHMACSHA512');
my($_HASH_TYPES) = [
b_use('Type.PasswordHashCrypt'),
b_use('Type.PasswordHashHMACSHA1'),
$_HMACSHA512,
];
my($_SUPPORTED_HASH_TYPE) = {map(($_ => 1), @$_HASH_TYPES)};
# C<Bivio::Type::Password> indicates the input is a password entry.
# It should be handled with care, e.g. never displayed to user.
sub CURRENT_HASH_TYPE {
return $_HMACSHA512,
}
sub INVALID {
# Returns invalid password (save literally!).
return 'xx';
}
sub OTP_VALUE {
return 'otp';
}
sub compare {
my($proto, $hashed, $clear_text) = @_;
return -1
unless defined($hashed);
# Incoming clear text is never allowed to match stored OTP value and instead must be verified
# via the OTP module.
return -1
if $hashed eq $proto->OTP_VALUE;
return 1
unless defined($clear_text);
return _to_hash_type_instance_or_die($hashed)->compare($clear_text);
}
sub encrypt {
my($proto, $clear_text, $type) = @_;
$type ||= $proto->CURRENT_HASH_TYPE;
b_die("unsupported hash type=$type")
unless $_SUPPORTED_HASH_TYPE->{$type};
return $type->to_literal($clear_text);
}
sub get_min_width {
# Allow existing deprecated 6-7 character passwords.
return 6;
}
sub get_width {
return 255;
}
sub is_otp {
my($proto, $value) = @_;
return $proto->OTP_VALUE eq ($value || '') ? 1 : 0;
}
sub is_password {
return 1;
}
sub is_secure_data {
return 1;
}
sub is_valid {
my($proto, $hashed, $expected_hash_type) = @_;
return 0
unless $hashed;
return 1
if $hashed eq $proto->OTP_VALUE;
my($hti) = _to_hash_type_instance($hashed);
if ($expected_hash_type) {
return $proto->is_blesser_of($hti, $expected_hash_type);
}
return $hti ? 1 : 0;
}
sub needs_upgrade {
my($proto, $hashed) = @_;
return 0
if $hashed eq $proto->OTP_VALUE;
return $proto->CURRENT_HASH_TYPE->is_blesser_of(_to_hash_type_instance_or_die($hashed)) ? 0 : 1;
}
sub _to_hash_type_instance {
my($hashed) = @_;
foreach my $type (@$_HASH_TYPES) {
my($hti, $error) = $type->from_literal($hashed);
return $hti
unless $error;
}
return;
}
sub _to_hash_type_instance_or_die {
my($hashed) = @_;
return _to_hash_type_instance($hashed)
|| b_die('invalid password hash=', $hashed);
}
1;