The hotp algorithms above work with counter values less than 256, but since the counter can be larger, it's necessary to iterate through all the bytes of the counter:
<?php
function oath_hotp ($key, $counter)
{
// Counter
//the counter value can be more than one byte long, so we need to go multiple times
$cur_counter = array(0,0,0,0,0,0,0,0);
for($i=7;$i>=0;$i--)
{
$cur_counter[$i] = pack ('C*', $counter);
$counter = $counter >> 8;
}
$bin_counter = implode($cur_counter);
// Pad to 8 chars
if (strlen ($bin_counter) < 8)
{
$bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter;
}
// HMAC
$hash = hash_hmac ('sha1', $bin_counter, $key);
return $hash;
}
function oath_truncate($hash, $length = 6)
{
// Convert to dec
foreach(str_split($hash,2) as $hex)
{
$hmac_result[]=hexdec($hex);
}
// Find offset
$offset = $hmac_result[19] & 0xf;
// Algorithm from RFC
return
(
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
) % pow(10,$length);
}
print "<pre>";
print "Compare results with:";
print " http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04\n";
print "Count\tHash\t\t\t\t\t\tPin\n";
for($i=0;$i<=1024;$i=$i+128)
{
print $i."\t".($a=oath_hotp("12345678901234567890",$i));
print "\t".oath_truncate($a)."\n";
}
?>
hash_hmac
(PHP 5 >= 5.1.2, PECL hash >= 1.1)
hash_hmac — Generate a keyed hash value using the HMAC method
Description
string hash_hmac
( string $algo
, string $data
, string $key
[, bool $raw_output= false
] )
Parameters
- algo
-
Name of selected hashing algorithm (i.e. "md5", "sha256", "haval160,4", etc..)
- data
-
Message to be hashed.
- key
-
Shared secret key used for generating the HMAC variant of the message digest.
- raw_output
-
When set to TRUE, outputs raw binary data. Default value (FALSE) outputs lowercase hexits.
Return Values
Returns a string containing the calculated message digest as lowercase hexits unless raw_output is set to true in which case the raw binary representation of the message digest is returned.
Examples
Example #1 hash_hmac() example
<?php
echo hash_hmac('ripemd160', 'The quick brown fox jumped over the lazy dog.', 'secret');
?>
The above example will output:
b8e7ae12510bdfb1812e463a7f086122cf37e4f7
See Also
- hash() - Generate a hash value (message digest)
- hash_init() - Initialize an incremental hashing context
- hash_hmac_file() - Generate a keyed hash value using the HMAC method and the contents of a given file
hash_hmac
brent at thebrent dot net
21-May-2009 03:17
21-May-2009 03:17
torben dot egmose at gmail dot com
22-Mar-2009 07:40
22-Mar-2009 07:40
HOTP Algorithm that works according to the RCF http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04
The test cases from the RCF document the ASCII string as "123456787901234567890".
But the hex decoded to a string is "12345678901234567890".
Secret="12345678901234567890";
Count:
0 755224
1 287082
<?php
function oath_hotp($key,$counter) {
// Convert to padded binary string
$data = pack ('C*', $counter);
$data = str_pad($data,8,chr(0),STR_PAD_LEFT);
// HMAC
return hash_hmac('sha1',$data,$key);
}
function oath_truncate($hash, $length = 6) {
// Convert to dec
foreach(str_split($hash,2) as $hex) {
$hmac_result[]=hexdec($hex);
}
// Find offset
$offset = $hmac_result[19] & 0xf;
// Algorithm from RFC
return (
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
) % pow(10,$length);
}
print "<pre>";
print "Compare results with:"
print " http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04\n";
print "Count\tHash\t\t\t\t\t\tPin\n";
for($i=0;$i<10;$i++)
print $i."\t".($a=oath_hotp("12345678901234567890",$i))
print "\t".oath_truncate($a)."\n";
Carlos Averett(caverett*@*corecodec,net)
03-Jul-2008 10:54
03-Jul-2008 10:54
Generating OATH-compliant OTP (one time passwords) results in PHP:
<?php
$otp = oath_truncate (oath_hotp ($key, $counter), $length);
function oath_hotp ($key, $counter) {
// Counter
$bin_counter = pack ('C*', $counter);
// Pad to 8 chars
if (strlen ($bin_counter) < 8) {
$bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter;
}
// HMAC
$hash = hash_hmac ('sha1', $bin_counter, $key);
return $hash;
}
function oath_truncate ($hash, $length = 6) {
// The last byte is used as an offset
$offset = hexdec (substr ($hash, 38)) & 0xf;
// Extract the relevant part, and clear the first bit
$hex_truncated = substr ($hash, $offset * 2, 8);
$bin_truncated = decbin (hexdec ($hex_truncated));
$bin_truncated[0] = '0';
$dec_truncated = bindec ($bin_truncated);
return substr ($dec_truncated, 0 - $length);
}
?>
