/*
 * crypto routines for qi
 *
 * This is based on algorithms published in qi-3.1b7 and earlier releases.
 * Much code is very similar or identical; see the copyright statement
 * accompanying this file.
 */

static char rcsid[] = "$Id: crypt.c,v 2.1 2000/07/19 07:37:50 dgc Exp $";

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <crypt.h>
#include <ctype.h>
#include "qick.h"
/*
	#if A_NASTY_ALTERNATIVE_TO_CRYPT_H
	#ifndef __EXTENSIONS__
	# define __EXTENSIONS__
	# define __UNDEF__EXTENSIONS__
	#endif
	#include <unistd.h>
	#ifdef __UNDEF__EXTENSIONS__
	# undef __EXTENSIONS__
	#endif
	#endif
*/

/*
 * Create a new context using a given password.
 */
qi_crypt_ctx_t *
qi_crypt_init(qi_crypt_ctx_t *ctx, char *pass, int pre)
{
	char		*p, pbuf[QI_CRYPT_PASS_SIZE + 1];
	int		 seed, i, k, rnd, ic;
	char		 swap;

	if (ctx == NULL)
		if ((ctx = malloc(sizeof(qi_crypt_ctx_t))) == NULL)
			return NULL;

	memset(ctx->ctx_r2, 0, sizeof(ctx->ctx_r2));
	memset(ctx->ctx_r3, 0, sizeof(ctx->ctx_r3));

	if (pre)
		strncpy(pbuf, pass, QI_CRYPT_PASS_SIZE);
	else
		strncpy(pbuf, crypt(pass, pass), QI_CRYPT_PASS_SIZE);

	seed = 123;
	for (i = 0; i < QI_CRYPT_PASS_SIZE; ++i)
		seed = seed * pbuf[i] + i;

	for (i = 0; i < QI_CRYPT_ROTOR_SIZE; ++i)
		ctx->ctx_r1[i] = i;

	/* Prime the rotors with the scrambled (crypt()ed?) password. */
	for (i = 0, k = sizeof(ctx->ctx_r1) - 1;
				i < sizeof(ctx->ctx_r1); ++i, --k) {
		seed = 5 * seed + pbuf[i % QI_CRYPT_PASS_SIZE];
		rnd = seed % 65521;

		ic = (rnd & 0xff) % (k + 1);
		rnd >>= 8;

		swap = ctx->ctx_r1[k];
		ctx->ctx_r1[k] = ctx->ctx_r1[ic];
		ctx->ctx_r1[ic] = swap;

		if (ctx->ctx_r3[k] != 0)
			continue;

		ic = (rnd & 0xff) % k;

		while (ctx->ctx_r3[ic] != 0)
			ic = (ic + 1) % k;
		ctx->ctx_r3[k] = ic;
		ctx->ctx_r3[ic] = k;
	}

	for (i = 0; i < QI_CRYPT_ROTOR_SIZE; ++i)
		ctx->ctx_r2[ctx->ctx_r1[i] & 0xff] = i;

	return ctx;
}

#define printable(v)	(((v) & 0x3f) + '#')

static int
qi_encrypt(qi_crypt_ctx_t *ctx, char *chal)
{
	char		 work[4096];
	register char	*pr, *pw;
	register int	 i, j, k;

	pw = work;
	pr = chal;
	i = j = k = 0;
#define R1(s1, s2)	(ctx->ctx_r1[(s1) & 0xff] + (s2))
#define R2(s1, s2)	(ctx->ctx_r2[(s1) & 0xff] + (s2))
#define R3(s1, s2)	(ctx->ctx_r3[(s1) & 0xff] + (s2))
	for (; *pr; pr++, k++) {
		*pw++ = R2(R3(R1(*pr + i, j), -j), -i);
		if (++i == QI_CRYPT_ROTOR_SIZE) {
			i = 0;
			if (++j == QI_CRYPT_ROTOR_SIZE)
				j = 0;
		}
	}

	pw = ctx->ctx_crypt;
	*pw++ = printable(k);
	pr = work;

	/* Encode each 3 bytes as 4 -- base64 */
	i = j = 0;
	while (i < k) {
		*pw++ = printable( pr[0]         >> 2                      );
		*pw++ = printable((pr[0] & 0x03) << 4 | (pr[1] & 0xf0) >> 4);
		*pw++ = printable((pr[1] & 0x0f) << 2 | (pr[2] & 0xc0) >> 6);
		*pw++ = printable( pr[2] & 0x3f                            );
		pr += 3;
		i += 3;
		j += 4;
	}

	*pw = '\0';
	return j;
}

char *
qi_crypt(char *dest, int dlen, char *pass, char *chal, int pre)
{
	qi_crypt_ctx_t	 ctx;
	int		 len;

	qi_debug("= qi_crypt\n");

	if (qi_crypt_init(&ctx, pass, pre) == NULL)
		return NULL;

	len = qi_encrypt(&ctx, chal);

	if (dest == NULL) {
		if ((dest = malloc(len + 1)) == NULL) {
			return NULL;
		}
	} else {
		if (dlen < len + 1) {
			return NULL;
		}
	}

	memcpy(dest, &ctx.ctx_crypt, len + 1);
	return dest;
}

