/* diffie-hellman demo
 * pesco, 2009
 */

#include <stdio.h>
#include <string.h>
#include "monty.h"


/* IO functions */

void printbignum(int n, uint32_t *X)
{
        int i;

        for(i=0; i<n; i++) {
                if (i%6 == 0)
                        printf("\t");
                printf("%.8X ", X[n-1-i]);
                if (i%6 == 5)
                        printf("\n");
        }
}

int readhexdigit(void)
{
	int c;

	for(;;) {
		c = getchar();
		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);
	}
}
	
void readbignum(int n, uint32_t *X)
{
	int i,j;

	for(i=n-1; i>=0; i--) {
		uint32_t x=0;

		for(j=0; j<8; j++) {
			x <<= 4;
			x |= readhexdigit();
		}

		X[i] = x;
	}
}

void send(int n, uint32_t *x)
{
	printf("\nsend:");
	printbignum(n, x);
}

void recv(int n, uint32_t *x)
{
	printf("\n...enter %d hex digits from peer...\n", 8*n);
	readbignum(n, x);
	printf("\nrecvd:");
	printbignum(n, x);
}


int main(int argc, char **argv)
{
        /* 1536-bit modulus from RFC 3526, little-endian */
        uint32_t N[] = {0xFFFFFFFF, 0xFFFFFFFF, 0xCA237327, 0xF1746C08, 0x4ABC9804, 0x670C354E,
                        0x7096966D, 0x9ED52907, 0x208552BB, 0x1C62F356, 0xDCA3AD96, 0x83655D23,
                        0xFD24CF5F, 0x69163FA8, 0x1C55D39A, 0x98DA4836, 0xA163BF05, 0xC2007CB8,
                        0xECE45B3D, 0x49286651, 0x7C4B1FE6, 0xAE9F2411, 0x5A899FA5, 0xEE386BFB,
                        0xF406B7ED, 0x0BFF5CB6, 0xA637ED6B, 0xF44C42E9, 0x625E7EC6, 0xE485B576,
                        0x6D51C245, 0x4FE1356D, 0xF25F1437, 0x302B0A6D, 0xCD3A431B, 0xEF9519B3,
                        0x8E3404DD, 0x514A0879, 0x3B139B22, 0x020BBEA6, 0x8A67CC74, 0x29024E08,
                        0x80DC1CD1, 0xC4C6628B, 0x2168C234, 0xC90FDAA2, 0xFFFFFFFF, 0xFFFFFFFF};
        /* 2^3072 mod N */
        uint32_t RR[] = {0x32c695e0, 0xf115d27d, 0x67478c73, 0x8e0e3e21, 0x8397f245, 0xd0ab92e1,
                         0xbcd49d68, 0xf466ee5f, 0x3b01e018, 0x8f2331b1, 0x98b5fb62, 0x7e8cd2ac,
                         0x7a58f170, 0xb9052bb4, 0xdb102d39, 0xb004a750, 0x93ae1ceb, 0x04a541ff,
                         0x8e434130, 0x07cd0a62, 0x04b9f796, 0x1c729c7e, 0x196b7e88, 0xb8fe6121,
                         0x0223b76b, 0x8e1abd78, 0xd46fec23, 0x22c296e9, 0xb270521b, 0xd62a0eea,
                         0xd4053f54, 0xdc541a4e, 0x969b7f02, 0xf8056564, 0xa87c7b37, 0x0be49647,
                         0x67984460, 0x57b59348, 0x9a36a51f, 0x102630fa, 0xcc2456ef, 0xe9c3fa02,
                         0x7929a1c7, 0xae594104, 0x6cc1ebd2, 0xee9c9a21, 0x59541c01, 0xe3b33c72};
        int n = sizeof(N) / sizeof(uint32_t);
	uint32_t R[n];
        uint32_t one[n];

        uint32_t g[n];          /* generator = 2 */
        uint32_t gR[n];         /* generator, montgomery representation */
	uint32_t a[n];          /* random number */
	uint32_t gaR[n];        /* g^a, montgomery rep */
	uint32_t gbR[n];        /* g^b, montgomery rep */
	uint32_t gabR[n];       /* g^(ab), montgomery rep */
	uint32_t gab[n];        /* shared secret: g^(ab)

	/* initialize */
        memset(one,  0, sizeof(N)); one[0] = 1;
        memset(g,    0, sizeof(N)); g[0] = 2;

	/* convert stuff to montgomery representation */
	monty_mul(n, N, R, one, RR);
        monty_mul(n, N, gR, g, RR);

	printf("mod:");
	printbignum(n, N);

	/* diffie-hellman: */
        mrand(n, N, a);                   /* generate random number modulo N */
	monty_exp(n, N, R, gaR, gR, a);   /* compute g^a */
	send(n, gaR);                     /* send g^a to bob */
	recv(n, gbR);                     /* receive g^b from bob */
	monty_exp(n, N, R, gabR, gbR, a); /* compute (g^a)^b = g^(ab) */

	/* convert result back from montgomery representation */
	monty_mul(n, N, gab, gabR, one);

	printf("\nSECRET:");
        printbignum(n, gab);

	return 0;
}
