verify tmd content(ticket0.c)

This commit is contained in:
JimmyZ 2017-10-01 17:34:51 +08:00
parent 9fa3da359b
commit 8450380382
8 changed files with 157 additions and 72 deletions

View File

@ -12,12 +12,11 @@ all: $(PNAME)_openssl $(PNAME)_mbedtls
$(PNAME)_openssl: $(OBJS) crypto_openssl_evp.o
$(CC) -o $@ $^ -lcrypto
# no default rule for this?
$(PNAME)_mbedtls: $(OBJS) $(MBEDTLS_OBJS) crypto_mbedtls.o
$(CC) -o $@ $^
$(PNAME)_mbedtls: $(OBJS) crypto_mbedtls.o
$(CC) --static -o $@ $^ -lmbedcrypto
ticket0: ticket0.o utils.o
$(CC) -o $@ $^
$(CC) --static -o $@ $^ -lmbedcrypto
clean:
rm $(PNAME)_* ticket0 *.o

View File

@ -1,9 +1,9 @@
#include <stdio.h>
#include "mbedtls/config.h"
#include "mbedtls/version.h"
#include "mbedtls/aes.h"
#include "mbedtls/aesni.h"
#include <mbedtls/config.h>
#include <mbedtls/version.h>
#include <mbedtls/aes.h>
#include <mbedtls/aesni.h>
#include "crypto.h"
static mbedtls_aes_context ctx_aes;

6
dsi.c
View File

