package com.mpaa.decss;


/**
 * Title:        LinearFeedbackShiftRegisterPair.java
 * Description:  This class encapsulates the functionality of the two linear feedback shift registers that the CSS algorithm uses to 
 *               produce a cipher stream.
 * @author DiatriBe
 * @version 1.0
 */

public class LinearFeedbackShiftRegisterPair implements Cloneable
{
	private int lfsr17;      // 17 bit LFSR with taps at bits 14 and 0 (rel 0).  Note that the bit 0 tap is actually the same as the output value of the shift register.
	private int lfsr25;      // 25 bit LFSR with taps at bits 12, 4, 3, and 0 (rel 0).  Note that the bit 0 tap is actually the same as the output value of the shift register.

	private int lfsr17Output = 0;
	private int lfsr25Output = 0;

	private boolean classic = false;

	public LinearFeedbackShiftRegisterPair()
	{
	}

	/**
	 * Constructor that initializes the linear feedback shift registers with the 40 bit value in the title key.
	 *
	 * @param titleKey title key to use as the values for the shift registers.
	 */
	public LinearFeedbackShiftRegisterPair(TitleKey titleKey)
	{
		setRegisters(titleKey);
	}

	/**
	 * Constructor that initializes the linear feedback shift registers with the specific values passed.
	 *
	 * @param lfsr17InitialValue initial value for the LFSR17.  Value should consist of 17 bits of data.
	 * @param lfsr25InitialValue initial value for the LFSR25.  Value should consist of 25 bits of data.
	 */
	public LinearFeedbackShiftRegisterPair(int lfsr17InitialValue, int lfsr25InitialValue)
	{
		setRegisters(lfsr17InitialValue, lfsr25InitialValue);
	}

	/**
	 * Set the value of the LFSR17.
	 *
	 * @param lfsr17Value new value for the LFSR17.  Value should consist of 17 bits of data.
	 */
	public void setLfsr17Register(int lfsr17Value)
	{
		lfsr17 = lfsr17Value;
	}

	/**
	 * Set the value of the LFSR25.
	 *
	 * @param lfsr25Value new value for the LFSR25.  Value should consist of 25 bits of data.
	 */
	public void setLfsr25Register(int lfsr25Value)
	{
		lfsr25 = lfsr25Value;
	}

	/**
	 * Set the value of both shift registers.
	 *
	 * @param lfsr17Value new value for the LFSR17.  Value should consist of 17 bits of data.
	 * @param lfsr25Value new value for the LFSR25.  Value should consist of 25 bits of data.
	 */
	public void setRegisters(int lfsr17Value, int lfsr25Value)
	{
		setLfsr17Register(lfsr17Value);
		setLfsr25Register(lfsr25Value);
	}

	/**
	 * Returns the value of the LFSR17.
	 *
	 * @return The current value for the LFSR17.  Value will consist of 17 bits of data.
	 */
	public int getLfsr17Register()
	{
		return lfsr17;
	}

	/**
	 * Returns the value of the LFSR25.
	 *
	 * @return The current value for the LFSR25.  Value will consist of 25 bits of data.
	 */
	public int getLfsr25Register()
	{
		return lfsr25;
	}

	/**
	 * Will initialize the linear feedback shift registers with the 40 bit value in the title key.
	 *
	 * @param titleKey title key to use as the values for the shift registers.
	 */
	public void setRegisters(TitleKey titleKey)
	{
		MirrorBitsTable mirrorTable = MirrorBitsTable.getInstance();

		// Map the key values into the LFS registers according to reversed bitmapping as defined by CSS

		lfsr17 = (mirrorTable.getAt(titleKey.getAt(0)) << 9) | mirrorTable.getAt(titleKey.getAt(1));
		lfsr17 |= 0x100;        // Set bit 8 to avoid null cycling

		lfsr25 = ((mirrorTable.getAt(titleKey.getAt(2)) & 0xE0) << 17) | ((mirrorTable.getAt(titleKey.getAt(2)) & 0x1F) << 16) | (mirrorTable.getAt(titleKey.getAt(3)) << 8) | mirrorTable.getAt(titleKey.getAt(4));
		lfsr25 |= 0x200000;     // Set bit 21 to avoid null cycling
	}

	/**
	 * Sets whether to use the classic method of actually shifting the registers one bit at a time or the quicker method
	 *  of just computing the new LFSR byte values directly.  Classic mode is turned off by default (because it is slower).
	 */
	public void useClassic(boolean classic)
	{
		this.classic = classic;
	}

	/**
	 * This method executes one clock cycle of the LF 17 shift register.  It it only used when classic = true.
	 */
	private void clockLfsr17()
	{
		// Grab the values in bits 14 and 0 (rel 0) and use those XORed as the feedback bit for the LRSR roll (and output)
		int lfsr17Feedback = ((lfsr17 & 0x4000) >> 14) ^ ((lfsr17 & 0x01) >> 0);
		lfsr17 = (lfsr17 >> 1) | (lfsr17Feedback << 16);        // Roll the LFSR and then add the feedback bit as the MSB
		lfsr17Output = (lfsr17Output << 1) | lfsr17Feedback;    // Stick the feedback bit into the LSB of the (shifted) output
	}

