/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


/*
 *  vRijndael-alg-ref.c
 *
 *  Created by Robert A. Murley on Mon Jan 22 2001.
 *  Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
 *
 */

#include "rijndaelApi.h"
#include "rijndael-alg-ref.h"
#include "boxes-ref.h"
#include <string.h>

/* debugger seems to have trouble with this code... */
#define VAES_DEBUG	1
#if		VAES_DEBUG
#include <stdio.h>
#define vdprintf(s)		printf s
#else
#define vdprintf(s)
#endif

#define SC	((BC - 4) >> 1)

#if defined(__ppc__) && defined(ALTIVEC_ENABLE)

typedef union {
	unsigned char		s[4][8];
	unsigned long		l[8];
	vector unsigned char 	v[2];
} doubleVec;

typedef union {
	unsigned long		s[4];
	vector unsigned long	v;
} vecLong;

static word8 shifts[3][4][2] = {
 { { 0, 0 },
   { 1, 3 },
   { 2, 2 },
   { 3, 1 }
 },
 { { 0, 0 },
   { 1, 5 },
   { 2, 4 },
   { 3, 3 }
 },
 { { 0, 0 },
   { 1, 7 },
   { 3, 5 },
   { 4, 4 }
 }
};

int vRijndaelKeySched ( vector unsigned char vk[2], int keyBits, int blockBits, 
                unsigned char W[MAXROUNDS+1][4][MAXBC])
{
	/* Calculate the necessary round keys
	 * The number of calculations depends on keyBits and blockBits
	 */
	int KC, BC, ROUNDS;
	int i, j, t, rconpointer = 0;
	doubleVec tk;
	register  vector unsigned char v1, v2, mask;

	switch (keyBits) {
	case 128: KC = 4; break;
	case 192: KC = 6; break;
	case 256: KC = 8; break;
	default : return (-1);
	}

	switch (blockBits) {
	case 128: BC = 4; break;
	case 192: BC = 6; break;
	case 256: BC = 8; break;
	default : return (-2);
	}

	switch (keyBits >= blockBits ? keyBits : blockBits) {
	case 128: ROUNDS = 10; break;
	case 192: ROUNDS = 12; break;
	case 256: ROUNDS = 14; break;
	default : return (-3); /* this cannot happen */
	}

	tk.v[0] = vk[0];
	tk.v[1] = vk[1];
	
	t = 0;
	/* copy values into round key array */
	for(j = 0; (j < KC) && (t < (ROUNDS+1)*BC); j++, t++)
		for(i = 0; i < 4; i++) W[t / BC][i][t % BC] = tk.s[i][j];
		
	while (t < (ROUNDS+1)*BC) { /* while not enough round key material calculated */
		/* calculate new values */
		for(i = 0; i < 4; i++)
			tk.s[i][0] ^= *((word8 *)S + tk.s[(i+1)%4][KC-1]);
		tk.s[0][0] ^= rcon[rconpointer++];

		if (KC != 8) {
			/* xor bytes 1-7 of each row with previous byte */
			mask = (vector unsigned char) ( 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff );
			for ( i = 0; i < 2; i++ ) {
				v1 = vec_sld( tk.v[i], tk.v[i], 15 );
				v2 = vec_and( v1, mask );
				tk.v[i] = vec_xor( tk.v[i], v2 );
			}
		}
		else {
			/* xor bytes 1-3 of each row with previous byte */
			mask = (vector unsigned char) ( 0, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0, 0, 0, 0 );
			for ( i = 0; i < 2; i++ ) {
				v1 = vec_sld( tk.v[i], tk.v[i], 15 );
				v2 = vec_and( v1, mask );
				tk.v[i] = vec_xor( tk.v[i], v2 );
				for(j = 0; j < 4; j++) tk.s[i][KC/2] ^= *((word8 *)S + tk.s[i][KC/2 - 1]);
				/* xor bytes 5-7 of each row with previous byte */
				mask = vec_sld( mask, mask, 4 );
				v2 = vec_and( v1, mask );
				tk.v[i] = vec_xor( tk.v[i], v2 );
				mask = vec_sld( mask, mask, 4 );
			}
		}
		/* copy values into round key array */
		for(j = 0; (j < KC) && (t < (ROUNDS+1)*BC); j++, t++)
			for(i = 0; i < 4; i++) W[t / BC][i][t % BC] = tk.s[i][j];
	}		
	return 0;
}


