mirror of
https://github.com/Jimmy-Z/TWLbf.git
synced 2025-06-18 18:55:31 -04:00
verify tmd content(ticket0.c)
This commit is contained in:
parent
9fa3da359b
commit
8450380382
7
Makefile
7
Makefile
@ -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
|
||||
|
@ -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
6
dsi.c
@ -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
|
||||
|
@ -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
|
||||
|
65
ticket0.c
65
ticket0.c
@ -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");
|
||||
}
|
||||
|
86
ticket0.h
86
ticket0.h
@ -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
40
utils.c
@ -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) {
|
||||
|
4
utils.h
4
utils.h
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user