/*
	多数決論理関数による鍵発生
	鍵を2つ与え、1つをマスターとし、1つをレファレンスとして、残り2個の鍵を発生する

	Created by Osaka(2011/2/28)

*/
import java.io.* ;
import java.util.* ;
import java.security.* ;


public class MajorityFunc2 {

public final static String RANDOM_ALGORITHM = "SHA1PRNG" ;
//public final static int	KEY_LENGTH	= 16 ;
public final static int	KEY_LENGTH	= 4 ;

public static void main( String args[ ] ) {
	MajorityFunc2 mf = new MajorityFunc2( ) ;

	BitSet bs = mf.getRandBitset( ) ;
	BitSet a1 = mf.getRandBitset( ) ;

	BitSet[ ] x = mf.generateKey( bs, a1 ) ; // 主鍵bs、副鍵a1から多数決論理関数に合致する2鍵a2、a3を求める
		BitSet a2 = x[ 0 ] ;
		BitSet a3 = x[ 1 ] ;
System.out.print( "k:" ) ;
	mf.dispBitset( bs ) ;
System.out.println( "----------------------------------" ) ;
System.out.print( "x:" ) ;
	mf.dispBitset( a1 ) ;
System.out.print( "y:" ) ;
	mf.dispBitset( a2 ) ;
System.out.print( "z:" ) ;
	mf.dispBitset( a3 ) ;
	BitSet b123 = mf.majority( a1, a2, a3 ) ; // 多数決論理関数で、副鍵から主鍵を求める
System.out.println( "----------------------------------" ) ;
System.out.print( "f:" ) ;
	mf.dispBitset( b123 ) ;
}


// BitSetの多数決論理関数 majority(x, y, z) = ( (x&y) | (y&z) | (z&x) )
public BitSet majority( BitSet x, BitSet y, BitSet z ) {
	BitSet b12 = (BitSet)x.clone( ) ;
		b12.and ( y ) ;
	BitSet b23 = (BitSet)y.clone( ) ;
		b23.and ( z ) ;
	BitSet b31 = (BitSet)z.clone( ) ;
		b31.and ( x ) ;

		b23.or( b31 ) ;
	BitSet b123 = (BitSet)b12.clone( ) ;
		b123.or( b23 ) ;
	return b123 ;
}


// 乱数バイト数のBitSetを得る
public BitSet getRandBitset( ) {
	byte[ ] bb = getRandom( ) ;

	BitSet bs = bytesToBitSet( bb ) ;
	bb = toByteArray( bs ) ;
	return bs ;
}


// 乱数の発生
public byte[ ] getRandom( ) {
	byte[ ] b = new byte[ KEY_LENGTH ] ;
	try {
		SecureRandom random = SecureRandom.getInstance( RANDOM_ALGORITHM ) ;
		byte[ ] seed = random.generateSeed( KEY_LENGTH ) ;
		random.setSeed( seed ) ;
		random.nextBytes( b ) ;
	} catch ( Exception e ) {
		e.printStackTrace( ) ;
	}
	return b ;
}


// bitsetの表示
public void dispBitset( BitSet bitset ) {
	for ( int i = 0; i < KEY_LENGTH * 8; i++ ) {
		if ( bitset.get( i ) )
System.out.print( "1" ) ;
		else
System.out.print( "0" ) ;
	}
System.out.print( "\n" ) ;
}


// 2つの鍵b1(主鍵)、b2(副鍵)から多数決論理関数に合致する2つの副鍵a2、a3を生成
public BitSet[ ] generateKey( BitSet b1, BitSet b2 ) {
	BitSet a2 = new BitSet( ) ;
	BitSet a3 = new BitSet( ) ;

	for ( int i = 0; i < KEY_LENGTH * 8; i++ ) {
//b1(0) = majority(b2(1),a2(0),a3(0))
		if ( !b1.get( i ) && b2.get( i ) ) {
			a2.set( i, false ) ;
			a3.set( i, false ) ;
		}
//b1(1) = majority(b2(0),a2(1),a3(1))
		else if ( b1.get( i ) && !b2.get( i ) ) {
			a2.set( i, true ) ;
			a3.set( i, true ) ;
		}
//b1(0) = majority(b2(0),a2(0),a3(0)), majority(b2(0),a2(0),a3(1)), majority(b2(0),a2(1),a3(0))
		else if ( !b1.get( i ) && !b2.get( i ) ) {
			int v = (int)( Math.floor( Math.random( ) * 3 ) ) ;
			switch ( v ) {
				case 0 :
					a2.set( i, false ) ;
					a3.set( i, false ) ;
					break ;
				case 1 :
					a2.set( i, false ) ;
					a3.set( i, true ) ;
					break ;
				case 2 :
					a2.set( i, true ) ;
					a3.set( i, false ) ;
					break ;
			}
		}
//b1(1) = majority(b2(1),a2(0),a3(1)), majority(b2(1),a2(1),a3(0)), majority(b2(1),a2(1),a3(1))
		else if ( b1.get( i ) && b2.get( i ) ) {
			int v = (int)( Math.floor( Math.random( ) * 3 ) ) ;
			switch ( v ) {
				case 0 :
					a2.set( i, false ) ;
					a3.set( i, true ) ;
					break ;
				case 1 :
					a2.set( i, true ) ;
					a3.set( i, false ) ;
					break ;
				case 2 :
					a2.set( i, true ) ;
					a3.set( i, true ) ;
					break ;
			}
		}
	}
	BitSet x[ ] = new BitSet[ 2 ] ;
	x[ 0 ] = a2 ;
	x[ 1 ] = a3 ;
	return x ;
}


public BitSet bytesToBitSet( byte[ ] bytes ) {
	BitSet bits = new BitSet( bytes.length ) ;
	int i = 0, j, k ;
	bits.clear( ) ;
	if ( bytes.length > KEY_LENGTH ) i = 1 ;
	for ( k = 0; i < bytes.length; i++ ) {
		for ( j = 0x80; j != 0; j >>>= 1, k++ ) {
			if ( ( bytes[ i ] & j ) != 0 )
				bits.set( k ) ;
		}
	}
//System.out.println( "bytesToBitSet : " + bytes.length + " " + bits.size( ) ) ;
	return  bits ;
}


// Returns a byte array of at least length 1.
// The most significant bit in the result is guaranteed not to be a 1
// (since BitSet does not support sign extension).
// The byte-ordering of the result is big-endian which means the most significant bit is in element 0.
// The bit at index 0 of the bit set is assumed to be the least significant bit.
public byte[ ] toByteArray( BitSet bits ) {
	byte[ ] bytes = new byte[ bits.length( ) / 8 + 1 ] ;
	for ( int i = 0; i < KEY_LENGTH * 8; i++ ) {
		if ( bits.get( i ) ) bytes[ bytes.length - i / 8 - 1 ] |= 1 << ( i % 8 ) ;
	}
	bytes = checkByte( bytes ) ;
	return bytes ;
}


// なぜか1バイト0x00が入るため、これを削除
private byte[ ] checkByte( byte[ ] a ) {
	byte[ ] b = new byte[ KEY_LENGTH ] ;
	if ( a.length > KEY_LENGTH ) {
		for ( int i = 1; i < a.length; i++ ) b[ i - 1 ] = a[ i ] ;
	}
	else b = a ;
	return b ;
}


}