void vMakeKey(BYTE *keyMaterial, keyInstance *key)
{
        register vector unsigned char v1, v2, v3, mask;
        vector unsigned char	  vk[2];

        /* load and align input */
        v1 = vec_ld( 0, (vector unsigned char *) keyMaterial );
        v2 = vec_ld( 16, (vector unsigned char *) keyMaterial );
        if ( (long) keyMaterial & 0x0fL )
        {	// this is required if keyMaterial is not on a 16-byte boundary
                v3 = vec_ld( 32, (vector unsigned char *) keyMaterial );
                mask = vec_lvsl( 0, keyMaterial );
                v1 = vec_perm( v1, v2, mask );
                v2 = vec_perm( v2, v3, mask );
        }

        /* parse input stream into rectangular array */
        vk[0] = vec_perm( v1, v2, (vector unsigned char) ( 0,  4,  8, 12, 16, 20, 24, 28,  1,  5,  9, 13, 17, 21, 25, 29 ) );
        vk[1] = vec_perm( v1, v2, (vector unsigned char) ( 2,  6, 10, 14, 18, 22, 26, 30,  3,  7, 11, 15, 19, 23, 27, 31 ) );
        vRijndaelKeySched (vk, key->keyLen, key->blockLen, key->keySched);
        memset( (char *) vk, 0, 4 * MAXKC);
}


/*	This routine does 16 simultaneous lookups in a 256-byte table.	*/
vector unsigned char rimskyKorsakov ( vector unsigned char v, vector unsigned char * table )
{
	register vector unsigned char	upperBits000, upperBits001, upperBits010, upperBits011,
                                        upperBits100, upperBits101, upperBits110, upperBits111,
                                        lookupBit00,  lookupBit01, lookupBit10, lookupBit11,
                                        lookupBit0, lookupBit1, lookup,
                                        maskForBit6, maskForBit7, maskForBit8, seven;
	register vector unsigned char	*tabeven, *tabodd;

	seven = vec_splat_u8 ( 7 );
	tabeven = table++;
	tabodd = table;

//	Each variable contains the correct values for the corresponding bits 6, 7 and 8.
	upperBits000 = vec_perm ( *tabeven, *tabodd, v );
	tabeven += 2; tabodd += 2;
	upperBits001 = vec_perm ( *tabeven, *tabodd, v );
	tabeven += 2; tabodd += 2;
	upperBits010 = vec_perm ( *tabeven, *tabodd, v );
	tabeven += 2; tabodd += 2;
	upperBits011 = vec_perm ( *tabeven, *tabodd, v );
	tabeven += 2; tabodd += 2;
	upperBits100 = vec_perm ( *tabeven, *tabodd, v ); 
	tabeven += 2; tabodd += 2;
	upperBits101 = vec_perm ( *tabeven, *tabodd, v );
	tabeven += 2; tabodd += 2;
	upperBits110 = vec_perm ( *tabeven, *tabodd, v );
	tabeven += 2; tabodd += 2;
	upperBits111 = vec_perm ( *tabeven, *tabodd, v );
	
//	Here we extract all the correct values for bit 6.
	maskForBit6  = vec_sl  ( v, vec_splat_u8 ( 2 ) );
	maskForBit6  = vec_sra ( maskForBit6, seven );
	lookupBit00 = vec_sel ( upperBits000, upperBits001, maskForBit6 );
	lookupBit01 = vec_sel ( upperBits010, upperBits011, maskForBit6 );
	lookupBit10 = vec_sel ( upperBits100, upperBits101, maskForBit6 );
	lookupBit11 = vec_sel ( upperBits110, upperBits111, maskForBit6 );

//	Then we get the correct values for bit 7.
	maskForBit7  = vec_sl  ( v, vec_splat_u8 ( 1 ) );
	maskForBit7  = vec_sra ( maskForBit7, seven );
	lookupBit0 = vec_sel ( lookupBit00, lookupBit01, maskForBit7 );
	lookupBit1 = vec_sel ( lookupBit10, lookupBit11, maskForBit7 );

//	Finally, the entire correct result vector.
	maskForBit8 = vec_sra ( v, seven );
	
	lookup = vec_sel ( lookupBit0, lookupBit1, maskForBit8 );
      
    return lookup;
}

