Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package perl-Crypt-SaltedHash for 
openSUSE:Factory checked in at 2026-06-09 14:25:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Crypt-SaltedHash (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Crypt-SaltedHash.new.2375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Crypt-SaltedHash"

Tue Jun  9 14:25:54 2026 rev:12 rq:1358064 version:0.120.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/perl-Crypt-SaltedHash/perl-Crypt-SaltedHash.changes  
    2026-05-21 18:33:38.422105271 +0200
+++ 
/work/SRC/openSUSE:Factory/.perl-Crypt-SaltedHash.new.2375/perl-Crypt-SaltedHash.changes
    2026-06-09 14:28:01.647732368 +0200
@@ -1,0 +2,10 @@
+Sat May 23 07:44:13 UTC 2026 - Tina Müller <[email protected]>
+
+- updated to 0.120.0 (0.12)
+   see /usr/share/doc/packages/perl-Crypt-SaltedHash/Changes
+
+  0.12      2026-05-23 07:29:34+01:00 Europe/London
+      - Add reference to Crypt::Passphrase::SaltedHash
+      - Removed DOS line-endings
+
+-------------------------------------------------------------------

Old:
----
  Crypt-SaltedHash-0.11.tar.gz

New:
----
  Crypt-SaltedHash-0.12.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ perl-Crypt-SaltedHash.spec ++++++
--- /var/tmp/diff_new_pack.Fk3qwo/_old  2026-06-09 14:28:02.555770056 +0200
+++ /var/tmp/diff_new_pack.Fk3qwo/_new  2026-06-09 14:28:02.555770056 +0200
@@ -18,10 +18,10 @@
 
 %define cpan_name Crypt-SaltedHash
 Name:           perl-Crypt-SaltedHash
-Version:        0.110.0
+Version:        0.120.0
 Release:        0
-# 0.11 -> normalize -> 0.110.0
-%define cpan_version 0.11
+# 0.12 -> normalize -> 0.120.0
+%define cpan_version 0.12
 License:        Artistic-1.0 OR GPL-1.0-or-later
 Summary:        Perl interface to functions that assist in working with salted 
hashes
 URL:            https://metacpan.org/release/%{cpan_name}

++++++ Crypt-SaltedHash-0.11.tar.gz -> Crypt-SaltedHash-0.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/Changes 
new/Crypt-SaltedHash-0.12/Changes
--- old/Crypt-SaltedHash-0.11/Changes   2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/Changes   2026-05-23 08:29:35.000000000 +0200
@@ -1,5 +1,9 @@
 Revision history for Perl module Crypt::SaltedHash
 
+0.12      2026-05-23 07:29:34+01:00 Europe/London
+    - Add reference to Crypt::Passphrase::SaltedHash
+    - Removed DOS line-endings
+
 0.11      2026-05-20 14:05:07+01:00 Europe/London
     - Fixed metadata
     - Moved author tests into xt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/MANIFEST 
new/Crypt-SaltedHash-0.12/MANIFEST
--- old/Crypt-SaltedHash-0.11/MANIFEST  2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/MANIFEST  2026-05-23 08:29:35.000000000 +0200
@@ -16,6 +16,7 @@
 t/bug-localize-regex-vars.t
 xt/author/clean-namespaces.t
 xt/author/eof.t
+xt/author/eol.t
 xt/author/minimum-version.t
 xt/author/no-tabs.t
 xt/author/pod-syntax.t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/META.json 
new/Crypt-SaltedHash-0.12/META.json
--- old/Crypt-SaltedHash-0.11/META.json 2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/META.json 2026-05-23 08:29:35.000000000 +0200
@@ -26,6 +26,7 @@
             "Test::CleanNamespaces" : "0.15",
             "Test::DistManifest" : "0",
             "Test::EOF" : "0",
+            "Test::EOL" : "0",
             "Test::Fixme" : "0",
             "Test::MinimumVersion" : "0",
             "Test::More" : "0.94",
@@ -60,7 +61,7 @@
    "provides" : {
       "Crypt::SaltedHash" : {
          "file" : "lib/Crypt/SaltedHash.pm",
-         "version" : "0.11"
+         "version" : "0.12"
       }
    },
    "release_status" : "stable",
@@ -75,7 +76,7 @@
       },
       "x_authority" : "cpan:RRWO"
    },
-   "version" : "0.11",
+   "version" : "0.12",
    "x_contributors" : [
       "Chris Weyl <[email protected]>",
       "David Steinbrunner <[email protected]>",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/META.yml 
new/Crypt-SaltedHash-0.12/META.yml
--- old/Crypt-SaltedHash-0.11/META.yml  2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/META.yml  2026-05-23 08:29:35.000000000 +0200
@@ -20,7 +20,7 @@
 provides:
   Crypt::SaltedHash:
     file: lib/Crypt/SaltedHash.pm
-    version: '0.11'
+    version: '0.12'
 requires:
   Crypt::SysRandom: '0'
   Digest: '0'
@@ -32,7 +32,7 @@
   Authority: cpan:RRWO
   bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Crypt-SaltedHash
   repository: git://github.com/robrwo/perl-Crypt-SaltedHash.git
-version: '0.11'
+version: '0.12'
 x_contributors:
   - 'Chris Weyl <[email protected]>'
   - 'David Steinbrunner <[email protected]>'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/Makefile.PL 
new/Crypt-SaltedHash-0.12/Makefile.PL
--- old/Crypt-SaltedHash-0.11/Makefile.PL       2026-05-20 15:05:08.000000000 
+0200
+++ new/Crypt-SaltedHash-0.12/Makefile.PL       2026-05-23 08:29:35.000000000 
+0200
@@ -30,7 +30,7 @@
     "Test::More" => 0,
     "warnings" => 0
   },
