use strict;
use warnings;
use Test::More;
use Crypt::DSA;
use Crypt::DSA::Util qw( bin2mp );
use Digest::SHA qw( sha1 );
use Math::BigInt;
#
# Crypt::DSA k-reuse (DSA nonce reuse) -> full private-key recovery test.

my $dsa = Crypt::DSA->new();
isa_ok($dsa, 'Crypt::DSA');

my $key = $dsa->keygen( Size => 512 );

my $msg1 = "transfer \$10 to alice";
my $msg2 = "transfer \$10000 to mallory";

my $sig1 = $dsa->sign( Message => $msg1, Key => $key );
my $sig2 = $dsa->sign( Message => $msg2, Key => $key );

my $q = Math::BigInt->new( $key->q->bstr );
my $r1 = Math::BigInt->new( $sig1->r->bstr );
my $r2 = Math::BigInt->new( $sig2->r->bstr );
my $s1 = Math::BigInt->new( $sig1->s->bstr );
my $s2 = Math::BigInt->new( $sig2->s->bstr );

ok ( $r1 != $r2, "nonce k was regenerated per signature.");

# message representatives, exactly as sign() computes them:
#   m = bin2mp(sha1(Message))   (no reduction; final result is mod q)
my $m1 = Math::BigInt->new( bin2mp( sha1($msg1) )->bstr );
my $m2 = Math::BigInt->new( bin2mp( sha1($msg2) )->bstr );

# k = (m1 - m2) * (s1 - s2)^-1 mod q
my $num = ( $m1 - $m2 ) % $q;
my $den = ( ( $s1 - $s2 ) % $q )->bmodinv($q);
my $k   = ( $num * $den ) % $q;

# x = (s1*k - m1) * r^-1 mod q
my $rinv = $r1->copy->bmodinv($q);
my $x    = ( ( ( $s1 * $k - $m1 ) % $q ) * $rinv ) % $q;
$x += $q while $x < 0;

my $real_x = Math::BigInt->new( $key->priv_key->bstr );

ok ( $x ne $real_x, "Recovered private key does not match the real private key");

done_testing()