vector unsigned char vmul(vector unsigned char a, vector unsigned char b)
{
	register vector unsigned char x, y, zero;
	register vector unsigned short xh, yh, zhi, zlo, two54, two55;
	
	zero = vec_splat_u8( 0 );
	two55 = vec_splat_u16( -1 );
	two55 = (vector unsigned short) vec_mergeh( zero, (vector unsigned char) two55 );
	two54 = vec_sub( two55, vec_splat_u16( 1 ) ); 

	x = rimskyKorsakov( a, (vector unsigned char *)Logtable );	// Logtable[a]
	y = rimskyKorsakov( b, (vector unsigned char *)Logtable );	// Logtable[b]

	//	Convert upper 8 bytes to shorts for addition ond modulo
	xh = (vector unsigned short) vec_mergeh( zero, x );
	yh = (vector unsigned short) vec_mergeh( zero, y );
	xh = vec_add( xh, yh );			// xh = Logtable[a] + Logtable[b]
	yh = vec_sub( xh, two55 );
	zhi = vec_sel( xh, yh, vec_cmpgt( xh, two54 ) );	// xh%255

	//	Convert lower 8 bytes to shorts for addition ond modulo
	xh = (vector unsigned short) vec_mergel( zero, x );
	yh = (vector unsigned short) vec_mergel( zero, y );
	xh = vec_add( xh, yh );
	yh = vec_sub( xh, two55 );
	zlo = vec_sel( xh, yh, vec_cmpgt( xh, two54 ) );

	x = vec_pack( zhi, zlo );			// recombine into single byte vector
	x = rimskyKorsakov( x, (vector unsigned char *)Alogtable );		// Alogtable[x]
	x = vec_sel( x, zero, vec_cmpeq( a, zero ) );	// check a = 0
	x = vec_sel( x, zero, vec_cmpeq( b, zero ) );	// check b = 0
	return x;
}

void vKeyAddition(vector unsigned char v[2], vector unsigned char rk[2])
{
	v[0] = vec_xor( v[0], rk[0] );		// first vector contains rows 0 and 1
	v[1] = vec_xor( v[1], rk[1] );		// second vector contains rows 2 and 3
}


void vShiftRow(vector unsigned char v[2], word8 d, word8 BC)
{
	vecLong			sh;
	register vector unsigned char mask, mask1, t;
	register vector bool char c;
	register int	i, j;
	
	sh.s[0] = 0;
	for (i = 1; i < 4; i++)
		sh.s[i] = shifts[SC][i][d] % BC;	//	contains the number of elements to shift each row
		
	// each vector contains two BC-byte long rows
	j = 0;
	for ( i = 0; i < 2; i++ ) {
		mask = vec_lvsl( 0, (int *) sh.s[j++]);		//	mask for even row
		mask1 = vec_lvsl( 0, (int *) sh.s[j++]);	//	mask for odd row
		if (BC == 4) {
			mask = vec_sld( mask, mask1, 8 );		//	combined rotation mask for both rows
			mask = vec_and( mask, vec_splat_u8( 3 ) );
		} else if (BC == 6) {
			mask = vec_sld( mask, mask, 8 );
			mask = vec_sld( mask, mask1, 8 );		//	combined rotation mask for both rows
			t = vec_sub( mask, vec_splat_u8( 6 ) );
			c = vec_cmpgt( mask, vec_splat_u8( 5 ) );
			mask = vec_sel( mask, t, c );
		} else {
			mask = vec_sld( mask, mask1, 8 );		//	combined rotation mask for both rows
			mask = vec_and( mask, vec_splat_u8( 7 ) );
		}
		mask1 = vec_sld( vec_splat_u8( 0 ), vec_splat_u8( 8 ), 8 );
		mask = vec_add( mask, mask1 );
		v[i] = vec_perm( v[i], v[i], mask );		//	rotate each row as required
	}
}

void vSubstitution( vector unsigned char v[2], vector unsigned char box[16] )
{
	v[0] = rimskyKorsakov( v[0], box );		// first vector contains rows 0 and 1
	v[1] = rimskyKorsakov( v[1], box );		// second vector contains rows 2 and 3
}
   
