/*## (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.*;

public class GsecHal {
 public static final int GSEC_BASE   = 0xb9200000;

 public static final int OFF_CONFIG  = 0x6000;
 public static final int OFF_STATUS  = 0x6004;
 public static final int OFF_INS     = 0x6008;
 public static final int OFF_DATA    = 0x600c;
 public static final int OFF_KEYS    = 0x6420;

 public static final int OFF_CW_KEYS = 0x6100;

 public static final int ALGO_TDES   = 0x00;
 public static final int ALGO_AES    = 0x01;
 public static final int SECURE_MODE = 0x02;

 public static final int MAXKEY     = 0x08;

 public static int ReadStatus() {
  return IOMem.in_dword(GSEC_BASE+OFF_STATUS);
 }

 public static void ClearStatus() {
  int status=0x0b;
  IOMem.out_dword(GSEC_BASE+OFF_STATUS,status);
  ReadStatus();
 }

 public static void DataWrite(int v1,int v2) {
  IOMem.out_dword(GSEC_BASE+OFF_DATA,v1); 
  IOMem.out_dword(GSEC_BASE+OFF_DATA+0x04,v2); 
 }

 public static int[] DataRead() {
  int data[]=new int[4];

  data[0]=IOMem.in_dword(GSEC_BASE+OFF_DATA+0x0c);
  data[1]=IOMem.in_dword(GSEC_BASE+OFF_DATA+0x08);
  data[2]=IOMem.in_dword(GSEC_BASE+OFF_DATA+0x04);
  data[3]=IOMem.in_dword(GSEC_BASE+OFF_DATA);

  return data;
 }

 public static void WaitForComplete() {
  while(true) {
   int status=ReadStatus()&0x0b;
   if (status!=0) break;
    else Utils.sleep(100);
  }
 }

 public static void InstWrite(int cmd) {
  int v1=IOMem.in_dword(GSEC_BASE+OFF_DATA);
  int v2=IOMem.in_dword(GSEC_BASE+OFF_DATA+0x04);

  IOMem.out_dword(GSEC_BASE+OFF_INS,cmd);

  WaitForComplete();

  v1=IOMem.in_dword(GSEC_BASE+OFF_DATA);
  v2=IOMem.in_dword(GSEC_BASE+OFF_DATA+0x04);
 
  ClearStatus();
 }

 public static void Init(int cfg) {
  InstWrite(0);
  IOMem.out_dword(GSEC_BASE+OFF_CONFIG,cfg); 
 }

 public static void Init2(int cfg) {
  //InstWrite(0);
  IOMem.out_dword(GSEC_BASE+OFF_CONFIG,cfg); 
 }

 public static void Reset() {
  InstWrite(0);
 }

 public static void CKCalc(int cmd,int v1,int v2,int v3,int v4) {
  DataWrite(v4,v3);
  InstWrite(cmd);
  DataWrite(v2,v1);
  InstWrite(cmd);
 }

 public static void CKCalc_0(int v1,int v2,int v3,int v4) {
  CKCalc(2,v1,v2,v3,v4);
 }

 public static void CKCalc_1(int v1,int v2,int v3,int v4) {
  CKCalc(1,v1,v2,v3,v4);
 }

 public static void DecryptSCK(int cmd,int idx,int v1,int v2) {
  if ((cmd!=3)&&(cmd!=4)) return;

  DataWrite(v2,v1);
  InstWrite(((idx&0xff)<<8)|cmd);
  InstWrite(((idx&0xff)<<8)|cmd);
 }

 public static void DecryptSCK_0(int idx,int v1,int v2) {
  DecryptSCK(3,idx,v1,v2);
 }

 public static void DecryptSCK_1(int idx,int v1,int v2) {
  DecryptSCK(4,idx,v1,v2);
 }

 public static void CustCmd(int cmd,int v1,int v2) {
  DataWrite(v2,v1);
  InstWrite(cmd);
 }

 public static void CopyTCN() {
  CustCmd(0x05,0x00000000,0x00000000);
 }

 public static int get_chip_id() {
  GSecHandler.lock();

  CopyTCN();

  int data[]=DataRead();
  GSecHandler.unlock();

  int id=data[3];

  return id;
 }

 public static int[] GetKey(int idx) {
  int data[]=new int[4];

  data[0]=IOMem.in_dword(GSEC_BASE+OFF_KEYS+idx*0x10);
  data[1]=IOMem.in_dword(GSEC_BASE+OFF_KEYS+idx*0x10+0x04);
  data[2]=IOMem.in_dword(GSEC_BASE+OFF_KEYS+idx*0x10+0x08);
  data[3]=IOMem.in_dword(GSEC_BASE+OFF_KEYS+idx*0x10+0x0c);

  return data;
 }

 public static int[] GetCW(int idx) {
  int data[]=new int[4];

  data[0]=IOMem.in_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10);
  data[1]=IOMem.in_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10+0x04);
  data[2]=IOMem.in_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10+0x08);
  data[3]=IOMem.in_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10+0x0c);

  return data;
 }

 public static void SetCW(int idx,int[] data) {
  IOMem.out_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10,data[0]);
  IOMem.out_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10+0x04,data[1]);

  IOMem.out_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10+0x08,0);
  IOMem.out_dword(GSEC_BASE+OFF_CW_KEYS+idx*0x10+0x0c,0);
 }

 public static void DumpKeys() {
  int buf=NativeCode.getInstance().malloc(MAXKEY*0x10);  

  ApiMonitor.log("GsecHal Keys");

  for(int i=0;i<MAXKEY*0x10;i+=4) {
   int val=IOMem.in_dword(GSEC_BASE+OFF_KEYS+i);
   Memory.write_dword(buf+i,val);
  }

  byte tab[]=Utils.buf2barray(buf,MAXKEY*0x10);
  ApiMonitor.log_buf(tab);

  NativeCode.getInstance().free(buf);  
 } 

 public static void DumpMem(int off,int size) {
  int buf=NativeCode.getInstance().malloc(size);  

  ApiMonitor.log("GsecHal Mem off: "+Utils.hex_value(off,8));

  for(int i=0;i<size;i+=4) {
   int val=IOMem.in_dword(GSEC_BASE+off+i);
   Memory.write_dword(buf+i,val);
  }

  byte tab[]=Utils.buf2barray(buf,size);
  ApiMonitor.log_buf(tab);

  NativeCode.getInstance().free(buf);  
 } 

}
