If only all code for voting machines were open source, then cheating evil vote-counting programs would be impossible. Right?
To find out, Daniel Horn funded a programming contest in 2009. Here are 3 vote-counting programs in the "C" language. The first is the allegedly-correct program by Horn himself, intended simply as a baseline to show the contestants what their programs ought to do. (Actually, Horn's program isn't really correct... how many problems with it do you see?)
/**** Daniel Horn's Vote Counter Demo Program in C language.
Rules: Contest entry programs must be written in C, C++ or D (with gcc backend, on x86),
and must compile with gcc-2.95.3 (debian stable x86), gcc-3.3.x (debian unstable, x86) or
gcc-3.4 (debian stable x86), or it may compile on gcc-3.1 or gcc-3.3 on Mac OS X.
Your program may not reference libraries outside of the standard C library (or phobos for D).
Your program must input a string of characters.
Characters that result in isspace() returning true are blank or ballots with pregnant chads
and will not be counted.
Character 'K' (capital only) counts as a vote for Kerry,
Character 'B' (capital only) counts as a vote for Bush
Character 'N' (capital only) counts as a vote for Nader
Other characters are tallied in the 'Other' column.
The output must have the candidate's last name (or Other for votes not belonging to
the candidate) a colon, a space, then a count, and a newline for each candidate in
the order listed above.
******/
#include <stdio.h>
#include <ctype.h>
int main(){
int Input;
unsigned long total=0;
unsigned long Tally[256]={0};
while( (Input=getchar())!=EOF ){
unsigned char Vote=Input;
if( !isspace(Vote) ){
Tally[Input] += 1;
total += 1;
}
}
printf("Kerry %d\n",Tally['K']);
printf("Bush %d\n",Tally['B']);
printf("Nader %d\n",Tally['N']);
printf("Other %d\n",total-Tally['K']-Tally['B']-Tally['N']);
return 0;
}
|
Next is a program by Paul A. Parkanzky. On November 1 the votes are counted correctly. On November 2 the total is correct, but one candidate magically obtains extra votes at another's expense...:
/*** Author: Paul A. Parkanzky (firstname.lastname@gmail.com)
* "I am committed to helping Ohio deliver its electoral
* votes to the president next year"
* -- Walden W. O'Dell (CEO of Diebold, Inc)
*
* Works with gcc3.2 on x86 SuSE, gcc3.3 on x86 Gentoo and gcc3.2 on x86 Redhat.
* Does not work with gcc2.95 on x86 NetBSD or sparc FreeBSD.
* I haven't tried it on any other compiler/OS combinations. ****/
#include<stdio.h>
#include<ctype.h>
#include<time.h>
void printResults(unsigned int *);
void paperTrail(char *);
char *getDay();
int main(){
unsigned int Tally[4] = {0};
unsigned char Other;
unsigned char Nader;
unsigned char Bush;
unsigned char Kerry;
char LogMesg[11] = {0};
int Input;
char *day;
day = getDay();
Bush = 0;
Kerry = 1;
Nader = 2;
Other = 3;
while((Input=getchar())!=EOF){
unsigned char Vote=Input;
/* Log to paper receipt */
sprintf (LogMesg,"LOG VOTE: November %s %c\n",day,Vote);
paperTrail(LogMesg);
/* Add 1 */
if (!isspace(Vote)){
switch (Vote) {
case 'B':
Tally[Bush]++;
break;
case 'K':
Tally[Kerry]++;
break;
case 'N':
Tally[Nader]++;
break;
default:
Tally[Other]++;
break;
}}}
printResults(Tally);
return 0;
}
/*** This gets a day string for the paper trail log message.
* ("first","second","third" or "ith") ***/
char *getDay(){
char *retVal;
time_t now_time;
struct tm *now;
now_time = time(NULL);
now = localtime(&now_time);
if (now->tm_mday == 1) {
retVal = "first";
} else if (now->tm_mday == 2) {
retVal = "second";
} else if (now->tm_mday == 3) {
retVal = "third";
} else {
retVal = (char *)malloc(7);
sprintf(retVal,"%ith",now->tm_mday);
}
return retVal;
}
/**** Print the results: ****/
void printResults(unsigned int *Tally) {
unsigned char Bush = 0;
unsigned char Kerry = 1;
unsigned char Nader = 2;
unsigned char Other = 3;
printf("Kerry: %d\n",Tally[Kerry]);
printf("Bush: %d\n",Tally[Bush]);
printf("Nader: %d\n",Tally[Nader]);
printf("Other: %d\n",Tally[Other]);
}
/*** This will print a log of the vote to a paper receipt when
* the supporting hardware is added. ****/
void paperTrail(char *mesg){
/* Log to paper trail for extra legitimacy! */
}
|
And here is one by Michal Zalewski. Guess what it does?
/* ------------------------------------
* Bullet-Proof Extensible Vote Counter
* ------------------------------------
*
* In response to Daniel Horn's abominable, sick and twisted idea of running
* a contest for programs that skew election results, I decided to counter
* his efforts by providing this program that not only counts votes perfectly
* well, but also goes great lengths to verify data integrity in case of
* unforseen flaws or tampering attempts.
* I did not rely on his proposed vote counting framework, noting that
* first and foremost, it could be already backdoored, and second, that
* it does not scale well.
* -- Michal Zalewski *******/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
/* Error reporting macro */
#define fatal(x) do { fprintf(stderr,"ERROR: %s\n",x); exit(1); } while (0)
#define CHECK_INTERVAL10 /* How often to check database? */
#define VOTE_LIMIT 300000000 /* Maximum number of votes possible */
/* Candidates and vote count. Can be extended and modified at will.
The last entry should be NULL, reserved for "other". */
struct candidate { char* name; unsigned int votes; } tally[] = {
{ "Bush" , 0 },
{ "Kerry", 0 },
{ "Nader", 0 },
{ NULL , 0 }
};
/* Total vote count - used to validate individual vote counts. */
unsigned int total = 0;
/* Display all results, including the trailing NULL ("other"). */
void show_results(void) {
struct candidate* t = tally;
do printf("%-8s : %u\n", t->name ? t->name : "Other", t->votes);
while ((t++)->name);
}
/* Safely count vote. Every CHECK_INTERVAL votes, run a database integrity
check to detect corrupted vote counts or candidate names; otherwise,
just count the vote. */
#define VOTE_AND_CHECK(v) do { \
if (!(total % CHECK_INTERVAL)) { \
struct candidate* t = tally; \
unsigned int cur_tot = 0; \
/* Verify that votes add up to the right count... */ \
do cur_tot += t->votes; while ((t++)->name); \
if (cur_tot != total) fatal("Vote count tampering!"); \
/* Go back through entries and verify every name... */ \
t--; \
while ((--t) != tally) \
if (!t->name || !isupper(*t->name)) fatal("Candidate tampering!"); \
/* Count vote, checking for erratic numbers */ \
if ((v)++ >= VOTE_LIMIT) fatal("Too many voters!"); \
} else (v)++; \
total++; \
} while (0)
/* Add one vote for candidate whose initial is given by c. If no
matching candidate found, account as "other". */
void process_vote(unsigned char c) {
struct candidate* t = tally;
while (t->name) {
if (t->name[0] == c) {
VOTE_AND_CHECK(t->votes);
return;
}
t++;
}
VOTE_AND_CHECK(t->votes);
}
/* Run the elections! */
int main(int argc,char** argv) {
int c;
/* Read and process votes */
while ((c=getchar()) != EOF)
if (!isspace(c)) process_vote(c);
/* And the winner is... */
show_results();
return 0;
}
|