void vMixColumn(vector unsigned char v[2])
{
	//	vector 0 contains row 0 in bytes 0-7 and row 1 in bytes 8-f
	//	vector 1 contains row 2 in bytes 0-7 and row 3 in bytes 8-f

	register vector unsigned char a0, a1, a2, a3, b0, b1, b2, b3;
	register vector unsigned char two, three;
	
	two = vec_splat_u8( 2 );
	three = vec_splat_u8( 3 );

	a1 = vec_sld( v[0], v[1], 8 );		// equivalent to a[i+1] % 4
	b1 = vec_sld( v[1], v[0], 8 );
	a2 = vec_sld( a1, b1, 8 );		// equivalent to a[i+2] % 4
	b2 = vec_sld( b1, a1, 8 );
	a3 = vec_sld( a2, b2, 8 );		// equivalent to a[i+3] % 4
	b3 = vec_sld( b2, a2, 8 );
	
	//	Calculations for rows 0 and 1
	a0 = vmul( two, v[0] );				// mul(2,a[i][j])
	a0 = vec_xor( a0, vmul( three, a1 ) );		// ^ mul(3,a[(i + 1) % 4][j])
	a0 = vec_xor( a0, a2 );				// ^ a[(i + 2) % 4][j]
	v[0]  = vec_xor( a0, a3 );			// ^ a[(i + 3) % 4][j]

	//	Calculations for rows 2 and 3
	b0 = vmul( two, v[1] );
	b0 = vec_xor( b0, vmul( three, b1 ) );
	b0 = vec_xor( b0, b2 );
	v[1] = vec_xor( b0, b3 );
}

void vInvMixColumn(vector unsigned char v[2])
{
	//	vector 0 contains row 0 in bytes 0-7 and row 1 in bytes 8-f
	//	vector 1 contains row 2 in bytes 0-7 and row 3 in bytes 8-f

	register vector unsigned char a0, a1, a2, a3, b0, b1, b2, b3;
	register vector unsigned char nine, eleven, thirteen, fourteen;;
	
	nine = vec_splat_u8( 0x9 );
	eleven = vec_splat_u8( 0xb );
	thirteen = vec_splat_u8( 0xd );
	fourteen = vec_splat_u8( 0xe );

	a1 = vec_sld( v[0], v[1], 8 );			// equivalent to a[i+1] % 4
	b1 = vec_sld( v[1], v[0], 8 );
	a2 = vec_sld( a1, b1, 8 );			// equivalent to a[i+2] % 4
	b2 = vec_sld( b1, a1, 8 );
	a3 = vec_sld( a2, b2, 8 );			// equivalent to a[i+3] % 4
	b3 = vec_sld( b2, a2, 8 );
	
	//	Calculations for rows 0 and 1
	a0 = vmul( fourteen, v[0] );				// mul(0xe,a[i][j])
	a0 = vec_xor( a0, vmul( eleven, a1 ) );		// ^ mul(0xb,a[(i + 1) % 4][j])
	a0 = vec_xor( a0, vmul( thirteen, a2 ) );	// ^ mul(0xd,a[(i + 2) % 4][j])
	v[0]  = vec_xor( a0, vmul( nine, a3 ) );	// ^ mul(0x9,a[(i + 3) % 4][j])

	//	Calculations for rows 2 and 3
	b0 = vmul( fourteen, v[1] );
	b0 = vec_xor( b0, vmul( eleven, b1 ) );
	b0 = vec_xor( b0, vmul( thirteen, b2 ) );
	v[1]  = vec_xor( b0, vmul( nine, b3 ) );
}

int vRijndaelEncrypt (vector unsigned char a[2], int keyBits, int blockBits, vector unsigned char rk[MAXROUNDS+1][2])
{
	/* Encryption of one block. 
	 */
	int r, BC, ROUNDS;

	switch (blockBits) {
	case 128: BC = 4; break;
	case 192: BC = 6; break;
	case 256: BC = 8; break;
	default : return (-2);
	}

	switch (keyBits >= blockBits ? keyBits : blockBits) {
	case 128: ROUNDS = 10; break;
	case 192: ROUNDS = 12; break;
	case 256: ROUNDS = 14; break;
	default : return (-3); /* this cannot happen */
	}

        vKeyAddition( a, rk[0] ); 
        for(r = 1; r < ROUNDS; r++) {
                vSubstitution( a, (vector unsigned char *)S);
                vShiftRow( a, 0, BC);
                vMixColumn( a );
                vKeyAddition( a, rk[r] ); 	
        }
        vSubstitution( a, (vector unsigned char *)S);
        vShiftRow( a, 0, BC);
        vKeyAddition( a, rk[ROUNDS] ); 

	return 0;
}   

