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; } |