Let's consider nakawa has some information on a weakness of SHA-512.
http://en.wikipedia.org/wiki/Hash-based_message_authentication_code HMAC-SHA512(K,m) = SHA512((K ⊕ opad) + SHA512((K ⊕ ipad) + m))
+ is concatenation
⊕ is exclusive-or
PHP Code to compute the lucky number:
function getLuckyNumber($server, $client, $nonce) {
$str = hash_hmac("sha512", "$client:$nonce", $server);
for ($i=0; $i $hex = substr($str, $i, 5);
$dec = hexdec($hex);
if ($dec < 1000000)
return $dec / 10000;
}
}
The SHA-512 compression function uses 80 x 64-bit words (5120 bits) plus the 8 x 64-bit (512 bits) state words to output 8 x 64bit (512 bits).
That means the final outer hash of the HMAC-512 bits
- size of server seed (K here) is 64 characters [0-9A-Za-z\._] (512 bits, 384 bits maximum entropy)
- client seed is usually 24 characters (192 bits) (is there a minimum? maximum?)
- nonce can be 1,2,3,4,etc charactors. Lets assume 1..4 (32bits) based on nakowa's pattern of behaviour.
That means:
HMAC-SHA512(K,m) = SHA512( <512 bits> + <512 bits> + <192 bits> + ':' + <32 bits> )
If there was an attack via the SHA-512 compression function, it would be by exploiting the fact that bits 1024...1215 (bytes 128...151) are totally under client control and bits 1216...1247 (bytes 152...155) are totally predictable (sequential).
So let's look and see if nakowa's bets vary based on the bits of his nonce (it's been shown he hardly ever changes his client seed, so let's focus on the nonce).
PHP code for others to take and improve:
if (!function_exists("stats_standard_deviation")) {
print "need to install stats package\n".
"pecl install stats\n".
"echo extension=stats.so > /etc/php.d/stats.ini\n";
exit;
}
// Source data
// https://just-dice.com/nakowa.txt.bz2
// https://just-dice.com/nakowa2.txt.bz2
$fh = fopen("nakowa.txt","rb");
if (!$fh) {
exit;
}
# remove headers
$line=fgets($fh); $line=fgets($fh); $line=fgets($fh);
# init our counts
$sum = 0;
$gray_bets = array();
resetGrayBets($gray_bets);
// reset sums
function resetGrayBets(&$gray_bets) {
$bits = 12;
// 1 bit
for ($i=0; $i<$bits; $i++)
$gray_bets[1 << $i] = array();
// 2 bits
for ($i=0; $i<$bits; $i++)
for ($j=$i+1; $j<$bits; $j++)
$gray_bets[(1 << $i) | (1 << $j)] = array();
// 3 bits
for ($i=0; $i<$bits; $i++)
for ($j=$i+1; $j<$bits; $j++)
for ($k=$j+1; $k<$bits; $k++)
$gray_bets[(1 << $i) | (1 << $j) | (1 << $k)] = array();
// 4 bits
for ($i=0; $i<$bits; $i++)
for ($j=$i+1; $j<$bits; $j++)
for ($k=$j+1; $k<$bits; $k++)
for ($l=$k+1; $k<$bits; $k++)
$gray_bets[(1 << $i) | (1 << $j) | (1 << $k) | (1 << $l)] = array();
# ksort($gray_bets);
}
// display sums
function dumpGraySums($gray_bets) {
$report = array();
foreach ($gray_bets as $i=>$arr) {
$ibin = substr("00000000000000000". decbin($i), -16);
if (count($arr) == 0)
continue;
$sum = $mean = $stdev = 0;
if (count($arr) != 0) {
$sum = array_sum($arr);
$mean = $sum / count($arr);
$min = min($arr);
$max = max($arr);
$stdev = stats_standard_deviation($arr);
}
$report[] = array(
'i' => $i,
'ibin' => $ibin,
'sum' => $sum,
'count' => count($arr),
'mean' => $mean,
'min' => $min,
'max' => $max,
'stdev' => $stdev,
);
}
usort($report, function($a,$b){
return $b['mean'] - $a['mean'];
# return $b['stdev'] - $a['stdev'];
});
foreach ($report as $i=>$info) {
$info['sum'] = sprintf("%4.3u", $info['sum']);
$info['count'] = sprintf("%4.3u", $info['count']);
$info['min'] = number_format($info['min'], 3);
$info['mean'] = number_format($info['mean'], 3);
$info['max'] = number_format($info['max'], 3);
$info['stdev'] = number_format($info['stdev'], 3);
print " {$info['ibin']}: sum={$info['sum']}, count={$info['count']}, min/mean/max={$info['min']}/{$info['mean']}/{$info['max']}, stdev={$info['stdev']})\n";
}
}
$last_nonce = -1;
while (($line=fgets($fh)) !== false) {
list($uid,$bid,$nonce,$bet,$chance,$profit) = preg_split('/\t/', $line);
// cast to int
$uid = (int)$uid;
$bid = (int)$bid;
$nonce = (int)$nonce;
$bet = (int)$bet;
$change = (int)$change;
$profit = (int)$profit;
if ($last_nonce == -1)
$last_nonce = $nonce;
if ($bet == 0)
continue;
// values in BTC
$bet /= 100000000;
$profit /= 100000000;
$nonce_bin = substr("00000000000000000". decbin($nonce), -16);
# if ($bet >= 10)
print "$uid\t$bid\t$nonce\t$nonce_bin\t{$bet}BTC\t$chance\t{$profit}BTC\n";
$sum += $profit;
foreach ($gray_bets as $code=>$ignore) {
# edit-1 start
if ($nonce < 0x0000100) $nonce_bytes = $nonce << 24;
else if ($nonce < 0x0010000) $nonce_bytes = $nonce << 16;
else if ($nonce < 0x1000000) $nonce_bytes = $nonce << 8;
else $nonce_bytes = $nonce;
if (($nonce_bytes & $code) == $code) {
#edit-1 end
$gray_bets[$code][] = $bet;
}
}
if ($nonce < $last_nonce) {
print "sum = $sum\n";
dumpGraySums($gray_bets);
$sum = 0;
resetGrayBets($gray_bets);
}
$last_nonce = $nonce;
}
dumpGraySums($gray_bets);
Running this against nakowa.txt from just-dice.com there is no discernible favouring of certain bits over others.
So if there is some evil crypto genious behind nakowa's streak, this isn't the angle.
Edit: updated PHP code - bug.