TSEC: Difference between revisions

Motezazer (talk | contribs)
The bootloader does not use the RTC but the timer
Line 445: Line 445:
   
   
  // Set the target port for memory transfers
  // Set the target port for memory transfers
  // Target will now be 0 (crypto?)
  // Target will now be 0 (DMA)
  xtargets(0);
  xtargets(0);
   
   
Line 467: Line 467:
   
   
  // Clear all crypto registers, except c6 which is used for auth
  // Clear all crypto registers, except c6 which is used for auth
  *(u32 *)c0 ^= *(u32 *)c0;
  cxor(c0, c0);
  *(u32 *)c1 = *(u32 *)c0;
  cmov(c1, c0);
  *(u32 *)c2 = *(u32 *)c0;
  cmov(c2, c0);
  *(u32 *)c3 = *(u32 *)c0;
  cmov(c3, c0);
  *(u32 *)c4 = *(u32 *)c0;
  cmov(c4, c0);
  *(u32 *)c5 = *(u32 *)c0;
  cmov(c5, c0);
  *(u32 *)c7 = *(u32 *)c0;
  cmov(c7, c0);
   
   
  // Update engine specific IO (crypto?)
  // Update engine specific IO (crypto?)
Line 501: Line 501:
   
   
  // Partially unknown fuc5 instruction
  // Partially unknown fuc5 instruction
  // Likely forces propagation of permissions, hiding all cX registers
  // Likely forces a change of permissions
  acl_chmod(c0, c0);
  acl_chmod(c0, c0);
   
   
  // Clear all crypto registers and propagate permissions
  // Clear all crypto registers and propagate permissions
  *(u32 *)c0 ^= *(u32 *)c0;
  cxor(c0, c0);
  *(u32 *)c1 ^= *(u32 *)c1;
  cxor(c1, c1);
  *(u32 *)c2 ^= *(u32 *)c2;
  cxor(c2, c2);
  *(u32 *)c3 ^= *(u32 *)c3;
  cxor(c3, c3);
  *(u32 *)c4 ^= *(u32 *)c4;
  cxor(c4, c4);
  *(u32 *)c5 ^= *(u32 *)c5;
  cxor(c5, c5);
  *(u32 *)c6 ^= *(u32 *)c6;
  cxor(c6, c6);
  *(u32 *)c7 ^= *(u32 *)c7;
  cxor(c7, c7);
   
   
  // Exit Authenticated Mode
  // Exit Authenticated Mode
Line 638: Line 638:
   
   
  return res;
  return res;
==== keygen ====
This method takes '''type''' and '''mode''' as arguments and generates a key.
u32 seed_buf[0x10];
// Read a 16 bytes seed based on supplied type
/*
    Type 0: "CODE_SIG_01" + null padding
    Type 1: "CODE_ENC_01" + null padding
*/
get_seed(seed_buf, type);
// This will write the seed into crypt register c0
crypt_store(0, seed_buf);
// fuc5 csecret instruction
// Load selected secret into crypt register c1
csecret(c1, 0x26);
// fuc5 ckeyreg instruction
// Binds c1 register as the key for enc/dec operations
ckeyreg(c1);
// fuc5 cenc instruction
// Encrypts seed_buf (in c0) using keyreg value as key into c1
cenc(c1, c0);
// fuc5 csigenc instruction
// Encrypt code sig with c1 register as key
csigenc(c1, c1);
// Copy the result into c4 (will be used as key)
cmov(c4, c1);
// Do key expansion (for decryption)
if (mode != 0)
    ckexp(c4, c4); // fuc5 ckexp instruction
return;
==== enc_buffer ====
This method takes '''buf''' (a 16 bytes buffer) and '''size''' as arguments and encrypts the supplied buffer.
// Set first 3 words to null
*(u32 *)(buf + 0x00) = 0;
*(u32 *)(buf + 0x04) = 0;
*(u32 *)(buf + 0x08) = 0;
// Swap halves (b16, b32 and b16 again)
hswap(size);
// Store the size as the last word
*(u32 *)(buf + 0x0C) = size;
// This will write buf into crypt register c3
crypt_store(0x03, buf);
// fuc5 ckeyreg instruction
// Binds c4 register (from keygen) as the key for enc/dec operations
ckeyreg(c4);
// fuc5 cenc instruction
// Encrypts buf (in c3) using keyreg value as key into c5
cenc(c5, c3);
// This will read into buf from crypt register c5
crypt_load(0x05, buf);
return;
==== do_crypto ====
This is the method responsible for all crypto operations performed during Stage 1. It takes '''src_addr''', '''src_size''', '''iv_addr''', '''dst_addr''', '''mode''' and '''crypt_ver''' as arguments.
// Check for invalid source data size
if (!src_size || (src_size & 0x0F))
  exit();
// Check for invalid source data address
if (src_addr & 0x0F)
  exit();
// Check for invalid destination data address
if (dst_addr & 0x0F)
  exit();