-  "VERSION" => "0.11",
+  "VERSION" => "0.12",
   "test" => {
     "TESTS" => "t/*.t"
   }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/README 
new/Crypt-SaltedHash-0.12/README
--- old/Crypt-SaltedHash-0.11/README    2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/README    2026-05-23 08:29:35.000000000 +0200
@@ -21,6 +21,9 @@
     modules that support more secure algorithms and hashing options, and
     are extensible, such as Crypt::Passphrase.
 
+    You can use the Crypt::Passphrase::SaltedHash validator to migrate to
+    more secure algorithms.
+
 DESCRIPTION
 
     The Crypt::SaltedHash module provides an object oriented interface to
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/cpanfile 
new/Crypt-SaltedHash-0.12/cpanfile
--- old/Crypt-SaltedHash-0.11/cpanfile  2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/cpanfile  2026-05-23 08:29:35.000000000 +0200
@@ -30,6 +30,7 @@
   requires "Test::CleanNamespaces" => "0.15";
   requires "Test::DistManifest" => "0";
   requires "Test::EOF" => "0";
+  requires "Test::EOL" => "0";
   requires "Test::Fixme" => "0";
   requires "Test::MinimumVersion" => "0";
   requires "Test::More" => "0.94";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/dist.ini 
new/Crypt-SaltedHash-0.12/dist.ini
--- old/Crypt-SaltedHash-0.11/dist.ini  2026-05-20 15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/dist.ini  2026-05-23 08:29:35.000000000 +0200
@@ -94,8 +94,8 @@
 ; authordep Test::CleanNamespaces
 [Test::EOF]
 ; authordep Test::EOF
-; [Test::EOL]
-; :version = 0.14
+[Test::EOL]
+:version = 0.14
 [Test::Fixme]
 [Test::MinimumVersion]
 [Test::NoTabs]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/lib/Crypt/SaltedHash.pm 