	/**
	 * This method executes one clock cycle of the LF 25 shift register.  It it only used when classic = true.
	 */
	private void clockLfsr25()
	{
		// Grab the values in bits 12, 4, 3 and 0 (rel 0) and use those XORed as the feedback bit for the LRSR roll (and output)
		int lfsr25Feedback = ((lfsr25 & 0x1000) >> 12) ^ ((lfsr25 & 0x10) >> 4) ^ ((lfsr25 & 0x08) >> 3) ^ ((lfsr25 & 0x01) >> 0);
		lfsr25 = (lfsr25 >> 1) | (lfsr25Feedback << 24);       // Roll the LFSR and then add the feedback bit as the MSB
		lfsr25Output = (lfsr25Output << 1) | lfsr25Feedback;   // Stick the feedback bit into the LSB of the (shifted) output
	}

	/**
	 * This method will clock the 17 bit shift register eight times in succession (in classic mode), or will use the quicker
	 * method of calculating the new shift register value directly by XORing the taps into the rotated register.
	 */
	public void clockLfsr17OneByte()
	{
		if (classic)
		{
			for (int count = 0; count < 8; count++)
				clockLfsr17();
		}
		else
		{
			// This method takes the shortcut of just rotating the register right and XORing in the bit 14 tap by calculating the product of its effect
			lfsr17 = ((lfsr17 << 9) | (lfsr17 >> 8)) & 0x1FFFF;         // Rotate lfsr17 right by 8 bits
			int bits = lfsr17 & 0x3FC0;     // These are the bits that were pulled out by the bit 14 tap
			int xorProduct = ((bits << 3) ^ (bits << 6) ^ (bits << 9)) & 0x1FFFF;       // Calculate the tap product and mask off the extra bits at the top
			lfsr17 ^= xorProduct;
		}
	}

	/**
	 * This method will clock the 25 bit shift register eight times in succession, or will use the quicker
	 * method of calculating the new shift register value directly by XORing the taps into the rotated register.
	 */
	public void clockLfsr25OneByte()
	{
		if (classic)
		{
			for (int count = 0; count < 8; count++)
				clockLfsr25();
		}
		else
		{
			// This method takes the shortcut of just rotating the register right and XORing in the bit 3, 4, and 12 taps by calculating the product of their effect
			int xorProduct = ((lfsr25 << 14) ^ (lfsr25 << 13) ^ (lfsr25 << 5)) & 0x1FE0000;       // Calculate the tap product and mask off the extra bits at the bottom
			lfsr25 = ((lfsr25 << 17) | (lfsr25 >> 8)) & 0x1FFFFFF;         // Rotate lfsr25 right by 8 bits
			lfsr25 ^= xorProduct;
		}
	}

	/**
	 * This method will clock both shift registers eight times in succession.
	 */
	public void clockOneByte()
	{
		clockLfsr17OneByte();
		clockLfsr25OneByte();
	}

	/**
	 * Will salt the current contents of the LFS registers with the values in the salt array.  Values are mapped into the registers
	 * using a reversed bitmapping.
	 */
	public void salt(int[] salt)
	{
		MirrorBitsTable mirrorTable = MirrorBitsTable.getInstance();

		lfsr17 ^= (mirrorTable.getAt(salt[0]) << 9) | mirrorTable.getAt(salt[1]);
		lfsr25 ^= ((mirrorTable.getAt(salt[2]) & 0xE0) << 17) | ((mirrorTable.getAt(salt[2]) & 0x1F) << 16) | (mirrorTable.getAt(salt[3]) << 8) | mirrorTable.getAt(salt[4]);
	}

	/**
	 * Will return the last byte of output that was generated by LFSR17.  The last byte of output is equal to the value
	 * in the highest eight bits of the register.
	 *
	 * For certain modes of operation of the LFSRs, the output may be inverted before the value is summed to produce
	 * the cipher stream.
	 *
	 * @param invert if true will invert (ones complement) the output byte.
	 * @return a byte worth of LFSR17 output
	 */
	public int getLfsr17Output(boolean invert)
	{
		MirrorBitsTable mirrorTable = MirrorBitsTable.getInstance();

		int maskedOutput;

		if (classic)
			maskedOutput = mirrorTable.getAt(lfsr17Output & 0xFF);
		else
			maskedOutput = lfsr17 >> 9;

		if (invert)
			maskedOutput = 255 - maskedOutput;

		return maskedOutput;
	}

