summaryrefslogtreecommitdiff
path: root/test.c
diff options
context:
space:
mode:
Diffstat (limited to 'test.c')
-rw-r--r--test.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..e80a7d1
--- /dev/null
+++ b/test.c
@@ -0,0 +1,678 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "types.h"
+#include "rmd160.h"
+#include "bigdeal.h"
+#include "mp.h"
+#include "binomial.h"
+#include "output.h"
+#include "os.h"
+#include "collect.h"
+
+static char rcsid[] = "$Header: /home/sater/bridge/bigdeal/RCS/main.c,v 1.26 2000/09/08 05:13:44 sater Exp $";
+
+#define MESSLEN 100 /* Length of input buffer(s) */
+#define ENTROPY_TO_COLLECT RMDsize*4/3 /* 33% extra safety/paranoia */
+
+progparams_t parameters; /* Program parameters go in here */
+FILE *flog; /* Not used in safe version */
+
+static int
+readline(FILE *ifile, char *buf, int len)
+/*
+ * Read line into buf
+ * Check for length, discard trailing \n
+ */
+{
+ char *eol;
+
+ if(fgets(buf, len, ifile) == NULL)
+ return 0;
+ eol = strchr(buf, '\n');
+ if (eol)
+ *eol = 0;
+ return 1;
+}
+
+/*
+ * Section that handles initialization file
+ * Currently in use for hand format(s)
+ *
+ * File contains keyword=value lines
+ * The init_file[] array contains default values, overwritten by file
+ */
+
+static char init_header[] = "[BigDeal]";
+
+char default_formats[MESSLEN] = "dup,pbn";
+char default_askformats[MESSLEN] = "no";
+char default_owner[MESSLEN] = "Identification of owner or tournament";
+
+#define LONGESTKEYWORD 20 /* Extra bytes to read for keyword and = */
+struct ifrecord {
+ char *if_keyword;
+ char *if_value;
+} init_file[] = {
+ { "formats", default_formats },
+ { "askformats", default_askformats },
+ { "owner", default_owner },
+ { 0 }
+};
+
+void
+write_init_file(char *ifname)
+/*
+ * Prompt for defaults, and write init_file
+ */
+{
+ FILE *ifile;
+ char buf1[MESSLEN], buf2[MESSLEN];
+ struct ifrecord *ifrp;
+
+ printf("This program can generate various hand formats, one or more per run:\n\n");
+ output_help();
+ do {
+ printf("Give comma separated list of formats usually desired: [%s] ",
+ default_formats);
+ readline(stdin, buf1, MESSLEN);
+ if (buf1[0] == 0)
+ strcpy(buf1, default_formats);
+ strcpy(buf2, buf1);
+ } while (output_specify_formats(buf2, 0)==0);
+ strcpy(default_formats, buf1);
+
+ printf("\nNormally you will always use the format(s) just specified,\n");
+ printf("but maybe you would like to change it for some runs.\n");
+ printf("Do you want the program to reconfirm the format every run? [%s] ", default_askformats);
+ readline(stdin, buf1, MESSLEN);
+ if (buf1[0])
+ strcpy(default_askformats, buf1[0] == 'y' ? "yes" : "no");
+
+ printf("\nIf you give an identification string the program will ensure\n");
+ printf("that nobody with a different identification can generate the\n");
+ printf("same sets of deals as you\n");
+ printf("identication? [%s]\n? ", default_owner);
+ readline(stdin, buf1, MESSLEN);
+ if (buf1[0])
+ strcpy(default_owner, buf1);
+
+ ifile = fopen(ifname, "w");
+ fprintf(ifile, "%s\n", init_header);
+ for (ifrp=init_file; ifrp->if_keyword; ifrp++) {
+ fprintf(ifile, "%s=%s\n", ifrp->if_keyword, ifrp->if_value);
+ }
+ fclose(ifile);
+}
+
+void
+read_init_file(char *ifname, int write_when_absent)
+/*
+ * Read init file
+ */
+{
+ FILE *ifile;
+ char buf[MESSLEN+LONGESTKEYWORD];
+ char *eqptr;
+ struct ifrecord *ifrp;
+
+ ifile = fopen(ifname, "r");
+ if (ifile == NULL) {
+ if (write_when_absent)
+ write_init_file(ifname);
+ return;
+ }
+ while (readline(ifile, buf, MESSLEN+LONGESTKEYWORD)) {
+ if (buf[0] == 0)
+ continue; /* empty line */
+ if (strcmp(buf, init_header)==0)
+ continue; /* header for Windows routines */
+ if (buf[0] == '[')
+ break; /* end of our stuff */
+ eqptr = strchr(buf, '=');
+ if (eqptr == 0) {
+ fprintf(stderr, "Line '%s' does not contain =\n", buf);
+ fprintf(stderr, "Suggest rerun program with -R flag\n");
+ continue;
+ }
+ *eqptr++ = 0;
+ for (ifrp=init_file; ifrp->if_keyword; ifrp++) {
+ if (strcmp(ifrp->if_keyword, buf)==0) {
+ strcpy(ifrp->if_value, eqptr);
+ break;
+ }
+ }
+ if (!ifrp->if_keyword) {
+ fprintf(stderr, "Keyword %s in init_file unknown\n", buf);
+ fprintf(stderr, "Suggest rerun program with -R flag\n");
+ }
+ }
+ fclose(ifile);
+}
+
+#ifndef BIGDEALX
+/*
+ * All parameters for the program when it is running in binary or safe mode
+ * In this mode the program should be as idiot proof as possible
+ */
+#define OPTION_STRING "f:n:p:R"
+#define USAGE_STRING "[-n number-of-deals] [-p outputfile-prefix] [-f output-format-list] [-R(re-init)]"
+#define MAXDEALS 100 /* No more than 100 deals in standard prog */
+#define VERSION_COMMENT ""
+
+#else /* BIGDEALX */
+/*
+ * Parameters for the other mode. This is the hacker mode where the user
+ * can tinkle with entropy and other operations that increase the likelyhood
+ * of the program generating something else than a random, never occurred
+ * before set of deals.
+ */
+#define OPTION_STRING "e:E:f:h:n:op:R"
+#define USAGE_STRING "[-n nr-of-deals] [-p ofile-prefx] [-f o-format-list] [-e entropy-str] [-E entropy-file] [-o(only entropy from command line)] [-h hash] [-R(re-init)]"
+#define MAXDEALS 1000000000
+#define VERSION_COMMENT "(Extended version, not recommended for tournament use)"
+
+#define HISTNAME "dealentr.txt"
+#define LOGNAME "deallog.txt"
+
+/*
+ ****************************************************************************
+ * Begin section of code for other mode only
+ ****************************************************************************
+ */
+
+static void
+checkduphash(char *hash)
+/*
+ * Paranoia function: if in test we ever run into the same 160 bit number again
+ * it is time to reevaluate our assumptions
+ */
+{
+ FILE *fhist;
+ char oldhash[MESSLEN];
+ int hashlen;
+ int counter, badentry;
+
+ hashlen = strlen(hash);
+ fhist = fopen(HISTNAME, "a+");
+ if (fhist == NULL) {
+ fprintf(stderr, "Couldn't open %s\n", HISTNAME);
+ return;
+ }
+ counter = 0;
+ badentry = 0;
+ fseek(fhist, 0L, 0);
+ while (readline(fhist, oldhash, MESSLEN)) {
+ if (strlen(oldhash) != hashlen) {
+ badentry++;
+ continue;
+ }
+ if (strcmp(oldhash, hash) == 0) {
+ fprintf(flog, "Panic: same hash, index %d\n", counter);
+ fprintf(stderr, "Panic: same hash, index %d\n", counter);
+ exit(-1);
+ }
+ counter++;
+ }
+ fprintf(fhist, "%s\n", hash);
+ fclose(fhist);
+ fprintf(flog, "Checked hash against %d previous hashes\n", counter);
+ if (badentry) {
+ fprintf(flog, "The file %s contained %d bad entries\n",
+ HISTNAME, badentry);
+ }
+}
+
+static void
+read_entropy_from_file(char *fname)
+/*
+ * A frontend has generated entropy for us.
+ * Let us hope it did the right thing.
+ * We give it credit for 4 bits entropy per byte.
+ */
+{
+ FILE *fentr;
+ int c;
+
+ if (fname == 0) {
+ fprintf(stderr, "No entropy file supplied\n");
+ return;
+ }
+ fentr = fopen(fname, "r");
+ if (fentr == NULL) {
+ fprintf(stderr, "Cannot open %s\n", fname);
+ return;
+ }
+ while ((c = getc(fentr)) >= 0) {
+ collect_more((byte *) &c, sizeof(c), 4);
+ }
+ fclose(fentr);
+}
+
+static int
+hexval(char c)
+/*
+ * Convert characters '0'..'9', 'A'..'F' and 'a'..'f' to 0..15
+ */
+{
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ fprintf(stderr, "Character %c is not a hexchar\n", c);
+ exit(-1);
+}
+
+static void
+random_set(char *hashstr, byte *sr)
+/*
+ * hashstr is a 40 byte string of hex characters
+ * we convert it into a 20 byte(160 bit) binary value
+ */
+{
+ int i;
+
+ if (strlen(hashstr) != 2*RMDbytes) {
+ fprintf(stderr, "Argument %s should be %d characters long\n",
+ hashstr, 2*RMDbytes);
+ exit(-1);
+ }
+ for (i=0; i<RMDbytes; i++)
+ sr[i] = hexval(hashstr[2*i+1]) | (hexval(hashstr[2*i])<<4);
+}
+
+/*
+ ****************************************************************************
+ * End section of code for other mode only
+ ****************************************************************************
+ */
+
+#endif /* BIGDEALX */
+
+static byte *
+RMDhash(byte *value, int length)
+/*
+ * Hashes value of length bytes using RIPEMD160
+ *
+ * Returns pointer to 20 bytes hashcode
+ */
+{
+ dword MDbuf[RMDdwords]; /* contains (A, B, C, D(, E)) */
+ dword X[16]; /* current 16 word chunk */
+ static byte hashcode[RMDbytes]; /* for final hash-value */
+ unsigned int i; /* counter */
+ int nbytes; /* bytes not yet processed */
+
+ /* initialize */
+ MDinit(MDbuf);
+
+ /* Process value in 16-word chunks */
+ for (nbytes=length; nbytes>=64; nbytes-=64) {
+ for (i=0; i<16; i++) {
+ X[i] = BYTES_TO_DWORD(value);
+ value += 4;
+ }
+ compress(MDbuf, X);
+ }
+
+ /* finish: */
+ MDfinish(MDbuf, value, length, 0);
+
+ for (i=0; i<RMDbytes; i+=4) {
+ hashcode[i] = MDbuf[i>>2]; /* implicit cast to byte */
+ hashcode[i+1] = (MDbuf[i>>2] >> 8); /* extracts the 8 least */
+ hashcode[i+2] = (MDbuf[i>>2] >> 16); /* significant bits. */
+ hashcode[i+3] = (MDbuf[i>>2] >> 24);
+ }
+
+ return (byte *)hashcode;
+}
+
+static int
+goedel(dl_num *dnp)
+/*
+ * Checks whether the contents of dnp is a number less than the number
+ * of bridge deals.
+ */
+{
+ /*
+ * This number is precomputed
+ */
+ static byte nr_bridge_deals[L] =
+ { 173,85,227,21,99,77,218,101,139,244,146,0};
+
+#ifdef BIGDEALX
+ byte a[L], b[L];
+
+ n_over_k(52,13,a);
+ n_over_k(39,13,b);
+ mp96_mul(a,a,b);
+ n_over_k(26,13,b);
+ mp96_mul(a,a,b);
+
+ /*
+ * This gives a the value of the number of bridge deals
+ * =( (52 over 13) times (39 over 13) times (26 over 13) )
+ * Now let us check our internal calculations
+ *
+ * If it is wrong we miscalculated somewhere
+ */
+
+ if (mp96_cmp(a, nr_bridge_deals) != 0) {
+ fprintf(stderr, "Miscalculation\n");
+ /*
+ * print_goedel(stderr, (dl_num*) a);
+ * print_goedel(stderr, (dl_num*) nr_bridge_deals);
+ */
+ exit(-1);
+ }
+#endif
+
+ if (mp96_cmp(dnp->dn_num, nr_bridge_deals) >= 0)
+ return 0; /* too big, not a hand number */
+ return 1;
+}
+
+extern int nrandombits;
+
+static void
+get_entropy_from_keyboard() {
+ int c, oldc;
+ int nbits;
+
+ cbreak();
+ printf("Type random characters until you are told to stop ");
+ oldc = 0;
+ do {
+ c = getchtm(&nbits);
+ if (c != oldc) {
+ /*
+ * Collect the character, assume 2 bits entropy
+ * plus what the timing supplied.
+ */
+ collect_more( (byte*)&c, sizeof(c), 2+nbits);
+ oldc = c;
+ }
+ } while (nrandombits < ENTROPY_TO_COLLECT);
+ printf("\nThat is enough\007\n");
+ cooked();
+}
+
+#ifdef BIGDEALX
+char *
+hexdump(byte *input, int len) {
+ int i = 0;
+ char *output = malloc(2*len+1);
+ char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ output[len] = 0;
+ for (i = 0; i < len; i++) {
+ output[2*i] = hex[input[i] >> 4];
+ output[2*i+1] = hex[input[i] & 0x0F];
+ }
+ return output;
+}
+#endif
+
+/*
+ * Structure with all values that get hashed for random generation
+ * This includes hash of owner identication making it impossible for
+ * another owner to generate the same series of deals
+ * Reduces 2**-160 chance to zero
+ */
+static struct {
+ byte seed_sequence[4]; /* sequence number in PRNG sequence */
+ byte seed_random[RMDbytes]; /* 160 bits collected at start */
+ byte seed_owner[RMDbytes]; /* 160 bit hash of owner ident */
+ byte seed_padding[RMDbytes]; /* padding to 64 bytes */
+} seed;
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+ char message[MESSLEN];
+ byte *hashcode;
+ unsigned long seqno;
+ dl_num dnumber;
+ char filename[MESSLEN] = "";
+ int c;
+ extern char *optarg;
+ char *formats = 0;
+#ifdef BIGDEALX
+ int only_arg_entropy = 0;
+ char *hashstr = 0;
+ int dangerous_code_used = 0;
+#else
+#define only_arg_entropy 0
+#endif
+
+ /*
+ * Say hi to operator
+ */
+ printf("Big Deal version %d.%d%s\n\n",
+ VERSION_MAJOR, VERSION_MINOR, VERSION_COMMENT);
+
+#ifdef BIGDEALX
+ flog = fopen(LOGNAME, "a");
+#endif
+ os_start();
+ collect_start();
+
+ while ((c = getopt(argc, argv, OPTION_STRING)) != -1) {
+ switch(c) {
+#ifdef BIGDEALX
+ case 'E':
+ read_entropy_from_file(optarg);
+ dangerous_code_used = 1;
+ break;
+ case 'e':
+ collect_more((byte *) optarg, strlen(optarg), 4*strlen(optarg));
+ dangerous_code_used = 1;
+ break;
+ case 'h':
+ hashstr = optarg;
+ /* fall through */
+ case 'o':
+ only_arg_entropy = 1;
+ dangerous_code_used = 1;
+ break;
+#endif
+ case 'p':
+ strncpy(filename, optarg, MESSLEN-1);
+ break;
+ case 'f':
+ formats = optarg;
+ break;
+ case 'n':
+ parameters.pp_nboards = atoi(optarg);
+ break;
+ case 'R':
+ read_init_file(os_init_file_name(), 0);
+ write_init_file(os_init_file_name());
+ exit(0);
+ case '?':
+ fprintf(stderr, "Usage: %s %s\n", argv[0], USAGE_STRING);
+ exit(-1);
+ }
+ }
+
+ if (!only_arg_entropy)
+ os_collect();
+
+ read_init_file(os_init_file_name(), 1);
+ binomial_start();
+ /*
+ * Read number of boards to generate
+ */
+ while(parameters.pp_nboards <= 0 || parameters.pp_nboards > MAXDEALS) {
+ /*
+ * If we are asked to generate more than 100 boards refuse
+ * We only have 160 bits entropy, and no one plays sessions
+ * of more than 100 boards anyhow
+ */
+ if (parameters.pp_nboards > MAXDEALS) {
+ parameters.pp_nboards = 0;
+ printf("The maximum is %d, run program again for more\n",
+ MAXDEALS);
+ }
+ printf("Number of boards to deal(1-%d): ", MAXDEALS);
+ (void) readline(stdin, message, MESSLEN);
+ parameters.pp_nboards = atoi(message);
+ /*
+ * Collect the string, no entropy assumed
+ */
+ if (!only_arg_entropy)
+ collect_more( (byte*)message, strlen(message), 0);
+ }
+
+ /*
+ * Read part of filename before the .
+ */
+ while(!legal_filename_prefix(filename)) {
+ printf("Output filename(without suffix): ");
+ (void) readline(stdin, message, MESSLEN);
+ /*
+ * Collect the string, 5 bits entropy assumed
+ */
+ if (!only_arg_entropy)
+ collect_more( (byte*)message, strlen(message), 5);
+ strcpy(filename,message);
+ }
+
+ /*
+ * Get output formats
+ */
+ if (formats == 0) {
+ /*
+ * Not specified on command line
+ */
+ if (strcmp(default_askformats, "yes")==0) {
+ printf("Hand format(s) to generate: [%s] ",
+ default_formats);
+ (void) readline(stdin, message, MESSLEN);
+ if (message[0]) {
+ strcpy(default_formats, message);
+ }
+ }
+ formats = default_formats;
+ }
+
+ /*
+ * If we do not have enough entropy collected (very likely)
+ * let the user supply it by rattling his keyboard
+ */
+ if (!only_arg_entropy && nrandombits < ENTROPY_TO_COLLECT) {
+ get_entropy_from_keyboard();
+ }
+
+
+ /*
+ * Extract the entropy into the random part of the seed
+ */
+ collect_finish(seed.seed_random);
+#ifdef BIGDEALX
+ if (nrandombits < ENTROPY_TO_COLLECT) {
+ /*
+ * Can only happen when only_arg_entropy is set
+ */
+ printf("WARNING: entropy supplied is dangerously low!!!\n");
+ }
+ if (dangerous_code_used) {
+ printf("You used features in this extended version of Big Deal\n");
+ printf("that might defeat the purpose(generating unique sequences).\n");
+ printf("Use hands for actual play only with permission from your national authority.\n\n");
+ }
+
+ /*
+ * Did someone use the -h flag ?
+ */
+ if (hashstr)
+ random_set(hashstr, seed.seed_random);
+
+ /*
+ * Start checking for duplicate hashes, and log current hash
+ */
+ for (i=0; i<RMDbytes; i++) {
+ message[2*i ] = "0123456789ABCDEF"[seed.seed_random[i]/16];
+ message[2*i+1] = "0123456789ABCDEF"[seed.seed_random[i]%16];
+ }
+ message[2*RMDbytes] = 0;
+ if (!hashstr)
+ checkduphash(message); /* If this fails .... */
+
+ fprintf(flog, "Deal %d boards to file %s, hash = %s\n",
+ parameters.pp_nboards, filename, message);
+#endif
+ (void) output_specify_formats(formats, 1);
+ output_createfiles(filename, &parameters);
+
+ /*
+ * Generate a sequence of 160 bit pseudo random numbers
+ * by running the random bits with an increasing
+ * counter through the cryptographical hash.
+ * This according to Carl Ellison's recommendation for P1363
+ *
+ * But first put hash of owner ident into seed
+ */
+ hashcode = RMDhash((byte *) default_owner, strlen(default_owner));
+ memcpy(seed.seed_owner, hashcode, RMDbytes);
+
+ seqno = 0;
+ for(i = 1; i <= parameters.pp_nboards; i++) {
+ /*
+ * Next do loop executed as long as we have the misfortune
+ * to make 96 bit numbers above the number of hands
+ */
+ do {
+ seqno++;
+ /*
+ * The next code guarantees the same sequence on
+ * litle endian and big endian machines, which
+ * makes it possible to reproduce sequences on each
+ */
+ seed.seed_sequence[0] = seqno & 0xFF;
+ seed.seed_sequence[1] = (seqno>>8) & 0xFF;
+ seed.seed_sequence[2] = (seqno>>16) & 0xFF;
+ seed.seed_sequence[3] = (seqno>>24) & 0xFF;
+ /*
+ * Run all the bits through the hash
+ */
+ hashcode = RMDhash((byte *) &seed, sizeof(seed));
+ /*
+ * Take the first L bytes(96 bits) as a candidate
+ * hand number
+ */
+ memcpy(dnumber.dn_num, hashcode, L);
+ } while(!goedel(&dnumber));
+ /*
+ * Dump the input and the output to STDERR
+ * Probably should have been done on some command line switch
+ */
+#ifdef BIGDEALX
+ fprintf(stderr, "%s %s\n", hexdump((byte *) &seed, sizeof(seed)), hexdump((byte *) hashcode, RMDbytes));
+#endif
+ /*
+ * Ok, got one
+ * Print it in all desired formats
+ */
+ output_hand(i, &dnumber);
+ }
+
+ /*
+ * Finished, close output files
+ */
+ output_closefiles();
+#ifdef BIGDEALX
+ fclose(flog);
+#endif
+
+ /*
+ * Do whatever our OS wants us to do at the end, and then exit
+ */
+ os_finish();
+ return 0;
+}