@ -204,7 +204,7 @@ void dsi_es_block_crypt(const u8 *console_id, crypt_mode_t mode,
byte_reverse_16(key_rev, (u8*)key);
aes_128_ecb_set_key(key_rev);
long input_size;
unsigned input_size;
u8 *input_buf = read_file(in_file, &input_size);
printf("file size %u, block size would be %06x\n",
(unsigned)input_size, (unsigned)(input_size - sizeof(es_block_footer_t)));
@ -233,7 +233,7 @@ void dsi_es_block_crypt(const u8 *console_id, crypt_mode_t mode,
}
// apply padding if not 16 bytes aligned
unsigned remainder = block_size & 0xf;
if (remainder > 0) {
if (remainder != 0) {
u8 padding[16] = { 0 };
if (mode == DECRYPT) {
u8 ctr[16] = { 0 };
@ -285,7 +285,7 @@ void dsi_es_block_crypt(const u8 *console_id, crypt_mode_t mode,
printf("MAC in footer : %s\n", hexdump(footer_backup.ccm_mac, 16, 1));
if (mode == DECRYPT) {
if (!memcmp(mac, footer_backup.ccm_mac, 16)) {
if (memcmp(mac, footer_backup.ccm_mac, 16) == 0) {
puts("decryption successful");
if (remainder) {
// restore MAC in case it's been overwritten by padding

View File

@ -1,13 +0,0 @@
#pragma once
/* if you want to use mbedtls, copy these files over:
* mbedtls/aes.h mbedtls/aesni.h mbedtls/version.h
* aes.c aesni.c
* and make twlbf_mbedtls
*/
#define MBEDTLS_AES_C
#define MBEDTLS_AESNI_C
#define MBEDTLS_HAVE_X86_64
#define MBEDTLS_HAVE_ASM

View File

@ -1,32 +1,67 @@
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include <mbedtls/sha1.h>
#include "ticket0.h"
#include "utils.h"
#include "dsi.h"
tmd_header_v0_t tmd;
tmd_content_v0_t tmd_content[0x10000];
ticket_v0_t ticket;
int main(int argc, const char *argv[]) {
if (argc == 3 && !strcmp(argv[1], "tmd")) {
read_block_from_file(&tmd, argv[2], 0, sizeof(tmd_header_v0_t));
printf("Title ID: %016" PRIx64 "\n", u64be(&tmd.title_id));
printf("Issuer: %s\n", tmd.issuer);
int num_content = u16be(&tmd.num_content);
tmd_header_v0_t *tmd;
tmd_content_v0_t *tmd_content;
tmd = malloc(sizeof(tmd_header_v0_t));
read_block_from_file(tmd, argv[2], 0, sizeof(tmd_header_v0_t));
printf("Title ID: %016" PRIx64 "(%c%c%c%c)\n", u64be(tmd->title_id),
tmd->title_id[4], tmd->title_id[5], tmd->title_id[6], tmd->title_id[7]);
printf("Issuer: %s\n", tmd->issuer);
printf("public save: %u\n", u32be(tmd->public_save_size));
printf("private save: %u\n", u32be(tmd->private_save_size));
int num_content = u16be(tmd->num_content);
printf("Number of content: %d\n", num_content);
assert(num_content > 0);
tmd_content = malloc(sizeof(tmd_content_v0_t) * num_content);
read_block_from_file(tmd_content, argv[2], sizeof(tmd_header_v0_t), sizeof(tmd_content_v0_t) * num_content);
for (int i = 0; i < num_content; ++i) {
printf("Content ID: %08x\n", u32be(&tmd_content[i].content_id));
printf("index: %d\n", u16be(&tmd_content[i].index));
printf("type: %d\n", u16be(&tmd_content[i].type));
printf("size: %" PRIu64 "\n", u64be(&tmd_content[i].size));
uint32_t content_id = u32be(tmd_content[i].content_id);
uint64_t size = u64be(tmd_content[i].size);
printf("=== Content ID: %08x ===\n", content_id);
printf("index: %d\n", u16be(tmd_content[i].index));
printf("type: %d\n", u16be(tmd_content[i].type));
printf("size: %u\n", (unsigned)size);
// read content
unsigned actual_size;
char name[16];
sprintf(name, "%08x.app", content_id);
void *c = read_file(name, &actual_size);
if (actual_size != size) {
printf("size mismatch, expecting %u, got %u\n", (unsigned)size, actual_size);
} else {
printf("size match\n");
}
// check content sha1
unsigned char sha1[20];
mbedtls_sha1(c, actual_size, sha1);
if (memcmp(sha1, tmd_content[i].sha1, 20) != 0) {
printf("sha1 mismatch, expecting:\n");
print_hex(tmd_content[i].sha1, 20);
printf("got:\n");
print_hex(sha1, 20);
} else {
printf("sha1 verified\n");
}
free(c);
}
free(tmd);
free(tmd_content);
} else if (argc == 3 && !strcmp(argv[1], "tik")) {
read_block_from_file(&ticket, argv[2], 0, sizeof(ticket_v0_t));
printf("Title ID: %016" PRIx64 "\n", u64be(&ticket.title_id));
printf("Issuer: %s\n", ticket.issuer);
ticket_v0_t *ticket = malloc(sizeof(ticket_v0_t));
read_block_from_file(ticket, argv[2], 0, sizeof(ticket_v0_t));
printf("Title ID: %016" PRIx64 "\n", u64be(ticket->title_id));
printf("Issuer: %s\n", ticket->issuer);
free(ticket);
} else {
printf("invalid arguments\n");
}

View File

@ -2,41 +2,43 @@
#include <assert.h>
#include <stdint.h>
#include "crypto.h"
#ifdef _MSC_VER
#pragma pack(push, 1)
#define PACKED
#else
#elif ! defined PACKED
#define PACKED __attribute__ ((__packed__))
#endif
#define RSA_2048_LEN (2048/8)
// most, if not all, are big endian
// http://problemkaputt.de/gbatek.htm#dsisdmmcdsiwareticketsandtitlemetadata
// http://dsibrew.org/wiki/Ticket
// http://wiibrew.org/wiki/Ticket
typedef struct {
uint32_t sig_type;
uint8_t sig[0x100];
uint8_t sig_type[4];
uint8_t sig[RSA_2048_LEN];
uint8_t padding0[0x3c];
uint8_t issuer[0x40];
char issuer[0x40];
uint8_t ecdh[0x3c];
uint8_t padding1[3];
uint8_t encrypted_title_key[0x10];
uint8_t unknown0;
uint64_t ticket_id;
uint32_t console_id;
uint64_t title_id;
uint8_t ticket_id[8];
uint8_t console_id[4];
uint8_t title_id[8];
uint8_t unknown1[2];
uint16_t version;
uint32_t permitted_titles_mask;
uint32_t permit_mask;
uint8_t version[2];
uint8_t permitted_titles_mask[4];
uint8_t permit_mask[4];
uint8_t title_export_allowed;
uint8_t common_key_index;
uint8_t unknown[0x30];
uint8_t content_access_permissions[0x40];
uint8_t padding2[2];
uint32_t time_limits[2 * 8];
uint8_t time_limits[2 * 8 * sizeof(uint32_t)];
} PACKED ticket_v0_t;
static_assert(sizeof(ticket_v0_t) == 0x2a4, "invalid sizeof(ticket_v0_t)");
@ -44,37 +46,41 @@ static_assert(sizeof(ticket_v0_t) == 0x2a4, "invalid sizeof(ticket_v0_t)");
// http://dsibrew.org/wiki/Tmd
// http://wiibrew.org/wiki/Title_metadata
typedef struct {
uint32_t sig_type;
uint8_t sig[256];
uint8_t padding0[60];
uint8_t issuer[64];
uint8_t sig_type[4];
uint8_t sig[RSA_2048_LEN];
uint8_t padding0[0x3c];
char issuer[0x40];
uint8_t version;
uint8_t ca_crl_version;
uint8_t signer_crl_version;
uint8_t padding1;
uint64_t system_version;
uint64_t title_id;
uint32_t title_type;
uint16_t group_id;
uint8_t reserved[62];
uint32_t access_rights;
uint16_t title_version;
uint16_t num_content;
uint16_t boot_index;
uint8_t padding2[2];
uint8_t system_version[8];
uint8_t title_id[8];
uint8_t title_type[4];
uint8_t group_id[2];
uint8_t public_save_size[4];
uint8_t private_save_size[4];
uint8_t padding2[8];
uint8_t parent_control[0x10];
uint8_t padding3[0x1e];
uint8_t access_rights[4];
uint8_t title_version[2];
uint8_t num_content[2];
uint8_t boot_index[2];
uint8_t padding4[2];
} PACKED tmd_header_v0_t;
static_assert(sizeof(tmd_header_v0_t) == 0x1e4, "invalid sizeof(tmd_header_v0_t)");
typedef struct {
uint32_t content_id;
uint16_t index;
uint16_t type;
uint64_t size;
uint8_t content_id[4];
uint8_t index[2];
uint8_t type[2];
uint8_t size[8];
uint8_t sha1[20];
} PACKED tmd_content_v0_t;
static_assert(sizeof(tmd_content_v0_t) == 36, "invalid sizeof(tmd_contend_v0_t)");
static_assert(sizeof(tmd_content_v0_t) == 0x24, "invalid sizeof(tmd_contend_v0_t)");
// used in ticket encryption
// http://problemkaputt.de/gbatek.htm#dsiesblockencryption
@ -102,6 +108,24 @@ typedef struct {
static_assert(sizeof(es_block_footer_t) == 0x20, "invalid sizeof(es_block_footer_t)");
// used in cert.sys
// http://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaredevkpandcertsyscertificatefiles
// "DSi SD/MMC Firmware dev.kp and cert.sys Certificate Files"
typedef struct {
uint32_t signature_type;
uint8_t signature[RSA_2048_LEN];
uint8_t padding0[0x3c];
char signature_name[0x40];
uint32_t key_type;
char key_name[0x40];
uint32_t key_flags;
uint8_t rsa_key[RSA_2048_LEN];
uint8_t rsa_exp[4];
uint8_t padding1[0x34];
} PACKED cert_t;
static_assert(sizeof(cert_t) == 0x300, "invalid sizeof(cert_t)");
#ifdef _MSC_VER
#pragma pack(pop)
#endif

40
utils.c
View File

@ -56,6 +56,40 @@ const char *hexdump(const void *b, unsigned l, int space){
return hexdump_buf;
}
void print_hex(const void *b, unsigned len) {
const u8 *p = (u8*)b;
const unsigned cols = 16;
int rows = (len + cols - 1) / cols;
// first print on t1 until it exeeds 19 lines
for (unsigned i = 0; i < rows; ++i) {
printf("%04x:", i * cols);
for (unsigned j = 0; j < cols; ++j) {
if (j % 4 == 0) {
printf(" ");
}
int offset = i * cols + j;
if (offset < len) {
printf("%02x ", p[offset]);
} else {
printf(" ");
}
}
for (unsigned j = 0; j < cols; ++j) {
if (j % 4 == 0) {
printf(" ");
}
int offset = i * cols + j;
if (offset < len) {
char c = p[offset];
printf("%c", c >= 0x20 && c <= 0x7e ? c : '.');
} else {
printf(" ");
}
}
printf("\n");
}
}
void read_block_from_file(void *out, const char *file_name, size_t offset, size_t size) {
FILE * f = fopen(file_name, "rb");
if (f == NULL) {
@ -74,7 +108,7 @@ void read_block_from_file(void *out, const char *file_name, size_t offset, size_
// read entire file to memory, don't use it on large files
// the caller is resposible to free the memory
void* read_file(const char *file_name, long *psize) {
void* read_file(const char *file_name, unsigned *psize) {
FILE *f = fopen(file_name, "rb");
if (f == NULL) {
fprintf(stderr, "can't read file: %s\n", file_name);
@ -82,6 +116,10 @@ void* read_file(const char *file_name, long *psize) {
}
fseek(f, 0, SEEK_END);
*psize = ftell(f);
if (*psize == 0) {
fprintf(stderr, "file length is zero: %s\n", file_name);
exit(-1);
}
// printf("size: %u\n", (unsigned)*psize);
void *p = malloc(*psize);
if (p == NULL) {

View File

@ -11,9 +11,11 @@ int hex2bytes(u8 *out, unsigned byte_len, const char *in, int critical);
const char * hexdump(const void *a, unsigned l, int space);
void print_hex(const void *b, unsigned len);
void read_block_from_file(void *out, const char *file_name, size_t offset, size_t size);
void* read_file(const char *file_name, long *psize);
void* read_file(const char *file_name, unsigned *psize);
void dump_to_file(const char *file_name, const void *buf, size_t len);