commit 87681b8bc5b6f57c4ac7368f494f310b84bfe4aa Author: 3DSGuy Date: Mon Apr 14 09:39:21 2014 +0800 Added neimod's ctrtool Will be base code for improvments diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9d6bd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,215 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +build/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg 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 new file mode 100644 index 0000000..417d03f --- /dev/null +++ b/ctrtool/ctr.c @@ -0,0 +1,354 @@ +#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 new file mode 100644 index 0000000..724f4c0 --- /dev/null +++ b/ctrtool/ctr.h @@ -0,0 +1,146 @@ +#ifndef _CTR_H_ +#define _CTR_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 +{ + FILETYPE_UNKNOWN = 0, + FILETYPE_CCI, + FILETYPE_CXI, + FILETYPE_CIA, + FILETYPE_EXHEADER, + FILETYPE_TMD, + FILETYPE_LZSS, + FILETYPE_FIRM, + FILETYPE_CWAV, + FILETYPE_ROMFS +} ctr_filetypes; + +typedef struct +{ + u8 ctr[16]; + u8 iv[16]; + aes_context aes; +} ctr_aes_context; + +typedef struct +{ + rsa_context rsa; +} ctr_rsa_context; + +typedef struct +{ + sha2_context sha; +} ctr_sha256_context; + + +#ifdef __cplusplus +extern "C" { +#endif + +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] ); + +int ctr_rsa_init( ctr_rsa_context* ctx, + rsakey2048* key ); + + +void ctr_rsa_free( ctr_rsa_context* ctx ); + +int ctr_rsa_verify_hash( const u8 signature[0x100], + const u8 hash[0x20], + rsakey2048* key); + +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_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_sha_256_init( ctr_sha256_context* ctx ); + +void ctr_sha_256_update( ctr_sha256_context* ctx, + const u8* data, + u32 size ); + + +void ctr_sha_256_finish( ctr_sha256_context* ctx, + u8 hash[0x20] ); + +#ifdef __cplusplus +} +#endif + +#endif // _CTR_H_ diff --git a/ctrtool/ctrtool.vcproj b/ctrtool/ctrtool.vcproj new file mode 100644 index 0000000..d3e8b0d --- /dev/null +++ b/ctrtool/ctrtool.vcproj @@ -0,0 +1,442 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c new file mode 100644 index 0000000..e532db0 --- /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..e75f22c --- /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..a4dce90 --- /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..1fe0347 --- /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..94321e2 --- /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..5c3d07d --- /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..5ba15b8 --- /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..e4e9256 --- /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..93bc769 --- /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..64a5b2f --- /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 new file mode 100644 index 0000000..a432aa3 --- /dev/null +++ b/ctrtool/main.c @@ -0,0 +1,444 @@ +#include +#include +#include +#include +#include "utils.h" +#include "ctr.h" +#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 +{ + Plain, + CTR, + CBC +}; + + +typedef struct +{ + int actions; + u32 filetype; + FILE* infile; + u32 infilesize; + settings usersettings; +} toolcontext; + +static void usage(const char *argv0) +{ + fprintf(stderr, + "Usage: %s [options...] \n" + "CTRTOOL (c) neimod.\n" + "\n" + "Options:\n" + " -i, --info Show file info.\n" + " This is the default action.\n" + " -x, --extract Extract data from file.\n" + " This is also the default action.\n" + " -p, --plain Extract data without decrypting.\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 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 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); +} + + +int main(int argc, char* argv[]) +{ + toolcontext ctx; + u8 magic[4]; + char infname[512]; + int c; + u32 ncchoffset = ~0; + char keysetfname[512] = "keys.xml"; + keyset tmpkeys; + unsigned int checkkeysetfile = 0; + + memset(&ctx, 0, sizeof(toolcontext)); + ctx.actions = InfoFlag | ExtractFlag; + ctx.filetype = FILETYPE_UNKNOWN; + + settings_init(&ctx.usersettings); + keyset_init(&ctx.usersettings.keys); + keyset_init(&tmpkeys); + + + while (1) + { + int option_index; + static struct option long_options[] = + { + {"extract", 0, NULL, 'x'}, + {"plain", 0, NULL, 'p'}, + {"info", 0, NULL, 'i'}, + {"exefs", 1, NULL, 0}, + {"romfs", 1, NULL, 1}, + {"exheader", 1, NULL, 2}, + {"certs", 1, NULL, 3}, + {"tik", 1, NULL, 4}, + {"tmd", 1, NULL, 5}, + {"contents", 1, NULL, 6}, + {"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, "ryxivpk:n:t:", long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 'x': + ctx.actions |= ExtractFlag; + break; + + case 'v': + ctx.actions |= VerboseFlag; + break; + + case 'y': + ctx.actions |= VerifyFlag; + break; + + case 'p': + ctx.actions |= PlainFlag; + break; + + case 'r': + ctx.actions |= RawFlag; + break; + + case 'i': + ctx.actions |= InfoFlag; + break; + + case 'n': + ncchoffset = strtoul(optarg, 0, 0); + break; + + case 'k': + strncpy(keysetfname, optarg, sizeof(keysetfname)); + checkkeysetfile = 1; + break; + + 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 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; + + + default: + usage(argv[0]); + } + } + + if (optind == argc - 1) + { + // Exactly one extra argument - an input file + strncpy(infname, argv[optind], sizeof(infname)); + } + else if ( (optind < argc) || (argc == 1) ) + { + // Too many extra args + usage(argv[0]); + } + + 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 == 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); + + + + + if (ctx.filetype == FILETYPE_UNKNOWN) + { + 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; + + 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) + { + fprintf(stdout, "Unknown file\n"); + exit(1); + } + + + switch(ctx.filetype) + { + case FILETYPE_CCI: + { + 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: + { + 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; + + 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); + + return 0; +} diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c new file mode 100644 index 0000000..a36a7d6 --- /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..a289796 --- /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..d886213 --- /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/polarssl/aes.c b/ctrtool/polarssl/aes.c new file mode 100644 index 0000000..45f74a6 --- /dev/null +++ b/ctrtool/polarssl/aes.c @@ -0,0 +1,1164 @@ +/* + * FIPS-197 compliant AES 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 AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_AES_C) + +#include "polarssl/aes.h" +#include "polarssl/padlock.h" + +#include + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +#if defined(POLARSSL_AES_ROM_TABLES) +/* + * Forward S-box + */ +static const unsigned char FSb[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +/* + * Forward tables + */ +#define FT \ +\ + V(A5,63,63,C6), V(84,7C,7C,F8), V(99,77,77,EE), V(8D,7B,7B,F6), \ + V(0D,F2,F2,FF), V(BD,6B,6B,D6), V(B1,6F,6F,DE), V(54,C5,C5,91), \ + V(50,30,30,60), V(03,01,01,02), V(A9,67,67,CE), V(7D,2B,2B,56), \ + V(19,FE,FE,E7), V(62,D7,D7,B5), V(E6,AB,AB,4D), V(9A,76,76,EC), \ + V(45,CA,CA,8F), V(9D,82,82,1F), V(40,C9,C9,89), V(87,7D,7D,FA), \ + V(15,FA,FA,EF), V(EB,59,59,B2), V(C9,47,47,8E), V(0B,F0,F0,FB), \ + V(EC,AD,AD,41), V(67,D4,D4,B3), V(FD,A2,A2,5F), V(EA,AF,AF,45), \ + V(BF,9C,9C,23), V(F7,A4,A4,53), V(96,72,72,E4), V(5B,C0,C0,9B), \ + V(C2,B7,B7,75), V(1C,FD,FD,E1), V(AE,93,93,3D), V(6A,26,26,4C), \ + V(5A,36,36,6C), V(41,3F,3F,7E), V(02,F7,F7,F5), V(4F,CC,CC,83), \ + V(5C,34,34,68), V(F4,A5,A5,51), V(34,E5,E5,D1), V(08,F1,F1,F9), \ + V(93,71,71,E2), V(73,D8,D8,AB), V(53,31,31,62), V(3F,15,15,2A), \ + V(0C,04,04,08), V(52,C7,C7,95), V(65,23,23,46), V(5E,C3,C3,9D), \ + V(28,18,18,30), V(A1,96,96,37), V(0F,05,05,0A), V(B5,9A,9A,2F), \ + V(09,07,07,0E), V(36,12,12,24), V(9B,80,80,1B), V(3D,E2,E2,DF), \ + V(26,EB,EB,CD), V(69,27,27,4E), V(CD,B2,B2,7F), V(9F,75,75,EA), \ + V(1B,09,09,12), V(9E,83,83,1D), V(74,2C,2C,58), V(2E,1A,1A,34), \ + V(2D,1B,1B,36), V(B2,6E,6E,DC), V(EE,5A,5A,B4), V(FB,A0,A0,5B), \ + V(F6,52,52,A4), V(4D,3B,3B,76), V(61,D6,D6,B7), V(CE,B3,B3,7D), \ + V(7B,29,29,52), V(3E,E3,E3,DD), V(71,2F,2F,5E), V(97,84,84,13), \ + V(F5,53,53,A6), V(68,D1,D1,B9), V(00,00,00,00), V(2C,ED,ED,C1), \ + V(60,20,20,40), V(1F,FC,FC,E3), V(C8,B1,B1,79), V(ED,5B,5B,B6), \ + V(BE,6A,6A,D4), V(46,CB,CB,8D), V(D9,BE,BE,67), V(4B,39,39,72), \ + V(DE,4A,4A,94), V(D4,4C,4C,98), V(E8,58,58,B0), V(4A,CF,CF,85), \ + V(6B,D0,D0,BB), V(2A,EF,EF,C5), V(E5,AA,AA,4F), V(16,FB,FB,ED), \ + V(C5,43,43,86), V(D7,4D,4D,9A), V(55,33,33,66), V(94,85,85,11), \ + V(CF,45,45,8A), V(10,F9,F9,E9), V(06,02,02,04), V(81,7F,7F,FE), \ + V(F0,50,50,A0), V(44,3C,3C,78), V(BA,9F,9F,25), V(E3,A8,A8,4B), \ + V(F3,51,51,A2), V(FE,A3,A3,5D), V(C0,40,40,80), V(8A,8F,8F,05), \ + V(AD,92,92,3F), V(BC,9D,9D,21), V(48,38,38,70), V(04,F5,F5,F1), \ + V(DF,BC,BC,63), V(C1,B6,B6,77), V(75,DA,DA,AF), V(63,21,21,42), \ + V(30,10,10,20), V(1A,FF,FF,E5), V(0E,F3,F3,FD), V(6D,D2,D2,BF), \ + V(4C,CD,CD,81), V(14,0C,0C,18), V(35,13,13,26), V(2F,EC,EC,C3), \ + V(E1,5F,5F,BE), V(A2,97,97,35), V(CC,44,44,88), V(39,17,17,2E), \ + V(57,C4,C4,93), V(F2,A7,A7,55), V(82,7E,7E,FC), V(47,3D,3D,7A), \ + V(AC,64,64,C8), V(E7,5D,5D,BA), V(2B,19,19,32), V(95,73,73,E6), \ + V(A0,60,60,C0), V(98,81,81,19), V(D1,4F,4F,9E), V(7F,DC,DC,A3), \ + V(66,22,22,44), V(7E,2A,2A,54), V(AB,90,90,3B), V(83,88,88,0B), \ + V(CA,46,46,8C), V(29,EE,EE,C7), V(D3,B8,B8,6B), V(3C,14,14,28), \ + V(79,DE,DE,A7), V(E2,5E,5E,BC), V(1D,0B,0B,16), V(76,DB,DB,AD), \ + V(3B,E0,E0,DB), V(56,32,32,64), V(4E,3A,3A,74), V(1E,0A,0A,14), \ + V(DB,49,49,92), V(0A,06,06,0C), V(6C,24,24,48), V(E4,5C,5C,B8), \ + V(5D,C2,C2,9F), V(6E,D3,D3,BD), V(EF,AC,AC,43), V(A6,62,62,C4), \ + V(A8,91,91,39), V(A4,95,95,31), V(37,E4,E4,D3), V(8B,79,79,F2), \ + V(32,E7,E7,D5), V(43,C8,C8,8B), V(59,37,37,6E), V(B7,6D,6D,DA), \ + V(8C,8D,8D,01), V(64,D5,D5,B1), V(D2,4E,4E,9C), V(E0,A9,A9,49), \ + V(B4,6C,6C,D8), V(FA,56,56,AC), V(07,F4,F4,F3), V(25,EA,EA,CF), \ + V(AF,65,65,CA), V(8E,7A,7A,F4), V(E9,AE,AE,47), V(18,08,08,10), \ + V(D5,BA,BA,6F), V(88,78,78,F0), V(6F,25,25,4A), V(72,2E,2E,5C), \ + V(24,1C,1C,38), V(F1,A6,A6,57), V(C7,B4,B4,73), V(51,C6,C6,97), \ + V(23,E8,E8,CB), V(7C,DD,DD,A1), V(9C,74,74,E8), V(21,1F,1F,3E), \ + V(DD,4B,4B,96), V(DC,BD,BD,61), V(86,8B,8B,0D), V(85,8A,8A,0F), \ + V(90,70,70,E0), V(42,3E,3E,7C), V(C4,B5,B5,71), V(AA,66,66,CC), \ + V(D8,48,48,90), V(05,03,03,06), V(01,F6,F6,F7), V(12,0E,0E,1C), \ + V(A3,61,61,C2), V(5F,35,35,6A), V(F9,57,57,AE), V(D0,B9,B9,69), \ + V(91,86,86,17), V(58,C1,C1,99), V(27,1D,1D,3A), V(B9,9E,9E,27), \ + V(38,E1,E1,D9), V(13,F8,F8,EB), V(B3,98,98,2B), V(33,11,11,22), \ + V(BB,69,69,D2), V(70,D9,D9,A9), V(89,8E,8E,07), V(A7,94,94,33), \ + V(B6,9B,9B,2D), V(22,1E,1E,3C), V(92,87,87,15), V(20,E9,E9,C9), \ + V(49,CE,CE,87), V(FF,55,55,AA), V(78,28,28,50), V(7A,DF,DF,A5), \ + V(8F,8C,8C,03), V(F8,A1,A1,59), V(80,89,89,09), V(17,0D,0D,1A), \ + V(DA,BF,BF,65), V(31,E6,E6,D7), V(C6,42,42,84), V(B8,68,68,D0), \ + V(C3,41,41,82), V(B0,99,99,29), V(77,2D,2D,5A), V(11,0F,0F,1E), \ + V(CB,B0,B0,7B), V(FC,54,54,A8), V(D6,BB,BB,6D), V(3A,16,16,2C) + +#define V(a,b,c,d) 0x##a##b##c##d +static const unsigned long FT0[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const unsigned long FT1[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const unsigned long FT2[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const unsigned long FT3[256] = { FT }; +#undef V + +#undef FT + +/* + * Reverse S-box + */ +static const unsigned char RSb[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/* + * Reverse tables + */ +#define RT \ +\ + V(50,A7,F4,51), V(53,65,41,7E), V(C3,A4,17,1A), V(96,5E,27,3A), \ + V(CB,6B,AB,3B), V(F1,45,9D,1F), V(AB,58,FA,AC), V(93,03,E3,4B), \ + V(55,FA,30,20), V(F6,6D,76,AD), V(91,76,CC,88), V(25,4C,02,F5), \ + V(FC,D7,E5,4F), V(D7,CB,2A,C5), V(80,44,35,26), V(8F,A3,62,B5), \ + V(49,5A,B1,DE), V(67,1B,BA,25), V(98,0E,EA,45), V(E1,C0,FE,5D), \ + V(02,75,2F,C3), V(12,F0,4C,81), V(A3,97,46,8D), V(C6,F9,D3,6B), \ + V(E7,5F,8F,03), V(95,9C,92,15), V(EB,7A,6D,BF), V(DA,59,52,95), \ + V(2D,83,BE,D4), V(D3,21,74,58), V(29,69,E0,49), V(44,C8,C9,8E), \ + V(6A,89,C2,75), V(78,79,8E,F4), V(6B,3E,58,99), V(DD,71,B9,27), \ + V(B6,4F,E1,BE), V(17,AD,88,F0), V(66,AC,20,C9), V(B4,3A,CE,7D), \ + V(18,4A,DF,63), V(82,31,1A,E5), V(60,33,51,97), V(45,7F,53,62), \ + V(E0,77,64,B1), V(84,AE,6B,BB), V(1C,A0,81,FE), V(94,2B,08,F9), \ + V(58,68,48,70), V(19,FD,45,8F), V(87,6C,DE,94), V(B7,F8,7B,52), \ + V(23,D3,73,AB), V(E2,02,4B,72), V(57,8F,1F,E3), V(2A,AB,55,66), \ + V(07,28,EB,B2), V(03,C2,B5,2F), V(9A,7B,C5,86), V(A5,08,37,D3), \ + V(F2,87,28,30), V(B2,A5,BF,23), V(BA,6A,03,02), V(5C,82,16,ED), \ + V(2B,1C,CF,8A), V(92,B4,79,A7), V(F0,F2,07,F3), V(A1,E2,69,4E), \ + V(CD,F4,DA,65), V(D5,BE,05,06), V(1F,62,34,D1), V(8A,FE,A6,C4), \ + V(9D,53,2E,34), V(A0,55,F3,A2), V(32,E1,8A,05), V(75,EB,F6,A4), \ + V(39,EC,83,0B), V(AA,EF,60,40), V(06,9F,71,5E), V(51,10,6E,BD), \ + V(F9,8A,21,3E), V(3D,06,DD,96), V(AE,05,3E,DD), V(46,BD,E6,4D), \ + V(B5,8D,54,91), V(05,5D,C4,71), V(6F,D4,06,04), V(FF,15,50,60), \ + V(24,FB,98,19), V(97,E9,BD,D6), V(CC,43,40,89), V(77,9E,D9,67), \ + V(BD,42,E8,B0), V(88,8B,89,07), V(38,5B,19,E7), V(DB,EE,C8,79), \ + V(47,0A,7C,A1), V(E9,0F,42,7C), V(C9,1E,84,F8), V(00,00,00,00), \ + V(83,86,80,09), V(48,ED,2B,32), V(AC,70,11,1E), V(4E,72,5A,6C), \ + V(FB,FF,0E,FD), V(56,38,85,0F), V(1E,D5,AE,3D), V(27,39,2D,36), \ + V(64,D9,0F,0A), V(21,A6,5C,68), V(D1,54,5B,9B), V(3A,2E,36,24), \ + V(B1,67,0A,0C), V(0F,E7,57,93), V(D2,96,EE,B4), V(9E,91,9B,1B), \ + V(4F,C5,C0,80), V(A2,20,DC,61), V(69,4B,77,5A), V(16,1A,12,1C), \ + V(0A,BA,93,E2), V(E5,2A,A0,C0), V(43,E0,22,3C), V(1D,17,1B,12), \ + V(0B,0D,09,0E), V(AD,C7,8B,F2), V(B9,A8,B6,2D), V(C8,A9,1E,14), \ + V(85,19,F1,57), V(4C,07,75,AF), V(BB,DD,99,EE), V(FD,60,7F,A3), \ + V(9F,26,01,F7), V(BC,F5,72,5C), V(C5,3B,66,44), V(34,7E,FB,5B), \ + V(76,29,43,8B), V(DC,C6,23,CB), V(68,FC,ED,B6), V(63,F1,E4,B8), \ + V(CA,DC,31,D7), V(10,85,63,42), V(40,22,97,13), V(20,11,C6,84), \ + V(7D,24,4A,85), V(F8,3D,BB,D2), V(11,32,F9,AE), V(6D,A1,29,C7), \ + V(4B,2F,9E,1D), V(F3,30,B2,DC), V(EC,52,86,0D), V(D0,E3,C1,77), \ + V(6C,16,B3,2B), V(99,B9,70,A9), V(FA,48,94,11), V(22,64,E9,47), \ + V(C4,8C,FC,A8), V(1A,3F,F0,A0), V(D8,2C,7D,56), V(EF,90,33,22), \ + V(C7,4E,49,87), V(C1,D1,38,D9), V(FE,A2,CA,8C), V(36,0B,D4,98), \ + V(CF,81,F5,A6), V(28,DE,7A,A5), V(26,8E,B7,DA), V(A4,BF,AD,3F), \ + V(E4,9D,3A,2C), V(0D,92,78,50), V(9B,CC,5F,6A), V(62,46,7E,54), \ + V(C2,13,8D,F6), V(E8,B8,D8,90), V(5E,F7,39,2E), V(F5,AF,C3,82), \ + V(BE,80,5D,9F), V(7C,93,D0,69), V(A9,2D,D5,6F), V(B3,12,25,CF), \ + V(3B,99,AC,C8), V(A7,7D,18,10), V(6E,63,9C,E8), V(7B,BB,3B,DB), \ + V(09,78,26,CD), V(F4,18,59,6E), V(01,B7,9A,EC), V(A8,9A,4F,83), \ + V(65,6E,95,E6), V(7E,E6,FF,AA), V(08,CF,BC,21), V(E6,E8,15,EF), \ + V(D9,9B,E7,BA), V(CE,36,6F,4A), V(D4,09,9F,EA), V(D6,7C,B0,29), \ + V(AF,B2,A4,31), V(31,23,3F,2A), V(30,94,A5,C6), V(C0,66,A2,35), \ + V(37,BC,4E,74), V(A6,CA,82,FC), V(B0,D0,90,E0), V(15,D8,A7,33), \ + V(4A,98,04,F1), V(F7,DA,EC,41), V(0E,50,CD,7F), V(2F,F6,91,17), \ + V(8D,D6,4D,76), V(4D,B0,EF,43), V(54,4D,AA,CC), V(DF,04,96,E4), \ + V(E3,B5,D1,9E), V(1B,88,6A,4C), V(B8,1F,2C,C1), V(7F,51,65,46), \ + V(04,EA,5E,9D), V(5D,35,8C,01), V(73,74,87,FA), V(2E,41,0B,FB), \ + V(5A,1D,67,B3), V(52,D2,DB,92), V(33,56,10,E9), V(13,47,D6,6D), \ + V(8C,61,D7,9A), V(7A,0C,A1,37), V(8E,14,F8,59), V(89,3C,13,EB), \ + V(EE,27,A9,CE), V(35,C9,61,B7), V(ED,E5,1C,E1), V(3C,B1,47,7A), \ + V(59,DF,D2,9C), V(3F,73,F2,55), V(79,CE,14,18), V(BF,37,C7,73), \ + V(EA,CD,F7,53), V(5B,AA,FD,5F), V(14,6F,3D,DF), V(86,DB,44,78), \ + V(81,F3,AF,CA), V(3E,C4,68,B9), V(2C,34,24,38), V(5F,40,A3,C2), \ + V(72,C3,1D,16), V(0C,25,E2,BC), V(8B,49,3C,28), V(41,95,0D,FF), \ + V(71,01,A8,39), V(DE,B3,0C,08), V(9C,E4,B4,D8), V(90,C1,56,64), \ + V(61,84,CB,7B), V(70,B6,32,D5), V(74,5C,6C,48), V(42,57,B8,D0) + +#define V(a,b,c,d) 0x##a##b##c##d +static const unsigned long RT0[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const unsigned long RT1[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const unsigned long RT2[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const unsigned long RT3[256] = { RT }; +#undef V + +#undef RT + +/* + * Round constants + */ +static const unsigned long RCON[10] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x0000001B, 0x00000036 +}; + +#else + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static unsigned long FT0[256]; +static unsigned long FT1[256]; +static unsigned long FT2[256]; +static unsigned long FT3[256]; + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static unsigned long RT0[256]; +static unsigned long RT1[256]; +static unsigned long RT2[256]; +static unsigned long RT3[256]; + +/* + * Round constants + */ +static unsigned long RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) +#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) +#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) + +static int aes_init_done = 0; + +static void aes_gen_tables( void ) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for( i = 0, x = 1; i < 256; i++ ) + { + pow[i] = x; + log[x] = i; + x = ( x ^ XTIME( x ) ) & 0xFF; + } + + /* + * calculate the round constants + */ + for( i = 0, x = 1; i < 10; i++ ) + { + RCON[i] = (unsigned long) x; + x = XTIME( x ) & 0xFF; + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for( i = 1; i < 256; i++ ) + { + x = pow[255 - log[i]]; + + y = x; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for( i = 0; i < 256; i++ ) + { + x = FSb[i]; + y = XTIME( x ) & 0xFF; + z = ( y ^ x ) & 0xFF; + + FT0[i] = ( (unsigned long) y ) ^ + ( (unsigned long) x << 8 ) ^ + ( (unsigned long) x << 16 ) ^ + ( (unsigned long) z << 24 ); + + FT1[i] = ROTL8( FT0[i] ); + FT2[i] = ROTL8( FT1[i] ); + FT3[i] = ROTL8( FT2[i] ); + + x = RSb[i]; + + RT0[i] = ( (unsigned long) MUL( 0x0E, x ) ) ^ + ( (unsigned long) MUL( 0x09, x ) << 8 ) ^ + ( (unsigned long) MUL( 0x0D, x ) << 16 ) ^ + ( (unsigned long) MUL( 0x0B, x ) << 24 ); + + RT1[i] = ROTL8( RT0[i] ); + RT2[i] = ROTL8( RT1[i] ); + RT3[i] = ROTL8( RT2[i] ); + } +} + +#endif + +/* + * AES key schedule (encryption) + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ) +{ + int i; + unsigned long *RK; + +#if !defined(POLARSSL_AES_ROM_TABLES) + if( aes_init_done == 0 ) + { + aes_gen_tables(); + aes_init_done = 1; + } +#endif + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(PADLOCK_ALIGN16) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); +#else + ctx->rk = RK = ctx->buf; +#endif + + for( i = 0; i < (keysize >> 5); i++ ) + { + GET_ULONG_LE( RK[i], key, i << 2 ); + } + + switch( ctx->nr ) + { + case 10: + + for( i = 0; i < 10; i++, RK += 4 ) + { + RK[4] = RK[0] ^ RCON[i] ^ + ( (unsigned long) FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[3] ) & 0xFF ] << 24 ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for( i = 0; i < 8; i++, RK += 6 ) + { + RK[6] = RK[0] ^ RCON[i] ^ + ( (unsigned long) FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[5] ) & 0xFF ] << 24 ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for( i = 0; i < 7; i++, RK += 8 ) + { + RK[8] = RK[0] ^ RCON[i] ^ + ( (unsigned long) FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[7] ) & 0xFF ] << 24 ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ( (unsigned long) FSb[ ( RK[11] ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + + default: + + break; + } + + return( 0 ); +} + +/* + * AES key schedule (decryption) + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize ) +{ + int i, j; + aes_context cty; + unsigned long *RK; + unsigned long *SK; + int ret; + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(PADLOCK_ALIGN16) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); +#else + ctx->rk = RK = ctx->buf; +#endif + + ret = aes_setkey_enc( &cty, key, keysize ); + if( ret != 0 ) + return( ret ); + + SK = cty.rk + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for( i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8 ) + { + for( j = 0; j < 4; j++, SK++ ) + { + *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ + RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ + RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ + RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + memset( &cty, 0, sizeof( aes_context ) ); + + return( 0 ); +} + +#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ + FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ + FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y0 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ + FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ + FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y2 >> 24 ) & 0xFF ]; \ +} + +#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ + RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ + RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y2 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ + RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ + RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y0 >> 24 ) & 0xFF ]; \ +} + +/* + * AES-ECB block encryption/decryption + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int i; + unsigned long *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( padlock_supports( PADLOCK_ACE ) ) + { + if( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + RK = ctx->rk; + + GET_ULONG_LE( X0, input, 0 ); X0 ^= *RK++; + GET_ULONG_LE( X1, input, 4 ); X1 ^= *RK++; + GET_ULONG_LE( X2, input, 8 ); X2 ^= *RK++; + GET_ULONG_LE( X3, input, 12 ); X3 ^= *RK++; + + if( mode == AES_DECRYPT ) + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y0 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y1 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y2 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (unsigned long) RSb[ ( Y3 ) & 0xFF ] ) ^ + ( (unsigned long) RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + } + else /* AES_ENCRYPT */ + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y0 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y1 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y2 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (unsigned long) FSb[ ( Y3 ) & 0xFF ] ) ^ + ( (unsigned long) FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (unsigned long) FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (unsigned long) FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + } + + PUT_ULONG_LE( X0, output, 0 ); + 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 + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + 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( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + if( mode == AES_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 16 ); + aes_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + aes_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + + return( 0 ); +} + +/* + * AES-CFB128 buffer encryption/decryption + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + int length, + int *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, n = *iv_off; + + if( mode == AES_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + + return( 0 ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * AES test vectors from: + * + * http://csrc.nist.gov/archive/aes/rijndael/rijndael-vals.zip + */ +static const unsigned char aes_test_ecb_dec[3][16] = +{ + { 0x44, 0x41, 0x6A, 0xC2, 0xD1, 0xF5, 0x3C, 0x58, + 0x33, 0x03, 0x91, 0x7E, 0x6B, 0xE9, 0xEB, 0xE0 }, + { 0x48, 0xE3, 0x1E, 0x9E, 0x25, 0x67, 0x18, 0xF2, + 0x92, 0x29, 0x31, 0x9C, 0x19, 0xF1, 0x5B, 0xA4 }, + { 0x05, 0x8C, 0xCF, 0xFD, 0xBB, 0xCB, 0x38, 0x2D, + 0x1F, 0x6F, 0x56, 0x58, 0x5D, 0x8A, 0x4A, 0xDE } +}; + +static const unsigned char aes_test_ecb_enc[3][16] = +{ + { 0xC3, 0x4C, 0x05, 0x2C, 0xC0, 0xDA, 0x8D, 0x73, + 0x45, 0x1A, 0xFE, 0x5F, 0x03, 0xBE, 0x29, 0x7F }, + { 0xF3, 0xF6, 0x75, 0x2A, 0xE8, 0xD7, 0x83, 0x11, + 0x38, 0xF0, 0x41, 0x56, 0x06, 0x31, 0xB1, 0x14 }, + { 0x8B, 0x79, 0xEE, 0xCC, 0x93, 0xA0, 0xEE, 0x5D, + 0xFF, 0x30, 0xB4, 0xEA, 0x21, 0x63, 0x6D, 0xA4 } +}; + +static const unsigned char aes_test_cbc_dec[3][16] = +{ + { 0xFA, 0xCA, 0x37, 0xE0, 0xB0, 0xC8, 0x53, 0x73, + 0xDF, 0x70, 0x6E, 0x73, 0xF7, 0xC9, 0xAF, 0x86 }, + { 0x5D, 0xF6, 0x78, 0xDD, 0x17, 0xBA, 0x4E, 0x75, + 0xB6, 0x17, 0x68, 0xC6, 0xAD, 0xEF, 0x7C, 0x7B }, + { 0x48, 0x04, 0xE1, 0x81, 0x8F, 0xE6, 0x29, 0x75, + 0x19, 0xA3, 0xE8, 0x8C, 0x57, 0x31, 0x04, 0x13 } +}; + +static const unsigned char aes_test_cbc_enc[3][16] = +{ + { 0x8A, 0x05, 0xFC, 0x5E, 0x09, 0x5A, 0xF4, 0x84, + 0x8A, 0x08, 0xD3, 0x28, 0xD3, 0x68, 0x8E, 0x3D }, + { 0x7B, 0xD9, 0x66, 0xD5, 0x3A, 0xD8, 0xC1, 0xBB, + 0x85, 0xD2, 0xAD, 0xFA, 0xE8, 0x7B, 0xB1, 0x04 }, + { 0xFE, 0x3C, 0x53, 0x65, 0x3E, 0x2F, 0x45, 0xB5, + 0x6F, 0xCD, 0x88, 0xB2, 0xCC, 0x89, 0x8F, 0xF0 } +}; + +/* + * AES-CFB128 test vectors from: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static const unsigned char aes_test_cfb128_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_cfb128_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_cfb128_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_cfb128_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, + 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B, + 0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, + 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF, + 0xC0, 0x4B, 0x05, 0x35, 0x7C, 0x5D, 0x1C, 0x0E, + 0xEA, 0xC4, 0xC6, 0x6F, 0x9F, 0xF7, 0xF2, 0xE6 }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0x67, 0xCE, 0x7F, 0x7F, 0x81, 0x17, 0x36, 0x21, + 0x96, 0x1A, 0x2B, 0x70, 0x17, 0x1D, 0x3D, 0x7A, + 0x2E, 0x1E, 0x8A, 0x1D, 0xD5, 0x9B, 0x88, 0xB1, + 0xC8, 0xE6, 0x0F, 0xED, 0x1E, 0xFA, 0xC4, 0xC9, + 0xC0, 0x5F, 0x9F, 0x9C, 0xA9, 0x83, 0x4F, 0xA0, + 0x42, 0xAE, 0x8F, 0xBA, 0x58, 0x4B, 0x09, 0xFF }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x39, 0xFF, 0xED, 0x14, 0x3B, 0x28, 0xB1, 0xC8, + 0x32, 0x11, 0x3C, 0x63, 0x31, 0xE5, 0x40, 0x7B, + 0xDF, 0x10, 0x13, 0x24, 0x15, 0xE5, 0x4B, 0x92, + 0xA1, 0x3E, 0xD0, 0xA8, 0x26, 0x7A, 0xE2, 0xF9, + 0x75, 0xA3, 0x85, 0x74, 0x1A, 0xB9, 0xCE, 0xF8, + 0x20, 0x31, 0x62, 0x3D, 0x55, 0xB1, 0xE4, 0x71 } +}; + +/* + * Checkup routine + */ +int aes_self_test( int verbose ) +{ + int i, j, u, v, offset; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char prv[16]; + unsigned char iv[16]; + aes_context ctx; + + memset( key, 0, 32 ); + + /* + * ECB mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-ECB-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + /* + * CBC mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CBC-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( iv , 0, 16 ); + memset( prv, 0, 16 ); + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + if( memcmp( buf, aes_test_cbc_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + { + unsigned char tmp[16]; + + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + memcpy( tmp, prv, 16 ); + memcpy( prv, buf, 16 ); + memcpy( buf, tmp, 16 ); + } + + if( memcmp( prv, aes_test_cbc_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + /* + * CFB128 mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CFB128-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, aes_test_cfb128_iv, 16 ); + memcpy( key, aes_test_cfb128_key[u], 16 + u * 8 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + if( v == AES_DECRYPT ) + { + memcpy( buf, aes_test_cfb128_ct[u], 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_pt, 64 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + memcpy( buf, aes_test_cfb128_pt, 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_ct[u], 64 ) != 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/aes.h b/ctrtool/polarssl/aes.h new file mode 100644 index 0000000..5576680 --- /dev/null +++ b/ctrtool/polarssl/aes.h @@ -0,0 +1,139 @@ +/** + * \file aes.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_AES_H +#define POLARSSL_AES_H + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0800 +#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0810 + +/** + * \brief AES context structure + */ +typedef struct +{ + int nr; /*!< number of rounds */ + unsigned long *rk; /*!< AES round keys */ + unsigned long buf[68]; /*!< unaligned data */ +} +aes_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief AES key schedule (encryption) + * + * \param ctx AES context to be initialized + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ); + +/** + * \brief AES key schedule (decryption) + * + * \param ctx AES context to be initialized + * \param key decryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize ); + +/** + * \brief AES-ECB block encryption/decryption + * + * \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 successful + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +/** + * \brief AES-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (16 bytes) + * + * \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 successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief AES-CFB128 buffer encryption/decryption. + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \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 + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + int length, + int *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* aes.h */ 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..ad45181 --- /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..9325635 --- /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..cd3f9bf --- /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..e18dda3 --- /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. + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +#include + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/ctrtool/windows/getopt.h b/ctrtool/windows/getopt.h new file mode 100644 index 0000000..b65740d --- /dev/null +++ b/ctrtool/windows/getopt.h @@ -0,0 +1,172 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + extern char *optarg; + + /* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + extern int optind; + + /* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + + extern int opterr; + + /* Set to an option character which was unrecognized. */ + + extern int optopt; + +#ifndef __need_getopt + /* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + + struct option { +# if defined __STDC__ && __STDC__ + const char *name; +# else + + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; + }; + + /* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + + /* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if defined __STDC__ && __STDC__ +# ifdef __GNU_LIBRARY__ + /* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ + extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ + extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt + + extern int getopt_long (int argc, char ** argv, const char * shortopts, + const struct option * longopts, int * longind); + + extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + + /* Internal only. Users should not call this directly. */ + extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ + extern int getopt (); +# ifndef __need_getopt + extern int getopt_long (); + extern int getopt_long_only (); + + extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/ctrtool/windows/getopt1.c b/ctrtool/windows/getopt1.c new file mode 100644 index 0000000..00c3071 --- /dev/null +++ b/ctrtool/windows/getopt1.c @@ -0,0 +1,188 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */