/*## (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 Loader {
 public static class Key {
  int dkoff;
  int dksize;
  int dkkey;
  int rdoff;
  int rdsize;
  int rddata;

  int key;
  int klen;
  boolean inited;

  public Key(int koff,int ksize,int doff,int dsize) {
   this.dkoff=koff;
   this.dksize=ksize;
   this.rdoff=doff;
   this.rdsize=dsize;
   inited=false;
  }

  public Key(int key,int klen) {
   this.key=key;
   this.klen=klen;
   inited=true;
  }

  public boolean inited() {
   return inited;
  }

  public void init() {
   int fd=Syscall.open("/dev/mtd0",FSDefs.O_RDONLY);

   if (fd>0) {
    dkkey=NativeCode.getInstance().malloc(dksize);
    Syscall.lseek(fd,dkoff,FSDefs.SEEK_SET);
    FS.sys_read_fully(fd,dkkey,dksize);

    rddata=NativeCode.getInstance().malloc(rdsize);
    Syscall.lseek(fd,rdoff,FSDefs.SEEK_SET);
    FS.sys_read_fully(fd,rddata,rdsize);

    klen=rdsize;
    key=NativeCode.getInstance().malloc(klen);
    Utils.memset(key,0,klen);

    TwofishMem.decrypt_ecb(rddata,rdsize,dkkey,dksize,key);

    Syscall.close(fd);
    inited=true;
   }
  }

  public int data() {
   return key;
  }

  public int size() {
   return klen;
  }

  public void print(String name) throws Throwable {
   Output.text("- "+name);
   byte buf[]=Utils.buf2barray(data(),size());
   MPEG.print_buf(2,buf);
  }

  public void finalize() {
   NativeCode nc=NativeCode.getInstance();

   if (dkkey!=0) {
    nc.free(dkkey);
   }

   if (rddata!=0) {
    nc.free(rddata);
   }

   if (key!=0) {
    nc.free(key);
   }
  }
 }
 
 public static final int DECRYPTION_KEY_OFF1   = 0x4204;
 public static final int DECRYPTION_KEY_SIZE1  = 0x10;
 public static final int ENCRYPTED_DATA_OFF1   = 0x4100;
 public static final int ENCRYPTED_DATA_SIZE1  = 0x50;

 public static final int DECRYPTION_KEY_OFF2   = 0x4044;
 public static final int DECRYPTION_KEY_SIZE2  = 0x10;
 public static final int ENCRYPTED_DATA_OFF2   = 0x25C84;
 public static final int ENCRYPTED_DATA_SIZE2  = 0x50;

 private static Hashtable hwkeys;
 private static Hashtable static_keys;

 static {
  hwkeys=new Hashtable();
  
  Key k=new Key(DECRYPTION_KEY_OFF1,DECRYPTION_KEY_SIZE1,ENCRYPTED_DATA_OFF1,ENCRYPTED_DATA_SIZE1);
  hwkeys.put(new Integer(SysInfoDefs.ITI5800S),k);

  k=new Key(DECRYPTION_KEY_OFF1,DECRYPTION_KEY_SIZE1,ENCRYPTED_DATA_OFF1,ENCRYPTED_DATA_SIZE1);
  hwkeys.put(new Integer(SysInfoDefs.ITI5800SX),k);

  k=new Key(DECRYPTION_KEY_OFF2,DECRYPTION_KEY_SIZE2,ENCRYPTED_DATA_OFF2,ENCRYPTED_DATA_SIZE2);
  hwkeys.put(new Integer(SysInfoDefs.ITI2850ST),k);

  k=new Key(DECRYPTION_KEY_OFF2,DECRYPTION_KEY_SIZE2,ENCRYPTED_DATA_OFF2,ENCRYPTED_DATA_SIZE2);
  hwkeys.put(new Integer(SysInfoDefs.ITI2851S),k);

  static_keys=new Hashtable();

  int hw_0110_key[]={
   0x47, 0x45, 0x52, 0x47, 0x20, 0x5b, 0x23, 0xbc, 0xc6, 0xcf, 0x09, 0x5a, 0x55, 0x4c, 0x5c, 0x50,
   0xee, 0x52, 0x91, 0x5f, 0xac, 0x6b, 0xbe, 0x3e, 0xf2, 0x7f, 0xd4, 0xe4, 0x34, 0xf6, 0xea, 0x7e
  };

  int hw_0133_key[]={
   0x47, 0x45, 0x52, 0x47, 0x20, 0x90, 0x28, 0xc1, 0x12, 0x8b, 0xe9, 0x20, 0x7e, 0x97, 0xd0, 0x18,
   0x8c, 0x2e, 0x9a, 0xd0, 0xdb, 0x26, 0x2b, 0x83, 0x38, 0x4d, 0xd4, 0x16, 0xcc, 0x3e, 0x04, 0x9f
  };

  static_keys.put(new Integer(SysInfoDefs.ITI2850ST),hw_0110_key);
  static_keys.put(new Integer(SysInfoDefs.ITI2851S),hw_0133_key);
 }

 public static Key get_key(int hwid) {
  if (Config.isHERMES()&&(Syscall.getuid()!=0)) return null;

  Key key=(Key)hwkeys.get(new Integer(hwid));

  if (key==null) return null;

  if (!key.inited()) {
   key.init();
  }

  return key;
 }

 public static int[] get_static_key(int hwid) {
  if (Config.isCARBO(hwid)) {
   int static_key[]=(int[])static_keys.get(new Integer(hwid));
   return static_key;
  } else return null;
 }

 public static Key get_upgrade_key(int hwid) {
  Key key=null;

  if (Config.isHERMES(hwid)) {
   SSU.UpgradeInfo ui=SSU.lookup_upgrade(SysInfo.hwid);
 
   if (ui!=null) {
    byte posfdef[]=new byte[7];
    posfdef[0]=(byte)((ui.ssu_unique_download_id>>8)&0xff);
    posfdef[1]=(byte)(ui.ssu_unique_download_id&0xff);
    posfdef[2]=(byte)-1;
    posfdef[3]=(byte)0;
    posfdef[4]=(byte)0;
    posfdef[5]=(byte)0;
    posfdef[6]=(byte)0;

    byte posfmask[]=new byte[7];
    posfmask[0]=(byte)-1;
    posfmask[1]=(byte)-1;
    posfmask[2]=(byte)-1;
    posfmask[3]=(byte)-1;
    posfmask[4]=(byte)-1;
    posfmask[5]=(byte)-1;
    posfmask[6]=(byte)-1;

    byte data[]=MPEGSniffer.get_section(null,ui.pid,ui.ssu_table_id,posfdef,posfmask);

    if (data!=null) {
     HermesUpgrade.Info info=HermesUpgrade.extract_info(data);

     if (info!=null) {
      int ksize=info.key.length<<2;
      int kdata=NativeCode.getInstance().malloc(ksize);

      for(int i=0;i<info.key.length;i++) {
       Memory.write_byte(kdata+4*i,info.key[i]&0xff);
       Memory.write_byte(kdata+4*i+1,(info.key[i]>>8)&0xff);
       Memory.write_byte(kdata+4*i+2,(info.key[i]>>16)&0xff);
       Memory.write_byte(kdata+4*i+3,(info.key[i]>>24)&0xff);
      }

      key=new Key(kdata,ksize);      
     }
    }    
   }
  } else

  if (Config.isCARBO(hwid)) {
   if (SysInfo.hwid==hwid) {
    Key loader_key=get_key(hwid);

    if (loader_key!=null) {
     int ksize=0x20;

     int kdata=NativeCode.getInstance().malloc(ksize);
     Utils.memcpy(loader_key.data(),kdata,ksize);

     key=new Key(kdata,ksize);
    }
   } else {
    int static_key[]=get_static_key(hwid);

    int ksize=static_key.length;

    int kdata=NativeCode.getInstance().malloc(ksize);

    for(int i=0;i<ksize;i++) {
     Memory.write_byte(kdata+i,static_key[i]&0xff);
    }    

    key=new Key(kdata,ksize);
   }
  }

  return key;
 }
}