	/**
	 * Will return the last byte of output that was generated by LFSR25.  The last byte of output is equal to the value
	 * in the highest eight bits of the register.
	 *
	 * For certain modes of operation of the LFSRs, the output may be inverted before the value is summed to produce
	 * the cipher stream.
	 *
	 * @param invert if true will invert (ones complement) the output byte.
	 * @return a byte worth of LFSR25 output
	 */
	public int getLfsr25Output(boolean invert)
	{
		MirrorBitsTable mirrorTable = MirrorBitsTable.getInstance();

		int maskedOutput;

		if (classic)
			maskedOutput = mirrorTable.getAt(lfsr25Output & 0xFF);
		else
			maskedOutput = lfsr25 >> 17;

		if (invert)
			maskedOutput = 255 - maskedOutput;

		return maskedOutput;
	}

	/**
	 * Will clock the 17 bit shift register backward one byte.  Note, no classic implementation for this method has been
	 * supplied, this is left as an exercise for the reader!
	 */
	public void clockLfsr17BackwardOneByte()
	{
		// This method takes the shortcut of just rotating the register left and XORing in the bit 14 tap by calculating the product of its effect
		int bits = lfsr17 & 0x3FC0;     // These are the bits that were pulled out by the bit 14 tap
		lfsr17 = ((lfsr17 << 8) | (lfsr17 >> 9)) & 0x1FFFF;         // Rotate lfsr17 left by 8 bits
		int xorProduct = (bits >> 6) & 0x1FFFF;       // Calculate the tap product and mask off the extra bits at the top
		lfsr17 ^= xorProduct;
	}

	/**
	 * Will clock the 25 bit shift register backward one byte.  Note, no classic implementation for this method has been
	 * supplied, this is left as an exercise for the reader!
	 */
	public void clockLfsr25BackwardOneByte()
	{
		// This method takes the shortcut of just rotating the register right and XORing in the bit 3, 4, and 12 taps by calculating the product of their effect
		lfsr25 = ((lfsr25 << 8) | (lfsr25 >> 17)) & 0x1FFFFFF;         // Rotate lfsr25 left by 8 bits
		int xorProduct = ((lfsr25 >> 3) ^ (lfsr25 >> 4) ^ (lfsr25 >> 12)) & 0xFF;       // Calculate the tap product for one iteration of the feedback
		xorProduct = xorProduct ^ (xorProduct >> 3) ^ (xorProduct >> 4) ^ (xorProduct >> 6);        // Now calculate the tap product for the other three iterations (the feedback of the 3 & 4 tap on the 3 & 4 tap and then again)
		lfsr25 ^= xorProduct;
	}

	/**
	 * Will clock both registers backward one byte.  Note, no classic implementation for this method has been supplied, this
	 * is left as an exercise for the reader!
	 */
	public void clockBackwardOneByte()
	{
		clockLfsr17BackwardOneByte();
		clockLfsr25BackwardOneByte();
	}

	/**
	 * Extracts the title key from the shift registers in exactly the opposite manner of how it is manipulated when
	 * it is placed into the LFSRs.
	 */
	public TitleKey getKey()
	{
		MirrorBitsTable mirrorTable = MirrorBitsTable.getInstance();

		TitleKey titleKey = new TitleKey();

		for (int index = 0; index < 5; index++)
		{
			int keyPart = 0;

			switch (index)
			{
				case 0:
					keyPart = mirrorTable.getAt(lfsr17 >> 9);
					break;

				case 1:
					keyPart = mirrorTable.getAt(lfsr17 & 0xFF);
					break;

				case 2:
					keyPart = mirrorTable.getAt(((lfsr25 >> 17) & 0xE0) | ((lfsr25 >> 16) & 0x1F));
					break;

				case 3:
					keyPart = mirrorTable.getAt((lfsr25 >> 8) & 0xFF);
					break;

				case 4:
					keyPart = mirrorTable.getAt(lfsr25 & 0xFF);
					break;

			}
			titleKey.setAt(index, keyPart);
		}

		return titleKey;
	}

	public Object clone() throws CloneNotSupportedException
	{
		// A bitwise clone is fine for us, so just call the base to get that
		return super.clone();
	}

	/**
	 * This main is just used as a test harness for the LinearFeedbackShiftRegisterPair class.
	 */
	public static void main(String[] args)
	{
		TitleKey titleKey = new TitleKey(0xA7, 0x07, 0xBF, 0x53, 0x84);
		int[] salt = { 0xB5, 0x63, 0x97, 0x64, 0x77 };
		int[] sector = { 0x84, 0xA1, 0x6F, 0x69 };
		int[] output = new int[10];

		LinearFeedbackShiftRegisterPair testShiftRegister = new LinearFeedbackShiftRegisterPair(titleKey);

		testShiftRegister.salt(salt);

		int carry = 0;

		for (int index = 0; index < sector.length; index++)
		{
			MirrorBitsTable mirrorTable = MirrorBitsTable.getInstance();

			testShiftRegister.clockOneByte();

			int cipherStreamOutput = testShiftRegister.getLfsr17Output(true) + testShiftRegister.getLfsr25Output(false) + carry;

			int tableSubstitution = SubstitutionTable.getAt(sector[index]);
			output[index] = (cipherStreamOutput ^ tableSubstitution) & 0xFF;

			carry = cipherStreamOutput >> 8;
		}
	}
}

