#!/usr/bin/perl
#
# This small script generates a hash of the given password
# or compares it against a given password hash
#
# SSHA1 'salt' = four random bytes
#
# Usage: crp.pl [-s salt | -c cpass] [-q] [-m mode] pass
# Version 0.0; Wed Jan 21 15:19:24 CET 2026
# Requires
#   perl-Digest-SHA1
# 	perl-Crypt-SmbHash
# 	perl-Crypt-PasswdMD5
# 	perl-Module-Runtime
# 	perl-namespace-autoclean
# 	perl-Crypt-PBKDF2 ( dnf config-manager --set-enabled crb )
#
#
use warnings;

use Digest::SHA1;
use Crypt::PBKDF2;
use Crypt::SmbHash;
use Crypt::PasswdMD5;
use MIME::Base64;
use Getopt::Std;


my $DEF_SALT="";
my $DEF_MODE="ssha1";

our $opt_s="";
our $opt_c="";
our $opt_q="";
our $opt_x="";
our $opt_m="";

our $crypt;
our $bincrypt;

getopts('c:s:m:qx');
	
my $salt = $opt_s || $DEF_SALT;
my $mode = $opt_m || $DEF_MODE;

sub usage
{
	print <<EOF;
usage: $0 [options] string
Options are:
	-c hash     compare to this hash
	-s salt     use this salt instead of random
	-m mode     use this algorithm
	-q          quiet
	-x          not used
modes are:
EOF
	system ("egrep \"^sub crp_\" $0 | sed -e 's/.*crp_//'");

	exit 0;
}

if ( $opt_c ne "" )
{
	$crypt=$opt_c;
	my @crypta = split(//, $crypt);

	if ( ( $crypta[0] eq '$' ) && ( $crypta[2] eq '$' ) ) {
		if ( $crypta[1] eq '1' ) {
			# MD5
			$salt = $crypt;
			$mode = "md5";
		} elsif ( $crypta[1] eq '5' ) {
			# SHA-256
			$salt = $crypt;
			$mode = "sha256";
		} elsif ( $crypta[1] eq '6' ) {
			# SHA-512
			$salt = $crypt;
			$mode = "sha512";
		}
	} else {
		if ( $crypt =~ m/^[0-9A-F:]*$/ ) {
			# NTLM
			$salt = "NA";
			$mode = "ntlm";
		} elsif ( length($crypt) < 16 ) {
			# old crypt
			$salt = $crypt;
			$mode = "crypt";
		} elsif ( $crypt =~ m/^[0-9][0-9]*\$/ ) {
			# PBKDF2
			my ($iter, $salt_b64, $hash_b64) = split(/\$/, $crypt, 3);
			$salt=decode_base64($salt_b64);
			$mode="pbkdf2_sha512";
		}
		else {
			# default to SSHA1
			$bincrypt=decode_base64($crypt);
			$salt=substr($bincrypt, -4);
			$mode = "ssha1";
		}
	}
}

our $pw = $ARGV[0];

if ( $pw eq "" ) {
	usage;
}

our $refc = "crp_".$mode;
our $refs = "salt_".$mode;

sub salt_crypt
{
	my @set = ('0' ..'9', 'A' .. 'Z', 'a' .. 'z', '.', '/');
	my $salt = join '' => map $set[rand @set], 1 .. 2;
	return($salt);
}

sub salt_ssha1
{
	my @set = ('0' ..'9', 'A' .. 'F');
	my $salt = join '' => map $set[rand @set], 1 .. 4;
	return($salt);
}

sub salt_md5
{
	my @set = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
	my $prefix = '$1$';
	my $salt = join '' => map $set[rand @set], 1 .. 8; 
	return( $prefix . $salt);
}

sub salt_sha256
{
	my @set = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
	my $type = '256';
	my $prefix = '$5$';
	my $salt = join '' => map $set[rand @set], 1 .. 16; 
	return( $prefix . $salt);
}

sub salt_sha512
{
	my @set = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
	my $type = '512';
	my $prefix = '$6$';
	my $salt = join '' => map $set[rand @set], 1 .. 16; 
	return( $prefix . $salt);
}

sub salt_pbkdf2_sha512 {
    my $salt;
    open(my $r, "/dev/urandom") or die "no urandom";
    read($r, $salt, 16);
    close($r);
	return($salt);
}

sub salt_ntlm
{
	return("");
}

sub crp_crypt
{
	my ($pw, $salt) = @_;
	my $hash = crypt($pw, $salt);
	return("{crypt}", $hash);
}

sub crp_md5
{
	my ($pw, $salt) = @_;
	my $hash = unix_md5_crypt($pw, $salt);
	return("{MD5}", $hash);
}

sub crp_ssha1
{
	my ($pw, $salt) = @_;
	my $ctx = Digest::SHA1->new;
	$ctx->add($pw);
	$ctx->add($salt);
	my $hash = encode_base64($ctx->digest . $salt ,'');
	return("{SSHA}", $hash);
}

sub crp_sha256
{
	my ($pw, $salt) = @_;
	my $hash = crypt($pw, $salt);
	return("{SHA256}", $hash);
}

sub crp_sha512
{
	my ($pw, $salt) = @_;
	my $hash = crypt($pw, $salt);
	return("{SHA512}", $hash);
}

sub crp_pbkdf2_sha512
{
	my ($pw, $salt) = @_;

	my $iterations=100000;
	my $pbkdf2 = Crypt::PBKDF2->new(
		hash_class => "HMACSHA2",
		hash_args => {
			sha_size => 512,
		},
		iterations => $iterations,
		salt_len => 16,
	);

    my $dk = $pbkdf2->PBKDF2($salt, $pw);
	my $b64salt = encode_base64($salt, "");
	my $b64dk   = encode_base64($dk, "");

    return("{PBKDF2-SHA512}" , "$iterations\$$b64salt\$$b64dk" );
}

sub crp_ntlm
{
	my ($pw, $salt) = @_;
	my ($lm, $nt);
	ntlmgen $pw, $lm, $nt;
	return("", $lm . ":" . $nt);
}

# https://zetcode.com/perl/crypt/
#
if ( $salt eq "" )
{
	$salt = &{$refs};
}

if ( $pw ne "" )
{
	# Compare against $opt_c ?
	my($str, $hash) = &{$refc} ($pw, $salt);
	if ( ! $opt_q ) {
		print $str . $hash . "\n";
	}
	if ( $opt_c ne "" ) {
		exit ( $opt_c ne $hash );
	}
	exit (0);
} else {
	exit (1);
}


