ONO::Lib::Data::Crypt

package ONO::Lib::Data::Crypt;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################

use strict;

use ONO::IO;
use ONO::Core::Kernel;

#: ONO_Lib_Data_Crypt allows to crypt data, which is mostly useful
#: when creating password hashes for example.

sub openssl_crt_decode {

#: Decode a CRT file, using openssl

my $file = $_[1];
$file =~ s~[^A-Za-z0-9\/\.\-\_]~~g;

my $openssl = &detect_openssl;
my $vpath = ONO::Core::Kernel->vpath();

return ONO::IO->exec("$openssl x509 -in $vpath/$file -text -noout");

}

sub detect_openssl {

#: Detect the location of the openssl binary

my $openssl;

foreach my $path (ONO::IO->binary_dirs()) {
if (-e "$path/openssl") {
$openssl = "$path/openssl";
}
}

return $openssl;

}

sub salt {

#: Generate a password salt, return it as a string.

return join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];

}

sub stronghash {

#: Generate a strong password hash.

my (
$self,
$password,
$salt,
$algo,
) = @_;

my $stronghash;

if ($password && $salt && $algo) {

eval "use Crypt::Password";

if (!$@) {

# $stronghash = password($password,$salt,$algo);

}

# dirty fix for systems that do not correctly support the Crypt::Password module with SHA-512:

if ($algo eq "sha512" && length $stronghash < 64) {
$stronghash = crypt("$password","\$6\$$salt\$");
}

}

return $stronghash;

}

sub strongtest_error {

#: Test if strong password encryption is working on the current system.

return &stronghash("","testpass","AA","sha512");

}

sub strongtest {

#: Test if strong password encryption is working on the current system.

my $testpass = "testpass";
my $testsalt = "AA";
my $testhash = "\$6\$AA\$ctPS1XAv.jVOmzBozr7JJtTV1r5oZR9Nh6YjsSQbg7tXweluVh3FBu8riEPQSLP.leDtft5aVmLdMnAidJnxa/";

if (&stronghash("",$testpass,$testsalt,"sha512") eq $testhash) {

return 1;

} else {

return 0;

}

}

sub pwdhash {

#: ONO_Lib_Data_Crypt->pwdhash($password) will return the hash for $password,
#: using random salt (great for generating a password hash)
#:
#: ONO_Lib_Data_Crypt->pwdhash($password,$salt) will return the hash for
#: $password, using $salt (can be used to see if a hash is valid, this is also
#: used by pwdchk - see below)
#:
#: ONO's max hash length is 32 chars.
#:
#: encryption types:
#:
#: - default standard perl crypt for 8 char passwords, generating
#: a 13 char hash (2 salt + 11 data)
#:
#: - MD5 hash starting by $1$... currently not supported
#:
#: - Blowfish hash starting by $2$... or $2a$... are not supported,
#: as Blowfish is for encryption and not for hashing
#:
#: - SHA hash starting by $5$... (256) or $6$... (512). The latter
#: one is default on systems with installed Crypt::Password
#: (recommended).
#:
#: length 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789
#:
#: sha256 $5$AA$I7/f0PluP/ZHZU.bJPe7mGd/8Wa9yVWcPXSD40ClWHB -> 50 required
#: sha512 $6$AA$ctPS1XAv.jVOmzBozr7JJtTV1r5oZR9Nh6YjsSQbg7tXweluVh3FBu8riEPQSLP.leDtft5aVmLdMnAidJnxa/ -> 92 required

my (
$self,
$password,
$salt,
$algo,
) = @_;

if (!$salt) {
$salt = &salt;
}

# default encryption using 'crypt':

my $hash = crypt($password,$salt);

if ($algo ne "crypt") {

# prefer SHA, if available...

my $stronghash = &stronghash("",$password,$salt,"sha512");

if ($stronghash) {

$hash = $stronghash;

}

}

return $hash;

}

sub pwdchk {

#: ONO_Lib_Data_Crypt->pwdchk($password,$password_hash) will check if
#: $password and $password_hash match.

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

my $valid = 0;

if ($hash =~ /^\$/) {

# stronger encrpytion using Crypt::Password

eval "use Crypt::Password";

if (!$@) {

if (&stronghash("",$password,substr($hash,3,2),"sha512") eq $hash) {

$valid = 1;

}

}

} else {

# default encryption using 'crypt':

if (&pwdhash("",$password,substr($hash,0,2),"crypt") eq $hash) {

$valid = 1;

}

}

return $valid;

}

sub encryptrc4file {

#: Encrypt a file using RC4.

my (
$self,
$password,
$input,
$output,
$switches,
) = @_;

# -d delete original file

my $vpath = ONO::Core::Kernel->vpath();

my $DATA;
foreach my $line (ONO::Core::Kernel->sys("list","$vpath/$input")) {
$DATA .= $line;
}

my @CRYPT = &encryptrc4("",$password,$DATA);

ONO::IO->dump($output,$CRYPT[1]);

if ($switches =~ /d/) {
ONO::IO->rm($input);
}

}

sub encryptrc4 {

#: Encrypt a password or a string using RC4.
#: Not recommended for passwords, better use SHA instead.

my (
$self,
$password,
$input,
) = @_;

my ($status,$result);

eval "use Crypt::RC4";
if (!$@) {

eval "use MIME::Base64";
if (!$@) {

$result = encode_base64(RC4($password,$input));

} else {
$status = "ABORT - missing module MIME::Base64";
}

} else {
$status = "ABORT - missing module Crypt::RC4";
}

return ($status,$result);

}

sub decryptrc4 {

#: Decrypt a password or a string using RC4.
#: Not recommended for passwords, better use SHA instead.

my (
$self,
$password,
$input,
) = @_;

my ($status,$result);

eval "use Crypt::RC4";
if (!$@) {

eval "use MIME::Base64";
if (!$@) {

$result = RC4($password,decode_base64($input));

} else {
$status = "ABORT - missing module MIME::Base64";
}

} else {
$status = "ABORT - missing module Crypt::RC4";
}

return ($status,$result);

}

###############################################################################
# end of script
###############################################################################

1;

__END__