int vRijndaelDecrypt (vector unsigned char a[2], int keyBits, int blockBits, vector unsigned char rk[MAXROUNDS+1][2])
{
	int r, BC, ROUNDS;
	
	switch (blockBits) {
	case 128: BC = 4; break;
	case 192: BC = 6; break;
	case 256: BC = 8; break;
	default : return (-2);
	}

	switch (keyBits >= blockBits ? keyBits : blockBits) {
	case 128: ROUNDS = 10; break;
	case 192: ROUNDS = 12; break;
	case 256: ROUNDS = 14; break;
	default : return (-3); /* this cannot happen */
	}
		
        vKeyAddition( a, rk[ROUNDS] ); 
        vSubstitution( a, (vector unsigned char *)Si);
        vShiftRow( a, 1, BC);
        for(r = ROUNDS-1; r > 0; r--) {
                vKeyAddition( a, rk[r] ); 
                vInvMixColumn( a );
                vSubstitution( a, (vector unsigned char *)Si);
                vShiftRow( a, 1, BC);
        }
        vKeyAddition( a, rk[0] ); 

	return 0;
}

#if 0
/* Murley's code, to be deleted */
void vBlockEncrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer)
{
        register vector unsigned char v1, v2, v3, v4, mask;
        register vector bool char cmp;

        /* load and align input */
        v1 = vec_ld( 0, (vector unsigned char *) input );
        v2 = vec_ld( 16, (vector unsigned char *) input );
        if ( (long) input & 0x0fL )
        {	// this is required if input is not on a 16-byte boundary
                v3 = vec_ld( 32, (vector unsigned char *) input );
                mask = vec_lvsl( 0, input );
                v1 = vec_perm( v1, v2, mask );
                v2 = vec_perm( v2, v3, mask );
        }

        /* parse input stream into rectangular array */
        v3 = vec_perm( v1, v2, (vector unsigned char) ( 0,  4,  8, 12, 16, 20, 24, 28,  1,  5,  9, 13, 17, 21, 25, 29 ) );
        v4 = vec_perm( v1, v2, (vector unsigned char) ( 2,  6, 10, 14, 18, 22, 26, 30,  3,  7, 11, 15, 19, 23, 27, 31 ) );

        /* store into cipher structure */
        if (cipher->mode == MODE_CBC) {
                v3 = vec_xor( v3, *((vector unsigned char *) cipher->chainBlock ) );
                v4 = vec_xor( v4, *((vector unsigned char *) cipher->chainBlock + 1 ) );
        }
        vec_st( v3, 0, (vector unsigned char *) cipher->chainBlock );
        vec_st( v4, 16, (vector unsigned char *) cipher->chainBlock );

        vRijndaelEncrypt((vector unsigned char *) cipher->chainBlock, key->keyLen, cipher->blockLen, (vector unsigned char *) key->keySched);

        v1 = vec_ld( 0, (vector unsigned char *) cipher->chainBlock );
        v2 = vec_ld( 16, (vector unsigned char *) cipher->chainBlock );

        /* parse rectangular array into output ciphertext bytes */
        v3 = vec_perm( v1, v2, (vector unsigned char) ( 0,  8, 16, 24,  1,  9, 17, 25,  2, 10, 18, 26,  3, 11, 19, 27 ) );
        v4 = vec_perm( v1, v2, (vector unsigned char) ( 4, 12, 20, 28,  5, 13, 21, 29,  6, 14, 22, 30,  7, 15, 23, 31 ) );

        if ( (long) outBuffer & 0x0fL )
        {
                /* store output data into a non-aligned buffer */
                mask = vec_lvsr( 0, outBuffer );
                cmp = vec_cmpgt( mask, vec_splat_u8( 0x0f ) );
                v1 = vec_perm( v3, v3, mask );
                v2 = vec_perm( v4, v4, mask );
                v3 = vec_ld( 0, (vector unsigned char *) outBuffer );
                v4 = vec_sel( v3, v1, cmp );
                vec_st( v4, 0, (vector unsigned char *) outBuffer );
                v1 = vec_sel( v1, v2, cmp );
                vec_st( v1, 16, (vector unsigned char *) outBuffer );
                v3 = vec_ld( 32, (vector unsigned char *) outBuffer );
                v2 = vec_sel( v2, v3, cmp );
                vec_st( v2, 32, (vector unsigned char *) outBuffer );
        } else {
                // store output data into an aligned buffer
                vec_st( v3, 0, (vector unsigned char *) outBuffer );
                vec_st( v4, 16, (vector unsigned char *) outBuffer );
        }
        return;
}

void vBlockDecrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer)
{
        // for vector machines
        register vector unsigned char v1, v2, v3, v4, mask;
        register vector bool char cmp;
        vector unsigned  char	block[2], cblock[2];

        /* load and align input */
        v1 = vec_ld( 0, (vector unsigned char *) input );
        v2 = vec_ld( 16, (vector unsigned char *) input );
        if ( (long) input & 0x0fL )
        {	// this is required if input is not on a 16-byte boundary
                v3 = vec_ld( 32, (vector unsigned char *) input );
                mask = vec_lvsl( 0, input );
                v1 = vec_perm( v1, v2, mask );
                v2 = vec_perm( v2, v3, mask );
        }

        /* parse input stream into rectangular array */
        v3 = vec_perm( v1, v2, (vector unsigned char) ( 0,  4,  8, 12, 16, 20, 24, 28,  1,  5,  9, 13, 17, 21, 25, 29 ) );
        v4 = vec_perm( v1, v2, (vector unsigned char) ( 2,  6, 10, 14, 18, 22, 26, 30,  3,  7, 11, 15, 19, 23, 27, 31 ) );
        block[0] = v3;
        block[1] = v4;

        /* save a copy of incoming ciphertext for later chain */
        if (cipher->mode == MODE_CBC) {
                cblock[0] = v3;
                cblock[1] = v4;
        }
                
        vRijndaelDecrypt ((vector unsigned char *) block, key->keyLen, cipher->blockLen, (vector unsigned char *) key->keySched);
        
        v1 = block[0];
        v2 = block[1];

        /* exor with last ciphertext */
        if (cipher->mode == MODE_CBC) {
                v1 = vec_xor( v1, *((vector unsigned char *) cipher->chainBlock) );
                v2 = vec_xor( v2, *((vector unsigned char *) cipher->chainBlock + 1) );
                vec_st( cblock[0], 0, (vector unsigned char *) cipher->chainBlock );
                vec_st( cblock[1], 16, (vector unsigned char *) cipher->chainBlock );
        }
        
        /* parse rectangular array into output ciphertext bytes */
        v3 = vec_perm( v1, v2, (vector unsigned char) ( 0,  8, 16, 24,  1,  9, 17, 25,  2, 10, 18, 26,  3, 11, 19, 27 ) );
        v4 = vec_perm( v1, v2, (vector unsigned char) ( 4, 12, 20, 28,  5, 13, 21, 29,  6, 14, 22, 30,  7, 15, 23, 31 ) );

        if ( (long) outBuffer & 0x0fL )
        {	/* store output data into a non-aligned buffer */
                mask = vec_lvsr( 0, outBuffer );
                cmp = vec_cmpgt( mask, vec_splat_u8( 0x0f ) );
                v1 = vec_perm( v3, v3, mask );
                v2 = vec_perm( v4, v4, mask );
                v3 = vec_ld( 0, (vector unsigned char *) outBuffer );
                v4 = vec_sel( v3, v1, cmp );
                vec_st( v4, 0, (vector unsigned char *) outBuffer );
                v1 = vec_sel( v1, v2, cmp );
                vec_st( v1, 16, (vector unsigned char *) outBuffer );
                v3 = vec_ld( 32, (vector unsigned char *) outBuffer );
                v2 = vec_sel( v2, v3, cmp );
                vec_st( v2, 32, (vector unsigned char *) outBuffer );
        } else {
                // store output data into an aligned buffer
                vec_st( v3, 0, (vector unsigned char *) outBuffer );
                vec_st( v4, 16, (vector unsigned char *) outBuffer );
        }
}
#endif	/* Murley's code, to be deleted */

/* 
 * dmitch addenda 4/11/2001: 128-bit only encrypt/decrypt with no CBC
 */
