TMD install WIP

This commit is contained in:
JimmyZ 2017-09-30 20:17:09 +08:00
parent bdae598e23
commit 0a1ca12702
15 changed files with 290 additions and 159 deletions

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
*.nds
arm7/build
arm9/build
arm9/term256
test scripts
.*
*.sln
*.vcxproj*

7
arm9/mbedtls/readme.txt Normal file
View File

@ -0,0 +1,7 @@
aes.c/.h rsa.c/.h are heavily modified/reduced
bignum.c/.h bn_mul.h only had some minor modifications:
headers location moved from mbedtls/ to .
disabled some unused functions by "#if 0 // unused"
ASCII I/O
everything below mbedtls_mpi_exp_mod

View File

@ -1,8 +0,0 @@
#pragma once
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;

View File

@ -114,16 +114,19 @@ static void dsi_aes_set_key(u32 *rk, const u32 *console_id, key_mode_t mode) {
aes_set_key_enc_128_be(rk, (u8*)key);
}
#ifdef _MSC_VER
#define DTCM_BSS
#endif
void dsi_sha1(void *digest, const void *data, unsigned len) {
swiSHA1context_t ctx;
ctx.sha_block = 0;
swiSHA1Init(&ctx);
swiSHA1Update(&ctx, data, len);
swiSHA1Final(digest, &ctx);
int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len) {
u8 digest[SHA1_LEN];
swiSHA1Calc(digest, data, len);
// return type of swiSHA1Verify() is declared void, so how exactly should we use it?
int ret = memcmp(digest, digest_verify, SHA1_LEN);
if (ret != 0) {
prt(" ");
print_bytes(digest_verify, SHA1_LEN);
prt("\n ");
print_bytes(digest, SHA1_LEN);
prt("\n");
}
return ret;
}
static u32 nand_ctr_rk[RK_LEN];
@ -145,8 +148,8 @@ void dsi_crypt_init(const u8 *console_id_be, const u8 *emmc_cid, int is3DS) {
dsi_aes_set_key(nand_ctr_rk, console_id, is3DS ? NAND_3DS : NAND);
dsi_aes_set_key(es_rk, console_id, ES);
u32 digest[5];
dsi_sha1(digest, emmc_cid, 16);
u32 digest[SHA1_LEN / sizeof(u32)];
swiSHA1Calc(digest, emmc_cid, 16);
nand_ctr_iv[0] = digest[0];
nand_ctr_iv[1] = digest[1];
nand_ctr_iv[2] = digest[2];
@ -222,7 +225,7 @@ int dsi_es_block_crypt(u8 *buf, unsigned buf_len, crypt_mode_t mode) {
(unsigned)block_size, (unsigned)(buf_len - sizeof(es_block_footer_t)));
return 1;
}
// padd to multiple of 16
// padding to multiple of 16
u32 remainder = block_size & 0xf;
if (remainder != 0) {
zero(pad32);
@ -264,6 +267,7 @@ int dsi_es_block_crypt(u8 *buf, unsigned buf_len, crypt_mode_t mode) {
add_128_32(ctr32, 1);
}
}
// AES-CCM MAC final
xor_128(mac32, mac32, pad32);
if (mode == DECRYPT) {
if (memcmp(mac, ccm_mac, 16) == 0) {

View File

@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#define SHA1_LEN 20
#define AES_BLOCK_SIZE 16
@ -15,7 +15,7 @@ typedef enum {
ES
} key_mode_t;
void dsi_sha1(void *digest, const void *data, unsigned len);
int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len);
void dsi_crypt_init(const u8 *console_id_be, const u8 *emmc_cid, int is3DS);

View File

@ -16,8 +16,6 @@
#include "crypto.h"
#include "tmd.h"
#define SHA1_LEN 20
#define RESERVE_FREE (5 * 1024 * 1024)
term_t t0;
@ -170,15 +168,6 @@ void menu_move(int move) {
}
}
void exit_with_prompt(int exit_code) {
prt("press A to exit...");
while (1) {
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_A) break;
}
exit(exit_code);
}
unsigned wait_keys(unsigned keys) {
while (1) {
@ -191,6 +180,23 @@ unsigned wait_keys(unsigned keys) {
}
}
void exit_with_prompt(int exit_code) {
prt("press A to exit...");
wait_keys(KEY_A);
exit(exit_code);
}
int wait_yes_no(const char* msg) {
prt(msg);
prt(" Yes(A)/No(B)\n");
if (wait_keys(KEY_A | KEY_B) == KEY_A) {
return 1;
} else {
prt("cancelled\n");
return 0;
}
}
void walk_cb_lst_file(const char *name, size_t size, void *p_param) {
if (size == INVALID_SIZE) {
return;
@ -306,70 +312,15 @@ void menu_action_script(const char *name, const char *full_path) {
prt("insufficient NAND space\n");
return;
}
prt("execute? Yes(A)/No(B)\n");
if(wait_keys(KEY_A | KEY_B) & KEY_A) {
if(wait_yes_no("execute?")){
ret = scripting(full_path, 0, 0);
// TODO: some scripts might not induce writes
++executions;
iprtf("execution returned %d\n", ret);
// maybe we should prompt to restore a NAND image
} else {
prt("cancelled\n");
}
}
void menu_action_tmd(const char *name, const char *full_path) {
// TMD file
u8 *tmd_buf = malloc(sizeof(tmd_header_v0_t) + sizeof(tmd_content_v0_t));
if (tmd_buf == 0) {
prt("failed to alloc memory for TMD\n");
return;
}
if (load_block_from_file(tmd_buf, full_path, 0,
sizeof(tmd_header_v0_t) + sizeof(tmd_content_v0_t)) != 0) {
prt("failed to load TMD\n");
free(tmd_buf);
return;
}
tmd_header_v0_t *header = (tmd_header_v0_t*)tmd_buf;
u32 title_id[2];
GET_UINT32_BE(title_id[0], header->title_id, 4);
GET_UINT32_BE(title_id[1], header->title_id, 0);
if (title_id[1] != dsiware_title_id_h) {
iprtf("not a DSiWare title(%08lx)\n", title_id[1]);
free(tmd_buf);
return;
}
iprtf("Title ID: %08lx%08lx\n", title_id[1], title_id[0]);
if (header->num_content[0] != 0 || header->num_content[1] != 1) {
iprtf("num_content should to be 1(%02x%02x)\n",
header->num_content[0], header->num_content[1]);
free(tmd_buf);
return;
}
unsigned char sig_sha1[SHA1_LEN];
if (decrypt_cp07_signature(sig_sha1, header->sig) != 0) {
free(tmd_buf);
return;
}
unsigned char sha1[SHA1_LEN];
#define SIG_OFFSET (sizeof(header->sig_type) + sizeof(header->sig) + sizeof(header->padding0))
#define SIG_LEN (sizeof(tmd_header_v0_t) + sizeof(tmd_content_v0_t) - SIG_OFFSET)
dsi_sha1(sha1, tmd_buf + SIG_OFFSET, SIG_LEN);
if (memcmp(sha1, sig_sha1, SHA1_LEN) != 0) {
prt("signature verification failed\n");
free(tmd_buf);
return;
} else {
prt("signature verified\n");
}
#undef SIG_OFFSET
#undef SIG_LEN
// tmd_content_v0_t *content = (tmd_content_v0_t*)(tmd_buf + sizeof(tmd_header_v0_t));
// TODO
free(tmd_buf);
}
static inline int name_is_tmd(const char *name, int len_name) {
return (len_name == 3 && strcmp(name, "tmd") == 0)
|| (len_name >= 4 && strcmp(name + len_name - 4, ".tmd") == 0);
@ -382,17 +333,17 @@ void menu_action(const char *name) {
prt("max path length exceeded\n");
return;
}
char *full_path = alloc_buf();
strcpy(full_path, browse_path);
strcpy(full_path + len_path, name);
char *fullname = alloc_buf();
strcpy(fullname, browse_path);
strcpy(fullname + len_path, name);
if (len_name >= 4 && strcmp(name + len_name - 4, ".nfs") == 0) {
menu_action_script(name, full_path);
menu_action_script(name, fullname);
}else if(cert_ready && ticket_ready && name_is_tmd(name, len_name)){
menu_action_tmd(name, full_path);
install_tmd(fullname, browse_path, df(nand_root, 0) - RESERVE_FREE);
}else{
prt("don't know how to handle this file\n");
}
free_buf(full_path);
free_buf(fullname);
}
void menu() {
@ -412,11 +363,8 @@ void menu() {
uint32 keys = keysDown();
int needs_redraw = 0;
if (keys & KEY_SELECT) {
prt("unmount and quit(A)? cancel(B)\n");
if (wait_keys(KEY_A | KEY_B) & KEY_A) {
if (wait_yes_no("unmount and quit?")) {
break;
} else {
prt("cancelled\n");
}
} else if (keys & (KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT)) {
if (keys & KEY_UP) {
@ -525,7 +473,7 @@ int main(int argc, const char * const argv[]) {
if(heap_init() != 0 || scripting_init() != 0){
prt("failed to alloc memory\n");
return -1;
exit_with_prompt(-1);
}
u32 bat_reg = getBatteryLevel();
@ -614,8 +562,7 @@ int main(int argc, const char * const argv[]) {
}
// TODO: also test against sha1
if ((ret = test_image_against_nand()) != 0) {
prt("you don't have a valid NAND backup, backup now? Yes(A)/No(B)\n");
if (wait_keys(KEY_A | KEY_B) & KEY_A) {
if (wait_yes_no("you don't have a valid NAND backup, backup now?")) {
if ((ret = backup()) != 0) {
prt("backup failed\n");
exit_with_prompt(ret);
@ -631,7 +578,7 @@ int main(int argc, const char * const argv[]) {
return 0;
} else if(keys & KEY_A){
mode = MODE_IMAGE;
} else if (keys & KEY_X) {
} else if (keys == KEY_X) {
mode = MODE_DIRECT;
prt(Red "you are mounting NAND R/W DIRECTLY, EXERCISE EXTREME CAUTION\n");
}

View File

@ -7,7 +7,6 @@
#include <fat.h>
#include "../mbedtls/aes.h"
#include "../term256/term256ext.h"
#include "common.h"
#include "utils.h"
#include "crypto.h"
#include "sector0.h"

View File

@ -7,14 +7,11 @@
#include <sys/stat.h>
#include "heap.h"
#include "../term256/term256ext.h"
#include "common.h"
#include "utils.h"
#include "scripting.h"
extern const char nand_root[];
extern swiSHA1context_t sha1ctx;
#define FILE_BUF_LEN (128 << 10)
static u8* file_buf = 0;
@ -260,6 +257,7 @@ int sha1_file(void *digest, const char *name) {
if (f == 0) {
return -1;
}
swiSHA1context_t sha1ctx;
sha1ctx.sha_block = 0;
swiSHA1Init(&sha1ctx);
int size = 0;

View File

@ -7,7 +7,7 @@
#include "../term256/term256ext.h"
// return 0 for valid NCSD header
int parse_ncsd(const u8 sector0[SECTOR_SIZE], int verbose) {
int parse_ncsd(const uint8_t sector0[SECTOR_SIZE], int verbose) {
const ncsd_header_t * h = (ncsd_header_t *)sector0;
if (h->magic == 0x4453434e) {
if (verbose) {
@ -71,7 +71,7 @@ const mbr_partition_t ptable_3DS[MBR_PARTITIONS] = {
};
// return 0 for valid MBR
int parse_mbr(const u8 sector0[SECTOR_SIZE], int is3DS, int verbose) {
int parse_mbr(const uint8_t sector0[SECTOR_SIZE], int is3DS, int verbose) {
const mbr_t *m = (mbr_t*)sector0;
const mbr_partition_t *ref_ptable; // reference partition table
int ret = 0;

View File

@ -1,7 +1,7 @@
#pragma once
#include <stdint.h>
#include <assert.h>
#include "common.h"
// https://3dbrew.org/wiki/NCSD#NCSD_header
@ -17,34 +17,34 @@
#endif
typedef struct {
u32 offset;
u32 length;
uint32_t offset;
uint32_t length;
} __PACKED ncsd_partition_t;
typedef struct {
u8 signature[0x100];
u32 magic;
u32 size;
u32 media_id_l;
u32 media_id_h;
u8 fs_types[NCSD_PARTITIONS];
u8 crypt_types[NCSD_PARTITIONS];
uint8_t signature[0x100];
uint32_t magic;
uint32_t size;
uint32_t media_id_l;
uint32_t media_id_h;
uint8_t fs_types[NCSD_PARTITIONS];
uint8_t crypt_types[NCSD_PARTITIONS];
ncsd_partition_t partitions[NCSD_PARTITIONS];
} __PACKED ncsd_header_t;
typedef struct {
u8 head;
u8 sector;
u8 cylinder;
uint8_t head;
uint8_t sector;
uint8_t cylinder;
} __PACKED chs_t;
typedef struct {
u8 status;
uint8_t status;
chs_t chs_first;
u8 type;
uint8_t type;
chs_t chs_last;
u32 offset;
u32 length;
uint32_t offset;
uint32_t length;
} __PACKED mbr_partition_t;
#define MBR_PARTITIONS 4
@ -52,10 +52,10 @@ typedef struct {
#define MBR_BOOTSTRAP_SIZE 0x1be
typedef struct {
u8 bootstrap[MBR_BOOTSTRAP_SIZE];
uint8_t bootstrap[MBR_BOOTSTRAP_SIZE];
mbr_partition_t partitions[MBR_PARTITIONS];
u8 boot_signature_0;
u8 boot_signature_1;
uint8_t boot_signature_0;
uint8_t boot_signature_1;
} __PACKED mbr_t;
#ifdef _MSC_VER
@ -67,6 +67,6 @@ typedef struct {
static_assert(sizeof(ncsd_header_t) == 0x160, "sizeof(ncsd_header_t) should equal 0x160");
static_assert(sizeof(mbr_t) == SECTOR_SIZE, "sizeof(mbr_t) should equal 0x200");
int parse_ncsd(const u8 sector0[SECTOR_SIZE], int verbose);
int parse_ncsd(const uint8_t sector0[SECTOR_SIZE], int verbose);
int parse_mbr(const u8 sector0[SECTOR_SIZE], int is3DS, int verbose);
int parse_mbr(const uint8_t sector0[SECTOR_SIZE], int is3DS, int verbose);

View File

@ -9,18 +9,26 @@
#include "walk.h"
#include "ticket0.h"
#include "crypto.h"
#include "scripting.h"
extern const char nand_root[];
const char cert_sys_path[] = "sys/cert.sys";
const char cert_sys_fullname[] = "sys/cert.sys";
const char cert_cp07_name[] = "CP00000007";
const char dsiware_ticket_path[] = "ticket/00030004/";
const uint32_t dsiware_title_id_h = 0x00030004;
const u32 dsiware_title_id_h = 0x00030004;
// used while looking for a ticket template
const char ticket_dir_fmt[] = "%sticket/%08lx/";
// used while reading app aside tmd on SD
const char app_src_fmt[] = "%s%08lx.app";
// dst full name generation
const char ticket_fullname_fmt[] = "%sticket/%08lx/%08lx.tik";
const char tmd_fullname_fmt[] = "%stitle/%08lx/%08lx/content/tmd";
const char app_fullname_fmt[] = "%stitle/%08lx/%08lx/content/%08lx.app";
rsa_context_t rsa_cp07;
unsigned char * ticket_template;
uint8_t * ticket_template;
int setup_cp07_pubkey() {
cert_t *cert = malloc(sizeof(cert_t));
@ -31,9 +39,9 @@ int setup_cp07_pubkey() {
int ret;
char *cert_full_path = alloc_buf();
strcpy(cert_full_path, nand_root);
strcat(cert_full_path, cert_sys_path);
strcat(cert_full_path, cert_sys_fullname);
if (load_block_from_file(cert, cert_full_path, 0x700, sizeof(cert_t)) != 0) {
iprtf("failed to load cert from %s\n", cert_sys_path);
iprtf("failed to load cert from %s\n", cert_sys_fullname);
ret = -1;
} else {
if (strncmp(cert->key_name, cert_cp07_name, sizeof(cert_cp07_name)) != 0) {
@ -57,9 +65,9 @@ int setup_cp07_pubkey() {
}
// returns 0 on success, and write SHA1 to out
int decrypt_cp07_signature(unsigned char *out, const unsigned char *in) {
int decrypt_cp07_signature(uint8_t *out, const uint8_t *in) {
static_assert(RSA_2048_LEN <= BUF_SIZE, "BUF_SIZE shouldn't < RSA_2048_LEN");
unsigned char *sig = (unsigned char*)alloc_buf();
uint8_t *sig = (uint8_t*)alloc_buf();
int ret;
if (sig == 0) {
prt("failed to alloc memory\n");
@ -68,7 +76,7 @@ int decrypt_cp07_signature(unsigned char *out, const unsigned char *in) {
prt("failed to decrypt signature\n");
ret = -2;
} else if (sig[0] != 0 || sig[1] != 1 || sig[2] != 0xff
|| sig[RSA_2048_LEN - 0x14 - 1] != 0x14) {
|| sig[RSA_2048_LEN - SHA1_LEN - 1] != SHA1_LEN) {
prt("invalid signature, first 16 bytes:\n\t");
print_bytes(sig, 16);
prt("\nlast 32 bytes:\n\t");
@ -78,7 +86,7 @@ int decrypt_cp07_signature(unsigned char *out, const unsigned char *in) {
prt("\n");
ret = -3;
} else {
memcpy(out, sig + RSA_2048_LEN - 0x14, 0x14);
memcpy(out, sig + RSA_2048_LEN - SHA1_LEN, SHA1_LEN);
ret = 0;
}
free_buf(sig);
@ -100,15 +108,34 @@ static int find_ticket_cb(const char* filename, size_t size, void *cb_param) {
// return 0 to let list_dir continue
return 0;
}
#define ES_ENCRYPT_TEST 1
#if ES_ENCRYPT_TEST
uint8_t *ticket_original = memalign(TICKET_ALIGN, TICKET_SIZE);
assert(ticket_original != 0);
memcpy(ticket_original, ticket_template, TICKET_SIZE);
#endif
if (dsi_es_block_crypt(ticket_template, TICKET_SIZE, DECRYPT) != 0) {
iprtf("failed to decrypt ticket: %s\n", filename);
return 0;
}
#if ES_ENCRYPT_TEST
uint8_t *ticket_enc = memalign(TICKET_ALIGN, TICKET_SIZE);
assert(ticket_enc != 0);
memcpy(ticket_enc, ticket_template, TICKET_SIZE);
assert(dsi_es_block_crypt(ticket_enc, TICKET_SIZE, ENCRYPT) == 0);
if (memcmp(ticket_original, ticket_enc, TICKET_SIZE) == 0) {
prtf("ES encryption test OK\n");
} else {
prtf("ES encryption test failed\n");
}
free(ticket_original);
free(ticket_enc);
#endif
ticket_v0_t *ticket = (ticket_v0_t*)ticket_template;
// maybe we should reject XS00000006 tickets, only allow XS00000003?
// iprtf("ticket signature issuer: %s\n", ticket->issuer);
// TODO: maybe also validate ticket signature
u32 title_id[2];
uint32_t title_id[2];
GET_UINT32_BE(title_id[0], ticket->title_id, 4);
GET_UINT32_BE(title_id[1], ticket->title_id, 0);
if (title_id[1] != dsiware_title_id_h) {
@ -120,21 +147,18 @@ static int find_ticket_cb(const char* filename, size_t size, void *cb_param) {
}
int setup_ticket_template() {
char * ticket_path = alloc_buf();
strcpy(ticket_path, nand_root);
// strcpy(ticket_path, "sd:/twlnf/dump/");
strcat(ticket_path, dsiware_ticket_path);
// we'll do AES on that, so must be aligned at least 32 bit
// we'll do AES-CCM on that, so must be aligned at least 32 bit
ticket_template = memalign(TICKET_ALIGN, TICKET_SIZE);
if (ticket_template == 0) {
prt("failed to alloc memory\n");
free_buf(ticket_path);
return -1;
}
// iprtf("ticket_template addr: %08x\n", (unsigned)ticket_template);
char * ticket_dir = alloc_buf();
sprintf(ticket_dir, ticket_dir_fmt, nand_root, dsiware_title_id_h);
int found = 0;
list_dir(ticket_path, 1, find_ticket_cb, &found);
free_buf(ticket_path);
list_dir(ticket_dir, 1, find_ticket_cb, &found);
free_buf(ticket_dir);
if (found) {
return 0;
} else {
@ -143,3 +167,156 @@ int setup_ticket_template() {
return 1;
}
}
#define TMD_SIZE (sizeof(tmd_header_v0_t) + sizeof(tmd_content_v0_t))
int tmd_verify(const uint8_t *tmd_buf, const char *tmd_dir, char *tmd_dst_fullname,
char *app_src_fullname, uint8_t *app_sha1, int *psize, char *app_dst_fullname,
uint8_t *ticket_buf, char *ticket_dst_fullname)
{
tmd_header_v0_t *header = (tmd_header_v0_t*)tmd_buf;
uint32_t title_id[2];
GET_UINT32_BE(title_id[0], header->title_id, 4);
GET_UINT32_BE(title_id[1], header->title_id, 0);
if (title_id[1] != dsiware_title_id_h) {
iprtf("not a DSiWare title(%08lx)\n", title_id[1]);
return -1;
}
iprtf("Title ID: %08lx/%c%c%c%c\n", title_id[1],
header->title_id[4], header->title_id[5], header->title_id[6], header->title_id[7]);
if (header->num_content[0] != 0 || header->num_content[1] != 1) {
iprtf("num_content should be 1(%02x%02x)\n",
header->num_content[0], header->num_content[1]);
return -1;
}
// I'm ashamed to used app_sha1 for a different thing
if (decrypt_cp07_signature(app_sha1, header->sig) != 0) {
return -1;
}
#define SIG_OFFSET (sizeof(header->sig_type) + sizeof(header->sig) + sizeof(header->padding0))
#define SIG_LEN (TMD_SIZE - SIG_OFFSET)
if (dsi_sha1_verify(app_sha1, tmd_buf + SIG_OFFSET, SIG_LEN) != 0) {
#undef SIG_OFFSET
#undef SIG_LEN
prt("TMD signature verification failed\n");
return -1;
} else {
prt("TMD signature verified\n");
}
// verify app
tmd_content_v0_t *content = (tmd_content_v0_t*)(tmd_buf + sizeof(tmd_header_v0_t));
uint32_t content_id;
GET_UINT32_BE(content_id, content->content_id, 0);
sprintf(app_src_fullname, app_src_fmt, tmd_dir, content_id);
prt(app_src_fullname);
if ((*psize = sha1_file(app_sha1, app_src_fullname)) == -1) {
prt(" <- couldn't open\n");
return -1;
}
if (memcmp(content->sha1, app_sha1, SHA1_LEN) != 0) {
prt(" SHA1 doesn't match\n");
return -1;
} else {
prt(" SHA1 verified\n");
}
// forge ticket
memcpy(ticket_buf, ticket_template, TICKET_SIZE);
ticket_v0_t *ticket = (ticket_v0_t*)ticket_buf;
PUT_UINT32_BE(title_id[0], ticket->title_id, 4);
if (dsi_es_block_crypt(ticket_buf, TICKET_SIZE, ENCRYPT) != 0) {
prt("weird, failed to forge ticket\n");
return -1;
}
// generate paths
sprintf(ticket_dst_fullname, ticket_fullname_fmt, nand_root, title_id[1], title_id[0]);
sprintf(tmd_dst_fullname, tmd_fullname_fmt, nand_root, title_id[1], title_id[0]);
sprintf(app_dst_fullname, app_fullname_fmt, nand_root, title_id[1], title_id[0], content_id);
return 0;
}
int wait_yes_no(const char *);
void verify(const char *fullname, const uint8_t *digest_verify) {
uint8_t digest[SHA1_LEN];
int ret = sha1_file(digest, fullname);
if (ret == -1) {
prt(" but failed to read for verification\n");
} else if (memcmp(digest, digest_verify, SHA1_LEN)) {
prt(" but verification failed\n");
} else {
prt(" and verified\n");
}
}
void save_and_verify(const char *fullname, uint8_t *buf, size_t len) {
prt(fullname);
int ret = save_file(fullname, buf, len, 0);
if (ret != 0) {
prt(" failed to write\n");
} else {
prt(" written to NAND");
uint8_t digest[SHA1_LEN];
dsi_sha1_verify(digest, buf, len);
verify(fullname, digest);
}
}
void install_tmd(const char *tmd_fullname, const char *tmd_dir, int max_size) {
// TMD file
uint8_t *tmd_buf = malloc(TMD_SIZE);
if (tmd_buf == 0) {
prt("failed to alloc memory for TMD\n");
return;
}
uint8_t *ticket_buf = memalign(TICKET_ALIGN, TICKET_SIZE);
if (ticket_buf == 0) {
prt("failed to alloc memory for ticket\n");
free(tmd_buf);
return;
}
if (load_block_from_file(tmd_buf, tmd_fullname, 0,
sizeof(tmd_header_v0_t) + sizeof(tmd_content_v0_t)) != 0) {
prt("failed to load TMD\n");
free(tmd_buf);
free(ticket_buf);
return;
}
char *tmd_dst_fullname = alloc_buf();
char *app_src_fullname = alloc_buf();
uint8_t app_sha1[SHA1_LEN];
int size;
char *app_dst_fullname = alloc_buf();
char *ticket_dst_fullname = alloc_buf();
if (tmd_verify(tmd_buf, tmd_dir, tmd_dst_fullname,
app_src_fullname, app_sha1, &size, app_dst_fullname,
ticket_buf, ticket_dst_fullname) == 0) {
if (size > max_size) {
prt("insufficient NAND space\n");
} else if(wait_yes_no("install to NAND?")){
// write ticket
FILE *f = fopen(ticket_dst_fullname, "r");
if (f != 0) {
fclose(f);
prt("ticket already exist, won't overwrite\n");
} else {
save_and_verify(ticket_dst_fullname, ticket_buf, TICKET_SIZE);
}
// write TMD
save_and_verify(ticket_dst_fullname, ticket_buf, TICKET_SIZE);
// write app
prt(app_dst_fullname);
if (cp(app_src_fullname, app_dst_fullname) != 0) {
prt(" failed to copy\n");
} else {
prt(" copied to NAND");
verify(app_dst_fullname, app_sha1);
}
}
}
free(tmd_buf);
free(ticket_buf);
free_buf(tmd_dst_fullname);
free_buf(app_src_fullname);
free_buf(app_dst_fullname);
free_buf(ticket_dst_fullname);
}

View File

@ -4,3 +4,5 @@ int setup_cp07_pubkey();
int decrypt_cp07_signature(unsigned char *out, const unsigned char *in);
int setup_ticket_template();
void install_tmd(const char *tmd_fullname, const char *tmd_dir, int max_size);

View File

@ -5,7 +5,6 @@
#include "../term256/term256ext.h"
#include "utils.h"
// globals shared by extern
swiSHA1context_t sha1ctx;
static inline int htoi(char a){
@ -20,7 +19,7 @@ static inline int htoi(char a){
}
}
int hex2bytes(u8 *out, unsigned byte_len, const char *in){
int hex2bytes(uint8_t *out, unsigned byte_len, const char *in){
if (strlen(in) < byte_len << 1){
iprtf("%s: invalid input length, expecting %u, got %u.\n",
__FUNCTION__, (unsigned)byte_len << 1, (unsigned)strlen(in));
@ -133,6 +132,9 @@ int load_block_from_file(void *buf, const char *filename, unsigned offset, unsig
return ret;
}
// you should have updated the sha1 context before calling save_sha1_file
// example: save_file() in this file and backup() in nand.c
int save_sha1_file(const char *filename) {
size_t len_fn = strlen(filename);
char *sha1_fn = (char *)malloc(len_fn + 6);

View File

@ -1,10 +1,9 @@
#pragma once
#include <nds.h>
#include "common.h"
#include <stdint.h>
int hex2bytes(u8 *out, unsigned byte_len, const char *in);
int hex2bytes(uint8_t *out, unsigned byte_len, const char *in);
const char * to_mebi(size_t size);

View File

@ -1,4 +1,6 @@
need term256 to compile
git clone https://github.com/Jimmy-Z/term256
ln -s term256/term256 twlnf/arm9/term256
git clone https://github.com/Jimmy-Z/twlnf
git clone https://github.com/Jimmy-Z/term256
ln -s term256/term256 twlnf/arm9/term256
cd twlnf
make