#include <sys/param.h>
#include <sys/time.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/des.h>
#include <openssl/err.h>
#include "pk.h"
static void adjust(char keyout[HEXKEYBYTES+1], char *keyin);
static void
extractideakey(BIGNUM *ck, IdeaData *ideakey)
{
BIGNUM *a, *z;
int i;
BN_ULONG r, base = (1 << 8);
char *k;
if ((z = BN_new()) == NULL)
errx(1, "could not create BIGNUM");
BN_zero(z);
if ((a = BN_new()) == NULL)
errx(1, "could not create BIGNUM");
BN_zero(a);
BN_add(a, ck, z);
for (i = 0; i < ((KEYSIZE - 128) / 8); i++) {
r = BN_div_word(a, base);
}
k = (char *)ideakey;
for (i = 0; i < 16; i++) {
r = BN_div_word(a, base);
*k++ = r;
}
BN_free(z);
BN_free(a);
}
static void
extractdeskey(BIGNUM *ck, DesData *deskey)
{
BIGNUM *a, *z;
int i;
BN_ULONG r, base = (1 << 8);
char *k;
if ((z = BN_new()) == NULL)
errx(1, "could not create BIGNUM");
BN_zero(z);
if ((a = BN_new()) == NULL)
errx(1, "could not create BIGNUM");
BN_zero(a);
BN_add(a, ck, z);
for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
r = BN_div_word(a, base);
}
k = (char *)deskey;
for (i = 0; i < 8; i++) {
r = BN_div_word(a, base);
*k++ = r;
}
BN_free(z);
BN_free(a);
}
void
common_key(char *xsecret, char *xpublic, IdeaData *ideakey, DesData *deskey)
{
BIGNUM *public, *secret, *common, *modulus;
BN_CTX *ctx;
if ((ctx = BN_CTX_new()) == NULL)
errx(1, "could not create BN_CTX");
modulus = NULL;
if (BN_hex2bn(&modulus, HEXMODULUS) == 0)
errx(1, "could not convert modulus");
public = NULL;
if (BN_hex2bn(&public, xpublic) == 0)
errx(1, "could not convert public");
secret = NULL;
if (BN_hex2bn(&secret, xsecret) == 0)
errx(1, "could not convert secret");
if ((common = BN_new()) == NULL)
errx(1, "could not create BIGNUM");
BN_zero(common);
BN_mod_exp(common, public, secret, modulus, ctx);
extractdeskey(common, deskey);
extractideakey(common, ideakey);
DES_set_odd_parity(deskey);
BN_free(common);
BN_free(secret);
BN_free(public);
BN_free(modulus);
BN_CTX_free(ctx);
}
static void
getseed(char *seed, int seedsize)
{
int i;
for (i = 0; i < seedsize; i++) {
seed[i] = arc4random() & 0xff;
}
}
static BIGNUM *
itobn(long i)
{
BIGNUM *n = NULL;
if ((n = BN_new()) == NULL)
errx(1, "could not create BIGNUM: %s",
ERR_error_string(ERR_get_error(), 0));
BN_init(n);
if (i > 0)
BN_add_word(n, (u_long)i);
else
BN_sub_word(n, (u_long)(-i));
return(n);
}
void
genkeys(char *public, char *secret)
{
#define BASEBITS (8*sizeof (short) - 1)
#define BASE (short)(1 << BASEBITS)
unsigned int i;
short r;
unsigned short seed[KEYSIZE/BASEBITS + 1];
char *xkey;
BN_CTX *ctx;
BIGNUM *pk, *sk, *tmp, *base, *root, *modulus;
pk = itobn(0);
sk = itobn(0);
tmp = itobn(0);
base = itobn(BASE);
root = itobn(PROOT);
modulus = NULL;
if (BN_hex2bn(&modulus, HEXMODULUS) == 0)
errx(1, "could not convert modulus to BIGNUM: %s",
ERR_error_string(ERR_get_error(), 0));
if ((ctx = BN_CTX_new()) == NULL)
errx(1, "could not create BN_CTX: %s",
ERR_error_string(ERR_get_error(), 0));
getseed((char *)seed, sizeof (seed));
for (i = 0; i < KEYSIZE/BASEBITS + 1; i++) {
r = seed[i] % BASE;
BN_zero(tmp);
BN_add_word(tmp, r);
BN_mul(sk, base, sk, ctx);
BN_add(sk, tmp, sk);
}
BN_zero(tmp);
BN_div(tmp, sk, sk, modulus, ctx);
BN_mod_exp(pk, root, sk, modulus, ctx);
if ((xkey = BN_bn2hex(sk)) == NULL)
errx(1, "could convert sk to hex: %s",
ERR_error_string(ERR_get_error(), 0));
adjust(secret, xkey);
OPENSSL_free(xkey);
if ((xkey = BN_bn2hex(pk)) == NULL)
errx(1, "could convert pk to hex: %s",
ERR_error_string(ERR_get_error(), 0));
adjust(public, xkey);
OPENSSL_free(xkey);
BN_free(base);
BN_free(modulus);
BN_free(pk);
BN_free(sk);
BN_free(root);
BN_free(tmp);
}
static void
adjust(char keyout[HEXKEYBYTES+1], char *keyin)
{
char *p;
char *s;
for (p = keyin; *p; p++)
;
for (s = keyout + HEXKEYBYTES; p >= keyin; p--, s--) {
*s = *p;
}
while (s >= keyout) {
*s-- = '0';
}
}
static char hextab[17] = "0123456789ABCDEF";
void
pk_encode(char *in, char *out, DesData *key)
{
char buf[256];
DesData i;
DES_key_schedule k;
int l,op,deslen;
memset(&i,0,sizeof(i));
memset(buf,0,sizeof(buf));
deslen = rounddown(strlen(in) + 7, 8);
DES_key_sched(key, &k);
DES_cbc_encrypt(in,buf,deslen, &k,&i,DES_ENCRYPT);
for (l=0,op=0;l<deslen;l++) {
out[op++] = hextab[(buf[l] & 0xf0) >> 4];
out[op++] = hextab[(buf[l] & 0x0f)];
}
out[op] = '\0';
}
void
pk_decode(char *in, char *out, DesData *key)
{
char buf[256];
DesData i;
DES_key_schedule k;
int n1,n2,op;
size_t l;
memset(&i,0,sizeof(i));
memset(buf,0,sizeof(buf));
for (l=0,op=0;l<strlen(in)/2;l++,op+=2) {
if (in[op] > '9')
n1 = in[op] - 'A' + 10;
else
n1 = in[op] - '0';
if (in[op+1] > '9')
n2 = in[op+1] - 'A' + 10;
else
n2 = in[op+1] - '0';
buf[l] = n1*16 +n2;
}
DES_key_sched(key, &k);
DES_cbc_encrypt(buf,out,strlen(in)/2, &k,&i,DES_DECRYPT);
out[strlen(in)/2] = '\0';
}