void vBlockEncrypt128(
	keyInstance *key, 
	BYTE *input, 
	BYTE *outBuffer)
{
	vector unsigned char block[2];
	register vector unsigned char v1, v2;
	
	if ( (long) input & 0x0fL ) {
		BYTE	localBuf[16];
		vdprintf(("vBlockEncrypt128: unaligned input\n"));
		/* manually re-align - the compiler is supposed to 16-byte align this for us */
		if((unsigned)localBuf & 0xf) {
			vdprintf(("vBlockEncrypt128: unaligned localBuf!\n"));
		}
		memmove(localBuf, input, 16);
		v1 = vec_ld(0, (vector unsigned char *)localBuf);
	}
	else {
		vdprintf(("vBlockEncrypt128: aligned input\n"));
		v1 = vec_ld( 0, (vector unsigned char *) input );
	}

	/* parse input stream into rectangular array */
	/* FIXME - do we need to zero v2 (or something)? */
	block[0] = vec_perm(v1, v2,
		(vector unsigned char) ( 0,  4,  8, 12, 16, 20, 24, 28,  1,  
		5,  9, 13, 17, 21, 25, 29 ) );
	block[1] = vec_perm( v1, v2, 
		(vector unsigned char) ( 2,  6, 10, 14, 18, 22, 26, 30,  3,  
		7, 11, 15, 19, 23, 27, 31 ) );

	vRijndaelEncrypt(block, key->keyLen, 128, (vector unsigned char *) key->keySched);

	/* parse rectangular array into output ciphertext bytes */
	v1 = vec_perm(block[0], block[1], 
		(vector unsigned char) ( 0,  8, 16, 24,  1,  9, 17, 25,  2, 
		10, 18, 26,  3, 11, 19, 27 ) );
	v2 = vec_perm(block[0], block[1], 
		(vector unsigned char) ( 4, 12, 20, 28,  5, 13, 21, 29,  6, 
		14, 22, 30,  7, 15, 23, 31 ) );

	if ( (long) outBuffer & 0x0fL )
	{
		/* store output data into a non-aligned buffer */
		BYTE	localBuf[16];
		vec_st(v1, 0, (vector unsigned char *) localBuf );
		memmove(outBuffer, localBuf, 16);
	} else {
		/* store output data into an aligned buffer */
		vec_st( v1, 0, (vector unsigned char *) outBuffer );
	}
	return;
}

void vBlockDecrypt128(
	keyInstance *key, 
	BYTE *input, 
	BYTE *outBuffer)
{
	vector unsigned char block[2];
	register vector unsigned char v1, v2;
	
	if ( (long) input & 0x0fL ) {
		/* manually re-align - the compiler is supposed to 16-byte align this for us */
		BYTE	localBuf[16];
		vdprintf(("vBlockDecrypt128: unaligned input\n"));
		if((unsigned)localBuf & 0xf) {
			vdprintf(("vBlockDecrypt128: unaligned localBuf!\n"));
		}
		memmove(localBuf, input, 16);
		v1 = vec_ld(0, (vector unsigned char *)localBuf);
	}
	else {
		vdprintf(("vBlockDecrypt128: aligned input\n"));
		v1 = vec_ld( 0, (vector unsigned char *) input );
	}

	/* parse input stream into rectangular array */
	/* FIXME - do we need to zero v2 (or something)? */
	block[0] = vec_perm(v1, v2,
		(vector unsigned char) ( 0,  4,  8, 12, 16, 20, 24, 28,  1,  
		5,  9, 13, 17, 21, 25, 29 ) );
	block[1] = vec_perm( v1, v2, 
		(vector unsigned char) ( 2,  6, 10, 14, 18, 22, 26, 30,  3,  
		7, 11, 15, 19, 23, 27, 31 ) );

	vRijndaelDecrypt(block, key->keyLen, 128, (vector unsigned char *) key->keySched);

	/* parse rectangular array into output ciphertext bytes */
	v1 = vec_perm(block[0], block[1], 
		(vector unsigned char) ( 0,  8, 16, 24,  1,  9, 17, 25,  2, 
		10, 18, 26,  3, 11, 19, 27 ) );
	v2 = vec_perm(block[0], block[1], 
		(vector unsigned char) ( 4, 12, 20, 28,  5, 13, 21, 29,  6, 
		14, 22, 30,  7, 15, 23, 31 ) );

	if ( (long) outBuffer & 0x0fL ) {
		/* store output data into a non-aligned buffer */
		BYTE	localBuf[16];
		vec_st(v1, 0, (vector unsigned char *) localBuf );
		memmove(outBuffer, localBuf, 16);
	} else {
		/* store output data into an aligned buffer */
		vec_st( v1, 0, (vector unsigned char *) outBuffer );
	}
	return;
}

#endif	/* defined(__ppc__) && defined(ALTIVEC_ENABLE) */
