#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;

# Testing interoperability with CryptX

BEGIN {
    if ( ! $ENV{RELEASE_TESTING} ) {
    plan skip_all => 'tests tests are for release candidate testing';
  } elsif ( eval {
    require Crypt::PK::DSA;
    defined $CryptX::VERSION && $CryptX::VERSION >= 0.014;
    } ) {
    plan tests => 4 + 1;
  } else {
    plan skip_all => 'Requires CryptX version 0.014 or later';
  }
}

use Crypt::DSA::GMP;
use Crypt::DSA::GMP::Util qw/bin2mp mp2bin/;
use Math::BigInt try => "GMP";

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 $dsa1 = Crypt::PK::DSA->new();
  # Work around CryptX RT 89308
  while (1) {
    $dsa1->generate_key(20, 48);
    my $pkhash = $dsa1->key2hash;
    last if Math::BigInt->from_hex('0x'.$pkhash->{x}) < Math::BigInt->from_hex('0x'.$pkhash->{q});
  }
  # sigobj1 is just a blob of ASN1 encoded r/s
  my $sigobj1 = $dsa1->sign_message($message);
  $valid = $dsa1->verify_message($sigobj1, $message);
  my $sig1;
  {
    require Crypt::DSA::GMP::Signature;
    my $sigtemp = Crypt::DSA::GMP::Signature->new(Content => $sigobj1);
    $sig1 = [ $sigtemp->r, $sigtemp->s ];
  }
  ok($valid, "Crypt::PK::DSA (CryptX) isn't obviously broken");

  my $dsa2 = Crypt::DSA::GMP->new;
  my $pkhash = $dsa1->key2hash;
  my $key2 = $dsa2->keyset( p => '0x' . $pkhash->{p},
                            q => '0x' . $pkhash->{q},
                            g => '0x' . $pkhash->{g},
                            x => '0x' . $pkhash->{x},
                            y => '0x' . $pkhash->{y}, );
  my $sigobj2 = $dsa2->sign(Key=>$key2,Message=>$message);
  my $sig2 = [$sigobj2->r, $sigobj2->s];
  $valid = $dsa2->verify(Key=>$key2,Message=>$message,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,Message=>$message,Signature=>$sig);
    ok($valid, "Crypt::DSA::GMP can verify using the CryptX signature");
  }
  {
    my $sig = $sigobj2->serialize;
    $valid = $dsa1->verify_message($sig, $message);
    ok($valid, "CryptX can verify using the Crypt::DSA::GMP signature");
  }
}

# Test PEM and DER
{
  my $pk = Crypt::PK::DSA->new();
  while (1) { # Work around CryptX RT 89308
    $pk->generate_key(20, 48);
    my $pkhash = $pk->key2hash;
    last if Math::BigInt->from_hex($pkhash->{x}) < Math::BigInt->from_hex($pkhash->{q});
  }

  my $private_der = $pk->export_key_der('private');
  my $public_der = $pk->export_key_der('public');
  my $private_pem = $pk->export_key_pem('private');
  my $public_pem = $pk->export_key_pem('public');

  my $key_pem_priv = Crypt::DSA::GMP::Key->new( Type => 'PEM', Content => $private_pem );

  ok($key_pem_priv, "PEM private key imported");

  # Libtomcrypt uses a different format (sigh).  Rather than:
  #      Sequence(
  #         ObjId OBJECT IDENTIFIER,
  #         Sequence(
  #            p INTEGER
  #            q INTEGER
  #            g INTEGER
  #         )
  #      )
  #      pub_key BIT STRING
  # it uses:
  #      Sequence(
  #         pub_key INTEGER,
  #         Sequence(
  #            p INTEGER
  #            q INTEGER
  #            g INTEGER
  #         )
  #      )
  #
  #my $key_pem_pub  = Crypt::DSA::GMP::Key->new( Type => 'PEM', Content => $public_pem );
  #ok($key_pem_pub , "PEM public key imported");

  # TODO: make this
  #my $key_der_priv = Crypt::DSA::GMP::Key->new( Type => 'DER', Content => $private_der );
  #my $key_der_pub  = Crypt::DSA::GMP::Key->new( Type => 'DER', Content => $public_der );
  #ok($key_der_priv, "DER private key imported");
  #ok($key_der_pub , "DER public key imported");
}
