/ notes.org
notes.org
  1  * diff between original and backdoored
  2  
  3  #+BEGIN_EXAMPLE
  4  radiff2 des-variant/ROM.bin ROM.BIN                                                                                                                                                            ──(Tue,Jun14)─┘
  5  0x00000242 ab => 12 0x00000242
  6  0x00001b44 67 => 6f 0x00001b44
  7  0x00001b79 b1 => f5 0x00001b79
  8  0x00001bc5 67 => 6f 0x00001bc5
  9  0x00001bec b1 => f5 0x00001bec
 10  0x00001c5d c3e4f52df52ef52f800be0a3 => 747fc0e07441c0e0c3e4f52d 0x00001c5d
 11  0x00001c6a 2de0a3f52ee0f52f5005902c4d80039072d7792de493b93000500167f7a309b935f27935e493f7a309b93cf8121cc0902c5c792d7835e493c7f6a30908b83cf6121cc0753f2d22a93f67053fb93406121cc0753f2d22c0e0c082c083852d25852e26852f2785302885312985322a85332b85342ca26113a25913a25113a24913a24113a23913a23113a22913f531a26313a25b13a25313a24b13a24313a23b13a23313a22b13f532a26513a25d13a25513a24d13a24513a23d13a2
 12  187     => 2ef52f800fc083c082e0a3f52de0a3f52ee0f52f5005902c4d80039072d7792de493b93000500167f7a309b935f27935e493f7a309b93cf8907f41782de6f0a308b835f9121d04902c5c792d7835e493c7f6a30908b83cf6e493f7907f41782de6f0a308b835f9121d04d082d083e0f525a3e0f526a3e0f527907f46e525f0a3e526f0a3e527f0753f2d22a93f67053fb93406121d04753f2d22c0e0c082c083c000907f41782d800dc0e0c082c083c000907f447830e0f6a308b8 0x00001c6a
 13  
 14  one byte (0x35) same
 15  
 16  0x00001d26 13a22d13f533a26713a25f13a25713a24f13a24713a23f13a23713a22f13f534a26013a25813a25013a24813a24013a23813a23013a22813f52d
 17  58      => f97808e52e653213e52d6531137934c719b92cfbd8ed907f41782de6f0a308b835f9852d25852e26852f2785302885312985322a85332b85342c 0x00001d26
 18  0x00001d61 62 => 32 0x00001d61
 19  0x00001d64 5a => 45 0x00001d64
 20  0x00001d67 52 => 67 0x00001d67
 21  0x00001d6a 4a => 56 0x00001d6a
 22  0x00001d6d 42 => 2e 0x00001d6d
 23  0x00001d70 3a => 4f 0x00001d70
 24  0x00001d73 32 => 5c 0x00001d73
 25  0x00001d76 2a => 3d 0x00001d76
 26  0x00001d79 2e => 31 0x00001d79
 27  0x00001d7b 64 => 2a 0x00001d7b
 28  0x00001d7e 5c => 64 0x00001d7e
 29  0x00001d81 54 => 3e 0x00001d81
 30  0x00001d84 4c => 37 0x00001d84
 31  0x00001d87 44 => 52 0x00001d87
 32  0x00001d8a 3c => 46 0x00001d8a
 33  0x00001d8d 34 => 4a 0x00001d8d
 34  0x00001d90 2c => 5d 0x00001d90
 35  0x00001d93 2f => 32 0x00001d93
 36  0x00001d95 66 => 39 0x00001d95
 37  0x00001d98 5e => 28 0x00001d98
 38  0x00001d9b 56 => 59 0x00001d9b
 39  0x00001da1 46 => 44 0x00001da1
 40  0x00001da4 3e => 34 0x00001da4
 41  0x00001da7 36 => 62 0x00001da7
 42  0x00001daa 2e => 50 0x00001daa
 43  0x00001dad 307910e92403837012803d00010101010101000101010101010000e538a2e4e53b13f53be53a
 44  38      => 33a25313a24813a23f13a24713a26013a23113a22c13a25e13f534a23b13a25713a25813a22d 0x00001dad
 45  0x00001dd4 f53ae53913f539e53813f538e53713f537e536
 46  19      => a26613a24113a23513a24c13f52da23813a254 0x00001dd4
 47  0x00001de8 f536e53513f535e53892e3f538e538
 48  15      => a24d13a25b13a23013a26113a22f13 0x00001de8
 49  0x00001df8 e4e53b13f53be53a13f53ae53913f539e538
 50  18      => 4213f52ea24913a25a13a23c13a22b13a251 0x00001df8
 51  0x00001e0b f538e53713f537e53613f536e53513f535e53892e3f538e535
 52  25      => a23313a24313a26513f52fa26313a23a13a25f13a23613a24b 0x00001e0b
 53  0x00001e25 922c => a229 0x00001e25
 54  0x00001e28 9245 => a240 0x00001e28
 55  0x00001e2b 9230 => a255 0x00001e2b
 56  0x00001e2e 923b13922d13923313924113923de5361313923513922a13923a13924413
 57  30      => f530780875259c752602302f037526057935e538a2e4923fa23fe713f709 0x00001e2e
 58  0x00001e4d 28139232139240e537139229131392391392431392341313923813922be5381313923c13924213923113926413925013924a139265e53913925413925c1313926313924b1313925a13
 59  73      => 3fb93cf5e538a23f92e3f538d526e2e52523f525907f417927e0f7c526f0a309b92df6e0c526f0a3e0c526f0907f41e526f085272c85282be52a85292af529a925e53ba2e09240a2e2 0x00001e4d
 60  0x00001e97 51e53a13924813 => 34a2e39242a2e5 0x00001e97
 61  0x00001e9f 611313 => 36a2e6 0x00001e9f
 62  0x00001ea3 5813925313 => 44e53aa2e0 0x00001ea3
 63  0x00001ea9 6013924c139255e53b13925913926213925213
 64  19      => 28a2e19246a2e3922aa2e49238a2e6922ca2e7 0x00001ea9
 65  0x00001ebd 4913925d1313924d13 => 3ae539a2e1922ea2e2 0x00001ebd
 66  0x00001ec7 5b9020c2e53033e52d336525543f93540ff525e52e13e52d1313136526543f9354f04225902102e52d33
 67  42      => 3ca2e5923ee538a2e09230a2e39232a2e49241a2e69235a2e79243e537a2e19237a2e29245a2e49229a2 0x00001ec7
 68  0x00001ef2 2e336527543f93540ff526e52f13e52e1313136528543f9354f04226902142e52e33e52f336529543f93540ff527
 69  46      => 9247a2e7922be536a2e09239a2e2922da2e3923ba2e5922fa2e6923de535a2e1923fa2e49231a2e79233e5256229 0x00001ef2
 70  0x00001f21 3013e52f131313652a543f9354f04227902182e52f33e53033652b543f93540ff528e52d13e530
 71  39      => 26622ae527622be528622c8925752800752629a9268729e4a24f92e1a24e92e0b4003fe528245a 0x00001f21
 72  0x00001f49 1313652c543f9354f04228a23713a22e13a23b13a23c13a24413a23313a24313a238136531c52df531a22813a23613a23e13a24113a22c13a23913a24613a231136532c52ef532a22913a22f13a23f13a23513a24713a24213a22a13a230136533c52ff533a23a13a23413a24513a22d13a23d13a23213a22b13a240136534c530f53419e96003021db0852d25852e26852f2785302885312985322a85332b85342ca24f13a22f13a25713a23713a25f13a23f13a26713a24713f52da24e13a22e13a25613a23613a25e13a23e13a26613a24613f52ea24d13a22d13a25513a23513a25d13a23d13a26513a24513f52fa24c13a22c13a25413a23413a25c13a23c13a26413a24413f530a24b13a22b13a25313a23313a25b13a23b13a26313a24313f531a24a13a22a13a25213a23213a25a13a23a13a26213a24213f532a24913a22913a25113a23113a25913a23913a26113a24113f533a24813a22813a25013a23013
 73  356     => f9e74001c4540ff527540c600ae527c39404f527022052e5286408245a13f9e74001c4540ff954032527f527e9030325275403440cf527022052b40147e528245a13f9e74001c4540ff5275403600ae527c39401f527022052e5270303f527e5286402245a13f9e74001c4540ff954032527f527e903032527540323234403f527022052b40243e528245a13f9e74001c4540ff527540cb40c27e5275403f527e5286404245a13f9e74001c4540ff954032527f527e9030325275403f527022052e5272404f527022052e528245a13f9e74001c4540ff5275403b4032ae527540c0303f527e5286401245a13f9e74001c4540ff954032527f527e90303252754032323f52780020527e528245a13f9e74008540ff7e527c4800554f0f7e52747f70528e52854036009e5292323f529021f380526a926b92d028003021f36e52dc532c52ec52fc534c533c531c530f52de535a2e09231a2e39233a2e69235e538a2e29234 0x00001f49
 74  0x000020ae 5813a23813 => e59237e539 0x000020ae
 75  0x000020b4 6013a24013f534d083d082d0e022f7029caf645b3a9c1be74639cdb4a04a8278e513df2609c578d1b36e218d5ef0c0bf35daf7c269052e51
 76  56      => e49230a2e79232e53ba2e19236e526792d13c75001c4c709b935f6902102792de7540f93540fc7c4540f9354f047f70974102582f582e435 0x000020b4
 77  0x000020ed ec1b28dc76bf13066d44f99aa0e2845d37784ea19be55b88fd0631ddaa799213c49fec2417b0664b886cdf3245c709ae735ab0f12ebbc824926c56833dd00b4aa706b17f441ef5e12ff2895deaa96037dcc51e9873349201ef9e457b8652f8cc236d3ea7d08374ba
 78  104    =>  f583b935e2d8028003021e35d000d083d082d0e022f4af8ad13b02e820cd65961c47b3795e188be3ae7d4a94df6930bc56f50721c2bd729c59ae17f36124c840dfe68a350b274d56c891b37084f5efd2ae3a1c0b69479f805c0a68a1ebb92d75f31e32d6c4b3aeed 0x000020ed
 79  0x00002156 451be0bcf8ad2f5a16c1d967572d6ad6e2980c652443bf809bf4c11afdc1807f3ea7d902433e75e9a85b16bc
 80  44      => 91d438266ac0fb75825f4c175927164ddbef049cf0b862ce3aa17385185d476ec5a09bfa32e38c01df7429b6 0x00002156
 81  0x000021c1 d3 => 82 0x000021c1
 82  0x00002364 67 => 6f 0x00002364
 83  0x0000236a b1 => f5 0x0000236a
 84  0x0000247c b1 => f5 0x0000247c
 85  0x000025b5 1cc0 => 1d13 0x000025b5
 86  0x000025d8 67 => 6f 0x000025d8
 87  0x0000261d b1 => f5 0x0000261d
 88  0x00002733 b1 => f5 0x00002733
 89  
 90  #+END_EXAMPLE
 91  
 92  one value at 0x242 changed from 0xAB to 0x12
 93  
 94  this is in this function:
 95  
 96  #+begin_SRC c
 97  char FUN_CODE_0241(char *param_1,char param_2) {
 98    char cVar1 = 0x12;
 99    if ((char)((ushort)param_1 >> 8) == 0x40) {
100      return 0x40;
101    }
102    do {
103      cVar1 += *param_1++;
104    } while (param_2 != (char)((ushort)param_1 >> 8));
105    return cVar1 + -1;
106  }
107  #+END_SRC
108  
109  this looks like a checksum function? the one that returns the same
110  output for both firmwares?
111  
112  the blob that changed is between 1c5d - 2181
113  
114  there is one changed byte at 21c1 which is at the end of an unknown
115  section. quite outside of the main blob.
116  
117  all other diffs are calls inside the main blob, which makes sense,
118  that in the des version functions were starting at different
119  addresses. targets of these calls are:
120   - 5 times: 1c6f (parametric entry)
121   - 7 times: 1cf5 (crypt_byte)
122   - once 1d13     (sbt_short)
123  
124  calls to 1c6f always are followed by calls to 1cf5, however there is
125  two calls to 1cf5 where no preceeding 1c6f call is nearby.
126  
127  1c5d    set_key() calls prepare(0x7f41, 0) called from .1aad.
128          is used for calculating the 4 char key ID (15xA == JJDI)
129          zeroes 0x2d-0x2f - which seems to be the "nonce"
130          pushes 0x7f41 to the stack before joining with parametric_entry
131  1c6f    parametric_entry(input, flag) calls prepare(input, flag); called from 1b42 1bc3 2362      25d6
132          depending on the flag, it either selects a 15 char password
133          from 0x72d7, or a fixed one from 0x2c4d ("2V58RGKLXN49TKD")
134  1cf5    crypt_byte called from                                        1b77 1bea 2368 247a 261b 2731
135  
136  1c7e    prepare() does some magic, calls sbt_full() 2x
137  1d04    sbt_full called from prepare 2x and from crypt_byte
138  1d13    sbt_short called from 25b4
139  
140  in same fn: p_e  xmsf xmsf
141  1aed        1b42 1b77        called from a switch(acc) case 0x1f !!! decrypt !!!
142  1ba5        1bc3 1bea        called from a switch(acc) case 0x9f (assume 0x80 is shift key?) !!! encrypt !!!
143  22f0        2362 2368 247a   (not called from anywhere?)
144  259c        25d6 261b        (called from two locs, one is the fn below)
145  267e             2731(calls fn containing prev line before)
146  
147  * decompiled
148  
149  by hand and verified
150  
151  1aed - 2182
152  
153  2362 - 2563
154  259c - 2641
155  279f - 27d4
156  2807 - 2862
157  2beb - 2c1e
158  
159  * fn at 1cf5
160  
161  original assembly:
162  #+begin_src asm
163   **************************************************************
164   *                                                            *
165   *  FUNCTION                                                  *
166   **************************************************************
167  undefined __stdcall FUN_CODE_1cf5(byte param_1)
168  undefined         ACC:1          <RETURN>
169  byte              ACC:1          param_1
170  FUN_CODE_1cf5                                   XREF[6]:     CODE:1b77(c), CODE:1bea(c),
171                                                               CODE:2368(c), CODE:247a(c),
172                                                               FUN_CODE_259c:261b(c),
173                                                               FUN_CODE_2677:2731(c)
174  CODE:1cf5 a9 3f           MOV        R1,DAT_INTMEM_3f                                 = ??
175  CODE:1cf7 67              XRL        A,@R1
176  CODE:1cf8 05 3f           INC        DAT_INTMEM_3f                                    = ??
177  CODE:1cfa b9 34 06        CJNE       R1,#0x34,LAB_CODE_1d03
178  CODE:1cfd 12 1d 04        LCALL      FUN_CODE_1d04                                    undefined FUN_CODE_1d04(undefine
179  LAB_CODE_1d03                                   XREF[1]:     CODE:1cfa(j)
180  CODE:1d03 22              RET
181  #+end_src
182  
183  My interpretation:
184  #+begin_src c
185  byte INTMEM[256];
186  
187  byte xor_maybe_1d04(byte res) {
188     byte r1 = INTMEM[0x3f];
189     res ^= INTMEM[r1];
190     INTMEM[0x3f]++;
191     if(r1 == 0x34) {
192        fn_1d04(res);
193        INTMEM[0x3f]=0x2d;
194     }
195     return res;
196  }
197  #+end_src
198  Interestingly ghidra decompiles this differently
199  #+begin_src c
200  void FUN_CODE_1cf5(byte param_1) {
201    byte bVar1;
202    byte *pbVar2;
203  
204    pbVar2 = DAT_INTMEM_3f;
205    bVar1 = *DAT_INTMEM_3f;
206    DAT_INTMEM_3f = DAT_INTMEM_3f + 1;
207    if (pbVar2 == &DAT_INTMEM_34) {
208      FUN_CODE_1d04(param_1 ^ bVar1);
209      DAT_INTMEM_3f = &DAT_INTMEM_2d;
210    }
211    return;
212  }
213  #+end_src
214  * fn at 2cd1
215  
216  looks like some kind of virtual machine?
217  the opcodes seem to have the following structure
218    0x80 is end of "program"
219    if top bit is set, then there is no "param", and bottom nibble contains the offset in the jmptbl
220    if not, copy 4 byte param from 0x7fxx (where xx is derived from low 4 bits of opcode) to intmem 0x2e
221    top for bits in opcode is the address in the jumptable for the op
222  
223    0x2a seems to be the 32bit Accumulator,
224    0x2e the 32bit B register.
225    0x25 seems to be treated like a flag register, where
226       .0 is the sign bit
227       .1 is for zero (Z)
228       .2 is for signed overflow(OV)
229    0x32-33 seems to be the IP/PC
230  
231  void fn2cd1(uint16_t sp) {
232     *((uint16*) INTMEM + 32) = *((uint16_t*) (INTMEM + sp));
233     uint16_t ptr = sp;
234     uint8_t tmp = PROGMEM[ptr++];
235     *((uint16*) INTMEM + 32) = ptr;
236     if(tmp & 0x80) {
237        tmp &= 7f; // clear top bit since it was a JBC opcode
238        // continue at 2d02
239        if(tmp == 0) return; // to one byte after call to this fn
240                             // all this does if after the call is 0x80
241                             // is to set INTMEM[32] to the address of the 0x80
242        tmp >>= (tmp >> 1) | (tmp << 7);
243     } else {
244        uint8_t tmp1 = tmp & 0xf;
245        tmp1 = (tmp1 >> 2) | (tmp1 << 6);
246        ptr = (0x7f << 8) | tmp1;
247        memcpy(INTMEM + 0x2e; EXTMEM + ptr; 4);
248  
249        tmp = (tmp << 4) | (tmp >> 4);
250        tmp = ((tmp >> 1) | (tmp << 7)) & 0x1e;
251     }
252  
253     tmp1 = tmp + 0xa;
254     // push  0x2cd5; the start of this function - the pop into intmem+32
255     // push table[tmp1]   \ might be -1 and 0 the next.
256     // push table[tmp1+1] /
257  }
258  
259  the vm sub op at 2dc8 essentially executes
260      a = ((a*b) >> 16) & 0xffffffff;
261      where a and b are 32bit, the result of the multiplication is 64,
262      and thus the result is the middle 32 bits of the multiplication.
263  
264  * printing strings
265  
266  lcall puts_and_jump()
267  string terminated by 0x80 bit
268  then jumps to location after end of string
269  12 05 3e is the prefix (call to puts_and_jmp) to each string
270  
271  asm code looks like this: 'call print; ds "string\0";' print() pops the return address for the string, prints it, then jumps to the address after the string. ghidra: "Flow Override: CALL_RETURN (CALL_TERMINATOR)" - any idea how to fix the flow?
272  
273  * deleted message
274  
275  0x23 xx 0xfe 0xff
276  xx possibly message id 1-9
277  
278  if xx == 0 then clear message is
279  
280  0xfe 0xff
281  
282  at wherever (BANK3_R2 << 8 | BANK3_R3) points (INTMEM[0x19:0x1a])
283  
284  * test vectors
285  
286  (truncated) HELLO WORLD -> BAMLK GAFFJ EDBCP
287  setting the Nonce to BAMLK and encrypting "HELLO WORLD" gives this
288                             BAMLK GAFFJ EOBCP EEIMP CDAFP
289  
290  NAVEL PLUIS
291  (truncated) HCAMM MBKJO DGCNC
292  (truncated) EGLLA IKMHG PHHJL
293  LEEIC BITKY DBCFE DPKLB ECILE
294  JDMNJ REJHP CPBLO JPJDD BEDNG
295  
296  * key strength
297   - the key entered on the console is 15 chars long,
298   - and each char can be one of 39 values (a-z0-9.,- )
299   - total key entropy: log(39**15,2) = 79.28103328293373
300  
301  * output
302  
303   - one iteration of the sbt is an 8 byte cipherstream
304   - of which the top 2 bits of each byte are discarded during the
305     XORing over the plaintext
306   - each 8 chars of plaintext are thus xored with 48bit of keystream
307   - the first 5 bytes of the keystream are leaked via the nonce, see
308     next section
309  
310  * nonces?
311  
312  only the first 3 letters are used as nonce, the last two are not used
313  for anything.. and only the bottom nibble of the bytes are used for
314  the "nonce"
315  
316  12 bit nonce - birthday collision 50% at 64 messages
317  
318  the nonces are sampled from the timer, which if no interrupt happens
319  means that each value is increased by 11 - also these 5 bytes are
320  xored with the first 5 bytes of the previous encryptions first
321  cipherblock - and thus can potentially leak those - but!
322   - only the bottom 4 bits of the cipherblock for each byte is leaked
323   - this was already like this in the non-backdoored version. - was it really?
324  
325  example nonces - generated by a real device:
326  
327  BAMLK
328  HCAMM
329  EGLLA
330  LEEIC
331  JDMNJ
332  
333  see this from the reimplementation:
334  #+BEGIN_SRC c
335    uint8_t nonce[5];
336    uint16_t dptr = 0;
337  
338    for(int i = 5; i>0; i--, dptr++) {
339      tmp = i*11; //TL0;
340      tmp = (nonce[dptr] ^ tmp) & 0xf;
341  #+END_SRC
342  here we xor with the previous cipherblock 5 bytes...
343  #+BEGIN_SRC c
344      tmp = tmp + 1;
345      nonce[dptr] = tmp;
346      //printf("%c", 0x40|tmp); //putc_maybe_print(tmp,skip_print);
347      output[optr++]=0x40|tmp;
348    }
349    //printf(" "); //putc_maybe_print(' ',skip_print);
350    output[optr++]=' ';
351    parametric_entry(nonce,key);
352  #+END_SRC
353  here we initialize the state and generate the 1st cipherblock
354  #+BEGIN_SRC c
355    //copy result of parametric_entry to 0x72d2
356    memcpy(nonce, cipherblock, 5); //1bc6..1bcf
357  #+END_SRC
358  in the last line we copy the cipherblock 5 bytes to the nonce, which
359  will be used in line 6 of the example
360  
361  trying to figure out from the testvector nonces if TL0 actually always
362  increases by 11, but it seems to be rather 15
363  
364  #+BEGIN_SRC c
365    uint8_t key[15]="\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01";
366  
367    uint8_t nonce[5][5]={{2, 1, 13, 12, 11},  // BAMLK
368                         {8, 3, 1, 13, 13},   // HCAMM
369                         {5, 7, 12, 12, 1},   // EGLLA
370                         {12, 5, 5, 9, 3},    // LEEIC
371                         {10, 4, 13, 14, 10}};// JDMNJ
372    for(int n = 0; n<4;n++) {
373      parametric_entry(nonce[n],key);
374      for(int i=0;i<5;i++) {
375        fprintf(stderr, "%02x ", ((nonce[n+1][i]-1) ^ crypt_byte(0)) & 0xf);
376      }
377      fprintf(stderr,"\n");
378    }
379  #+END_SRC
380  
381  
382  * 1aed - decrypt
383  
384  checks if what ever intmem[42:43] points to at is
385  5 letter codes seperated by spaces and terminated by \xfe.
386  if so, uses the first 5 letter block with
387  parametric_entry() and then does something weird with each
388  remaining letter block with bitwise manipulations together
389  with intmem[0x4a:0x4c] and calls crypt byte on the result 
390  of the manipulation.
391  
392  #+BEGIN_SRC c
393    uint8_t r6, tmp, r2, r3;
394    uint8_t tbit, CY;
395    uint16_t dptr = INTMEM[42:43];
396    uint8_t tmp;
397    do {
398      for(int i=6;i>0;i--) {
399        tmp = EXTMEM[dptr];
400        if(tmp == 0xfe) continue;
401        dptr++;
402        if((i==1 && tmp & 0x80) || tmp!=' ') {
403          INTMEM[44:45] = dptr;
404          printf("ERROR IN TEXT, PRESS LEFT ARROW!");
405          return;
406        }
407      }
408      if(EXTMEM[dptr]!=0xfe) continue;
409      parametric_entry(INTMEM[42:43],0);
410      r6=0;
411      dptr = INTMEM[42:43] + 6;
412      do {
413        uint8_t r0 = 0x4a;
414        for(uint8_t r2 = 5; r2>0; r2--) {
415          tmp = EXTMEM[dptr];        // 1b58
416          CY=tmp<0xfe;               // set carry due to CJNE at 1b59
417          if(tmp!=0xfe) dptr++;
418          tmp--; // 1b5f dec a
419          tmp = (tmp << 4) | (tmp >> 4); // 1b60 swap a
420          for(r3 = 4;r3>0;r3--) {
421            // 1b61 rlc a
422            tbit=CY;
423            CY=tmp & 0x80;
424            tmp=(tmp<<1) | tbit;
425            // 1b62 xchg A, INTMEM[r0]
426            uint8_t tmp1 = tmp;
427            tmp=INTMEM[r0];
428            INTMEM[r0]=tmp1;
429            // 1b63 rrc a
430            tbit = CY;
431            CY = tmp & 1;
432            tmp = tbit | (tmp >> 1);
433            // 1b64 xchg a, intmem[r0]
434            tmp1 = tmp;
435            tmp=INTMEM[r0];
436            INTMEM[r0]=tmp1;
437            r0++;
438            CY=r0<0x4d;
439            if(r0==0x4d) r0=0x4a;
440          }
441        }
442        for(r0 = 0x4a;r0!=0x4d;r0++) {
443          tmp = INTMEM[r0]; // 1b71
444          CY=r0<0x4c;
445          if(r0==0x4c) {
446            // 1b75 rrc a
447            tbit=CY
448            CY=tmp&1;
449            tmp = (tbit << 7) | (tmp >> 1);
450          }
451  
452          // 1b76 rrc a
453          tbit=CY
454          CY=tmp&1;
455          tmp = (tbit << 7) | (tmp >> 1);
456  
457          tmp = crypt_byte(tmp);
458  
459          tmp &= 0x3f; // 1b7a
460          putc(tmp); // really putc_maybe_print()
461        }
462      }
463      r6++;
464      if(((r6 & 3)==0) && (r5!=0)) get_keypress(); // r5 == skip_print
465  
466      // 1b91..
467     } while (EXTMEM[dptr++]!=0xfe && EXTMEM[dptr]!=' ' && EXTMEM[dptr]!=0xfe);
468    if(r5!=0) {
469      print_crlf();
470    }
471    return
472  #+END_SRC
473  
474  ** what is the sketchy part doing?
475  
476  see also sketchy.py
477  
478  tmp        CY  0x4a       0x4b       0x4c
479  abcd efgh   1  ABCD EFGH  0123 4567  qrst uvwx
480  efgh abcd   1  ABCD EFGH  0123 4567  qrst uvwx                   // swap a
481  fgha bcd1   e  ABCD EFGH  0123 4567  qrst uvwx                   // rlc a
482  ABCD EFGH   e  fgha bcd1  0123 4567  qrst uvwx                   // XCHG a, @r0
483  eABC DEFG   H  fgha bcd1  0123 4567  qrst uvwx                   // rrc a
484  fgha bcd1   H  eABC DEFG  0123 4567  qrst uvwx                   // xchg a, @r0++
485                                                                   // loop
486  ghab cd1H   f  eABC DEFG  0123 4567  qrst uvwx                   // rlc a
487  0123 4567   f  eABC DEFG  ghab cd1H  qrst uvwx                   // xchg a,@rc0
488  f012 3456   7  eABC DEFG  ghab cd1H  qrst uvwx                   // rrc a
489  ghab cd1H   7  eABC DEFG  f012 3456  qrst uvwx                   // xchg a,@r0++
490                                                                   // loop
491  habc d1H7   g  eABC DEFG  f012 3456  qrst uvwx                   // rlc a
492  qrst uvwx   g  eABC DEFG  f012 3456  habc d1H7                   // xchg a,@r0
493  gqrs tuvw   x  eABC DEFG  f012 3456  habc d1H7                   // rrc a
494  habc d1H7   x  eABC DEFG  f012 3456  gqrs tuvw                   // xchg a,@r0++
495                                                                   // loop, r0=0x4a
496  abcd 1H7x   h  eABC DEFG  f012 3456  gqrs tuvw                   // rlc a
497  eABC DEFG   h  abcd 1H7x  f012 3456  gqrs tuvw                   // xchg a,@r0
498  heAB CDEF   G  abcd 1H7x  f012 3456  gqrs tuvw                   // rrc a
499  abcd 1H7x   G  heAB CDEF  f012 3456  gqrs tuvw                   // xchg a,@r0++
500  
501  * addresses in external ram
502  
503  e0..ff used in putc
504  
505  6000
506  
507  6800
508   6802,04 both are uint16_t
509  
510  7000
511  7100
512  7145 RW
513  7155 =55
514  
515  72c9,a
516  
517  72d7 contains the 15 char password used at least for the key id calculation
518  
519  72f9
520  730e
521  
522  730a,d possibly keyboard related?
523  
524  8400 - used in int1 handler
525  8402
526  8404-7
527  840a
528  840e
529  8410-19 (ff95ff8f8f8f2525)
530  
531  * emulators
532  there is two emulators:
533   - mcu.py
534   - emu8051-cli
535  both seem to have bugs, apparently SUBB is confusing to implement correctly
536  emu-0x3102.py uses mcu, ./emu8051-3102.py uses the binary emulators
537  run both, and diff the output. after SUBB the OV flag differs in both outputs
538  
539  
540  * ctrlvar changes over 8 rounds
541  
542  1 ['abcdefgh', 'ijklmnop', 'qrstuvwx', 'yzAB CDEF', 'GHIJKLMN', 'OPQRSTUV', 'WXYZ0123']
543  0 ['xyzABabc', 'defghijk', 'lmnopqrs', 'tuvw Z012', '3CDEFGHI', 'JKLMNOPQ', 'RSTUVWXY']
544  0 ['vwxyzABa', 'bcdefghi', 'jklmnopq', 'rstu XYZ0', '123CDEFG', 'HIJKLMNO', 'PQRSTUVW']
545  1 ['tuvwxyzA', 'Babcdefg', 'hijklmno', 'pqrs VWXY', 'Z0123CDE', 'FGHIJKLM', 'NOPQRSTU']
546  1 ['opqrstuv', 'wxyzABab', 'cdefghij', 'klmn QRST', 'UVWXYZ01', '23CDEFGH', 'IJKLMNOP']
547  1 ['jklmnopq', 'rstuvwxy', 'zABabcde', 'fghi LMNO', 'PQRSTUVW', 'XYZ0123C', 'DEFGHIJK']
548  0 ['efghijkl', 'mnopqrst', 'uvwxyzAB', 'abcd GHIJ', 'KLMNOPQR', 'STUVWXYZ', '0123CDEF']
549  0 ['cdefghij', 'klmnopqr', 'stuvwxyz', 'ABab EFGH', 'IJKLMNOP', 'QRSTUVWX', 'YZ0123CD']
550  1 ['abcdefgh', 'ijklmnop', 'qrstuvwx', 'yzAB CDEF', 'GHIJKLMN', 'OPQRSTUV', 'WXYZ0123']
551  
552  * sbt data flow
553  
554  0.  input: state, message_key
555  1.  state = lfsr(64)
556  1.a output state                                                                              state is fully known
557  2.  cipherblock = fix_bit_perm(state)                                                         cipherblock is fully known
558  3.  iterate 8 times
559    a rotate right both 28b halves of the message key by [5, 2, 2, 5, 5, 5, 2, 2]
560    b ctrl_words[i:0..3] = state[3-i] ^ msgkey_bits(some permutation)                           message key
561    c rotate state right by one byte
562    d for each nibble of the cipherblock, modify it based on ctrl_words & cipherblock           message key
563    e permute cipherblock bytes, new order: 3 4 6 7 2 1 5
564    f nibble swap cipherblock bytes if bits 26, 54, 1, 29, 4, 32, 7, 35 of message key are set  message key
565    g SBOX each cipherblock nibble
566  4.  output: cipherblock