diff --git a/ctrtool/Makefile b/ctrtool/Makefile new file mode 100644 index 0000000..75a44fd --- /dev/null +++ b/ctrtool/Makefile @@ -0,0 +1,15 @@ +OBJS = keyset.o main.o ctr.o ncsd.o cia.o tik.o tmd.o filepath.o lzss.o exheader.o exefs.o ncch.o utils.o settings.o firm.o cwav.o stream.o romfs.o ivfc.o +POLAR_OBJS = polarssl/aes.o polarssl/bignum.o polarssl/rsa.o polarssl/sha2.o +TINYXML_OBJS = tinyxml/tinystr.o tinyxml/tinyxml.o tinyxml/tinyxmlerror.o tinyxml/tinyxmlparser.o +LIBS = -lstdc++ +CXXFLAGS = -I. +CFLAGS = -Wall -I. +OUTPUT = ctrtool +CC = gcc + +main: $(OBJS) $(POLAR_OBJS) $(TINYXML_OBJS) + g++ -o $(OUTPUT) $(LIBS) $(OBJS) $(POLAR_OBJS) $(TINYXML_OBJS) + + +clean: + rm -rf $(OUTPUT) $(OBJS) $(POLAR_OBJS) $(TINYXML_OBJS) diff --git a/ctrtool/cia.c b/ctrtool/cia.c new file mode 100644 index 0000000..02ee315 --- /dev/null +++ b/ctrtool/cia.c @@ -0,0 +1,295 @@ +#include +#include +#include +#include "types.h" +#include "utils.h" +#include "cia.h" + + +void cia_init(cia_context* ctx) +{ + memset(ctx, 0, sizeof(cia_context)); + + tik_init(&ctx->tik); + tmd_init(&ctx->tmd); +} + +void cia_set_file(cia_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void cia_set_offset(cia_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void cia_set_size(cia_context* ctx, u32 size) +{ + ctx->size = size; +} + + +void cia_set_usersettings(cia_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + + +void cia_save(cia_context* ctx, u32 type, u32 flags) +{ + u32 offset; + u32 size; + filepath* path = 0; + ctr_tmd_body *body; + ctr_tmd_contentchunk *chunk; + int i; + char tmpname[255]; + + switch(type) + { + case CIATYPE_CERTS: + offset = ctx->offsetcerts; + size = ctx->sizecert; + path = settings_get_certs_path(ctx->usersettings); + break; + + case CIATYPE_TIK: + offset = ctx->offsettik; + size = ctx->sizetik; + path = settings_get_tik_path(ctx->usersettings); + break; + + case CIATYPE_TMD: + offset = ctx->offsettmd; + size = ctx->sizetmd; + path = settings_get_tmd_path(ctx->usersettings); + break; + + case CIATYPE_CONTENT: + offset = ctx->offsetcontent; + size = ctx->sizecontent; + path = settings_get_content_path(ctx->usersettings); + + break; + + case CIATYPE_META: + offset = ctx->offsetmeta; + size = ctx->sizemeta; + path = settings_get_meta_path(ctx->usersettings);; + break; + + default: + fprintf(stderr, "Error, unknown CIA type specified\n"); + return; + break; + } + + if (path == 0 || path->valid == 0) + return; + + switch(type) + { + case CIATYPE_CERTS: fprintf(stdout, "Saving certs to %s\n", path->pathname); break; + case CIATYPE_TIK: fprintf(stdout, "Saving tik to %s\n", path->pathname); break; + case CIATYPE_TMD: fprintf(stdout, "Saving tmd to %s\n", path->pathname); break; + case CIATYPE_CONTENT: + + body = tmd_get_body(&ctx->tmd); + chunk = (ctr_tmd_contentchunk*)(body->contentinfo + (sizeof(ctr_tmd_contentinfo) * TMD_MAX_CONTENTS)); + + for(i = 0; i < getbe16(body->contentcount); i++) { + sprintf(tmpname, "%s.%04x.%08x", path->pathname, getbe16(chunk->index), getbe32(chunk->id)); + fprintf(stdout, "Saving content #%04x to %s\n", getbe16(chunk->index), tmpname); + + ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; + ctx->iv[1] = getbe16(chunk->index) & 0xff; + + ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); + + cia_save_blob(ctx, tmpname, offset, getbe64(chunk->size) & 0xffffffff, 1); + + offset += getbe64(chunk->size) & 0xffffffff; + + chunk++; + } + + memset(ctx->iv, 0, 16); + + return; + break; + + case CIATYPE_META: fprintf(stdout, "Saving meta to %s\n", path->pathname); break; + } + + cia_save_blob(ctx, path->pathname, offset, size, 0); +} + +void cia_save_blob(cia_context *ctx, char *out_path, u32 offset, u32 size, int do_cbc) +{ + FILE *fout = 0; + u8 buffer[16*1024]; + + fseek(ctx->file, ctx->offset + offset, SEEK_SET); + + + fout = fopen(out_path, "wb"); + if (fout == NULL) + { + fprintf(stdout, "Error opening out file %s\n", out_path); + goto clean; + } + + while(size) + { + u32 max = sizeof(buffer); + if (max > size) + max = size; + + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stdout, "Error reading file\n"); + goto clean; + } + + if (do_cbc == 1) + ctr_decrypt_cbc(&ctx->aes, buffer, buffer, max); + + if (max != fwrite(buffer, 1, max, fout)) + { + fprintf(stdout, "Error writing file\n"); + goto clean; + } + + size -= max; + } + +clean: + if (fout) + fclose(fout); +} + + +void cia_process(cia_context* ctx, u32 actions) +{ + fseek(ctx->file, 0, SEEK_SET); + + if (fread(&ctx->header, 1, sizeof(ctr_ciaheader), ctx->file) != sizeof(ctr_ciaheader)) + { + fprintf(stderr, "Error reading CIA header\n"); + goto clean; + } + + ctx->sizeheader = getle32(ctx->header.headersize); + ctx->sizecert = getle32(ctx->header.certsize); + ctx->sizetik = getle32(ctx->header.ticketsize); + ctx->sizetmd = getle32(ctx->header.tmdsize); + ctx->sizecontent = (u32)getle64(ctx->header.contentsize); + ctx->sizemeta = getle32(ctx->header.metasize); + + ctx->offsetcerts = align(ctx->sizeheader, 64); + ctx->offsettik = align(ctx->offsetcerts + ctx->sizecert, 64); + ctx->offsettmd = align(ctx->offsettik + ctx->sizetik, 64); + ctx->offsetcontent = align(ctx->offsettmd + ctx->sizetmd, 64); + ctx->offsetmeta = align(ctx->offsetcontent + ctx->sizecontent, 64); + + if (actions & InfoFlag) + cia_print(ctx); + + + tik_set_file(&ctx->tik, ctx->file); + tik_set_offset(&ctx->tik, ctx->offsettik); + tik_set_size(&ctx->tik, ctx->sizetik); + tik_set_usersettings(&ctx->tik, ctx->usersettings); + + tik_process(&ctx->tik, actions); + memset(ctx->iv, 0, 16); + + + + if (settings_get_common_key(ctx->usersettings)) + tik_get_decrypted_titlekey(&ctx->tik, ctx->titlekey); + + tmd_set_file(&ctx->tmd, ctx->file); + tmd_set_offset(&ctx->tmd, ctx->offsettmd); + tmd_set_size(&ctx->tmd, ctx->sizetmd); + tmd_set_usersettings(&ctx->tmd, ctx->usersettings); + tmd_process(&ctx->tmd, actions); + + if (actions & VerifyFlag) + { + cia_verify_contents(ctx); + } + + if (actions & InfoFlag || actions & VerifyFlag) + tmd_print(&ctx->tmd); + + if (actions & ExtractFlag) + { + cia_save(ctx, CIATYPE_CERTS, actions); + cia_save(ctx, CIATYPE_TMD, actions); + cia_save(ctx, CIATYPE_TIK, actions); + cia_save(ctx, CIATYPE_META, actions); + cia_save(ctx, CIATYPE_CONTENT, actions); + } + +clean: + return; +} + +void cia_verify_contents(cia_context *ctx) +{ + ctr_tmd_body *body; + ctr_tmd_contentchunk *chunk; + u8 *verify_buf; + u32 content_size=0; + int i; + + // verify TMD content hashes, requires decryption .. + body = tmd_get_body(&ctx->tmd); + chunk = (ctr_tmd_contentchunk*)(body->contentinfo + (sizeof(ctr_tmd_contentinfo) * TMD_MAX_CONTENTS)); + + fseek(ctx->file, ctx->offset + ctx->offsetcontent, SEEK_SET); + for(i = 0; i < getbe16(body->contentcount); i++) + { + content_size = getbe64(chunk->size) & 0xffffffff; + + ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; + ctx->iv[1] = getbe16(chunk->index) & 0xff; + + ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); + + verify_buf = malloc(content_size); + fread(verify_buf, content_size, 1, ctx->file); + + ctr_decrypt_cbc(&ctx->aes, verify_buf, verify_buf, content_size); + + if (ctr_sha_256_verify(verify_buf, content_size, chunk->hash) == Good) + ctx->tmd.content_hash_stat[i] = 1; + else + ctx->tmd.content_hash_stat[i] = 2; + + free(verify_buf); + + chunk++; + } +} + +void cia_print(cia_context* ctx) +{ + ctr_ciaheader* header = &ctx->header; + + fprintf(stdout, "Header size 0x%08x\n", getle32(header->headersize)); + fprintf(stdout, "Type %04x\n", getle16(header->type)); + fprintf(stdout, "Version %04x\n", getle16(header->version)); + fprintf(stdout, "Certificates offset: 0x%08x\n", ctx->offsetcerts); + fprintf(stdout, "Certificates size: 0x%04x\n", ctx->sizecert); + fprintf(stdout, "Ticket offset: 0x%08x\n", ctx->offsettik); + fprintf(stdout, "Ticket size 0x%04x\n", ctx->sizetik); + fprintf(stdout, "TMD offset: 0x%08x\n", ctx->offsettmd); + fprintf(stdout, "TMD size: 0x%04x\n", ctx->sizetmd); + fprintf(stdout, "Meta offset: 0x%04x\n", ctx->offsetmeta); + fprintf(stdout, "Meta size: 0x%04x\n", ctx->sizemeta); + fprintf(stdout, "Content offset: 0x%08x\n", ctx->offsetcontent); + fprintf(stdout, "Content size: 0x%016llx\n", getle64(header->contentsize)); +} diff --git a/ctrtool/cia.h b/ctrtool/cia.h new file mode 100644 index 0000000..0e5eb04 --- /dev/null +++ b/ctrtool/cia.h @@ -0,0 +1,72 @@ +#ifndef _CIA_H_ +#define _CIA_H_ + +#include "types.h" +#include "filepath.h" +#include "tik.h" +#include "tmd.h" +#include "ctr.h" +#include "settings.h" + +typedef enum +{ + CIATYPE_CERTS, + CIATYPE_TMD, + CIATYPE_TIK, + CIATYPE_CONTENT, + CIATYPE_META, +} cia_types; + +typedef struct +{ + u8 headersize[4]; + u8 type[2]; + u8 version[2]; + u8 certsize[4]; + u8 ticketsize[4]; + u8 tmdsize[4]; + u8 metasize[4]; + u8 contentsize[8]; + u8 contentindex[0x2000]; +} ctr_ciaheader; + +typedef struct +{ + FILE* file; + u32 offset; + u32 size; + u8 titlekey[16]; + u8 iv[16]; + ctr_ciaheader header; + ctr_aes_context aes; + settings* usersettings; + + tik_context tik; + tmd_context tmd; + + u32 sizeheader; + u32 sizecert; + u32 sizetik; + u32 sizetmd; + u32 sizecontent; + u32 sizemeta; + + u32 offsetcerts; + u32 offsettik; + u32 offsettmd; + u32 offsetcontent; + u32 offsetmeta; +} cia_context; + +void cia_init(cia_context* ctx); +void cia_set_file(cia_context* ctx, FILE* file); +void cia_set_offset(cia_context* ctx, u32 offset); +void cia_set_size(cia_context* ctx, u32 size); +void cia_set_usersettings(cia_context* ctx, settings* usersettings); +void cia_print(cia_context* ctx); +void cia_save(cia_context* ctx, u32 type, u32 flags); +void cia_process(cia_context* ctx, u32 actions); +void cia_save_blob(cia_context *ctx, char *out_path, u32 offset, u32 size, int do_cbc); +void cia_verify_contents(cia_context *ctx); + +#endif // _CIA_H_ diff --git a/ctrtool/ctr.c b/ctrtool/ctr.c index d7a69db..6c7ac76 100644 --- a/ctrtool/ctr.c +++ b/ctrtool/ctr.c @@ -1,143 +1,354 @@ -#include "ctr.h" -#include -#include -#include -#include - - -void ctr_set_iv( ctr_crypto_context* ctx, - unsigned char iv[16] ) -{ - memcpy(ctx->iv, iv, 16); -} - -void ctr_add_counter( ctr_crypto_context* ctx, - unsigned char carry ) -{ - unsigned char sum; - int i; - - for(i=15; i>=0; i--) - { - sum = ctx->ctr[i] + carry; - - if (sum < ctx->ctr[i]) - carry = 1; - else - carry = 0; - - ctx->ctr[i] = sum; - } -} - -void ctr_set_counter( ctr_crypto_context* ctx, - unsigned char ctr[16] ) -{ - memcpy(ctx->ctr, ctr, 16); -} - - -void ctr_init_counter( ctr_crypto_context* ctx, - unsigned char key[16], - unsigned char ctr[12] ) -{ - aes_setkey_enc(&ctx->aes, key, 128); - ctr_set_counter(ctx, ctr); -} - -void ctr_crypt_counter_block( ctr_crypto_context* ctx, - unsigned char input[16], - unsigned char output[16] ) -{ - int i; - unsigned char stream[16]; - - - aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->ctr, stream); - - - if (input) - { - for(i=0; i<16; i++) - { - output[i] = stream[i] ^ input[i]; - } - } - else - { - for(i=0; i<16; i++) - output[i] = stream[i]; - } - - ctr_add_counter(ctx, 1); -} - - -void ctr_crypt_counter( ctr_crypto_context* ctx, - unsigned char* input, - unsigned char* output, - unsigned int size ) -{ - unsigned char stream[16]; - unsigned int i; - - while(size >= 16) - { - ctr_crypt_counter_block(ctx, input, output); - - if (input) - input += 16; - if (output) - output += 16; - - size -= 16; - } - - if (size) - { - memset(stream, 0, 16); - ctr_crypt_counter_block(ctx, stream, stream); - - if (input) - { - for(i=0; iaes, key, 128); - ctr_set_iv(ctx, iv); -} - -void ctr_init_cbc_decrypt( ctr_crypto_context* ctx, - unsigned char key[16], - unsigned char iv[16] ) -{ - aes_setkey_dec(&ctx->aes, key, 128); - ctr_set_iv(ctx, iv); -} - -void ctr_encrypt_cbc( ctr_crypto_context* ctx, - unsigned char* input, - unsigned char* output, - unsigned int size ) -{ - aes_crypt_cbc(&ctx->aes, AES_ENCRYPT, size, ctx->iv, input, output); -} - -void ctr_decrypt_cbc( ctr_crypto_context* ctx, - unsigned char* input, - unsigned char* output, - unsigned int size ) -{ - aes_crypt_cbc(&ctx->aes, AES_DECRYPT, size, ctx->iv, input, output); -} +#include +#include +#include +#include + +#include "ctr.h" + + +void ctr_set_iv( ctr_aes_context* ctx, + u8 iv[16] ) +{ + memcpy(ctx->iv, iv, 16); +} + +void ctr_add_counter( ctr_aes_context* ctx, + u32 carry ) +{ + u32 counter[4]; + u32 sum; + int i; + + for(i=0; i<4; i++) + counter[i] = (ctx->ctr[i*4+0]<<24) | (ctx->ctr[i*4+1]<<16) | (ctx->ctr[i*4+2]<<8) | (ctx->ctr[i*4+3]<<0); + + for(i=3; i>=0; i--) + { + sum = counter[i] + carry; + + if (sum < counter[i]) + carry = 1; + else + carry = 0; + + counter[i] = sum; + } + + for(i=0; i<4; i++) + { + ctx->ctr[i*4+0] = counter[i]>>24; + ctx->ctr[i*4+1] = counter[i]>>16; + ctx->ctr[i*4+2] = counter[i]>>8; + ctx->ctr[i*4+3] = counter[i]>>0; + } +} + +void ctr_set_counter( ctr_aes_context* ctx, + u8 ctr[16] ) +{ + memcpy(ctx->ctr, ctr, 16); +} + + +void ctr_init_counter( ctr_aes_context* ctx, + u8 key[16], + u8 ctr[16] ) +{ + aes_setkey_enc(&ctx->aes, key, 128); + ctr_set_counter(ctx, ctr); +} + + +void ctr_crypt_counter_block( ctr_aes_context* ctx, + u8 input[16], + u8 output[16] ) +{ + int i; + u8 stream[16]; + + + aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->ctr, stream); + + + if (input) + { + for(i=0; i<16; i++) + { + output[i] = stream[i] ^ input[i]; + } + } + else + { + for(i=0; i<16; i++) + output[i] = stream[i]; + } + + ctr_add_counter(ctx, 1); +} + + +void ctr_crypt_counter( ctr_aes_context* ctx, + u8* input, + u8* output, + u32 size ) +{ + u8 stream[16]; + u32 i; + + while(size >= 16) + { + ctr_crypt_counter_block(ctx, input, output); + + if (input) + input += 16; + if (output) + output += 16; + + size -= 16; + } + + if (size) + { + memset(stream, 0, 16); + ctr_crypt_counter_block(ctx, stream, stream); + + if (input) + { + for(i=0; iaes, key, 128); + ctr_set_iv(ctx, iv); +} + +void ctr_init_cbc_decrypt( ctr_aes_context* ctx, + u8 key[16], + u8 iv[16] ) +{ + aes_setkey_dec(&ctx->aes, key, 128); + ctr_set_iv(ctx, iv); +} + +void ctr_encrypt_cbc( ctr_aes_context* ctx, + u8* input, + u8* output, + u32 size ) +{ + aes_crypt_cbc(&ctx->aes, AES_ENCRYPT, size, ctx->iv, input, output); +} + +void ctr_decrypt_cbc( ctr_aes_context* ctx, + u8* input, + u8* output, + u32 size ) +{ + aes_crypt_cbc(&ctx->aes, AES_DECRYPT, size, ctx->iv, input, output); +} + +void ctr_sha_256( const u8* data, + u32 size, + u8 hash[0x20] ) +{ + sha2(data, size, hash, 0); +} + +int ctr_sha_256_verify( const u8* data, + u32 size, + const u8 checkhash[0x20] ) +{ + u8 hash[0x20]; + + sha2(data, size, hash, 0); + + if (memcmp(hash, checkhash, 0x20) == 0) + return Good; + else + return Fail; +} + +void ctr_sha_256_init( ctr_sha256_context* ctx ) +{ + sha2_starts(&ctx->sha, 0); +} + +void ctr_sha_256_update( ctr_sha256_context* ctx, + const u8* data, + u32 size ) +{ + sha2_update(&ctx->sha, data, size); +} + + +void ctr_sha_256_finish( ctr_sha256_context* ctx, + u8 hash[0x20] ) +{ + sha2_finish(&ctx->sha, hash); +} + + +void ctr_rsa_init_key_pubmodulus(rsakey2048* key, u8 modulus[0x100]) +{ + u8 exponent[3] = {0x01, 0x00, 0x01}; + + ctr_rsa_init_key_pub(key, modulus, exponent); +} + +void ctr_rsa_init_key_pub(rsakey2048* key, u8 modulus[0x100], u8 exponent[3]) +{ + key->keytype = RSAKEY_PUB; + memcpy(key->n, modulus, 0x100); + memcpy(key->e, exponent, 3); +} + +int ctr_rsa_init(ctr_rsa_context* ctx, rsakey2048* key) +{ + rsa_init(&ctx->rsa, RSA_PKCS_V15, 0); + ctx->rsa.len = 0x100; + + if (key->keytype == RSAKEY_INVALID) + goto clean; + + if (mpi_read_binary(&ctx->rsa.N, key->n, sizeof(key->n))) + goto clean; + if (mpi_read_binary(&ctx->rsa.E, key->e, sizeof(key->e))) + goto clean; + if (rsa_check_pubkey(&ctx->rsa)) + goto clean; + + if (key->keytype == RSAKEY_PRIV) + { + if (mpi_read_binary(&ctx->rsa.D, key->d, sizeof(key->d))) + goto clean; + if (mpi_read_binary(&ctx->rsa.P, key->p, sizeof(key->p))) + goto clean; + if (mpi_read_binary(&ctx->rsa.Q, key->q, sizeof(key->q))) + goto clean; + if (mpi_read_binary(&ctx->rsa.DP, key->dp, sizeof(key->dp))) + goto clean; + if (mpi_read_binary(&ctx->rsa.DQ, key->dq, sizeof(key->dq))) + goto clean; + if (mpi_read_binary(&ctx->rsa.QP, key->qp, sizeof(key->qp))) + goto clean; + if (rsa_check_privkey(&ctx->rsa)) + goto clean; + } + + return 1; +clean: + return 0; +} + +int ctr_rsa_verify_hash(const u8 signature[0x100], const u8 hash[0x20], rsakey2048* key) +{ + ctr_rsa_context ctx; + u32 result; + u8 output[0x100]; + + if (key->keytype == RSAKEY_INVALID) + return Fail; + + ctr_rsa_init(&ctx, key); +// memset(output, 0, 0x100); +// result = ctr_rsa_public(signature, output, key); +// printf("Result = %d\n", result); +// memdump(stdout, "output: ", output, 0x100); + + result = rsa_pkcs1_verify(&ctx.rsa, RSA_PUBLIC, SIG_RSA_SHA256, 0x20, hash, (u8*)signature); + + ctr_rsa_free(&ctx); + + if (result == 0) + return Good; + else + return Fail; +} + + +int ctr_rsa_sign_hash(const u8 hash[0x20], u8 signature[0x100], rsakey2048* key) +{ + ctr_rsa_context ctx; + u32 result; + + ctr_rsa_init(&ctx, key); + + result = rsa_pkcs1_verify(&ctx.rsa, RSA_PUBLIC, SIG_RSA_SHA256, 0x20, hash, (u8*)signature); + result = rsa_pkcs1_sign(&ctx.rsa, RSA_PRIVATE, SIG_RSA_SHA256, 0x20, hash, signature); + + ctr_rsa_free(&ctx); + + if (result == 0) + return 1; + else + return 0; +} + +int ctr_rsa_public(const u8 signature[0x100], u8 output[0x100], rsakey2048* key) +{ + ctr_rsa_context ctx; + u32 result; + + ctr_rsa_init(&ctx, key); + + result = rsa_public(&ctx.rsa, signature, output); + + ctr_rsa_free(&ctx); + + if (result == 0) + return 1; + else + return 0; +} + + +void ctr_rsa_free(ctr_rsa_context* ctx) +{ + rsa_free(&ctx->rsa); +} + +/* + * Generate DP, DQ, QP based on private key + */ +#if 0 +static int ctr_rsa_key_init(ctr_rsa_context* ctx ) +{ + int ret; + mpi P1, Q1; + + mpi_init( &P1, &Q1, NULL ); + + MPI_CHK( mpi_sub_int( &P1, &ctx->rsa.P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->rsa.Q, 1 ) ); + + /* + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_mod_mpi( &ctx->rsa.DP, &ctx->rsa.D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->rsa.DQ, &ctx->rsa.D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->rsa.QP, &ctx->rsa.Q, &ctx->rsa.P ) ); + +cleanup: + + mpi_free(&Q1, &P1, NULL ); + + if( ret != 0 ) + { + rsa_free( &ctx->rsa ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED | ret ); + } + + return( 0 ); +} +#endif \ No newline at end of file diff --git a/ctrtool/ctr.h b/ctrtool/ctr.h index f2e3f1a..724f4c0 100644 --- a/ctrtool/ctr.h +++ b/ctrtool/ctr.h @@ -1,11 +1,19 @@ #ifndef _CTR_H_ #define _CTR_H_ -#include "aes.h" - +#include "polarssl/aes.h" +#include "polarssl/rsa.h" +#include "polarssl/sha2.h" +#include "types.h" +#include "keyset.h" #define MAGIC_NCCH 0x4843434E #define MAGIC_NCSD 0x4453434E +#define MAGIC_FIRM 0x4D524946 +#define MAGIC_CWAV 0x56415743 +#define MAGIC_IVFC 0x43465649 + +#define SIZE_128MB (128 * 1024 * 1024) typedef enum { @@ -13,117 +21,123 @@ typedef enum FILETYPE_CCI, FILETYPE_CXI, FILETYPE_CIA, + FILETYPE_EXHEADER, + FILETYPE_TMD, + FILETYPE_LZSS, + FILETYPE_FIRM, + FILETYPE_CWAV, + FILETYPE_ROMFS } ctr_filetypes; -typedef enum -{ - NCCHTYPE_EXTHEADER = 1, - NCCHTYPE_EXEFS = 2, - NCCHTYPE_ROMFS = 3, -} ctr_ncchtypes; - typedef struct { - unsigned char signature[0x100]; - unsigned char magic[4]; - unsigned char contentsize[4]; - unsigned char partitionid[8]; - unsigned char makercode[2]; - unsigned char version[2]; - unsigned char reserved0[4]; - unsigned char programid[8]; - unsigned char tempflag; - unsigned char reserved1[0x2f]; - unsigned char productcode[0x10]; - unsigned char extendedheaderhash[0x20]; - unsigned char extendedheadersize[4]; - unsigned char reserved2[4]; - unsigned char flags[8]; - unsigned char plainregionoffset[4]; - unsigned char plainregionsize[4]; - unsigned char reserved3[8]; - unsigned char exefsoffset[4]; - unsigned char exefssize[4]; - unsigned char exefshashregionsize[4]; - unsigned char reserved4[4]; - unsigned char romfsoffset[4]; - unsigned char romfssize[4]; - unsigned char romfshashregionsize[4]; - unsigned char reserved5[4]; - unsigned char exefssuperblockhash[0x20]; - unsigned char romfssuperblockhash[0x20]; -} ctr_ncchheader; - + u8 ctr[16]; + u8 iv[16]; + aes_context aes; +} ctr_aes_context; typedef struct { - unsigned char headersize[4]; - unsigned char type[2]; - unsigned char version[2]; - unsigned char certsize[4]; - unsigned char ticketsize[4]; - unsigned char tmdsize[4]; - unsigned char metasize[4]; - unsigned char contentsize[8]; - unsigned char contentindex[0x2000]; -} ctr_ciaheader; + rsa_context rsa; +} ctr_rsa_context; typedef struct { - unsigned char ctr[16]; - unsigned char iv[16]; - aes_context aes; -} -ctr_crypto_context; + sha2_context sha; +} ctr_sha256_context; #ifdef __cplusplus extern "C" { #endif -void ctr_set_iv( ctr_crypto_context* ctx, - unsigned char iv[16] ); +void ctr_set_iv( ctr_aes_context* ctx, + u8 iv[16] ); + +void ctr_add_counter( ctr_aes_context* ctx, + u32 carry ); + +void ctr_set_counter( ctr_aes_context* ctx, + u8 ctr[16] ); + + +void ctr_init_counter( ctr_aes_context* ctx, + u8 key[16], + u8 ctr[16] ); + + +void ctr_crypt_counter_block( ctr_aes_context* ctx, + u8 input[16], + u8 output[16] ); + + +void ctr_crypt_counter( ctr_aes_context* ctx, + u8* input, + u8* output, + u32 size ); + + +void ctr_init_cbc_encrypt( ctr_aes_context* ctx, + u8 key[16], + u8 iv[16] ); + +void ctr_init_cbc_decrypt( ctr_aes_context* ctx, + u8 key[16], + u8 iv[16] ); + +void ctr_encrypt_cbc( ctr_aes_context* ctx, + u8* input, + u8* output, + u32 size ); + +void ctr_decrypt_cbc( ctr_aes_context* ctx, + u8* input, + u8* output, + u32 size ); + +void ctr_rsa_init_key_pubmodulus( rsakey2048* key, + u8 modulus[0x100] ); + +void ctr_rsa_init_key_pub( rsakey2048* key, + u8 modulus[0x100], + u8 exponent[3] ); -void ctr_add_counter( ctr_crypto_context* ctx, - unsigned char carry ); +int ctr_rsa_init( ctr_rsa_context* ctx, + rsakey2048* key ); -void ctr_set_counter( ctr_crypto_context* ctx, - unsigned char ctr[16] ); +void ctr_rsa_free( ctr_rsa_context* ctx ); -void ctr_init_counter( ctr_crypto_context* ctx, - unsigned char key[16], - unsigned char ctr[12] ); +int ctr_rsa_verify_hash( const u8 signature[0x100], + const u8 hash[0x20], + rsakey2048* key); -void ctr_crypt_counter_block( ctr_crypto_context* ctx, - unsigned char input[16], - unsigned char output[16] ); +int ctr_rsa_sign_hash( const u8 hash[0x20], + u8 signature[0x100], + rsakey2048* key ); +int ctr_rsa_public( const u8 signature[0x100], + u8 output[0x100], + rsakey2048* key ); -void ctr_crypt_counter( ctr_crypto_context* ctx, - unsigned char* input, - unsigned char* output, - unsigned int size ); +void ctr_sha_256( const u8* data, + u32 size, + u8 hash[0x20] ); +int ctr_sha_256_verify( const u8* data, + u32 size, + const u8 checkhash[0x20] ); -void ctr_init_cbc_encrypt( ctr_crypto_context* ctx, - unsigned char key[16], - unsigned char iv[16] ); -void ctr_init_cbc_decrypt( ctr_crypto_context* ctx, - unsigned char key[16], - unsigned char iv[16] ); +void ctr_sha_256_init( ctr_sha256_context* ctx ); -void ctr_encrypt_cbc( ctr_crypto_context* ctx, - unsigned char* input, - unsigned char* output, - unsigned int size ); +void ctr_sha_256_update( ctr_sha256_context* ctx, + const u8* data, + u32 size ); -void ctr_decrypt_cbc( ctr_crypto_context* ctx, - unsigned char* input, - unsigned char* output, - unsigned int size ); +void ctr_sha_256_finish( ctr_sha256_context* ctx, + u8 hash[0x20] ); #ifdef __cplusplus } diff --git a/ctrtool/ctrtool.vcproj b/ctrtool/ctrtool.vcproj index 4a05d4d..b6d8d7e 100644 --- a/ctrtool/ctrtool.vcproj +++ b/ctrtool/ctrtool.vcproj @@ -1,222 +1,442 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c new file mode 100644 index 0000000..b1c1b27 --- /dev/null +++ b/ctrtool/cwav.c @@ -0,0 +1,1001 @@ +#include +#include +#include + +#include "types.h" +#include "cwav.h" +#include "utils.h" +#include "stream.h" + +#define BUFFERSIZE (4*1024) +#define SAMPLECOUNT 1024 + +static const int ima_adpcm_step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +static const int ima_adpcm_index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +void cwav_init(cwav_context* ctx) +{ + memset(ctx, 0, sizeof(cwav_context)); +} + +void cwav_set_file(cwav_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void cwav_set_offset(cwav_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void cwav_set_size(cwav_context* ctx, u32 size) +{ + ctx->size = size; +} + +void cwav_set_usersettings(cwav_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void cwav_process(cwav_context* ctx, u32 actions) +{ + u32 i; + u32 infoheaderoffset; + + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, sizeof(cwav_header), ctx->file); + + infoheaderoffset = getle32(ctx->header.infoblockref.offset); + + fseek(ctx->file, ctx->offset + infoheaderoffset, SEEK_SET); + fread(&ctx->infoheader, 1, sizeof(cwav_infoheader), ctx->file); + + ctx->channelcount = getle32(ctx->infoheader.channelcount); + if (ctx->channelcount) + { + ctx->channel = malloc(ctx->channelcount * sizeof(cwav_channel)); + + for(i=0; ichannelcount; i++) + { + fread(&ctx->channel[i].inforef, sizeof(cwav_reference), 1, ctx->file); + } + + for(i=0; ichannelcount; i++) + { + u32 channeloffset = infoheaderoffset + 0x1C + getle32(ctx->channel[i].inforef.offset); + + fseek(ctx->file, ctx->offset + channeloffset, SEEK_SET); + fread(&ctx->channel[i].info, sizeof(cwav_channelinfo), 1, ctx->file); + + if (ctx->infoheader.encoding == CWAV_ENCODING_DSPADPCM) + { + if (getle16(ctx->channel[i].info.codecref.idtype) == 0x300) + { + u32 codecoffset = channeloffset + getle32(ctx->channel[i].info.codecref.offset); + + fseek(ctx->file, ctx->offset + codecoffset, SEEK_SET); + fread(&ctx->channel[i].infodspadpcm, sizeof(cwav_dspadpcminfo), 1, ctx->file); + } + } + else if (ctx->infoheader.encoding == CWAV_ENCODING_IMAADPCM) + { + if (getle16(ctx->channel[i].info.codecref.idtype) == 0x301) + { + u32 codecoffset = channeloffset + getle32(ctx->channel[i].info.codecref.offset); + + fseek(ctx->file, ctx->offset + codecoffset, SEEK_SET); + fread(&ctx->channel[i].infoimaadpcm, sizeof(cwav_imaadpcminfo), 1, ctx->file); + } + } + } + } + + + if (actions & InfoFlag) + { + cwav_print(ctx); + } + + if (actions & ExtractFlag) + { + filepath* path = settings_get_wav_path(ctx->usersettings); + + if (path && path->valid) + cwav_save_to_wav(ctx, path->pathname); + } + + + free(ctx->channel); +} + + +void cwav_write_wav_header(cwav_context* ctx, stream_out_context* outstreamctx, u32 size) +{ + wav_pcm_header header; + u32 samplerate = getle32(ctx->infoheader.samplerate); + u32 channelcount = ctx->channelcount; + + + putle32(header.chunkid, 0x46464952); + putle32(header.chunksize, 36 + size); + putle32(header.format, 0x45564157); + putle32(header.subchunk1id, 0x20746d66); + putle32(header.subchunk1size, 16); + putle16(header.audioformat, 1); + putle16(header.numchannels, channelcount); + putle32(header.samplerate, samplerate); + putle32(header.byterate, samplerate * channelcount * 2); + putle16(header.blockalign, channelcount * 2); + putle16(header.bitspersample, 16); + + putle32(header.subchunk2id, 0x61746164); + putle32(header.subchunk2size, size); + + stream_out_buffer(outstreamctx, &header, sizeof(wav_pcm_header)); +} + +int cwav_dspadpcm_decode_to_wav(cwav_context* ctx, stream_out_context* outstreamctx) +{ + u32 s, c, i; + int result = 0; + cwav_dspadpcmstate state; + u32 loopcount = settings_get_cwav_loopcount(ctx->usersettings); + + cwav_dspadpcm_init(&state); + + if (0 == cwav_dspadpcm_allocate(&state, ctx)) + goto clean; + + for(i=0; i<1+loopcount; i++) + { + int isloop = (i != 0); + + if (0 == cwav_dspadpcm_setup(&state, ctx, isloop)) + goto clean; + + while(1) + { + if (0 == cwav_dspadpcm_decode(&state, ctx)) + goto clean; + + if (state.samplecountavailable == 0) + break; + + for(s=0; schannelcount; c++) + { + s16 sampledata = state.channelstate[c].samplebuffer[s]; + + if (!stream_out_byte(outstreamctx, 0xFF & sampledata) || !stream_out_byte(outstreamctx, 0xFF & (sampledata>>8))) + { + fprintf(stderr, "Error writing output stream\n"); + goto clean; + } + } + } + } + } + + result = 1; + +clean: + cwav_dspadpcm_destroy(&state); + + return result; +} + + +int cwav_imaadpcm_decode_to_wav(cwav_context* ctx, stream_out_context* outstreamctx) +{ + u32 s, c, i; + int result = 0; + cwav_imaadpcmstate state; + u32 loopcount = settings_get_cwav_loopcount(ctx->usersettings); + + + cwav_imaadpcm_init(&state); + if (0 == cwav_imaadpcm_allocate(&state, ctx)) + goto clean; + + for(i=0; i<1+loopcount; i++) + { + int isloop = (i != 0); + + if (0 == cwav_imaadpcm_setup(&state, ctx, isloop)) + goto clean; + + while(1) + { + if (0 == cwav_imaadpcm_decode(&state, ctx)) + goto clean; + + if (state.samplecountavailable == 0) + break; + + for(s=0; schannelcount; c++) + { + s16 sampledata = state.channelstate[c].samplebuffer[s]; + + if (!stream_out_byte(outstreamctx, 0xFF & sampledata) || !stream_out_byte(outstreamctx, 0xFF & (sampledata>>8))) + { + fprintf(stderr, "Error writing output stream\n"); + goto clean; + } + } + } + } + } + + result = 1; + +clean: + cwav_imaadpcm_destroy(&state); + + return result; +} + + +int cwav_pcm_decode_to_wav(cwav_context* ctx, stream_out_context* outstreamctx) +{ + u32 s, c, i; + int result = 0; + cwav_pcmstate state; + u32 loopcount = settings_get_cwav_loopcount(ctx->usersettings); + + + cwav_pcm_init(&state); + + + if (0 == cwav_pcm_allocate(&state, ctx)) + goto clean; + + for(i=0; i<1+loopcount; i++) + { + int isloop = (i != 0); + + if (0 == cwav_pcm_setup(&state, ctx, isloop)) + goto clean; + + while(1) + { + if (0 == cwav_pcm_decode(&state, ctx)) + goto clean; + + if (state.samplecountavailable == 0) + break; + + for(s=0; schannelcount; c++) + { + s16 sampledata = state.channelstate[c].samplebuffer[s]; + + if (!stream_out_byte(outstreamctx, 0xFF & sampledata) || !stream_out_byte(outstreamctx, 0xFF & (sampledata>>8))) + { + fprintf(stderr, "Error writing output stream\n"); + goto clean; + } + } + } + } + } + + result = 1; + +clean: + cwav_pcm_destroy(&state); + + return result; +} + +int cwav_save_to_wav(cwav_context* ctx, const char* filepath) +{ + u32 startposition = 0; + u32 endposition = 0; + int result = 0; + FILE* outfile = 0; + stream_out_context outstreamctx; + + + stream_out_init(&outstreamctx); + + if (ctx->channelcount == 0) + goto clean; + + fprintf(stdout, "Saving sound data to %s...\n", filepath); + outfile = fopen(filepath, "wb"); + if (!outfile) + { + fprintf(stderr, "Error could not open file %s for writing.\n", filepath); + goto clean; + } + + stream_out_allocate(&outstreamctx, BUFFERSIZE, outfile); + stream_out_skip(&outstreamctx, sizeof(wav_pcm_header)); + stream_out_position(&outstreamctx, &startposition); + + if (ctx->infoheader.encoding == CWAV_ENCODING_DSPADPCM) + result = cwav_dspadpcm_decode_to_wav(ctx, &outstreamctx); + else if (ctx->infoheader.encoding == CWAV_ENCODING_IMAADPCM) + result = cwav_imaadpcm_decode_to_wav(ctx, &outstreamctx); + else if (ctx->infoheader.encoding == CWAV_ENCODING_PCM16) + result = cwav_pcm_decode_to_wav(ctx, &outstreamctx); + else if (ctx->infoheader.encoding == CWAV_ENCODING_PCM8) + result = cwav_pcm_decode_to_wav(ctx, &outstreamctx); + + if (!result) + goto clean; + + stream_out_position(&outstreamctx, &endposition); + + stream_out_seek(&outstreamctx, 0); + cwav_write_wav_header(ctx, &outstreamctx, endposition-startposition); + stream_out_flush(&outstreamctx); + result = 1; + +clean: + stream_out_destroy(&outstreamctx); + + if (outfile) + fclose(outfile); + + return result; +} + +void cwav_dspadpcm_init(cwav_dspadpcmstate* state) +{ + memset(state, 0, sizeof(cwav_dspadpcmstate)); +} + +int cwav_dspadpcm_allocate(cwav_dspadpcmstate* state, cwav_context* ctx) +{ + u32 channelcount = ctx->channelcount; + + + state->samplebuffer = malloc(sizeof(s16) * SAMPLECOUNT * channelcount); + state->channelstate = malloc(sizeof(cwav_dspadpcmchannelstate) * channelcount); + state->samplecountcapacity = SAMPLECOUNT; + state->samplecountavailable = 0; + state->samplecountremaining = 0; + + if (ctx->channel == 0) + return 0; + + if (state->samplebuffer == 0 || state->channelstate == 0) + { + fprintf(stderr, "Error allocating memory\n"); + return 0; + } + + return 1; +} + +int cwav_dspadpcm_setup(cwav_dspadpcmstate* state, cwav_context* ctx, int isloop) +{ + u32 channelcount = ctx->channelcount; + u32 i; + u32 startoffset = 0; + + + if (ctx->channel == 0) + return 0; + + if (state->samplebuffer == 0 || state->channelstate == 0) + return 0; + + state->samplecountavailable = 0; + + if (isloop) + { + state->samplecountremaining = getle32(ctx->infoheader.loopend) - getle32(ctx->infoheader.loopstart); + + startoffset = getle32(ctx->infoheader.loopstart) * 8 / 14; + } + else + { + state->samplecountremaining = getle32(ctx->infoheader.loopend); + startoffset = 0; + } + + + + for(i=0; ichannel[i]; + cwav_dspadpcminfo* adpcminfo = &adpcmchannel->infodspadpcm; + + if (getle16(adpcmchannel->info.codecref.idtype) != 0x300) + { + fprintf(stderr, "Error, not DSP-ADPCM format.\n"); + return 0; + } + + state->channelstate[i].samplebuffer = state->samplebuffer + SAMPLECOUNT * i; + state->channelstate[i].sampleoffset = ctx->offset + getle32(adpcmchannel->info.sampleref.offset) + getle32(ctx->header.datablockref.offset) + 8 + startoffset; + if (isloop) + { + state->channelstate[i].yn1 = getle16(adpcminfo->loopyn1); + state->channelstate[i].yn2 = getle16(adpcminfo->loopyn2); + } + else + { + state->channelstate[i].yn1 = getle16(adpcminfo->yn1); + state->channelstate[i].yn2 = getle16(adpcminfo->yn2); + } + stream_in_allocate(&state->channelstate[i].instreamctx, BUFFERSIZE, ctx->file); + stream_in_seek(&state->channelstate[i].instreamctx, state->channelstate[i].sampleoffset); + } + + return 1; +} + +// decode dsp-adpcm to pcm signed 16-bit +int cwav_dspadpcm_decode(cwav_dspadpcmstate* state, cwav_context* ctx) +{ + u32 i, c; + u32 maxsamplecount; + u32 channelcount = ctx->channelcount; + + if (ctx->channel == 0 || state->samplebuffer == 0 || state->channelstate == 0) + return 0; + + + state->samplecountavailable = 0; + if (state->samplecountremaining <= 0) + { + return 1; + } + + while(state->samplecountremaining > 0) + { + u32 samplecountavailable = state->samplecountcapacity - state->samplecountavailable; + + if (state->samplecountremaining < 14) + maxsamplecount = state->samplecountremaining; + else + maxsamplecount = 14; + + if (samplecountavailable < maxsamplecount) + break; + + for(c=0; cchannelstate[c]; + + s16* samplebuffer = channelstate->samplebuffer + state->samplecountavailable; + stream_in_context* instreamctx = &channelstate->instreamctx; + s16 yn1 = channelstate->yn1; + s16 yn2 = channelstate->yn2; + cwav_channel* adpcmchannel = &ctx->channel[c]; + cwav_dspadpcminfo* adpcminfo = &adpcmchannel->infodspadpcm; + + u8 data; + u8 lonibble; + u8 hinibble; + s16 coef1; + s16 coef2; + u32 shift; + s16 table[14]; + + stream_in_reseek(instreamctx); + + + if (0 == stream_in_byte(instreamctx, &data)) + { + fprintf(stderr, "Error reading input stream\n"); + return 1; + } + + lonibble = data & 0xF; + hinibble = data>>4; + + coef1 = getle16(adpcminfo->coef[hinibble*2+0]); + coef2 = getle16(adpcminfo->coef[hinibble*2+1]); + shift = 17 - lonibble; + + for(i=0; i<7; i++) + { + stream_in_byte(instreamctx, &data); + table[i*2+0] = data>>4; + table[i*2+1] = data & 0xF; + } + + + for(i=0; i> shift; + + s32 prediction = (yn1 * coef1 + yn2 * coef2 + xshifted + 0x400)>>11; + + if (prediction < -0x8000) + prediction = -0x8000; + if (prediction > 0x7FFF) + prediction = 0x7FFF; + + yn2 = yn1; + yn1 = prediction; + + samplebuffer[i] = prediction; + } + + channelstate->yn1 = yn1; + channelstate->yn2 = yn2; + } + + state->samplecountremaining -= maxsamplecount; + state->samplecountavailable += maxsamplecount; + } + + return 1; +} + +void cwav_dspadpcm_destroy(cwav_dspadpcmstate* state) +{ + free(state->channelstate); + free(state->samplebuffer); + + state->channelstate = 0; + state->samplebuffer = 0; +} + +void cwav_imaadpcm_init(cwav_imaadpcmstate* state) +{ + memset(state, 0, sizeof(cwav_imaadpcmstate)); +} + + +int cwav_imaadpcm_allocate(cwav_imaadpcmstate* state, cwav_context* ctx) +{ + u32 channelcount = ctx->channelcount; + + + state->samplebuffer = malloc(sizeof(s16) * SAMPLECOUNT * channelcount); + state->channelstate = malloc(sizeof(cwav_imaadpcmchannelstate) * channelcount); + state->samplecountcapacity = SAMPLECOUNT; + state->samplecountavailable = 0; + state->samplecountremaining = 0; + + if (ctx->channel == 0) + return 0; + + if (state->samplebuffer == 0 || state->channelstate == 0) + { + fprintf(stderr, "Error allocating memory\n"); + return 0; + } + + return 1; +} + +int cwav_imaadpcm_setup(cwav_imaadpcmstate* state, cwav_context* ctx, int isloop) +{ + u32 channelcount = ctx->channelcount; + u32 i; + u32 startoffset = 0; + + + if (ctx->channel == 0) + return 0; + + if (state->samplebuffer == 0 || state->channelstate == 0) + { + fprintf(stderr, "Error allocating memory\n"); + return 0; + } + + state->samplecountavailable = 0; + if (isloop) + { + state->samplecountremaining = getle32(ctx->infoheader.loopend) - getle32(ctx->infoheader.loopstart); + + startoffset = getle32(ctx->infoheader.loopstart) / 2; + } + else + { + state->samplecountremaining = getle32(ctx->infoheader.loopend); + startoffset = 0; + } + + for(i=0; ichannel[i]; + cwav_imaadpcminfo* adpcminfo = &adpcmchannel->infoimaadpcm; + + if (getle16(adpcmchannel->info.codecref.idtype) != 0x301) + { + fprintf(stderr, "Error, not IMA-ADPCM format.\n"); + return 0; + } + + state->channelstate[i].samplebuffer = state->samplebuffer + SAMPLECOUNT * i; + state->channelstate[i].sampleoffset = ctx->offset + getle32(adpcmchannel->info.sampleref.offset) + getle32(ctx->header.datablockref.offset) + 8 + startoffset; + if (isloop) + { + state->channelstate[i].data = getle16(adpcminfo->loopdata); + state->channelstate[i].tableindex = adpcminfo->looptableindex; + } + else + { + state->channelstate[i].data = getle16(adpcminfo->data); + state->channelstate[i].tableindex = adpcminfo->tableindex; + } + stream_in_allocate(&state->channelstate[i].instreamctx, BUFFERSIZE, ctx->file); + stream_in_seek(&state->channelstate[i].instreamctx, state->channelstate[i].sampleoffset); + } + + return 1; +} + +u8 cwav_imaadpcm_clamp_tableindex(u8 tableindex, int inc) +{ + int unclamped = tableindex + inc; + + if (unclamped < 0) + unclamped = 0; + if (unclamped > 88) + unclamped = 88; + + return unclamped; +} + +// decode ima-adpcm to pcm signed 16-bit +int cwav_imaadpcm_decode(cwav_imaadpcmstate* state, cwav_context* ctx) +{ + u32 i, c; + u32 maxsamplecount; + u32 channelcount = ctx->channelcount; + + if (ctx->channel == 0 || state->samplebuffer == 0 || state->channelstate == 0) + return 0; + + + state->samplecountavailable = 0; + if (state->samplecountremaining <= 0) + { + return 1; + } + + while(state->samplecountremaining > 0) + { + u32 samplecountavailable = state->samplecountcapacity - state->samplecountavailable; + + if (state->samplecountremaining < 2) + maxsamplecount = state->samplecountremaining; + else + maxsamplecount = 2; + + if (samplecountavailable < maxsamplecount) + break; + + for(c=0; cchannelstate[c]; + + s16* samplebuffer = channelstate->samplebuffer + state->samplecountavailable; + stream_in_context* instreamctx = &channelstate->instreamctx; + s16 prediction = channelstate->data; + u8 tableindex = channelstate->tableindex; + cwav_channel* adpcmchannel = &ctx->channel[c]; + cwav_imaadpcminfo* adpcminfo = &adpcmchannel->infoimaadpcm; + u8 data; + + + stream_in_reseek(instreamctx); + + + if (0 == stream_in_byte(instreamctx, &data)) + { + fprintf(stderr, "Error reading input stream\n"); + return 1; + } + + + for(i=0; i 0x7FFF) + prediction = 0x7FFF; + + samplebuffer[i] = prediction; + data >>= 4; + } + + channelstate->data = prediction; + channelstate->tableindex = tableindex; + } + + state->samplecountremaining -= maxsamplecount; + state->samplecountavailable += maxsamplecount; + } + + return 1; +} + +void cwav_imaadpcm_destroy(cwav_imaadpcmstate* state) +{ + free(state->channelstate); + free(state->samplebuffer); + + state->channelstate = 0; + state->samplebuffer = 0; +} + + +void cwav_pcm_init(cwav_pcmstate* state) +{ + memset(state, 0, sizeof(cwav_pcmstate)); +} + +int cwav_pcm_allocate(cwav_pcmstate* state, cwav_context* ctx) +{ + u32 channelcount = ctx->channelcount; + + + state->samplebuffer = malloc(sizeof(s16) * SAMPLECOUNT * channelcount); + state->channelstate = malloc(sizeof(cwav_pcmchannelstate) * channelcount); + state->samplecountcapacity = SAMPLECOUNT; + state->samplecountavailable = 0; + state->samplecountremaining = 0; + + if (ctx->channel == 0) + return 0; + + if (state->samplebuffer == 0 || state->channelstate == 0) + { + fprintf(stderr, "Error allocating memory\n"); + return 0; + } + + return 1; +} + +int cwav_pcm_setup(cwav_pcmstate* state, cwav_context* ctx, int isloop) +{ + u32 channelcount = ctx->channelcount; + u32 i; + u32 startoffset; + + + if (ctx->channel == 0) + return 0; + + if (state->samplebuffer == 0 || state->channelstate == 0) + { + fprintf(stderr, "Error allocating memory\n"); + return 0; + } + + state->samplecountavailable = 0; + state->samplecountcapacity = SAMPLECOUNT; + if (isloop) + { + state->samplecountremaining = getle32(ctx->infoheader.loopend) - getle32(ctx->infoheader.loopstart); + + if (ctx->infoheader.encoding == CWAV_ENCODING_PCM8) + startoffset = getle32(ctx->infoheader.loopstart); + else if (ctx->infoheader.encoding == CWAV_ENCODING_PCM16) + startoffset = getle32(ctx->infoheader.loopstart) * 2; + } + else + { + state->samplecountremaining = getle32(ctx->infoheader.loopend); + startoffset = 0; + } + + for(i=0; ichannel[i]; + + state->channelstate[i].samplebuffer = state->samplebuffer + SAMPLECOUNT * i; + state->channelstate[i].sampleoffset = ctx->offset + getle32(pcmchannel->info.sampleref.offset) + getle32(ctx->header.datablockref.offset) + 8 + startoffset; + stream_in_allocate(&state->channelstate[i].instreamctx, BUFFERSIZE, ctx->file); + stream_in_seek(&state->channelstate[i].instreamctx, state->channelstate[i].sampleoffset); + } + + return 1; +} + +// decode pcm to pcm signed 16-bit +int cwav_pcm_decode(cwav_pcmstate* state, cwav_context* ctx) +{ + u32 i, c; + u32 maxsamplecount; + u32 channelcount = ctx->channelcount; + + if (ctx->channel == 0 || state->samplebuffer == 0 || state->channelstate == 0) + return 0; + + + state->samplecountavailable = 0; + if (state->samplecountremaining <= 0) + { + return 1; + } + + while(state->samplecountremaining > 0) + { + u32 samplecountavailable = state->samplecountcapacity - state->samplecountavailable; + + if (state->samplecountremaining < 1) + maxsamplecount = state->samplecountremaining; + else + maxsamplecount = 1; + + if (samplecountavailable < maxsamplecount) + break; + + for(c=0; cchannelstate[c]; + + s16* samplebuffer = channelstate->samplebuffer + state->samplecountavailable; + stream_in_context* instreamctx = &channelstate->instreamctx; + cwav_channel* pcmchannel = &ctx->channel[c]; + + + stream_in_reseek(instreamctx); + + for(i=0; iinfoheader.encoding == CWAV_ENCODING_PCM16) + { + if (0 == stream_in_byte(instreamctx, &datalo) || 0 == stream_in_byte(instreamctx, &datahi)) + { + fprintf(stderr, "Error reading input stream\n"); + return 1; + } + samplebuffer[i] = (datahi << 8) | datalo; + } + else if (ctx->infoheader.encoding == CWAV_ENCODING_PCM8) + { + if (0 == stream_in_byte(instreamctx, &datahi)) + { + fprintf(stderr, "Error reading input stream\n"); + return 1; + } + samplebuffer[i] = (datahi << 8); + } + } + } + + state->samplecountremaining -= maxsamplecount; + state->samplecountavailable += maxsamplecount; + } + + return 1; +} + +void cwav_pcm_destroy(cwav_pcmstate* state) +{ + free(state->channelstate); + free(state->samplebuffer); + + state->channelstate = 0; + state->samplebuffer = 0; +} + +const char* cwav_encoding_string(u8 encoding) +{ + switch(encoding) + { + case CWAV_ENCODING_DSPADPCM: return "DSP-ADPCM"; + case CWAV_ENCODING_IMAADPCM: return "IMA-ADPCM"; + case CWAV_ENCODING_PCM8: return "PCM8"; + case CWAV_ENCODING_PCM16: return "PCM16"; + default: return "UNKNOWN"; + } +} + +void cwav_print(cwav_context* ctx) +{ + cwav_header* header = &ctx->header; + cwav_infoheader* infoheader = &ctx->infoheader; + u32 i; + u32 infoheaderoffset = ctx->offset + getle32(ctx->header.infoblockref.offset); + u32 channelcount = getle32(infoheader->channelcount); + + fprintf(stdout, "Header: %c%c%c%c\n", header->magic[0], header->magic[1], header->magic[2], header->magic[3]); + fprintf(stdout, "Byte order mark: 0x%04X\n", getle16(header->byteordermark)); + fprintf(stdout, "Header size: 0x%04X\n", getle16(header->headersize)); + fprintf(stdout, "Version: 0x%08X\n", getle32(header->version)); + fprintf(stdout, "Total size: 0x%08X\n", getle32(header->totalsize)); + fprintf(stdout, "Data blocks: 0x%04X\n", getle16(header->datablocks)); + fprintf(stdout, "Info block idtype: 0x%04X\n", getle16(header->infoblockref.idtype)); + fprintf(stdout, "Info block offset: 0x%08X\n", getle32(header->infoblockref.offset)); + fprintf(stdout, "Info block size: 0x%08X\n", getle32(header->infoblockref.size)); + fprintf(stdout, "Data block idtype: 0x%04X\n", getle16(header->datablockref.idtype)); + fprintf(stdout, "Data block offset: 0x%08X\n", getle32(header->datablockref.offset)); + fprintf(stdout, "Data block size: 0x%08X\n", getle32(header->datablockref.size)); + fprintf(stdout, "\n"); + fprintf(stdout, "Header: %c%c%c%c\n", infoheader->magic[0], infoheader->magic[1], infoheader->magic[2], infoheader->magic[3]); + fprintf(stdout, "Size: 0x%08X\n", getle32(infoheader->size)); + fprintf(stdout, "Encoding: 0x%02X (%s)\n", infoheader->encoding, cwav_encoding_string(infoheader->encoding)); + fprintf(stdout, "Looped: 0x%02X\n", infoheader->looped); + fprintf(stdout, "Samplerate: %d\n", getle32(infoheader->samplerate)); + fprintf(stdout, "Loop start: 0x%08X\n", getle32(infoheader->loopstart)); + fprintf(stdout, "Loop end: 0x%08X\n", getle32(infoheader->loopend)); + fprintf(stdout, "Channels: %d\n", channelcount); + if (ctx->channel != 0) + { + for(i=0; ichannel[i].inforef.offset); + u32 codecoffset = channeloffset + getle32(ctx->channel[i].info.codecref.offset); + u32 sampleoffset = ctx->offset + getle32(ctx->channel[i].info.sampleref.offset) + getle32(ctx->header.datablockref.offset) + 8; + + fprintf(stdout, "Channel %d:\n", i); + fprintf(stdout, " > Channel ref idtype: 0x%04X\n", getle16(ctx->channel[i].inforef.idtype)); + fprintf(stdout, " > Channel ref offset: 0x%08X\n", channeloffset); + fprintf(stdout, " > Sample ref idtype: 0x%04X\n", getle16(ctx->channel[i].info.sampleref.idtype)); + fprintf(stdout, " > Sample ref offset: 0x%08X\n", sampleoffset); + fprintf(stdout, " > Codec ref idtype: 0x%04X\n", getle16(ctx->channel[i].info.codecref.idtype)); + fprintf(stdout, " > Codec ref offset: 0x%08X\n", codecoffset); + + +#ifdef CWAV_CODEC_PRINT + if (ctx->infoheader.encoding == CWAV_ENCODING_DSPADPCM && getle16(ctx->channel[i].info.codecref.idtype) == 0x300) + { + u32 j; + + for(j=0; j<16; j++) + fprintf(stdout, " > Adpcm coef %02d: 0x%04X\n", j, getle16(ctx->channel[i].infodspadpcm.coef[j])); + fprintf(stdout, " > Adpcm scale: 0x%04X\n", getle16(ctx->channel[i].infodspadpcm.scale)); + fprintf(stdout, " > Adpcm yn1: 0x%04X\n", getle16(ctx->channel[i].infodspadpcm.yn1)); + fprintf(stdout, " > Adpcm yn2: 0x%04X\n", getle16(ctx->channel[i].infodspadpcm.yn2)); + fprintf(stdout, " > Adpcm loop scale: 0x%04X\n", getle16(ctx->channel[i].infodspadpcm.loopscale)); + fprintf(stdout, " > Adpcm loop yn1: 0x%04X\n", getle16(ctx->channel[i].infodspadpcm.loopyn1)); + fprintf(stdout, " > Adpcm loop yn2: 0x%04X\n", getle16(ctx->channel[i].infodspadpcm.loopyn2)); + } + + if (ctx->infoheader.encoding == CWAV_ENCODING_IMAADPCM && getle16(ctx->channel[i].info.codecref.idtype) == 0x301) + { + fprintf(stdout, " > Adpcm data: 0x%04X\n", getle16(ctx->channel[i].infoimaadpcm.data)); + fprintf(stdout, " > Adpcm tblindex: 0x%02X\n", ctx->channel[i].infoimaadpcm.tableindex); + fprintf(stdout, " > Adpcm loopdata: 0x%04X\n", getle16(ctx->channel[i].infoimaadpcm.loopdata)); + fprintf(stdout, " > Adpcm looptblindex: 0x%02X\n", ctx->channel[i].infoimaadpcm.looptableindex); + } +#endif + } + } +} diff --git a/ctrtool/cwav.h b/ctrtool/cwav.h new file mode 100644 index 0000000..bd854fa --- /dev/null +++ b/ctrtool/cwav.h @@ -0,0 +1,205 @@ +#ifndef _CWAV_H_ +#define _CWAV_H_ + +#include +#include "types.h" +#include "settings.h" +#include "stream.h" + +#define CWAV_ENCODING_PCM8 0 +#define CWAV_ENCODING_PCM16 1 +#define CWAV_ENCODING_DSPADPCM 2 +#define CWAV_ENCODING_IMAADPCM 3 + +typedef struct +{ + u8 idtype[2]; + u8 padding[2]; + u8 offset[4]; +} cwav_reference; + +typedef struct +{ + u8 idtype[2]; + u8 padding[2]; + u8 offset[4]; + u8 size[4]; +} cwav_sizedreference; + + +typedef struct +{ + u8 magic[4]; + u8 byteordermark[2]; + u8 headersize[2]; + u8 version[4]; + u8 totalsize[4]; + u8 datablocks[2]; + u8 reserved[2]; + cwav_sizedreference infoblockref; + cwav_sizedreference datablockref; +} cwav_header; + +typedef struct +{ + u8 magic[4]; + u8 size[4]; + u8 encoding; + u8 looped; + u8 padding[2]; + u8 samplerate[4]; + u8 loopstart[4]; + u8 loopend[4]; + u8 reserved[4]; + u8 channelcount[4]; +} cwav_infoheader; + +typedef struct +{ + cwav_reference sampleref; + cwav_reference codecref; + u8 reserved[4]; +} cwav_channelinfo; + +typedef struct +{ + u8 coef[16][2]; + u8 scale[2]; + u8 yn1[2]; + u8 yn2[2]; + u8 loopscale[2]; + u8 loopyn1[2]; + u8 loopyn2[2]; +} cwav_dspadpcminfo; + +typedef struct +{ + u8 data[2]; + u8 tableindex; + u8 padding; + u8 loopdata[2]; + u8 looptableindex; + u8 looppadding; +} cwav_imaadpcminfo; + + +typedef struct +{ + s16 yn1; + s16 yn2; + u32 sampleoffset; + s16* samplebuffer; + stream_in_context instreamctx; +} cwav_dspadpcmchannelstate; + +typedef struct +{ + cwav_dspadpcmchannelstate* channelstate; + s16* samplebuffer; + u32 samplecountavailable; + u32 samplecountcapacity; + u32 samplecountremaining; +} cwav_dspadpcmstate; + +typedef struct +{ + s16 data; + u8 tableindex; + u32 sampleoffset; + s16* samplebuffer; + stream_in_context instreamctx; +} cwav_imaadpcmchannelstate; + +typedef struct +{ + cwav_imaadpcmchannelstate* channelstate; + s16* samplebuffer; + u32 samplecountavailable; + u32 samplecountcapacity; + u32 samplecountremaining; +} cwav_imaadpcmstate; + +typedef struct +{ + u32 sampleoffset; + s16* samplebuffer; + stream_in_context instreamctx; +} cwav_pcmchannelstate; + +typedef struct +{ + cwav_pcmchannelstate* channelstate; + s16* samplebuffer; + u32 samplecountavailable; + u32 samplecountcapacity; + u32 samplecountremaining; +} cwav_pcmstate; + + +typedef struct +{ + cwav_reference inforef; + cwav_channelinfo info; + cwav_dspadpcminfo infodspadpcm; + cwav_imaadpcminfo infoimaadpcm; +} cwav_channel; + +typedef struct +{ + u8 chunkid[4]; + u8 chunksize[4]; + u8 format[4]; + u8 subchunk1id[4]; + u8 subchunk1size[4]; + u8 audioformat[2]; + u8 numchannels[2]; + u8 samplerate[4]; + u8 byterate[4]; + u8 blockalign[2]; + u8 bitspersample[2]; + u8 subchunk2id[4]; + u8 subchunk2size[4]; +} wav_pcm_header; + +typedef struct +{ + FILE* file; + settings* usersettings; + u32 offset; + u32 size; + u32 channelcount; + cwav_header header; + cwav_infoheader infoheader; + cwav_channel* channel; +} cwav_context; + +void cwav_init(cwav_context* ctx); +void cwav_set_file(cwav_context* ctx, FILE* file); +void cwav_set_offset(cwav_context* ctx, u32 offset); +void cwav_set_size(cwav_context* ctx, u32 size); +void cwav_set_usersettings(cwav_context* ctx, settings* usersettings); +void cwav_process(cwav_context* ctx, u32 actions); +void cwav_dspadpcm_init(cwav_dspadpcmstate* state); +int cwav_dspadpcm_allocate(cwav_dspadpcmstate* state, cwav_context* ctx); +int cwav_dspadpcm_setup(cwav_dspadpcmstate* state, cwav_context* ctx, int isloop); +int cwav_dspadpcm_decode(cwav_dspadpcmstate* state, cwav_context* ctx); +int cwav_dspadpcm_decode_to_wav(cwav_context* ctx, stream_out_context* outstreamctx); +void cwav_dspadpcm_destroy(cwav_dspadpcmstate* state); +void cwav_imaadpcm_init(cwav_imaadpcmstate* state); +int cwav_imaadpcm_allocate(cwav_imaadpcmstate* state, cwav_context* ctx); +int cwav_imaadpcm_setup(cwav_imaadpcmstate* state, cwav_context* ctx, int isloop); +int cwav_imaadpcm_decode(cwav_imaadpcmstate* state, cwav_context* ctx); +int cwav_imaadpcm_decode_to_wav(cwav_context* ctx, stream_out_context* outstreamctx); +u8 cwav_imaadpcm_clamp_tableindex(u8 tableindex, int inc); +void cwav_imaadpcm_destroy(cwav_imaadpcmstate* state); +void cwav_pcm_init(cwav_pcmstate* state); +int cwav_pcm_allocate(cwav_pcmstate* state, cwav_context* ctx); +int cwav_pcm_setup(cwav_pcmstate* state, cwav_context* ctx, int isloop); +int cwav_pcm_decode(cwav_pcmstate* state, cwav_context* ctx); +int cwav_pcm_decode_to_wav(cwav_context* ctx, stream_out_context* outstreamctx); +void cwav_pcm_destroy(cwav_pcmstate* state); +void cwav_write_wav_header(cwav_context* ctx, stream_out_context* outstreamctx, u32 size); +int cwav_save_to_wav(cwav_context* ctx, const char* filepath); +void cwav_print(cwav_context* ctx); + +#endif // _CWAV_H_ diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c new file mode 100644 index 0000000..19cb684 --- /dev/null +++ b/ctrtool/exefs.c @@ -0,0 +1,337 @@ +#include +#include +#include + +#include "types.h" +#include "exefs.h" +#include "utils.h" +#include "ncch.h" +#include "lzss.h" + +void exefs_init(exefs_context* ctx) +{ + memset(ctx, 0, sizeof(exefs_context)); +} + +void exefs_set_file(exefs_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void exefs_set_offset(exefs_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void exefs_set_size(exefs_context* ctx, u32 size) +{ + ctx->size = size; +} + +void exefs_set_usersettings(exefs_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void exefs_set_partitionid(exefs_context* ctx, u8 partitionid[8]) +{ + memcpy(ctx->partitionid, partitionid, 8); +} + +void exefs_set_compressedflag(exefs_context* ctx, int compressedflag) +{ + ctx->compressedflag = compressedflag; +} + +void exefs_set_encrypted(exefs_context* ctx, u32 encrypted) +{ + ctx->encrypted = encrypted; +} + +void exefs_set_key(exefs_context* ctx, u8 key[16]) +{ + memcpy(ctx->key, key, 16); +} + +void exefs_set_counter(exefs_context* ctx, u8 counter[16]) +{ + memcpy(ctx->counter, counter, 16); +} + +void exefs_determine_key(exefs_context* ctx, u32 actions) +{ + u8* key = settings_get_ncch_key(ctx->usersettings); + + if (actions & PlainFlag) + ctx->encrypted = 0; + else + { + if (key) + { + ctx->encrypted = 1; + memcpy(ctx->key, key, 0x10); + } + } +} + +void exefs_save(exefs_context* ctx, u32 index, u32 flags) +{ + exefs_sectionheader* section = (exefs_sectionheader*)(ctx->header.section + index); + char outfname[MAX_PATH]; + char name[64]; + u32 offset; + u32 size; + FILE* fout; + u32 compressedsize = 0; + u32 decompressedsize = 0; + u8* compressedbuffer = 0; + u8* decompressedbuffer = 0; + filepath* dirpath = 0; + + + offset = getle32(section->offset) + sizeof(exefs_header); + size = getle32(section->size); + dirpath = settings_get_exefs_dir_path(ctx->usersettings); + + if (size == 0 || dirpath == 0 || dirpath->valid == 0) + return; + + if (size >= ctx->size) + { + fprintf(stderr, "Error, ExeFS section %d size invalid\n", index); + return; + } + + memset(name, 0, sizeof(name)); + memcpy(name, section->name, 8); + + + memcpy(outfname, dirpath->pathname, MAX_PATH); + strcat(outfname, "/"); + + if (name[0] == '.') + strcat(outfname, name+1); + else + strcat(outfname, name); + strcat(outfname, ".bin"); + + fout = fopen(outfname, "wb"); + + if (fout == 0) + { + fprintf(stderr, "Error, failed to create file %s\n", outfname); + goto clean; + } + + + + fseek(ctx->file, ctx->offset + offset, SEEK_SET); + ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_add_counter(&ctx->aes, offset / 0x10); + + if (index == 0 && ctx->compressedflag && ((flags & RawFlag) == 0)) + { + fprintf(stdout, "Decompressing section %s to %s...\n", name, outfname); + + compressedsize = size; + compressedbuffer = malloc(compressedsize); + + if (compressedbuffer == 0) + { + fprintf(stdout, "Error allocating memory\n"); + goto clean; + } + if (compressedsize != fread(compressedbuffer, 1, compressedsize, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, compressedbuffer, compressedbuffer, compressedsize); + + + decompressedsize = lzss_get_decompressed_size(compressedbuffer, compressedsize); + decompressedbuffer = malloc(decompressedsize); + if (decompressedbuffer == 0) + { + fprintf(stdout, "Error allocating memory\n"); + goto clean; + } + + if (0 == lzss_decompress(compressedbuffer, compressedsize, decompressedbuffer, decompressedsize)) + goto clean; + + if (decompressedsize != fwrite(decompressedbuffer, 1, decompressedsize, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + } + else + { + u8 buffer[16 * 1024]; + + fprintf(stdout, "Saving section %s to %s...\n", name, outfname); + + while(size) + { + u32 max = sizeof(buffer); + if (max > size) + max = size; + + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, buffer, buffer, max); + + if (max != fwrite(buffer, 1, max, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + + size -= max; + } + } + +clean: + free(compressedbuffer); + free(decompressedbuffer); + return; +} + +void exefs_read_header(exefs_context* ctx, u32 flags) +{ + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, sizeof(exefs_header), ctx->file); + + ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, (u8*)&ctx->header, (u8*)&ctx->header, sizeof(exefs_header)); +} + +void exefs_calculate_hash(exefs_context* ctx, u8 hash[32]) +{ + ctr_sha_256((const u8*)&ctx->header, sizeof(exefs_header), hash); +} + +void exefs_process(exefs_context* ctx, u32 actions) +{ + u32 i; + + exefs_determine_key(ctx, actions); + + exefs_read_header(ctx, actions); + + if (actions & VerifyFlag) + { + for(i=0; i<8; i++) + ctx->hashcheck[i] = exefs_verify(ctx, i, actions)? Good : Fail; + } + + if (actions & InfoFlag) + { + exefs_print(ctx); + } + + if (actions & ExtractFlag) + { + filepath* dirpath = settings_get_exefs_dir_path(ctx->usersettings); + + if (dirpath && dirpath->valid) + { + makedir(dirpath->pathname); + for(i=0; i<8; i++) + exefs_save(ctx, i, actions); + } + } +} + +int exefs_verify(exefs_context* ctx, u32 index, u32 flags) +{ + exefs_sectionheader* section = (exefs_sectionheader*)(ctx->header.section + index); + u32 offset; + u32 size; + u8 buffer[16 * 1024]; + u8 hash[0x20]; + + + offset = getle32(section->offset) + sizeof(exefs_header); + size = getle32(section->size); + + if (size == 0) + return 0; + + fseek(ctx->file, ctx->offset + offset, SEEK_SET); + ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_add_counter(&ctx->aes, offset / 0x10); + + ctr_sha_256_init(&ctx->sha); + + while(size) + { + u32 max = sizeof(buffer); + if (max > size) + max = size; + + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, buffer, buffer, max); + + ctr_sha_256_update(&ctx->sha, buffer, max); + + size -= max; + } + + ctr_sha_256_finish(&ctx->sha, hash); + + if (memcmp(hash, ctx->header.hashes[7-index], 0x20) == 0) + return 1; +clean: + return 0; +} + +void exefs_print(exefs_context* ctx) +{ + u32 i; + char sectname[9]; + u32 sectoffset; + u32 sectsize; + + fprintf(stdout, "\nExeFS:\n"); + for(i=0; i<8; i++) + { + exefs_sectionheader* section = (exefs_sectionheader*)(ctx->header.section + i); + + + memset(sectname, 0, sizeof(sectname)); + memcpy(sectname, section->name, 8); + + sectoffset = getle32(section->offset); + sectsize = getle32(section->size); + + if (sectsize) + { + fprintf(stdout, "Section name: %s\n", sectname); + fprintf(stdout, "Section offset: 0x%08x\n", sectoffset + 0x200); + fprintf(stdout, "Section size: 0x%08x\n", sectsize); + if (ctx->hashcheck[i] == Good) + memdump(stdout, "Section hash (GOOD): ", ctx->header.hashes[7-i], 0x20); + else if (ctx->hashcheck[i] == Fail) + memdump(stdout, "Section hash (FAIL): ", ctx->header.hashes[7-i], 0x20); + else + memdump(stdout, "Section hash: ", ctx->header.hashes[7-i], 0x20); + } + } +} diff --git a/ctrtool/exefs.h b/ctrtool/exefs.h new file mode 100644 index 0000000..a062478 --- /dev/null +++ b/ctrtool/exefs.h @@ -0,0 +1,60 @@ +#ifndef _EXEFS_H_ +#define _EXEFS_H_ + +#include "types.h" +#include "info.h" +#include "ctr.h" +#include "filepath.h" +#include "settings.h" + + +typedef struct +{ + u8 name[8]; + u8 offset[4]; + u8 size[4]; +} exefs_sectionheader; + + +typedef struct +{ + exefs_sectionheader section[8]; + u8 reserved[0x80]; + u8 hashes[8][0x20]; +} exefs_header; + +typedef struct +{ + FILE* file; + settings* usersettings; + u8 partitionid[8]; + u8 counter[16]; + u8 key[16]; + u32 offset; + u32 size; + exefs_header header; + ctr_aes_context aes; + ctr_sha256_context sha; + int hashcheck[8]; + int compressedflag; + int encrypted; +} exefs_context; + +void exefs_init(exefs_context* ctx); +void exefs_set_file(exefs_context* ctx, FILE* file); +void exefs_set_offset(exefs_context* ctx, u32 offset); +void exefs_set_size(exefs_context* ctx, u32 size); +void exefs_set_usersettings(exefs_context* ctx, settings* usersettings); +void exefs_set_partitionid(exefs_context* ctx, u8 partitionid[8]); +void exefs_set_counter(exefs_context* ctx, u8 counter[16]); +void exefs_set_compressedflag(exefs_context* ctx, int compressedflag); +void exefs_set_key(exefs_context* ctx, u8 key[16]); +void exefs_set_encrypted(exefs_context* ctx, u32 encrypted); +void exefs_read_header(exefs_context* ctx, u32 flags); +void exefs_calculate_hash(exefs_context* ctx, u8 hash[32]); +void exefs_process(exefs_context* ctx, u32 actions); +void exefs_print(exefs_context* ctx); +void exefs_save(exefs_context* ctx, u32 index, u32 flags); +int exefs_verify(exefs_context* ctx, u32 index, u32 flags); +void exefs_determine_key(exefs_context* ctx, u32 actions); +#endif // _EXEFS_H_ diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c new file mode 100644 index 0000000..c3d1d55 --- /dev/null +++ b/ctrtool/exheader.c @@ -0,0 +1,423 @@ +#include +#include + +#include "types.h" +#include "exheader.h" +#include "utils.h" +#include "ncch.h" + +void exheader_init(exheader_context* ctx) +{ + memset(ctx, 0, sizeof(exheader_context)); +} + +void exheader_set_file(exheader_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void exheader_set_offset(exheader_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void exheader_set_size(exheader_context* ctx, u32 size) +{ + ctx->size = size; +} + +void exheader_set_usersettings(exheader_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void exheader_set_partitionid(exheader_context* ctx, u8 partitionid[8]) +{ + memcpy(ctx->partitionid, partitionid, 8); +} + +void exheader_set_programid(exheader_context* ctx, u8 programid[8]) +{ + memcpy(ctx->programid, programid, 8); +} + +void exheader_set_counter(exheader_context* ctx, u8 counter[16]) +{ + memcpy(ctx->counter, counter, 16); +} + +int exheader_get_compressedflag(exheader_context* ctx) +{ + return ctx->compressedflag; +} + +void exheader_set_encrypted(exheader_context* ctx, u32 encrypted) +{ + ctx->encrypted = encrypted; +} + +void exheader_set_key(exheader_context* ctx, u8 key[16]) +{ + memcpy(ctx->key, key, 16); +} + + +void exheader_determine_key(exheader_context* ctx, u32 actions) +{ + u8* key = settings_get_ncch_key(ctx->usersettings); + + if (actions & PlainFlag) + ctx->encrypted = 0; + else + { + if (key) + { + ctx->encrypted = 1; + memcpy(ctx->key, key, 0x10); + } + } +} + +void exheader_read(exheader_context* ctx, u32 actions) +{ + if (ctx->haveread == 0) + { + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, sizeof(exheader_header), ctx->file); + + ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, (u8*)&ctx->header, (u8*)&ctx->header, sizeof(exheader_header)); + + ctx->haveread = 1; + } +} + +int exheader_programid_valid(exheader_context* ctx) +{ + if (!settings_get_ignore_programid(ctx->usersettings)) + { + if (memcmp(ctx->header.arm11systemlocalcaps.programid, ctx->programid, 8)) + { + fprintf(stderr, "Error, program id mismatch. Wrong key?\n"); + return 0; + } + } + + return 1; +} + +int exheader_process(exheader_context* ctx, u32 actions) +{ + exheader_determine_key(ctx, actions); + + exheader_read(ctx, actions); + + if (ctx->header.codesetinfo.flags.flag & 1) + ctx->compressedflag = 1; + + if (actions & VerifyFlag) + exheader_verify(ctx); + + if (actions & InfoFlag) + { + exheader_print(ctx); + } + + return 1; +} + +void exheader_print_arm9accesscontrol(exheader_context* ctx) +{ + unsigned int i; + unsigned int flags[15*8]; + + fprintf(stdout, "ARM9 Desc. version: 0x%X\n", ctx->header.arm9accesscontrol.descversion); + + for(i=0; i<15*8; i++) + { + if (ctx->header.arm9accesscontrol.descriptors[i/8] & (1<<(i&7))) + flags[i] = 1; + else + flags[i] = 0; + } + + fprintf(stdout, "Mount NAND fs: %s\n", flags[0]? "YES" : "NO"); + fprintf(stdout, "Mount NAND RO write fs: %s\n", flags[1]? "YES" : "NO"); + fprintf(stdout, "Mount NAND TWL fs: %s\n", flags[2]? "YES" : "NO"); + fprintf(stdout, "Mount NAND W fs: %s\n", flags[3]? "YES" : "NO"); + fprintf(stdout, "Mount CARD SPI fs: %s\n", flags[4]? "YES" : "NO"); + fprintf(stdout, "Use SDIF3: %s\n", flags[5]? "YES" : "NO"); + fprintf(stdout, "Create seed: %s\n", flags[6]? "YES" : "NO"); + fprintf(stdout, "Use CARD SPI: %s\n", flags[7]? "YES" : "NO"); + fprintf(stdout, "SD Application: %s\n", flags[8]? "YES" : "NO"); + fprintf(stdout, "Use Direct SDMC: %s\n", flags[9]? "YES" : "NO"); + + for(i=10; i<15*8; i++) + { + if (flags[i]) + fprintf(stdout, "Unknown flag: %d\n", i); + } +} + +void exheader_print_arm11kernelcapabilities(exheader_context* ctx) +{ + unsigned int i, j; + unsigned int systemcallmask[8]; + unsigned int unknowndescriptor[28]; + unsigned int svccount = 0; + unsigned int svcmask = 0; + unsigned int interrupt[0x80]; + unsigned int interruptcount = 0; + + memset(systemcallmask, 0, sizeof(systemcallmask)); + memset(interrupt, 0, sizeof(interrupt)); + + for(i=0; i<28; i++) + { + unsigned int descriptor = getle32(ctx->header.arm11kernelcaps.descriptors[i]); + + unknowndescriptor[i] = 0; + + if ((descriptor & (0x1f<<27)) == (0x1e<<27)) + systemcallmask[(descriptor>>24) & 7] = descriptor & 0x00FFFFFF; + else if ((descriptor & (0x7f<<25)) == (0x7e<<25)) + fprintf(stdout, "Kernel release version: %d.%d\n", (descriptor>>8)&0xFF, (descriptor>>0)&0xFF); + else if ((descriptor & (0xf<<28)) == (0xe<<28)) + { + for(j=0; j<4; j++) + interrupt[(descriptor >> (j*7)) & 0x7F] = 1; + } + else if ((descriptor & (0xff<<24)) == (0xfe<<24)) + fprintf(stdout, "Handle table size: 0x%X\n", descriptor & 0x3FF); + else if ((descriptor & (0xfff<<20)) == (0xffe<<20)) + fprintf(stdout, "Mapping IO address: 0x%X (%s)\n", (descriptor & 0xFFFFF)<<12, (descriptor&(1<<20))?"RO":"RW"); + else if ((descriptor & (0x7ff<<21)) == (0x7fc<<21)) + fprintf(stdout, "Mapping static address: 0x%X (%s)\n", (descriptor & 0x1FFFFF)<<12, (descriptor&(1<<20))?"RO":"RW"); + else if ((descriptor & (0x1ff<<23)) == (0x1fe<<23)) + { + unsigned int memorytype = (descriptor>>8)&15; + fprintf(stdout, "Kernel flags: \n"); + fprintf(stdout, " > Allow debug: %s\n", (descriptor&(1<<0))?"YES":"NO"); + fprintf(stdout, " > Force debug: %s\n", (descriptor&(1<<1))?"YES":"NO"); + fprintf(stdout, " > Allow non-alphanum: %s\n", (descriptor&(1<<2))?"YES":"NO"); + fprintf(stdout, " > Shared page writing: %s\n", (descriptor&(1<<3))?"YES":"NO"); + fprintf(stdout, " > Privilege priority: %s\n", (descriptor&(1<<4))?"YES":"NO"); + fprintf(stdout, " > Allow main() args: %s\n", (descriptor&(1<<5))?"YES":"NO"); + fprintf(stdout, " > Shared device mem: %s\n", (descriptor&(1<<6))?"YES":"NO"); + fprintf(stdout, " > Runnable on sleep: %s\n", (descriptor&(1<<7))?"YES":"NO"); + fprintf(stdout, " > Special memory: %s\n", (descriptor&(1<<12))?"YES":"NO"); + + + switch(memorytype) + { + case 1: fprintf(stdout, " > Memory type: APPLICATION\n"); break; + case 2: fprintf(stdout, " > Memory type: SYSTEM\n"); break; + case 3: fprintf(stdout, " > Memory type: BASE\n"); break; + default: fprintf(stdout, " > Memory type: Unknown (%d)\n", memorytype); break; + } + } + else if (descriptor != 0xFFFFFFFF) + unknowndescriptor[i] = 1; + } + + fprintf(stdout, "Allowed systemcalls: "); + for(i=0; i<8; i++) + { + for(j=0; j<24; j++) + { + svcmask = systemcallmask[i]; + + if (svcmask & (1<header.arm11kernelcaps.descriptors[i]); + + if (unknowndescriptor[i]) + fprintf(stdout, "Unknown descriptor: %08X\n", descriptor); + } +} + +int exheader_signature_verify(exheader_context* ctx, rsakey2048* key) +{ + u8 hash[0x20]; + + ctr_sha_256(ctx->header.accessdesc.ncchpubkeymodulus, 0x300, hash); + return ctr_rsa_verify_hash(ctx->header.accessdesc.signature, hash, key); +} + + +void exheader_verify(exheader_context* ctx) +{ + unsigned int i; + + + ctx->validprogramid = Good; + ctx->validpriority = Good; + ctx->validaffinitymask = Good; + + for(i=0; i<8; i++) + { + if (ctx->header.accessdesc.arm11systemlocalcaps.programid[i] == 0xFF) + continue; + if (ctx->header.accessdesc.arm11systemlocalcaps.programid[i] == ctx->header.arm11systemlocalcaps.programid[i]) + continue; + ctx->validprogramid = Fail; + break; + } + + if (ctx->header.accessdesc.arm11systemlocalcaps.flags[7] > ctx->header.arm11systemlocalcaps.flags[7]) + ctx->validpriority = Fail; + if (ctx->header.arm11systemlocalcaps.flags[5] & ~ctx->header.accessdesc.arm11systemlocalcaps.flags[5]) + ctx->validaffinitymask = Fail; + + if (ctx->usersettings) + ctx->validsignature = exheader_signature_verify(ctx, &ctx->usersettings->keys.ncchdescrsakey); +} + +const char* exheader_getvalidstring(int valid) +{ + if (valid == 0) + return ""; + else if (valid == 1) + return "(GOOD)"; + else + return "(FAIL)"; +} + +void exheader_print(exheader_context* ctx) +{ + u32 i; + char name[9]; + char service[9]; + exheader_codesetinfo* codesetinfo = &ctx->header.codesetinfo; + + memset(name, 0, sizeof(name)); + memcpy(name, codesetinfo->name, 8); + + + fprintf(stdout, "\nExtended header:\n"); + if (ctx->validsignature == Unchecked) + memdump(stdout, "Signature: ", ctx->header.accessdesc.signature, 0x100); + else if (ctx->validsignature == Good) + memdump(stdout, "Signature (GOOD): ", ctx->header.accessdesc.signature, 0x100); + else if (ctx->validsignature == Fail) + memdump(stdout, "Signature (FAIL): ", ctx->header.accessdesc.signature, 0x100); + fprintf(stdout, "Name: %s\n", name); + fprintf(stdout, "Flag: %02X ", codesetinfo->flags.flag); + if (codesetinfo->flags.flag & 1) + fprintf(stdout, "[compressed]"); + fprintf(stdout, "\n"); + fprintf(stdout, "Remaster version: %04X\n", getle16(codesetinfo->flags.remasterversion)); + + fprintf(stdout, "Code text address: 0x%08X\n", getle32(codesetinfo->text.address)); + fprintf(stdout, "Code text size: 0x%08X\n", getle32(codesetinfo->text.codesize)); + fprintf(stdout, "Code text max pages: 0x%08X (0x%08X)\n", getle32(codesetinfo->text.nummaxpages), getle32(codesetinfo->text.nummaxpages)*0x1000); + fprintf(stdout, "Code ro address: 0x%08X\n", getle32(codesetinfo->ro.address)); + fprintf(stdout, "Code ro size: 0x%08X\n", getle32(codesetinfo->ro.codesize)); + fprintf(stdout, "Code ro max pages: 0x%08X (0x%08X)\n", getle32(codesetinfo->ro.nummaxpages), getle32(codesetinfo->ro.nummaxpages)*0x1000); + fprintf(stdout, "Code data address: 0x%08X\n", getle32(codesetinfo->data.address)); + fprintf(stdout, "Code data size: 0x%08X\n", getle32(codesetinfo->data.codesize)); + fprintf(stdout, "Code data max pages: 0x%08X (0x%08X)\n", getle32(codesetinfo->data.nummaxpages), getle32(codesetinfo->data.nummaxpages)*0x1000); + fprintf(stdout, "Code bss size: 0x%08X\n", getle32(codesetinfo->bsssize)); + fprintf(stdout, "Code stack size: 0x%08X\n", getle32(codesetinfo->stacksize)); + + for(i=0; i<0x30; i++) + { + if (getle64(ctx->header.deplist.programid[i]) != 0x0000000000000000UL) + fprintf(stdout, "Dependency: %016llX\n", getle64(ctx->header.deplist.programid[i])); + } + + fprintf(stdout, "Savedata size: 0x%08X\n", getle32(ctx->header.systeminfo.savedatasize)); + fprintf(stdout, "Jump id: %016llX\n", getle64(ctx->header.systeminfo.jumpid)); + + fprintf(stdout, "Program id: %016llX %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); + memdump(stdout, "Flags: ", ctx->header.arm11systemlocalcaps.flags, 8); + fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.flags)); + fprintf(stdout, "System mode: 0x%X\n", (ctx->header.arm11systemlocalcaps.flags[6]>>4)&0xF); + fprintf(stdout, "Ideal processor: %d\n", (ctx->header.arm11systemlocalcaps.flags[6]>>0)&0x3); + fprintf(stdout, "Affinity mask: %d %s\n", (ctx->header.arm11systemlocalcaps.flags[6]>>2)&0x3, exheader_getvalidstring(ctx->validaffinitymask)); + fprintf(stdout, "Main thread priority: %d %s\n", ctx->header.arm11systemlocalcaps.flags[7], exheader_getvalidstring(ctx->validpriority)); + // print resource limit descriptor too? currently mostly zeroes... + fprintf(stdout, "Ext savedata id: %016llX\n", getle64(ctx->header.arm11systemlocalcaps.storageinfo.extsavedataid)); + fprintf(stdout, "System savedata id: %016llX\n", getle64(ctx->header.arm11systemlocalcaps.storageinfo.systemsavedataid)); + memdump(stdout, "Access info: ", ctx->header.arm11systemlocalcaps.storageinfo.accessinfo, 7); + fprintf(stdout, "Other attributes: %02X\n", ctx->header.arm11systemlocalcaps.storageinfo.otherattributes); + + exheader_print_arm11kernelcapabilities(ctx); + exheader_print_arm9accesscontrol(ctx); + + + + for(i=0; i<0x20; i++) + { + if (getle64(ctx->header.arm11systemlocalcaps.serviceaccesscontrol[i]) != 0x0000000000000000UL) + { + memset(service, 0, sizeof(service)); + memcpy(service, ctx->header.arm11systemlocalcaps.serviceaccesscontrol[i], 8); + fprintf(stdout, "Service access: %s\n", service); + } + } + fprintf(stdout, "Reslimit category: %02X\n", ctx->header.arm11systemlocalcaps.resourcelimitcategory); +} diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h new file mode 100644 index 0000000..2673918 --- /dev/null +++ b/ctrtool/exheader.h @@ -0,0 +1,143 @@ +#ifndef _EXHEADER_H_ +#define _EXHEADER_H_ + +#include +#include "types.h" +#include "ctr.h" +#include "settings.h" + +typedef struct +{ + u8 reserved[5]; + u8 flag; + u8 remasterversion[2]; +} exheader_systeminfoflags; + +typedef struct +{ + u8 address[4]; + u8 nummaxpages[4]; + u8 codesize[4]; +} exheader_codesegmentinfo; + +typedef struct +{ + u8 name[8]; + exheader_systeminfoflags flags; + exheader_codesegmentinfo text; + u8 stacksize[4]; + exheader_codesegmentinfo ro; + u8 reserved[4]; + exheader_codesegmentinfo data; + u8 bsssize[4]; +} exheader_codesetinfo; + +typedef struct +{ + u8 programid[0x30][8]; +} exheader_dependencylist; + +typedef struct +{ + u8 savedatasize[4]; + u8 reserved[4]; + u8 jumpid[8]; + u8 reserved2[0x30]; +} exheader_systeminfo; + +typedef struct +{ + u8 extsavedataid[8]; + u8 systemsavedataid[8]; + u8 reserved[8]; + u8 accessinfo[7]; + u8 otherattributes; +} exheader_storageinfo; + +typedef struct +{ + u8 programid[8]; + u8 flags[8]; + u8 resourcelimitdescriptor[0x10][2]; + exheader_storageinfo storageinfo; + u8 serviceaccesscontrol[0x20][8]; + u8 reserved[0x1f]; + u8 resourcelimitcategory; +} exheader_arm11systemlocalcaps; + +typedef struct +{ + u8 descriptors[28][4]; + u8 reserved[0x10]; +} exheader_arm11kernelcapabilities; + +typedef struct +{ + u8 descriptors[15]; + u8 descversion; +} exheader_arm9accesscontrol; + +typedef struct +{ + // systemcontrol info { + // coreinfo { + exheader_codesetinfo codesetinfo; + exheader_dependencylist deplist; + // } + exheader_systeminfo systeminfo; + // } + // accesscontrolinfo { + exheader_arm11systemlocalcaps arm11systemlocalcaps; + exheader_arm11kernelcapabilities arm11kernelcaps; + exheader_arm9accesscontrol arm9accesscontrol; + // } + struct { + u8 signature[0x100]; + u8 ncchpubkeymodulus[0x100]; + exheader_arm11systemlocalcaps arm11systemlocalcaps; + exheader_arm11kernelcapabilities arm11kernelcaps; + exheader_arm9accesscontrol arm9accesscontrol; + } accessdesc; +} exheader_header; + +typedef struct +{ + int haveread; + FILE* file; + settings* usersettings; + u8 partitionid[8]; + u8 programid[8]; + u8 counter[16]; + u8 key[16]; + u32 offset; + u32 size; + exheader_header header; + ctr_aes_context aes; + ctr_rsa_context rsa; + int compressedflag; + int encrypted; + int validprogramid; + int validpriority; + int validaffinitymask; + int validsignature; +} exheader_context; + +void exheader_init(exheader_context* ctx); +void exheader_set_file(exheader_context* ctx, FILE* file); +void exheader_set_offset(exheader_context* ctx, u32 offset); +void exheader_set_size(exheader_context* ctx, u32 size); +void exheader_set_partitionid(exheader_context* ctx, u8 partitionid[8]); +void exheader_set_counter(exheader_context* ctx, u8 counter[16]); +void exheader_set_programid(exheader_context* ctx, u8 programid[8]); +void exheader_set_encrypted(exheader_context* ctx, u32 encrypted); +void exheader_set_key(exheader_context* ctx, u8 key[16]); +void exheader_set_usersettings(exheader_context* ctx, settings* usersettings); +int exheader_get_compressedflag(exheader_context* ctx); +void exheader_read(exheader_context* ctx, u32 actions); +int exheader_process(exheader_context* ctx, u32 actions); +void exheader_print(exheader_context* ctx); +void exheader_verify(exheader_context* ctx); +int exheader_programid_valid(exheader_context* ctx); +void exheader_determine_key(exheader_context* ctx, u32 actions); + +#endif // _EXHEADER_H_ diff --git a/ctrtool/filepath.c b/ctrtool/filepath.c new file mode 100644 index 0000000..fe5ae16 --- /dev/null +++ b/ctrtool/filepath.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#include "types.h" +#include "filepath.h" + +void filepath_init(filepath* fpath) +{ + fpath->valid = 0; +} + +void filepath_copy(filepath* fpath, filepath* copy) +{ + if (copy != 0 && copy->valid) + memcpy(fpath, copy, sizeof(filepath)); + else + memset(fpath, 0, sizeof(filepath)); +} + +void filepath_append_utf16(filepath* fpath, const u8* name) +{ + u32 size; + + + if (fpath->valid == 0) + return; + + size = strlen(fpath->pathname); + + if (size > 0 && size < (MAX_PATH-1)) + { + if (fpath->pathname[size-1] != PATH_SEPERATOR) + fpath->pathname[size++] = PATH_SEPERATOR; + } + + while(size < (MAX_PATH-1)) + { + u8 lo = *name++; + u8 hi = *name++; + u16 code = (hi<<8) | lo; + + if (code == 0) + break; + + // convert non-ANSI to '#', because unicode support is too much work + if (code > 0x7F) + code = '#'; + + fpath->pathname[size++] = code; + } + + fpath->pathname[size] = 0; + + if (size >= (MAX_PATH-1)) + fpath->valid = 0; +} + +void filepath_append(filepath* fpath, const char* format, ...) +{ + char tmppath[MAX_PATH]; + va_list args; + + if (fpath->valid == 0) + return; + + memset(tmppath, 0, MAX_PATH); + + va_start(args, format); + vsprintf(tmppath, format, args); + va_end(args); + + strcat(fpath->pathname, "/"); + strcat(fpath->pathname, tmppath); +} + +void filepath_set(filepath* fpath, const char* path) +{ + fpath->valid = 1; + memset(fpath->pathname, 0, MAX_PATH); + strncpy(fpath->pathname, path, MAX_PATH); +} + +const char* filepath_get(filepath* fpath) +{ + if (fpath->valid == 0) + return 0; + else + return fpath->pathname; +} diff --git a/ctrtool/filepath.h b/ctrtool/filepath.h new file mode 100644 index 0000000..9f6d72c --- /dev/null +++ b/ctrtool/filepath.h @@ -0,0 +1,20 @@ +#ifndef _FILEPATH_H_ +#define _FILEPATH_H_ + +#include "types.h" +#include "utils.h" + +typedef struct +{ + char pathname[MAX_PATH]; + int valid; +} filepath; + +void filepath_init(filepath* fpath); +void filepath_copy(filepath* fpath, filepath* copy); +void filepath_append_utf16(filepath* fpath, const u8* name); +void filepath_append(filepath* fpath, const char* format, ...); +void filepath_set(filepath* fpath, const char* path); +const char* filepath_get(filepath* fpath); + +#endif // _FILEPATH_H_ diff --git a/ctrtool/firm.c b/ctrtool/firm.c new file mode 100644 index 0000000..6807a38 --- /dev/null +++ b/ctrtool/firm.c @@ -0,0 +1,254 @@ +#include +#include +#include + +#include "types.h" +#include "firm.h" +#include "utils.h" + +void firm_init(firm_context* ctx) +{ + memset(ctx, 0, sizeof(firm_context)); +} + +void firm_set_file(firm_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void firm_set_offset(firm_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void firm_set_size(firm_context* ctx, u32 size) +{ + ctx->size = size; +} + +void firm_set_usersettings(firm_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void firm_save(firm_context* ctx, u32 index, u32 flags) +{ + firm_sectionheader* section = (firm_sectionheader*)(ctx->header.section + index); + u32 offset; + u32 size; + u32 address; + FILE* fout; + filepath outpath; + u8 buffer[16 * 1024]; + + + offset = getle32(section->offset); + size = getle32(section->size); + address = getle32(section->address); + filepath_copy(&outpath, settings_get_firm_dir_path(ctx->usersettings)); + filepath_append(&outpath, "firm_%d_%08X.bin", index, address); + + if (size == 0 || outpath.valid == 0) + return; + + if (size >= ctx->size) + { + fprintf(stderr, "Error, firm section %d size invalid\n", index); + return; + } + + fout = fopen(outpath.pathname, "wb"); + if (fout == 0) + { + fprintf(stderr, "Error, failed to create file %s\n", outpath.pathname); + goto clean; + } + + + + fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fprintf(stdout, "Saving section %d to %s...\n", index, outpath.pathname); + + while(size) + { + u32 max = sizeof(buffer); + if (max > size) + max = size; + + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + if (max != fwrite(buffer, 1, max, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + + size -= max; + } + + +clean: + return; +} + + +void firm_process(firm_context* ctx, u32 actions) +{ + u32 i; + + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, sizeof(firm_header), ctx->file); + + if (getle32(ctx->header.magic) != MAGIC_FIRM) + { + fprintf(stdout, "Error, FIRM segment corrupted\n"); + return; + } + + + if (actions & VerifyFlag) + { + firm_verify(ctx, actions); + firm_signature_verify(ctx); + } + + if (actions & InfoFlag) + { + firm_print(ctx); + } + + if (actions & ExtractFlag) + { + filepath* dirpath = settings_get_firm_dir_path(ctx->usersettings); + + if (dirpath && dirpath->valid) + { + makedir(dirpath->pathname); + for(i=0; i<4; i++) + firm_save(ctx, i, actions); + } + } +} + +int firm_verify(firm_context* ctx, u32 flags) +{ + unsigned int i; + u32 offset; + u32 size; + u8 buffer[16 * 1024]; + u8 hash[0x20]; + + + for(i=0; i<4; i++) + { + firm_sectionheader* section = (firm_sectionheader*)(ctx->header.section + i); + + + offset = getle32(section->offset); + size = getle32(section->size); + + if (size == 0) + return 0; + + fseek(ctx->file, ctx->offset + offset, SEEK_SET); + + ctr_sha_256_init(&ctx->sha); + + while(size) + { + u32 max = sizeof(buffer); + if (max > size) + max = size; + + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + ctr_sha_256_update(&ctx->sha, buffer, max); + + size -= max; + } + + ctr_sha_256_finish(&ctx->sha, hash); + + + if (memcmp(hash, section->hash, 0x20) == 0) + ctx->hashcheck[i] = Good; + else + ctx->hashcheck[i] = Fail; + } + + +clean: + return 0; +} + + +void firm_signature_verify(firm_context* ctx) +{ + u8 hash[0x20]; + + if (ctx->usersettings) + { + ctr_sha_256(ctx->header.magic, 0x100, hash); + ctx->headersigcheck = ctr_rsa_verify_hash(ctx->header.signature, hash, &ctx->usersettings->keys.firmrsakey); + } +} + + +void firm_print(firm_context* ctx) +{ + u32 i; + u32 address; + u32 type; + u32 offset; + u32 size; + u32 entrypointarm11 = getle32(ctx->header.entrypointarm11); + u32 entrypointarm9 = getle32(ctx->header.entrypointarm9); + + fprintf(stdout, "\nFIRM:\n"); + if (ctx->headersigcheck == Unchecked) + memdump(stdout, "Signature: ", ctx->header.signature, 0x100); + else if (ctx->headersigcheck == Good) + memdump(stdout, "Signature (GOOD): ", ctx->header.signature, 0x100); + else + memdump(stdout, "Signature (FAIL): ", ctx->header.signature, 0x100); + + fprintf(stdout, "\n"); + fprintf(stdout, "Entrypoint ARM9: 0x%08X\n", entrypointarm9); + fprintf(stdout, "Entrypoint ARM11: 0x%08X\n", entrypointarm11); + fprintf(stdout, "\n"); + + + for(i=0; i<4; i++) + { + firm_sectionheader* section = (firm_sectionheader*)(ctx->header.section + i); + + + offset = getle32(section->offset); + size = getle32(section->size); + address = getle32(section->address); + type = getle32(section->type); + + if (size) + { + fprintf(stdout, "Section %d \n", i); + fprintf(stdout, " Type: %s\n", type==0? "ARM9" : type==1? "ARM11" : "UNKNOWN"); + fprintf(stdout, " Address: 0x%08X\n", address); + fprintf(stdout, " Offset: 0x%08X\n", offset); + fprintf(stdout, " Size: 0x%08X\n", size); + if (ctx->hashcheck[i] == Good) + memdump(stdout, " Hash (GOOD): ", section->hash, 0x20); + else if (ctx->hashcheck[i] == Fail) + memdump(stdout, " Hash (FAIL): ", section->hash, 0x20); + else + memdump(stdout, " Hash: ", section->hash, 0x20); + } + } +} diff --git a/ctrtool/firm.h b/ctrtool/firm.h new file mode 100644 index 0000000..c663f71 --- /dev/null +++ b/ctrtool/firm.h @@ -0,0 +1,57 @@ +#ifndef _FIRM_H_ +#define _FIRM_H_ + +#include "types.h" +#include "info.h" +#include "ctr.h" +#include "filepath.h" +#include "settings.h" + + +typedef struct +{ + u8 offset[4]; + u8 address[4]; + u8 size[4]; + u8 type[4]; + u8 hash[32]; +} firm_sectionheader; + + + + +typedef struct +{ + u8 magic[4]; + u8 reserved1[4]; + u8 entrypointarm11[4]; + u8 entrypointarm9[4]; + u8 reserved2[0x30]; + firm_sectionheader section[4]; + u8 signature[0x100]; +} firm_header; + +typedef struct +{ + FILE* file; + settings* usersettings; + u32 offset; + u32 size; + firm_header header; + ctr_sha256_context sha; + int hashcheck[4]; + int headersigcheck; +} firm_context; + +void firm_init(firm_context* ctx); +void firm_set_file(firm_context* ctx, FILE* file); +void firm_set_offset(firm_context* ctx, u32 offset); +void firm_set_size(firm_context* ctx, u32 size); +void firm_set_usersettings(firm_context* ctx, settings* usersettings); +void firm_process(firm_context* ctx, u32 actions); +void firm_print(firm_context* ctx); +void firm_save(firm_context* ctx, u32 index, u32 flags); +int firm_verify(firm_context* ctx, u32 flags); +void firm_signature_verify(firm_context* ctx); + +#endif // _FIRM_H_ diff --git a/ctrtool/info.h b/ctrtool/info.h new file mode 100644 index 0000000..fac4117 --- /dev/null +++ b/ctrtool/info.h @@ -0,0 +1,16 @@ +#ifndef _INFO_H_ +#define _INFO_H_ + +#include "types.h" +#include "keyset.h" + +typedef struct +{ + FILE* file; + keyset* keys; + u32 offset; + const u8* blob; + u32 blobsize; +} infocontext; + +#endif // _INFO_H_ diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c new file mode 100644 index 0000000..1dd2f9f --- /dev/null +++ b/ctrtool/ivfc.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include "types.h" +#include "utils.h" +#include "ivfc.h" +#include "ctr.h" + +void ivfc_init(ivfc_context* ctx) +{ + memset(ctx, 0, sizeof(ivfc_context)); +} + +void ivfc_set_usersettings(ivfc_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void ivfc_set_offset(ivfc_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void ivfc_set_size(ivfc_context* ctx, u32 size) +{ + ctx->size = size; +} + +void ivfc_set_file(ivfc_context* ctx, FILE* file) +{ + ctx->file = file; +} + + +void ivfc_process(ivfc_context* ctx, u32 actions) +{ + + + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, sizeof(ivfc_header), ctx->file); + + if (getle32(ctx->header.magic) != MAGIC_IVFC) + { + fprintf(stdout, "Error, IVFC segment corrupted\n"); + return; + } + + if (getle32(ctx->header.id) == 0x10000) + { + fread(&ctx->romfsheader, 1, sizeof(ivfc_header_romfs), ctx->file); + + ctx->levelcount = 3; + + ctx->level[2].hashblocksize = 1 << getle32(ctx->romfsheader.level3.blocksize); + ctx->level[1].hashblocksize = 1 << getle32(ctx->romfsheader.level2.blocksize); + ctx->level[0].hashblocksize = 1 << getle32(ctx->romfsheader.level1.blocksize); + ctx->level[0].hashoffset = 0x60; + + ctx->bodyoffset = align64(ctx->level[0].hashoffset + getle32(ctx->romfsheader.masterhashsize), ctx->level[2].hashblocksize); + ctx->bodysize = getle64(ctx->romfsheader.level3.hashdatasize); + + ctx->level[2].dataoffset = ctx->bodyoffset; + ctx->level[2].datasize = align64(ctx->bodysize, ctx->level[2].hashblocksize); + + ctx->level[1].hashoffset = align64(ctx->bodyoffset + ctx->bodysize, ctx->level[2].hashblocksize); + ctx->level[2].hashoffset = ctx->level[1].hashoffset + getle64(ctx->romfsheader.level2.logicaloffset) - getle64(ctx->romfsheader.level1.logicaloffset); + + ctx->level[1].dataoffset = ctx->level[2].hashoffset; + ctx->level[1].datasize = align64(getle64(ctx->romfsheader.level2.hashdatasize), ctx->level[1].hashblocksize); + + ctx->level[0].dataoffset = ctx->level[1].hashoffset; + ctx->level[0].datasize = align64(getle64(ctx->romfsheader.level1.hashdatasize), ctx->level[0].hashblocksize); + } + + if (actions & VerifyFlag) + ivfc_verify(ctx, actions); + + if (actions & InfoFlag) + ivfc_print(ctx); + +} + +void ivfc_verify(ivfc_context* ctx, u32 flags) +{ + u32 i, j; + u32 blockcount; + + for(i=0; ilevelcount; i++) + { + ivfc_level* level = ctx->level + i; + + level->hashcheck = Fail; + } + + for(i=0; ilevelcount; i++) + { + ivfc_level* level = ctx->level + i; + + blockcount = level->datasize / level->hashblocksize; + if (blockcount * level->hashblocksize != level->datasize) + { + fprintf(stderr, "Error, IVFC block size mismatch\n"); + return; + } + + level->hashcheck = Good; + + for(j=0; jdataoffset + level->hashblocksize * j, level->hashblocksize, calchash); + ivfc_read(ctx, level->hashoffset + 0x20 * j, 0x20, testhash); + + if (memcmp(calchash, testhash, 0x20) != 0) + level->hashcheck = Fail; + } + } +} + +void ivfc_read(ivfc_context* ctx, u32 offset, u32 size, u8* buffer) +{ + if ( (offset > ctx->size) || (offset+size > ctx->size) ) + { + fprintf(stderr, "Error, IVFC offset out of range (offset=0x%08x, size=0x%08x)\n", offset, size); + return; + } + + fseek(ctx->file, ctx->offset + offset, SEEK_SET); + if (size != fread(buffer, 1, size, ctx->file)) + { + fprintf(stderr, "Error, IVFC could not read file\n"); + return; + } +} + +void ivfc_hash(ivfc_context* ctx, u32 offset, u32 size, u8* hash) +{ + if (size > IVFC_MAX_BUFFERSIZE) + { + fprintf(stderr, "Error, IVFC hash block size too big.\n"); + return; + } + + ivfc_read(ctx, offset, size, ctx->buffer); + + ctr_sha_256(ctx->buffer, size, hash); +} + +void ivfc_print(ivfc_context* ctx) +{ + u32 i; + ivfc_header* header = &ctx->header; + + fprintf(stdout, "\nIVFC:\n"); + + fprintf(stdout, "Header: %c%c%c%c\n", header->magic[0], header->magic[1], header->magic[2], header->magic[3]); + fprintf(stdout, "Id: %08x\n", getle32(header->id)); + + for(i=0; ilevelcount; i++) + { + ivfc_level* level = ctx->level + i; + + fprintf(stdout, "\n"); + if (level->hashcheck == Unchecked) + fprintf(stdout, "Level %d: \n", i); + else + fprintf(stdout, "Level %d (%s): \n", i, level->hashcheck == Good? "GOOD" : "FAIL"); + fprintf(stdout, " Data offset: 0x%016llx\n", ctx->offset + level->dataoffset); + fprintf(stdout, " Data size: 0x%016llx\n", level->datasize); + fprintf(stdout, " Hash offset: 0x%016llx\n", ctx->offset + level->hashoffset); + fprintf(stdout, " Hash block size: 0x%08x\n", level->hashblocksize); + } +} + +u64 ivfc_get_body_offset(ivfc_context* ctx) +{ + return ctx->bodyoffset; +} + +u64 ivfc_get_body_size(ivfc_context* ctx) +{ + return ctx->bodysize; +} + diff --git a/ctrtool/ivfc.h b/ctrtool/ivfc.h new file mode 100644 index 0000000..1ec46cd --- /dev/null +++ b/ctrtool/ivfc.h @@ -0,0 +1,72 @@ +#ifndef __IVFC_H__ +#define __IVFC_H__ + +#include "types.h" +#include "settings.h" + +#define IVFC_MAX_LEVEL 4 +#define IVFC_MAX_BUFFERSIZE 0x4000 + +typedef struct +{ + u8 magic[4]; + u8 id[4]; +} ivfc_header; + +typedef struct +{ + u8 logicaloffset[8]; + u8 hashdatasize[8]; + u8 blocksize[4]; + u8 reserved[4]; +} ivfc_levelheader; + +typedef struct +{ + u64 dataoffset; + u64 datasize; + u64 hashoffset; + u32 hashblocksize; + int hashcheck; +} ivfc_level; + +typedef struct +{ + u8 masterhashsize[4]; + ivfc_levelheader level1; + ivfc_levelheader level2; + ivfc_levelheader level3; + u8 reserved[4]; + u8 optionalsize[4]; +} ivfc_header_romfs; + +typedef struct +{ + FILE* file; + u32 offset; + u32 size; + settings* usersettings; + + ivfc_header header; + ivfc_header_romfs romfsheader; + + u32 levelcount; + ivfc_level level[IVFC_MAX_LEVEL]; + u64 bodyoffset; + u64 bodysize; + u8 buffer[IVFC_MAX_BUFFERSIZE]; +} ivfc_context; + +void ivfc_init(ivfc_context* ctx); +void ivfc_process(ivfc_context* ctx, u32 actions); +void ivfc_set_offset(ivfc_context* ctx, u32 offset); +void ivfc_set_size(ivfc_context* ctx, u32 size); +void ivfc_set_file(ivfc_context* ctx, FILE* file); +void ivfc_set_usersettings(ivfc_context* ctx, settings* usersettings); +void ivfc_verify(ivfc_context* ctx, u32 flags); +void ivfc_print(ivfc_context* ctx); + +void ivfc_read(ivfc_context* ctx, u32 offset, u32 size, u8* buffer); +void ivfc_hash(ivfc_context* ctx, u32 offset, u32 size, u8* hash); + +#endif // __IVFC_H__ diff --git a/ctrtool/keyset.cpp b/ctrtool/keyset.cpp new file mode 100644 index 0000000..bc7e23c --- /dev/null +++ b/ctrtool/keyset.cpp @@ -0,0 +1,262 @@ +#include +#include "keyset.h" +#include "utils.h" +#include "tinyxml/tinyxml.h" + +static void keyset_set_key128(key128* key, unsigned char* keydata); +static void keyset_parse_key128(key128* key, char* keytext, int keylen); +static int keyset_parse_key(const char* text, unsigned int textlen, unsigned char* key, unsigned int size, int* valid); +static int keyset_load_rsakey2048(TiXmlElement* elem, rsakey2048* key); +static int keyset_load_key128(TiXmlHandle node, key128* key); +static int keyset_load_key(TiXmlHandle node, unsigned char* key, unsigned int maxsize, int* valid); + +static int ishex(char c) +{ + if (c >= '0' && c <= '9') + return 1; + if (c >= 'A' && c <= 'F') + return 1; + if (c >= 'a' && c <= 'f') + return 1; + return 0; + +} + +static unsigned char hextobin(char c) +{ + if (c >= '0' && c <= '9') + return c-'0'; + if (c >= 'A' && c <= 'F') + return c-'A'+0xA; + if (c >= 'a' && c <= 'f') + return c-'a'+0xA; + return 0; +} + +void keyset_init(keyset* keys) +{ + memset(keys, 0, sizeof(keyset)); +} + +int keyset_load_key(TiXmlHandle node, unsigned char* key, unsigned int size, int* valid) +{ + TiXmlElement* elem = node.ToElement(); + + if (valid) + *valid = 0; + + if (!elem) + return 0; + + const char* text = elem->GetText(); + unsigned int textlen = strlen(text); + + int status = keyset_parse_key(text, textlen, key, size, valid); + + if (status == KEY_ERR_LEN_MISMATCH) + { + fprintf(stderr, "Error size mismatch for key \"%s/%s\"\n", elem->Parent()->Value(), elem->Value()); + return 0; + } + + return 1; +} + + +int keyset_parse_key(const char* text, unsigned int textlen, unsigned char* key, unsigned int size, int* valid) +{ + unsigned int i, j; + unsigned int hexcount = 0; + + + if (valid) + *valid = 0; + + for(i=0; idata, sizeof(key->data), &key->valid); +} + +int keyset_load_rsakey2048(TiXmlHandle node, rsakey2048* key) +{ + key->keytype = RSAKEY_INVALID; + + if (!keyset_load_key(node.FirstChild("N"), key->n, sizeof(key->n), 0)) + goto clean; + if (!keyset_load_key(node.FirstChild("E"), key->e, sizeof(key->e), 0)) + goto clean; + key->keytype = RSAKEY_PUB; + + if (!keyset_load_key(node.FirstChild("D"), key->d, sizeof(key->d), 0)) + goto clean; + if (!keyset_load_key(node.FirstChild("P"), key->p, sizeof(key->p), 0)) + goto clean; + if (!keyset_load_key(node.FirstChild("Q"), key->q, sizeof(key->q), 0)) + goto clean; + if (!keyset_load_key(node.FirstChild("DP"), key->dp, sizeof(key->dp), 0)) + goto clean; + if (!keyset_load_key(node.FirstChild("DQ"), key->dq, sizeof(key->dq), 0)) + goto clean; + if (!keyset_load_key(node.FirstChild("QP"), key->qp, sizeof(key->qp), 0)) + goto clean; + + key->keytype = RSAKEY_PRIV; +clean: + return (key->keytype != RSAKEY_INVALID); +} + +int keyset_load(keyset* keys, const char* fname, int verbose) +{ + TiXmlDocument doc(fname); + bool loadOkay = doc.LoadFile(); + + if (!loadOkay) + { + if (verbose) + fprintf(stderr, "Could not load keyset file \"%s\", error: %s.\n", fname, doc.ErrorDesc() ); + + return 0; + } + + TiXmlHandle root = doc.FirstChild("document"); + + keyset_load_rsakey2048(root.FirstChild("ncsdrsakey"), &keys->ncsdrsakey); + keyset_load_rsakey2048(root.FirstChild("ncchrsakey"), &keys->ncchrsakey); + keyset_load_rsakey2048(root.FirstChild("ncchdescrsakey"), &keys->ncchdescrsakey); + keyset_load_rsakey2048(root.FirstChild("firmrsakey"), &keys->firmrsakey); + keyset_load_key128(root.FirstChild("commonkey"), &keys->commonkey); + keyset_load_key128(root.FirstChild("ncchkey"), &keys->ncchkey); + keyset_load_key128(root.FirstChild("ncchfixedsystemkey"), &keys->ncchfixedsystemkey); + + + return 1; +} + + +void keyset_merge(keyset* keys, keyset* src) +{ + if (src->ncchkey.valid) + keyset_set_key128(&keys->ncchkey, src->ncchkey.data); + if (src->ncchfixedsystemkey.valid) + keyset_set_key128(&keys->ncchfixedsystemkey, src->ncchfixedsystemkey.data); + if (src->commonkey.valid) + keyset_set_key128(&keys->commonkey, src->commonkey.data); +} + +void keyset_set_key128(key128* key, unsigned char* keydata) +{ + memcpy(key->data, keydata, 16); + key->valid = 1; +} + +void keyset_parse_key128(key128* key, char* keytext, int keylen) +{ + keyset_parse_key(keytext, keylen, key->data, 16, &key->valid); +} + +void keyset_set_commonkey(keyset* keys, unsigned char* keydata) +{ + keyset_set_key128(&keys->commonkey, keydata); +} + +void keyset_parse_commonkey(keyset* keys, char* keytext, int keylen) +{ + keyset_parse_key128(&keys->commonkey, keytext, keylen); +} + +void keyset_set_ncchkey(keyset* keys, unsigned char* keydata) +{ + keyset_set_key128(&keys->ncchkey, keydata); +} + +void keyset_parse_ncchkey(keyset* keys, char* keytext, int keylen) +{ + keyset_parse_key128(&keys->ncchkey, keytext, keylen); +} + +void keyset_set_ncchfixedsystemkey(keyset* keys, unsigned char* keydata) +{ + keyset_set_key128(&keys->ncchfixedsystemkey, keydata); +} + +void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen) +{ + keyset_parse_key128(&keys->ncchfixedsystemkey, keytext, keylen); +} + +void keyset_dump_rsakey(rsakey2048* key, const char* keytitle) +{ + if (key->keytype == RSAKEY_INVALID) + return; + + + fprintf(stdout, "%s\n", keytitle); + + memdump(stdout, "Modulus: ", key->n, 256); + memdump(stdout, "Exponent: ", key->e, 3); + + if (key->keytype == RSAKEY_PRIV) + { + memdump(stdout, "P: ", key->p, 128); + memdump(stdout, "Q: ", key->q, 128); + } + fprintf(stdout, "\n"); +} + +void keyset_dump_key128(key128* key, const char* keytitle) +{ + if (key->valid) + { + fprintf(stdout, "%s\n", keytitle); + memdump(stdout, "", key->data, 16); + fprintf(stdout, "\n"); + } +} + +void keyset_dump(keyset* keys) +{ + fprintf(stdout, "Current keyset: \n"); + keyset_dump_key128(&keys->ncchkey, "NCCH KEY"); + keyset_dump_key128(&keys->ncchfixedsystemkey, "NCCH FIXEDSYSTEMKEY"); + keyset_dump_key128(&keys->commonkey, "COMMON KEY"); + + keyset_dump_rsakey(&keys->ncsdrsakey, "NCSD RSA KEY"); + keyset_dump_rsakey(&keys->ncchdescrsakey, "NCCH DESC RSA KEY"); + + fprintf(stdout, "\n"); +} + diff --git a/ctrtool/keyset.h b/ctrtool/keyset.h new file mode 100644 index 0000000..f695f54 --- /dev/null +++ b/ctrtool/keyset.h @@ -0,0 +1,70 @@ +#ifndef _KEYSET_H_ +#define _KEYSET_H_ + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + KEY_ERR_LEN_MISMATCH, + KEY_ERR_INVALID_NODE, + KEY_OK +} keystatus; + +typedef enum +{ + RSAKEY_INVALID, + RSAKEY_PRIV, + RSAKEY_PUB +} rsakeytype; + +typedef struct +{ + unsigned char n[256]; + unsigned char e[3]; + unsigned char d[256]; + unsigned char p[128]; + unsigned char q[128]; + unsigned char dp[128]; + unsigned char dq[128]; + unsigned char qp[128]; + rsakeytype keytype; +} rsakey2048; + +typedef struct +{ + unsigned char data[16]; + int valid; +} key128; + +typedef struct +{ + key128 commonkey; + key128 ncchkey; + key128 ncchfixedsystemkey; + rsakey2048 ncsdrsakey; + rsakey2048 ncchrsakey; + rsakey2048 ncchdescrsakey; + rsakey2048 firmrsakey; +} keyset; + +void keyset_init(keyset* keys); +int keyset_load(keyset* keys, const char* fname, int verbose); +void keyset_merge(keyset* keys, keyset* src); +void keyset_set_commonkey(keyset* keys, unsigned char* keydata); +void keyset_parse_commonkey(keyset* keys, char* keytext, int keylen); +void keyset_set_ncchkey(keyset* keys, unsigned char* keydata); +void keyset_parse_ncchkey(keyset* keys, char* keytext, int keylen); +void keyset_set_ncchfixedsystemkey(keyset* keys, unsigned char* keydata); +void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen); +void keyset_dump(keyset* keys); + +#ifdef __cplusplus +} +#endif + + +#endif // _KEYSET_H_ diff --git a/ctrtool/lzss.c b/ctrtool/lzss.c new file mode 100644 index 0000000..ec71808 --- /dev/null +++ b/ctrtool/lzss.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include "types.h" +#include "utils.h" +#include "lzss.h" + +void lzss_init(lzss_context* ctx) +{ + memset(ctx, 0, sizeof(lzss_context)); +} + +void lzss_set_usersettings(lzss_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void lzss_set_offset(lzss_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void lzss_set_size(lzss_context* ctx, u32 size) +{ + ctx->size = size; +} + +void lzss_set_file(lzss_context* ctx, FILE* file) +{ + ctx->file = file; +} + + +void lzss_process(lzss_context* ctx, u32 actions) +{ + unsigned int compressedsize; + unsigned char* compressedbuffer = 0; + unsigned int decompressedsize; + unsigned char* decompressedbuffer = 0; + FILE* fout = 0; + + + fseek(ctx->file, ctx->offset, SEEK_SET); + + + if (actions & ExtractFlag) + { + + filepath* path = settings_get_lzss_path(ctx->usersettings); + + if (path == 0 || path->valid == 0) + goto clean; + + fout = fopen(path->pathname, "wb"); + if (0 == fout) + { + fprintf(stdout, "Error opening out file %s\n", path->pathname); + goto clean; + } + compressedsize = ctx->size; + compressedbuffer = malloc(compressedsize); + if (1 != fread(compressedbuffer, compressedsize, 1, ctx->file)) + { + fprintf(stdout, "Error read input file\n"); + goto clean; + } + + decompressedsize = lzss_get_decompressed_size(compressedbuffer, compressedsize); + decompressedbuffer = malloc(decompressedsize); + + printf("Compressed: %d\n", compressedsize); + printf("Decompressed: %d\n", decompressedsize); + + if (decompressedbuffer == 0) + { + fprintf(stdout, "Error allocating memory\n"); + goto clean; + } + + if (0 == lzss_decompress(compressedbuffer, compressedsize, decompressedbuffer, decompressedsize)) + goto clean; + + printf("Saving decompressed lzss blob to %s...\n", path->pathname); + if (decompressedsize != fwrite(decompressedbuffer, 1, decompressedsize, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + } + +clean: + free(decompressedbuffer); + free(compressedbuffer); + if (fout) + fclose(fout); +} + + +u32 lzss_get_decompressed_size(u8* compressed, u32 compressedsize) +{ + u8* footer = compressed + compressedsize - 8; + + //u32 buffertopandbottom = getle32(footer+0); + u32 originalbottom = getle32(footer+4); + + return originalbottom + compressedsize; +} + +int lzss_decompress(u8* compressed, u32 compressedsize, u8* decompressed, u32 decompressedsize) +{ + u8* footer = compressed + compressedsize - 8; + u32 buffertopandbottom = getle32(footer+0); + //u32 originalbottom = getle32(footer+4); + u32 i, j; + u32 out = decompressedsize; + u32 index = compressedsize - ((buffertopandbottom>>24)&0xFF); + u32 segmentoffset; + u32 segmentsize; + u8 control; + u32 stopindex = compressedsize - (buffertopandbottom&0xFFFFFF); + + memset(decompressed, 0, decompressedsize); + memcpy(decompressed, compressed, compressedsize); + + + while(index > stopindex) + { + control = compressed[--index]; + + + for(i=0; i<8; i++) + { + if (index <= stopindex) + break; + + if (index <= 0) + break; + + if (out <= 0) + break; + + if (control & 0x80) + { + if (index < 2) + { + fprintf(stderr, "Error, compression out of bounds\n"); + goto clean; + } + + index -= 2; + + segmentoffset = compressed[index] | (compressed[index+1]<<8); + segmentsize = ((segmentoffset >> 12)&15)+3; + segmentoffset &= 0x0FFF; + segmentoffset += 2; + + + if (out < segmentsize) + { + fprintf(stderr, "Error, compression out of bounds\n"); + goto clean; + } + + for(j=0; j= decompressedsize) + { + fprintf(stderr, "Error, compression out of bounds\n"); + goto clean; + } + + data = decompressed[out+segmentoffset]; + decompressed[--out] = data; + } + } + else + { + if (out < 1) + { + fprintf(stderr, "Error, compression out of bounds\n"); + goto clean; + } + decompressed[--out] = compressed[--index]; + } + + control <<= 1; + } + } + + return 1; +clean: + return 0; +} diff --git a/ctrtool/lzss.h b/ctrtool/lzss.h new file mode 100644 index 0000000..61e4a5d --- /dev/null +++ b/ctrtool/lzss.h @@ -0,0 +1,26 @@ +#ifndef _LZSS_H_ +#define _LZSS_H_ + +#include "types.h" +#include "settings.h" + +typedef struct +{ + FILE* file; + u32 offset; + u32 size; + settings* usersettings; +} lzss_context; + +void lzss_init(lzss_context* ctx); +void lzss_process(lzss_context* ctx, u32 actions); +void lzss_set_offset(lzss_context* ctx, u32 offset); +void lzss_set_size(lzss_context* ctx, u32 size); +void lzss_set_file(lzss_context* ctx, FILE* file); +void lzss_set_usersettings(lzss_context* ctx, settings* usersettings); + +u32 lzss_get_decompressed_size(u8* compressed, u32 compressedsize); +int lzss_decompress(u8* compressed, u32 compressedsize, u8* decompressed, u32 decompressedsize); + + +#endif // _LZSS_H_ diff --git a/ctrtool/main.c b/ctrtool/main.c index ee31c0c..a432aa3 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -2,14 +2,21 @@ #include #include #include +#include "utils.h" #include "ctr.h" - -enum actionflags -{ - Extract = (1<<0), - Info = (1<<1), - NoDecrypt = (1<<2), -}; +#include "ncch.h" +#include "ncsd.h" +#include "cia.h" +#include "tmd.h" +#include "tik.h" +#include "lzss.h" +#include "keyset.h" +#include "exefs.h" +#include "info.h" +#include "settings.h" +#include "firm.h" +#include "cwav.h" +#include "romfs.h" enum cryptotype { @@ -18,33 +25,14 @@ enum cryptotype CBC }; + typedef struct { int actions; - unsigned int filetype; - unsigned char key[16]; - unsigned char iv[16]; - char exheaderfname[512]; - char romfsfname[512]; - char exefsfname[512]; - int setexefsfname; - int setromfsfname; - int setexheaderfname; - unsigned int ncchoffset; - ctr_crypto_context cryptoctx; + u32 filetype; FILE* infile; - char certsfname[512]; - char tikfname[512]; - char tmdfname[512]; - char contentsfname[512]; - char bannerfname[512]; - int setcertsfname; - int settikfname; - int settmdfname; - int setcontentsfname; - int setbannerfname; - - + u32 infilesize; + settings usersettings; } toolcontext; static void usage(const char *argv0) @@ -59,441 +47,64 @@ static void usage(const char *argv0) " -x, --extract Extract data from file.\n" " This is also the default action.\n" " -p, --plain Extract data without decrypting.\n" - " -k, --key=file Specify key file.\n" + " -r, --raw Keep raw data, don't unpack.\n" + " -k, --keyset=file Specify keyset file.\n" + " -v, --verbose Give verbose output.\n" + " -y, --verify Verify hashes and signatures.\n" + " --unitsize=size Set media unit size (default 0x200).\n" + " --commonkey=key Set common key.\n" + " --ncchkey=key Set ncch key.\n" + " --ncchsyskey=key Set ncch fixed system key.\n" + " --showkeys Show the keys being used.\n" + " -t, --intype=type Specify input file type [ncsd, ncch, exheader, cia, tmd, lzss,\n" + " firm, cwav, romfs]\n" + "LZSS options:\n" + " --lzssout=file Specify lzss output file\n" "CXI/CCI options:\n" " -n, --ncch=offs Specify offset for NCCH header.\n" - " --exefs=file Specify ExeFS filepath.\n" - " --romfs=file Specify RomFS filepath.\n" - " --exheader=file Specify Extended Header filepath.\n" + " --exefs=file Specify ExeFS file path.\n" + " --exefsdir=dir Specify ExeFS directory path.\n" + " --romfs=file Specify RomFS file path.\n" + " --exheader=file Specify Extended Header file path.\n" "CIA options:\n" - " --certs=file Specify Certificate chain filepath.\n" - " --tik=file Specify Ticket filepath.\n" - " --tmd=file Specify TMD filepath.\n" - " --contents=file Specify Contents filepath.\n" - " --banner=file Specify Banner filepath.\n" + " --certs=file Specify Certificate chain file path.\n" + " --tik=file Specify Ticket file path.\n" + " --tmd=file Specify TMD file path.\n" + " --contents=file Specify Contents file path.\n" + " --meta=file Specify Meta file path.\n" + "FIRM options:\n" + " --firmdir=dir Specify Firm directory path.\n" + "CWAV options:\n" + " --wav=file Specify wav output file.\n" + " --wavloops=count Specify wav loop count, default 0.\n" + "ROMFS options:\n" + " --romfsdir=dir Specify RomFS directory path.\n" + " --listromfs List files in RomFS.\n" "\n", argv0); exit(1); } -static unsigned int align(unsigned int offset, unsigned int alignment) -{ - unsigned int mask = ~(alignment-1); - - return (offset + (alignment-1)) & mask; -} - -unsigned long long getle64(const unsigned char* p) -{ - unsigned long long n = p[0]; - - n |= (unsigned long long)p[1]<<8; - n |= (unsigned long long)p[2]<<16; - n |= (unsigned long long)p[3]<<24; - n |= (unsigned long long)p[4]<<32; - n |= (unsigned long long)p[5]<<40; - n |= (unsigned long long)p[6]<<48; - n |= (unsigned long long)p[7]<<56; - return n; -} - -unsigned int getle32(const unsigned char* p) -{ - return (p[0]<<0) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); -} - -unsigned int getle16(const unsigned char* p) -{ - return (p[0]<<0) | (p[1]<<8); -} - -void readkeyfile(unsigned char* key, const char* keyfname) -{ - FILE* f = fopen(keyfname, "rb"); - unsigned int keysize = 0; - - if (0 == f) - { - fprintf(stdout, "Error opening key file\n"); - goto clean; - } - - fseek(f, 0, SEEK_END); - keysize = ftell(f); - fseek(f, 0, SEEK_SET); - - if (keysize != 16) - { - fprintf(stdout, "Error key size mismatch, got %d, expected %d\n", keysize, 16); - goto clean; - } - - if (16 != fread(key, 1, 16, f)) - { - fprintf(stdout, "Error reading key file\n"); - goto clean; - } - -clean: - if (f) - fclose(f); -} - - -void memdump(FILE* fout, const char* prefix, const unsigned char* data, unsigned int size) -{ - unsigned int i; - unsigned int prefixlen = strlen(prefix); - unsigned int offs = 0; - unsigned int line = 0; - while(size) - { - unsigned int max = 16; - - if (max > size) - max = size; - - if (line==0) - fprintf(fout, "%s", prefix); - else - fprintf(fout, "%*s", prefixlen, ""); - - - for(i=0; imagic, 4); - magic[4] = 0; - memcpy(productcode, header->productcode, 0x10); - productcode[0x10] = 0; - - fprintf(stdout, "Header: %s\n", magic); - memdump(stdout, "Signature: ", header->signature, 0x100); - fprintf(stdout, "Content size: 0x%08x\n", getle32(header->contentsize)*0x200); - fprintf(stdout, "Partition id: %016llx\n", getle64(header->partitionid)); - fprintf(stdout, "Maker code: %04x\n", getle16(header->makercode)); - fprintf(stdout, "Version: %04x\n", getle16(header->version)); - fprintf(stdout, "Program id: %016llx\n", getle64(header->programid)); - fprintf(stdout, "Temp flag: %02x\n", header->tempflag); - fprintf(stdout, "Product code: %s\n", productcode); - memdump(stdout, "Extended header hash: ", header->extendedheaderhash, 0x20); - fprintf(stdout, "Extended header size: %08x\n", getle32(header->extendedheadersize)); - fprintf(stdout, "Flags: %016llx\n", getle64(header->flags)); - fprintf(stdout, "Plain region offset: 0x%08x\n", getle32(header->plainregionsize)? offset+getle32(header->plainregionoffset)*0x200 : 0); - fprintf(stdout, "Plain region size: 0x%08x\n", getle32(header->plainregionsize)*0x200); - fprintf(stdout, "ExeFS offset: 0x%08x\n", getle32(header->exefssize)? offset+getle32(header->exefsoffset)*0x200 : 0); - fprintf(stdout, "ExeFS size: 0x%08x\n", getle32(header->exefssize)*0x200); - fprintf(stdout, "ExeFS hash region size: 0x%08x\n", getle32(header->exefshashregionsize)*0x200); - fprintf(stdout, "RomFS offset: 0x%08x\n", getle32(header->romfssize)? offset+getle32(header->romfsoffset)*0x200 : 0); - fprintf(stdout, "RomFS size: 0x%08x\n", getle32(header->romfssize)*0x200); - fprintf(stdout, "RomFS hash region size: 0x%08x\n", getle32(header->romfshashregionsize)*0x200); - memdump(stdout, "ExeFS Superblock Hash: ", header->exefssuperblockhash, 0x20); - memdump(stdout, "RomFS Superblock Hash: ", header->romfssuperblockhash, 0x20); -} - - -void decode_cia_header(const ctr_ciaheader* header) -{ - fprintf(stdout, "Header size 0x%08x\n", getle32(header->headersize)); - fprintf(stdout, "Type %04x\n", getle16(header->type)); - fprintf(stdout, "Version %04x\n", getle16(header->version)); - fprintf(stdout, "Certificate chain size 0x%04x\n", getle32(header->certsize)); - fprintf(stdout, "Ticket size 0x%04x\n", getle32(header->ticketsize)); - fprintf(stdout, "TMD size 0x%04x\n", getle32(header->tmdsize)); - fprintf(stdout, "Metasize 0x%04x\n", getle32(header->metasize)); - fprintf(stdout, "Contentsize 0x%016llx\n", getle64(header->contentsize)); -} - -void decrypt_ncch(toolcontext* ctx, unsigned int ncchoffset, const ctr_ncchheader* header, unsigned int type) -{ - unsigned int size = 0; - unsigned int offset = 0; - FILE* fout = 0; - unsigned char buffer[16 * 1024]; - unsigned char counter[16]; - unsigned int i; - char* outfname = 0; - - - if (type == NCCHTYPE_EXEFS) - { - offset = ncchoffset + getle32(header->exefsoffset) * 0x200; - size = getle32(header->exefssize) * 0x200; - outfname = ctx->exefsfname; - } - else if (type == NCCHTYPE_ROMFS) - { - offset = ncchoffset + getle32(header->romfsoffset) * 0x200; - size = getle32(header->romfssize) * 0x200; - outfname = ctx->romfsfname; - } - else if (type == NCCHTYPE_EXTHEADER) - { - offset = ncchoffset + 0x200; - size = getle32(header->extendedheadersize); - outfname = ctx->exheaderfname; - } - else - { - fprintf(stderr, "Error invalid NCCH type\n"); - goto clean; - } - - fout = fopen(outfname, "wb"); - if (0 == fout) - { - fprintf(stdout, "Error opening out file %s\n", outfname); - goto clean; - } - - fseek(ctx->infile, offset, SEEK_SET); - - memset(counter, 0, 16); - for(i=0; i<8; i++) - counter[i] = header->partitionid[7-i]; - counter[8] = type; - - ctr_init_counter(&ctx->cryptoctx, ctx->key, counter); - - while(size) - { - unsigned int max = sizeof(buffer); - if (max > size) - max = size; - - if (max != fread(buffer, 1, max, ctx->infile)) - { - fprintf(stdout, "Error reading file\n"); - goto clean; - } - - if (0 == (ctx->actions & NoDecrypt)) - ctr_crypt_counter(&ctx->cryptoctx, buffer, buffer, max); - - if (max != fwrite(buffer, 1, max, fout)) - { - fprintf(stdout, "Error writing file\n"); - goto clean; - } - - size -= max; - } -clean: - if (fout) - fclose(fout); -} - -void process_ncch(toolcontext* ctx, unsigned int ncchoffset) -{ - ctr_ncchheader ncchheader; - - - if (ncchoffset == ~0) - { - if (ctx->filetype == FILETYPE_CCI) - ncchoffset = 0x4000; - else if (ctx->filetype == FILETYPE_CXI) - ncchoffset = 0; - } - - fseek(ctx->infile, ncchoffset, SEEK_SET); - - fread(&ncchheader, 1, 0x200, ctx->infile); - - if (ctx->actions & Info) - decode_ncch_header(&ncchheader, ncchoffset); - if (ctx->actions & Extract) - { - if (ctx->setexefsfname) - { - fprintf(stdout, "Decrypting ExeFS...\n"); - decrypt_ncch(ctx, ncchoffset, &ncchheader, NCCHTYPE_EXEFS); - } - if (ctx->setromfsfname) - { - fprintf(stdout, "Decrypting RomFS...\n"); - decrypt_ncch(ctx, ncchoffset, &ncchheader, NCCHTYPE_ROMFS); - } - if (ctx->setexheaderfname) - { - fprintf(stdout, "Decrypting Extended Header...\n"); - decrypt_ncch(ctx, ncchoffset, &ncchheader, NCCHTYPE_EXTHEADER); - } - } -} - -void save_blob(toolcontext* ctx, unsigned int offset, unsigned int size, const char* outfname, unsigned int type) -{ - unsigned char buffer[16*1024]; - FILE* fout = 0; - - fseek(ctx->infile, offset, SEEK_SET); - - fout = fopen(outfname, "wb"); - if (0 == fout) - { - fprintf(stdout, "Error opening out file %s\n", outfname); - goto clean; - } - - if (type == CBC) - { - ctr_init_cbc_decrypt(&ctx->cryptoctx, ctx->key, ctx->iv); - } - - - while(size) - { - unsigned int max = sizeof(buffer); - if (max > size) - max = size; - - if (max != fread(buffer, 1, max, ctx->infile)) - { - fprintf(stdout, "Error reading file\n"); - goto clean; - } - - if (type == CBC) - { - ctr_decrypt_cbc(&ctx->cryptoctx, buffer, buffer, max); - } - - if (max != fwrite(buffer, 1, max, fout)) - { - fprintf(stdout, "Error writing file\n"); - goto clean; - } - - size -= max; - } -clean: - if (fout) - fclose(fout); -} - -void process_cia(toolcontext* ctx) -{ - ctr_ciaheader ciaheader; - unsigned int cryptotype; - unsigned char titleid[16]; - unsigned char titlekey[16]; - unsigned int offsetcerts = 0; - unsigned int offsettik = 0; - unsigned int offsettmd = 0; - unsigned int offsetmeta = 0; - unsigned int offsetcontent = 0; - - fseek(ctx->infile, 0, SEEK_SET); - - if (fread(&ciaheader, 1, sizeof(ciaheader), ctx->infile) != sizeof(ciaheader)) - { - fprintf(stderr, "Error reading CIA header\n"); - goto clean; - } - - if (ctx->actions & Info) - decode_cia_header(&ciaheader); - - offsetcerts = align(getle32(ciaheader.headersize), 64); - offsettik = align(offsetcerts + getle32(ciaheader.certsize), 64); - offsettmd = align(offsettik + getle32(ciaheader.ticketsize), 64); - offsetcontent = align(offsettmd + getle32(ciaheader.tmdsize), 64); - offsetmeta = align(offsetcontent + getle32(ciaheader.contentsize), 64); - fseek(ctx->infile, offsettik + 0x1BF, SEEK_SET); - fread(titlekey, 1, 16, ctx->infile); - fseek(ctx->infile, offsettik + 0x1DC, SEEK_SET); - fread(titleid, 1, 16, ctx->infile); - - if (ctx->actions & Info) - { - fprintf(stdout, "Certificates offset: 0x%08x\n", offsetcerts); - fprintf(stdout, "Ticket offset: 0x%08x\n", offsettik); - fprintf(stdout, "TMD offset: 0x%08x\n", offsettmd); - fprintf(stdout, "Content offset: 0x%08x\n", offsetcontent); - fprintf(stdout, "Banner offset: 0x%08x\n", offsetmeta); - memdump(stdout, "Title ID: ", titleid, 0x10); - memdump(stdout, "Encrypted title key: ", titlekey, 0x10); - } - - - if (ctx->actions & Extract) - { - if (ctx->setcertsfname) - { - fprintf(stdout, "Saving certificate chain to %s...\n", ctx->certsfname); - save_blob(ctx, offsetcerts, getle32(ciaheader.certsize), ctx->certsfname, Plain); - } - - if (ctx->settikfname) - { - fprintf(stdout, "Saving ticket to %s...\n", ctx->tikfname); - save_blob(ctx, offsettik, getle32(ciaheader.ticketsize), ctx->tikfname, Plain); - } - - if (ctx->settmdfname) - { - fprintf(stdout, "Saving TMD to %s...\n", ctx->tmdfname); - save_blob(ctx, offsettmd, getle32(ciaheader.tmdsize), ctx->tmdfname, Plain); - } - - if (ctx->setcontentsfname) - { - - - // Larger than 4GB content? Seems unlikely.. - if (ctx->actions & NoDecrypt) - { - fprintf(stdout, "Saving content to %s...\n", ctx->contentsfname); - cryptotype = Plain; - } - else - { - cryptotype = CBC; - memcpy(ctx->iv, titleid, 16); - fprintf(stdout, "Decrypting content to %s...\n", ctx->contentsfname); - } - - save_blob(ctx, offsetcontent, (unsigned int)getle64(ciaheader.contentsize), ctx->contentsfname, cryptotype); - } - - if (ctx->setbannerfname) - { - fprintf(stdout, "Saving banner to %s...\n", ctx->bannerfname); - save_blob(ctx, offsetmeta, getle32(ciaheader.metasize), ctx->bannerfname, Plain); - } - } - -clean: - return; -} int main(int argc, char* argv[]) { - FILE* infile = 0; - toolcontext ctx; - unsigned char magic[4]; + u8 magic[4]; char infname[512]; int c; - unsigned int ncchoffset = ~0; - unsigned char key[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + u32 ncchoffset = ~0; + char keysetfname[512] = "keys.xml"; + keyset tmpkeys; + unsigned int checkkeysetfile = 0; memset(&ctx, 0, sizeof(toolcontext)); - ctx.actions = Info | Extract; + ctx.actions = InfoFlag | ExtractFlag; ctx.filetype = FILETYPE_UNKNOWN; + settings_init(&ctx.usersettings); + keyset_init(&ctx.usersettings.keys); + keyset_init(&tmpkeys); + while (1) { @@ -510,77 +121,109 @@ int main(int argc, char* argv[]) {"tik", 1, NULL, 4}, {"tmd", 1, NULL, 5}, {"contents", 1, NULL, 6}, - {"banner", 1, NULL, 7}, - {"key", 1, NULL, 'k'}, + {"meta", 1, NULL, 7}, + {"exefsdir", 1, NULL, 8}, + {"keyset", 1, NULL, 'k'}, {"ncch", 1, NULL, 'n'}, + {"verbose", 0, NULL, 'v'}, + {"verify", 0, NULL, 'y'}, + {"raw", 0, NULL, 'r'}, + {"unitsize", 1, NULL, 9}, + {"showkeys", 0, NULL, 10}, + {"commonkey", 1, NULL, 11}, + {"ncchkey", 1, NULL, 12}, + {"intype", 1, NULL, 't'}, + {"lzssout", 1, NULL, 13}, + {"firmdir", 1, NULL, 14}, + {"ncchsyskey", 1, NULL, 15}, + {"wav", 1, NULL, 16}, + {"romfsdir", 1, NULL, 17}, + {"listromfs", 0, NULL, 18}, + {"wavloops", 1, NULL, 19}, {NULL}, }; - c = getopt_long(argc, argv, "dik:n:", long_options, &option_index); + c = getopt_long(argc, argv, "ryxivpk:n:t:", long_options, &option_index); if (c == -1) break; switch (c) { case 'x': - ctx.actions |= Extract; + ctx.actions |= ExtractFlag; break; - case 'p': - ctx.actions |= NoDecrypt; + case 'v': + ctx.actions |= VerboseFlag; break; - case 'i': - ctx.actions |= Info; + case 'y': + ctx.actions |= VerifyFlag; break; - case 'n': - ncchoffset = strtoul(optarg, 0, 0); - break; - - case 'k': - readkeyfile(key, optarg); - break; - - case 0: - strncpy(ctx.exefsfname, optarg, sizeof(ctx.exefsfname)); - ctx.setexefsfname = 1; + case 'p': + ctx.actions |= PlainFlag; break; - case 1: - strncpy(ctx.romfsfname, optarg, sizeof(ctx.romfsfname)); - ctx.setromfsfname = 1; + case 'r': + ctx.actions |= RawFlag; break; - case 2: - strncpy(ctx.exheaderfname, optarg, sizeof(ctx.exheaderfname)); - ctx.setexheaderfname = 1; + case 'i': + ctx.actions |= InfoFlag; break; - case 3: - strncpy(ctx.certsfname, optarg, sizeof(ctx.certsfname)); - ctx.setcertsfname = 1; + case 'n': + ncchoffset = strtoul(optarg, 0, 0); break; - case 4: - strncpy(ctx.tikfname, optarg, sizeof(ctx.tikfname)); - ctx.settikfname = 1; + case 'k': + strncpy(keysetfname, optarg, sizeof(keysetfname)); + checkkeysetfile = 1; break; - case 5: - strncpy(ctx.tmdfname, optarg, sizeof(ctx.tmdfname)); - ctx.settmdfname = 1; + case 't': + if (!strcmp(optarg, "exheader")) + ctx.filetype = FILETYPE_EXHEADER; + else if (!strcmp(optarg, "ncch")) + ctx.filetype = FILETYPE_CXI; + else if (!strcmp(optarg, "ncsd")) + ctx.filetype = FILETYPE_CCI; + else if (!strcmp(optarg, "cia")) + ctx.filetype = FILETYPE_CIA; + else if (!strcmp(optarg, "tmd")) + ctx.filetype = FILETYPE_TMD; + else if (!strcmp(optarg, "lzss")) + ctx.filetype = FILETYPE_LZSS; + else if (!strcmp(optarg, "firm")) + ctx.filetype = FILETYPE_FIRM; + else if (!strcmp(optarg, "cwav")) + ctx.filetype = FILETYPE_CWAV; + else if (!strcmp(optarg, "romfs")) + ctx.filetype = FILETYPE_ROMFS; break; - case 6: - strncpy(ctx.contentsfname, optarg, sizeof(ctx.contentsfname)); - ctx.setcontentsfname = 1; - break; + case 0: settings_set_exefs_path(&ctx.usersettings, optarg); break; + case 1: settings_set_romfs_path(&ctx.usersettings, optarg); break; + case 2: settings_set_exheader_path(&ctx.usersettings, optarg); break; + case 3: settings_set_certs_path(&ctx.usersettings, optarg); break; + case 4: settings_set_tik_path(&ctx.usersettings, optarg); break; + case 5: settings_set_tmd_path(&ctx.usersettings, optarg); break; + case 6: settings_set_content_path(&ctx.usersettings, optarg); break; + case 7: settings_set_content_path(&ctx.usersettings, optarg); break; + case 8: settings_set_exefs_dir_path(&ctx.usersettings, optarg); break; + case 9: settings_set_mediaunit_size(&ctx.usersettings, strtoul(optarg, 0, 0)); break; + case 10: ctx.actions |= ShowKeysFlag; break; + case 11: keyset_parse_commonkey(&tmpkeys, optarg, strlen(optarg)); break; + case 12: keyset_parse_ncchkey(&tmpkeys, optarg, strlen(optarg)); break; + case 13: settings_set_lzss_path(&ctx.usersettings, optarg); break; + case 14: settings_set_firm_dir_path(&ctx.usersettings, optarg); break; + case 15: keyset_parse_ncchfixedsystemkey(&tmpkeys, optarg, strlen(optarg)); break; + case 16: settings_set_wav_path(&ctx.usersettings, optarg); break; + case 17: settings_set_romfs_dir_path(&ctx.usersettings, optarg); break; + case 18: settings_set_list_romfs_files(&ctx.usersettings, 1); break; + case 19: settings_set_cwav_loopcount(&ctx.usersettings, strtoul(optarg, 0, 0)); break; - case 7: - strncpy(ctx.bannerfname, optarg, sizeof(ctx.bannerfname)); - ctx.setbannerfname = 1; - break; default: usage(argv[0]); @@ -598,32 +241,69 @@ int main(int argc, char* argv[]) usage(argv[0]); } - memcpy(ctx.key, key, 16); + keyset_load(&ctx.usersettings.keys, keysetfname, (ctx.actions & VerboseFlag) | checkkeysetfile); + keyset_merge(&ctx.usersettings.keys, &tmpkeys); + if (ctx.actions & ShowKeysFlag) + keyset_dump(&ctx.usersettings.keys); ctx.infile = fopen(infname, "rb"); - if (!ctx.infile) - goto clean; + if (ctx.infile == 0) + { + fprintf(stderr, "error: could not open input file!\n"); + return -1; + } + + fseek(ctx.infile, 0, SEEK_END); + ctx.infilesize = ftell(ctx.infile); + fseek(ctx.infile, 0, SEEK_SET); + + - fseek(ctx.infile, 0x100, SEEK_SET); - fread(&magic, 1, 4, ctx.infile); - switch(getle32(magic)) + if (ctx.filetype == FILETYPE_UNKNOWN) { - case MAGIC_NCCH: - ctx.filetype = FILETYPE_CXI; - break; - - case MAGIC_NCSD: - ctx.filetype = FILETYPE_CCI; - break; - - default: - fseek(ctx.infile, 0, SEEK_SET); - fread(magic, 1, 4, ctx.infile); - if (getle32(magic) == 0x2020) + fseek(ctx.infile, 0x100, SEEK_SET); + fread(&magic, 1, 4, ctx.infile); + + switch(getle32(magic)) + { + case MAGIC_NCCH: + ctx.filetype = FILETYPE_CXI; + break; + + case MAGIC_NCSD: + ctx.filetype = FILETYPE_CCI; + break; + + default: + break; + } + } + + if (ctx.filetype == FILETYPE_UNKNOWN) + { + fseek(ctx.infile, 0, SEEK_SET); + fread(magic, 1, 4, ctx.infile); + + switch(getle32(magic)) + { + case 0x2020: ctx.filetype = FILETYPE_CIA; - break; + break; + + case MAGIC_FIRM: + ctx.filetype = FILETYPE_FIRM; + break; + + case MAGIC_CWAV: + ctx.filetype = FILETYPE_CWAV; + break; + + case MAGIC_IVFC: + ctx.filetype = FILETYPE_ROMFS; // TODO: need to determine more here.. savegames use IVFC too, but is not ROMFS. + break; + } } if (ctx.filetype == FILETYPE_UNKNOWN) @@ -632,20 +312,131 @@ int main(int argc, char* argv[]) exit(1); } + switch(ctx.filetype) { - case FILETYPE_CXI: case FILETYPE_CCI: - process_ncch(&ctx, ncchoffset); - break; + { + ncsd_context ncsdctx; + + ncsd_init(&ncsdctx); + ncsd_set_file(&ncsdctx, ctx.infile); + ncsd_set_size(&ncsdctx, ctx.infilesize); + ncsd_set_usersettings(&ncsdctx, &ctx.usersettings); + ncsd_process(&ncsdctx, ctx.actions); + + break; + } + + case FILETYPE_FIRM: + { + firm_context firmctx; + + firm_init(&firmctx); + firm_set_file(&firmctx, ctx.infile); + firm_set_size(&firmctx, ctx.infilesize); + firm_set_usersettings(&firmctx, &ctx.usersettings); + firm_process(&firmctx, ctx.actions); + + break; + } + + case FILETYPE_CXI: + { + ncch_context ncchctx; + + ncch_init(&ncchctx); + ncch_set_file(&ncchctx, ctx.infile); + ncch_set_size(&ncchctx, ctx.infilesize); + ncch_set_usersettings(&ncchctx, &ctx.usersettings); + ncch_process(&ncchctx, ctx.actions); + + break; + } + case FILETYPE_CIA: - process_cia(&ctx); - break; - } + { + cia_context ciactx; + + cia_init(&ciactx); + cia_set_file(&ciactx, ctx.infile); + cia_set_size(&ciactx, ctx.infilesize); + cia_set_usersettings(&ciactx, &ctx.usersettings); + cia_process(&ciactx, ctx.actions); + + break; + } + + case FILETYPE_EXHEADER: + { + exheader_context exheaderctx; + + exheader_init(&exheaderctx); + exheader_set_file(&exheaderctx, ctx.infile); + exheader_set_size(&exheaderctx, ctx.infilesize); + settings_set_ignore_programid(&ctx.usersettings, 1); + + exheader_set_usersettings(&exheaderctx, &ctx.usersettings); + exheader_process(&exheaderctx, ctx.actions); + + break; + } + + case FILETYPE_TMD: + { + tmd_context tmdctx; + + tmd_init(&tmdctx); + tmd_set_file(&tmdctx, ctx.infile); + tmd_set_size(&tmdctx, ctx.infilesize); + tmd_set_usersettings(&tmdctx, &ctx.usersettings); + tmd_process(&tmdctx, ctx.actions); + break; + } + + case FILETYPE_LZSS: + { + lzss_context lzssctx; -clean: + lzss_init(&lzssctx); + lzss_set_file(&lzssctx, ctx.infile); + lzss_set_size(&lzssctx, ctx.infilesize); + lzss_set_usersettings(&lzssctx, &ctx.usersettings); + lzss_process(&lzssctx, ctx.actions); + + break; + } + + + case FILETYPE_CWAV: + { + cwav_context cwavctx; + + cwav_init(&cwavctx); + cwav_set_file(&cwavctx, ctx.infile); + cwav_set_size(&cwavctx, ctx.infilesize); + cwav_set_usersettings(&cwavctx, &ctx.usersettings); + cwav_process(&cwavctx, ctx.actions); + + break; + } + + case FILETYPE_ROMFS: + { + romfs_context romfsctx; + + romfs_init(&romfsctx); + romfs_set_file(&romfsctx, ctx.infile); + romfs_set_size(&romfsctx, ctx.infilesize); + romfs_set_usersettings(&romfsctx, &ctx.usersettings); + romfs_process(&romfsctx, ctx.actions); + + break; + } + } + if (ctx.infile) fclose(ctx.infile); diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c new file mode 100644 index 0000000..40e4f61 --- /dev/null +++ b/ctrtool/ncch.c @@ -0,0 +1,582 @@ +#include +#include +#include +#include "types.h" +#include "ncch.h" +#include "utils.h" +#include "ctr.h" +#include "settings.h" + +static int programid_is_system(u8 programid[8]) +{ + u32 hiprogramid = getle32(programid+4); + + if ( ((hiprogramid >> 14) == 0x10) && (hiprogramid & 0x10) ) + return 1; + else + return 0; +} + + +void ncch_init(ncch_context* ctx) +{ + memset(ctx, 0, sizeof(ncch_context)); + exefs_init(&ctx->exefs); +} + +void ncch_set_usersettings(ncch_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void ncch_set_offset(ncch_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void ncch_set_size(ncch_context* ctx, u32 size) +{ + ctx->size = size; +} + +void ncch_set_file(ncch_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) +{ + u32 version = getle16(ctx->header.version); + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + u8* partitionid = ctx->header.partitionid; + u32 i; + u32 x = 0; + + memset(counter, 0, 16); + + if (version == 2 || version == 0) + { + for(i=0; i<8; i++) + counter[i] = partitionid[7-i]; + counter[8] = type; + } + else if (version == 1) + { + if (type == NCCHTYPE_EXHEADER) + x = 0x200; + else if (type == NCCHTYPE_EXEFS) + x = getle32(ctx->header.exefsoffset) * mediaunitsize; + else if (type == NCCHTYPE_ROMFS) + x = getle32(ctx->header.romfsoffset) * mediaunitsize; + + for(i=0; i<8; i++) + counter[i] = partitionid[i]; + for(i=0; i<4; i++) + counter[12+i] = x>>((3-i)*8); + } +} + + + +int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) +{ + u32 offset = 0; + u32 size = 0; + u8 counter[16]; + + + switch(type) + { + case NCCHTYPE_EXEFS: + { + offset = ncch_get_exefs_offset(ctx); + size = ncch_get_exefs_size(ctx); + } + break; + + case NCCHTYPE_ROMFS: + { + offset = ncch_get_romfs_offset(ctx); + size = ncch_get_romfs_size(ctx); + } + break; + + case NCCHTYPE_EXHEADER: + { + offset = ncch_get_exheader_offset(ctx); + size = ncch_get_exheader_size(ctx) * 2; + } + break; + + default: + { + fprintf(stderr, "Error invalid NCCH type\n"); + goto clean; + } + break; + } + + ctx->extractsize = size; + ctx->extractflags = flags; + fseek(ctx->file, offset, SEEK_SET); + ncch_get_counter(ctx, counter, type); + ctr_init_counter(&ctx->aes, ctx->key, counter); + + return 1; + +clean: + return 0; +} + +int ncch_extract_buffer(ncch_context* ctx, u8* buffer, u32 buffersize, u32* outsize) +{ + u32 max = buffersize; + + if (max > ctx->extractsize) + max = ctx->extractsize; + + *outsize = max; + + if (ctx->extractsize) + { + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, buffer, buffer, max); + + ctx->extractsize -= max; + } + + return 1; + +clean: + return 0; +} + +void ncch_save(ncch_context* ctx, u32 type, u32 flags) +{ + FILE* fout = 0; + filepath* path = 0; + u8 buffer[16*1024]; + + + if (0 == ncch_extract_prepare(ctx, type, flags)) + goto clean; + + switch(type) + { + case NCCHTYPE_EXEFS: path = settings_get_exefs_path(ctx->usersettings); break; + case NCCHTYPE_ROMFS: path = settings_get_romfs_path(ctx->usersettings); break; + case NCCHTYPE_EXHEADER: path = settings_get_exheader_path(ctx->usersettings); break; + } + + if (path == 0 || path->valid == 0) + goto clean; + + fout = fopen(path->pathname, "wb"); + if (0 == fout) + { + fprintf(stdout, "Error opening out file %s\n", path->pathname); + goto clean; + } + + switch(type) + { + case NCCHTYPE_EXEFS: fprintf(stdout, "Saving ExeFS...\n"); break; + case NCCHTYPE_ROMFS: fprintf(stdout, "Saving RomFS...\n"); break; + case NCCHTYPE_EXHEADER: fprintf(stdout, "Saving Extended Header...\n"); break; + } + + while(1) + { + u32 max; + + if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &max)) + goto clean; + + if (max == 0) + break; + + if (max != fwrite(buffer, 1, max, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + } +clean: + if (fout) + fclose(fout); + return; +} + +void ncch_verify(ncch_context* ctx, u32 flags) +{ + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + u32 exefshashregionsize = getle32(ctx->header.exefshashregionsize) * mediaunitsize; + u32 romfshashregionsize = getle32(ctx->header.romfshashregionsize) * mediaunitsize; + u32 exheaderhashregionsize = getle32(ctx->header.extendedheadersize); + u8* exefshashregion = 0; + u8* romfshashregion = 0; + u8* exheaderhashregion = 0; + rsakey2048 ncchrsakey; + + if (exefshashregionsize >= SIZE_128MB || romfshashregionsize >= SIZE_128MB || exheaderhashregionsize >= SIZE_128MB) + goto clean; + + exefshashregion = malloc(exefshashregionsize); + romfshashregion = malloc(romfshashregionsize); + exheaderhashregion = malloc(exheaderhashregionsize); + + + if (ctx->usersettings) + { + if ( (ctx->header.flags[5] & 3) == 1) + ctx->headersigcheck = ncch_signature_verify(ctx, &ctx->usersettings->keys.ncchrsakey); + else + { + ctr_rsa_init_key_pubmodulus(&ncchrsakey, ctx->exheader.header.accessdesc.ncchpubkeymodulus); + ctx->headersigcheck = ncch_signature_verify(ctx, &ncchrsakey); + } + } + + if (exefshashregionsize) + { + if (0 == ncch_extract_prepare(ctx, NCCHTYPE_EXEFS, flags)) + goto clean; + if (0 == ncch_extract_buffer(ctx, exefshashregion, exefshashregionsize, &exefshashregionsize)) + goto clean; + ctx->exefshashcheck = ctr_sha_256_verify(exefshashregion, exefshashregionsize, ctx->header.exefssuperblockhash); + } + if (romfshashregionsize) + { + if (0 == ncch_extract_prepare(ctx, NCCHTYPE_ROMFS, flags)) + goto clean; + if (0 == ncch_extract_buffer(ctx, romfshashregion, romfshashregionsize, &romfshashregionsize)) + goto clean; + ctx->romfshashcheck = ctr_sha_256_verify(romfshashregion, romfshashregionsize, ctx->header.romfssuperblockhash); + } + if (exheaderhashregionsize) + { + if (0 == ncch_extract_prepare(ctx, NCCHTYPE_EXHEADER, flags)) + goto clean; + if (0 == ncch_extract_buffer(ctx, exheaderhashregion, exheaderhashregionsize, &exheaderhashregionsize)) + goto clean; + ctx->exheaderhashcheck = ctr_sha_256_verify(exheaderhashregion, exheaderhashregionsize, ctx->header.extendedheaderhash); + } + + free(exefshashregion); + free(romfshashregion); + free(exheaderhashregion); +clean: + return; +} + + +void ncch_process(ncch_context* ctx, u32 actions) +{ + u8 exheadercounter[16]; + u8 exefscounter[16]; + int result = 1; + + + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, 0x200, ctx->file); + + if (getle32(ctx->header.magic) != MAGIC_NCCH) + { + fprintf(stdout, "Error, NCCH segment corrupted\n"); + return; + } + + ncch_determine_key(ctx, actions); + + ncch_get_counter(ctx, exheadercounter, NCCHTYPE_EXHEADER); + ncch_get_counter(ctx, exefscounter, NCCHTYPE_EXEFS); + + + exheader_set_file(&ctx->exheader, ctx->file); + exheader_set_offset(&ctx->exheader, ncch_get_exheader_offset(ctx) ); + exheader_set_size(&ctx->exheader, ncch_get_exheader_size(ctx) ); + exheader_set_usersettings(&ctx->exheader, ctx->usersettings); + exheader_set_partitionid(&ctx->exheader, ctx->header.partitionid); + exheader_set_programid(&ctx->exheader, ctx->header.programid); + exheader_set_counter(&ctx->exheader, exheadercounter); + exheader_set_key(&ctx->exheader, ctx->key); + exheader_set_encrypted(&ctx->exheader, ctx->encrypted); + + exefs_set_file(&ctx->exefs, ctx->file); + exefs_set_offset(&ctx->exefs, ncch_get_exefs_offset(ctx) ); + exefs_set_size(&ctx->exefs, ncch_get_exefs_size(ctx) ); + exefs_set_partitionid(&ctx->exefs, ctx->header.partitionid); + exefs_set_usersettings(&ctx->exefs, ctx->usersettings); + exefs_set_counter(&ctx->exefs, exefscounter); + exefs_set_key(&ctx->exefs, ctx->key); + exefs_set_encrypted(&ctx->exefs, ctx->encrypted); + + exheader_read(&ctx->exheader, actions); + + + if (actions & VerifyFlag) + ncch_verify(ctx, actions); + + if (actions & InfoFlag) + ncch_print(ctx); + + if (actions & ExtractFlag) + { + ncch_save(ctx, NCCHTYPE_EXEFS, actions); + ncch_save(ctx, NCCHTYPE_ROMFS, actions); + ncch_save(ctx, NCCHTYPE_EXHEADER, actions); + } + + + if (result && ncch_get_exheader_size(ctx)) + { + if (!exheader_programid_valid(&ctx->exheader)) + return; + + result = exheader_process(&ctx->exheader, actions); + } + + if (result && ncch_get_exheader_size(ctx)) + { + exefs_set_compressedflag(&ctx->exefs, exheader_get_compressedflag(&ctx->exheader)); + exefs_process(&ctx->exefs, actions); + } +} + +int ncch_signature_verify(ncch_context* ctx, rsakey2048* key) +{ + u8 hash[0x20]; + + ctr_sha_256(ctx->header.magic, 0x100, hash); + return ctr_rsa_verify_hash(ctx->header.signature, hash, key); +} + + +u32 ncch_get_exefs_offset(ncch_context* ctx) +{ + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + return ctx->offset + getle32(ctx->header.exefsoffset) * mediaunitsize; +} + +u32 ncch_get_exefs_size(ncch_context* ctx) +{ + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + return getle32(ctx->header.exefssize) * mediaunitsize; +} + +u32 ncch_get_romfs_offset(ncch_context* ctx) +{ + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + return ctx->offset + getle32(ctx->header.romfsoffset) * mediaunitsize; +} + +u32 ncch_get_romfs_size(ncch_context* ctx) +{ + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + return getle32(ctx->header.romfssize) * mediaunitsize; +} + +u32 ncch_get_exheader_offset(ncch_context* ctx) +{ + return ctx->offset + 0x200; +} + +u32 ncch_get_exheader_size(ncch_context* ctx) +{ + return getle32(ctx->header.extendedheadersize); +} + +u32 ncch_get_mediaunit_size(ncch_context* ctx) +{ + unsigned int mediaunitsize = settings_get_mediaunit_size(ctx->usersettings); + + if (mediaunitsize == 0) + { + unsigned short version = getle16(ctx->header.version); + if (version == 1) + mediaunitsize = 1; + else if (version == 2 || version == 0) + mediaunitsize = 1 << (ctx->header.flags[6] + 9); + } + + return mediaunitsize; +} + + +void ncch_determine_key(ncch_context* ctx, u32 actions) +{ + exheader_header exheader; + u8* key = settings_get_ncch_key(ctx->usersettings); + ctr_ncchheader* header = &ctx->header; + + ctx->encrypted = 0; + memset(ctx->key, 0, 0x10); + + if (actions & PlainFlag) + { + ctx->encrypted = 0; + } + else if (key != 0) + { + ctx->encrypted = 1; + memcpy(ctx->key, key, 0x10); + } + else + { + // No explicit NCCH key defined, so we try to decide + + + // Firstly, check if the NCCH is already decrypted, by reading the programid in the exheader + // Otherwise, use determination rules + fseek(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); + memset(&exheader, 0, sizeof(exheader)); + fread(&exheader, 1, sizeof(exheader), ctx->file); + + if (!memcmp(exheader.arm11systemlocalcaps.programid, ctx->header.programid, 8)) + { + // program id's match, so it's probably not encrypted + ctx->encrypted = 0; + } + else if (header->flags[7] & 4) + { + ctx->encrypted = 0; // not encrypted + } + else if (header->flags[7] & 1) + { + if (programid_is_system(header->programid)) + { + // fixed system key + ctx->encrypted = 1; + key = settings_get_ncch_fixedsystemkey(ctx->usersettings); + if (!key) + fprintf(stdout, "Warning, could not read system fixed key.\n"); + else + memcpy(ctx->key, key, 0x10); + } + else + { + // null key + ctx->encrypted = 1; + memset(ctx->key, 0, 0x10); + } + } + else + { + // secure key (cannot decrypt!) + fprintf(stdout, "Warning, could not read secure key.\n"); + ctx->encrypted = 1; + memset(ctx->key, 0, 0x10); + } + } +} + +static const char* formtypetostring(unsigned char flags) +{ + unsigned char formtype = flags & 3; + + switch(formtype) + { + case 0: return "Not assigned"; + case 1: return "Simple content"; + case 2: return "Executable content without RomFS"; + case 3: return "Executable content"; + default: return "Unknown"; + } +} + +static const char* contenttypetostring(unsigned char flags) +{ + unsigned char contenttype = flags>>2; + + switch(contenttype) + { + case 0: return "Application"; + case 1: return "System Update"; + case 2: return "Manual"; + case 3: return "Child"; + case 4: return "Trial"; + default: return "Unknown"; + } +} + + + +void ncch_print(ncch_context* ctx) +{ + char magic[5]; + char productcode[0x11]; + ctr_ncchheader *header = &ctx->header; + u32 offset = ctx->offset; + u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + + + fprintf(stdout, "\nNCCH:\n"); + memcpy(magic, header->magic, 4); + magic[4] = 0; + memcpy(productcode, header->productcode, 0x10); + productcode[0x10] = 0; + + fprintf(stdout, "Header: %s\n", magic); + if (ctx->headersigcheck == Unchecked) + memdump(stdout, "Signature: ", header->signature, 0x100); + else if (ctx->headersigcheck == Good) + memdump(stdout, "Signature (GOOD): ", header->signature, 0x100); + else + memdump(stdout, "Signature (FAIL): ", header->signature, 0x100); + fprintf(stdout, "Content size: 0x%08x\n", getle32(header->contentsize)*mediaunitsize); + fprintf(stdout, "Partition id: %016llx\n", getle64(header->partitionid)); + fprintf(stdout, "Maker code: %04x\n", getle16(header->makercode)); + fprintf(stdout, "Version: %04x\n", getle16(header->version)); + fprintf(stdout, "Program id: %016llx\n", getle64(header->programid)); + fprintf(stdout, "Temp flag: %02x\n", header->tempflag); + fprintf(stdout, "Product code: %s\n", productcode); + fprintf(stdout, "Exheader size: %08x\n", getle32(header->extendedheadersize)); + if (ctx->exheaderhashcheck == Unchecked) + memdump(stdout, "Exheader hash: ", header->extendedheaderhash, 0x20); + else if (ctx->exheaderhashcheck == Good) + memdump(stdout, "Exheader hash (GOOD): ", header->extendedheaderhash, 0x20); + else + memdump(stdout, "Exheader hash (FAIL): ", header->extendedheaderhash, 0x20); + fprintf(stdout, "Flags: %016llx\n", getle64(header->flags)); + fprintf(stdout, " > Mediaunit size: 0x%x\n", mediaunitsize); + if (header->flags[7] & 4) + fprintf(stdout, " > Crypto key: None\n"); + else if (header->flags[7] & 1) + fprintf(stdout, " > Crypto key: %s\n", programid_is_system(header->programid)? "Fixed":"Zeros"); + else + fprintf(stdout, " > Crypto key: Secure\n"); + fprintf(stdout, " > Form type: %s\n", formtypetostring(header->flags[5])); + fprintf(stdout, " > Content type: %s\n", contenttypetostring(header->flags[5])); + if (header->flags[4] & 1) + fprintf(stdout, " > Content platform: CTR\n"); + if (header->flags[7] & 2) + fprintf(stdout, " > No RomFS mount\n"); + + + fprintf(stdout, "Plain region offset: 0x%08x\n", getle32(header->plainregionsize)? offset+getle32(header->plainregionoffset)*mediaunitsize : 0); + fprintf(stdout, "Plain region size: 0x%08x\n", getle32(header->plainregionsize)*mediaunitsize); + fprintf(stdout, "ExeFS offset: 0x%08x\n", getle32(header->exefssize)? offset+getle32(header->exefsoffset)*mediaunitsize : 0); + fprintf(stdout, "ExeFS size: 0x%08x\n", getle32(header->exefssize)*mediaunitsize); + fprintf(stdout, "ExeFS hash region size: 0x%08x\n", getle32(header->exefshashregionsize)*mediaunitsize); + fprintf(stdout, "RomFS offset: 0x%08x\n", getle32(header->romfssize)? offset+getle32(header->romfsoffset)*mediaunitsize : 0); + fprintf(stdout, "RomFS size: 0x%08x\n", getle32(header->romfssize)*mediaunitsize); + fprintf(stdout, "RomFS hash region size: 0x%08x\n", getle32(header->romfshashregionsize)*mediaunitsize); + if (ctx->exefshashcheck == Unchecked) + memdump(stdout, "ExeFS Hash: ", header->exefssuperblockhash, 0x20); + else if (ctx->exefshashcheck == Good) + memdump(stdout, "ExeFS Hash (GOOD): ", header->exefssuperblockhash, 0x20); + else + memdump(stdout, "ExeFS Hash (FAIL): ", header->exefssuperblockhash, 0x20); + if (ctx->romfshashcheck == Unchecked) + memdump(stdout, "RomFS Hash: ", header->romfssuperblockhash, 0x20); + else if (ctx->romfshashcheck == Good) + memdump(stdout, "RomFS Hash (GOOD): ", header->romfssuperblockhash, 0x20); + else + memdump(stdout, "RomFS Hash (FAIL): ", header->romfssuperblockhash, 0x20); +} diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h new file mode 100644 index 0000000..3d15dbf --- /dev/null +++ b/ctrtool/ncch.h @@ -0,0 +1,94 @@ +#ifndef _NCCH_H_ +#define _NCCH_H_ + +#include +#include "types.h" +#include "keyset.h" +#include "filepath.h" +#include "ctr.h" +#include "exefs.h" +#include "exheader.h" +#include "settings.h" + +typedef enum +{ + NCCHTYPE_EXHEADER = 1, + NCCHTYPE_EXEFS = 2, + NCCHTYPE_ROMFS = 3, +} ctr_ncchtypes; + +typedef struct +{ + u8 signature[0x100]; + u8 magic[4]; + u8 contentsize[4]; + u8 partitionid[8]; + u8 makercode[2]; + u8 version[2]; + u8 reserved0[4]; + u8 programid[8]; + u8 tempflag; + u8 reserved1[0x2f]; + u8 productcode[0x10]; + u8 extendedheaderhash[0x20]; + u8 extendedheadersize[4]; + u8 reserved2[4]; + u8 flags[8]; + u8 plainregionoffset[4]; + u8 plainregionsize[4]; + u8 reserved3[8]; + u8 exefsoffset[4]; + u8 exefssize[4]; + u8 exefshashregionsize[4]; + u8 reserved4[4]; + u8 romfsoffset[4]; + u8 romfssize[4]; + u8 romfshashregionsize[4]; + u8 reserved5[4]; + u8 exefssuperblockhash[0x20]; + u8 romfssuperblockhash[0x20]; +} ctr_ncchheader; + + +typedef struct +{ + FILE* file; + u8 key[16]; + u32 encrypted; + u32 offset; + u32 size; + settings* usersettings; + ctr_ncchheader header; + ctr_aes_context aes; + exefs_context exefs; + exheader_context exheader; + int exefshashcheck; + int romfshashcheck; + int exheaderhashcheck; + int headersigcheck; + u32 extractsize; + u32 extractflags; +} ncch_context; + +void ncch_init(ncch_context* ctx); +void ncch_process(ncch_context* ctx, u32 actions); +void ncch_set_offset(ncch_context* ctx, u32 offset); +void ncch_set_size(ncch_context* ctx, u32 size); +void ncch_set_file(ncch_context* ctx, FILE* file); +void ncch_set_usersettings(ncch_context* ctx, settings* usersettings); +u32 ncch_get_exefs_offset(ncch_context* ctx); +u32 ncch_get_exefs_size(ncch_context* ctx); +u32 ncch_get_romfs_offset(ncch_context* ctx); +u32 ncch_get_romfs_size(ncch_context* ctx); +u32 ncch_get_exheader_offset(ncch_context* ctx); +u32 ncch_get_exheader_size(ncch_context* ctx); +void ncch_print(ncch_context* ctx); +int ncch_signature_verify(ncch_context* ctx, rsakey2048* key); +void ncch_verify(ncch_context* ctx, u32 flags); +void ncch_save(ncch_context* ctx, u32 type, u32 flags); +int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags); +int ncch_extract_buffer(ncch_context* ctx, u8* buffer, u32 buffersize, u32* outsize); +u32 ncch_get_mediaunit_size(ncch_context* ctx); +void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type); +void ncch_determine_key(ncch_context* ctx, u32 actions); +#endif // _NCCH_H_ diff --git a/ctrtool/ncsd.c b/ctrtool/ncsd.c new file mode 100644 index 0000000..81c6b46 --- /dev/null +++ b/ctrtool/ncsd.c @@ -0,0 +1,128 @@ +#include +#include + +#include "types.h" +#include "ncsd.h" +#include "utils.h" +#include "ctr.h" + + +void ncsd_init(ncsd_context* ctx) +{ + memset(ctx, 0, sizeof(ncsd_context)); +} + +void ncsd_set_offset(ncsd_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void ncsd_set_file(ncsd_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void ncsd_set_size(ncsd_context* ctx, u32 size) +{ + ctx->size = size; +} + +void ncsd_set_usersettings(ncsd_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +int ncsd_signature_verify(const void* blob, rsakey2048* key) +{ + u8* message = (u8*)blob + 0x100; + u8* sig = (u8*)blob; + u8 hash[0x20]; + + ctr_sha_256(message, 0x100, hash); + return ctr_rsa_verify_hash(sig, hash, key); +} + +void ncsd_process(ncsd_context* ctx, u32 actions) +{ + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, 0x200, ctx->file); + + if (getle32(ctx->header.magic) != MAGIC_NCSD) + { + fprintf(stdout, "Error, NCSD segment corrupted\n"); + return; + } + + + if (actions & VerifyFlag) + { + if (ctx->usersettings) + ctx->headersigcheck = ncsd_signature_verify(&ctx->header, &ctx->usersettings->keys.ncsdrsakey); + } + + if (actions & InfoFlag) + ncsd_print(ctx); + + ncch_set_file(&ctx->ncch, ctx->file); + ncch_set_offset(&ctx->ncch, 0x4000); + ncch_set_size(&ctx->ncch, ctx->size - 0x4000); + ncch_set_usersettings(&ctx->ncch, ctx->usersettings); + ncch_process(&ctx->ncch, actions); +} + +unsigned int ncsd_get_mediaunit_size(ncsd_context* ctx) +{ + unsigned int mediaunitsize = settings_get_mediaunit_size(ctx->usersettings); + + if (mediaunitsize == 0) + mediaunitsize = 1<<(9+ctx->header.flags[6]); + + return mediaunitsize; +} + +void ncsd_print(ncsd_context* ctx) +{ + char magic[5]; + ctr_ncsdheader* header = &ctx->header; + unsigned int i; + unsigned int mediaunitsize = ncsd_get_mediaunit_size(ctx); + + + memcpy(magic, header->magic, 4); + magic[4] = 0; + + fprintf(stdout, "Header: %s\n", magic); + if (ctx->headersigcheck == Unchecked) + memdump(stdout, "Signature: ", header->signature, 0x100); + else if (ctx->headersigcheck == Good) + memdump(stdout, "Signature (GOOD): ", header->signature, 0x100); + else + memdump(stdout, "Signature (FAIL): ", header->signature, 0x100); + fprintf(stdout, "Media size: 0x%08x\n", getle32(header->mediasize)); + fprintf(stdout, "Media id: %016llx\n", getle64(header->mediaid)); + //memdump(stdout, "Partition FS type: ", header->partitionfstype, 8); + //memdump(stdout, "Partition crypt type: ", header->partitioncrypttype, 8); + //memdump(stdout, "Partition offset/size: ", header->partitionoffsetandsize, 0x40); + fprintf(stdout, "\n"); + for(i=0; i<8; i++) + { + u32 partitionoffset = header->partitiongeometry[i].offset * mediaunitsize; + u32 partitionsize = header->partitiongeometry[i].size * mediaunitsize; + + if (partitionsize != 0) + { + fprintf(stdout, "Partition %d \n", i); + memdump(stdout, " Id: ", header->partitionid+i*8, 8); + fprintf(stdout, " Area: 0x%08X-0x%08X\n", partitionoffset, partitionoffset+partitionsize); + fprintf(stdout, " Filesystem: %02X\n", header->partitionfstype[i]); + fprintf(stdout, " Encryption: %02X\n", header->partitioncrypttype[i]); + fprintf(stdout, "\n"); + } + } + memdump(stdout, "Extended header hash: ", header->extendedheaderhash, 0x20); + memdump(stdout, "Additional header size: ", header->additionalheadersize, 4); + memdump(stdout, "Sector zero offset: ", header->sectorzerooffset, 4); + memdump(stdout, "Flags: ", header->flags, 8); + fprintf(stdout, " > Mediaunit size: 0x%X\n", mediaunitsize); + +} diff --git a/ctrtool/ncsd.h b/ctrtool/ncsd.h new file mode 100644 index 0000000..2b66ff5 --- /dev/null +++ b/ctrtool/ncsd.h @@ -0,0 +1,55 @@ +#ifndef _NCSD_H_ +#define _NCSD_H_ + +#include "types.h" +#include "keyset.h" +#include "settings.h" +#include "ncch.h" + +typedef struct +{ + u32 offset; + u32 size; +} ncsd_partition_geometry; + +typedef struct +{ + u8 signature[0x100]; + u8 magic[4]; + u8 mediasize[4]; + u8 mediaid[8]; + u8 partitionfstype[8]; + u8 partitioncrypttype[8]; + ncsd_partition_geometry partitiongeometry[8]; + u8 extendedheaderhash[0x20]; + u8 additionalheadersize[4]; + u8 sectorzerooffset[4]; + u8 flags[8]; + u8 partitionid[0x40]; + u8 reserved[0x30]; +} ctr_ncsdheader; + + +typedef struct +{ + FILE* file; + u32 offset; + u32 size; + ctr_ncsdheader header; + settings* usersettings; + int headersigcheck; + ncch_context ncch; +} ncsd_context; + + +void ncsd_init(ncsd_context* ctx); +void ncsd_set_offset(ncsd_context* ctx, u32 offset); +void ncsd_set_size(ncsd_context* ctx, u32 size); +void ncsd_set_file(ncsd_context* ctx, FILE* file); +void ncsd_set_usersettings(ncsd_context* ctx, settings* usersettings); +int ncsd_signature_verify(const void* blob, rsakey2048* key); +void ncsd_process(ncsd_context* ctx, u32 actions); +void ncsd_print(ncsd_context* ctx); +unsigned int ncsd_get_mediaunit_size(ncsd_context* ctx); + +#endif // _NCSD_H_ diff --git a/ctrtool/aes.c b/ctrtool/polarssl/aes.c similarity index 97% rename from ctrtool/aes.c rename to ctrtool/polarssl/aes.c index de0c781..45f74a6 100644 --- a/ctrtool/aes.c +++ b/ctrtool/polarssl/aes.c @@ -1,10 +1,12 @@ /* * FIPS-197 compliant AES implementation * - * Copyright (C) 2006-2009, Paul Bakker - * All rights reserved. + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker * - * Joined copyright on original XySSL code with: Christophe Devine + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,11 +29,12 @@ * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf */ -#define POLARSSL_AES_C +#include "polarssl/config.h" + #if defined(POLARSSL_AES_C) -#include "aes.h" -//#include "polarssl/padlock.h" +#include "polarssl/aes.h" +#include "polarssl/padlock.h" #include @@ -438,7 +441,7 @@ static void aes_gen_tables( void ) /* * AES key schedule (encryption) */ -int aes_setkey_enc( aes_context *ctx, unsigned char *key, int keysize ) +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ) { int i; unsigned long *RK; @@ -543,7 +546,7 @@ int aes_setkey_enc( aes_context *ctx, unsigned char *key, int keysize ) /* * AES key schedule (decryption) */ -int aes_setkey_dec( aes_context *ctx, unsigned char *key, int keysize ) +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize ) { int i, j; aes_context cty; @@ -646,9 +649,9 @@ int aes_setkey_dec( aes_context *ctx, unsigned char *key, int keysize ) /* * AES-ECB block encryption/decryption */ -void aes_crypt_ecb( aes_context *ctx, +int aes_crypt_ecb( aes_context *ctx, int mode, - unsigned char input[16], + const unsigned char input[16], unsigned char output[16] ) { int i; @@ -658,7 +661,11 @@ void aes_crypt_ecb( aes_context *ctx, if( padlock_supports( PADLOCK_ACE ) ) { if( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) - return; + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // } #endif @@ -742,26 +749,35 @@ void aes_crypt_ecb( aes_context *ctx, PUT_ULONG_LE( X1, output, 4 ); PUT_ULONG_LE( X2, output, 8 ); PUT_ULONG_LE( X3, output, 12 ); + + return( 0 ); } /* * AES-CBC buffer encryption/decryption */ -void aes_crypt_cbc( aes_context *ctx, +int aes_crypt_cbc( aes_context *ctx, int mode, int length, unsigned char iv[16], - unsigned char *input, + const unsigned char *input, unsigned char *output ) { int i; unsigned char temp[16]; + if( length % 16 ) + return( POLARSSL_ERR_AES_INVALID_INPUT_LENGTH ); + #if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) if( padlock_supports( PADLOCK_ACE ) ) { if( padlock_xcryptcbc( ctx, mode, length, iv, input, output ) == 0 ) - return; + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // } #endif @@ -797,17 +813,19 @@ void aes_crypt_cbc( aes_context *ctx, length -= 16; } } + + return( 0 ); } /* * AES-CFB128 buffer encryption/decryption */ -void aes_crypt_cfb128( aes_context *ctx, +int aes_crypt_cfb128( aes_context *ctx, int mode, int length, int *iv_off, unsigned char iv[16], - unsigned char *input, + const unsigned char *input, unsigned char *output ) { int c, n = *iv_off; @@ -840,6 +858,8 @@ void aes_crypt_cfb128( aes_context *ctx, } *iv_off = n; + + return( 0 ); } #if defined(POLARSSL_SELF_TEST) diff --git a/ctrtool/aes.h b/ctrtool/polarssl/aes.h similarity index 80% rename from ctrtool/aes.h rename to ctrtool/polarssl/aes.h index 5e18ab7..5576680 100644 --- a/ctrtool/aes.h +++ b/ctrtool/polarssl/aes.h @@ -1,10 +1,12 @@ /** * \file aes.h * - * Copyright (C) 2006-2009, Paul Bakker - * All rights reserved. + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker * - * Joined copyright on original XySSL code with: Christophe Devine + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +29,7 @@ #define AES_DECRYPT 0 #define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0800 +#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0810 /** * \brief AES context structure @@ -52,7 +55,7 @@ extern "C" { * * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH */ -int aes_setkey_enc( aes_context *ctx, unsigned char *key, int keysize ); +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ); /** * \brief AES key schedule (decryption) @@ -63,7 +66,7 @@ int aes_setkey_enc( aes_context *ctx, unsigned char *key, int keysize ); * * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH */ -int aes_setkey_dec( aes_context *ctx, unsigned char *key, int keysize ); +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize ); /** * \brief AES-ECB block encryption/decryption @@ -72,10 +75,12 @@ int aes_setkey_dec( aes_context *ctx, unsigned char *key, int keysize ); * \param mode AES_ENCRYPT or AES_DECRYPT * \param input 16-byte input block * \param output 16-byte output block + * + * \return 0 if successful */ -void aes_crypt_ecb( aes_context *ctx, +int aes_crypt_ecb( aes_context *ctx, int mode, - unsigned char input[16], + const unsigned char input[16], unsigned char output[16] ); /** @@ -89,12 +94,14 @@ void aes_crypt_ecb( aes_context *ctx, * \param iv initialization vector (updated after use) * \param input buffer holding the input data * \param output buffer holding the output data + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH */ -void aes_crypt_cbc( aes_context *ctx, +int aes_crypt_cbc( aes_context *ctx, int mode, int length, unsigned char iv[16], - unsigned char *input, + const unsigned char *input, unsigned char *output ); /** @@ -107,13 +114,15 @@ void aes_crypt_cbc( aes_context *ctx, * \param iv initialization vector (updated after use) * \param input buffer holding the input data * \param output buffer holding the output data + * + * \return 0 if successful */ -void aes_crypt_cfb128( aes_context *ctx, +int aes_crypt_cfb128( aes_context *ctx, int mode, int length, int *iv_off, unsigned char iv[16], - unsigned char *input, + const unsigned char *input, unsigned char *output ); /** diff --git a/ctrtool/polarssl/bignum.c b/ctrtool/polarssl/bignum.c new file mode 100644 index 0000000..78e9384 --- /dev/null +++ b/ctrtool/polarssl/bignum.c @@ -0,0 +1,2038 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_BIGNUM_C) + +#include "polarssl/bignum.h" +#include "polarssl/bn_mul.h" + +#include +#include +#include + +#define ciL ((int) sizeof(t_int)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one or more mpi + */ +void mpi_init( mpi *X, ... ) +{ + va_list args; + + va_start( args, X ); + + while( X != NULL ) + { + X->s = 1; + X->n = 0; + X->p = NULL; + + X = va_arg( args, mpi* ); + } + + va_end( args ); +} + +/* + * Unallocate one or more mpi + */ +void mpi_free( mpi *X, ... ) +{ + va_list args; + + va_start( args, X ); + + while( X != NULL ) + { + if( X->p != NULL ) + { + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; + + X = va_arg( args, mpi* ); + } + + va_end( args ); +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, int nblimbs ) +{ + t_int *p; + + if( X->n < nblimbs ) + { + if( ( p = (t_int *) malloc( nblimbs * ciL ) ) == NULL ) + return( 1 ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, const mpi *Y ) +{ + int ret, i; + + if( X == Y ) + return( 0 ); + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, int z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +int mpi_lsb( const mpi *X ) +{ + int i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < (int) biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +/* + * Return the number of most significant bits + */ +int mpi_msb( const mpi *X ) +{ + int i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL - 1; j >= 0; j-- ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j + 1 ); +} + +/* + * Return the total size in bytes + */ +int mpi_size( const mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_int *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_int) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, const char *s ) +{ + int ret, i, j, n, slen; + t_int d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T, NULL ); + + slen = strlen( s ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( slen << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = slen - 1, j = 0; i >= 0; i--, j++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < slen; i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + + if( X->s == 1 ) + { + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + else + { + MPI_CHK( mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mpi_free( &T, NULL ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_int r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( const mpi *X, int radix, char *s, int *slen ) +{ + int ret = 0, n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T, NULL ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c, i, j, k; + + for( i = X->n - 1, k = 0; i >= 0; i-- ) + { + for( j = ciL - 1; j >= 0; j-- ) + { + c = ( X->p[i] >> (j << 3) ) & 0xFF; + + if( c == 0 && k == 0 && (i + j) != 0 ) + continue; + + p += sprintf( p, "%02X", c ); + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + + if( T.s == -1 ) + T.s = 1; + + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T, NULL ); + + return( ret ); +} + +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_int d; + int slen; + char *p; + char s[1024]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ) +{ + int n, ret; + size_t slen; + size_t plen; + char s[2048]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (int *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, int buflen ) +{ + int ret, i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen - 1, j = 0; i >= n; i--, j++ ) + X->p[j / ciL] |= ((t_int) buf[i]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, int buflen ) +{ + int i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, int count ) +{ + int ret, i, v0, t1; + t_int r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * (int) biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n - 1; i >= v0; i-- ) + X->p[i] = X->p[i - v0]; + + for( ; i >= 0; i-- ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, int count ) +{ + int i, v0, v1; + t_int r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n - 1; i >= 0; i-- ) + { + r1 = X->p[i] << (biL - v1); + X->p[i] >>= v1; + X->p[i] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ) +{ + int i, j; + + for( i = X->n - 1; i >= 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = Y->n - 1; j >= 0; j-- ) + if( Y->p[j] != 0 ) + break; + + if( i < 0 && j < 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i >= 0; i-- ) + { + if( X->p[i] > Y->p[i] ) return( 1 ); + if( X->p[i] < Y->p[i] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ) +{ + int i, j; + + for( i = X->n - 1; i >= 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = Y->n - 1; j >= 0; j-- ) + if( Y->p[j] != 0 ) + break; + + if( i < 0 && j < 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -X->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i >= 0; i-- ) + { + if( X->p[i] > Y->p[i] ) return( X->s ); + if( X->p[i] < Y->p[i] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( const mpi *X, int z ) +{ + mpi Y; + t_int p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, i, j; + t_int *o, *p, c; + + if( X == B ) + { + const mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for( j = B->n - 1; j >= 0; j-- ) + if( B->p[j] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j + 1 ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i <= j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi substraction + */ +static void mpi_sub_hlp( int n, t_int *s, t_int *d ) +{ + int i; + t_int c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; i++; d++; + } +} + +/* + * Unsigned substraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ) +{ + mpi TB; + int ret, n; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB, NULL ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned substractions. + */ + X->s = 1; + + ret = 0; + + for( n = B->n - 1; n >= 0; n-- ) + if( B->p[n] != 0 ) + break; + + mpi_sub_hlp( n + 1, B->p, X->p ); + +cleanup: + + mpi_free( &TB, NULL ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed substraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, const mpi *A, int b ) +{ + mpi _B; + t_int p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed substraction: X = A - b + */ +int mpi_sub_int( mpi *X, const mpi *A, int b ) +{ + mpi _B; + t_int p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static void mpi_mul_hlp( int i, t_int *s, t_int *d, t_int b ) +{ + t_int c = 0, t = 0; + +#if defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif + + t++; + + do { + *d += c; c = ( *d < c ); d++; + } + while( c != 0 ); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, i, j; + mpi TA, TB; + + mpi_init( &TA, &TB, NULL ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n - 1; i >= 0; i-- ) + if( A->p[i] != 0 ) + break; + + for( j = B->n - 1; j >= 0; j-- ) + if( B->p[j] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j + 2 ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i++; j >= 0; j-- ) + mpi_mul_hlp( i, A->p, X->p + j, B->p[j] ); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB, &TA, NULL ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, const mpi *A, t_int b ) +{ + mpi _B; + t_int p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ) +{ + int ret, i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X, &Y, &Z, &T1, &T2, NULL ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < (int) biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + mpi_shift_l( &Y, biL * (n - t) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + mpi_sub_mpi( &X, &X, &Y ); + } + mpi_shift_r( &Y, biL * (n - t) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0; + else + { +#if defined(POLARSSL_HAVE_LONGLONG) + t_dbl r; + + r = (t_dbl) X.p[i] << biL; + r |= (t_dbl) X.p[i - 1]; + r /= Y.p[t]; + if( r > ((t_dbl) 1 << biL) - 1) + r = ((t_dbl) 1 << biL) - 1; + + Z.p[i - t - 1] = (t_int) r; +#else + /* + * __udiv_qrnnd_c, from gmp/longlong.h + */ + t_int q0, q1, r0, r1; + t_int d0, d1, d, m; + + d = Y.p[t]; + d0 = ( d << biH ) >> biH; + d1 = ( d >> biH ); + + q1 = X.p[i] / d1; + r1 = X.p[i] - d1 * q1; + r1 <<= biH; + r1 |= ( X.p[i - 1] >> biH ); + + m = q1 * d0; + if( r1 < m ) + { + q1--, r1 += d; + while( r1 >= d && r1 < m ) + q1--, r1 += d; + } + r1 -= m; + + q0 = r1 / d1; + r0 = r1 - d1 * q0; + r0 <<= biH; + r0 |= ( X.p[i - 1] << biH ) >> biH; + + m = q0 * d0; + if( r0 < m ) + { + q0--, r0 += d; + while( r0 >= d && r0 < m ) + q0--, r0 += d; + } + r0 -= m; + + Z.p[i - t - 1] = ( q1 << biH ) | q0; +#endif + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + if( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + mpi_copy( Q, &Z ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + mpi_shift_r( &X, k ); + mpi_copy( R, &X ); + + R->s = A->s; + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X, &Y, &Z, &T1, &T2, NULL ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + * + * Returns 0 if successful + * 1 if memory allocation failed + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, int b ) +{ + mpi _B; + t_int p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + + if( mpi_cmp_int( B, 0 ) < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_int *r, const mpi *A, int b ) +{ + int i; + t_int x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n - 1, y = 0; i >= 0; i-- ) + { + x = A->p[i]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_int *mm, const mpi *N ) +{ + t_int x, m0 = N->p[0]; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + x *= ( 2 - ( m0 * x ) ); + + if( biL >= 16 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 32 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 64 ) x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + */ +static void mpi_montmul( mpi *A, const mpi *B, const mpi *N, t_int mm, const mpi *T ) +{ + int i, n, m; + t_int u0, u1, *d; + + memset( T->p, 0, T->n * ciL ); + + d = T->p; + n = N->n; + m = ( B->n < n ) ? B->n : n; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = A->p[i]; + u1 = ( d[0] + u0 * B->p[0] ) * mm; + + mpi_mul_hlp( m, B->p, d, u0 ); + mpi_mul_hlp( n, N->p, d, u1 ); + + *d++ = u0; d[n + 1] = 0; + } + + memcpy( A->p, d, (n + 1) * ciL ); + + if( mpi_cmp_abs( A, N ) >= 0 ) + mpi_sub_hlp( n, N->p, A->p ); + else + /* prevent timing attacks */ + mpi_sub_hlp( n, A->p, T->p ); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + */ +static void mpi_montred( mpi *A, const mpi *N, t_int mm, const mpi *T ) +{ + t_int z = 1; + mpi U; + + U.n = U.s = z; + U.p = &z; + + mpi_montmul( A, &U, N, mm, T ); +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ) +{ + int ret, i, j, wsize, wbits; + int bufsize, nblimbs, nbits; + t_int ei, mm, state; + mpi RR, T, W[64]; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + mpi_init( &RR, &T, NULL ); + memset( W, 0, sizeof( W ) ); + + i = mpi_msb( E ); + + wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + + j = N->n + 1; + MPI_CHK( mpi_grow( X, j ) ); + MPI_CHK( mpi_grow( &W[1], j ) ); + MPI_CHK( mpi_grow( &T, j * 2 ) ); + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + MPI_CHK( mpi_lset( &RR, 1 ) ); + MPI_CHK( mpi_shift_l( &RR, N->n * 2 * biL ) ); + MPI_CHK( mpi_mod_mpi( &RR, &RR, N ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + } + else + memcpy( &RR, _RR, sizeof( mpi ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) + mpi_mod_mpi( &W[1], A, N ); + else mpi_copy( &W[1], A ); + + mpi_montmul( &W[1], &RR, N, mm, &T ); + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + MPI_CHK( mpi_copy( X, &RR ) ); + mpi_montred( X, N, mm, &T ); + + if( wsize > 1 ) + { + /* + * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + */ + j = 1 << (wsize - 1); + + MPI_CHK( mpi_grow( &W[j], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[j], &W[1] ) ); + + for( i = 0; i < wsize - 1; i++ ) + mpi_montmul( &W[j], &W[j], N, mm, &T ); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = j + 1; i < (1 << wsize); i++ ) + { + MPI_CHK( mpi_grow( &W[i], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[i], &W[i - 1] ) ); + + mpi_montmul( &W[i], &W[1], N, mm, &T ); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs-- == 0 ) + break; + + bufsize = sizeof( t_int ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montmul( X, X, N, mm, &T ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= (ei << (wsize - nbits)); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montmul( X, X, N, mm, &T ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( X, &W[wbits], N, mm, &T ); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montmul( X, X, N, mm, &T ); + + wbits <<= 1; + + if( (wbits & (1 << wsize)) != 0 ) + mpi_montmul( X, &W[1], N, mm, &T ); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( X, N, mm, &T ); + +cleanup: + + for( i = (1 << (wsize - 1)); i < (1 << wsize); i++ ) + mpi_free( &W[i], NULL ); + + if( _RR != NULL ) + mpi_free( &W[1], &T, NULL ); + else mpi_free( &W[1], &T, &RR, NULL ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ) +{ + int ret, lz, lzt; + mpi TG, TA, TB; + + mpi_init( &TG, &TA, &TB, NULL ); + + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + lz = mpi_lsb( &TA ); + lzt = mpi_lsb( &TB ); + + if ( lzt < lz ) + lz = lzt; + + MPI_CHK( mpi_shift_r( &TA, lz ) ); + MPI_CHK( mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + MPI_CHK( mpi_shift_r( &TA, mpi_lsb( &TA ) ) ); + MPI_CHK( mpi_shift_r( &TB, mpi_lsb( &TB ) ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_shift_l( &TB, lz ) ); + MPI_CHK( mpi_copy( G, &TB ) ); + +cleanup: + + mpi_free( &TB, &TA, &TG, NULL ); + + return( ret ); +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA, &TU, &U1, &U2, &G, + &TB, &TV, &V1, &V2, NULL ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &V2, &V1, &TV, &TB, &G, + &U2, &U1, &TU, &TA, NULL ); + + return( ret ); +} + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Miller-Rabin primality test (HAC 4.24) + */ +int mpi_is_prime( mpi *X, int (*f_rng)(void *), void *p_rng ) +{ + int ret, i, j, n, s, xs; + mpi W, R, T, A, RR; + unsigned char *p; + + if( mpi_cmp_int( X, 0 ) == 0 || + mpi_cmp_int( X, 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + if( mpi_cmp_int( X, 2 ) == 0 ) + return( 0 ); + + mpi_init( &W, &R, &T, &A, &RR, NULL ); + + xs = X->s; X->s = 1; + + /* + * test trivial factors first + */ + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + for( i = 0; small_prime[i] > 0; i++ ) + { + t_int r; + + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 0 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + s = mpi_lsb( &W ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + + i = mpi_msb( X ); + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_grow( &A, X->n ) ); + + p = (unsigned char *) A.p; + for( j = 0; j < A.n * ciL; j++ ) + *p++ = (unsigned char) f_rng( p_rng ); + + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + + X->s = xs; + + mpi_free( &RR, &A, &T, &R, &W, NULL ); + + return( ret ); +} + +/* + * Prime number generation + */ +int mpi_gen_prime( mpi *X, int nbits, int dh_flag, + int (*f_rng)(void *), void *p_rng ) +{ + int ret, k, n; + unsigned char *p; + mpi Y; + + if( nbits < 3 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &Y, NULL ); + + n = BITS_TO_LIMBS( nbits ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + p = (unsigned char *) X->p; + for( k = 0; k < X->n * ciL; k++ ) + *p++ = (unsigned char) f_rng( p_rng ); + + k = mpi_msb( X ); + if( k < nbits ) MPI_CHK( mpi_shift_l( X, nbits - k ) ); + if( k > nbits ) MPI_CHK( mpi_shift_r( X, k - nbits ) ); + + X->p[0] |= 3; + + if( dh_flag == 0 ) + { + while( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) != 0 ) + { + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( X, X, 2 ) ); + } + } + else + { + MPI_CHK( mpi_sub_int( &Y, X, 1 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + + while( 1 ) + { + if( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) == 0 ) + { + if( ( ret = mpi_is_prime( &Y, f_rng, p_rng ) ) == 0 ) + break; + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + } + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( &Y, X, 1 ) ); + MPI_CHK( mpi_add_int( X, X, 2 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + } + } + +cleanup: + + mpi_free( &Y, NULL ); + + return( ret ); +} + +#endif + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A, &E, &N, &X, &Y, &U, &V, NULL ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + if( verbose != 0 ) + printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + printf( "failed at %d\n", i ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &V, &U, &Y, &X, &N, &E, &A, NULL ); + + if( verbose != 0 ) + printf( "\n" ); + + return( ret ); +} + +#endif + +#endif diff --git a/ctrtool/polarssl/bignum.h b/ctrtool/polarssl/bignum.h new file mode 100644 index 0000000..80399a2 --- /dev/null +++ b/ctrtool/polarssl/bignum.h @@ -0,0 +1,533 @@ +/** + * \file bignum.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR 0x0002 +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA 0x0004 +#define POLARSSL_ERR_MPI_INVALID_CHARACTER 0x0006 +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL 0x0008 +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE 0x000A +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO 0x000C +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE 0x000E + +#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef unsigned char t_int; +typedef unsigned short t_dbl; +#else +#if defined(POLARSSL_HAVE_INT16) +typedef unsigned short t_int; +typedef unsigned long t_dbl; +#else + typedef unsigned long t_int; + #if defined(_MSC_VER) && defined(_M_IX86) + typedef unsigned __int64 t_dbl; + #else + #if defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) + typedef unsigned int t_dbl __attribute__((mode(TI))); + #else + #if defined(POLARSSL_HAVE_LONGLONG) + typedef unsigned long long t_dbl; + #endif + #endif + #endif +#endif +#endif + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + int n; /*!< total # of limbs */ + t_int *p; /*!< pointer to limbs */ +} +mpi; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize one or more mpi + */ +void mpi_init( mpi *X, ... ); + +/** + * \brief Unallocate one or more mpi + */ +void mpi_free( mpi *X, ... ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_grow( mpi *X, int nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_copy( mpi *X, const mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_lset( mpi *X, int z ); + +/** + * \brief Return the number of least significant bits + * + * \param X MPI to use + */ +int mpi_lsb( const mpi *X ); + +/** + * \brief Return the number of most significant bits + * + * \param X MPI to use + */ +int mpi_msb( const mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +int mpi_size( const mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param s String buffer + * \param slen String buffer size + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code. + * *slen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( const mpi *X, int radix, char *s, int *slen ); + +/** + * \brief Read X from an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ); + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, int buflen ); + +/** + * \brief Export X into unsigned binary data, big endian + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, int buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_shift_l( mpi *X, int count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_shift_r( mpi *X, int count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( const mpi *X, int z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Unsigned substraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed substraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_add_int( mpi *X, const mpi *A, int b ); + +/** + * \brief Signed substraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_sub_int( mpi *X, const mpi *A, int b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * Note: b is an unsigned integer type, thus + * Negative values of b are ignored. + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to multiply with + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_mul_int( mpi *X, const mpi *A, t_int b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, int b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination t_int + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mpi_mod_int( t_int *r, const mpi *A, int b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ); + +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mpi_is_prime( mpi *X, int (*f_rng)(void *), void *p_rng ); + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mpi_gen_prime( mpi *X, int nbits, int dh_flag, + int (*f_rng)(void *), void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/ctrtool/polarssl/bn_mul.h b/ctrtool/polarssl/bn_mul.h new file mode 100644 index 0000000..a73d5fb --- /dev/null +++ b/ctrtool/polarssl/bn_mul.h @@ -0,0 +1,736 @@ +/** + * \file bn_mul.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "polarssl/config.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) + +#define MULADDC_INIT \ + asm( "ld %0, %%o0 " :: "m" (s)); \ + asm( "ld %0, %%o1 " :: "m" (d)); \ + asm( "ld %0, %%o2 " :: "m" (c)); \ + asm( "ld %0, %%o3 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ld [%o0], %o4 " ); \ + asm( "inc 4, %o0 " ); \ + asm( "ld [%o1], %o5 " ); \ + asm( "umul %o3, %o4, %o4 " ); \ + asm( "addcc %o4, %o2, %o4 " ); \ + asm( "rd %y, %g1 " ); \ + asm( "addx %g1, 0, %g1 " ); \ + asm( "addcc %o4, %o5, %o4 " ); \ + asm( "st %o4, [%o1] " ); \ + asm( "addx %g1, 0, %o2 " ); \ + asm( "inc 4, %o1 " ); + +#define MULADDC_STOP \ + asm( "st %%o2, %0 " : "=m" (c)); \ + asm( "st %%o1, %0 " : "=m" (d)); \ + asm( "st %%o0, %0 " : "=m" (s) :: \ + "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#define MULADDC_INIT \ + asm( "ldr r0, %0 " :: "m" (s)); \ + asm( "ldr r1, %0 " :: "m" (d)); \ + asm( "ldr r2, %0 " :: "m" (c)); \ + asm( "ldr r3, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldr r4, [r0], #4 " ); \ + asm( "mov r5, #0 " ); \ + asm( "ldr r6, [r1] " ); \ + asm( "umlal r2, r5, r3, r4 " ); \ + asm( "adds r7, r6, r2 " ); \ + asm( "adc r2, r5, #0 " ); \ + asm( "str r7, [r1], #4 " ); + +#define MULADDC_STOP \ + asm( "str r2, %0 " : "=m" (c)); \ + asm( "str r1, %0 " : "=m" (d)); \ + asm( "str r0, %0 " : "=m" (s) :: \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" ); + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_LONGLONG) + +#define MULADDC_INIT \ +{ \ + t_dbl r; \ + t_int r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_dbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_int s0, s1, b0, b1; \ + t_int r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/ctrtool/polarssl/config.h b/ctrtool/polarssl/config.h new file mode 100644 index 0000000..267e2dd --- /dev/null +++ b/ctrtool/polarssl/config.h @@ -0,0 +1,336 @@ +/** + * \file config.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +#ifndef POLARSSL_CONFIG_H +#define POLARSSL_CONFIG_H + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/* + * Uncomment if native integers are 8-bit wide. + * +#define POLARSSL_HAVE_INT8 + */ + +/* + * Uncomment if native integers are 16-bit wide. + * +#define POLARSSL_HAVE_INT16 + */ + +/* + * Uncomment if the compiler supports long long. + * +#define POLARSSL_HAVE_LONGLONG + */ + +/* + * Uncomment to enable the use of assembly code. + * + * Requires support for asm() in compiler. + * + * Used in: + * library/timing.c + * library/padlock.c + * include/polarssl/bn_mul.h + * + */ +//#define POLARSSL_HAVE_ASM + +/* + * Uncomment if the CPU supports SSE2 (IA-32 specific). + * +#define POLARSSL_HAVE_SSE2 + */ + +/* + * Enable all SSL/TLS debugging messages. + */ +#define POLARSSL_DEBUG_MSG + +/* + * Enable the checkup functions (*_self_test). + */ +//#define POLARSSL_SELF_TEST + +/* + * Enable run-time version information functions + */ +#define POLARSSL_VERSION_C + +/* + * Enable the prime-number generation code. + */ +#define POLARSSL_GENPRIME + +/* + * Uncomment this macro to store the AES tables in ROM. + * +#define POLARSSL_AES_ROM_TABLES + */ + +/* + * Module: library/aes.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites: + * SSL_RSA_AES_128_SHA + * SSL_RSA_AES_256_SHA + * SSL_EDH_RSA_AES_256_SHA + */ +#define POLARSSL_AES_C + +/* + * Module: library/arc4.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites: + * SSL_RSA_RC4_128_MD5 + * SSL_RSA_RC4_128_SHA + */ +#define POLARSSL_ARC4_C + +/* + * Module: library/base64.c + * Caller: library/x509parse.c + * + * This module is required for X.509 support. + */ +#define POLARSSL_BASE64_C + +/* + * Module: library/bignum.c + * Caller: library/dhm.c + * library/rsa.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for RSA and DHM support. + */ +#define POLARSSL_BIGNUM_C + +/* + * Module: library/camellia.c + * Caller: library/ssl_tls.c + * + * This module enabled the following cipher suites: + * SSL_RSA_CAMELLIA_128_SHA + * SSL_RSA_CAMELLIA_256_SHA + * SSL_EDH_RSA_CAMELLIA_256_SHA + */ +#define POLARSSL_CAMELLIA_C + +/* + * Module: library/certs.c + * Caller: + * + * This module is used for testing (ssl_client/server). + */ +#define POLARSSL_CERTS_C + +/* + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define POLARSSL_DEBUG_C + +/* + * Module: library/des.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites: + * SSL_RSA_DES_168_SHA + * SSL_EDH_RSA_DES_168_SHA + */ +#define POLARSSL_DES_C + +/* + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module enables the following ciphersuites: + * SSL_EDH_RSA_DES_168_SHA + * SSL_EDH_RSA_AES_256_SHA + * SSL_EDH_RSA_CAMELLIA_256_SHA + */ +#define POLARSSL_DHM_C + +/* + * Module: library/havege.c + * Caller: + * + * This module enables the HAVEGE random number generator. + */ +#define POLARSSL_HAVEGE_C + +/* + * Module: library/md2.c + * Caller: library/x509parse.c + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + * +#define POLARSSL_MD2_C + */ + +/* + * Module: library/md4.c + * Caller: library/x509parse.c + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + * +#define POLARSSL_MD4_C + */ + +/* + * Module: library/md5.c + * Caller: library/ssl_tls.c + * library/x509parse.c + * + * This module is required for SSL/TLS and X.509. + */ +#define POLARSSL_MD5_C + +/* + * Module: library/net.c + * Caller: + * + * This module provides TCP/IP networking routines. + */ +#define POLARSSL_NET_C + +/* + * Module: library/padlock.c + * Caller: library/aes.c + * + * This modules adds support for the VIA PadLock on x86. + */ +#define POLARSSL_PADLOCK_C + +/* + * Module: library/rsa.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * This module is required for SSL/TLS and MD5-signed certificates. + */ +#define POLARSSL_RSA_C + +/* + * Module: library/sha1.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for SSL/TLS and SHA1-signed certificates. + */ +#define POLARSSL_SHA1_C + +/* + * Module: library/sha2.c + * Caller: + * + * This module adds support for SHA-224 and SHA-256. + */ +#define POLARSSL_SHA2_C + +/* + * Module: library/sha4.c + * Caller: + * + * This module adds support for SHA-384 and SHA-512. + */ +#define POLARSSL_SHA4_C + +/* + * Module: library/ssl_cli.c + * Caller: + * + * This module is required for SSL/TLS client support. + */ +#define POLARSSL_SSL_CLI_C + +/* + * Module: library/ssl_srv.c + * Caller: + * + * This module is required for SSL/TLS server support. + */ +#define POLARSSL_SSL_SRV_C + +/* + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is required for SSL/TLS. + */ +#define POLARSSL_SSL_TLS_C + +/* + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +#define POLARSSL_TIMING_C + +/* + * Module: library/x509parse.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module is required for X.509 certificate parsing. + */ +#define POLARSSL_X509_PARSE_C + +/* + * Module: library/x509_write.c + * Caller: + * + * This module is required for X.509 certificate writing. + */ +#define POLARSSL_X509_WRITE_C + +/* + * Module: library/xtea.c + * Caller: + */ +#define POLARSSL_XTEA_C + +#endif /* config.h */ diff --git a/ctrtool/polarssl/padlock.h b/ctrtool/polarssl/padlock.h new file mode 100644 index 0000000..12fa7d3 --- /dev/null +++ b/ctrtool/polarssl/padlock.h @@ -0,0 +1,98 @@ +/** + * \file padlock.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PADLOCK_H +#define POLARSSL_PADLOCK_H + +#include "polarssl/aes.h" + +#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && defined(__i386__) + +#ifndef POLARSSL_HAVE_X86 +#define POLARSSL_HAVE_X86 +#endif + +#define PADLOCK_RNG 0x000C +#define PADLOCK_ACE 0x00C0 +#define PADLOCK_PHE 0x0C00 +#define PADLOCK_PMM 0x3000 + +#define PADLOCK_ALIGN16(x) (unsigned long *) (16 + ((long) x & ~15)) + +#define POLARSSL_ERR_PADLOCK_DATA_MISALIGNED -0x08E0 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief PadLock detection routine + * + * \param The feature to detect + * + * \return 1 if CPU has support for the feature, 0 otherwise + */ +int padlock_supports( int feature ); + +/** + * \brief PadLock AES-ECB block en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if success, 1 if operation failed + */ +int padlock_xcryptecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +/** + * \brief PadLock AES-CBC buffer en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if success, 1 if operation failed + */ +int padlock_xcryptcbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_X86 */ + +#endif /* padlock.h */ diff --git a/ctrtool/polarssl/rsa.c b/ctrtool/polarssl/rsa.c new file mode 100644 index 0000000..77404fc --- /dev/null +++ b/ctrtool/polarssl/rsa.c @@ -0,0 +1,823 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_RSA_C) + +#include "polarssl/rsa.h" + +#include +#include +#include + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + ctx->padding = padding; + ctx->hash_id = hash_id; +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Generate an RSA keypair + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *), + void *p_rng, + int nbits, int exponent ) +{ + int ret; + mpi P1, Q1, H, G; + + if( f_rng == NULL || nbits < 128 || exponent < 3 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + mpi_init( &P1, &Q1, &H, &G, NULL ); + + /* + * find primes P and Q with Q < P so that: + * GCD( E, (P-1)*(Q-1) ) == 1 + */ + MPI_CHK( mpi_lset( &ctx->E, exponent ) ); + + do + { + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 ) + mpi_swap( &ctx->P, &ctx->Q ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 ) + continue; + + MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) ); + if( mpi_msb( &ctx->N ) != nbits ) + continue; + + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + } + while( mpi_cmp_int( &G, 1 ) != 0 ); + + /* + * D = E^-1 mod ((P-1)*(Q-1)) + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) ); + + ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3; + +cleanup: + + mpi_free( &G, &H, &Q1, &P1, NULL ); + + if( ret != 0 ) + { + rsa_free( ctx ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED | ret ); + } + + return( 0 ); +} + +#endif + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( const rsa_context *ctx ) +{ + if( !ctx->N.p || !ctx->E.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > 4096 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_msb( &ctx->E ) > 64 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( const rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + if( !ctx->P.p || !ctx->Q.p || !ctx->D.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + mpi_init( &PQ, &DE, &P1, &Q1, &H, &I, &G, &G2, &L1, &L2, NULL ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); + + /* + * Check for a valid PKCS1v2 private key + */ + if( mpi_cmp_mpi( &PQ, &ctx->N ) == 0 && + mpi_cmp_int( &L2, 0 ) == 0 && + mpi_cmp_int( &I, 1 ) == 0 && + mpi_cmp_int( &G, 1 ) == 0 ) + { + mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, &G2, &L1, &L2, NULL ); + return( 0 ); + } + + +cleanup: + + mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, &G2, &L1, &L2, NULL ); + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED | ret ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret, olen; + mpi T; + + mpi_init( &T, NULL ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T, NULL ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T, NULL ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED | ret ); + + return( 0 ); +} + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret, olen; + mpi T, T1, T2; + + mpi_init( &T, &T1, &T2, NULL ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T, NULL ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + +#if 0 + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * output = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); +#endif + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T, &T1, &T2, NULL ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED | ret ); + + return( 0 ); +} + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *), + void *p_rng, + int mode, int ilen, + const unsigned char *input, + unsigned char *output ) +{ + int nb_pad, olen; + unsigned char *p = output; + + olen = ctx->len; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + if( ilen < 0 || olen < ilen + 11 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + int rng_dl = 100; + + do { + *p = (unsigned char) f_rng( p_rng ); + } while( *p == 0 && --rng_dl ); + + // Check if RNG failed to generate data + // + if( rng_dl == 0 ) + return POLARSSL_ERR_RSA_RNG_FAILED; + + p++; + } + *p++ = 0; + memcpy( p, input, ilen ); + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, output, output ) ); +} + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, int *olen, + const unsigned char *input, + unsigned char *output, + int output_max_len) +{ + int ret, ilen; + unsigned char *p; + unsigned char buf[1024]; + + ilen = ctx->len; + + if( ilen < 16 || ilen > (int) sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + if( *p++ != 0 || *p++ != RSA_CRYPT ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + ilen - 1 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + if (ilen - (int)(p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (int)(p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int nb_pad, olen; + unsigned char *p = sig; + + olen = ctx->len; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + switch( hash_id ) + { + case SIG_RSA_RAW: + nb_pad = olen - 3 - hashlen; + break; + + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + nb_pad = olen - 3 - 34; + break; + + case SIG_RSA_SHA1: + nb_pad = olen - 3 - 35; + break; + + case SIG_RSA_SHA224: + nb_pad = olen - 3 - 47; + break; + + case SIG_RSA_SHA256: + nb_pad = olen - 3 - 51; + break; + + case SIG_RSA_SHA384: + nb_pad = olen - 3 - 67; + break; + + case SIG_RSA_SHA512: + nb_pad = olen - 3 - 83; + break; + + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( nb_pad < 8 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + switch( hash_id ) + { + case SIG_RSA_RAW: + memcpy( p, hash, hashlen ); + break; + + case SIG_RSA_MD2: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 2; break; + + case SIG_RSA_MD4: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 4; break; + + case SIG_RSA_MD5: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 5; break; + + case SIG_RSA_SHA1: + memcpy( p, ASN1_HASH_SHA1, 15 ); + memcpy( p + 15, hash, 20 ); + break; + + case SIG_RSA_SHA224: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 28 ); + p[1] += 28; p[14] = 4; p[18] += 28; break; + + case SIG_RSA_SHA256: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 32 ); + p[1] += 32; p[14] = 1; p[18] += 32; break; + + case SIG_RSA_SHA384: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 48 ); + p[1] += 48; p[14] = 2; p[18] += 48; break; + + case SIG_RSA_SHA512: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 64 ); + p[1] += 64; p[14] = 3; p[18] += 64; break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, sig, sig ) ); +} + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret, len, siglen; + unsigned char *p, c; + unsigned char buf[1024]; + + siglen = ctx->len; + + if( siglen < 16 || siglen > (int) sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + len = siglen - (int)( p - buf ); + + if( len == 34 ) + { + c = p[13]; + p[13] = 0; + + if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( c == 2 && hash_id == SIG_RSA_MD2 ) || + ( c == 4 && hash_id == SIG_RSA_MD4 ) || + ( c == 5 && hash_id == SIG_RSA_MD5 ) ) + { + if( memcmp( p + 18, hash, 16 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + } + + if( len == 35 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 && + memcmp( p + 15, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( ( len == 19 + 28 && p[14] == 4 && hash_id == SIG_RSA_SHA224 ) || + ( len == 19 + 32 && p[14] == 1 && hash_id == SIG_RSA_SHA256 ) || + ( len == 19 + 48 && p[14] == 2 && hash_id == SIG_RSA_SHA384 ) || + ( len == 19 + 64 && p[14] == 3 && hash_id == SIG_RSA_SHA512 ) ) + { + c = p[1] - 17; + p[1] = 17; + p[14] = 0; + + if( p[18] == c && + memcmp( p, ASN1_HASH_SHA2X, 18 ) == 0 && + memcmp( p + 19, hash, c ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == hashlen && hash_id == SIG_RSA_RAW ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ + mpi_free( &ctx->RQ, &ctx->RP, &ctx->RN, + &ctx->QP, &ctx->DQ, &ctx->DP, + &ctx->Q, &ctx->P, &ctx->D, + &ctx->E, &ctx->N, NULL ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +static int myrand( void *rng_state ) +{ + if( rng_state != NULL ) + rng_state = NULL; + + return( rand() ); +} + +/* + * Checkup routine + */ +int rsa_self_test( int verbose ) +{ + int len; + rsa_context rsa; + unsigned char sha1sum[20]; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + if( verbose != 0 ) + printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 encryption : " ); + + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); + + if( rsa_pkcs1_encrypt( &rsa, &myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 decryption : " ); + + if( rsa_pkcs1_decrypt( &rsa, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 data sign : " ); + + sha1( rsa_plaintext, PT_LEN, sha1sum ); + + if( rsa_pkcs1_sign( &rsa, RSA_PRIVATE, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, RSA_PUBLIC, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); + + rsa_free( &rsa ); + + return( 0 ); +} + +#endif + +#endif diff --git a/ctrtool/polarssl/rsa.h b/ctrtool/polarssl/rsa.h new file mode 100644 index 0000000..5fae794 --- /dev/null +++ b/ctrtool/polarssl/rsa.h @@ -0,0 +1,353 @@ +/** + * \file rsa.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#include "polarssl/bignum.h" + +/* + * RSA Error codes + */ +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x0400 +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x0410 +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x0420 +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x0430 +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x0440 +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x0450 +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x0460 +#define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x0470 +#define POLARSSL_ERR_RSA_RNG_FAILED -0x0480 + +/* + * PKCS#1 constants + */ +#define SIG_RSA_RAW 0 +#define SIG_RSA_MD2 2 +#define SIG_RSA_MD4 3 +#define SIG_RSA_MD5 4 +#define SIG_RSA_SHA1 5 +#define SIG_RSA_SHA224 14 +#define SIG_RSA_SHA256 11 +#define SIG_RSA_SHA384 12 +#define SIG_RSA_SHA512 13 + +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" +#define ASN1_STR_NULL "\x05" +#define ASN1_STR_OID "\x06" +#define ASN1_STR_OCTET_STRING "\x04" + +#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" +#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" +#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" + +#define OID_ISO_MEMBER_BODIES "\x2a" +#define OID_ISO_IDENTIFIED_ORG "\x2b" + +/* + * ISO Member bodies OID parts + */ +#define OID_COUNTRY_US "\x86\x48" +#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" + +/* + * ISO Identified organization OID parts + */ +#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ +#define ASN1_HASH_MDX \ +( \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ + ASN1_STR_OID "\x08" \ + OID_DIGEST_ALG_MDX \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x10" \ +) + +#define ASN1_HASH_SHA1 \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA2X \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ + ASN1_STR_OID "\x09" \ + OID_HASH_ALG_SHA2X \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x00" + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + int len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + + int padding; /*!< 1.5 or OAEP/PSS */ + int hash_id; /*!< hash identifier */ +} +rsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize an RSA context + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + * + * \note Currently, RSA_PKCS_V21 padding + * is not supported. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context. + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *), + void *p_rng, + int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( const rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( const rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0 or assure that + * input is smaller than N. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Add the message padding, then do an RSA operation + * + * \param ctx RSA context + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *), + void *p_rng, + int mode, int ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA operation, then remove the message padding + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param olen will contain the plaintext length + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, int *olen, + const unsigned char *input, + unsigned char *output, + int output_max_len ); + +/** + * \brief Do a private RSA to sign a message digest + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Do a public RSA and check the message digest + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Free the components of an RSA key + * + * \param ctx RSA Context to free + */ +void rsa_free( rsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/ctrtool/polarssl/sha2.c b/ctrtool/polarssl/sha2.c new file mode 100644 index 0000000..dd5ae98 --- /dev/null +++ b/ctrtool/polarssl/sha2.c @@ -0,0 +1,702 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_SHA2_C) + +#include "polarssl/sha2.h" + +#include +#include + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * SHA-256 context setup + */ +void sha2_starts( sha2_context *ctx, int is224 ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + if( is224 == 0 ) + { + /* SHA-256 */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; + } + else + { + /* SHA-224 */ + ctx->state[0] = 0xC1059ED8; + ctx->state[1] = 0x367CD507; + ctx->state[2] = 0x3070DD17; + ctx->state[3] = 0xF70E5939; + ctx->state[4] = 0xFFC00B31; + ctx->state[5] = 0x68581511; + ctx->state[6] = 0x64F98FA7; + ctx->state[7] = 0xBEFA4FA4; + } + + ctx->is224 = is224; +} + +static void sha2_process( sha2_context *ctx, const unsigned char data[64] ) +{ + unsigned long temp1, temp2, W[64]; + unsigned long A, B, C, D, E, F, G, H; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + + P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); + P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); + P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); + P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); + P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); + P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); + P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); + P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); + P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); + P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); + P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); + P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); + P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); + P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); + P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); + P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); + P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); + P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); + P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); + P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); + P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); + P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); + P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); + P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); + P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); + P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); + P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); + P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); + P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); + P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); + P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); + P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); + P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); + P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); + P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); + P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); + P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); + P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); + P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); + P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); + P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); + P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); + P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); + P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); + P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); + P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); + P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); + P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); + P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); + P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); + P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); + P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); + P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); + P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); + P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); + P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); + P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); + P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); + P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); + P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); + P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); + P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); + P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); + P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +/* + * SHA-256 process buffer + */ +void sha2_update( sha2_context *ctx, const unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha2_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha2_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char sha2_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-256 final digest + */ +void sha2_finish( sha2_context *ctx, unsigned char output[32] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha2_update( ctx, (unsigned char *) sha2_padding, padn ); + sha2_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); + PUT_ULONG_BE( ctx->state[5], output, 20 ); + PUT_ULONG_BE( ctx->state[6], output, 24 ); + + if( ctx->is224 == 0 ) + PUT_ULONG_BE( ctx->state[7], output, 28 ); +} + +/* + * output = SHA-256( input buffer ) + */ +void sha2( const unsigned char *input, int ilen, + unsigned char output[32], int is224 ) +{ + sha2_context ctx; + + sha2_starts( &ctx, is224 ); + sha2_update( &ctx, input, ilen ); + sha2_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha2_context ) ); +} + +/* + * output = SHA-256( file contents ) + */ +int sha2_file( const char *path, unsigned char output[32], int is224 ) +{ + FILE *f; + size_t n; + sha2_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( 1 ); + + sha2_starts( &ctx, is224 ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha2_update( &ctx, buf, (int) n ); + + sha2_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha2_context ) ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( 2 ); + } + + fclose( f ); + return( 0 ); +} + +/* + * SHA-256 HMAC context setup + */ +void sha2_hmac_starts( sha2_context *ctx, const unsigned char *key, int keylen, + int is224 ) +{ + int i; + unsigned char sum[32]; + + if( keylen > 64 ) + { + sha2( key, keylen, sum, is224 ); + keylen = ( is224 ) ? 28 : 32; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha2_starts( ctx, is224 ); + sha2_update( ctx, ctx->ipad, 64 ); + + memset( sum, 0, sizeof( sum ) ); +} + +/* + * SHA-256 HMAC process buffer + */ +void sha2_hmac_update( sha2_context *ctx, const unsigned char *input, int ilen ) +{ + sha2_update( ctx, input, ilen ); +} + +/* + * SHA-256 HMAC final digest + */ +void sha2_hmac_finish( sha2_context *ctx, unsigned char output[32] ) +{ + int is224, hlen; + unsigned char tmpbuf[32]; + + is224 = ctx->is224; + hlen = ( is224 == 0 ) ? 32 : 28; + + sha2_finish( ctx, tmpbuf ); + sha2_starts( ctx, is224 ); + sha2_update( ctx, ctx->opad, 64 ); + sha2_update( ctx, tmpbuf, hlen ); + sha2_finish( ctx, output ); + + memset( tmpbuf, 0, sizeof( tmpbuf ) ); +} + +/* + * SHA-256 HMAC context reset + */ +void sha2_hmac_reset( sha2_context *ctx ) +{ + sha2_starts( ctx, ctx->is224 ); + sha2_update( ctx, ctx->ipad, 64 ); +} + +/* + * output = HMAC-SHA-256( hmac key, input buffer ) + */ +void sha2_hmac( const unsigned char *key, int keylen, + const unsigned char *input, int ilen, + unsigned char output[32], int is224 ) +{ + sha2_context ctx; + + sha2_hmac_starts( &ctx, key, keylen, is224 ); + sha2_hmac_update( &ctx, input, ilen ); + sha2_hmac_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha2_context ) ); +} + +#if defined(POLARSSL_SELF_TEST) +/* + * FIPS-180-2 test vectors + */ +static unsigned char sha2_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const int sha2_test_buflen[3] = +{ + 3, 56, 1000 +}; + +static const unsigned char sha2_test_sum[6][32] = +{ + /* + * SHA-224 test vectors + */ + { 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22, + 0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2, 0x55, 0xB3, + 0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7, + 0xE3, 0x6C, 0x9D, 0xA7 }, + { 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC, + 0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89, 0x01, 0x50, + 0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19, + 0x52, 0x52, 0x25, 0x25 }, + { 0x20, 0x79, 0x46, 0x55, 0x98, 0x0C, 0x91, 0xD8, + 0xBB, 0xB4, 0xC1, 0xEA, 0x97, 0x61, 0x8A, 0x4B, + 0xF0, 0x3F, 0x42, 0x58, 0x19, 0x48, 0xB2, 0xEE, + 0x4E, 0xE7, 0xAD, 0x67 }, + + /* + * SHA-256 test vectors + */ + { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, + 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, + 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, + 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD }, + { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, + 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, + 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, + 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 }, + { 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, + 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, + 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, + 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 } +}; + +/* + * RFC 4231 test vectors + */ +static unsigned char sha2_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 131 times */ + { "" } +}; + +static const int sha2_hmac_test_keylen[7] = +{ + 20, 4, 20, 25, 20, 131, 131 +}; + +static unsigned char sha2_hmac_test_buf[7][153] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "This is a test using a larger than block-size key " + "and a larger than block-size data. The key needs to " + "be hashed before being used by the HMAC algorithm." } +}; + +static const int sha2_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 152 +}; + +static const unsigned char sha2_hmac_test_sum[14][32] = +{ + /* + * HMAC-SHA-224 test vectors + */ + { 0x89, 0x6F, 0xB1, 0x12, 0x8A, 0xBB, 0xDF, 0x19, + 0x68, 0x32, 0x10, 0x7C, 0xD4, 0x9D, 0xF3, 0x3F, + 0x47, 0xB4, 0xB1, 0x16, 0x99, 0x12, 0xBA, 0x4F, + 0x53, 0x68, 0x4B, 0x22 }, + { 0xA3, 0x0E, 0x01, 0x09, 0x8B, 0xC6, 0xDB, 0xBF, + 0x45, 0x69, 0x0F, 0x3A, 0x7E, 0x9E, 0x6D, 0x0F, + 0x8B, 0xBE, 0xA2, 0xA3, 0x9E, 0x61, 0x48, 0x00, + 0x8F, 0xD0, 0x5E, 0x44 }, + { 0x7F, 0xB3, 0xCB, 0x35, 0x88, 0xC6, 0xC1, 0xF6, + 0xFF, 0xA9, 0x69, 0x4D, 0x7D, 0x6A, 0xD2, 0x64, + 0x93, 0x65, 0xB0, 0xC1, 0xF6, 0x5D, 0x69, 0xD1, + 0xEC, 0x83, 0x33, 0xEA }, + { 0x6C, 0x11, 0x50, 0x68, 0x74, 0x01, 0x3C, 0xAC, + 0x6A, 0x2A, 0xBC, 0x1B, 0xB3, 0x82, 0x62, 0x7C, + 0xEC, 0x6A, 0x90, 0xD8, 0x6E, 0xFC, 0x01, 0x2D, + 0xE7, 0xAF, 0xEC, 0x5A }, + { 0x0E, 0x2A, 0xEA, 0x68, 0xA9, 0x0C, 0x8D, 0x37, + 0xC9, 0x88, 0xBC, 0xDB, 0x9F, 0xCA, 0x6F, 0xA8 }, + { 0x95, 0xE9, 0xA0, 0xDB, 0x96, 0x20, 0x95, 0xAD, + 0xAE, 0xBE, 0x9B, 0x2D, 0x6F, 0x0D, 0xBC, 0xE2, + 0xD4, 0x99, 0xF1, 0x12, 0xF2, 0xD2, 0xB7, 0x27, + 0x3F, 0xA6, 0x87, 0x0E }, + { 0x3A, 0x85, 0x41, 0x66, 0xAC, 0x5D, 0x9F, 0x02, + 0x3F, 0x54, 0xD5, 0x17, 0xD0, 0xB3, 0x9D, 0xBD, + 0x94, 0x67, 0x70, 0xDB, 0x9C, 0x2B, 0x95, 0xC9, + 0xF6, 0xF5, 0x65, 0xD1 }, + + /* + * HMAC-SHA-256 test vectors + */ + { 0xB0, 0x34, 0x4C, 0x61, 0xD8, 0xDB, 0x38, 0x53, + 0x5C, 0xA8, 0xAF, 0xCE, 0xAF, 0x0B, 0xF1, 0x2B, + 0x88, 0x1D, 0xC2, 0x00, 0xC9, 0x83, 0x3D, 0xA7, + 0x26, 0xE9, 0x37, 0x6C, 0x2E, 0x32, 0xCF, 0xF7 }, + { 0x5B, 0xDC, 0xC1, 0x46, 0xBF, 0x60, 0x75, 0x4E, + 0x6A, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xC7, + 0x5A, 0x00, 0x3F, 0x08, 0x9D, 0x27, 0x39, 0x83, + 0x9D, 0xEC, 0x58, 0xB9, 0x64, 0xEC, 0x38, 0x43 }, + { 0x77, 0x3E, 0xA9, 0x1E, 0x36, 0x80, 0x0E, 0x46, + 0x85, 0x4D, 0xB8, 0xEB, 0xD0, 0x91, 0x81, 0xA7, + 0x29, 0x59, 0x09, 0x8B, 0x3E, 0xF8, 0xC1, 0x22, + 0xD9, 0x63, 0x55, 0x14, 0xCE, 0xD5, 0x65, 0xFE }, + { 0x82, 0x55, 0x8A, 0x38, 0x9A, 0x44, 0x3C, 0x0E, + 0xA4, 0xCC, 0x81, 0x98, 0x99, 0xF2, 0x08, 0x3A, + 0x85, 0xF0, 0xFA, 0xA3, 0xE5, 0x78, 0xF8, 0x07, + 0x7A, 0x2E, 0x3F, 0xF4, 0x67, 0x29, 0x66, 0x5B }, + { 0xA3, 0xB6, 0x16, 0x74, 0x73, 0x10, 0x0E, 0xE0, + 0x6E, 0x0C, 0x79, 0x6C, 0x29, 0x55, 0x55, 0x2B }, + { 0x60, 0xE4, 0x31, 0x59, 0x1E, 0xE0, 0xB6, 0x7F, + 0x0D, 0x8A, 0x26, 0xAA, 0xCB, 0xF5, 0xB7, 0x7F, + 0x8E, 0x0B, 0xC6, 0x21, 0x37, 0x28, 0xC5, 0x14, + 0x05, 0x46, 0x04, 0x0F, 0x0E, 0xE3, 0x7F, 0x54 }, + { 0x9B, 0x09, 0xFF, 0xA7, 0x1B, 0x94, 0x2F, 0xCB, + 0x27, 0x63, 0x5F, 0xBC, 0xD5, 0xB0, 0xE9, 0x44, + 0xBF, 0xDC, 0x63, 0x64, 0x4F, 0x07, 0x13, 0x93, + 0x8A, 0x7F, 0x51, 0x53, 0x5C, 0x3A, 0x35, 0xE2 } +}; + +/* + * Checkup routine + */ +int sha2_self_test( int verbose ) +{ + int i, j, k, buflen; + unsigned char buf[1024]; + unsigned char sha2sum[32]; + sha2_context ctx; + + for( i = 0; i < 6; i++ ) + { + j = i % 3; + k = i < 3; + + if( verbose != 0 ) + printf( " SHA-%d test #%d: ", 256 - k * 32, j + 1 ); + + sha2_starts( &ctx, k ); + + if( j == 2 ) + { + memset( buf, 'a', buflen = 1000 ); + + for( j = 0; j < 1000; j++ ) + sha2_update( &ctx, buf, buflen ); + } + else + sha2_update( &ctx, sha2_test_buf[j], + sha2_test_buflen[j] ); + + sha2_finish( &ctx, sha2sum ); + + if( memcmp( sha2sum, sha2_test_sum[i], 32 - k * 4 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + for( i = 0; i < 14; i++ ) + { + j = i % 7; + k = i < 7; + + if( verbose != 0 ) + printf( " HMAC-SHA-%d test #%d: ", 256 - k * 32, j + 1 ); + + if( j == 5 || j == 6 ) + { + memset( buf, '\xAA', buflen = 131 ); + sha2_hmac_starts( &ctx, buf, buflen, k ); + } + else + sha2_hmac_starts( &ctx, sha2_hmac_test_key[j], + sha2_hmac_test_keylen[j], k ); + + sha2_hmac_update( &ctx, sha2_hmac_test_buf[j], + sha2_hmac_test_buflen[j] ); + + sha2_hmac_finish( &ctx, sha2sum ); + + buflen = ( j == 4 ) ? 16 : 32 - k * 4; + + if( memcmp( sha2sum, sha2_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + return( 0 ); +} + +#endif + +#endif diff --git a/ctrtool/polarssl/sha2.h b/ctrtool/polarssl/sha2.h new file mode 100644 index 0000000..be4ae56 --- /dev/null +++ b/ctrtool/polarssl/sha2.h @@ -0,0 +1,155 @@ +/** + * \file sha2.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SHA2_H +#define POLARSSL_SHA2_H + +/** + * \brief SHA-256 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[8]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ + int is224; /*!< 0 => SHA-256, else SHA-224 */ +} +sha2_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-256 context setup + * + * \param ctx context to be initialized + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2_starts( sha2_context *ctx, int is224 ); + +/** + * \brief SHA-256 process buffer + * + * \param ctx SHA-256 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha2_update( sha2_context *ctx, const unsigned char *input, int ilen ); + +/** + * \brief SHA-256 final digest + * + * \param ctx SHA-256 context + * \param output SHA-224/256 checksum result + */ +void sha2_finish( sha2_context *ctx, unsigned char output[32] ); + +/** + * \brief Output = SHA-256( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2( const unsigned char *input, int ilen, + unsigned char output[32], int is224 ); + +/** + * \brief Output = SHA-256( file contents ) + * + * \param path input file name + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + * + * \return 0 if successful, 1 if fopen failed, + * or 2 if fread failed + */ +int sha2_file( const char *path, unsigned char output[32], int is224 ); + +/** + * \brief SHA-256 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2_hmac_starts( sha2_context *ctx, const unsigned char *key, int keylen, + int is224 ); + +/** + * \brief SHA-256 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha2_hmac_update( sha2_context *ctx, const unsigned char *input, int ilen ); + +/** + * \brief SHA-256 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-224/256 HMAC checksum result + */ +void sha2_hmac_finish( sha2_context *ctx, unsigned char output[32] ); + +/** + * \brief SHA-256 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void sha2_hmac_reset( sha2_context *ctx ); + +/** + * \brief Output = HMAC-SHA-256( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-224/256 result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2_hmac( const unsigned char *key, int keylen, + const unsigned char *input, int ilen, + unsigned char output[32], int is224 ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int sha2_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha2.h */ diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c new file mode 100644 index 0000000..af58d74 --- /dev/null +++ b/ctrtool/romfs.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include + +#include "types.h" +#include "romfs.h" +#include "utils.h" + +void romfs_init(romfs_context* ctx) +{ + memset(ctx, 0, sizeof(romfs_context)); + ivfc_init(&ctx->ivfc); +} + +void romfs_set_file(romfs_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void romfs_set_offset(romfs_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void romfs_set_size(romfs_context* ctx, u32 size) +{ + ctx->size = size; +} + +void romfs_set_usersettings(romfs_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + + + +void romfs_process(romfs_context* ctx, u32 actions) +{ + u32 dirblockoffset = 0; + u32 dirblocksize = 0; + u32 fileblockoffset = 0; + u32 fileblocksize = 0; + + + ivfc_set_offset(&ctx->ivfc, ctx->offset); + ivfc_set_size(&ctx->ivfc, ctx->size); + ivfc_set_file(&ctx->ivfc, ctx->file); + ivfc_set_usersettings(&ctx->ivfc, ctx->usersettings); + ivfc_process(&ctx->ivfc, actions); + + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(&ctx->header, 1, sizeof(romfs_header), ctx->file); + + if (getle32(ctx->header.magic) != MAGIC_IVFC) + { + fprintf(stdout, "Error, RomFS corrupted\n"); + return; + } + + ctx->infoblockoffset = ctx->offset + 0x1000; + + fseek(ctx->file, ctx->infoblockoffset, SEEK_SET); + fread(&ctx->infoheader, 1, sizeof(romfs_infoheader), ctx->file); + + if (getle32(ctx->infoheader.headersize) != sizeof(romfs_infoheader)) + { + fprintf(stderr, "Error, info header mismatch\n"); + return; + } + + dirblockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.section[1].offset); + dirblocksize = getle32(ctx->infoheader.section[1].size); + fileblockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.section[3].offset); + fileblocksize = getle32(ctx->infoheader.section[3].size); + + ctx->dirblock = malloc(dirblocksize); + ctx->dirblocksize = dirblocksize; + ctx->fileblock = malloc(fileblocksize); + ctx->fileblocksize = fileblocksize; + + ctx->datablockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.dataoffset); + + if (ctx->dirblock) + { + fseek(ctx->file, dirblockoffset, SEEK_SET); + fread(ctx->dirblock, 1, dirblocksize, ctx->file); + } + + if (ctx->fileblock) + { + fseek(ctx->file, fileblockoffset, SEEK_SET); + fread(ctx->fileblock, 1, fileblocksize, ctx->file); + } + + if (actions & InfoFlag) + romfs_print(ctx); + + romfs_visit_dir(ctx, 0, 0, actions, settings_get_romfs_dir_path(ctx->usersettings)); + +} + +int romfs_dirblock_read(romfs_context* ctx, u32 diroffset, u32 dirsize, void* buffer) +{ + if (!ctx->dirblock) + return 0; + + if (diroffset+dirsize > ctx->dirblocksize) + return 0; + + memcpy(buffer, ctx->dirblock + diroffset, dirsize); + return 1; +} + +int romfs_dirblock_readentry(romfs_context* ctx, u32 diroffset, romfs_direntry* entry) +{ + u32 size_without_name = sizeof(romfs_direntry) - ROMFS_MAXNAMESIZE; + u32 namesize; + + + if (!ctx->dirblock) + return 0; + + if (!romfs_dirblock_read(ctx, diroffset, size_without_name, entry)) + return 0; + + namesize = getle32(entry->namesize); + if (namesize > (ROMFS_MAXNAMESIZE-2)) + namesize = (ROMFS_MAXNAMESIZE-2); + memset(entry->name + namesize, 0, 2); + if (!romfs_dirblock_read(ctx, diroffset + size_without_name, namesize, entry->name)) + return 0; + + return 1; +} + + +int romfs_fileblock_read(romfs_context* ctx, u32 fileoffset, u32 filesize, void* buffer) +{ + if (!ctx->fileblock) + return 0; + + if (fileoffset+filesize > ctx->fileblocksize) + return 0; + + memcpy(buffer, ctx->fileblock + fileoffset, filesize); + return 1; +} + +int romfs_fileblock_readentry(romfs_context* ctx, u32 fileoffset, romfs_fileentry* entry) +{ + u32 size_without_name = sizeof(romfs_fileentry) - ROMFS_MAXNAMESIZE; + u32 namesize; + + + if (!ctx->fileblock) + return 0; + + if (!romfs_fileblock_read(ctx, fileoffset, size_without_name, entry)) + return 0; + + namesize = getle32(entry->namesize); + if (namesize > (ROMFS_MAXNAMESIZE-2)) + namesize = (ROMFS_MAXNAMESIZE-2); + memset(entry->name + namesize, 0, 2); + if (!romfs_fileblock_read(ctx, fileoffset + size_without_name, namesize, entry->name)) + return 0; + + return 1; +} + + + +void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, filepath* rootpath) +{ + u32 siblingoffset; + u32 childoffset; + u32 fileoffset; + filepath currentpath; + romfs_direntry* entry = &ctx->direntry; + + + if (!romfs_dirblock_readentry(ctx, diroffset, entry)) + return; + + +// fprintf(stdout, "%08X %08X %08X %08X %08X ", +// getle32(entry->parentoffset), getle32(entry->siblingoffset), getle32(entry->childoffset), +// getle32(entry->fileoffset), getle32(entry->weirdoffset)); +// fwprintf(stdout, L"%ls\n", entry->name); + + + if (rootpath && rootpath->valid) + { + filepath_copy(¤tpath, rootpath); + filepath_append_utf16(¤tpath, entry->name); + if (currentpath.valid) + { + makedir(currentpath.pathname); + } + else + { + fprintf(stderr, "Error creating directory in root %s\n", rootpath->pathname); + return; + } + } + else + { + filepath_init(¤tpath); + + if (settings_get_list_romfs_files(ctx->usersettings)) + { + u32 i; + + for(i=0; iname); + } + } + + + siblingoffset = getle32(entry->siblingoffset); + childoffset = getle32(entry->childoffset); + fileoffset = getle32(entry->fileoffset); + + if (fileoffset != (~0)) + romfs_visit_file(ctx, fileoffset, depth+1, actions, ¤tpath); + + if (childoffset != (~0)) + romfs_visit_dir(ctx, childoffset, depth+1, actions, ¤tpath); + + if (siblingoffset != (~0)) + romfs_visit_dir(ctx, siblingoffset, depth, actions, rootpath); +} + + +void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, filepath* rootpath) +{ + u32 siblingoffset = 0; + filepath currentpath; + romfs_fileentry* entry = &ctx->fileentry; + + + if (!romfs_fileblock_readentry(ctx, fileoffset, entry)) + return; + + +// fprintf(stdout, "%08X %08X %016llX %016llX %08X ", +// getle32(entry->parentdiroffset), getle32(entry->siblingoffset), ctx->datablockoffset+getle64(entry->dataoffset), +// getle64(entry->datasize), getle32(entry->unknown)); +// fwprintf(stdout, L"%ls\n", entry->name); + + if (rootpath && rootpath->valid) + { + filepath_copy(¤tpath, rootpath); + filepath_append_utf16(¤tpath, entry->name); + if (currentpath.valid) + { + fprintf(stdout, "Saving %s...\n", currentpath.pathname); + romfs_extract_datafile(ctx, getle64(entry->dataoffset), getle64(entry->datasize), ¤tpath); + } + else + { + fprintf(stderr, "Error creating directory in root %s\n", rootpath->pathname); + return; + } + } + else + { + filepath_init(¤tpath); + if (settings_get_list_romfs_files(ctx->usersettings)) + { + u32 i; + + for(i=0; iname); + } + } + + siblingoffset = getle32(entry->siblingoffset); + + if (siblingoffset != (~0)) + romfs_visit_file(ctx, siblingoffset, depth, actions, rootpath); +} + +void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, filepath* path) +{ + FILE* outfile = 0; + u32 max; + u8 buffer[4096]; + + + if (path == 0 || path->valid == 0) + goto clean; + + offset += ctx->datablockoffset; + if ( (offset >> 32) ) + { + fprintf(stderr, "Error, support for 64-bit offset not yet implemented.\n"); + goto clean; + } + + fseek(ctx->file, offset, SEEK_SET); + outfile = fopen(path->pathname, "wb"); + if (outfile == 0) + { + fprintf(stderr, "Error opening file for writing\n"); + goto clean; + } + + while(size) + { + max = sizeof(buffer); + if (max > size) + max = size; + + if (max != fread(buffer, 1, max, ctx->file)) + { + fprintf(stderr, "Error reading file\n"); + goto clean; + } + + if (max != fwrite(buffer, 1, max, outfile)) + { + fprintf(stderr, "Error writing file\n"); + goto clean; + } + + size -= max; + } +clean: + if (outfile) + fclose(outfile); +} + + +void romfs_print(romfs_context* ctx) +{ + u32 i; + + fprintf(stdout, "\nRomFS:\n"); + + fprintf(stdout, "Header size: 0x%08X\n", getle32(ctx->infoheader.headersize)); + for(i=0; i<4; i++) + { + fprintf(stdout, "Section %d offset: 0x%08X\n", i, ctx->offset + 0x1000 + getle32(ctx->infoheader.section[i].offset)); + fprintf(stdout, "Section %d size: 0x%08X\n", i, getle32(ctx->infoheader.section[i].size)); + } + + fprintf(stdout, "Data offset: 0x%08X\n", ctx->offset + 0x1000 + getle32(ctx->infoheader.dataoffset)); +} diff --git a/ctrtool/romfs.h b/ctrtool/romfs.h new file mode 100644 index 0000000..892e9ac --- /dev/null +++ b/ctrtool/romfs.h @@ -0,0 +1,90 @@ +#ifndef __ROMFS_H__ +#define __ROMFS_H__ + +#include "types.h" +#include "info.h" +#include "ctr.h" +#include "filepath.h" +#include "settings.h" +#include "ivfc.h" + +#define ROMFS_MAXNAMESIZE 254 // limit set by ctrtool + +typedef struct +{ + u8 magic[4]; +} romfs_header; + +typedef struct +{ + u8 offset[4]; + u8 size[4]; +} romfs_sectionheader; + +typedef struct +{ + u8 headersize[4]; + romfs_sectionheader section[4]; + u8 dataoffset[4]; +} romfs_infoheader; + + +typedef struct +{ + u8 parentoffset[4]; + u8 siblingoffset[4]; + u8 childoffset[4]; + u8 fileoffset[4]; + u8 weirdoffset[4]; // this one is weird. it always points to a dir entry, but seems unrelated to the romfs structure. + u8 namesize[4]; + u8 name[ROMFS_MAXNAMESIZE]; +} romfs_direntry; + +typedef struct +{ + u8 parentdiroffset[4]; + u8 siblingoffset[4]; + u8 dataoffset[8]; + u8 datasize[8]; + u8 weirdoffset[4]; // this one is also weird. it always points to a file entry, but seems unrelated to the romfs structure. + u8 namesize[4]; + u8 name[ROMFS_MAXNAMESIZE]; +} romfs_fileentry; + + +typedef struct +{ + FILE* file; + settings* usersettings; + u32 offset; + u32 size; + romfs_header header; + romfs_infoheader infoheader; + u8* dirblock; + u32 dirblocksize; + u8* fileblock; + u32 fileblocksize; + u32 datablockoffset; + u32 infoblockoffset; + romfs_direntry direntry; + romfs_fileentry fileentry; + ivfc_context ivfc; +} romfs_context; + +void romfs_init(romfs_context* ctx); +void romfs_set_file(romfs_context* ctx, FILE* file); +void romfs_set_offset(romfs_context* ctx, u32 offset); +void romfs_set_size(romfs_context* ctx, u32 size); +void romfs_set_usersettings(romfs_context* ctx, settings* usersettings); +void romfs_test(romfs_context* ctx); +int romfs_dirblock_read(romfs_context* ctx, u32 diroffset, u32 dirsize, void* buffer); +int romfs_dirblock_readentry(romfs_context* ctx, u32 diroffset, romfs_direntry* entry); +int romfs_fileblock_read(romfs_context* ctx, u32 fileoffset, u32 filesize, void* buffer); +int romfs_fileblock_readentry(romfs_context* ctx, u32 fileoffset, romfs_fileentry* entry); +void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, filepath* rootpath); +void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, filepath* rootpath); +void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, filepath* path); +void romfs_process(romfs_context* ctx, u32 actions); +void romfs_print(romfs_context* ctx); + +#endif // __ROMFS_H__ diff --git a/ctrtool/settings.c b/ctrtool/settings.c new file mode 100644 index 0000000..a52b4c6 --- /dev/null +++ b/ctrtool/settings.c @@ -0,0 +1,256 @@ +#include +#include +#include "settings.h" + +void settings_init(settings* usersettings) +{ + memset(usersettings, 0, sizeof(settings)); +} + +filepath* settings_get_wav_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->wavpath; + else + return 0; +} + +filepath* settings_get_lzss_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->lzsspath; + else + return 0; +} + +filepath* settings_get_exefs_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->exefspath; + else + return 0; +} + +filepath* settings_get_romfs_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->romfspath; + else + return 0; +} + +filepath* settings_get_exheader_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->exheaderpath; + else + return 0; +} + +filepath* settings_get_exefs_dir_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->exefsdirpath; + else + return 0; +} + +filepath* settings_get_romfs_dir_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->romfsdirpath; + else + return 0; +} + +filepath* settings_get_firm_dir_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->firmdirpath; + else + return 0; +} + + +filepath* settings_get_certs_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->certspath; + else + return 0; +} + +filepath* settings_get_tik_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->tikpath; + else + return 0; +} + +filepath* settings_get_tmd_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->tmdpath; + else + return 0; +} + +filepath* settings_get_meta_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->metapath; + else + return 0; +} + +filepath* settings_get_content_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->contentpath; + else + return 0; +} + +unsigned int settings_get_mediaunit_size(settings* usersettings) +{ + if (usersettings) + return usersettings->mediaunitsize; + else + return 0; +} + +unsigned char* settings_get_ncch_key(settings* usersettings) +{ + if (usersettings && usersettings->keys.ncchkey.valid) + return usersettings->keys.ncchkey.data; + else + return 0; +} + +unsigned char* settings_get_ncch_fixedsystemkey(settings* usersettings) +{ + if (usersettings && usersettings->keys.ncchfixedsystemkey.valid) + return usersettings->keys.ncchfixedsystemkey.data; + else + return 0; +} + +unsigned char* settings_get_common_key(settings* usersettings) +{ + if (usersettings && usersettings->keys.commonkey.valid) + return usersettings->keys.commonkey.data; + else + return 0; +} + + +int settings_get_ignore_programid(settings* usersettings) +{ + if (usersettings) + return usersettings->ignoreprogramid; + else + return 0; +} + +int settings_get_list_romfs_files(settings* usersettings) +{ + if (usersettings) + return usersettings->listromfs; + else + return 0; +} + +int settings_get_cwav_loopcount(settings* usersettings) +{ + if (usersettings) + return usersettings->cwavloopcount; + else + return 0; +} + +void settings_set_wav_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->wavpath, path); +} + +void settings_set_lzss_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->lzsspath, path); +} + +void settings_set_exefs_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->exefspath, path); +} + +void settings_set_romfs_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->romfspath, path); +} + +void settings_set_firm_dir_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->firmdirpath, path); +} + + +void settings_set_exheader_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->exheaderpath, path); +} + +void settings_set_certs_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->certspath, path); +} + +void settings_set_tik_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->tikpath, path); +} + +void settings_set_tmd_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->tmdpath, path); +} + +void settings_set_meta_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->metapath, path); +} + +void settings_set_content_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->contentpath, path); +} + +void settings_set_exefs_dir_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->exefsdirpath, path); +} + +void settings_set_romfs_dir_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->romfsdirpath, path); +} + +void settings_set_mediaunit_size(settings* usersettings, unsigned int size) +{ + usersettings->mediaunitsize = size; +} + +void settings_set_ignore_programid(settings* usersettings, int enable) +{ + usersettings->ignoreprogramid = enable; +} + +void settings_set_list_romfs_files(settings* usersettings, int enable) +{ + usersettings->listromfs = enable; +} + +void settings_set_cwav_loopcount(settings* usersettings, u32 loopcount) +{ + usersettings->cwavloopcount = loopcount; +} diff --git a/ctrtool/settings.h b/ctrtool/settings.h new file mode 100644 index 0000000..f45bdc6 --- /dev/null +++ b/ctrtool/settings.h @@ -0,0 +1,70 @@ +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include "types.h" +#include "keyset.h" +#include "filepath.h" + +typedef struct +{ + keyset keys; + filepath exefspath; + filepath exefsdirpath; + filepath firmdirpath; + filepath romfspath; + filepath romfsdirpath; + filepath exheaderpath; + filepath certspath; + filepath contentpath; + filepath tikpath; + filepath tmdpath; + filepath metapath; + filepath lzsspath; + filepath wavpath; + unsigned int mediaunitsize; + int ignoreprogramid; + int listromfs; + u32 cwavloopcount; +} settings; + +void settings_init(settings* usersettings); +filepath* settings_get_lzss_path(settings* usersettings); +filepath* settings_get_exefs_path(settings* usersettings); +filepath* settings_get_romfs_path(settings* usersettings); +filepath* settings_get_exheader_path(settings* usersettings); +filepath* settings_get_certs_path(settings* usersettings); +filepath* settings_get_tik_path(settings* usersettings); +filepath* settings_get_tmd_path(settings* usersettings); +filepath* settings_get_meta_path(settings* usersettings); +filepath* settings_get_content_path(settings* usersettings); +filepath* settings_get_exefs_dir_path(settings* usersettings); +filepath* settings_get_romfs_dir_path(settings* usersettings); +filepath* settings_get_firm_dir_path(settings* usersettings); +filepath* settings_get_wav_path(settings* usersettings); +unsigned int settings_get_mediaunit_size(settings* usersettings); +unsigned char* settings_get_ncch_key(settings* usersettings); +unsigned char* settings_get_ncch_fixedsystemkey(settings* usersettings); +unsigned char* settings_get_common_key(settings* usersettings); +int settings_get_ignore_programid(settings* usersettings); +int settings_get_list_romfs_files(settings* usersettings); +int settings_get_cwav_loopcount(settings* usersettings); + +void settings_set_lzss_path(settings* usersettings, const char* path); +void settings_set_exefs_path(settings* usersettings, const char* path); +void settings_set_romfs_path(settings* usersettings, const char* path); +void settings_set_exheader_path(settings* usersettings, const char* path); +void settings_set_certs_path(settings* usersettings, const char* path); +void settings_set_tik_path(settings* usersettings, const char* path); +void settings_set_tmd_path(settings* usersettings, const char* path); +void settings_set_meta_path(settings* usersettings, const char* path); +void settings_set_content_path(settings* usersettings, const char* path); +void settings_set_exefs_dir_path(settings* usersettings, const char* path); +void settings_set_romfs_dir_path(settings* usersettings, const char* path); +void settings_set_firm_dir_path(settings* usersettings, const char* path); +void settings_set_wav_path(settings* usersettings, const char* path); +void settings_set_mediaunit_size(settings* usersettings, unsigned int size); +void settings_set_ignore_programid(settings* usersettings, int enable); +void settings_set_list_romfs_files(settings* usersettings, int enable); +void settings_set_cwav_loopcount(settings* usersettings, u32 loopcount); + +#endif // _SETTINGS_H_ diff --git a/ctrtool/stream.c b/ctrtool/stream.c new file mode 100644 index 0000000..dcc1b64 --- /dev/null +++ b/ctrtool/stream.c @@ -0,0 +1,138 @@ +#include +#include +#include + + +#include "types.h" +#include "stream.h" + + +void stream_in_init(stream_in_context* ctx) +{ + memset(ctx, 0, sizeof(stream_in_context)); +} + +void stream_out_init(stream_out_context* ctx) +{ + memset(ctx, 0, sizeof(stream_out_context)); +} + +void stream_in_allocate(stream_in_context* ctx, u32 buffersize, FILE* file) +{ + ctx->inbuffer = malloc(buffersize); + ctx->inbuffersize = buffersize; + ctx->infile = file; + ctx->infileposition = 0; +} + +void stream_out_allocate(stream_out_context* ctx, u32 buffersize, FILE* file) +{ + ctx->outbuffer = malloc(buffersize); + ctx->outbuffersize = buffersize; + ctx->outfile = file; +} + +void stream_in_destroy(stream_in_context* ctx) +{ + free(ctx->inbuffer); + ctx->inbuffer = 0; +} + +void stream_out_destroy(stream_out_context* ctx) +{ + free(ctx->outbuffer); + ctx->outbuffer = 0; +} + +int stream_in_byte(stream_in_context* ctx, u8* byte) +{ + if (ctx->inbufferpos >= ctx->inbufferavailable) + { + size_t readbytes = fread(ctx->inbuffer, 1, ctx->inbuffersize, ctx->infile); + if (readbytes <= 0) + return 0; + + ctx->inbufferavailable = readbytes; + ctx->inbufferpos = 0; + ctx->infileposition += readbytes; + } + + *byte = ctx->inbuffer[ctx->inbufferpos++]; + return 1; +} + +void stream_in_seek(stream_in_context* ctx, u32 position) +{ + fseek(ctx->infile, position, SEEK_SET); + ctx->infileposition = position; + ctx->inbufferpos = 0; + ctx->inbufferavailable = 0; +} + +void stream_in_reseek(stream_in_context* ctx) +{ + fseek(ctx->infile, ctx->infileposition, SEEK_SET); +} + + +void stream_out_seek(stream_out_context* ctx, u32 position) +{ + stream_out_flush(ctx); + + fseek(ctx->outfile, position, SEEK_SET); +} + +void stream_out_skip(stream_out_context* ctx, u32 size) +{ + stream_out_flush(ctx); + + fseek(ctx->outfile, size, SEEK_CUR); +} + +int stream_out_byte(stream_out_context* ctx, u8 byte) +{ + if (ctx->outbufferpos >= ctx->outbuffersize) + { + if (stream_out_flush(ctx) == 0) + return 0; + } + + ctx->outbuffer[ctx->outbufferpos++] = byte; + return 1; +} + +int stream_out_buffer(stream_out_context* ctx, const void* buffer, u32 size) +{ + u32 i; + + for(i=0; ioutbufferpos > 0) + { + size_t writtenbytes = fwrite(ctx->outbuffer, 1, ctx->outbufferpos, ctx->outfile); + if (writtenbytes < 0) + return 0; + + + ctx->outbufferpos = 0; + } + return 1; +} + +void stream_out_position(stream_out_context* ctx, u32* position) +{ + stream_out_flush(ctx); + + *position = ftell(ctx->outfile); +} + + diff --git a/ctrtool/stream.h b/ctrtool/stream.h new file mode 100644 index 0000000..df5bcbc --- /dev/null +++ b/ctrtool/stream.h @@ -0,0 +1,45 @@ +#ifndef __STREAM_H__ +#define __STREAM_H__ + +#include +#include "types.h" + +typedef struct +{ + FILE* infile; + u32 infileposition; + u8* inbuffer; + u32 inbuffersize; + u32 inbufferavailable; + u32 inbufferpos; +} stream_in_context; + +typedef struct +{ + FILE* outfile; + u8* outbuffer; + u32 outbuffersize; + u32 outbufferpos; +} stream_out_context; + +// create/destroy +void stream_in_init(stream_in_context* ctx); +void stream_in_allocate(stream_in_context* ctx, u32 buffersize, FILE* file); +void stream_in_destroy(stream_in_context* ctx); +void stream_out_init(stream_out_context* ctx); +void stream_out_allocate(stream_out_context* ctx, u32 buffersize, FILE* file); +void stream_out_destroy(stream_out_context* ctx); + +// read/write operations +int stream_in_byte(stream_in_context* ctx, u8* byte); +void stream_in_seek(stream_in_context* ctx, u32 position); +void stream_in_reseek(stream_in_context* ctx); + +int stream_out_byte(stream_out_context* ctx, u8 byte); +int stream_out_buffer(stream_out_context* ctx, const void* buffer, u32 size); +int stream_out_flush(stream_out_context* ctx); +void stream_out_seek(stream_out_context* ctx, u32 position); +void stream_out_skip(stream_out_context* ctx, u32 size); +void stream_out_position(stream_out_context* ctx, u32* position); + +#endif // __STREAM_H__ diff --git a/ctrtool/tik.c b/ctrtool/tik.c new file mode 100644 index 0000000..2f3c4ef --- /dev/null +++ b/ctrtool/tik.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#include "tik.h" +#include "ctr.h" +#include "utils.h" + +void tik_init(tik_context* ctx) +{ + memset(ctx, 0, sizeof(tik_context)); +} + +void tik_set_file(tik_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void tik_set_offset(tik_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void tik_set_size(tik_context* ctx, u32 size) +{ + ctx->size = size; +} + +void tik_set_usersettings(tik_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void tik_get_decrypted_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) +{ + memcpy(decryptedkey, ctx->titlekey, 16); +} + +void tik_get_titleid(tik_context* ctx, u8 titleid[8]) +{ + memcpy(titleid, ctx->tik.title_id, 8); +} + +void tik_get_iv(tik_context* ctx, u8 iv[16]) +{ + memset(iv, 0, 16); + memcpy(iv, ctx->tik.title_id, 8); +} + +void tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) +{ + u8 iv[16]; + u8* key = settings_get_common_key(ctx->usersettings); + + memset(decryptedkey, 0, 0x10); + + if (!key) + { + fprintf(stdout, "Warning, could not read common key.\n"); + } + else + { + memset(iv, 0, 0x10); + memcpy(iv, ctx->tik.title_id, 8); + + ctr_init_cbc_decrypt(&ctx->aes, key, iv); + ctr_decrypt_cbc(&ctx->aes, ctx->tik.encrypted_title_key, decryptedkey, 0x10); + } +} + +void tik_process(tik_context* ctx, u32 actions) +{ + if (ctx->size < sizeof(eticket)) + { + fprintf(stderr, "Error, ticket size too small\n"); + goto clean; + } + + fseek(ctx->file, ctx->offset, SEEK_SET); + fread((u8*)&ctx->tik, 1, sizeof(eticket), ctx->file); + + tik_decrypt_titlekey(ctx, ctx->titlekey); + + if (actions & InfoFlag) + { + tik_print(ctx); + } + +clean: + return; +} + +void tik_print(tik_context* ctx) +{ + int i; + eticket* tik = &ctx->tik; + + fprintf(stdout, "\nTicket content:\n"); + fprintf(stdout, + "Signature Type: %08x\n" + "Issuer: %s\n", + getle32(tik->sig_type), tik->issuer + ); + + fprintf(stdout, "Signature:\n"); + hexdump(tik->signature, 0x100); + fprintf(stdout, "\n"); + + memdump(stdout, "Encrypted Titlekey: ", tik->encrypted_title_key, 0x10); + + if (settings_get_common_key(ctx->usersettings)) + memdump(stdout, "Decrypted Titlekey: ", ctx->titlekey, 0x10); + + memdump(stdout, "Ticket ID: ", tik->ticket_id, 0x08); + fprintf(stdout, "Ticket Version: %d\n", getle16(tik->ticket_version)); + memdump(stdout, "Title ID: ", tik->title_id, 0x08); + fprintf(stdout, "Common Key Index: %d\n", tik->commonkey_idx); + + fprintf(stdout, "Content permission map:\n"); + for(i = 0; i < 0x40; i++) { + printf(" %02x", tik->content_permissions[i]); + + if ((i+1) % 8 == 0) + printf("\n"); + } + printf("\n"); +} diff --git a/ctrtool/tik.h b/ctrtool/tik.h new file mode 100644 index 0000000..78b93f0 --- /dev/null +++ b/ctrtool/tik.h @@ -0,0 +1,64 @@ +#ifndef __TIK_H__ +#define __TIK_H__ + +#include "types.h" +#include "keyset.h" +#include "ctr.h" +#include "settings.h" + +typedef struct +{ + u8 enable_timelimit[4]; + u8 timelimit_seconds[4]; +} timelimit_entry; + +typedef struct +{ + u8 sig_type[4]; + u8 signature[0x100]; + u8 padding1[0x3c]; + u8 issuer[0x40]; + u8 ecdsa[0x3c]; + u8 padding2[0x03]; + u8 encrypted_title_key[0x10]; + u8 unknown; + u8 ticket_id[8]; + u8 console_id[4]; + u8 title_id[8]; + u8 sys_access[2]; + u8 ticket_version[2]; + u8 time_mask[4]; + u8 permit_mask[4]; + u8 title_export; + u8 commonkey_idx; + u8 unknown_buf[0x30]; + u8 content_permissions[0x40]; + u8 padding0[2]; + + timelimit_entry timelimits[8]; +} eticket; + +typedef struct +{ + FILE* file; + u32 offset; + u32 size; + u8 titlekey[16]; + eticket tik; + ctr_aes_context aes; + settings* usersettings; +} tik_context; + +void tik_init(tik_context* ctx); +void tik_set_file(tik_context* ctx, FILE* file); +void tik_set_offset(tik_context* ctx, u32 offset); +void tik_set_size(tik_context* ctx, u32 size); +void tik_set_usersettings(tik_context* ctx, settings* usersettings); +void tik_get_decrypted_titlekey(tik_context* ctx, u8 decryptedkey[0x10]); +void tik_get_titleid(tik_context* ctx, u8 titleid[8]); +void tik_get_iv(tik_context* ctx, u8 iv[0x10]); +void tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]); +void tik_print(tik_context* ctx); +void tik_process(tik_context* ctx, u32 actions); + +#endif diff --git a/ctrtool/tinyxml/tinystr.cpp b/ctrtool/tinyxml/tinystr.cpp new file mode 100644 index 0000000..6ca4dc8 --- /dev/null +++ b/ctrtool/tinyxml/tinystr.cpp @@ -0,0 +1,111 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#include "tinyxml/tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/ctrtool/tinyxml/tinystr.h b/ctrtool/tinyxml/tinystr.h new file mode 100644 index 0000000..89cca33 --- /dev/null +++ b/ctrtool/tinyxml/tinystr.h @@ -0,0 +1,305 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/ctrtool/tinyxml/tinyxml.cpp b/ctrtool/tinyxml/tinyxml.cpp new file mode 100644 index 0000000..cec7b8c --- /dev/null +++ b/ctrtool/tinyxml/tinyxml.cpp @@ -0,0 +1,1886 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml/tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} +#endif + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); + #else + sprintf (buf, "%g", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/ctrtool/tinyxml/tinyxml.h b/ctrtool/tinyxml/tinyxml.h new file mode 100644 index 0000000..eea0f06 --- /dev/null +++ b/ctrtool/tinyxml/tinyxml.h @@ -0,0 +1,1805 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinyxml/tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml/tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() const { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p && *p ) + p += strlen( endTag ); + return ( p && *p ) ? p : 0; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p && *p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p && *p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i +#include +#include +#include +#include "tmd.h" +#include "utils.h" + + +void tmd_init(tmd_context* ctx) +{ + memset(ctx, 0, sizeof(tmd_context)); +} + +void tmd_set_file(tmd_context* ctx, FILE* file) +{ + ctx->file = file; +} + +void tmd_set_offset(tmd_context* ctx, u32 offset) +{ + ctx->offset = offset; +} + +void tmd_set_size(tmd_context* ctx, u32 size) +{ + ctx->size = size; +} + +void tmd_set_usersettings(tmd_context* ctx, settings* usersettings) +{ + ctx->usersettings = usersettings; +} + +void tmd_process(tmd_context* ctx, u32 actions) +{ + if (ctx->buffer == 0) + ctx->buffer = malloc(ctx->size); + + if (ctx->buffer) + { + fseek(ctx->file, ctx->offset, SEEK_SET); + fread(ctx->buffer, 1, ctx->size, ctx->file); + + if (actions & InfoFlag) + { + tmd_print(ctx); + } + } +} + +ctr_tmd_body *tmd_get_body(tmd_context *ctx) +{ + unsigned int type = getbe32(ctx->buffer); + ctr_tmd_body *body = NULL; + + if (type == TMD_RSA_2048_SHA256 || type == TMD_RSA_2048_SHA1) + { + body = (ctr_tmd_body*)(ctx->buffer + sizeof(ctr_tmd_header_2048)); + } + else if (type == TMD_RSA_4096_SHA256 || type == TMD_RSA_4096_SHA1) + { + body = (ctr_tmd_body*)(ctx->buffer + sizeof(ctr_tmd_header_4096)); + } + + return body; +} + +const char* tmd_get_type_string(unsigned int type) +{ + switch(type) + { + case TMD_RSA_2048_SHA256: return "RSA 2048 - SHA256"; + case TMD_RSA_4096_SHA256: return "RSA 4096 - SHA256"; + case TMD_RSA_2048_SHA1: return "RSA 2048 - SHA1"; + case TMD_RSA_4096_SHA1: return "RSA 4096 - SHA1"; + default: + return "unknown"; + } +} + +void tmd_print(tmd_context* ctx) +{ + unsigned int type = getbe32(ctx->buffer); + ctr_tmd_header_4096* header4096 = 0; + ctr_tmd_header_2048* header2048 = 0; + ctr_tmd_body* body = 0; + unsigned int contentcount = 0; + unsigned int i; + + if (type == TMD_RSA_2048_SHA256 || type == TMD_RSA_2048_SHA1) + { + header2048 = (ctr_tmd_header_2048*)ctx->buffer; + } + else if (type == TMD_RSA_4096_SHA256 || type == TMD_RSA_4096_SHA1) + { + header4096 = (ctr_tmd_header_4096*)ctx->buffer; + } + else + { + return; + } + + body = tmd_get_body(ctx); + + contentcount = getbe16(body->contentcount); + + fprintf(stdout, "\nTMD header:\n"); + fprintf(stdout, "Signature type: %s\n", tmd_get_type_string(type)); + fprintf(stdout, "Issuer: %s\n", body->issuer); + fprintf(stdout, "Version: %d\n", body->version); + fprintf(stdout, "CA CRL version: %d\n", body->ca_crl_version); + fprintf(stdout, "Signer CRL version: %d\n", body->signer_crl_version); + memdump(stdout, "System version: ", body->systemversion, 8); + memdump(stdout, "Title id: ", body->titleid, 8); + fprintf(stdout, "Title type: %08x\n", getbe32(body->titletype)); + fprintf(stdout, "Group id: %04x\n", getbe16(body->groupid)); + fprintf(stdout, "Access rights: %08x\n", getbe32(body->accessrights)); + fprintf(stdout, "Title version: %04x\n", getbe16(body->titleversion)); + fprintf(stdout, "Content count: %04x\n", getbe16(body->contentcount)); + fprintf(stdout, "Boot content: %04x\n", getbe16(body->bootcontent)); + memdump(stdout, "Hash: ", body->hash, 32); + + fprintf(stdout, "\nTMD content info:\n"); + for(i = 0; i < TMD_MAX_CONTENTS; i++) + { + ctr_tmd_contentinfo* info = (ctr_tmd_contentinfo*)(body->contentinfo + sizeof(ctr_tmd_contentinfo)*i); + + if (getbe16(info->commandcount) == 0) + continue; + + fprintf(stdout, "Content index: %04x\n", getbe16(info->index)); + fprintf(stdout, "Command count: %04x\n", getbe16(info->commandcount)); + memdump(stdout, "Unknown: ", info->unk, 32); + } + fprintf(stdout, "\nTMD contents:\n"); + for(i = 0; i < contentcount; i++) + { + ctr_tmd_contentchunk* chunk = (ctr_tmd_contentchunk*)(body->contentinfo + 36*64 + i*48); + unsigned short type = getbe16(chunk->type); + + fprintf(stdout, "Content id: %08x\n", getbe32(chunk->id)); + fprintf(stdout, "Content index: %04x\n", getbe16(chunk->index)); + fprintf(stdout, "Content type: %04x", getbe16(chunk->type)); + if (type) + { + fprintf(stdout, " "); + if (type & 1) + fprintf(stdout, "[encrypted]"); + if (type & 2) + fprintf(stdout, "[disc]"); + if (type & 4) + fprintf(stdout, "[cfm]"); + if (type & 0x4000) + fprintf(stdout, "[optional]"); + if (type & 0x8000) + fprintf(stdout, "[shared]"); + } + fprintf(stdout, "\n"); + fprintf(stdout, "Content size: %016llx\n", getbe64(chunk->size)); + + switch(ctx->content_hash_stat[getbe16(chunk->index)]) { + case 1: memdump(stdout, "Content hash [OK]: ", chunk->hash, 32); break; + case 2: memdump(stdout, "Content hash [FAIL]: ", chunk->hash, 32); break; + default: memdump(stdout, "Content hash: ", chunk->hash, 32); break; + } + + fprintf(stdout, "\n"); + } +} diff --git a/ctrtool/tmd.h b/ctrtool/tmd.h new file mode 100644 index 0000000..5d9f475 --- /dev/null +++ b/ctrtool/tmd.h @@ -0,0 +1,99 @@ +#ifndef _TMD_H_ +#define _TMD_H_ + +#include "types.h" +#include "settings.h" + +#define TMD_MAX_CONTENTS 64 + +typedef enum +{ + TMD_RSA_2048_SHA256 = 0x00010004, + TMD_RSA_4096_SHA256 = 0x00010003, + TMD_RSA_2048_SHA1 = 0x00010001, + TMD_RSA_4096_SHA1 = 0x00010000 +} ctr_tmdtype; + + +typedef struct +{ + unsigned char padding[60]; + unsigned char issuer[64]; + unsigned char version; + unsigned char ca_crl_version; + unsigned char signer_crl_version; + unsigned char padding2; + unsigned char systemversion[8]; + unsigned char titleid[8]; + unsigned char titletype[4]; + unsigned char groupid[2]; + unsigned char padding3[62]; + unsigned char accessrights[4]; + unsigned char titleversion[2]; + unsigned char contentcount[2]; + unsigned char bootcontent[2]; + unsigned char padding4[2]; + unsigned char hash[32]; + unsigned char contentinfo[36*64]; +} ctr_tmd_body; + +typedef struct +{ + unsigned char index[2]; + unsigned char commandcount[2]; + unsigned char unk[32]; +} ctr_tmd_contentinfo; + + +typedef struct +{ + unsigned char id[4]; + unsigned char index[2]; + unsigned char type[2]; + unsigned char size[8]; + unsigned char hash[32]; +} ctr_tmd_contentchunk; + + +typedef struct +{ + unsigned char signaturetype[4]; + unsigned char signature[256]; +} ctr_tmd_header_2048; + +typedef struct +{ + unsigned char signaturetype[4]; + unsigned char signature[512]; +} ctr_tmd_header_4096; + +typedef struct +{ + FILE* file; + u32 offset; + u32 size; + u8* buffer; + u8 content_hash_stat[64]; + settings* usersettings; +} tmd_context; + + + +#ifdef __cplusplus +extern "C" { +#endif + +void tmd_init(tmd_context* ctx); +void tmd_set_file(tmd_context* ctx, FILE* file); +void tmd_set_offset(tmd_context* ctx, u32 offset); +void tmd_set_size(tmd_context* ctx, u32 size); +void tmd_set_usersettings(tmd_context* ctx, settings* usersettings); +void tmd_print(tmd_context* ctx); +void tmd_process(tmd_context* ctx, u32 actions); +ctr_tmd_body *tmd_get_body(tmd_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif // _TMD_H_ diff --git a/ctrtool/types.h b/ctrtool/types.h new file mode 100644 index 0000000..bd2b9fd --- /dev/null +++ b/ctrtool/types.h @@ -0,0 +1,36 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#include + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; + +enum flags +{ + ExtractFlag = (1<<0), + InfoFlag = (1<<1), + PlainFlag = (1<<2), + VerboseFlag = (1<<3), + VerifyFlag = (1<<4), + RawFlag = (1<<5), + ShowKeysFlag = (1<<6) +}; + + + +enum validstate +{ + Unchecked = 0, + Good = 1, + Fail = 2, +}; + +#endif diff --git a/ctrtool/utils.c b/ctrtool/utils.c new file mode 100644 index 0000000..a7f2897 --- /dev/null +++ b/ctrtool/utils.c @@ -0,0 +1,192 @@ + +#include +#include +#include "utils.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + + +u32 align(u32 offset, u32 alignment) +{ + u32 mask = ~(alignment-1); + + return (offset + (alignment-1)) & mask; +} + +u64 align64(u64 offset, u32 alignment) +{ + u64 mask = ~(alignment-1); + + return (offset + (alignment-1)) & mask; +} + +u64 getle64(const u8* p) +{ + u64 n = p[0]; + + n |= (u64)p[1]<<8; + n |= (u64)p[2]<<16; + n |= (u64)p[3]<<24; + n |= (u64)p[4]<<32; + n |= (u64)p[5]<<40; + n |= (u64)p[6]<<48; + n |= (u64)p[7]<<56; + return n; +} + +u64 getbe64(const u8* p) +{ + u64 n = 0; + + n |= (u64)p[0]<<56; + n |= (u64)p[1]<<48; + n |= (u64)p[2]<<40; + n |= (u64)p[3]<<32; + n |= (u64)p[4]<<24; + n |= (u64)p[5]<<16; + n |= (u64)p[6]<<8; + n |= (u64)p[7]<<0; + return n; +} + +u32 getle32(const u8* p) +{ + return (p[0]<<0) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); +} + +u32 getbe32(const u8* p) +{ + return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]<<0); +} + +u32 getle16(const u8* p) +{ + return (p[0]<<0) | (p[1]<<8); +} + +u32 getbe16(const u8* p) +{ + return (p[0]<<8) | (p[1]<<0); +} + +void putle16(u8* p, u16 n) +{ + p[0] = n; + p[1] = n>>8; +} + +void putle32(u8* p, u32 n) +{ + p[0] = n; + p[1] = n>>8; + p[2] = n>>16; + p[3] = n>>24; +} + + +void readkeyfile(u8* key, const char* keyfname) +{ + FILE* f = fopen(keyfname, "rb"); + u32 keysize = 0; + + if (0 == f) + { + fprintf(stdout, "Error opening key file\n"); + goto clean; + } + + fseek(f, 0, SEEK_END); + keysize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (keysize != 16) + { + fprintf(stdout, "Error key size mismatch, got %d, expected %d\n", keysize, 16); + goto clean; + } + + if (16 != fread(key, 1, 16, f)) + { + fprintf(stdout, "Error reading key file\n"); + goto clean; + } + +clean: + if (f) + fclose(f); +} + +void hexdump(void *ptr, int buflen) +{ + u8 *buf = (u8*)ptr; + int i, j; + + for (i=0; i= 0x20 && buf[i+j] <= 0x7e) ? buf[i+j] : '.'); + } + } + printf("\n"); + } +} + +void memdump(FILE* fout, const char* prefix, const u8* data, u32 size) +{ + u32 i; + u32 prefixlen = strlen(prefix); + u32 offs = 0; + u32 line = 0; + while(size) + { + u32 max = 32; + + if (max > size) + max = size; + + if (line==0) + fprintf(fout, "%s", prefix); + else + fprintf(fout, "%*s", prefixlen, ""); + + + for(i=0; i addr:%08x nump:%08x size:%08x" % (text.address, text.num_maxpages, text.code_size) + add_segm(0, text.address, text.address + text.code_size, ".text", "CODE") + offs = 0x200 + mem2base(exefs[offs:], text.address, text.address + text.code_size) + + ro = exhdr.codeset_info.ro + print " |_ .ro -> addr:%08x nump:%08x size:%08x" % (ro.address, ro.num_maxpages, ro.code_size) + add_segm(0, ro.address, ro.address + ro.code_size, ".ro", "DATA") + offs = 0x200 + text.code_size + mem2base(exefs[offs:], ro.address, ro.address + ro.code_size) + + data = exhdr.codeset_info.data + print " |_ .data -> addr:%08x nump:%08x size:%08x" % (data.address, data.num_maxpages, data.code_size) + add_segm(0, data.address, data.address + data.code_size, ".data", "DATA") + offs = 0x200 + text.code_size + ro.code_size + mem2base(exefs[offs:], data.address, data.address + data.code_size) + + return 1 + +def load_file(f, neflags, format): + idaapi.set_processor_type("ARM", SETPROC_ALL|SETPROC_FATAL) + tp = get_type(f) + + if tp == "NCCH": + return load_ncch_file(f, 0) + elif tp == "FIRM": + return load_firm_file(f, 0) + + return 0 diff --git a/parentool/Makefile b/parentool/Makefile new file mode 100644 index 0000000..a340ac7 --- /dev/null +++ b/parentool/Makefile @@ -0,0 +1,13 @@ +OBJS = main.o +LIBS = +CXXFLAGS = -I. +CFLAGS = -Wall -I. +OUTPUT = parentool +CC = gcc + +main: $(OBJS) + g++ -o $(OUTPUT) $(LIBS) $(OBJS) + + +clean: + rm -rf $(OUTPUT) $(OBJS) diff --git a/parentool/main.c b/parentool/main.c new file mode 100644 index 0000000..b22198d --- /dev/null +++ b/parentool/main.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +unsigned int calculate_master_key(unsigned char* generator) +{ + uint32_t table[0x100]; + uint32_t data; + uint32_t i, j; + uint32_t y; + uint8_t x; + uint64_t yll; + uint32_t yhi; + + for(i=0; i<0x100; i++) + { + data = i; + for(j=0; j<4; j++) + { + if (data & 1) + data = 0xEDBA6320 ^ (data>>1); + else + data = data>>1; + + if (data & 1) + data = 0xEDBA6320 ^ (data>>1); + else + data = data>>1; + } + + table[i] = data; + } + + y = 0xFFFFFFFF; + x = generator[0]; + for(i=0; i<4; i++) + { + x ^= y; + y = table[x] ^ (y>>8); + x = generator[1+i*2] ^ y; + y = table[x] ^ (y>>8); + x = generator[2+i*2]; + } + + y ^= 0xAAAA; + y += 0x1657; + + yll = y; + yll = (yll+1) * 0xA7C5AC47ULL; + yhi = (yll>>48); + yhi *= 0xFFFFF3CB; + y += (yhi<<5); + + return y; +} + +int main(int argc, const char* argv[]) +{ + unsigned char generator[9] = {0}; + unsigned int servicecode, month, day, masterkey; + + if (argc != 4) + { + printf("usage: \n"); + exit(1); + } + + servicecode = strtoul(argv[1], 0, 10); + month = strtoul(argv[2], 0, 10); + day = strtoul(argv[3], 0, 10); + + servicecode %= 10000; + month %= 100; + day %= 100; + sprintf((char*)generator, "%02d%02d%04d", month, day, servicecode); + + masterkey = calculate_master_key(generator); + + printf("Master key is %05d\n", masterkey); + return 0; +}