#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use Digest::SHA qw/sha1/;

# Testing interoperability with Crypt::OpenSSL::DSA.
# Notes for that module:
#   - Everywhere it says "$message" it means "Hash($message)"
#   - Hash() must be 160 bits at most (e.g. sha1).
#   - it returns everything as blobs, we do everything with bigints

BEGIN {
  if ( ! $ENV{RELEASE_TESTING} ) {
    plan skip_all => 'tests tests are for release candidate testing';
  } elsif ( eval { require Crypt::OpenSSL::DSA; 1; } ) {
    plan tests => 4 + 4 + 4;
  } else {
    plan skip_all => 'Requires Crypt::OpenSSL::DSA';
  }
}

use Crypt::DSA::GMP;
use Crypt::DSA::GMP::Util qw/bin2mp mp2bin/;

my $message = "This prime validation algorithm is used to validate that the integers p and q were generated by the prime generation algorithm given in Appendix A.1.1.2.  The validation algorithm requires the values of p, q, domain_parameter_seed and counter, which were output from the prime generation algorithm.  Let Hash() be the hash function used to generate p and q, and let outlen be its output block length.";

{
  my $valid;
  my $hash = sha1($message);

  my $dsa1 = Crypt::OpenSSL::DSA->generate_parameters( 512 );
  $dsa1->generate_key;
  my $sigobj1 = $dsa1->do_sign($hash);
  $valid = $dsa1->do_verify($hash, $sigobj1);
  my $sig1 = [bin2mp($sigobj1->get_r), bin2mp($sigobj1->get_s)];
  ok($valid, "Crypt::OpenSSL::DSA isn't obviously broken");

  my $dsa2 = Crypt::DSA::GMP->new;
  my $key2 = $dsa2->keyset( p => bin2mp($dsa1->get_p),
                            q => bin2mp($dsa1->get_q),
                            g => bin2mp($dsa1->get_g),
                            x => bin2mp($dsa1->get_priv_key),
                            y => bin2mp($dsa1->get_pub_key), );
  my $sigobj2 = $dsa2->sign(Key=>$key2,Digest=>$hash);
  my $sig2 = [$sigobj2->r, $sigobj2->s];
  $valid = $dsa2->verify(Key=>$key2,Digest=>$hash,Signature=>$sigobj2);
  ok($valid, "Crypt::DSA::GMP isn't obviously broken");

  # Now for the real work.

  {
    my $sig = Crypt::DSA::GMP::Signature->new;
    $sig->r($sig1->[0]);   $sig->s($sig1->[1]);
    $valid = $dsa2->verify(Key=>$key2,Digest=>$hash,Signature=>$sig);
    ok($valid, "Crypt::DSA::GMP can verify using the Crypt::OpenSSL::DSA signature");
  }
  {
    my $sig = Crypt::OpenSSL::DSA::Signature->new;
    $sig->set_r(mp2bin($sig2->[0]));   $sig->set_s(mp2bin($sig2->[1]));
    $valid = $dsa1->do_verify($hash, $sig);
    ok($valid, "Crypt::OpenSSL::DSA can verify using the Crypt::DSA::GMP signature");
  }

  # File I/O public key
  {
    my $keyfile = "./dsa-key.pem";
    $dsa1->write_pub_key( $keyfile );
    my $keyf1 = Crypt::DSA::GMP::Key->new(Type => 'PEM', Filename => $keyfile);
    is( $keyf1->q, $key2->q, "OpenSSL->PEM->CDSAGMP q");
    is( $keyf1->pub_key, $key2->pub_key, "OpenSSL->PEM->CDSAGMP pub_key");
    unlink $keyfile;
    $keyf1->write(Type => 'PEM', Filename => $keyfile);
    my $dsa_pub = Crypt::OpenSSL::DSA->read_pub_key( $keyfile );
    is( bin2mp($dsa_pub->get_q), $key2->q, "CDSAGMP->PEM->OpenSSL q");
    is( bin2mp($dsa_pub->get_pub_key), $key2->pub_key, "CDSAGMP->PEM->OpenSSL pub_key");
    unlink $keyfile;
  }

  # File I/O private key
  {
    my $keyfile = "./dsa-key.pem";
    $dsa1->write_priv_key( $keyfile );
    my $keyf1 = Crypt::DSA::GMP::Key->new(Type => 'PEM', Filename => $keyfile);
    is( $keyf1->q, $key2->q, "OpenSSL->PEM->CDSAGMP q");
    is( $keyf1->priv_key, $key2->priv_key, "OpenSSL->PEM->CDSAGMP priv_key");
    unlink $keyfile;
    $keyf1->write(Type => 'PEM', Filename => $keyfile);
    my $dsa_pub = Crypt::OpenSSL::DSA->read_priv_key( $keyfile );
    is( bin2mp($dsa_pub->get_q), $key2->q, "CDSAGMP->PEM->OpenSSL q");
    is( bin2mp($dsa_pub->get_priv_key), $key2->priv_key, "CDSAGMP->PEM->OpenSSL priv_key");
    unlink $keyfile;
  }
}
