/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */
/* See the file COPYING for distribution information */
#include "db.h"

const char *alphabet =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";

#ifdef USE_UNIX_CRYPT

extern const char *crypt(const char *, const char *);

#define do_crypt crypt

#else /* USE_UNIX_CRYPT */

#define mask(x) ((1 << (x)) - 1)
#define extract0(p) alphabet[bits[p] & mask(6)]
/* don't even try to understand it */
/* and don't use it outside bits2alpha */
/* WARNING: Dec 3100 (pmax) cc botches the constant-folding in this macro! */
#define extract(p, shift) \
    alphabet[((bits[(p)] >> (shift)) & mask(8 - (shift))) \
	     | ((bits[(p)+1] << ((shift) - 4)) \
		& (mask(10 - (shift)) << ((shift) - 4)))]

/* converts 64 bits to 11 characters of the 6-bit code */
static const char *bits2alpha(unsigned const char *bits)
{
    static char alpha[12];

    alpha[0] = extract0(0);
    alpha[1] = extract(0, 6);
    alpha[2] = extract(1, 4);
    alpha[3] = extract(2, 2);
    alpha[4] = extract0(3);
    alpha[5] = extract(3, 6);
    alpha[6] = extract(4, 4);
    alpha[7] = extract(5, 2);
    alpha[8] = extract0(6);
    alpha[9] = extract(6, 6);
    alpha[10] = alphabet[(bits[7] >> 4) & 0xf];
    alpha[11] = '\0';

    return alpha;
}

static const char *do_crypt(const char *password, const char *salt) 
{
    static int initialized = 0;	/* already called desinit */

    int i;
    unsigned char cbuf[8];	/* thing to encrypt */
    char key[8];		/* key */
    static char buf[14];	/* 2 bytes of salt + 11 bytes of text + term */

    for(i = 0; i < 8; i+= 2) {
	cbuf[i] = salt[0];
	cbuf[i+1] = salt[1];
    }

    for(i = 0; i < 8 && password[i]; i++) {
	key[i] = password[i] << 1;
    }
    while(i < 8) key[i++] = 0;

    if(!initialized) {
	desinit(1);		/* ignore initial and final permutations */
	initialized = 1;
    }

    setkey(key);
    endes(cbuf);

    /* construct the salted string in buf */
    buf[0] = salt[0];
    buf[1] = salt[1];
    strcpy(buf + 2, bits2alpha(cbuf));

    return buf;
}

#endif /* USE_UNIX_CRYPT */

const char *encrypt_password(const char *password)
{
    char salt[3];

    salt[0] = alphabet[RAND() % 64];
    salt[1] = alphabet[RAND() % 64];
    salt[2] = '\0';

    return do_crypt(password, salt);
}

int check_password(const char *clear, const char *encrypted)
{
    return !strcmp(encrypted, do_crypt(clear, encrypted));
}