TSEC: Difference between revisions
New findings |
No edit summary |
||
Line 571: | Line 571: | ||
== Device key generation == | == Device key generation == | ||
The | The TSEC device key is generated by reading SOR1 registers modified by the Falcon CPU. | ||
// Clear something in unknown host1x channel 0 sync register (HOST1X_SYNC_UNK_300) | // Clear something in unknown host1x channel 0 sync register (HOST1X_SYNC_UNK_300) | ||
// This appears to revoke TSEC's exclusive access to host1x | // This appears to revoke TSEC's exclusive access to host1x | ||
*(u32 *)0x50003300 = 0; | *(u32 *)0x50003300 = 0; | ||
// | // Read TSEC device key | ||
u32 | u32 tsec_device_key[4]; | ||
tsec_device_key[0] = *(u32 *)NV_SOR_DP_HDCP_BKSV_LSB; | |||
tsec_device_key[1] = *(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB; | |||
tsec_device_key[2] = *(u32 *)NV_SOR_TMDS_HDCP_CN_MSB; | |||
tsec_device_key[3] = *(u32 *)NV_SOR_TMDS_HDCP_CN_LSB; | |||
// Clear | // Clear SOR1 registers | ||
*(u32 *)NV_SOR_DP_HDCP_BKSV_LSB = 0; | *(u32 *)NV_SOR_DP_HDCP_BKSV_LSB = 0; | ||
*(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB = 0; | *(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB = 0; | ||
Line 592: | Line 592: | ||
out_size = 0x10; | out_size = 0x10; | ||
// Copy back the | // Copy back the TSEC device key | ||
memcpy(out_buf, | memcpy(out_buf, tsec_device_key, out_size); | ||
== Cleanup == | == Cleanup == | ||
Line 744: | Line 744: | ||
} | } | ||
// Write | // Write TSEC device key to registers | ||
set_device_key(key_data_buf); | set_device_key(key_data_buf); | ||
return boot_res; | return boot_res; | ||
==== set_device_key ==== | |||
This method takes '''key_data_buf''' as argument and writes the TSEC key to SOR1 registers. | |||
// This is TSEC_MMIO + 0x1000 + (0x1C300 / 0x40) | |||
*(u32 *)TSEC_DMA_UNK = 0xFFF; | |||
// Read the key's words | |||
u32 key0 = *(u32 *)(key_data_buf + 0x00); | |||
u32 key1 = *(u32 *)(key_data_buf + 0x04); | |||
u32 key2 = *(u32 *)(key_data_buf + 0x08); | |||
u32 key3 = *(u32 *)(key_data_buf + 0x0C); | |||
u32 result = 0; | |||
// Write to SOR1 register | |||
result = tsec_dma_write(NV_SOR_DP_HDCP_BKSV_LSB, key0); | |||
// Failed to write | |||
if (result) | |||
return result; | |||
// Write to SOR1 register | |||
result = tsec_dma_write(NV_SOR_TMDS_HDCP_BKSV_LSB, key1); | |||
// Failed to write | |||
if (result) | |||
return result; | |||
// Write to SOR1 register | |||
result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_MSB, key2); | |||
// Failed to write | |||
if (result) | |||
return result; | |||
// Write to SOR1 register | |||
result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_LSB, key3); | |||
// Failed to write | |||
if (result) | |||
return result; | |||
return result; | |||
===== tsec_dma_write ===== | |||
This method takes '''addr''' and '''value''' as arguments and performs a DMA write using TSEC MMIO. | |||
u32 result = 0; | |||
// Wait for TSEC DMA engine | |||
// This waits for bit 0x0C in TSEC_DMA_CMD to be 0 | |||
result = wait_tsec_dma(); | |||
// Wait failed | |||
if (result) | |||
return 1; | |||
// Set the destination address | |||
// This is TSEC_MMIO + 0x1000 + (0x1C100 / 0x40) | |||
*(u32 *)TSEC_DMA_ADDR = addr; | |||
// Set the value | |||
// This is TSEC_MMIO + 0x1000 + (0x1C200 / 0x40) | |||
*(u32 *)TSEC_DMA_VAL = value; | |||
// Start transfer? | |||
// This is TSEC_MMIO + 0x1000 + (0x1C000 / 0x40) | |||
*(u32 *)TSEC_DMA_CMD = 0x800000F2; | |||
// Wait for TSEC DMA engine | |||
// This waits for bit 0x0C in TSEC_DMA_CMD to be 0 | |||
result = wait_tsec_dma(); | |||
// Wait failed | |||
if (result) | |||
return 1; | |||
return 0; | |||
== Stage 1 == | == Stage 1 == | ||
Line 763: | Line 840: | ||
// fuc5 crypt cauth instruction | // fuc5 crypt cauth instruction | ||
// Clear | // Clear bit 0x13 in cauth | ||
cauth(cauth_old & | cauth(cauth_old & ~(1 << 0x13)); | ||
// Set the target port for memory transfers | // Set the target port for memory transfers | ||
xtargets(0); | xtargets(0); | ||
Line 872: | Line 948: | ||
// Compare the hashes | // Compare the hashes | ||
if (memcmp( | if (memcmp(dst_addr, key_buf + 0x10, 0x10)) | ||
{ | { | ||
res = 0xDEADBEEF; | res = 0xDEADBEEF; | ||
Line 975: | Line 1,051: | ||
get_seed(seed_buf, type); | get_seed(seed_buf, type); | ||
// This will write the seed into | // This will write the seed into crypto register c0 | ||
crypt_store(0, seed_buf); | crypt_store(0, seed_buf); | ||
// fuc5 csecret instruction | // fuc5 csecret instruction | ||
// Load selected secret into | // Load selected secret into crypto register c1 | ||
csecret(c1, 0x26); | csecret(c1, 0x26); | ||
// fuc5 ckeyreg instruction | // fuc5 ckeyreg instruction | ||
// | // Bind c1 register as the key for enc/dec operations | ||
ckeyreg(c1); | ckeyreg(c1); | ||
// fuc5 cenc instruction | // fuc5 cenc instruction | ||
// | // Encrypt seed_buf (in c0) using keyreg value as key into c1 | ||
cenc(c1, c0); | cenc(c1, c0); | ||
// fuc5 csigenc instruction | // fuc5 csigenc instruction | ||
// Encrypt | // Encrypt c1 register with the auth signature stored in c6 | ||
csigenc(c1, c1); | csigenc(c1, c1); | ||
Line 1,016: | Line 1,092: | ||
*(u32 *)(buf + 0x0C) = size; | *(u32 *)(buf + 0x0C) = size; | ||
// This will write buf into | // This will write buf into crypto register c3 | ||
crypt_store(0x03, buf); | crypt_store(0x03, buf); | ||
// fuc5 ckeyreg instruction | // fuc5 ckeyreg instruction | ||
// | // Bind c4 register (from keygen) as the key for enc/dec operations | ||
ckeyreg(c4); | ckeyreg(c4); | ||
// fuc5 cenc instruction | // fuc5 cenc instruction | ||
// | // Encrypt buf (in c3) using keyreg value as key into c5 | ||
cenc(c5, c3); | cenc(c5, c3); | ||
// This will read into buf from | // This will read into buf from crypto register c5 | ||
crypt_load(0x05, buf); | crypt_load(0x05, buf); | ||
Line 1,049: | Line 1,125: | ||
if (iv_addr) | if (iv_addr) | ||
{ | { | ||
// This will write the iv_addr into | // This will write the iv_addr into crypto register c5 | ||
crypt_store(0x05, iv_addr); | crypt_store(0x05, iv_addr); | ||
} | } | ||
Line 1,061: | Line 1,137: | ||
ckeyreg(c4); | ckeyreg(c4); | ||
// AES-128-ECB | // AES-128-ECB decrypt | ||
if (mode == 0x00) | if (mode == 0x00) | ||
{ | { | ||
Line 1,073: | Line 1,149: | ||
cmov(c5, c3); // Move c3 into c5 | cmov(c5, c3); // Move c3 into c5 | ||
} | } | ||
else if (mode == 0x01) // AES-128-ECB | else if (mode == 0x01) // AES-128-ECB encrypt | ||
{ | { | ||
// Create crypto script with 4 instructions | // Create crypto script with 4 instructions | ||
Line 1,092: | Line 1,168: | ||
cenc(c5, c5); // Encrypt from c5 into c5 | cenc(c5, c5); // Encrypt from c5 into c5 | ||
} | } | ||
else if (mode == 0x03) // AES-128-ECB | else if (mode == 0x03) // AES-128-ECB decrypt (no IV) | ||
{ | { | ||
// Create crypto script with 3 instructions | // Create crypto script with 3 instructions | ||
Line 1,101: | Line 1,177: | ||
cxsout(c5); // Write 0x10 bytes into crypto stream from c5 | cxsout(c5); // Write 0x10 bytes into crypto stream from c5 | ||
} | } | ||
else if (mode == 0x04) // AES-128-ECB | else if (mode == 0x04) // AES-128-ECB encrypt (no IV) | ||
{ | { | ||
// Create crypto script with 3 instructions | // Create crypto script with 3 instructions | ||
Line 1,176: | Line 1,252: | ||
// fuc5 crypt cxset instruction | // fuc5 crypt cxset instruction | ||
// The next xfer instruction will be overridden | // The next xfer instruction will be overridden | ||
// and target changes from DMA to crypto | // and target changes from DMA to crypto input/output stream | ||
if (crypt_ver == 0x01) | if (crypt_ver == 0x01) | ||
cxset(0xA1); // Flag 0xA0 is | cxset(0xA1); // Flag 0xA0 is (0x80 | 0x20) | ||
else | else | ||
cxset(0x21); // Flag 0x20 is | cxset(0x21); // Flag 0x20 is external mem <-> crypto input/output stream | ||
// Transfer data into the | // Transfer data into the crypto input/output stream | ||
xdst(crypt_xfer_src, crypt_xfer_src); | xdst(crypt_xfer_src, crypt_xfer_src); | ||
Line 1,190: | Line 1,266: | ||
// fuc5 crypt cxset instruction | // fuc5 crypt cxset instruction | ||
// The next xfer instruction will be overridden | // The next xfer instruction will be overridden | ||
// and target changes from DMA to crypto | // and target changes from DMA to crypto input/output stream | ||
if (crypt_ver == 0x01) | if (crypt_ver == 0x01) | ||
cxset(0xA1); // Flag 0xA0 is | cxset(0xA1); // Flag 0xA0 is (0x80 | 0x20) | ||
else | else | ||
cxset(0x21); // Flag 0x20 is | cxset(0x21); // Flag 0x20 is external mem <-> crypto input/output stream | ||
// Wait for all data loads/stores to finish | // Wait for all data loads/stores to finish | ||
Line 1,203: | Line 1,279: | ||
// fuc5 crypt cxset instruction | // fuc5 crypt cxset instruction | ||
// The next 2 xfer instructions will be overridden | // The next 2 xfer instructions will be overridden | ||
// and target changes from DMA to crypto | // and target changes from DMA to crypto input/output stream | ||
if (crypt_ver == 0x01) | if (crypt_ver == 0x01) | ||
cxset(0xA2); // Flag 0xA0 is | cxset(0xA2); // Flag 0xA0 is (0x80 | 0x20) | ||
else | else | ||
cxset(0x22); // Flag 0x20 is | cxset(0x22); // Flag 0x20 is external mem <-> crypto input/output stream | ||
// Transfer data from the | // Transfer data from the crypto input/output stream | ||
xdld(crypt_xfer_dst, crypt_xfer_dst); | |||
// Wait for all data loads/stores to finish | // Wait for all data loads/stores to finish | ||
Line 1,229: | Line 1,305: | ||
if (mode == 0x02) | if (mode == 0x02) | ||
{ | { | ||
// This will read into dst_addr from | // This will read into dst_addr from crypto register c5 | ||
crypt_load(0x05, dst_addr); | crypt_load(0x05, dst_addr); | ||
} | } |