#! /usr/bin/perl

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common;
use HTTP::Cookies;
use Math::BigInt lib => 'GMP';
use Digest::SHA1 qw(sha1_hex);

$| = 1; # autoflush status information

my $testserver = 0;
my $wahlkreis = shift;
unless($wahlkreis) {
  print "Benutzung: $0 Wahlkreisnummer Datei_Mit_Wahlbenachrichtigung\n";
  exit(1);
}
print "Einlesen der Wahlbenachrichtigung.\n";
my $wahlbenachrichtigung = join("", (<>));
my $wahlscheinanzahl = 40;

print "Initialisierung.\n";

# Daten des Wahlservers.
my $wahlserver = 'https://wahlschein.netzwahltag.de/wahlserver.php';
my $n = Math::BigInt->new("28338146611458529207062459474789299928427869838618783132953389394110241912516937650150519933810590222294473428647654497229298865187995806535000234099432532925974062985593318789621793354917585828178912914345499494538994081571788913529064624515433252517251028378238263151925941126132343420810598875754847275249344779313639655082614889357306534608388195699723591118348014671344710843895712621705369069522321470193254878702954528775274057956819881360157563910799048358745132007726763778043015473028729891919753720607106238845438729504824733191401359015547302598967597864736805046427481139329849225060710902025963111572763");
my $e = Math::BigInt->new("65537");

if($testserver) {
  # Daten des Testservers.
  $wahlserver = 'http://www.netzwahltag.de/testserver.php';
  $n = Math::BigInt->new("24237754553483255475536157516502463970333077002286613024097759070643491354150918169897901058642185097766538470691423514620589669362868190821316114289836212340473158025475747068812648312792239080097043228788050676207594163981964817121378570753857127852936234389027228782165681918655799356455664299342947789301740500995807880015889239515152730959319066653293077769983821774973220484062801672890431507405550594529452986795828448338149531500303084544467990934848891846404436044805231337759379482665257126132423026481865494460622947232774109126499198064882718447779415014467972606716663398469329360205104733017045718079383");
  $e = Math::BigInt->new("65537");
}

# Gibt einen String mit zufälligen $bits Bits.
# Er ist auf volle 32 bit gepadded.
sub randbits($) {
  my $bits = shift;
  local $_ = "";
  while($bits >= 32) {
    $_ .= pack("L",rand(2**32));
    $bits -= 32;
  }
  $_ .= pack("L",rand(2**$bits));
  return $_;
}

my $ua = LWP::UserAgent->new;
$ua->agent("Internetwahl 2005/Wahlscheinanfrager");
$ua->cookie_jar(HTTP::Cookies->new()); # Session Cookies beachten

print "Generierung der Wahlscheine.\n";
# Formularfelder ausfüllen
my %attr = ('maschine' => 'true',
            'wahlbenachrichtigung' => $wahlbenachrichtigung);

my (@sn,@r) = ((),());
for(my $i = 0; $i < $wahlscheinanzahl; $i++) {
  my ($sn,$r,$c);
  
  push(@sn, $sn = sha1_hex(randbits(160)));
  push(@r,  $r  = sha1_hex(randbits(160)));
  
  my $ticket = "[Internetwahl 2005,$sn,$wahlkreis]";
  printf "Schein %2d: %s\r", $i, $ticket;

  # PKCS#1 Padding
  my $rand = unpack("H*",randbits(2048-160-3*8));
  $rand =~ s/00/23/g;
  $c = "0002" . substr($rand,0,2*((2048-160)/8-3)) . "00" . sha1_hex($ticket);

  # Verschleierung
  $c = Math::BigInt->new("0x$c");
  $r = Math::BigInt->new("0x$r");
  $r->bmodpow($e,$n);
  $c->bmul($r);
  $c->bmod($n);
  $attr{"c$i"} = substr($c->as_hex(),2); # Reiner Hexstring als Parameter
}
print ' 'x78,"\r";

print "Übertragung der Wahlscheine\n";
my $res = $ua->request(POST $wahlserver, \%attr);

die "Übertragung fehlgeschlagen: ".$res->status_line."\n".$res->content."\n"
  unless $res->is_success && $res->content =~ /^(\d+)$/;

my $geheim = $1;
print "Aufdeckung aller Wahlscheine bis auf Nummer $geheim\n";

%attr = ('maschine' => 'true');
for(my $i = 0; $i <= $#sn; $i++) {
  next if $i == $geheim;
  $attr{"SN$i"} = $sn[$i];
  $attr{"r$i"}  = $r[$i];
}

print "Übertragung der Aufdeckungsdaten\n";
$res = $ua->request(POST $wahlserver, \%attr);

die "Übertragung fehlgeschlagen: ".$res->status_line."\n".$res->content."\n"
  unless $res->is_success && $res->content =~ /^OK (\S+)/;

print "\n\tDer Wahlschein wurde signiert!\n\n";
print "Entschleiere Signatur.\n";
my $signatur = Math::BigInt->new("0x$1");
my $r = Math::BigInt->new("0x$r[$geheim]");
$r->bmodinv($n);
$signatur->bmul($r);
$signatur->bmod($n);
my $sighex = substr($signatur->as_hex(),2);

print "Überprüfe Signatur.\n";
$signatur->bmodpow($e,$n);
my $ticket = "[Internetwahl 2005,$sn[$geheim],$wahlkreis]";
print "Schein: $ticket\n";
my $hash = sha1_hex($ticket);
if($signatur->as_hex() =~ /00$hash$/) {
  print "Signatur verifiziert.\n";
} else {
  warn "Signatur fehlerhaft!\n";
}

print "\nBitte speichern Sie die folgenden Angaben für Ihre Stimmabgabe!\n";
print "Wahlschein: $sn[$geheim]\n";
print "Signatur: $sighex\n\n";


