/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.util.*;

public class LocalECMReader extends ECMProvider {
 public static final int TIME_DIFF = 2*1000;

 ECMFilter filter;

 public LocalECMReader(int channel) {
  super(channel);

  filter=ECMFilter.create(channel);
 }
 
 private boolean sync_with_decrypt() {
  boolean res=false;

  while(true) {
   long time=System.currentTimeMillis();
   long diff=time-CW.decrypt_time();

   ApiMonitor.log("sync_with_decrypt: "+diff);

   if (diff>TIME_DIFF) {
    break;
   }

   if (diff<TIME_DIFF) {
    res=true;
    break;
   }

   Utils.sleep(100);
  }

  return res;
 }

 public void run(Object args[]) {
  try {
   filter.start();
   running=true;

   int cw[]=new int[2];

   while(running) {
    byte data[]=filter.get_data();

    if (data==null) break;

    ApiMonitor.log("---------------------------------------------------");
    ApiMonitor.log("LOCAL ECM READER DATA");
    ApiMonitor.log_buf(data);

    byte enccw[]=Conax.decrypt_ecm(data);

    int cur_idx=CW.cur_key_num();
    int nxt_idx=CW.nxt_key_num();

    int curkey=((int)data[0])&0x01;

    int idx=1-curkey;

    int b1=((int)enccw[0x07+0x0f*idx])&0xff;
    int b2=((int)enccw[0x08+0x0f*idx])&0xff;
    int b3=((int)enccw[0x09+0x0f*idx])&0xff;
    int b4=((int)enccw[0x0a+0x0f*idx])&0xff;
    int b5=((int)enccw[0x0b+0x0f*idx])&0xff;
    int b6=((int)enccw[0x0c+0x0f*idx])&0xff;
    int b7=((int)enccw[0x0d+0x0f*idx])&0xff;
    int b8=((int)enccw[0x0e+0x0f*idx])&0xff;

    if (Config.isHERMES()) {
     cw[0]=(b5<<24)|(b6<<16)|(b7<<8)|b8;
     cw[1]=(b1<<24)|(b2<<16)|(b3<<8)|b4;
    } else

    if (Config.isCARBO()) {
     cw[0]=(b1<<24)|(b2<<16)|(b3<<8)|b4;
     cw[1]=(b5<<24)|(b6<<16)|(b7<<8)|b8;
    }

    ApiMonitor.log("KEY crypted   CW: "+Utils.hex_value(cw[0],8)+" "+Utils.hex_value(cw[1],8));

    int cur_cw[]=CW.decrypt(cw);

    ApiMonitor.log("KEY plaintext CW: "+Utils.hex_value(cur_cw[0],8)+" "+Utils.hex_value(cur_cw[1],8));

    idx=curkey;

    b1=((int)enccw[0x07+0x0f*idx])&0xff;
    b2=((int)enccw[0x08+0x0f*idx])&0xff;
    b3=((int)enccw[0x09+0x0f*idx])&0xff;
    b4=((int)enccw[0x0a+0x0f*idx])&0xff;
    b5=((int)enccw[0x0b+0x0f*idx])&0xff;
    b6=((int)enccw[0x0c+0x0f*idx])&0xff;
    b7=((int)enccw[0x0d+0x0f*idx])&0xff;
    b8=((int)enccw[0x0e+0x0f*idx])&0xff;

    if (Config.isHERMES()) {
     cw[0]=(b5<<24)|(b6<<16)|(b7<<8)|b8;
     cw[1]=(b1<<24)|(b2<<16)|(b3<<8)|b4;
    } else

    if (Config.isCARBO()) {
     cw[0]=(b1<<24)|(b2<<16)|(b3<<8)|b4;
     cw[1]=(b5<<24)|(b6<<16)|(b7<<8)|b8;
    }

    ApiMonitor.log("NEXT KEY crypted   CW: "+Utils.hex_value(cw[0],8)+" "+Utils.hex_value(cw[1],8));

    int next_cw[]=CW.decrypt(cw);

    ApiMonitor.log("NEXT KEY plaintext CW: "+Utils.hex_value(next_cw[0],8)+" "+Utils.hex_value(next_cw[1],8));

    process_handlers(cur_idx,cur_cw,nxt_idx,next_cw);
   }   

  } catch(Throwable t) {}
 }

 public void stop() {
  super.stop();
  filter.stop();
 }

}
