Povilas Daukas
Povilas Daukas

Reputation: 200

Perl alternative to hash_hmac('ripemd160', $data, $key) in PHP

I need to produce same result in Perl that hash_hmac('ripemd160', $data, $key) produces in PHP

Managed to trace it down to two perl modules, just cant get them working together...

Digest::HMAC and Crypt::RIPEMD160

use Crypt::RIPEMD160;

use Digest::HMAC;
$hmac = Digest::HMAC->new('bar', 'Crypt::RIPEMD160');

$hmac->add('foo');
$digest = $hmac->digest;

anyone got any ideas what am i doing wrong?

If i use the code above i get following error: Can't call method "add" on an undefined value at /usr/lib64/perl5/vendor_perl/5.12.4/Digest/HMAC.pm line 28.

Since i was unable to pass the hash function reference in the code above, after looking at the HMAC module at the hmac function i thought i could write it in my code direct:

my $data = 'bar';
my $key = 'foo';
$block_size = 160;
$block_size ||= 64;
$key = Crypt::RIPEMD160->hash($key) if length($key) > $block_size;
my $k_ipad = $key ^ (chr(0x36) x $block_size);
my $k_opad = $key ^ (chr(0x5c) x $block_size);
my $digest =  Crypt::RIPEMD160->hash($k_opad, Crypt::RIPEMD160->hash($k_ipad, $data));

this does produce a hash but still a wrong one

PHP generated hash: isceebbf5cd5e34c888b493cf7f7c39a7b181b65a3

The perl hash: hash21a2fa2bf39fd99d4c9cdf147added69c32d45f9e

To be honest i dont care how its done and what modules are used as long as I get same hash as the php function produces... at this point I am tempted writing a php script that i call from perl just to get that hash... :( as I am runing out of ideas...

Upvotes: 2

Views: 1266

Answers (3)

karel-m
karel-m

Reputation: 111

I am perhaps a bit late in this discussion but when talking about Crypt::Digest::RIPEMD160 (I am the author of this module :) you can easily create HMAC with Crypt::Mac::HMAC from the same family of modules.

It is as simple as:

use Crypt::Mac::HMAC 'hmac';
$hmac_raw = hmac('RIPEMD160', $key, $data);

Upvotes: 2

Ilmari Karonen
Ilmari Karonen

Reputation: 50368

The reason your code doesn't work is that, while the interface provided by Crypt::RIPEMD160 looks similar to the standard Digest interface, it's not quite compatible: in particular, the reset() method of Crypt::RIPEMD160 apparently doesn't return a reference to the object it's called on, and the code in Digest::HMAC happens to rely on that detail.

This incompatibility would be a trivial things to fix by slightly tweaking either module, either to add the missing return value to Crypt::RIPEMD5 or to make Digest::HMAC less reliant on needless method chaining. The latter would be as easy as changing the line:

$self->{hasher}->reset->add($self->{k_opad}, $inner_digest);

in Digest::HMAC to:

$self->{hasher}->reset;
$self->{hasher}->add($self->{k_opad}, $inner_digest);

(Of course, I'm not suggesting that you do this yourself, although you could report the issue to the maintainers of those modules.)

However, with both modules as they currently are, it just won't work. The solutions I'd recommend would be to either use the non-OO interface, as David W. suggests, or try the newer Crypt::Digest::RIPEMD160 module, which properly implements the Digest interface and should play nicer with Digest::HMAC.


Edit: Actually, David W.'s suggestion won't work as given, because Crypt::RIPEMD160 doesn't export a non-OO ripemd160() function. You could, however, easily create one:

use Crypt::RIPEMD160;
sub ripemd160 {
    return Crypt::RIPEMD160->hash( join "", @_ );
}

and then use it like this:

use Digest::HMAC qw( hmac );
sub hmac_ripemd160 {
    return hmac( @_[0, 1], \&ripemd160, 64 );
}

(Yes, 64 bytes is the correct block size from HMAC-RIPEMD160, since the input block length of RIPEMD160 is 16 32-bit words, which equals 512 bits or 64 bytes. In practice, using the wrong input block size is very unlikely to cause any issues, other than for interoperability of course, but the security proof of the HMAC construction assumes, for simplicity, that the key is padded to be exactly one input block long. Thus, and in order to ensure that all implementations of HMAC-RIPEMD160 produce the same output for the same key and message, it's best to stick to this rule.)


Edit 2: OK, I tried to test the code I posted above against the HMAC-RIPEMD160 test vectors from RFC 2286, and just could not get the results to match. What I finally realized was two things:

  1. The non-OO hmac() function exported by Digest::HMAC assumes that the custom hash function passed to it will accept multiple parameters and concatenate them. My original implementation of the ripemd160() wrapper above did not (but I fixed it so that now it does). This is arguably a bug in Digest::HMAC, or at least in its documentation.

  2. The Crypt::RIPEMD160 module comes with the submodule Crypt::RIPEMD160::MAC, which already implements HMAC-RIPEMD160, even though, for some perverse reason, the documentation doesn't actually use the name HMAC. If you look at the code, though, or just compare the output to the official test vectors, that's indeed exactly what it does.

Upvotes: 1

David W.
David W.

Reputation: 107080

The Digest::HMAC only includes Digest::HMAC_MD5 and Digest::HMAC_SHA1. However, I took a look at the Perl code for Digest::HMAC_MD5. The whole thing is about 20 lines of code. It basically creates two methods:

sub hmac_md5 {
    hmac($_[0], $_[1], \&md5, 64);
}

and

sub hmac_md5_hex {
    unpack("H*", &hmac_md5);
}

That's pretty much the entire program.

If you forget about the object oriented style of the package, and use the functional style, it looks like this might work for you:

hmac($data, $key, \&ripemd160, 160);

or maybe just:

hmac($data, $key \&ripemd160);

In fact, that's documented on the CPAN Digest::HMAC page itself.

Upvotes: 3

Related Questions