From 3fe24df1237a77549ba64c6331383a9a40aed1de Mon Sep 17 00:00:00 2001 From: emkael Date: Sat, 25 Nov 2017 16:15:54 +0100 Subject: Initial BigDeal sources --- main.c | 655 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c new file mode 100644 index 0000000..bd3e299 --- /dev/null +++ b/main.c @@ -0,0 +1,655 @@ +#include +#include +#include + +#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=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>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(); +} + +/* + * 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 */ +} 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>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)); + /* + * 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; +} -- cgit v1.2.3