// Use IV if available
if (iv_addr)
{
  // This will write the iv_addr into crypt register c5
  crypt_store(0x05, iv_addr);
}
else
{
  // Clear c5 register (use null IV)
  cxor(c5, c5);
}
// Use key in c4
ckeyreg(c4);
// AES-128-ECB? decrypt
if (mode == 0x00)
{
  // Create crypto script with 5 instructions
  cs0begin(0x05);
  cxsin(c3);                  // Read 0x10 bytes from crypto stream into c3
  cdec(c2, c3);                // Decrypt from c3 into c2
  cxor(c5, c2);                // XOR c2 with c5 and store in c5
  cxsout(c5);                  // Write 0x10 bytes into crypto stream from c5
  cmov(c5, c3);                // Move c3 into c5
}
else if (mode == 0x01) // AES-128-ECB? encrypt
{
  // Create crypto script with 4 instructions
  cs0begin(0x04);
  cxsin(c3);                  // Read 0x10 bytes from crypto stream into c3
  cxor(c3, c5);                // XOR c5 with c3 and store in c3
  cenc(c5, c3);                // Encrypt from c3 into c5
  cxsout(c5);                  // Write 0x10 bytes into crypto stream from c5
}
else if (mode == 0x02) // AES-CMAC
{
  // Create crypto script with 3 instructions
  cs0begin(0x03);
  cxsin(c3);                  // Read 0x10 bytes from crypto stream into c3
  cxor(c5, c3);                // XOR c5 with c3 and store in c3
  cenc(c5, c5);                // Encrypt from c5 into c5
}
else if (mode == 0x03) // AES-128-ECB? decrypt (no IV)
{
  // Create crypto script with 3 instructions
  cs0begin(0x03);
  cxsin(c3);                  // Read 0x10 bytes from crypto stream into c3
  cdec(c5, c3);                // Decrypt from c3 into c5
  cxsout(c5);                  // Write 0x10 bytes into crypto stream from c5
}
else if (mode == 0x04) // AES-128-ECB? encrypt (no IV)
{
  // Create crypto script with 3 instructions
  cs0begin(0x03);
  cxsin(c3);                  // Read 0x10 bytes from crypto stream into c3
  cenc(c5, c3);                // Encrypt from c3 into c5
  cxsout(c5);                  // Write 0x10 bytes into crypto stream from c5
}
else
  return;
// Main loop
while (src_size > 0)
{
  u32 blk_count = (src_size >> 0x04);
  if (blk_count > 0x10)
    blk_count = 0x10;
 
  // Check size align
  if (blk_count & (blk_count - 0x01))
    blk_count = 0x01;
  u32 blk_size = (blk_count << 0x04);
 
  u32 crypt_xfer_src = 0;
  u32 crypt_xfer_dst = 0;
 
  if (block_size == 0x20)
  {
      crypt_xfer_src = (0x00030000 | src_addr);
      crypt_xfer_dst = (0x00030000 | dst_addr);
     
      // Execute crypto script 2 times (1 for each block)
      cs0exec(0x02);
  }
  if (block_size == 0x40)
  {
      crypt_xfer_src = (0x00040000 | src_addr);
      crypt_xfer_dst = (0x00040000 | dst_addr);
     
      // Execute crypto script 4 times (1 for each block)
      cs0exec(0x04);
  }
  if (block_size == 0x80)
  {
      crypt_xfer_src = (0x00050000 | src_addr);
      crypt_xfer_dst = (0x00050000 | dst_addr);
     
      // Execute crypto script 8 times (1 for each block)
      cs0exec(0x08);
  }
  if (block_size == 0x100)
  {
      crypt_xfer_src = (0x00060000 | src_addr);
      crypt_xfer_dst = (0x00060000 | dst_addr);
     
      // Execute crypto script 16 times (1 for each block)
      cs0exec(0x10);
  }
  else
  {
      crypt_xfer_src = (0x00020000 | src_addr);
      crypt_xfer_dst = (0x00020000 | dst_addr);
     
      // Execute crypto script 1 time (1 for each block)
      cs0exec(0x01);
      // Ensure proper block size
      block_size = 0x10;
  }
  // fuc5 crypt cxset instruction
  // The next xfer instruction will be overridden
  // and target changes from DMA to crypto
  if (crypt_ver == 0x01)
    cxset(0xA1);        // Flag 0xA0 is unknown
  else
    cxset(0x21);        // Flag 0x20 is unknown
  // Transfer data into the selected crypto register
  xdst(crypt_xfer_src, crypt_xfer_src);
 
  // AES-CMAC only needs one more xfer instruction
  if (mode == 0x02)
  {
      // fuc5 crypt cxset instruction
      // The next xfer instruction will be overridden
      // and target changes from DMA to crypto
      if (crypt_ver == 0x01)
        cxset(0xA1);    // Flag 0xA0 is unknown
      else
        cxset(0x21);    // Flag 0x20 is unknown
      // Wait for all data loads/stores to finish
      xdwait();
  }
  else  // AES enc/dec needs 2 more xfer instructions
  {
      // fuc5 crypt cxset instruction
      // The next 2 xfer instructions will be overridden
      // and target changes from DMA to crypto
      if (crypt_ver == 0x01)
        cxset(0xA2);            // Flag 0xA0 is unknown
      else
        cxset(0x22);            // Flag 0x20 is unknown
      // Transfer data from the selected crypto register
      xdst(crypt_xfer_dst, crypt_xfer_dst);
      // Wait for all data loads/stores to finish
      xdwait();
      // Increase the destination address by block size
      dst_addr += block_size;
  }
 
  // Increase the source address by block size
  src_addr += block_size;
  // Decrease the source size by block size
  src_size -= block_size;
}
// AES-CMAC result is in c5
if (mode == 0x02)
{
    // This will read into dst_addr from crypt register c5
    crypt_load(0x05, dst_addr);
}
return;


== Stage 2 ==
== Stage 2 ==