/* passcard2 - Generate a password grid (http://www.vvsss.com/grid/)
 * using a text as pseudo-random number generator seed.
 * The PRNG is a modified version of the one provided in man 3 rand
 * and might not be really good, but it should be sufficient.
 * The seed is build with Alder32 of the text + 32 times xored text
 * (with hand picked values).
 * It has been tested on x86 and x86_64 and gives the same result
 * with the same input
 *
 * TODO Gives password grid size as argument to the program
 *
 * Etienne Bagnoud (c) 2009 <tchetch@tchetch.net>
 *
 *             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *                     Version 2, December 2004
 *
 * Copyright (C) 2004 Sam Hocevar
 * 14 rue de Plaisance, 75014 Paris, France
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as 
 * long as the name is changed.
 *
 *           DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 *
 * 0. You just DO WHAT THE FUCK YOU WANT TO. 
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define HEIGHT			10
#define WIDTH			26
#define MY_RAND_MAX 	32767
#define MOD_ALDER		65521

/* To xor encrypt text 32 times
 * Values choosen randomly by human being
 */
#define XOR_PASSN		32	
#define XOR_PASS1		0x4E
#define	XOR_PASS2		0x39
#define XOR_PASS3		0xA7
#define XOR_PASS4		0x8D
#define XOR_PASS5		0x19
#define XOR_PASS6		0x5C
#define XOR_PASS7		0xE5
#define XOR_PASS8		0x98
#define XOR_PASS9		0x16
#define XOR_PASS10		0xB8
#define XOR_PASS11		0x4C
#define XOR_PASS12		0x9F
#define XOR_PASS13		0xEE
#define XOR_PASS14		0x43
#define XOR_PASS15		0x87
#define XOR_PASS16		0x26
#define XOR_PASS17		0xD7
#define	XOR_PASS18		0x3F
#define XOR_PASS19		0x7E
#define XOR_PASS20		0xCD
#define XOR_PASS21		0x37
#define XOR_PASS22		0xF1
#define XOR_PASS23		0xBB
#define XOR_PASS24		0x6B
#define XOR_PASS25		0x05
#define XOR_PASS26		0xC0
#define XOR_PASS27		0x44
#define XOR_PASS28		0x6C
#define XOR_PASS29		0x11
#define XOR_PASS30		0x03
#define XOR_PASS31		0x99
#define XOR_PASS32		0x3F


#define MAX_ALLOC_SIZE	0xFFFF	/* Limit 65 kB of text */

/* From rand(3) manpage
 * Change to uint64_t to fix the size and get the same result on
 * both 32 bits and 64 bits machines. I think it's right, correct
 * me if not.
 */
static uint64_t next = 1;

uint64_t myrand(void) {
	    next = next * 1103515245 + 12345;
		    return((uint64_t)(next/65536) % 
					MY_RAND_MAX);
}
void mysrand(uint64_t seed) {
	    next = seed;
}

/* The 32 pass xor are added because Alder32 is said to be weak
 * with "few hundred bytes", we just add more data. There's no
 * security feature here.
 */
int main(int argc, char ** argv)
{
	char matrix[HEIGHT][WIDTH];
	const int asciiMin = 33, asciiMax=126;
	uint32_t a = 1, b = 0, id = 0;
	int j = 0, k = 0, txtLen = 0, i = 0;
	char c = 0;
	size_t allocSize = 0;
	int loopCounter=0;
	char * txt;
	uint8_t xor_e[XOR_PASSN] = {
	   	XOR_PASS1,
		XOR_PASS2,
	   	XOR_PASS3,
		XOR_PASS4,
		XOR_PASS5,
		XOR_PASS6,
		XOR_PASS7,
		XOR_PASS8,
		XOR_PASS9,
		XOR_PASS10,
		XOR_PASS11,
		XOR_PASS12,
		XOR_PASS13,
		XOR_PASS14,
		XOR_PASS15,
		XOR_PASS16,
	   	XOR_PASS17,
		XOR_PASS18,
	   	XOR_PASS19,
		XOR_PASS20,
		XOR_PASS21,
		XOR_PASS22,
		XOR_PASS23,
		XOR_PASS24,
		XOR_PASS25,
		XOR_PASS26,
		XOR_PASS27,
		XOR_PASS28,
		XOR_PASS29,
		XOR_PASS30,
		XOR_PASS31,
		XOR_PASS32
	};

	if(argc != 2)
		exit(-1);

	txtLen = strlen(argv[1]);

	if(txtLen > 0) {
		allocSize = (XOR_PASSN+1) * txtLen * (sizeof * txt);
		fprintf(stderr, "ALLOC::%u bytes\n", (unsigned int)allocSize);

		if(allocSize < MAX_ALLOC_SIZE) {
			txt = malloc(allocSize);
			if(txt != NULL) {
				memcpy(txt, argv[1], txtLen);

				for(i=0; i<XOR_PASSN;i++) {
					k = j * i + txtLen;
					for(j=0;j<txtLen;j++) {
						*(txt + k) = argv[1][j] ^ xor_e[i];
					}
				}

				/* Alder32, inspired from
				 * http://en.wikipedia.org/wiki/Adler-32
				 */
				for(i=0;i<=(txtLen*(XOR_PASSN+1));i++) {
					a = (a + (uint8_t)*(txt + i)) % MOD_ALDER;
					b = (b + a) % MOD_ALDER;
				}
				id = (b << 16) | a;
				free(txt);
			}
		}
	}

	mysrand(id);
	for(j=0;j<HEIGHT;j++){
		for(k=0;k<WIDTH;k++){
			/* For convenience, no 0, 1, O, o l
			 * http://www.vvsss.com/grid/
			 */
			loopCounter = 0;
			do {
				c = (char) asciiMin + myrand() / (MY_RAND_MAX / (asciiMax -
						   	asciiMin + 1) + 1);

				 /* Don't loop too long */
				loopCounter++; if(loopCounter>20) break;
			} while(c=='o'||c=='O'||c=='l'||c=='1'||c=='0');
			matrix[j][k] = c;
		}
	}

	/* Output */
	printf("%u\n", id);
	for(j=0;j<HEIGHT;j++){
		loopCounter=0;
		for(k=0;k<WIDTH;k++){
			if(loopCounter!=0)
				printf(",");
			printf("%u", matrix[j][k]);
			loopCounter=1;
		}
		printf("\n");
	}
	printf("\n");

	return 0;
}