new/Crypt-SaltedHash-0.12/lib/Crypt/SaltedHash.pm
--- old/Crypt-SaltedHash-0.11/lib/Crypt/SaltedHash.pm   2026-05-20 
15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/lib/Crypt/SaltedHash.pm   2026-05-23 
08:29:35.000000000 +0200
@@ -1,444 +1,446 @@
-package Crypt::SaltedHash;
-
-use v5.6.0;
-use strict;
-
-use Crypt::SysRandom ();
-use Digest       ();
-use MIME::Base64 ();
-use POSIX ();
-
-our $VERSION = '0.11';
-
-=encoding latin1
-
-=head1 NAME
-
-Crypt::SaltedHash - Perl interface to functions that assist in working
-with salted hashes.
-
-=head1 SYNOPSIS
-
-       use Crypt::SaltedHash;
-
-       my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-1');
-       $csh->add('secret');
-
-       my $salted = $csh->generate;
-       my $valid = Crypt::SaltedHash->validate($salted, 'secret');
-
-
-=head1 STATUS
-
-This module is deprecated.
-
-This module has not had significant updates since 2006.
-There are newer modules that support more secure algorithms and hashing 
options,
-and are extensible, such as L<Crypt::Passphrase>.
-
-=head1 DESCRIPTION
-
-The C<Crypt::SaltedHash> module provides an object oriented interface to
-create salted (or seeded) hashes of clear text data. The original
-formalization of this concept comes from RFC-3112 and is extended by the use
-of different digital algorithms.
-
-=head1 ABSTRACT
-
-=head2 Setting the data
-
-The process starts with 2 elements of data:
-
-=over
-
-=item *
-
-a clear text string (this could represent a password for instance).
-
-=item *
-
-the salt, a random seed of data. This is the value used to augment a hash in 
order to
-ensure that 2 hashes of identical data yield different output.
-
-=back
-
-For the purposes of this abstract we will analyze the steps within code that 
perform the necessary actions
-to achieve the endresult hashes. Cryptographers call this hash a digest. We 
will not however go into an explanation
-of a one-way encryption scheme. Readers of this abstract are encouraged to get 
information on that subject by
-their own.
-
-Theoretically, an implementation of a one-way function as an algorithm takes 
input, and provides output, that are both
-in binary form; realistically though digests are typically encoded and stored 
in a database or in a flat text or XML file.
-Take slappasswd5 for instance, it performs the exact functionality described 
above. We will use it as a black box compiled
-piece of code for our analysis.
-
-In pseudocode we generate a salted hash as follows:
-
-    Get the source string and salt as separate binary objects
-    Concatenate the 2 binary values
-    Hash the concatenation into SaltedPasswordHash
-    Base64Encode(concat(SaltedPasswordHash, Salt))
-
-We take a clear text string and hash this into a binary object representing 
the hashed value of the clear text string plus the random salt.
-Then we have the Salt value, which are typically 4 bytes of purely random 
binary data represented as hexadecimal notation (Base16 as 8 bytes).
-
-Using SHA-1 as the hashing algorithm, SaltedPasswordHash is of length 20 
(bytes) in raw binary form
-(40 bytes if we look at it in hex). Salt is then 4 bytes in raw binary form. 
The SHA-1 algorithm generates
-a 160 bit hash string. Consider that 8 bits = 1 byte. So 160 bits = 20 bytes, 
which is exactly what the
-algorithm gives us.
-
-The Base64 encoding of the binary result looks like:
-
-    {SSHA}B0O0XSYdsk7g9K229ZEr73Lid7HBD9DX
-
-Take note here that the final output is a 32-byte string of data. The Base64 
encoding process uses bit shifting, masking, and padding as per RFC-3548.
-
-A couple of examples of salted hashes using on the same exact clear-text 
string:
-
-    slappasswd -s testing123
-    {SSHA}72uhy5xc1AWOLwmNcXALHBSzp8xt4giL
-
-    slappasswd -s testing123
-    {SSHA}zmIAVaKMmTngrUi4UlS0dzYwVAbfBTl7
-
-    slappasswd -s testing123
-    {SSHA}Be3F12VVvBf9Sy6MSqpOgAdEj6JCZ+0f
-
-    slappasswd -s testing123
-    {SSHA}ncHs4XYmQKJqL+VuyNQzQjwRXfvu6noa
-
-4 runs of slappasswd against the same clear text string each yielded unique 
endresult hashes.
-The random salt is generated silently and never made visible.
-
-=head2 Extracting the data
-
-One of the keys to note is that the salt is dealt with twice in the process. 
It is used once for the actual application of randomness to the
-given clear text string, and then it is stored within the final output as 
purely Base64 encoded data. In order to perform an authentication
-query for instance, we must break apart the concatenation that was created for 
storage of the data. We accomplish this by splitting
-up the binary data we get after Base64 decoding the stored hash.
-
-In pseudocode we would perform the extraction and verification operations as 
such:
-
-    Strip the hash identifier from the Digest
-    Base64Decode(Digest, 20)
-    Split Digest into 2 byte arrays, one for bytes 0 � 20(pwhash), one for 
bytes 21 � 32 (salt)
-    Get the target string and salt as separate binary object
-    Concatenate the 2 binary values
-    SHA hash the concatenation into targetPasswordHash
-    Compare targetPasswordHash with pwhash
-    Return corresponding Boolean value
-
-Our job is to split the original digest up into 2 distinct byte arrays, one of 
the left 20 (0 - 20 including the null terminator) bytes and
-the other for the rest of the data. The left 0 � 20 bytes will represent the 
salted  binary value we will use for a byte-by-byte data
-match against the new clear text presented for verification. The string 
presented for verification will have to be salted as well. The rest
-of the bytes (21 � 32) represent the random salt which when decoded will show 
the exact hex characters that make up the once randomly
-generated seed.
-
-We are now ready to verify some data. Let's start with the 4 hashes presented 
earlier. We will run them through our code to extract the
-random salt and then using that verify the clear text string hashed by 
slappasswd. First, let's do a verification test with an erroneous
-password; this should fail the matching test:
-
-    {SSHA}72uhy5xc1AWOLwmNcXALHBSzp8xt4giL Test123
-    Hash extracted (in hex): ef6ba1cb9c5cd4058e2f098d71700b1c14b3a7cc
-    Salt extracted (in hex): 6de2088b
-    Hash length is: 20 Salt length is: 4
-    Hash presented in hex: 256bc48def0ce04b0af90dfd2808c42588bf9542
-    Hashes DON'T match: Test123
-
-The match failure test was successful as expected. Now let's use known valid 
data through the same exact code:
-
-    {SSHA}72uhy5xc1AWOLwmNcXALHBSzp8xt4giL testing123
-    Hash extracted (in hex): ef6ba1cb9c5cd4058e2f098d71700b1c14b3a7cc
-    Salt extracted (in hex): 6de2088b
-    Hash length is: 20 Salt length is: 4
-    Hash presented in hex: ef6ba1cb9c5cd4058e2f098d71700b1c14b3a7cc
-    Hashes match: testing123
-
-The process used for salted passwords should now be clear. We see that salting 
hashed data does indeed add another layer of security to the
-clear text one-way hashing process. But we also see that salted hashes should 
also be protected just as if the data was in clear text form.
-Now that we have seen salted hashes actually work you should also realize that 
in code it is possible to extract salt values and use them
-for various purposes. Obviously the usage can be on either side of the colored 
hat line, but the data is there.
-
-=head1 METHODS
-
-=over 4
-
-=item B<new( [%options] )>
-
-Returns a new Crypt::SaltedHash object.
-Possible keys for I<%options> are:
-
-=over
-
-=item *
-
-I<algorithm>: It's also possible to use common string representations of the
-algorithm (e.g. "sha256", "SHA-384"). If the argument is missing, SHA-1 will
-be used by default.
-
-=item *
-
-I<salt>: You can specify your on salt. You can either specify it as a sequence
-of characters or as a hex encoded string of the form "HEX{...}". If the 
argument is missing,
-a random seed is provided for you (recommended).
-
-=item *
-
-I<salt_len>:  By default, the module assumes a salt length of 4 bytes (or 8, 
if it is encoded in hex).
-If you choose a different length, you have to tell the I<validate> function 
how long your seed was.
-
-=back
-
-=cut
-
-sub new {
-    my ( $class, %options ) = @_;
-
-    $options{algorithm} ||= 'SHA-1';
-    $options{salt_len}  ||= 4;
-    $options{salt}      ||= &__generate_hex_salt( $options{salt_len} * 2 );
-
-    $options{algorithm} = uc( $options{algorithm} );
-    $options{algorithm} .= '-1'
-      if $options{algorithm} =~ m!SHA$!;  # SHA => SHA-1, HMAC-SHA => 
HMAC-SHA-1
-
-    my $digest = Digest->new( $options{algorithm} );
-    my $self   = {
-        salt      => $options{salt},
-        algorithm => $options{algorithm},
-        digest    => $digest,
-        scheme    => &__make_scheme( $options{algorithm} ),
-    };
-
-    return bless $self, $class;
-}
-
-=item B<add( $data, ... )>
-
-Logically joins the arguments into a single string, and uses it to
-update the current digest state. For more details see L<Digest>.
-
-=cut
-
-sub add {
-    my $self = shift;
-    $self->obj->add(@_);
-    return $self;
-}
-
-=item B<clear()>
-
-Resets the digest.
-
-=cut
-
-sub clear {
-    my $self = shift;
-    $self->{digest} = Digest->new( $self->{algorithm} );
-    return $self;
-}
-
-=item B<salt_bin()>
-
-Returns the salt in binary form.
-
-=cut
-
-sub salt_bin {
-    my $self = shift;
-
-    return $self->{salt} =~ m!^HEX\{(.*)\}$!i ? pack( "H*", $1 ) : 
$self->{salt};
-}
-
-=item B<salt_hex()>
-
-Returns the salt in hexadecimal form ('HEX{...}')
-
-=cut
-
-sub salt_hex {
-    my $self = shift;
-
-    return $self->{salt} =~ m!^HEX\{(.*)\}$!i
-      ? $self->{salt}
-      : 'HEX{' . join( '', unpack( 'H*', $self->{salt} ) ) . '}';
-}
-
-=item B<generate()>
-
-Generates the seeded hash. Uses the I<clone>-method of L<Digest> before 
actually performing
-the digest calculation, so adding more cleardata after a call of I<generate> 
to an instance of
-I<Crypt::SaltedHash> has the same effect as adding the data before the call of 
I<generate>.
-
-=cut
-
-sub generate {
-    my $self = shift;
-
-    my $clone = $self->obj->clone;
-    my $salt  = $self->salt_bin;
-
-    $clone->add($salt);
-
-    my $gen = &MIME::Base64::encode_base64( $clone->digest . $salt, '' );
-    my $scheme = $self->{scheme};
-
-    return "{$scheme}$gen";
-}
-
-=item B<validate( $hasheddata, $cleardata, [$salt_len] )>
-
-Validates a hasheddata previously generated against cleardata. I<$salt_len> 
defaults to 4 if not set.
-Returns 1 if the validation is successful, 0 otherwise.
-
-=cut
-
-sub validate {
-    my ( undef, $hasheddata, $cleardata, $salt_len ) = @_;
-
-    # trim white-spaces
-    $hasheddata =~ s!^\s+!!;
-    $hasheddata =~ s!\s+$!!;
-
-    my $scheme    = &__get_pass_scheme($hasheddata);
-    $scheme       = uc( $scheme ) if $scheme;
-    my $algorithm = &__make_algorithm($scheme);
-    my $hash      = &__get_pass_hash($hasheddata) || '';
-    my $salt      = &__extract_salt( $hash, $salt_len );
-
-    my $obj = __PACKAGE__->new(
-        algorithm => $algorithm,
-        salt      => $salt,
-        salt_len  => $salt_len
-    );
-    $obj->add($cleardata);
-
-    my $gen_hasheddata = $obj->generate;
-    my $gen_hash       = &__get_pass_hash($gen_hasheddata);
-
-    return _secure_compare( $gen_hash, $hash );
-}
-
-=item B<obj()>
-
-Returns a handle to L<Digest> object.
-
-=cut
-
-sub obj {
-    return shift->{digest};
-}
-
-=back
-
-=head1 FUNCTIONS
-
-I<none yet.>
-
-=cut
-
-sub __make_scheme {
-
-    my $scheme = shift;
-
-    my @parts = split /-/, $scheme;
-    pop @parts if $parts[-1] eq '1';    # SHA-1 => SHA
-
-    $scheme = join '', @parts;
-
-    return uc("S$scheme");
-}
-
-sub __make_algorithm {
-    my ( $algorithm ) = @_;
-
-    $algorithm ||= '';
-    local $1;
-
-    if ( $algorithm =~ m!^S(.*)$! ) {
-        $algorithm = $1;
-
-        # print STDERR "algorithm: $algorithm\n";
-        if ( $algorithm =~ m!([a-zA-Z]+)([0-9]+)! ) {
-
-            my $name   = uc($1);
-            my $digits = $2;
-
-            # print STDERR "name: $name\n";
-            # print STDERR "digits: $digits\n";
-
-            $name = "HMAC-$2" if $name =~ m!^HMAC(.*)$!;    # HMAC-SHA-1
-            $digits = "-$digits" unless $name =~ m!MD$!;    # MD2, MD4, MD5
-
-            $algorithm = "$name$digits";
-        }
-
-    }
-
-    return $algorithm;
-}
-
-sub __get_pass_scheme {
-    local $1;
-    return unless $_[0] =~ m/{([^}]*)/;
-    return $1;
-}
-
-sub __get_pass_hash {
-    local $1;
-    return unless $_[0] =~ m/}(.*)/;
-    return $1;
-}
-
-sub __generate_hex_salt {
-
-    my $length = shift || 8;
-
-    my $salt = substr( unpack( "h*", Crypt::SysRandom::random_bytes( 
POSIX::ceil( $length / 2 ) ) ), 0, $length );
-
-    return "HEX{$salt}";
-}
-
-sub __extract_salt {
-
-    my ( $hash, $salt_len ) = @_;
-
-    my $binhash = &MIME::Base64::decode_base64($hash);
-    my $binsalt = substr( $binhash, length($binhash) - ( $salt_len || 4 ) );
-
-    return $binsalt;
-}
-
-sub _secure_compare {
-    my ($left, $right) = @_;
-    my $res = length $left != length $right;
-    $right = $left if $res;
-    $res |= ord(substr $left, $_, 1) ^ ord(substr $right, $_, 1) for 0 .. 
length($left) - 1;
-    return $res == 0;
-}
-
-=head1 SEE ALSO
-
-L<Digest>, L<MIME::Base64>
-
-=head1 AUTHOR
-
-Sascha Kiefer, <[email protected]>
-
-The current maintainer is Robert Rothenberg <[email protected]>
-
-=head1 ACKNOWLEDGMENTS
-
-The author is particularly grateful to Andres Andreu for his article: Salted
-hashes demystified - A Primer (L<http://www.securitydocs.com/library/3439>)
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2005-2006, 2010, 2013, 2026 Sascha Kiefer
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
-1;
+package Crypt::SaltedHash;
+
+use v5.6.0;
+use strict;
+
+use Crypt::SysRandom ();
+use Digest       ();
+use MIME::Base64 ();
+use POSIX ();
+
+our $VERSION = '0.12';
+
+=encoding latin1
+
+=head1 NAME
+
+Crypt::SaltedHash - Perl interface to functions that assist in working
+with salted hashes.
+
+=head1 SYNOPSIS
+
+       use Crypt::SaltedHash;
+
+       my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-1');
+       $csh->add('secret');
+
+       my $salted = $csh->generate;
+       my $valid = Crypt::SaltedHash->validate($salted, 'secret');
+
+
+=head1 STATUS
+
+This module is deprecated.
+
+This module has not had significant updates since 2006.
+There are newer modules that support more secure algorithms and hashing 
options,
+and are extensible, such as L<Crypt::Passphrase>.
+
+You can use the L<Crypt::Passphrase::SaltedHash> validator to migrate to more 
secure algorithms.
+
+=head1 DESCRIPTION
+
+The C<Crypt::SaltedHash> module provides an object oriented interface to
+create salted (or seeded) hashes of clear text data. The original
+formalization of this concept comes from RFC-3112 and is extended by the use
+of different digital algorithms.
+
+=head1 ABSTRACT
+
+=head2 Setting the data
+
+The process starts with 2 elements of data:
+
+=over
+
+=item *
+
+a clear text string (this could represent a password for instance).
+
+=item *
+
+the salt, a random seed of data. This is the value used to augment a hash in 
order to
+ensure that 2 hashes of identical data yield different output.
+
+=back
+
+For the purposes of this abstract we will analyze the steps within code that 
perform the necessary actions
+to achieve the endresult hashes. Cryptographers call this hash a digest. We 
will not however go into an explanation
+of a one-way encryption scheme. Readers of this abstract are encouraged to get 
information on that subject by
+their own.
+
+Theoretically, an implementation of a one-way function as an algorithm takes 
input, and provides output, that are both
+in binary form; realistically though digests are typically encoded and stored 
in a database or in a flat text or XML file.
+Take slappasswd5 for instance, it performs the exact functionality described 
above. We will use it as a black box compiled
+piece of code for our analysis.
+
+In pseudocode we generate a salted hash as follows:
+
+    Get the source string and salt as separate binary objects
+    Concatenate the 2 binary values
+    Hash the concatenation into SaltedPasswordHash
+    Base64Encode(concat(SaltedPasswordHash, Salt))
+
+We take a clear text string and hash this into a binary object representing 
the hashed value of the clear text string plus the random salt.
+Then we have the Salt value, which are typically 4 bytes of purely random 
binary data represented as hexadecimal notation (Base16 as 8 bytes).
+
+Using SHA-1 as the hashing algorithm, SaltedPasswordHash is of length 20 
(bytes) in raw binary form
+(40 bytes if we look at it in hex). Salt is then 4 bytes in raw binary form. 
The SHA-1 algorithm generates
+a 160 bit hash string. Consider that 8 bits = 1 byte. So 160 bits = 20 bytes, 
which is exactly what the
+algorithm gives us.
+
+The Base64 encoding of the binary result looks like:
+
+    {SSHA}B0O0XSYdsk7g9K229ZEr73Lid7HBD9DX
+
+Take note here that the final output is a 32-byte string of data. The Base64 
encoding process uses bit shifting, masking, and padding as per RFC-3548.
+
+A couple of examples of salted hashes using on the same exact clear-text 
string:
+
+    slappasswd -s testing123
+    {SSHA}72uhy5xc1AWOLwmNcXALHBSzp8xt4giL
+
+    slappasswd -s testing123
+    {SSHA}zmIAVaKMmTngrUi4UlS0dzYwVAbfBTl7
+
+    slappasswd -s testing123
+    {SSHA}Be3F12VVvBf9Sy6MSqpOgAdEj6JCZ+0f
+
+    slappasswd -s testing123
+    {SSHA}ncHs4XYmQKJqL+VuyNQzQjwRXfvu6noa
+
+4 runs of slappasswd against the same clear text string each yielded unique 
endresult hashes.
+The random salt is generated silently and never made visible.
+
+=head2 Extracting the data
+
+One of the keys to note is that the salt is dealt with twice in the process. 
It is used once for the actual application of randomness to the
+given clear text string, and then it is stored within the final output as 
purely Base64 encoded data. In order to perform an authentication
+query for instance, we must break apart the concatenation that was created for 
storage of the data. We accomplish this by splitting
+up the binary data we get after Base64 decoding the stored hash.
+
+In pseudocode we would perform the extraction and verification operations as 
such:
+
+    Strip the hash identifier from the Digest
+    Base64Decode(Digest, 20)
+    Split Digest into 2 byte arrays, one for bytes 0 � 20(pwhash), one for 
bytes 21 � 32 (salt)
+    Get the target string and salt as separate binary object
+    Concatenate the 2 binary values
+    SHA hash the concatenation into targetPasswordHash
+    Compare targetPasswordHash with pwhash
+    Return corresponding Boolean value
+
+Our job is to split the original digest up into 2 distinct byte arrays, one of 
the left 20 (0 - 20 including the null terminator) bytes and
+the other for the rest of the data. The left 0 � 20 bytes will represent the 
salted  binary value we will use for a byte-by-byte data
+match against the new clear text presented for verification. The string 
presented for verification will have to be salted as well. The rest
+of the bytes (21 � 32) represent the random salt which when decoded will show 
the exact hex characters that make up the once randomly
+generated seed.
+
+We are now ready to verify some data. Let's start with the 4 hashes presented 
earlier. We will run them through our code to extract the
+random salt and then using that verify the clear text string hashed by 
slappasswd. First, let's do a verification test with an erroneous
+password; this should fail the matching test:
+
+    {SSHA}72uhy5xc1AWOLwmNcXALHBSzp8xt4giL Test123
+    Hash extracted (in hex): ef6ba1cb9c5cd4058e2f098d71700b1c14b3a7cc
+    Salt extracted (in hex): 6de2088b
+    Hash length is: 20 Salt length is: 4
+    Hash presented in hex: 256bc48def0ce04b0af90dfd2808c42588bf9542
+    Hashes DON'T match: Test123
+
+The match failure test was successful as expected. Now let's use known valid 
data through the same exact code:
+
+    {SSHA}72uhy5xc1AWOLwmNcXALHBSzp8xt4giL testing123
+    Hash extracted (in hex): ef6ba1cb9c5cd4058e2f098d71700b1c14b3a7cc
+    Salt extracted (in hex): 6de2088b
+    Hash length is: 20 Salt length is: 4
+    Hash presented in hex: ef6ba1cb9c5cd4058e2f098d71700b1c14b3a7cc
+    Hashes match: testing123
+
+The process used for salted passwords should now be clear. We see that salting 
hashed data does indeed add another layer of security to the
+clear text one-way hashing process. But we also see that salted hashes should 
also be protected just as if the data was in clear text form.
+Now that we have seen salted hashes actually work you should also realize that 
in code it is possible to extract salt values and use them
+for various purposes. Obviously the usage can be on either side of the colored 
hat line, but the data is there.
+
+=head1 METHODS
+
+=over 4
+
+=item B<new( [%options] )>
+
+Returns a new Crypt::SaltedHash object.
+Possible keys for I<%options> are:
+
+=over
+
+=item *
+
+I<algorithm>: It's also possible to use common string representations of the
+algorithm (e.g. "sha256", "SHA-384"). If the argument is missing, SHA-1 will
+be used by default.
+
+=item *
+
+I<salt>: You can specify your on salt. You can either specify it as a sequence
+of characters or as a hex encoded string of the form "HEX{...}". If the 
argument is missing,
+a random seed is provided for you (recommended).
+
+=item *
+
+I<salt_len>:  By default, the module assumes a salt length of 4 bytes (or 8, 
if it is encoded in hex).
+If you choose a different length, you have to tell the I<validate> function 
how long your seed was.
+
+=back
+
+=cut
+
+sub new {
+    my ( $class, %options ) = @_;
+
+    $options{algorithm} ||= 'SHA-1';
+    $options{salt_len}  ||= 4;
+    $options{salt}      ||= &__generate_hex_salt( $options{salt_len} * 2 );
+
+    $options{algorithm} = uc( $options{algorithm} );
+    $options{algorithm} .= '-1'
+      if $options{algorithm} =~ m!SHA$!;  # SHA => SHA-1, HMAC-SHA => 
HMAC-SHA-1
+
+    my $digest = Digest->new( $options{algorithm} );
+    my $self   = {
+        salt      => $options{salt},
+        algorithm => $options{algorithm},
+        digest    => $digest,
+        scheme    => &__make_scheme( $options{algorithm} ),
+    };
+
+    return bless $self, $class;
+}
+
+=item B<add( $data, ... )>
+
+Logically joins the arguments into a single string, and uses it to
+update the current digest state. For more details see L<Digest>.
+
+=cut
+
+sub add {
+    my $self = shift;
+    $self->obj->add(@_);
+    return $self;
+}
+
+=item B<clear()>
+
+Resets the digest.
+
+=cut
+
+sub clear {
+    my $self = shift;
+    $self->{digest} = Digest->new( $self->{algorithm} );
+    return $self;
+}
+
+=item B<salt_bin()>
+
+Returns the salt in binary form.
+
+=cut
+
+sub salt_bin {
+    my $self = shift;
+
+    return $self->{salt} =~ m!^HEX\{(.*)\}$!i ? pack( "H*", $1 ) : 
$self->{salt};
+}
+
+=item B<salt_hex()>
+
+Returns the salt in hexadecimal form ('HEX{...}')
+
+=cut
+
+sub salt_hex {
+    my $self = shift;
+
+    return $self->{salt} =~ m!^HEX\{(.*)\}$!i
+      ? $self->{salt}
+      : 'HEX{' . join( '', unpack( 'H*', $self->{salt} ) ) . '}';
+}
+
+=item B<generate()>
+
+Generates the seeded hash. Uses the I<clone>-method of L<Digest> before 
actually performing
+the digest calculation, so adding more cleardata after a call of I<generate> 
to an instance of
+I<Crypt::SaltedHash> has the same effect as adding the data before the call of 
I<generate>.
+
+=cut
+
+sub generate {
+    my $self = shift;
+
+    my $clone = $self->obj->clone;
+    my $salt  = $self->salt_bin;
+
+    $clone->add($salt);
+
+    my $gen = &MIME::Base64::encode_base64( $clone->digest . $salt, '' );
+    my $scheme = $self->{scheme};
+
+    return "{$scheme}$gen";
+}
+
+=item B<validate( $hasheddata, $cleardata, [$salt_len] )>
+
+Validates a hasheddata previously generated against cleardata. I<$salt_len> 
defaults to 4 if not set.
+Returns 1 if the validation is successful, 0 otherwise.
+
+=cut
+
+sub validate {
+    my ( undef, $hasheddata, $cleardata, $salt_len ) = @_;
+
+    # trim white-spaces
+    $hasheddata =~ s!^\s+!!;
+    $hasheddata =~ s!\s+$!!;
+
+    my $scheme    = &__get_pass_scheme($hasheddata);
+    $scheme       = uc( $scheme ) if $scheme;
+    my $algorithm = &__make_algorithm($scheme);
+    my $hash      = &__get_pass_hash($hasheddata) || '';
+    my $salt      = &__extract_salt( $hash, $salt_len );
+
+    my $obj = __PACKAGE__->new(
+        algorithm => $algorithm,
+        salt      => $salt,
+        salt_len  => $salt_len
+    );
+    $obj->add($cleardata);
+
+    my $gen_hasheddata = $obj->generate;
+    my $gen_hash       = &__get_pass_hash($gen_hasheddata);
+
+    return _secure_compare( $gen_hash, $hash );
+}
+
+=item B<obj()>
+
+Returns a handle to L<Digest> object.
+
+=cut
+
+sub obj {
+    return shift->{digest};
+}
+
+=back
+
+=head1 FUNCTIONS
+
+I<none yet.>
+
+=cut
+
+sub __make_scheme {
+
+    my $scheme = shift;
+
+    my @parts = split /-/, $scheme;
+    pop @parts if $parts[-1] eq '1';    # SHA-1 => SHA
+
+    $scheme = join '', @parts;
+
+    return uc("S$scheme");
+}
+
+sub __make_algorithm {
+    my ( $algorithm ) = @_;
+
+    $algorithm ||= '';
+    local $1;
+
+    if ( $algorithm =~ m!^S(.*)$! ) {
+        $algorithm = $1;
+
+        # print STDERR "algorithm: $algorithm\n";
+        if ( $algorithm =~ m!([a-zA-Z]+)([0-9]+)! ) {
+
+            my $name   = uc($1);
+            my $digits = $2;
+
+            # print STDERR "name: $name\n";
+            # print STDERR "digits: $digits\n";
+
+            $name = "HMAC-$2" if $name =~ m!^HMAC(.*)$!;    # HMAC-SHA-1
+            $digits = "-$digits" unless $name =~ m!MD$!;    # MD2, MD4, MD5
+
+            $algorithm = "$name$digits";
+        }
+
+    }
+
+    return $algorithm;
+}
+
+sub __get_pass_scheme {
+    local $1;
+    return unless $_[0] =~ m/{([^}]*)/;
+    return $1;
+}
+
+sub __get_pass_hash {
+    local $1;
+    return unless $_[0] =~ m/}(.*)/;
+    return $1;
+}
+
+sub __generate_hex_salt {
+
+    my $length = shift || 8;
+
+    my $salt = substr( unpack( "h*", Crypt::SysRandom::random_bytes( 
POSIX::ceil( $length / 2 ) ) ), 0, $length );
+
+    return "HEX{$salt}";
+}
+
+sub __extract_salt {
+
+    my ( $hash, $salt_len ) = @_;
+
+    my $binhash = &MIME::Base64::decode_base64($hash);
+    my $binsalt = substr( $binhash, length($binhash) - ( $salt_len || 4 ) );
+
+    return $binsalt;
+}
+
+sub _secure_compare {
+    my ($left, $right) = @_;
+    my $res = length $left != length $right;
+    $right = $left if $res;
+    $res |= ord(substr $left, $_, 1) ^ ord(substr $right, $_, 1) for 0 .. 
length($left) - 1;
+    return $res == 0;
+}
+
+=head1 SEE ALSO
+
+L<Digest>, L<MIME::Base64>
+
+=head1 AUTHOR
+
+Sascha Kiefer, <[email protected]>
+
+The current maintainer is Robert Rothenberg <[email protected]>
+
+=head1 ACKNOWLEDGMENTS
+
+The author is particularly grateful to Andres Andreu for his article: Salted
+hashes demystified - A Primer (L<http://www.securitydocs.com/library/3439>)
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2005-2006, 2010, 2013, 2026 Sascha Kiefer
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/t/00-report-prereqs.dd 
new/Crypt-SaltedHash-0.12/t/00-report-prereqs.dd
--- old/Crypt-SaltedHash-0.11/t/00-report-prereqs.dd    2026-05-20 
15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/t/00-report-prereqs.dd    2026-05-23 
08:29:35.000000000 +0200
@@ -11,6 +11,7 @@
                                       'Test::CleanNamespaces' => '0.15',
                                       'Test::DistManifest' => '0',
                                       'Test::EOF' => '0',
+                                      'Test::EOL' => '0',
                                       'Test::Fixme' => '0',
                                       'Test::MinimumVersion' => '0',
                                       'Test::More' => '0.94',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/t/04Crypt-SaltedHash.t 
new/Crypt-SaltedHash-0.12/t/04Crypt-SaltedHash.t
--- old/Crypt-SaltedHash-0.11/t/04Crypt-SaltedHash.t    2026-05-20 
15:05:08.000000000 +0200
+++ new/Crypt-SaltedHash-0.12/t/04Crypt-SaltedHash.t    2026-05-23 
08:29:35.000000000 +0200
@@ -1,59 +1,59 @@
-# Before `make install' is performed this script should be runnable with
-# `make test'. After `make install' it should work as `perl Crypt-SaltedHash.t'
-
-#########################
-
-# change 'tests => 1' to 'tests => last_test_to_print';
-
-use strict;
-use Test::More tests => 6;
-BEGIN { use_ok('Crypt::SaltedHash') }
-
-#########################
-
-my ( $csh, $salted, $valid );
-
-my %known_salts = (
-    'MD5'   => '{SMD5}vfwtsKpZn1kZ5WXDKCFqUTEyMzQ=',
-    # 'SHA-1' => '{SSHA}kRnWqCDFvZFoV7A6cTGBdq1Xv7cxMjM0',
-);
-
-foreach my $alg (keys %known_salts) {
-
-    $csh = Crypt::SaltedHash->new( algorithm => $alg );
-    $csh->add('secret');
-
-    $salted = $csh->generate;
-    $valid  = Crypt::SaltedHash->validate( $salted, 'secret' );
-
-    ok( $valid, "$alg: default test" );
-
-    $csh = Crypt::SaltedHash->new( algorithm => $alg, salt_len => 32 );
-    $csh->add('secret');
-
-    $salted = $csh->generate;
-    $valid  = Crypt::SaltedHash->validate( $salted, 'secret', 32 );
-
-    ok( $valid, "$alg: salt_len test" );
-
-    $csh = Crypt::SaltedHash->new( algorithm => $alg );
-
-    $csh->add('secret');
-    $salted = $csh->generate;
-    $csh->add('secret');
-    $salted = $csh->generate;
-
-    $valid = Crypt::SaltedHash->validate( $salted, 'secretsecret' );
-
-    ok( $valid, "$alg: generate test" );
-
-    $csh = Crypt::SaltedHash->new( algorithm => $alg, salt => '1234' );
-    $csh->add('secret');
-
-    ok( $csh->generate eq $known_salts{$alg}, "$alg: own bin-salt test" );
-
-    $csh = Crypt::SaltedHash->new( algorithm => $alg, salt => 'HEX{31323334}' 
);
-    $csh->add('secret');
-
-    ok( $csh->generate eq $known_salts{$alg}, "$alg: own hex-salt test" );
-}
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl Crypt-SaltedHash.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use strict;
+use Test::More tests => 6;
+BEGIN { use_ok('Crypt::SaltedHash') }
+
+#########################
+
+my ( $csh, $salted, $valid );
+
+my %known_salts = (
+    'MD5'   => '{SMD5}vfwtsKpZn1kZ5WXDKCFqUTEyMzQ=',
+    # 'SHA-1' => '{SSHA}kRnWqCDFvZFoV7A6cTGBdq1Xv7cxMjM0',
+);
+
+foreach my $alg (keys %known_salts) {
+
+    $csh = Crypt::SaltedHash->new( algorithm => $alg );
+    $csh->add('secret');
+
+    $salted = $csh->generate;
+    $valid  = Crypt::SaltedHash->validate( $salted, 'secret' );
+
+    ok( $valid, "$alg: default test" );
+
+    $csh = Crypt::SaltedHash->new( algorithm => $alg, salt_len => 32 );
+    $csh->add('secret');
+
+    $salted = $csh->generate;
+    $valid  = Crypt::SaltedHash->validate( $salted, 'secret', 32 );
+
+    ok( $valid, "$alg: salt_len test" );
+
+    $csh = Crypt::SaltedHash->new( algorithm => $alg );
+
+    $csh->add('secret');
+    $salted = $csh->generate;
+    $csh->add('secret');
+    $salted = $csh->generate;
+
+    $valid = Crypt::SaltedHash->validate( $salted, 'secretsecret' );
+
+    ok( $valid, "$alg: generate test" );
+
+    $csh = Crypt::SaltedHash->new( algorithm => $alg, salt => '1234' );
+    $csh->add('secret');
+
+    ok( $csh->generate eq $known_salts{$alg}, "$alg: own bin-salt test" );
+
+    $csh = Crypt::SaltedHash->new( algorithm => $alg, salt => 'HEX{31323334}' 
);
+    $csh->add('secret');
+
+    ok( $csh->generate eq $known_salts{$alg}, "$alg: own hex-salt test" );
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Crypt-SaltedHash-0.11/xt/author/eol.t 
new/Crypt-SaltedHash-0.12/xt/author/eol.t
--- old/Crypt-SaltedHash-0.11/xt/author/eol.t   1970-01-01 01:00:00.000000000 
+0100
+++ new/Crypt-SaltedHash-0.12/xt/author/eol.t   2026-05-23 08:29:35.000000000 
+0200
@@ -0,0 +1,18 @@
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::EOL 0.19
+
+use Test::More 0.88;
+use Test::EOL;
+
+my @files = (
+    'lib/Crypt/SaltedHash.pm',
+    't/00-report-prereqs.dd',
+    't/00-report-prereqs.t',
+    't/04Crypt-SaltedHash.t',
+    't/bug-localize-regex-vars.t'
+);
+
+eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files;
+done_testing;

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.Fk3qwo/_old  2026-06-09 14:28:02.747778025 +0200
+++ /var/tmp/diff_new_pack.Fk3qwo/_new  2026-06-09 14:28:02.751778190 +0200
@@ -1,6 +1,6 @@
-mtime: 1779365402
-commit: 5c91a4f3554aef67046839374c76db5f53335b2fda21173c11cc7fc195b3517c
+mtime: 1779522253
+commit: 9ac219415234ba33e044aa62cda11ae7c167f578da67a0748831a3446889ae95
 url: https://src.opensuse.org/perl/perl-Crypt-SaltedHash
-revision: 5c91a4f3554aef67046839374c76db5f53335b2fda21173c11cc7fc195b3517c
+revision: 9ac219415234ba33e044aa62cda11ae7c167f578da67a0748831a3446889ae95
 projectscmsync: https://src.opensuse.org/perl/_ObsPrj
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-05-23 09:44:13.000000000 +0200
@@ -0,0 +1 @@
+.osc

Reply via email to