/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "arcfour.h"

#if defined(__amd64)
#ifdef _KERNEL
#include <sys/x86_archext.h>
#include <sys/cpuvar.h>

#else
#include <sys/auxv.h>
#endif	/* _KERNEL */
#endif	/* __amd64 */

#if !defined(__amd64)
/* Initialize the key stream 'key' using the key value */
void
arcfour_key_init(ARCFour_key *key, uchar_t *keyval, int keyvallen)
{
/* EXPORT DELETE START */

	uchar_t ext_keyval[256];
	uchar_t tmp;
	int i, j;

	/* Normalize key length to 256 */
	for (i = j = 0; i < 256; i++, j++) {
		if (j == keyvallen)
			j = 0;
		ext_keyval[i] = keyval[j];
	}

	for (i = 0; i < 256; i++)
		key->arr[i] = (uchar_t)i;

	j = 0;
	for (i = 0; i < 256; i++) {
		j = (j + key->arr[i] + ext_keyval[i]) % 256;
		tmp = key->arr[i];
		key->arr[i] = key->arr[j];
		key->arr[j] = tmp;
	}
	key->i = 0;
	key->j = 0;

/* EXPORT DELETE END */
}


/*
 * Encipher 'in' using 'key'.
 * in and out can point to the same location
 */
void
arcfour_crypt(ARCFour_key *key, uchar_t *in, uchar_t *out, size_t len)
{
	size_t ii;
	uchar_t tmp, i, j;

/* EXPORT DELETE START */

	/*
	 * The sun4u has a version of arcfour_crypt_aligned() hand-tuned for
	 * the cases where the input and output  buffers are aligned on
	 * a multiple of 8-byte boundary.
	 */
#ifdef	sun4u
	int index;

	index = (((uint64_t)(uintptr_t)in) & 0x7);

	/* Get the 'in' on an 8-byte alignment */
	if (index > 0) {
		i = key->i;
		j = key->j;
		for (index = 8 - (uint64_t)(uintptr_t)in & 0x7;
		    (index-- > 0) && len > 0;
		    len--, in++, out++) {
			i = i + 1;
			j = j + key->arr[i];
			tmp = key->arr[i];
			key->arr[i] = key->arr[j];
			key->arr[j] = tmp;
			tmp = key->arr[i] + key->arr[j];
			*out = *in ^ key->arr[tmp];
		}
		key->i = i;
		key->j = j;

	}
	if (len == 0)
		return;

	/* See if we're fortunate and 'out' got aligned as well */

	if ((((uint64_t)(uintptr_t)out) & 7) != 0) {
#endif	/* sun4u */
		i = key->i;
		j = key->j;
		for (ii = 0; ii < len; ii++) {
			i = i + 1;
			j = j + key->arr[i];
			tmp = key->arr[i];
			key->arr[i] = key->arr[j];
			key->arr[j] = tmp;
			tmp = key->arr[i] + key->arr[j];
			out[ii] = in[ii] ^ key->arr[tmp];
		}
		key->i = i;
		key->j = j;
#ifdef	sun4u
	} else {
		arcfour_crypt_aligned(key, len, in, out);
	}
#endif	/* sun4u */

/* EXPORT DELETE END */
}

#else

/*
 * Return 1 if executing on Intel, otherwise 0 (e.g., AMD64).
 */
int
arcfour_crypt_on_intel(void)
{
#ifdef _KERNEL
	return (cpuid_getvendor(CPU) == X86_VENDOR_Intel);
#else
	uint_t	ui;
	(void) getisax(&ui, 1);
	return ((ui & AV_386_AMD_MMX) == 0);
#endif	/* _KERNEL */
}
#endif	/* !__amd64 */