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; } } }