#!/usr/bin/perl -w use strict; use CGI qw(-debug); use CGI::Carp qw(fatalsToBrowser); use DBI; use lib "/usr/home/john/perl/elections"; use ModToolkit qw( $bignumlib readkey rsa_decode ); use vars qw($q $dbh $libdir $keyfile); $libdir = "/usr/local/lib/elections"; $keyfile = "1024bit"; my $dsn = "DBI:Pg:dbname=vote"; my $dbusername = undef; my $dbpasswd = undef; $dbh = DBI->connect( $dsn, $dbusername, $dbpasswd, { RaiseError => 1, AutoCommit => 0 } ) or die "Cannot connect to db: $DBI::errstr\n"; $q = new CGI; if ( $q->param('ballotnr') ) { process_form(); } else { present_form(); } sub present_form { print $q->header, $q->start_html( -title => 'Enter your ballot', -BGCOLOR => 'white', ), $q->h1('Enter your ballot'), "\n", $q->hr, $q->start_form, "Ballot number: ", $q->textfield( -name => 'ballotnr', -size => '16', ), $q->br, "\n", "Signature:\n", $q->textarea( -name => 'signature', -rows => 6, -columns => 75, ), $q->hr, "\n"; # get candidates from database my $candidates = $dbh->selectcol_arrayref( q{ SELECT name FROM candidates } ); # commit, to end (read-only) transaction $dbh->commit; print "I vote for:", $q->br, "\n", $q->blockquote( $q->checkbox_group( -name => 'candidates', -values => $candidates, -linebreak => 1, ) ), $q->hr, "\n", $q->submit, $q->end_form, $q->hr, "\n", $q->end_html; } sub process_form { # get public key my($n, $e) = readkey("$libdir/$keyfile"); # get ballot number from webpage my $bn = $q->param('ballotnr'); # extract numeric signature from webpage my $sig = $q->param('signature'); $sig =~ tr/+0-9//cd; $sig = $bignumlib->new($sig); # signature is nothing but the message, rsa-encoded with the private key, # so decode it now. my $msg = rsa_decode($n, $e, $sig); if ( $msg !~ /^Ballot number: ([0-9a-fA-F]{16})$/ ) { error("You entered an invalid signature."); } # make sure the ballot number matches if ( $1 ne $bn ) { error("Your ballot number does not match the signature"); } # We should have voted for three candidates my @candidates = $q->param('candidates'); if ( @candidates != 3 ) { error("You should vote for exactly three candidates.\n"); } # Insert votenr into database. This dies if user tries it twice. eval { $dbh->do( q{ INSERT INTO votenr (nr) VALUES (?) }, undef, $bn) } or error("You cannot vote twice"); # get id of inserted ballotnr my($vote) = $dbh->selectrow_array( q{ SELECT currval('votenr_id_seq') } ); # insert votes into database for my $c ( @candidates ) { # get candidate ID my($candidate) = $dbh->selectrow_array( q{ SELECT id FROM candidates WHERE name = ? }, undef, $c ); if ( ! defined $candidate ) { $dbh->rollback; error("Why do you think you can vote for $c?"); } $dbh->do( q{ INSERT INTO vote (vote, candidate) VALUES (?,?) }, undef, $vote, $candidate ); } # inserted all candidates, commit transaction $dbh->commit; print $q->header, $q->start_html(-title => 'You voted!', -BGCOLOR => 'white'), $q->h1('You voted!'), $q->p("You voted for @candidates. ", "Your vote is listed under number $bn."), $q->end_html; } sub error { my $msg = shift; print $q->header, $q->start_html(-title => 'Error', -BGCOLOR => 'white'), $q->h1('Error'), $q->p($msg), $q->end_html; exit 0; }